From 4a2e0e5d452026b5fd4e5554964f70ff65d4b1b2 Mon Sep 17 00:00:00 2001 From: Peter Thatcher Date: Fri, 15 Apr 2022 17:13:23 -0600 Subject: [PATCH] Update to 4896 (M100) (#72) --- .gn | 4 + .mailmap | 1 + .style.yapf | 4 + .vpython3 | 82 + AUTHORS | 10 + BUILD.gn | 82 +- DEPS | 776 +-- ENG_REVIEW_OWNERS | 1 - OWNERS | 4 + PRESUBMIT.py | 2105 +++--- api/BUILD.gn | 55 + api/DEPS | 41 +- api/async_dns_resolver.h | 7 +- api/audio/BUILD.gn | 3 +- api/audio/audio_frame.cc | 24 - api/audio/audio_frame.h | 8 +- api/audio/channel_layout.cc | 2 +- api/audio/echo_canceller3_config.h | 3 +- api/audio/echo_canceller3_config_json.cc | 31 +- api/audio/test/audio_frame_unittest.cc | 50 - api/audio_codecs/BUILD.gn | 1 + api/audio_codecs/L16/BUILD.gn | 2 + api/audio_codecs/L16/audio_decoder_L16.cc | 18 +- api/audio_codecs/L16/audio_decoder_L16.h | 7 +- api/audio_codecs/L16/audio_encoder_L16.cc | 16 +- api/audio_codecs/L16/audio_encoder_L16.h | 8 +- api/audio_codecs/audio_decoder.cc | 3 +- api/audio_codecs/audio_decoder.h | 10 +- .../audio_decoder_factory_template.h | 25 +- api/audio_codecs/audio_encoder.cc | 3 +- api/audio_codecs/audio_encoder.h | 3 + .../audio_encoder_factory_template.h | 24 +- .../builtin_audio_encoder_factory.cc | 6 +- api/audio_codecs/g711/BUILD.gn | 2 + api/audio_codecs/g711/audio_decoder_g711.cc | 14 +- api/audio_codecs/g711/audio_decoder_g711.h | 8 +- api/audio_codecs/g711/audio_encoder_g711.cc | 14 +- api/audio_codecs/g711/audio_encoder_g711.h | 8 +- api/audio_codecs/g722/BUILD.gn | 2 + api/audio_codecs/g722/audio_decoder_g722.cc | 20 +- api/audio_codecs/g722/audio_decoder_g722.h | 4 +- api/audio_codecs/g722/audio_encoder_g722.cc | 15 +- api/audio_codecs/g722/audio_encoder_g722.h | 4 +- .../g722/audio_encoder_g722_config.h | 3 +- api/audio_codecs/ilbc/BUILD.gn | 2 + api/audio_codecs/ilbc/audio_decoder_ilbc.cc | 12 +- api/audio_codecs/ilbc/audio_decoder_ilbc.h | 4 +- api/audio_codecs/ilbc/audio_encoder_ilbc.cc | 15 +- api/audio_codecs/ilbc/audio_encoder_ilbc.h | 4 +- api/audio_codecs/isac/BUILD.gn | 4 + .../isac/audio_decoder_isac_fix.cc | 12 +- .../isac/audio_decoder_isac_fix.h | 4 +- .../isac/audio_decoder_isac_float.cc | 12 +- .../isac/audio_decoder_isac_float.h | 4 +- .../isac/audio_encoder_isac_fix.cc | 12 +- .../isac/audio_encoder_isac_fix.h | 4 +- .../isac/audio_encoder_isac_float.cc | 12 +- .../isac/audio_encoder_isac_float.h | 4 +- api/audio_codecs/opus/BUILD.gn | 4 + .../opus/audio_decoder_multi_channel_opus.cc | 3 +- .../opus/audio_decoder_multi_channel_opus.h | 4 +- .../audio_decoder_multi_channel_opus_config.h | 3 +- api/audio_codecs/opus/audio_decoder_opus.cc | 13 +- api/audio_codecs/opus/audio_decoder_opus.h | 4 +- .../opus/audio_encoder_multi_channel_opus.cc | 3 +- .../opus/audio_encoder_multi_channel_opus.h | 4 +- api/audio_codecs/opus/audio_encoder_opus.cc | 7 +- api/audio_codecs/opus/audio_encoder_opus.h | 4 +- .../opus_audio_encoder_factory.cc | 6 +- api/audio_codecs/test/BUILD.gn | 1 + ...audio_decoder_factory_template_unittest.cc | 5 +- ...audio_encoder_factory_template_unittest.cc | 5 +- api/audio_options.cc | 22 +- api/audio_options.h | 15 +- api/candidate.cc | 21 +- api/candidate.h | 53 +- api/create_peerconnection_factory.cc | 4 + api/data_channel_interface.cc | 4 + api/data_channel_interface.h | 15 +- api/dtls_transport_interface.cc | 18 + api/dtls_transport_interface.h | 16 + api/ice_transport_factory.cc | 15 +- api/ice_transport_interface.h | 17 + api/jsep_ice_candidate.h | 7 +- api/jsep_session_description.h | 6 +- api/media_stream_interface.h | 14 +- api/media_types.cc | 2 +- .../run-server.sh => api/metronome/BUILD.gn | 22 +- api/metronome/metronome.h | 65 + api/metronome/test/BUILD.gn | 30 + api/metronome/test/fake_metronome.cc | 93 + api/metronome/test/fake_metronome.h | 77 + api/neteq/neteq.cc | 3 +- api/neteq/neteq.h | 12 - api/notifier.h | 12 +- api/packet_socket_factory.h | 23 +- api/peer_connection_interface.cc | 6 - api/peer_connection_interface.h | 188 +- api/ref_counted_base.h | 11 +- api/rtc_error.h | 12 +- api/rtc_event_log/rtc_event.h | 13 +- api/rtc_event_log/rtc_event_log.h | 2 +- api/rtp_packet_infos.h | 11 +- api/rtp_parameters.cc | 16 +- api/rtp_parameters.h | 8 +- api/rtp_parameters_unittest.cc | 32 + api/rtp_transceiver_interface.cc | 35 +- api/rtp_transceiver_interface.h | 13 +- api/scoped_refptr.h | 3 +- api/scoped_refptr_unittest.cc | 12 +- api/stats/rtcstats_objects.h | 26 +- api/stats_types.cc | 9 +- api/stats_types.h | 13 +- api/task_queue/task_queue_base.h | 73 +- api/task_queue/test/BUILD.gn | 18 + api/task_queue/test/mock_task_queue_base.h | 32 + api/test/DEPS | 5 + api/test/dummy_peer_connection.h | 4 +- api/test/frame_generator_interface.cc | 2 +- api/test/mock_async_dns_resolver.h | 6 +- api/test/mock_data_channel.h | 3 +- api/test/mock_media_stream_interface.h | 6 +- .../mock_peer_connection_factory_interface.h | 7 +- api/test/mock_peerconnectioninterface.h | 25 +- api/test/mock_rtp_transceiver.h | 6 +- api/test/mock_rtpreceiver.h | 12 +- api/test/mock_rtpsender.h | 16 +- api/test/mock_transformable_video_frame.h | 4 +- api/test/mock_video_encoder_factory.h | 4 - api/test/mock_video_track.h | 2 +- api/test/network_emulation_manager.h | 9 + api/test/peer_network_dependencies.h | 32 + .../peerconnection_quality_test_fixture.h | 49 +- .../video/function_video_encoder_factory.h | 2 +- api/test/video_quality_analyzer_interface.h | 3 + api/test/videocodec_test_fixture.h | 8 + api/test/videocodec_test_stats.cc | 4 + api/test/videocodec_test_stats.h | 5 + api/transport/BUILD.gn | 5 +- api/transport/network_types.cc | 4 - api/transport/network_types.h | 3 - api/transport/rtp/dependency_descriptor.cc | 2 +- api/transport/stun.cc | 11 +- api/transport/stun.h | 1 - api/transport/stun_unittest.cc | 43 - api/transport/webrtc_key_value_config.h | 22 +- api/video/BUILD.gn | 11 +- api/video/DEPS | 4 + api/video/encoded_frame.cc | 16 +- api/video/encoded_frame.h | 10 + api/video/encoded_image.h | 12 + api/video/i010_buffer.cc | 33 +- api/video/i010_buffer.h | 6 - api/video/i420_buffer.cc | 31 - api/video/i420_buffer.h | 6 - api/video/i444_buffer.cc | 211 + api/video/i444_buffer.h | 104 + api/video/rtp_video_frame_assembler.cc | 25 +- api/video/rtp_video_frame_assembler.h | 25 +- .../rtp_video_frame_assembler_unittests.cc | 224 +- api/video/test/BUILD.gn | 1 + api/video/test/i444_buffer_unittest.cc | 112 + api/video/video_frame_buffer.cc | 18 +- api/video/video_frame_buffer.h | 9 + api/video/video_sink_interface.h | 7 + api/video/video_source_interface.h | 4 + api/video/video_stream_encoder_interface.h | 4 +- api/video/video_stream_encoder_settings.h | 16 +- api/video/video_timing.cc | 9 + api/video/video_timing.h | 3 + api/video_codecs/BUILD.gn | 44 + .../builtin_video_encoder_factory.cc | 9 - api/video_codecs/h264_profile_level_id.cc | 6 +- api/video_codecs/h264_profile_level_id.h | 1 + api/video_codecs/test/BUILD.gn | 17 + ...oder_software_fallback_wrapper_unittest.cc | 4 +- .../video_encoder_factory_template_tests.cc | 160 + ...oder_software_fallback_wrapper_unittest.cc | 44 +- api/video_codecs/video_codec.cc | 29 +- api/video_codecs/video_codec.h | 6 + ...video_decoder_software_fallback_wrapper.cc | 7 +- api/video_codecs/video_encoder.cc | 5 +- api/video_codecs/video_encoder.h | 7 - api/video_codecs/video_encoder_config.cc | 9 +- api/video_codecs/video_encoder_factory.h | 20 - .../video_encoder_factory_template.h | 116 + ...oder_factory_template_libaom_av1_adapter.h | 40 + ...oder_factory_template_libvpx_vp8_adapter.h | 37 + ...oder_factory_template_libvpx_vp9_adapter.h | 37 + ...coder_factory_template_open_h264_adapter.h | 41 + ...video_encoder_software_fallback_wrapper.cc | 18 +- api/video_codecs/vp8_frame_config.cc | 4 +- api/video_track_source_constraints.h | 32 + api/voip/voip_engine_factory.cc | 2 +- api/webrtc_key_value_config.h | 41 + api/wrapping_async_dns_resolver.cc | 31 + api/wrapping_async_dns_resolver.h | 117 + audio/BUILD.gn | 9 +- audio/audio_receive_stream.cc | 4 +- audio/audio_receive_stream.h | 5 +- audio/audio_send_stream.cc | 108 +- audio/audio_send_stream.h | 10 +- audio/audio_send_stream_unittest.cc | 40 +- audio/audio_state.cc | 4 +- audio/audio_transport_impl.cc | 46 +- audio/audio_transport_impl.h | 42 +- audio/channel_receive.cc | 12 +- ...nnel_receive_frame_transformer_delegate.cc | 2 +- audio/channel_send.cc | 32 +- audio/channel_send.h | 6 +- ...channel_send_frame_transformer_delegate.cc | 2 +- audio/mock_voe_channel_proxy.h | 2 +- audio/test/OWNERS | 3 + audio/test/low_bandwidth_audio_test.py | 492 +- audio/test/pc_low_bandwidth_audio_test.cc | 14 +- audio/utility/audio_frame_operations.cc | 8 +- audio/voip/test/audio_egress_unittest.cc | 3 +- audio/voip/voip_core.h | 3 - call/BUILD.gn | 13 +- call/OWNERS | 2 + call/adaptation/BUILD.gn | 2 + call/adaptation/OWNERS | 2 +- .../adaptation/broadcast_resource_listener.cc | 3 +- .../resource_adaptation_processor.cc | 48 +- .../resource_adaptation_processor.h | 3 - .../resource_adaptation_processor_interface.h | 2 - .../resource_adaptation_processor_unittest.cc | 6 +- call/adaptation/test/fake_resource.cc | 3 +- call/adaptation/video_stream_adapter.cc | 19 +- call/adaptation/video_stream_adapter.h | 4 +- .../video_stream_adapter_unittest.cc | 16 +- call/audio_receive_stream.h | 6 +- call/bitrate_allocator.cc | 1 - call/bitrate_estimator_tests.cc | 13 +- call/call.cc | 238 +- call/call.h | 24 +- call/call_config.h | 3 + call/call_factory.cc | 118 +- call/call_perf_tests.cc | 4 +- call/degraded_call.cc | 88 +- call/degraded_call.h | 33 +- call/fake_network_pipe.cc | 1 - call/fake_network_pipe.h | 6 +- call/flexfec_receive_stream_impl.cc | 1 - call/flexfec_receive_stream_unittest.cc | 2 +- call/rampup_tests.cc | 42 +- call/rampup_tests.h | 4 +- call/receive_time_calculator.cc | 20 +- call/receive_time_calculator.h | 9 +- call/receive_time_calculator_unittest.cc | 4 +- call/rtp_bitrate_configurator.h | 7 +- call/rtp_config.cc | 2 +- call/rtp_demuxer.cc | 96 +- call/rtp_demuxer.h | 36 +- call/rtp_demuxer_unittest.cc | 98 +- call/rtp_payload_params.cc | 7 +- call/rtp_payload_params.h | 5 +- call/rtp_payload_params_unittest.cc | 36 +- call/rtp_transport_config.h | 1 - call/rtp_transport_controller_send.cc | 111 +- call/rtp_transport_controller_send.h | 28 +- call/rtp_transport_controller_send_factory.h | 3 +- .../rtp_transport_controller_send_interface.h | 2 +- call/rtp_video_sender.cc | 57 +- call/rtp_video_sender.h | 31 +- call/rtp_video_sender_interface.h | 2 +- call/rtp_video_sender_unittest.cc | 105 +- .../test/mock_rtp_transport_controller_send.h | 2 +- call/version.cc | 2 +- call/video_receive_stream.h | 14 +- call/video_send_stream.cc | 3 +- call/video_send_stream.h | 5 +- common_audio/audio_converter.h | 7 +- common_audio/fir_filter_factory.cc | 2 +- common_audio/resampler/push_sinc_resampler.h | 6 +- common_audio/resampler/sinc_resampler.h | 6 +- .../sinusoidal_linear_chirp_source.h | 7 +- common_audio/vad/vad.cc | 2 +- common_video/BUILD.gn | 7 +- common_video/h264/h264_bitstream_parser.cc | 142 +- common_video/h264/pps_parser.cc | 162 +- common_video/h264/pps_parser.h | 16 +- common_video/h264/pps_parser_unittest.cc | 4 +- common_video/h264/sps_parser.cc | 146 +- common_video/h264/sps_parser.h | 9 +- common_video/h264/sps_vui_rewriter.cc | 296 +- .../include/video_frame_buffer_pool.h | 2 + common_video/incoming_video_stream.cc | 3 +- common_video/libyuv/webrtc_libyuv.cc | 4 +- common_video/video_frame_buffer_pool.cc | 40 +- common_video/video_frame_unittest.cc | 406 +- docs/native-code/development/contributing.md | 99 + docs/native-code/development/index.md | 6 +- examples/BUILD.gn | 1 - examples/aarproject/.idea/misc.xml | 10 +- .../appspot/apprtc/AppRTCProximitySensor.java | 14 +- .../src/org/appspot/apprtc/CallActivity.java | 11 +- .../src/org/appspot/apprtc/CpuMonitor.java | 4 +- .../appspot/apprtc/PeerConnectionClient.java | 17 +- .../appspot/apprtc/RoomParametersFetcher.java | 1 + .../apprtc/util/AsyncHttpURLConnection.java | 1 + .../jni/android_call_client.cc | 12 +- examples/androidtests/gradle_project_test.py | 80 - .../apprtc/test/PeerConnectionClientTest.java | 20 - .../androidvoip/jni/android_voip_client.cc | 45 +- .../androidvoip/jni/android_voip_client.h | 4 +- examples/objc/AppRTCMobile/ARDAppClient.m | 12 + .../objcnativeapi/objc/objc_call_client.mm | 12 +- examples/peerconnection/client/conductor.cc | 86 +- examples/peerconnection/client/conductor.h | 4 +- examples/peerconnection/client/linux/main.cc | 3 +- .../peerconnection/client/linux/main_wnd.cc | 8 +- examples/peerconnection/client/main.cc | 12 +- .../client/peer_connection_client.cc | 22 +- examples/stunprober/main.cc | 2 +- .../unityplugin/simple_peer_connection.cc | 42 +- examples/unityplugin/simple_peer_connection.h | 6 +- examples/unityplugin/unity_plugin_apis.cc | 2 +- examples/unityplugin/unity_plugin_apis.h | 8 +- g3doc/abseil-in-webrtc.md | 1 + g3doc/become_a_committer.md | 24 +- g3doc/implementation_basics.md | 8 + g3doc/style-guide.md | 37 +- infra/config/OWNERS | 6 + infra/config/PRESUBMIT.py | 20 + infra/config/README.md | 52 + infra/config/codereview.settings | 6 + infra/config/commit-queue.cfg | 365 + infra/config/config.star | 856 +++ infra/config/console-header.textpb | 59 + infra/config/cr-buildbucket.cfg | 5864 +++++++++++++++++ infra/config/luci-logdog.cfg | 9 + infra/config/luci-milo.cfg | 606 ++ infra/config/luci-notify.cfg | 2179 ++++++ .../email-templates/build_failure.template | 24 + .../luci-notify/email-templates/cron.template | 33 + .../email-templates/infra_failure.template | 24 + infra/config/luci-scheduler.cfg | 715 ++ infra/config/project.cfg | 15 + infra/config/realms.cfg | 179 + logging/BUILD.gn | 133 +- logging/rtc_event_log/encoder/bit_writer.cc | 49 + logging/rtc_event_log/encoder/bit_writer.h | 61 + .../rtc_event_log/encoder/delta_encoding.cc | 64 +- .../encoder/delta_encoding_unittest.cc | 2 +- .../encoder/rtc_event_log_encoder_legacy.cc | 46 +- .../rtc_event_log_encoder_new_format.cc | 44 +- .../encoder/rtc_event_log_encoder_unittest.cc | 7 + .../encoder/rtc_event_log_encoder_v3.cc | 164 + .../encoder/rtc_event_log_encoder_v3.h | 46 + .../fixed_length_encoding_parameters_v3.cc | 137 + .../fixed_length_encoding_parameters_v3.h | 96 + .../rtc_event_log/events/logged_rtp_rtcp.h | 259 + .../events/rtc_event_alr_state.cc | 10 + .../events/rtc_event_alr_state.h | 46 +- .../rtc_event_audio_network_adaptation.h | 44 +- .../events/rtc_event_audio_playout.cc | 5 + .../events/rtc_event_audio_playout.h | 53 +- .../rtc_event_audio_receive_stream_config.h | 42 +- .../rtc_event_audio_send_stream_config.h | 41 +- .../events/rtc_event_begin_log.cc | 73 + .../events/rtc_event_begin_log.h | 74 + .../rtc_event_bwe_update_delay_based.cc | 6 + .../events/rtc_event_bwe_update_delay_based.h | 102 +- .../events/rtc_event_bwe_update_loss_based.h | 58 +- .../events/rtc_event_definition.h | 152 + .../events/rtc_event_dtls_transport_state.h | 34 +- .../events/rtc_event_dtls_writable_state.h | 40 +- .../rtc_event_log/events/rtc_event_end_log.cc | 58 + .../rtc_event_log/events/rtc_event_end_log.h | 64 + .../events/rtc_event_field_encoding.cc | 299 + .../events/rtc_event_field_encoding.h | 179 + .../events/rtc_event_field_encoding_parser.cc | 398 ++ .../events/rtc_event_field_encoding_parser.h | 285 + .../rtc_event_field_encoding_unittest.cc | 885 +++ .../events/rtc_event_field_extraction.cc | 60 + .../events/rtc_event_field_extraction.h | 84 + .../rtc_event_field_extraction_unittest.cc | 97 + .../events/rtc_event_frame_decoded.h | 45 +- .../events/rtc_event_generic_ack_received.h | 57 +- .../rtc_event_generic_packet_received.h | 52 +- .../events/rtc_event_generic_packet_sent.h | 69 +- .../events/rtc_event_ice_candidate_pair.h | 58 +- .../rtc_event_ice_candidate_pair_config.h | 50 +- .../events/rtc_event_probe_cluster_created.h | 64 +- .../events/rtc_event_probe_result_failure.h | 48 +- .../events/rtc_event_probe_result_success.h | 48 +- .../events/rtc_event_remote_estimate.h | 39 +- .../events/rtc_event_route_change.h | 44 +- .../events/rtc_event_rtcp_packet_incoming.h | 18 + .../events/rtc_event_rtcp_packet_outgoing.h | 18 + .../events/rtc_event_rtp_packet_incoming.h | 19 + .../events/rtc_event_rtp_packet_outgoing.h | 19 + .../rtc_event_video_receive_stream_config.h | 42 +- .../rtc_event_video_send_stream_config.h | 41 +- logging/rtc_event_log/logged_events.cc | 57 - logging/rtc_event_log/logged_events.h | 333 +- logging/rtc_event_log/rtc_event_log_impl.cc | 14 +- logging/rtc_event_log/rtc_event_log_parser.cc | 310 +- logging/rtc_event_log/rtc_event_log_parser.h | 133 +- .../rtc_event_log/rtc_event_log_unittest.cc | 67 +- .../rtc_event_log_unittest_helper.cc | 4 +- media/BUILD.gn | 21 +- media/base/adapted_video_track_source.cc | 5 + media/base/adapted_video_track_source.h | 2 + media/base/codec.cc | 20 +- media/base/codec.h | 13 +- media/base/fake_frame_source.cc | 3 +- media/base/fake_media_engine.h | 2 +- media/base/fake_rtp.cc | 2 +- media/base/fake_rtp.h | 2 +- media/base/media_channel.cc | 15 +- media/base/media_channel.h | 41 +- media/base/media_constants.cc | 5 +- media/base/media_constants.h | 1 + media/base/media_engine.h | 11 +- media/base/rtp_utils.cc | 10 +- media/base/stream_params.cc | 5 +- media/base/stream_params.h | 32 +- media/base/test_utils.h | 6 +- media/base/video_adapter.h | 6 +- media/base/video_broadcaster.cc | 19 + media/base/video_broadcaster.h | 13 + media/base/video_broadcaster_unittest.cc | 98 + .../encoder_simulcast_proxy_unittest.cc | 30 - media/engine/fake_webrtc_call.cc | 12 +- media/engine/fake_webrtc_call.h | 27 +- media/engine/fake_webrtc_video_engine.cc | 15 - media/engine/fake_webrtc_video_engine.h | 3 - media/engine/internal_decoder_factory.cc | 59 +- media/engine/internal_decoder_factory.h | 2 + .../internal_decoder_factory_unittest.cc | 94 +- media/engine/internal_encoder_factory.cc | 38 +- media/engine/internal_encoder_factory.h | 6 +- .../internal_encoder_factory_unittest.cc | 139 + media/engine/payload_type_mapper.cc | 7 +- media/engine/payload_type_mapper_unittest.cc | 3 +- media/engine/simulcast.cc | 2 +- media/engine/simulcast_encoder_adapter.cc | 54 +- .../simulcast_encoder_adapter_unittest.cc | 92 +- media/engine/webrtc_media_engine.cc | 46 +- media/engine/webrtc_media_engine.h | 7 +- media/engine/webrtc_media_engine_unittest.cc | 114 +- media/engine/webrtc_video_engine.cc | 297 +- media/engine/webrtc_video_engine.h | 16 +- media/engine/webrtc_video_engine_unittest.cc | 352 +- media/engine/webrtc_voice_engine.cc | 135 +- media/engine/webrtc_voice_engine.h | 10 +- media/engine/webrtc_voice_engine_unittest.cc | 236 +- media/sctp/dcsctp_transport.cc | 35 +- media/sctp/dcsctp_transport.h | 4 +- media/sctp/sctp_transport_factory.cc | 14 +- media/sctp/sctp_transport_factory.h | 7 +- media/sctp/usrsctp_transport.cc | 8 +- media/sctp/usrsctp_transport.h | 8 +- .../usrsctp_transport_reliability_unittest.cc | 24 +- media/sctp/usrsctp_transport_unittest.cc | 2 +- modules/audio_coding/BUILD.gn | 21 +- modules/audio_coding/acm2/acm_receive_test.h | 6 +- modules/audio_coding/acm2/acm_receiver.cc | 18 +- .../acm2/acm_receiver_unittest.cc | 2 +- modules/audio_coding/acm2/acm_resampler.cc | 2 +- modules/audio_coding/acm2/acm_send_test.h | 6 +- .../audio_coding/acm2/audio_coding_module.cc | 14 +- .../acm2/audio_coding_module_unittest.cc | 482 +- modules/audio_coding/acm2/call_statistics.cc | 2 +- .../audio_network_adaptor_impl.h | 6 +- .../bitrate_controller.h | 5 +- .../channel_controller.h | 5 +- .../audio_network_adaptor/config.proto | 1 + .../controller_manager.cc | 6 +- .../controller_manager.h | 6 +- .../debug_dump_writer.cc | 2 +- .../audio_network_adaptor/debug_dump_writer.h | 1 - .../audio_network_adaptor/dtx_controller.h | 5 +- .../audio_network_adaptor/event_log_writer.h | 6 +- .../fec_controller_plr_based.cc | 2 +- .../fec_controller_plr_based.h | 6 +- .../frame_length_controller.h | 6 +- .../builtin_audio_decoder_factory_unittest.cc | 32 +- .../builtin_audio_encoder_factory_unittest.cc | 31 + .../codecs/cng/audio_encoder_cng_unittest.cc | 6 +- .../codecs/g711/audio_decoder_pcm.cc | 16 +- .../codecs/g711/audio_decoder_pcm.h | 11 +- .../codecs/g711/audio_encoder_pcm.h | 9 +- .../codecs/g722/audio_decoder_g722.cc | 6 + .../codecs/g722/audio_decoder_g722.h | 13 +- .../codecs/g722/audio_encoder_g722.h | 5 +- .../codecs/ilbc/audio_decoder_ilbc.h | 6 +- .../codecs/ilbc/audio_encoder_ilbc.h | 5 +- .../codecs/isac/audio_decoder_isac_t.h | 6 +- .../codecs/isac/audio_encoder_isac_t.h | 6 +- .../codecs/isac/fix/include/isacfix.h | 2 +- .../codecs/isac/fix/source/arith_routins.h | 10 +- .../isac/fix/source/bandwidth_estimator.h | 28 +- .../codecs/isac/fix/source/entropy_coding.h | 80 +- .../isac/fix/source/filterbank_internal.h | 8 +- .../codecs/isac/fix/source/isacfix.c | 2 +- .../codecs/isac/fix/test/kenny.cc | 883 --- .../codecs/isac/isac_webrtc_api_test.cc | 4 +- .../codecs/isac/main/include/isac.h | 2 +- .../codecs/isac/main/source/arith_routines.h | 18 +- .../isac/main/source/bandwidth_estimator.h | 24 +- .../codecs/isac/main/source/isac.c | 2 +- .../legacy_encoded_audio_frame_unittest.cc | 4 +- .../audio_decoder_multi_channel_opus_impl.cc | 4 + .../audio_decoder_multi_channel_opus_impl.h | 7 +- ...dio_decoder_multi_channel_opus_unittest.cc | 16 +- .../codecs/opus/audio_decoder_opus.h | 5 +- .../audio_encoder_multi_channel_opus_impl.cc | 4 + .../audio_encoder_multi_channel_opus_impl.h | 7 +- ...dio_encoder_multi_channel_opus_unittest.cc | 28 +- .../codecs/opus/audio_encoder_opus.cc | 10 +- .../codecs/opus/audio_encoder_opus.h | 5 +- .../codecs/pcm16b/audio_decoder_pcm16b.cc | 7 +- .../codecs/pcm16b/audio_decoder_pcm16b.h | 6 +- .../codecs/pcm16b/audio_encoder_pcm16b.h | 7 +- .../codecs/red/audio_encoder_copy_red.cc | 32 +- .../codecs/red/audio_encoder_copy_red.h | 10 +- .../red/audio_encoder_copy_red_unittest.cc | 113 +- .../include/audio_coding_module.h | 6 +- modules/audio_coding/neteq/accelerate.h | 7 +- .../neteq/audio_decoder_unittest.cc | 4 +- .../audio_coding/neteq/audio_multi_vector.h | 7 +- modules/audio_coding/neteq/audio_vector.h | 6 +- modules/audio_coding/neteq/background_noise.h | 6 +- .../audio_coding/neteq/buffer_level_filter.h | 8 +- modules/audio_coding/neteq/comfort_noise.h | 6 +- modules/audio_coding/neteq/decision_logic.h | 6 +- modules/audio_coding/neteq/decoder_database.h | 6 +- modules/audio_coding/neteq/delay_manager.cc | 51 +- modules/audio_coding/neteq/delay_manager.h | 21 +- .../neteq/delay_manager_unittest.cc | 84 +- modules/audio_coding/neteq/dsp_helper.cc | 2 +- modules/audio_coding/neteq/dsp_helper.h | 6 +- modules/audio_coding/neteq/dtmf_buffer.h | 7 +- .../audio_coding/neteq/dtmf_tone_generator.cc | 2 +- .../audio_coding/neteq/dtmf_tone_generator.h | 7 +- modules/audio_coding/neteq/expand.h | 6 +- .../audio_coding/neteq/expand_uma_logger.h | 6 +- modules/audio_coding/neteq/expand_unittest.cc | 2 - modules/audio_coding/neteq/merge.cc | 2 +- modules/audio_coding/neteq/merge.h | 6 +- modules/audio_coding/neteq/nack_tracker.cc | 136 +- modules/audio_coding/neteq/nack_tracker.h | 98 +- .../neteq/nack_tracker_unittest.cc | 359 +- modules/audio_coding/neteq/neteq_impl.cc | 106 +- modules/audio_coding/neteq/neteq_impl.h | 23 +- .../audio_coding/neteq/neteq_impl_unittest.cc | 198 +- .../neteq/neteq_network_stats_unittest.cc | 2 +- modules/audio_coding/neteq/neteq_unittest.cc | 305 +- modules/audio_coding/neteq/normal.cc | 2 +- modules/audio_coding/neteq/normal.h | 6 +- modules/audio_coding/neteq/packet_buffer.h | 5 +- modules/audio_coding/neteq/post_decode_vad.h | 6 +- .../audio_coding/neteq/preemptive_expand.h | 6 +- modules/audio_coding/neteq/random_vector.h | 7 +- .../audio_coding/neteq/red_payload_splitter.h | 7 +- .../neteq/relative_arrival_delay_tracker.cc | 2 +- .../neteq/statistics_calculator.h | 6 +- modules/audio_coding/neteq/sync_buffer.cc | 2 +- modules/audio_coding/neteq/sync_buffer.h | 6 +- .../neteq/test/neteq_decoding_test.cc | 16 +- modules/audio_coding/neteq/time_stretch.h | 6 +- modules/audio_coding/neteq/timestamp_scaler.h | 6 +- .../audio_coding/neteq/tools/audio_checksum.h | 6 +- modules/audio_coding/neteq/tools/audio_loop.h | 8 +- modules/audio_coding/neteq/tools/audio_sink.h | 19 +- .../neteq/tools/constant_pcm_packet_source.h | 6 +- .../neteq/tools/input_audio_file.h | 8 +- .../neteq/tools/neteq_stats_plotter.cc | 3 +- .../neteq/tools/neteq_stats_plotter.h | 2 +- .../audio_coding/neteq/tools/neteq_test.cc | 3 +- modules/audio_coding/neteq/tools/neteq_test.h | 2 +- .../neteq/tools/output_audio_file.h | 6 +- .../neteq/tools/output_wav_file.h | 6 +- modules/audio_coding/neteq/tools/packet.h | 6 +- .../audio_coding/neteq/tools/packet_source.h | 7 +- .../neteq/tools/resample_input_audio_file.h | 5 +- .../neteq/tools/rtc_event_log_source.h | 6 +- .../audio_coding/neteq/tools/rtp_encode.cc | 6 +- .../neteq/tools/rtp_file_source.h | 6 +- .../audio_coding/neteq/tools/rtp_generator.h | 12 +- .../audio_coding/neteq/underrun_optimizer.cc | 2 +- modules/audio_coding/test/EncodeDecodeTest.h | 8 +- modules/audio_coding/test/RTPFile.h | 24 +- modules/audio_coding/test/TestRedFec.cc | 3 +- modules/audio_coding/test/TestRedFec.h | 2 + modules/audio_coding/test/TestStereo.cc | 2 +- modules/audio_coding/test/TestStereo.h | 8 +- .../audio_coding/test/TwoWayCommunication.h | 4 +- modules/audio_coding/test/iSACTest.cc | 5 - modules/audio_device/android/aaudio_player.cc | 32 +- .../audio_device/android/aaudio_recorder.cc | 40 +- .../audio_device/android/aaudio_wrapper.cc | 52 +- .../android/audio_device_template.h | 88 +- .../android/audio_device_unittest.cc | 4 +- modules/audio_device/android/audio_manager.cc | 27 +- .../audio_device/android/audio_record_jni.cc | 38 +- .../audio_device/android/audio_track_jni.cc | 30 +- .../voiceengine/WebRtcAudioEffects.java | 11 - .../voiceengine/WebRtcAudioManager.java | 7 +- .../webrtc/voiceengine/WebRtcAudioTrack.java | 18 +- .../webrtc/voiceengine/WebRtcAudioUtils.java | 2 + modules/audio_device/audio_device_buffer.cc | 113 +- modules/audio_device/audio_device_buffer.h | 13 + .../audio_device_data_observer.cc | 40 +- modules/audio_device/audio_device_impl.cc | 252 +- modules/audio_device/audio_device_unittest.cc | 2 +- modules/audio_device/fine_audio_buffer.cc | 16 +- .../include/audio_device_data_observer.h | 20 +- .../include/audio_device_defines.h | 45 +- .../include/audio_device_factory.cc | 4 +- .../include/mock_audio_transport.h | 39 +- .../linux/audio_device_alsa_linux.h | 8 +- .../linux/latebindingsymboltable_linux.h | 6 +- modules/audio_device/mac/audio_device_mac.cc | 45 +- modules/audio_device/mac/audio_device_mac.h | 19 +- .../mac/audio_mixer_manager_mac.h | 2 +- .../audio_device/win/audio_device_core_win.cc | 90 +- .../win/audio_device_module_win.cc | 88 +- .../audio_device/win/core_audio_base_win.cc | 123 +- .../audio_device/win/core_audio_input_win.cc | 54 +- .../audio_device/win/core_audio_output_win.cc | 56 +- .../win/core_audio_utility_win.cc | 227 +- .../audio_device/win/core_audio_utility_win.h | 18 +- modules/audio_mixer/audio_mixer_impl.h | 6 +- modules/audio_processing/BUILD.gn | 95 +- .../aec3/adaptive_fir_filter.cc | 32 +- .../aec3/adaptive_fir_filter_avx2.cc | 16 +- modules/audio_processing/aec3/aec3_common.h | 4 +- modules/audio_processing/aec3/aec3_fft.cc | 8 +- modules/audio_processing/aec3/aec3_fft.h | 6 +- .../audio_processing/aec3/alignment_mixer.h | 2 +- .../aec3/block_delay_buffer.cc | 17 +- .../aec3/block_processor_metrics.h | 7 +- .../aec3/comfort_noise_generator.h | 1 - modules/audio_processing/aec3/decimator.h | 6 +- .../audio_processing/aec3/echo_audibility.h | 1 - .../audio_processing/aec3/echo_canceller3.cc | 11 +- .../aec3/echo_path_delay_estimator.h | 6 +- .../aec3/echo_remover_metrics.cc | 2 +- .../aec3/echo_remover_metrics.h | 6 +- modules/audio_processing/aec3/erl_estimator.h | 5 +- .../audio_processing/aec3/filter_analyzer.cc | 21 +- .../audio_processing/aec3/filter_analyzer.h | 1 - .../aec3/fullband_erle_estimator.h | 2 +- .../audio_processing/aec3/matched_filter.cc | 65 +- .../audio_processing/aec3/matched_filter.h | 3 + .../aec3/matched_filter_avx2.cc | 14 +- .../aec3/matched_filter_unittest.cc | 22 + .../audio_processing/aec3/render_buffer.cc | 17 +- .../aec3/render_delay_controller_metrics.h | 7 +- .../aec3/render_signal_analyzer.h | 6 +- .../aec3/reverb_decay_estimator.cc | 2 +- .../aec3/suppression_filter.cc | 31 +- .../aec3/suppression_filter.h | 6 +- .../audio_processing/aec3/suppression_gain.cc | 9 +- .../audio_processing/aec3/suppression_gain.h | 7 +- .../aec_dump/aec_dump_impl.cc | 4 +- .../aec_dump/aec_dump_integration_test.cc | 9 +- .../aec_dump/capture_stream_info.cc | 4 +- .../aec_dump/write_to_file_task.cc | 4 +- modules/audio_processing/aecm/aecm_core.h | 14 +- modules/audio_processing/agc/BUILD.gn | 21 + modules/audio_processing/agc/agc.cc | 16 +- modules/audio_processing/agc/agc.h | 3 +- .../agc/agc_manager_direct.cc | 89 +- .../audio_processing/agc/agc_manager_direct.h | 10 +- .../agc/agc_manager_direct_unittest.cc | 281 +- .../agc/analog_gain_stats_reporter.cc | 130 + .../agc/analog_gain_stats_reporter.h | 67 + .../analog_gain_stats_reporter_unittest.cc | 161 + .../agc/clipping_predictor.cc | 2 +- modules/audio_processing/agc/mock_agc.h | 6 +- modules/audio_processing/agc2/BUILD.gn | 51 +- modules/audio_processing/agc2/adaptive_agc.cc | 114 - .../agc2/adaptive_digital_gain_applier.cc | 112 +- .../agc2/adaptive_digital_gain_applier.h | 22 +- .../adaptive_digital_gain_applier_unittest.cc | 243 +- .../agc2/adaptive_digital_gain_controller.cc | 108 + ...c.h => adaptive_digital_gain_controller.h} | 38 +- .../agc2/adaptive_mode_level_estimator.cc | 57 +- .../agc2/adaptive_mode_level_estimator.h | 19 +- .../adaptive_mode_level_estimator_unittest.cc | 157 +- modules/audio_processing/agc2/agc2_common.h | 22 +- .../agc2/agc2_testing_common.h | 2 +- .../audio_processing/agc2/biquad_filter.cc | 37 +- modules/audio_processing/agc2/biquad_filter.h | 52 +- .../agc2/biquad_filter_unittest.cc | 87 +- .../agc2/fixed_digital_level_estimator.cc | 17 +- .../agc2/fixed_digital_level_estimator.h | 7 +- modules/audio_processing/agc2/gain_applier.cc | 11 +- modules/audio_processing/agc2/gain_applier.h | 2 +- .../agc2/interpolated_gain_curve.cc | 2 +- .../agc2/interpolated_gain_curve.h | 6 +- modules/audio_processing/agc2/limiter.cc | 40 +- modules/audio_processing/agc2/limiter.h | 5 +- .../agc2/noise_level_estimator.cc | 2 +- .../audio_processing/agc2/rnn_vad/BUILD.gn | 2 +- .../agc2/rnn_vad/features_extraction.cc | 9 +- .../rnn_vad/spectral_features_unittest.cc | 5 +- .../agc2/saturation_protector.cc | 11 +- .../agc2/saturation_protector.h | 1 - .../agc2/saturation_protector_unittest.cc | 45 +- .../audio_processing/agc2/vad_with_level.cc | 107 - .../audio_processing/agc2/vad_with_level.h | 64 - .../agc2/vad_with_level_unittest.cc | 139 - modules/audio_processing/agc2/vad_wrapper.cc | 106 + modules/audio_processing/agc2/vad_wrapper.h | 78 + .../agc2/vad_wrapper_unittest.cc | 181 + .../audio_frame_view_unittest.cc | 2 +- .../audio_processing_builder_impl.cc | 17 +- .../audio_processing/audio_processing_impl.cc | 283 +- .../audio_processing/audio_processing_impl.h | 62 +- .../audio_processing_impl_locking_unittest.cc | 95 +- .../audio_processing_impl_unittest.cc | 88 +- .../audio_processing_performance_unittest.cc | 19 +- .../audio_processing_unittest.cc | 480 +- modules/audio_processing/common.h | 38 - modules/audio_processing/config_unittest.cc | 77 - .../echo_control_mobile_bit_exact_unittest.cc | 4 +- .../echo_control_mobile_impl.cc | 7 +- modules/audio_processing/gain_control_impl.cc | 2 +- .../audio_processing/gain_control_unittest.cc | 4 +- modules/audio_processing/gain_controller2.cc | 156 +- modules/audio_processing/gain_controller2.h | 27 +- .../gain_controller2_unittest.cc | 354 +- modules/audio_processing/high_pass_filter.cc | 4 +- .../high_pass_filter_unittest.cc | 10 +- .../include/audio_frame_proxies.cc | 12 +- .../include/audio_frame_view.h | 21 +- .../include/audio_processing.cc | 188 +- .../include/audio_processing.h | 163 +- .../include/audio_processing_statistics.h | 10 +- modules/audio_processing/include/config.h | 131 - .../include/mock_audio_processing.h | 30 +- modules/audio_processing/level_estimator.cc | 29 - modules/audio_processing/level_estimator.h | 47 - .../level_estimator_unittest.cc | 89 - .../audio_processing/ns/suppression_params.cc | 2 +- .../residual_echo_detector.cc | 8 - .../test/aec_dump_based_simulator.cc | 15 +- .../audio_processing_builder_for_testing.cc | 19 +- .../audio_processing_builder_for_testing.h | 40 +- .../test/audio_processing_simulator.cc | 23 +- .../test/audio_processing_simulator.h | 9 +- .../test/audioproc_float_impl.cc | 65 +- .../test/bitexactness_tools.cc | 4 +- .../conversational_speech/multiend_call.h | 6 +- .../test/conversational_speech/simulator.cc | 1 - .../test/conversational_speech/simulator.h | 1 - .../test/debug_dump_replayer.cc | 3 +- .../audio_processing/test/debug_dump_test.cc | 51 +- .../test/fake_recording_device.cc | 2 +- .../test/fake_recording_device.h | 4 +- .../test/simulator_buffers.cc | 2 +- modules/audio_processing/test/test_utils.cc | 11 - modules/audio_processing/test/test_utils.h | 18 +- .../test/wav_based_simulator.cc | 2 +- .../three_band_filter_bank.cc | 6 +- .../transient/transient_suppression_test.cc | 3 +- .../transient/transient_suppressor_impl.cc | 2 +- .../utility/cascaded_biquad_filter.cc | 29 +- .../vad/voice_activity_detector.cc | 1 + .../vad/voice_activity_detector.h | 2 + modules/audio_processing/voice_detection.cc | 92 - modules/audio_processing/voice_detection.h | 59 - .../voice_detection_unittest.cc | 104 - modules/congestion_controller/OWNERS | 3 +- .../congestion_controller/goog_cc/BUILD.gn | 3 + .../goog_cc/delay_based_bwe.cc | 99 +- .../goog_cc/delay_based_bwe.h | 2 +- .../goog_cc/delay_based_bwe_unittest.cc | 55 +- .../delay_based_bwe_unittest_helper.cc | 7 +- .../goog_cc/delay_based_bwe_unittest_helper.h | 14 +- .../delay_increase_detector_interface.h | 8 +- .../goog_cc/goog_cc_network_control.cc | 25 +- .../goog_cc/goog_cc_network_control.h | 1 + .../goog_cc_network_control_unittest.cc | 441 +- .../loss_based_bandwidth_estimation.cc | 8 +- .../goog_cc/loss_based_bwe_v2.cc | 304 +- .../goog_cc/loss_based_bwe_v2.h | 46 +- .../goog_cc/loss_based_bwe_v2_test.cc | 291 +- .../goog_cc/probe_controller.cc | 2 +- .../goog_cc/probe_controller.h | 6 +- .../goog_cc/send_side_bandwidth_estimation.cc | 26 +- .../goog_cc/send_side_bandwidth_estimation.h | 11 +- ...send_side_bandwidth_estimation_unittest.cc | 10 +- .../goog_cc/test/goog_cc_printer.h | 2 +- .../goog_cc/trendline_estimator.cc | 2 +- .../goog_cc/trendline_estimator.h | 6 +- .../pcc/monitor_interval.cc | 3 - ...ive_side_congestion_controller_unittest.cc | 4 +- modules/congestion_controller/rtp/BUILD.gn | 1 + .../rtp/control_handler.h | 5 +- .../rtp/transport_feedback_adapter.cc | 1 - .../rtp/transport_feedback_demuxer.cc | 39 +- .../rtp/transport_feedback_demuxer.h | 25 +- modules/desktop_capture/BUILD.gn | 113 +- modules/desktop_capture/OWNERS | 4 +- ...blank_detector_desktop_capturer_wrapper.cc | 14 +- .../blank_detector_desktop_capturer_wrapper.h | 7 +- .../desktop_capture/cropped_desktop_frame.cc | 12 +- .../cropped_desktop_frame_unittest.cc | 11 +- .../cropping_window_capturer.cc | 13 +- .../cropping_window_capturer_win.cc | 7 +- .../desktop_and_cursor_composer.cc | 6 +- .../desktop_and_cursor_composer.h | 6 +- .../desktop_capture_options.cc | 17 +- .../desktop_capture/desktop_capture_options.h | 43 +- .../desktop_capture/desktop_capture_types.h | 7 +- modules/desktop_capture/desktop_capturer.h | 4 + modules/desktop_capture/desktop_frame.h | 17 +- .../desktop_capture/desktop_frame_rotation.cc | 8 +- modules/desktop_capture/desktop_frame_win.cc | 4 + modules/desktop_capture/desktop_frame_win.h | 6 +- .../full_screen_application_handler.h | 8 +- .../full_screen_window_detector.h | 6 +- .../linux/base_capturer_pipewire.cc | 1065 --- .../linux/base_capturer_pipewire.h | 182 - .../linux/wayland/base_capturer_pipewire.cc | 100 + .../linux/wayland/base_capturer_pipewire.h | 52 + .../desktop_capture/linux/wayland/drm.sigs | 11 + .../linux/wayland/egl_dmabuf.cc | 703 ++ .../linux/wayland/egl_dmabuf.h | 68 + .../wayland/mouse_cursor_monitor_pipewire.cc | 59 + .../wayland/mouse_cursor_monitor_pipewire.h | 44 + .../pipewire.sigs} | 6 +- .../pipewire_stub_header.fragment | 1 + .../linux/wayland/scoped_glib.cc | 57 + .../linux/wayland/scoped_glib.h | 65 + .../linux/wayland/screencast_portal.cc | 369 ++ .../linux/wayland/screencast_portal.h | 156 + .../linux/wayland/shared_screencast_stream.cc | 876 +++ .../linux/wayland/shared_screencast_stream.h | 71 + .../linux/wayland/xdg_desktop_portal_utils.cc | 169 + .../linux/wayland/xdg_desktop_portal_utils.h | 234 + .../{ => x11}/mouse_cursor_monitor_x11.cc | 6 +- .../{ => x11}/mouse_cursor_monitor_x11.h | 8 +- .../linux/{ => x11}/screen_capturer_x11.cc | 43 +- .../linux/{ => x11}/screen_capturer_x11.h | 18 +- .../linux/{ => x11}/shared_x_display.cc | 6 +- .../linux/{ => x11}/shared_x_display.h | 12 +- .../linux/{ => x11}/window_capturer_x11.cc | 8 +- .../linux/{ => x11}/window_capturer_x11.h | 20 +- .../linux/{ => x11}/window_finder_x11.cc | 4 +- .../linux/{ => x11}/window_finder_x11.h | 6 +- .../linux/{ => x11}/window_list_utils.cc | 7 +- .../linux/{ => x11}/window_list_utils.h | 8 +- .../linux/{ => x11}/x_atom_cache.cc | 2 +- .../linux/{ => x11}/x_atom_cache.h | 6 +- .../linux/{ => x11}/x_error_trap.cc | 2 +- .../linux/{ => x11}/x_error_trap.h | 13 +- .../linux/{ => x11}/x_server_pixel_buffer.cc | 8 +- .../linux/{ => x11}/x_server_pixel_buffer.h | 12 +- .../linux/{ => x11}/x_window_property.cc | 2 +- .../linux/{ => x11}/x_window_property.h | 18 +- .../mac/desktop_configuration_monitor.h | 7 +- .../mac/desktop_frame_cgimage.h | 5 +- .../mac/desktop_frame_iosurface.h | 5 +- .../mac/desktop_frame_provider.h | 5 +- .../desktop_capture/mac/screen_capturer_mac.h | 5 +- .../mac/screen_capturer_mac.mm | 9 +- .../mock_desktop_capturer_callback.h | 7 +- modules/desktop_capture/mouse_cursor.h | 6 +- .../mouse_cursor_monitor_linux.cc | 13 +- .../mouse_cursor_monitor_mac.mm | 5 +- .../screen_capture_frame_queue.h | 6 +- .../desktop_capture/screen_capturer_helper.h | 6 +- .../screen_capturer_integration_test.cc | 9 +- .../desktop_capture/screen_capturer_linux.cc | 6 +- .../screen_capturer_unittest.cc | 11 +- .../desktop_capture/screen_drawer_linux.cc | 6 +- .../screen_drawer_lock_posix.cc | 2 +- .../desktop_capture/shared_desktop_frame.cc | 4 +- .../desktop_capture/shared_desktop_frame.h | 6 +- modules/desktop_capture/shared_memory.h | 13 +- modules/desktop_capture/win/desktop.h | 6 +- .../win/dxgi_output_duplicator.cc | 2 +- .../full_screen_win_application_handler.cc | 17 +- .../desktop_capture/win/scoped_gdi_object.h | 7 +- .../win/scoped_thread_desktop.h | 6 +- .../win/screen_capture_utils.h | 11 +- .../win/screen_capturer_win_directx.h | 5 +- .../win/screen_capturer_win_gdi.cc | 2 +- .../win/screen_capturer_win_gdi.h | 6 +- .../win/screen_capturer_win_magnifier.h | 7 +- .../win/test_support/test_window.h | 14 +- .../win/wgc_capture_session.cc | 95 +- .../desktop_capture/win/wgc_capturer_win.cc | 24 +- .../desktop_capture/win/wgc_capturer_win.h | 11 +- .../win/wgc_capturer_win_unittest.cc | 2 +- .../win/window_capture_utils.cc | 7 +- .../win/window_capture_utils.h | 6 +- .../win/window_capture_utils_unittest.cc | 2 +- .../win/window_capturer_win_gdi.cc | 4 +- .../desktop_capture/window_capturer_linux.cc | 6 +- .../desktop_capture/window_capturer_mac.mm | 6 +- .../desktop_capture/window_capturer_null.cc | 6 +- .../desktop_capture/window_capturer_win.cc | 27 +- .../desktop_capture/window_finder_unittest.cc | 4 +- modules/pacing/BUILD.gn | 8 +- modules/pacing/paced_sender.cc | 39 +- modules/pacing/paced_sender.h | 32 +- modules/pacing/paced_sender_unittest.cc | 6 +- modules/pacing/pacing_controller.cc | 88 +- modules/pacing/pacing_controller.h | 34 +- modules/pacing/pacing_controller_unittest.cc | 229 +- modules/pacing/packet_router.h | 7 +- modules/pacing/round_robin_packet_queue.cc | 11 +- modules/pacing/round_robin_packet_queue.h | 4 +- modules/pacing/rtp_packet_pacer.h | 3 +- modules/pacing/task_queue_paced_sender.cc | 158 +- modules/pacing/task_queue_paced_sender.h | 61 +- .../task_queue_paced_sender_unittest.cc | 1068 +-- modules/remote_bitrate_estimator/BUILD.gn | 1 + modules/remote_bitrate_estimator/OWNERS | 2 +- .../aimd_rate_control.cc | 66 +- .../aimd_rate_control.h | 11 +- .../aimd_rate_control_unittest.cc | 151 + .../remote_bitrate_estimator/bwe_defines.cc | 2 - .../include/remote_bitrate_estimator.h | 11 - .../overuse_detector.h | 6 +- .../overuse_estimator.h | 6 +- .../remote_bitrate_estimator_abs_send_time.cc | 8 +- ...itrate_estimator_abs_send_time_unittest.cc | 8 +- ...itrate_estimator_single_stream_unittest.cc | 8 +- ...remote_bitrate_estimator_unittest_helper.h | 18 +- .../test/bwe_test_logging.h | 7 +- modules/rtp_rtcp/BUILD.gn | 4 +- .../include/remote_ntp_time_estimator.h | 5 +- modules/rtp_rtcp/include/rtp_cvo.h | 4 +- .../include/rtp_header_extension_map.h | 6 +- modules/rtp_rtcp/include/rtp_rtcp_defines.cc | 3 + modules/rtp_rtcp/include/rtp_rtcp_defines.h | 8 +- modules/rtp_rtcp/mocks/mock_rtp_rtcp.h | 5 - .../deprecated_rtp_sender_egress.cc | 14 +- .../source/flexfec_header_reader_writer.cc | 7 +- modules/rtp_rtcp/source/flexfec_receiver.cc | 26 +- modules/rtp_rtcp/source/flexfec_sender.cc | 10 +- .../forward_error_correction_internal.cc | 2 +- modules/rtp_rtcp/source/packet_sequencer.cc | 15 +- .../source/packet_sequencer_unittest.cc | 16 +- .../source/receive_statistics_impl.cc | 7 +- .../source/remote_ntp_time_estimator.cc | 25 +- .../source/rtcp_packet/compound_packet.h | 7 +- modules/rtp_rtcp/source/rtcp_packet/dlrr.h | 10 + .../rtcp_packet/extended_reports_unittest.cc | 13 - .../source/rtcp_packet/loss_notification.h | 4 +- modules/rtp_rtcp/source/rtcp_packet/rrtr.h | 8 + .../source/rtcp_packet/transport_feedback.cc | 17 +- .../transport_feedback_unittest.cc | 9 + modules/rtp_rtcp/source/rtcp_receiver.cc | 7 +- .../rtp_rtcp/source/rtcp_receiver_unittest.cc | 11 +- modules/rtp_rtcp/source/rtcp_sender.cc | 8 +- .../rtp_rtcp/source/rtcp_sender_unittest.cc | 13 +- .../source/rtcp_transceiver_config.cc | 10 +- .../rtp_rtcp/source/rtcp_transceiver_config.h | 55 +- .../rtp_rtcp/source/rtcp_transceiver_impl.cc | 328 +- .../rtp_rtcp/source/rtcp_transceiver_impl.h | 48 +- .../source/rtcp_transceiver_impl_unittest.cc | 375 +- .../rtp_dependency_descriptor_extension.cc | 1 - .../rtp_dependency_descriptor_extension.h | 9 +- modules/rtp_rtcp/source/rtp_format_h264.h | 6 +- .../source/rtp_format_video_generic.h | 6 +- modules/rtp_rtcp/source/rtp_format_vp8.h | 6 +- .../source/rtp_format_vp8_test_helper.h | 7 +- modules/rtp_rtcp/source/rtp_format_vp9.h | 6 +- .../rtp_generic_frame_descriptor_extension.cc | 1 - .../rtp_generic_frame_descriptor_extension.h | 8 +- .../source/rtp_header_extension_map.cc | 23 +- .../rtp_header_extension_map_unittest.cc | 16 +- .../rtp_rtcp/source/rtp_header_extensions.cc | 21 +- .../rtp_rtcp/source/rtp_header_extensions.h | 80 +- modules/rtp_rtcp/source/rtp_packet_history.cc | 159 +- modules/rtp_rtcp/source/rtp_packet_history.h | 72 +- .../source/rtp_packet_history_unittest.cc | 424 +- modules/rtp_rtcp/source/rtp_packet_received.h | 10 - modules/rtp_rtcp/source/rtp_packet_to_send.h | 24 +- .../rtp_rtcp/source/rtp_packet_unittest.cc | 3 +- modules/rtp_rtcp/source/rtp_rtcp_config.h | 2 +- modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 6 +- modules/rtp_rtcp/source/rtp_rtcp_impl.h | 1 - modules/rtp_rtcp/source/rtp_rtcp_impl2.cc | 6 +- modules/rtp_rtcp/source/rtp_rtcp_impl2.h | 1 - .../source/rtp_rtcp_impl2_unittest.cc | 8 +- modules/rtp_rtcp/source/rtp_rtcp_interface.h | 8 +- modules/rtp_rtcp/source/rtp_sender.cc | 52 +- modules/rtp_rtcp/source/rtp_sender.h | 2 - modules/rtp_rtcp/source/rtp_sender_audio.cc | 8 +- .../source/rtp_sender_audio_unittest.cc | 24 +- modules/rtp_rtcp/source/rtp_sender_egress.cc | 14 +- .../source/rtp_sender_egress_unittest.cc | 70 +- .../rtp_rtcp/source/rtp_sender_unittest.cc | 219 +- modules/rtp_rtcp/source/rtp_sender_video.cc | 76 +- modules/rtp_rtcp/source/rtp_sender_video.h | 22 +- ...sender_video_frame_transformer_delegate.cc | 2 +- .../source/rtp_sender_video_unittest.cc | 255 +- modules/rtp_rtcp/source/rtp_video_header.h | 6 + .../rtp_video_layers_allocation_extension.cc | 8 +- .../rtp_video_layers_allocation_extension.h | 8 +- ...eo_layers_allocation_extension_unittest.cc | 27 + .../source/video_rtp_depacketizer_av1.cc | 31 +- .../source/video_rtp_depacketizer_h264.cc | 3 +- .../source/video_rtp_depacketizer_vp9.cc | 4 +- .../video_rtp_depacketizer_vp9_unittest.cc | 2 + .../test/testFec/test_packet_masks_metrics.cc | 2 +- modules/utility/source/jvm_android.cc | 40 +- modules/video_capture/BUILD.gn | 1 - modules/video_capture/device_info_impl.h | 2 +- .../linux/video_capture_linux.cc | 23 +- .../test/video_capture_unittest.cc | 3 +- modules/video_capture/video_capture.h | 2 +- .../video_capture/windows/device_info_ds.cc | 3 + .../video_capture/windows/device_info_ds.h | 2 +- .../windows/help_functions_ds.cc | 6 +- .../video_capture/windows/sink_filter_ds.cc | 125 +- modules/video_coding/BUILD.gn | 207 +- modules/video_coding/codecs/av1/BUILD.gn | 64 +- modules/video_coding/codecs/av1/DEPS | 1 + .../video_coding/codecs/av1/av1_svc_config.cc | 14 +- .../codecs/av1/av1_svc_config_unittest.cc | 15 +- .../video_coding/codecs/av1/dav1d_decoder.cc | 197 + ..._av1_encoder_absent.cc => dav1d_decoder.h} | 15 +- .../codecs/av1/libaom_av1_encoder.cc | 141 +- .../codecs/av1/libaom_av1_encoder.h | 6 +- .../av1/libaom_av1_encoder_supported.cc | 41 + .../codecs/av1/libaom_av1_encoder_supported.h | 29 + .../codecs/av1/libaom_av1_encoder_unittest.cc | 70 +- .../codecs/av1/libaom_av1_unittest.cc | 32 +- modules/video_coding/codecs/h264/h264.cc | 51 +- .../codecs/h264/h264_decoder_impl.cc | 181 +- .../codecs/h264/h264_encoder_impl.cc | 13 +- .../video_coding/codecs/h264/include/h264.h | 9 +- .../codecs/h264/include/h264_globals.h | 2 +- .../codecs/interface/libvpx_interface.cc | 26 +- .../multiplex/multiplex_decoder_adapter.cc | 4 +- .../multiplex/multiplex_encoder_adapter.cc | 2 - .../codecs/test/video_codec_unittest.h | 4 +- ...deo_encoder_decoder_instantiation_tests.cc | 2 +- ..._test_libaom.cc => videocodec_test_av1.cc} | 40 +- .../test/videocodec_test_fixture_impl.cc | 81 +- .../codecs/test/videocodec_test_libvpx.cc | 2 +- .../codecs/test/videocodec_test_mediacodec.cc | 95 +- .../codecs/test/videocodec_test_stats_impl.cc | 12 +- .../video_coding/codecs/test/videoprocessor.h | 8 +- .../codecs/vp8/default_temporal_layers.cc | 12 +- modules/video_coding/codecs/vp8/include/vp8.h | 2 + .../codecs/vp8/libvpx_vp8_encoder.cc | 44 +- .../codecs/vp8/screenshare_layers.cc | 7 +- .../codecs/vp8/screenshare_layers_unittest.cc | 12 +- .../codecs/vp8/test/vp8_impl_unittest.cc | 9 +- modules/video_coding/codecs/vp9/include/vp9.h | 2 + .../codecs/vp9/include/vp9_globals.h | 27 +- .../codecs/vp9/libvpx_vp9_decoder.cc | 7 +- .../codecs/vp9/libvpx_vp9_encoder.cc | 165 +- .../codecs/vp9/libvpx_vp9_encoder.h | 19 +- modules/video_coding/codecs/vp9/svc_config.cc | 13 +- .../codecs/vp9/svc_config_unittest.cc | 58 +- .../codecs/vp9/test/vp9_impl_unittest.cc | 42 +- modules/video_coding/codecs/vp9/vp9.cc | 22 +- .../codecs/vp9/vp9_frame_buffer_pool.cc | 2 +- .../video_coding/fec_controller_default.cc | 5 +- modules/video_coding/fec_controller_default.h | 12 +- modules/video_coding/frame_buffer.cc | 2 +- modules/video_coding/frame_buffer2.cc | 210 +- modules/video_coding/frame_buffer2.h | 33 +- .../video_coding/frame_buffer2_unittest.cc | 158 +- modules/video_coding/frame_buffer3.cc | 281 + modules/video_coding/frame_buffer3.h | 97 + .../video_coding/frame_buffer3_unittest.cc | 344 + modules/video_coding/frame_helpers.cc | 90 + modules/video_coding/frame_helpers.h | 30 + modules/video_coding/generic_decoder.cc | 2 +- modules/video_coding/h264_packet_buffer.cc | 287 + modules/video_coding/h264_packet_buffer.h | 56 + .../h264_packet_buffer_unittest.cc | 778 +++ .../video_coding/h264_sprop_parameter_sets.h | 7 +- modules/video_coding/inter_frame_delay.cc | 91 +- modules/video_coding/inter_frame_delay.h | 40 +- .../inter_frame_delay_unittest.cc | 190 + modules/video_coding/jitter_buffer.cc | 29 +- modules/video_coding/jitter_buffer.h | 7 +- modules/video_coding/jitter_estimator.cc | 420 +- modules/video_coding/jitter_estimator.h | 139 +- .../video_coding/jitter_estimator_tests.cc | 51 +- .../video_coding/nack_requester_unittest.cc | 2 +- modules/video_coding/receiver.cc | 23 +- modules/video_coding/receiver_unittest.cc | 19 +- .../rtp_seq_num_only_ref_finder.cc | 2 +- modules/video_coding/rtp_vp8_ref_finder.cc | 2 +- modules/video_coding/rtp_vp9_ref_finder.cc | 110 +- modules/video_coding/rtp_vp9_ref_finder.h | 17 +- .../rtp_vp9_ref_finder_unittest.cc | 14 + modules/video_coding/rtt_filter.cc | 186 +- modules/video_coding/rtt_filter.h | 49 +- modules/video_coding/rtt_filter_unittest.cc | 105 + .../svc/create_scalability_structure.cc | 110 +- .../svc/create_scalability_structure.h | 6 + .../svc/scalability_structure_full_svc.cc | 9 +- ...scalability_structure_full_svc_unittest.cc | 21 + .../svc/scalability_structure_key_svc.cc | 5 +- .../scalability_structure_l2t2_key_shift.cc | 1 + .../svc/scalability_structure_simulcast.cc | 5 +- .../svc/scalability_structure_unittest.cc | 25 + .../svc/scalable_video_controller.h | 2 + .../scalable_video_controller_no_layering.cc | 1 + modules/video_coding/test/stream_generator.h | 7 +- modules/video_coding/timing.cc | 243 +- modules/video_coding/timing.h | 79 +- modules/video_coding/timing_unittest.cc | 257 +- .../utility/bandwidth_quality_scaler.cc | 150 + .../utility/bandwidth_quality_scaler.h | 95 + .../bandwidth_quality_scaler_unittest.cc | 279 + .../utility/decoded_frames_history.cc | 7 +- .../utility/decoded_frames_history.h | 6 +- modules/video_coding/utility/ivf_defines.h | 23 + .../video_coding/utility/ivf_file_reader.cc | 10 +- .../video_coding/utility/ivf_file_reader.h | 6 +- .../video_coding/utility/ivf_file_writer.cc | 19 +- .../video_coding/utility/ivf_file_writer.h | 6 +- .../utility/ivf_file_writer_unittest.cc | 112 + .../utility/simulcast_rate_allocator.h | 6 +- .../utility/simulcast_test_fixture_impl.cc | 2 +- .../utility/simulcast_test_fixture_impl.h | 6 +- .../utility/vp9_uncompressed_header_parser.cc | 3 +- .../video_codec_initializer_unittest.cc | 4 +- modules/video_coding/video_receiver.cc | 5 +- .../video_processing/util/skin_detection.h | 10 +- net/dcsctp/common/BUILD.gn | 11 +- net/dcsctp/common/handover_testing.cc | 22 + net/dcsctp/common/handover_testing.h | 29 + net/dcsctp/common/internal_types.h | 17 +- net/dcsctp/common/sequence_numbers.h | 2 +- net/dcsctp/common/sequence_numbers_test.cc | 2 +- net/dcsctp/fuzzers/BUILD.gn | 1 + net/dcsctp/fuzzers/dcsctp_fuzzers.h | 5 +- net/dcsctp/packet/chunk/data_chunk.cc | 2 +- net/dcsctp/packet/chunk/data_chunk_test.cc | 2 +- net/dcsctp/packet/chunk/data_common.h | 2 +- net/dcsctp/packet/chunk/forward_tsn_chunk.cc | 3 + .../packet/chunk/forward_tsn_chunk_test.cc | 3 +- net/dcsctp/packet/data.h | 4 +- net/dcsctp/packet/sctp_packet.cc | 4 +- net/dcsctp/public/BUILD.gn | 12 +- net/dcsctp/public/dcsctp_handover_state.cc | 68 + net/dcsctp/public/dcsctp_handover_state.h | 132 + net/dcsctp/public/dcsctp_options.h | 16 +- net/dcsctp/public/dcsctp_socket.h | 77 +- net/dcsctp/public/mock_dcsctp_socket.h | 16 + net/dcsctp/public/types.h | 30 +- net/dcsctp/rx/BUILD.gn | 5 + net/dcsctp/rx/data_tracker.cc | 13 + net/dcsctp/rx/data_tracker.h | 21 +- net/dcsctp/rx/data_tracker_test.cc | 374 +- net/dcsctp/rx/reassembly_queue.cc | 75 +- net/dcsctp/rx/reassembly_queue.h | 11 +- net/dcsctp/rx/reassembly_queue_test.cc | 111 + net/dcsctp/rx/reassembly_streams.h | 4 + .../rx/traditional_reassembly_streams.cc | 90 +- .../rx/traditional_reassembly_streams.h | 21 +- .../rx/traditional_reassembly_streams_test.cc | 105 + net/dcsctp/socket/BUILD.gn | 16 + net/dcsctp/socket/DEPS | 5 + net/dcsctp/socket/callback_deferrer.cc | 161 + net/dcsctp/socket/callback_deferrer.h | 158 +- net/dcsctp/socket/dcsctp_socket.cc | 141 +- net/dcsctp/socket/dcsctp_socket.h | 11 +- .../socket/dcsctp_socket_network_test.cc | 526 ++ net/dcsctp/socket/dcsctp_socket_test.cc | 1798 +++-- net/dcsctp/socket/heartbeat_handler.cc | 7 +- net/dcsctp/socket/heartbeat_handler_test.cc | 28 +- .../socket/mock_dcsctp_socket_callbacks.h | 5 +- net/dcsctp/socket/state_cookie_test.cc | 13 + net/dcsctp/socket/stream_reset_handler.cc | 16 + net/dcsctp/socket/stream_reset_handler.h | 17 +- .../socket/stream_reset_handler_test.cc | 324 +- .../socket/transmission_control_block.cc | 30 +- .../socket/transmission_control_block.h | 53 +- net/dcsctp/testing/data_generator.h | 4 +- net/dcsctp/timer/BUILD.gn | 5 +- net/dcsctp/timer/fake_timeout.h | 12 +- net/dcsctp/timer/task_queue_timeout.cc | 7 +- net/dcsctp/timer/task_queue_timeout.h | 10 +- net/dcsctp/timer/task_queue_timeout_test.cc | 38 + net/dcsctp/timer/timer.cc | 4 +- net/dcsctp/timer/timer.h | 29 +- net/dcsctp/timer/timer_test.cc | 34 +- net/dcsctp/tx/BUILD.gn | 37 +- net/dcsctp/tx/outstanding_data.cc | 479 ++ net/dcsctp/tx/outstanding_data.h | 285 + net/dcsctp/tx/outstanding_data_test.cc | 401 ++ net/dcsctp/tx/retransmission_queue.cc | 579 +- net/dcsctp/tx/retransmission_queue.h | 247 +- net/dcsctp/tx/retransmission_queue_test.cc | 256 +- net/dcsctp/tx/retransmission_timeout.cc | 25 +- net/dcsctp/tx/retransmission_timeout.h | 1 + net/dcsctp/tx/retransmission_timeout_test.cc | 38 +- net/dcsctp/tx/rr_send_queue.cc | 86 +- net/dcsctp/tx/rr_send_queue.h | 38 +- net/dcsctp/tx/send_queue.h | 4 +- p2p/BUILD.gn | 7 + p2p/base/async_stun_tcp_socket.cc | 11 +- p2p/base/async_stun_tcp_socket.h | 9 +- p2p/base/async_stun_tcp_socket_unittest.cc | 29 +- p2p/base/basic_async_resolver_factory.cc | 95 +- p2p/base/basic_ice_controller.cc | 4 +- p2p/base/basic_packet_socket_factory.cc | 62 +- p2p/base/basic_packet_socket_factory.h | 20 +- p2p/base/connection.cc | 296 +- p2p/base/connection.h | 228 +- p2p/base/connection_info.cc | 1 + p2p/base/connection_info.h | 7 +- p2p/base/default_ice_transport_factory.cc | 6 +- p2p/base/dtls_transport.cc | 6 +- p2p/base/dtls_transport.h | 11 +- p2p/base/dtls_transport_internal.h | 5 +- p2p/base/fake_ice_transport.h | 2 +- p2p/base/fake_port_allocator.h | 14 +- p2p/base/ice_transport_internal.h | 9 + p2p/base/p2p_transport_channel.cc | 393 +- p2p/base/p2p_transport_channel.h | 42 +- .../p2p_transport_channel_ice_field_trials.h | 9 + p2p/base/p2p_transport_channel_unittest.cc | 207 +- p2p/base/port.cc | 22 +- p2p/base/port.h | 2 + p2p/base/port_allocator.h | 11 - p2p/base/port_unittest.cc | 144 +- p2p/base/pseudo_tcp.cc | 10 +- p2p/base/stun_port.cc | 71 +- p2p/base/stun_port.h | 35 +- p2p/base/stun_port_unittest.cc | 8 +- p2p/base/stun_request.cc | 12 +- p2p/base/stun_request.h | 9 - p2p/base/tcp_port.cc | 106 +- p2p/base/tcp_port.h | 27 +- p2p/base/tcp_port_unittest.cc | 12 +- p2p/base/test_turn_server.h | 38 +- p2p/base/transport_description_factory.cc | 7 +- p2p/base/transport_description_factory.h | 7 +- .../transport_description_factory_unittest.cc | 4 + p2p/base/turn_port.cc | 129 +- p2p/base/turn_port.h | 64 +- p2p/base/turn_port_unittest.cc | 88 +- p2p/base/turn_server.cc | 26 +- p2p/base/turn_server.h | 15 +- p2p/client/basic_port_allocator.cc | 77 +- p2p/client/basic_port_allocator.h | 30 +- p2p/client/basic_port_allocator_unittest.cc | 13 +- p2p/client/relay_port_factory_interface.h | 3 +- p2p/client/turn_port_factory.cc | 8 +- p2p/stunprober/stun_prober.cc | 6 +- p2p/stunprober/stun_prober.h | 8 +- pc/BUILD.gn | 1383 +++- pc/audio_rtp_receiver.cc | 168 +- pc/audio_rtp_receiver.h | 27 +- pc/audio_rtp_receiver_unittest.cc | 96 + pc/audio_track.cc | 8 +- pc/audio_track.h | 7 +- pc/channel.cc | 708 +- pc/channel.h | 220 +- pc/channel_interface.h | 56 +- pc/channel_manager.cc | 112 +- pc/channel_manager.h | 45 +- pc/channel_manager_unittest.cc | 47 +- pc/channel_unittest.cc | 278 +- pc/connection_context.cc | 92 +- pc/connection_context.h | 8 +- pc/data_channel_controller.cc | 8 +- pc/data_channel_controller.h | 7 +- pc/data_channel_integrationtest.cc | 168 +- pc/data_channel_unittest.cc | 32 +- pc/dtls_srtp_transport.cc | 7 +- pc/dtls_srtp_transport.h | 4 +- pc/dtls_srtp_transport_unittest.cc | 9 +- pc/dtls_transport.cc | 19 +- pc/dtls_transport_unittest.cc | 21 +- pc/dtmf_sender.cc | 6 +- pc/dtmf_sender.h | 9 +- pc/external_hmac.h | 2 +- pc/ice_server_parsing.cc | 20 +- pc/ice_transport.h | 1 - pc/ice_transport_unittest.cc | 5 +- pc/jitter_buffer_delay.h | 1 + pc/jitter_buffer_delay_unittest.cc | 3 - pc/jsep_ice_candidate.cc | 2 - pc/jsep_session_description.cc | 13 +- pc/jsep_session_description_unittest.cc | 3 +- pc/jsep_transport.cc | 25 +- pc/jsep_transport.h | 6 +- pc/jsep_transport_collection.h | 1 + pc/jsep_transport_controller.cc | 18 +- pc/jsep_transport_controller.h | 12 +- pc/jsep_transport_controller_unittest.cc | 21 +- pc/jsep_transport_unittest.cc | 33 +- pc/media_protocol_names.cc | 59 +- pc/media_protocol_names.h | 30 +- pc/media_session.cc | 549 +- pc/media_session.h | 11 +- pc/media_session_unittest.cc | 84 +- pc/media_stream.cc | 17 +- pc/media_stream.h | 2 +- pc/media_stream_track_proxy.h | 9 +- pc/media_stream_unittest.cc | 4 +- pc/peer_connection.cc | 204 +- pc/peer_connection.h | 135 +- ...r_connection_adaptation_integrationtest.cc | 13 +- pc/peer_connection_bundle_unittest.cc | 48 +- pc/peer_connection_crypto_unittest.cc | 32 +- pc/peer_connection_data_channel_unittest.cc | 19 +- pc/peer_connection_end_to_end_unittest.cc | 84 +- pc/peer_connection_factory.cc | 14 +- pc/peer_connection_factory.h | 2 + pc/peer_connection_factory_unittest.cc | 18 +- ...er_connection_header_extension_unittest.cc | 25 +- pc/peer_connection_histogram_unittest.cc | 27 +- pc/peer_connection_ice_unittest.cc | 56 +- pc/peer_connection_integrationtest.cc | 39 +- pc/peer_connection_interface_unittest.cc | 88 +- pc/peer_connection_internal.h | 126 +- pc/peer_connection_jsep_unittest.cc | 77 +- pc/peer_connection_media_unittest.cc | 464 +- pc/peer_connection_message_handler.cc | 3 +- pc/peer_connection_proxy.h | 5 +- pc/peer_connection_rampup_tests.cc | 25 +- pc/peer_connection_rtp_unittest.cc | 62 +- pc/peer_connection_signaling_unittest.cc | 160 +- pc/peer_connection_simulcast_unittest.cc | 29 +- pc/peer_connection_wrapper.cc | 5 +- pc/proxy.h | 309 +- pc/proxy_unittest.cc | 56 +- pc/remote_audio_source.cc | 8 +- pc/rtc_stats_collector.cc | 149 +- pc/rtc_stats_collector.h | 2 + pc/rtc_stats_collector_unittest.cc | 303 +- pc/rtc_stats_integrationtest.cc | 20 +- pc/rtc_stats_traversal.cc | 2 +- pc/rtc_stats_traversal_unittest.cc | 1 - pc/rtp_media_utils.cc | 4 +- pc/rtp_media_utils.h | 3 +- pc/rtp_parameters_conversion.cc | 2 +- pc/rtp_parameters_conversion.h | 1 - pc/rtp_parameters_conversion_unittest.cc | 7 +- pc/rtp_receiver.cc | 2 +- pc/rtp_receiver.h | 21 +- pc/rtp_sender.cc | 28 +- pc/rtp_sender.h | 38 +- pc/rtp_sender_receiver_unittest.cc | 46 +- pc/rtp_transceiver.cc | 77 +- pc/rtp_transceiver.h | 43 +- pc/rtp_transceiver_unittest.cc | 257 +- pc/rtp_transmission_manager.cc | 39 +- pc/rtp_transmission_manager.h | 6 +- pc/rtp_transport.cc | 4 +- pc/rtp_transport.h | 1 + pc/rtp_transport_unittest.cc | 12 +- pc/scenario_tests/goog_cc_test.cc | 5 +- pc/sctp_data_channel.cc | 27 +- pc/sctp_data_channel.h | 3 - pc/sctp_data_channel_transport.cc | 3 - pc/sctp_transport.h | 2 + pc/sctp_transport_unittest.cc | 7 + pc/sctp_utils.cc | 4 +- pc/sctp_utils_unittest.cc | 2 + pc/sdp_offer_answer.cc | 1565 +++-- pc/sdp_offer_answer.h | 123 +- pc/sdp_offer_answer_unittest.cc | 116 + pc/sdp_serializer.cc | 16 +- pc/sdp_serializer_unittest.cc | 4 +- pc/sdp_utils.cc | 2 +- pc/session_description.cc | 3 +- pc/session_description.h | 9 +- pc/session_description_unittest.cc | 2 - pc/simulcast_description.cc | 2 +- pc/simulcast_description.h | 4 +- pc/srtp_filter.cc | 4 +- pc/srtp_filter.h | 1 - pc/srtp_session.cc | 14 +- pc/srtp_session.h | 10 +- pc/srtp_session_unittest.cc | 6 +- pc/srtp_transport.cc | 13 +- pc/srtp_transport.h | 6 +- pc/srtp_transport_unittest.cc | 15 +- pc/stats_collector.cc | 37 +- pc/stats_collector.h | 18 +- pc/stats_collector_unittest.cc | 44 +- pc/stream_collection.h | 5 +- pc/test/fake_audio_capture_module.cc | 56 +- pc/test/fake_peer_connection_base.h | 77 +- pc/test/fake_peer_connection_for_stats.h | 50 +- pc/test/fake_rtc_certificate_generator.h | 4 +- pc/test/integration_test_helpers.cc | 42 + pc/test/integration_test_helpers.h | 87 +- pc/test/mock_channel_interface.h | 8 +- pc/test/mock_peer_connection_observers.h | 44 +- pc/test/mock_rtp_receiver_internal.h | 2 +- pc/test/mock_voice_media_channel.h | 151 + pc/track_media_info_map.cc | 2 +- pc/track_media_info_map_unittest.cc | 18 +- pc/transceiver_list.cc | 2 + pc/used_ids.h | 3 +- pc/used_ids_unittest.cc | 1 + pc/video_rtp_receiver.cc | 163 +- pc/video_rtp_receiver.h | 24 +- pc/video_rtp_receiver_unittest.cc | 41 +- pc/video_rtp_track_source.h | 6 +- pc/video_rtp_track_source_unittest.cc | 6 + pc/video_track.cc | 79 +- pc/video_track.h | 30 +- pc/video_track_source.cc | 3 +- pc/video_track_source.h | 13 +- pc/video_track_source_proxy.cc | 6 +- pc/video_track_source_proxy.h | 11 + pc/video_track_unittest.cc | 13 +- pc/webrtc_sdp.cc | 55 +- pc/webrtc_sdp_unittest.cc | 25 +- pc/webrtc_session_description_factory.cc | 21 +- pc/webrtc_session_description_factory.h | 12 +- presubmit_test.py | 390 +- presubmit_test_mocks.py | 172 +- pylintrc | 8 +- .../output_data_float.pb.sha1 | 2 +- .../output_data_float_avx2.pb.sha1 | 2 +- resources/speech_and_misc_wb.pcm.sha1 | 1 - rtc_base/BUILD.gn | 62 +- rtc_base/async_invoker.cc | 22 - rtc_base/async_invoker.h | 25 +- rtc_base/async_invoker_inl.h | 2 +- rtc_base/async_packet_socket.h | 27 +- rtc_base/async_resolver.cc | 36 +- rtc_base/async_tcp_socket.cc | 165 +- rtc_base/async_tcp_socket.h | 32 +- rtc_base/bit_buffer.cc | 208 +- rtc_base/bit_buffer.h | 115 +- rtc_base/bit_buffer_unittest.cc | 302 +- rtc_base/bitstream_reader.cc | 10 +- rtc_base/bitstream_reader.h | 7 +- rtc_base/bitstream_reader_unittest.cc | 10 + rtc_base/boringssl_certificate.cc | 14 +- rtc_base/boringssl_certificate.h | 12 +- rtc_base/boringssl_identity.cc | 13 +- rtc_base/boringssl_identity.h | 17 +- rtc_base/buffer_queue.h | 6 +- rtc_base/byte_buffer.h | 20 +- rtc_base/callback_list_unittest.cc | 4 +- rtc_base/checks.cc | 11 +- rtc_base/checks.h | 2 +- rtc_base/constructor_magic.h | 20 - rtc_base/copy_on_write_buffer.h | 22 +- rtc_base/copy_on_write_buffer_unittest.cc | 20 + rtc_base/crc32.h | 6 +- .../deprecated/recursive_critical_section.h | 5 +- rtc_base/event_tracer.h | 12 +- rtc_base/experiments/BUILD.gn | 29 +- .../balanced_degradation_settings.cc | 6 +- .../balanced_degradation_settings.h | 3 +- .../balanced_degradation_settings_unittest.cc | 150 +- .../bandwidth_quality_scaler_settings.cc | 43 + .../bandwidth_quality_scaler_settings.h | 35 + ...dwidth_quality_scaler_settings_unittest.cc | 49 + rtc_base/experiments/encoder_info_settings.cc | 104 +- rtc_base/experiments/encoder_info_settings.h | 12 +- rtc_base/experiments/field_trial_list.cc | 6 +- rtc_base/experiments/field_trial_list.h | 16 +- .../experiments/field_trial_list_unittest.cc | 34 +- rtc_base/experiments/field_trial_parser.cc | 100 +- rtc_base/experiments/field_trial_parser.h | 49 +- .../field_trial_parser_unittest.cc | 3 +- rtc_base/experiments/field_trial_units.cc | 20 +- rtc_base/experiments/field_trial_units.h | 7 +- .../experiments/field_trial_units_unittest.cc | 3 +- .../min_video_bitrate_experiment.cc | 2 +- .../experiments/quality_rampup_experiment.cc | 7 +- .../experiments/quality_rampup_experiment.h | 1 + .../experiments/struct_parameters_parser.cc | 3 +- .../experiments/struct_parameters_parser.h | 2 +- rtc_base/fake_ssl_identity.cc | 17 +- rtc_base/fake_ssl_identity.h | 9 +- rtc_base/file_rotating_stream.cc | 97 +- rtc_base/file_rotating_stream.h | 25 +- rtc_base/file_rotating_stream_unittest.cc | 20 +- rtc_base/gunit.cc | 7 +- rtc_base/gunit.h | 5 +- rtc_base/helpers.cc | 7 +- rtc_base/helpers.h | 3 +- rtc_base/http_common.cc | 32 +- rtc_base/http_common.h | 10 +- rtc_base/ifaddrs_android.cc | 17 +- rtc_base/ip_address.cc | 11 +- rtc_base/ip_address.h | 8 +- rtc_base/ip_address_unittest.cc | 11 +- rtc_base/log_sinks.cc | 11 +- rtc_base/log_sinks.h | 16 +- rtc_base/logging.cc | 52 +- rtc_base/logging.h | 20 +- rtc_base/logging_unittest.cc | 6 +- rtc_base/mdns_responder_interface.h | 3 +- rtc_base/memory/BUILD.gn | 4 + rtc_base/memory/always_valid_pointer.h | 40 + rtc_base/memory/fifo_buffer.cc | 71 +- rtc_base/memory/fifo_buffer.h | 47 +- rtc_base/memory/fifo_buffer_unittest.cc | 68 - rtc_base/message_digest.cc | 33 +- rtc_base/message_digest.h | 36 +- rtc_base/message_handler.h | 8 +- rtc_base/nat_server.h | 5 +- rtc_base/nat_socket_factory.h | 9 +- rtc_base/nat_types.cc | 2 +- rtc_base/nat_unittest.cc | 5 +- rtc_base/net_helper.cc | 4 +- rtc_base/net_helper.h | 4 +- rtc_base/net_helpers.cc | 6 +- rtc_base/network.cc | 168 +- rtc_base/network.h | 37 +- rtc_base/network_constants.cc | 2 +- rtc_base/network_constants.h | 10 + rtc_base/network_monitor.h | 11 +- rtc_base/network_unittest.cc | 207 +- rtc_base/null_socket_server.cc | 2 +- .../numerics/histogram_percentile_counter.cc | 2 +- rtc_base/numerics/moving_max_counter.h | 6 +- rtc_base/numerics/moving_median_filter.h | 6 +- .../numerics/percentile_filter_unittest.cc | 7 +- rtc_base/openssl_adapter.cc | 72 +- rtc_base/openssl_adapter.h | 24 +- rtc_base/openssl_certificate.h | 5 +- rtc_base/openssl_digest.cc | 8 +- rtc_base/openssl_digest.h | 7 +- rtc_base/openssl_identity.h | 6 +- rtc_base/openssl_key_pair.cc | 12 +- rtc_base/openssl_key_pair.h | 9 +- rtc_base/openssl_session_cache.cc | 7 +- rtc_base/openssl_session_cache.h | 14 +- rtc_base/openssl_stream_adapter.cc | 18 +- rtc_base/openssl_stream_adapter.h | 7 +- rtc_base/openssl_utility.cc | 11 +- rtc_base/openssl_utility.h | 6 +- rtc_base/operations_chain.cc | 11 +- rtc_base/operations_chain.h | 16 +- rtc_base/operations_chain_unittest.cc | 60 +- rtc_base/physical_socket_server.cc | 112 +- rtc_base/proxy_server.h | 14 +- rtc_base/rate_statistics.cc | 2 +- rtc_base/rate_statistics_unittest.cc | 14 +- rtc_base/ref_counted_object.h | 77 +- rtc_base/ref_counted_object_unittest.cc | 7 +- rtc_base/rolling_accumulator.h | 6 +- rtc_base/rtc_certificate.cc | 4 +- rtc_base/rtc_certificate.h | 5 +- rtc_base/rtc_certificate_generator.cc | 8 +- .../rtc_certificate_generator_unittest.cc | 2 +- rtc_base/server_socket_adapters.h | 9 +- rtc_base/sigslot_tester.h | 31 +- rtc_base/sigslottester.h.pump | 12 +- rtc_base/socket.h | 7 +- rtc_base/socket_adapters.cc | 28 +- rtc_base/socket_adapters.h | 24 +- rtc_base/socket_address.cc | 26 +- rtc_base/socket_address.h | 8 +- rtc_base/socket_stream.h | 6 +- rtc_base/socket_unittest.cc | 3 +- rtc_base/socket_unittest.h | 3 +- rtc_base/ssl_adapter.cc | 4 +- rtc_base/ssl_adapter.h | 18 +- rtc_base/ssl_adapter_unittest.cc | 14 +- rtc_base/ssl_certificate.cc | 3 +- rtc_base/ssl_certificate.h | 12 +- rtc_base/ssl_fingerprint.cc | 22 +- rtc_base/ssl_fingerprint.h | 20 +- rtc_base/ssl_identity.cc | 37 +- rtc_base/ssl_identity.h | 22 +- rtc_base/ssl_identity_unittest.cc | 11 +- rtc_base/ssl_stream_adapter.cc | 9 +- rtc_base/ssl_stream_adapter.h | 12 +- rtc_base/ssl_stream_adapter_unittest.cc | 30 +- rtc_base/stream.h | 7 +- rtc_base/string_encode.cc | 56 +- rtc_base/string_encode.h | 22 +- rtc_base/string_utils.cc | 15 +- rtc_base/string_utils.h | 23 +- rtc_base/string_utils_unittest.cc | 8 - rtc_base/strings/json.cc | 18 +- rtc_base/strings/json.h | 16 +- rtc_base/strings/string_builder.cc | 11 +- rtc_base/strings/string_builder.h | 3 +- .../dcsctp/public => rtc_base}/strong_alias.h | 10 +- .../strong_alias_unittest.cc | 6 +- rtc_base/swap_queue_unittest.cc | 9 +- rtc_base/synchronization/mutex_unittest.cc | 2 +- .../sequence_checker_internal.cc | 2 +- rtc_base/system/unused.h | 4 + rtc_base/system_time.cc | 2 +- rtc_base/task_queue.cc | 17 +- rtc_base/task_queue.h | 41 +- rtc_base/task_queue_libevent.cc | 2 +- rtc_base/task_queue_win.cc | 8 +- rtc_base/task_utils/BUILD.gn | 4 + .../task_utils/pending_task_safety_flag.cc | 16 +- .../task_utils/pending_task_safety_flag.h | 2 + rtc_base/task_utils/repeating_task.cc | 14 +- rtc_base/task_utils/repeating_task.h | 25 +- .../task_utils/repeating_task_unittest.cc | 273 +- rtc_base/test_client.h | 5 +- rtc_base/test_echo_server.h | 7 +- rtc_base/thread.cc | 50 +- rtc_base/thread.h | 113 +- rtc_base/thread_unittest.cc | 102 +- rtc_base/time/BUILD.gn | 2 + rtc_base/time/timestamp_extrapolator.cc | 60 +- rtc_base/time/timestamp_extrapolator.h | 15 +- rtc_base/time_utils.h | 6 + rtc_base/timestamp_aligner.h | 5 +- rtc_base/unique_id_generator.cc | 8 +- rtc_base/unique_id_generator.h | 3 +- rtc_base/unique_id_generator_unittest.cc | 3 + rtc_base/units/unit_base.h | 8 + rtc_base/units/unit_base_unittest.cc | 11 + rtc_base/virtual_socket_server.cc | 150 +- rtc_base/virtual_socket_server.h | 86 +- rtc_base/win/scoped_com_initializer.cc | 8 +- rtc_base/win/windows_version.cc | 9 +- rtc_base/win/windows_version.h | 7 +- rtc_base/win/windows_version_unittest.cc | 6 +- rtc_base/win32.cc | 24 - rtc_base/win32.h | 62 +- rtc_base/win32_socket_server.cc | 813 --- rtc_base/win32_socket_server.h | 151 - rtc_base/win32_socket_server_unittest.cc | 162 - rtc_tools/BUILD.gn | 3 + rtc_tools/converter/yuv_to_ivf_converter.cc | 10 +- rtc_tools/data_channel_benchmark/BUILD.gn | 62 + .../data_channel_benchmark.cc | 323 + .../data_channel_benchmark/grpc_signaling.cc | 267 + .../data_channel_benchmark/grpc_signaling.h | 64 + .../peer_connection_client.cc | 301 + .../peer_connection_client.h | 108 + .../peer_connection_signaling.proto | 29 + .../signaling_interface.h | 42 + .../frame_analyzer/video_color_aligner.cc | 4 +- .../frame_analyzer/video_geometry_aligner.cc | 4 +- .../frame_analyzer/video_temporal_aligner.cc | 20 +- rtc_tools/loopback_test/README | 12 - rtc_tools/loopback_test/adapter.js | 211 - rtc_tools/loopback_test/loopback_test.html | 227 - rtc_tools/loopback_test/loopback_test.js | 240 - rtc_tools/loopback_test/record-test.sh | 60 - rtc_tools/loopback_test/stat_tracker.js | 94 - rtc_tools/metrics_plotter.py | 3 +- rtc_tools/network_tester/config_reader.h | 5 +- rtc_tools/network_tester/packet_logger.h | 6 +- rtc_tools/network_tester/packet_sender.h | 6 +- rtc_tools/network_tester/test_controller.cc | 6 +- rtc_tools/network_tester/test_controller.h | 6 +- rtc_tools/rtc_event_log_visualizer/alerts.cc | 18 +- rtc_tools/rtc_event_log_visualizer/alerts.h | 5 +- .../rtc_event_log_visualizer/analyze_audio.cc | 24 +- .../rtc_event_log_visualizer/analyzer.cc | 157 +- .../analyzer_common.h | 42 +- rtc_tools/rtc_event_log_visualizer/main.cc | 9 +- .../rtc_event_log_visualizer/plot_base.cc | 6 +- .../rtc_event_log_visualizer/plot_base.h | 6 + .../proto/chart.proto | 4 + rtc_tools/sanitizers_unittest.cc | 6 +- rtc_tools/video_file_reader.cc | 4 +- rtc_tools/video_replay.cc | 6 +- sdk/BUILD.gn | 27 +- sdk/android/BUILD.gn | 106 +- sdk/android/OWNERS | 3 + .../api/org/webrtc/Camera2Capturer.java | 1 + .../api/org/webrtc/Camera2Enumerator.java | 2 + .../android/api/org/webrtc/Dav1dDecoder.java | 19 +- sdk/android/api/org/webrtc/EglBase.java | 6 +- .../webrtc/HardwareVideoEncoderFactory.java | 49 +- .../org/webrtc/IceCandidateErrorEvent.java | 43 + .../api/org/webrtc/LibaomAv1Encoder.java | 2 - .../webrtc/LibaomAv1EncoderIfSupported.java | 27 + .../org/webrtc/NetworkMonitorAutoDetect.java | 103 +- .../api/org/webrtc/PeerConnection.java | 45 +- .../api/org/webrtc/PeerConnectionFactory.java | 2 + .../api/org/webrtc/ScreenCapturerAndroid.java | 1 + .../webrtc/SoftwareVideoDecoderFactory.java | 12 +- .../webrtc/SoftwareVideoEncoderFactory.java | 18 +- .../api/org/webrtc/SurfaceTextureHelper.java | 1 + sdk/android/api/org/webrtc/VideoEncoder.java | 39 + sdk/android/api/org/webrtc/VideoFrame.java | 6 +- sdk/android/api/org/webrtc/YuvHelper.java | 123 +- ...ndroidVideoDecoderInstrumentationTest.java | 24 +- .../src/org/webrtc/Camera2CapturerTest.java | 2 - .../DefaultVideoEncoderFactoryTest.java | 6 +- .../org/webrtc/HardwareVideoEncoderTest.java | 72 +- .../src/org/webrtc/NetworkMonitorTest.java | 100 +- .../webrtc/PeerConnectionEndToEndTest.java | 60 +- .../src/org/webrtc/PeerConnectionTest.java | 3 + .../src/org/webrtc/YuvHelperTest.java | 35 + .../audio_device_android.cc | 8 +- sdk/android/native_api/codecs/wrapper.h | 2 +- sdk/android/native_api/jni/java_types.h | 10 +- sdk/android/native_api/jni/scoped_java_ref.h | 24 +- .../peerconnection/peer_connection_factory.cc | 5 +- .../peerconnection/peer_connection_factory.h | 1 + .../audio_device/audio_device_unittest.cc | 4 +- .../org/webrtc/FakeVideoEncoder.java | 2 + .../peer_connection_factory_unittest.cc | 17 +- .../stacktrace/stacktrace_unittest.cc | 4 + .../src/java/org/webrtc/Camera2Session.java | 1 + .../src/java/org/webrtc/EglBase10Impl.java | 25 +- .../src/java/org/webrtc/EglBase14Impl.java | 37 +- .../java/org/webrtc/HardwareVideoEncoder.java | 81 +- .../src/java/org/webrtc/MediaCodecUtils.java | 11 +- .../webrtc/MediaCodecVideoDecoderFactory.java | 7 +- .../java/org/webrtc/MediaCodecWrapper.java | 2 + .../webrtc/MediaCodecWrapperFactoryImpl.java | 8 +- .../src/java/org/webrtc/NativeLibrary.java | 9 +- .../java/org/webrtc/VideoCodecMimeType.java | 8 - .../org/webrtc/audio/WebRtcAudioEffects.java | 7 - .../org/webrtc/audio/WebRtcAudioManager.java | 7 +- .../org/webrtc/audio/WebRtcAudioRecord.java | 17 +- .../org/webrtc/audio/WebRtcAudioTrack.java | 9 +- .../org/webrtc/audio/WebRtcAudioUtils.java | 1 + .../src/jni/android_network_monitor.cc | 24 +- sdk/android/src/jni/android_network_monitor.h | 22 +- .../src/jni/android_video_track_source.cc | 2 +- .../src/jni/audio_device/aaudio_player.cc | 32 +- .../src/jni/audio_device/aaudio_recorder.cc | 38 +- .../src/jni/audio_device/aaudio_wrapper.cc | 52 +- .../jni/audio_device/audio_device_module.cc | 168 +- .../src/jni/audio_device/audio_record_jni.cc | 46 +- .../src/jni/audio_device/audio_record_jni.h | 3 +- .../src/jni/audio_device/audio_track_jni.cc | 30 +- .../src/jni/audio_device/opensles_common.cc | 5 +- sdk/android/src/jni/dav1d_codec.cc | 25 + .../jni/{av1_codec.cc => libaom_av1_codec.cc} | 13 +- sdk/android/src/jni/libaom_av1_encoder.cc | 25 + sdk/android/src/jni/logging/log_sink.cc | 19 +- sdk/android/src/jni/logging/log_sink.h | 6 +- .../src/jni/pc/owned_factory_and_threads.cc | 4 +- .../src/jni/pc/owned_factory_and_threads.h | 5 + sdk/android/src/jni/pc/peer_connection.cc | 34 +- sdk/android/src/jni/pc/peer_connection.h | 6 + .../src/jni/pc/peer_connection_factory.cc | 28 +- .../src/jni/pc/peer_connection_factory.h | 1 + .../rtc_stats_collector_callback_wrapper.cc | 2 +- sdk/android/src/jni/pc/rtp_receiver.cc | 5 +- sdk/android/src/jni/pc/rtp_sender.cc | 5 +- sdk/android/src/jni/video_encoder_wrapper.cc | 27 +- sdk/android/src/jni/video_encoder_wrapper.h | 2 + .../org/webrtc/AndroidVideoDecoderTest.java | 3 +- .../src/org/webrtc/FakeMediaCodecWrapper.java | 9 +- .../org/webrtc/HardwareVideoEncoderTest.java | 3 +- sdk/media_constraints.cc | 23 +- sdk/media_constraints.h | 14 +- sdk/media_constraints_unittest.cc | 4 - sdk/objc/api/logging/RTCCallbackLogger.mm | 17 +- sdk/objc/api/peerconnection/RTCAudioSource.mm | 2 +- sdk/objc/api/peerconnection/RTCAudioTrack.mm | 8 +- .../api/peerconnection/RTCConfiguration.h | 36 +- .../api/peerconnection/RTCConfiguration.mm | 7 +- .../peerconnection/RTCEncodedImage+Private.mm | 2 +- sdk/objc/api/peerconnection/RTCFieldTrials.h | 1 - sdk/objc/api/peerconnection/RTCFieldTrials.mm | 14 +- .../RTCIceCandidateErrorEvent+Private.h | 26 + .../RTCIceCandidateErrorEvent.h | 42 + .../RTCIceCandidateErrorEvent.mm | 42 + .../RTCPeerConnection+Private.h | 6 + .../peerconnection/RTCPeerConnection+Stats.mm | 17 +- .../api/peerconnection/RTCPeerConnection.h | 5 + .../api/peerconnection/RTCPeerConnection.mm | 47 +- .../RTCPeerConnectionFactory.mm | 11 +- .../peerconnection/RTCRtpCodecParameters.mm | 6 +- sdk/objc/api/peerconnection/RTCRtpSender.mm | 12 +- .../peerconnection/RTCSessionDescription.mm | 2 +- .../api/peerconnection/RTCStatisticsReport.mm | 2 +- sdk/objc/api/peerconnection/RTCVideoSource.mm | 6 +- sdk/objc/api/peerconnection/RTCVideoTrack.mm | 9 +- .../api/video_codec/RTCVideoEncoderAV1.mm | 4 +- .../RTCWrappedNativeVideoDecoder.mm | 10 +- .../RTCWrappedNativeVideoEncoder.mm | 20 +- sdk/objc/base/RTCVideoEncoder.h | 2 +- .../audio/RTCAudioSession+Private.h | 4 +- sdk/objc/components/audio/RTCAudioSession.mm | 69 + .../components/network/RTCNetworkMonitor.mm | 6 +- .../renderer/metal/RTCMTLRGBRenderer.mm | 2 +- .../renderer/metal/RTCMTLRenderer.mm | 6 +- .../renderer/metal/RTCMTLVideoView.m | 3 +- .../video_codec/RTCVideoDecoderH264.mm | 2 +- .../video_codec/RTCVideoEncoderH264.mm | 3 +- sdk/objc/components/video_codec/helpers.h | 2 +- .../video_frame_buffer/RTCCVPixelBuffer.mm | 10 +- sdk/objc/helpers/NSString+StdString.h | 8 + sdk/objc/helpers/NSString+StdString.mm | 12 + sdk/objc/native/api/audio_device_module.mm | 7 +- sdk/objc/native/api/video_capturer.mm | 4 +- sdk/objc/native/api/video_frame_buffer.mm | 2 +- sdk/objc/native/src/audio/audio_device_ios.mm | 34 +- .../src/audio/audio_device_module_ios.mm | 199 +- .../src/audio/voice_processing_audio_unit.mm | 5 +- .../native/src/network_monitor_observer.h | 5 +- sdk/objc/native/src/objc_frame_buffer.mm | 5 +- sdk/objc/native/src/objc_network_monitor.h | 17 +- sdk/objc/native/src/objc_network_monitor.mm | 18 +- .../native/src/objc_video_decoder_factory.mm | 3 +- .../native/src/objc_video_encoder_factory.mm | 1 - .../native/src/objc_video_track_source.mm | 6 +- .../unittests/ObjCVideoTrackSource_xctest.mm | 2 +- sdk/objc/unittests/RTCAudioSessionTest.mm | 41 + .../unittests/RTCCameraVideoCapturerTests.mm | 12 +- .../RTCPeerConnectionFactory_xctest.m | 2 + sdk/objc/unittests/RTCPeerConnectionTest.mm | 7 +- sdk/objc/unittests/nalu_rewriter_xctest.mm | 2 + .../objc_video_encoder_factory_tests.mm | 4 +- sdk/objc/unittests/scoped_cftyperef_tests.mm | 2 +- stats/rtcstats_objects.cc | 30 +- system_wrappers/include/field_trial.h | 2 - system_wrappers/include/metrics.h | 2 +- .../include/rtp_to_ntp_estimator.h | 67 +- system_wrappers/source/clock.cc | 5 +- system_wrappers/source/cpu_features_linux.cc | 1 + system_wrappers/source/cpu_info.cc | 2 +- system_wrappers/source/field_trial.cc | 6 +- system_wrappers/source/metrics.cc | 11 +- .../source/rtp_to_ntp_estimator.cc | 165 +- .../source/rtp_to_ntp_estimator_unittest.cc | 351 +- test/BUILD.gn | 27 +- test/DEPS | 4 + test/audio_decoder_proxy_factory.h | 2 +- test/call_test.h | 4 +- test/direct_transport.h | 7 +- test/explicit_key_value_config.cc | 3 +- test/explicit_key_value_config.h | 2 +- test/fake_decoder.cc | 2 +- test/fake_encoder.cc | 12 + test/fake_encoder.h | 6 +- test/frame_generator.cc | 2 +- test/frame_generator.h | 9 +- test/frame_generator_capturer.cc | 86 +- test/frame_generator_capturer.h | 14 + test/frame_generator_capturer_unittest.cc | 44 + test/function_audio_decoder_factory.h | 2 +- test/fuzzers/BUILD.gn | 53 + test/fuzzers/OWNERS | 2 +- test/fuzzers/audio_decoder_g722_fuzzer.cc | 39 + test/fuzzers/audio_decoder_pcm16b_fuzzer.cc | 56 + test/fuzzers/audio_decoder_pcm_fuzzer.cc | 45 + .../audio_processing_configs_fuzzer.cc | 69 +- .../h264-depacketizer-fuzzer-corpus/h264-0 | Bin 0 -> 10 bytes .../h264-depacketizer-fuzzer-corpus/h264-1 | Bin 0 -> 127 bytes .../video_layers_allocation-corpus/vla-0 | 1 + .../0cee4d5fd2905dc1fb2979f10a9724265b7075e2 | Bin 0 -> 11 bytes .../a8b3fb7be82395c9462684c766841d668dc0029f | Bin 0 -> 8 bytes test/fuzzers/frame_buffer2_fuzzer.cc | 4 +- test/fuzzers/frame_buffer3_fuzzer.cc | 88 + .../rtp_video_layers_allocation_fuzzer.cc | 57 + test/fuzzers/sdp_integration_fuzzer.cc | 57 +- test/fuzzers/vp9_encoder_references_fuzzer.cc | 66 +- test/ios/Info.plist | 2 +- test/layer_filtering_transport.cc | 2 +- test/mac/video_renderer_mac.h | 6 +- test/mac_capturer.mm | 2 +- test/mappable_native_buffer.cc | 8 +- test/mock_audio_encoder_factory.h | 6 +- test/network/BUILD.gn | 2 + test/network/cross_traffic_unittest.cc | 8 +- test/network/emulated_network_manager.cc | 25 +- test/network/emulated_network_manager.h | 9 +- test/network/emulated_turn_server.cc | 7 +- test/network/fake_network_socket_server.cc | 4 +- test/network/feedback_generator.cc | 39 +- test/network/feedback_generator.h | 13 +- test/network/feedback_generator_unittest.cc | 16 + test/network/network_emulation.cc | 33 +- test/network/network_emulation.h | 4 +- test/network/network_emulation_unittest.cc | 16 +- test/pc/e2e/BUILD.gn | 9 + .../video/default_video_quality_analyzer.cc | 161 +- .../video/default_video_quality_analyzer.h | 10 +- ...ideo_quality_analyzer_frames_comparator.cc | 12 +- ...quality_analyzer_frames_comparator_test.cc | 6 +- ...t_video_quality_analyzer_shared_objects.cc | 64 +- ...lt_video_quality_analyzer_shared_objects.h | 45 +- .../default_video_quality_analyzer_test.cc | 95 +- .../video/example_video_quality_analyzer.cc | 10 +- .../video/quality_analyzing_video_encoder.cc | 11 +- .../video/quality_analyzing_video_encoder.h | 2 - test/pc/e2e/echo/echo_emulation.cc | 2 +- test/pc/e2e/media/media_helper.cc | 4 +- .../pc/e2e/network_quality_metrics_reporter.h | 2 +- test/pc/e2e/peer_configurer.cc | 288 +- test/pc/e2e/peer_configurer.h | 99 +- test/pc/e2e/peer_connection_e2e_smoke_test.cc | 30 +- test/pc/e2e/peer_connection_quality_test.cc | 98 +- test/pc/e2e/peer_connection_quality_test.h | 5 +- .../e2e/peer_connection_quality_test_params.h | 34 +- ..._based_network_quality_metrics_reporter.cc | 2 +- ...s_based_network_quality_metrics_reporter.h | 2 +- test/pc/e2e/stats_poller.cc | 15 +- test/pc/e2e/stats_poller.h | 15 +- test/pc/e2e/test_activities_executor.cc | 4 +- test/pc/e2e/test_peer_factory.cc | 39 +- test/pc/e2e/test_peer_factory.h | 1 - test/peer_scenario/BUILD.gn | 3 + test/peer_scenario/peer_scenario_client.cc | 20 +- test/peer_scenario/peer_scenario_client.h | 7 +- test/peer_scenario/scenario_connection.cc | 36 +- test/peer_scenario/scenario_connection.h | 3 + test/peer_scenario/signaling_route.cc | 5 +- .../tests/remote_estimate_test.cc | 2 + .../tests/unsignaled_stream_test.cc | 2 + test/rtp_file_reader.cc | 11 +- test/rtp_file_writer.cc | 6 +- test/run_loop.cc | 8 +- test/scenario/audio_stream.h | 16 +- test/scenario/call_client.h | 19 +- test/scenario/column_printer.h | 7 +- test/scenario/hardware_codecs.cc | 6 +- test/scenario/network_node.h | 1 - test/scenario/scenario.h | 7 +- test/scenario/stats_collection.cc | 5 +- test/scenario/video_stream.cc | 8 +- test/scenario/video_stream.h | 16 +- test/scenario/video_stream_unittest.cc | 2 +- test/scoped_key_value_config.cc | 119 + test/scoped_key_value_config.h | 52 + test/test_main_lib.cc | 17 +- test/test_video_capturer.cc | 11 + test/test_video_capturer.h | 3 + test/testsupport/file_utils.cc | 4 +- test/testsupport/file_utils_override.cc | 2 +- test/testsupport/perf_result_reporter.cc | 2 +- test/testsupport/perf_result_reporter.h | 4 +- test/testsupport/perf_test.cc | 11 +- test/testsupport/perf_test.h | 8 +- .../testsupport/perf_test_histogram_writer.cc | 2 +- .../perf_test_histogram_writer_no_protobuf.cc | 5 +- test/testsupport/perf_test_result_writer.h | 14 +- test/testsupport/perf_test_unittest.cc | 7 +- test/testsupport/y4m_frame_reader.cc | 30 +- test/testsupport/y4m_frame_writer.cc | 13 +- test/testsupport/yuv_frame_reader.cc | 25 +- test/testsupport/yuv_frame_writer.cc | 16 +- .../external_time_controller.cc | 6 +- .../simulated_process_thread.h | 2 +- test/time_controller/simulated_thread.cc | 2 +- .../simulated_time_controller.h | 6 +- .../simulated_time_controller_unittest.cc | 5 +- .../time_controller_conformance_test.cc | 12 +- test/video_decoder_proxy_factory.h | 2 +- test/video_encoder_proxy_factory.h | 12 +- test/win/d3d_renderer.cc | 18 +- tools_webrtc/OWNERS | 3 + tools_webrtc/PRESUBMIT.py | 67 +- tools_webrtc/android/build_aar.py | 281 +- tools_webrtc/android/test_aar.py | 170 +- tools_webrtc/apple/copy_framework_header.py | 49 +- .../apple/copy_framework_header_test.py | 40 +- tools_webrtc/autoroller/roll_deps.py | 902 +-- .../autoroller/unittests/roll_deps_test.py | 494 +- tools_webrtc/binary_version_check.py | 34 +- tools_webrtc/clang_tidy.py | 108 +- .../coverage/generate_coverage_command.py | 28 +- .../coverage/generate_ios_coverage_command.py | 137 +- tools_webrtc/cpu/cpu_mon.py | 96 +- tools_webrtc/download_tools.py | 55 +- tools_webrtc/ensure_webcam_is_running.py | 104 +- tools_webrtc/executable_host_build.py | 51 +- tools_webrtc/flags_compatibility.py | 45 +- tools_webrtc/get_landmines.py | 71 +- tools_webrtc/gn_check_autofix.py | 229 +- tools_webrtc/gtest-parallel-wrapper.py | 258 +- tools_webrtc/gtest_parallel_wrapper_test.py | 250 +- tools_webrtc/ios/build_ios_libs.py | 508 +- tools_webrtc/ios/generate_modulemap.py | 30 +- tools_webrtc/ios/generate_umbrella_header.py | 34 +- tools_webrtc/ios/merge_ios_libs.py | 173 +- tools_webrtc/iwyu/apply-iwyu | 126 +- tools_webrtc/iwyu/iwyu | 11 - tools_webrtc/iwyu/iwyu-filter-list | 3 + tools_webrtc/iwyu/mappings.imp | 32 + tools_webrtc/libs/generate_licenses.py | 239 +- tools_webrtc/libs/generate_licenses_test.py | 168 +- tools_webrtc/mb/PRESUBMIT.py | 31 +- tools_webrtc/mb/gn_isolate_map.pyl | 12 - tools_webrtc/mb/mb.bat | 2 +- tools_webrtc/mb/mb.py | 541 +- tools_webrtc/mb/mb_config.pyl | 93 +- tools_webrtc/mb/mb_unittest.py | 595 +- tools_webrtc/network_emulator/config.py | 38 +- tools_webrtc/network_emulator/emulate.py | 282 +- .../network_emulator/network_emulator.py | 209 +- tools_webrtc/perf/catapult_uploader.py | 354 +- tools_webrtc/perf/catapult_uploader_test.py | 122 + tools_webrtc/perf/process_perf_results.py | 123 + tools_webrtc/perf/process_perf_results_py2.py | 25 + .../perf/process_perf_results_test.py | 70 + tools_webrtc/perf/webrtc_dashboard_upload.py | 171 +- .../presubmit_checks_lib/build_helpers.py | 126 +- .../build_helpers_test.py | 21 +- .../check_orphan_headers.py | 78 +- .../check_orphan_headers_test.py | 120 +- .../check_package_boundaries.py | 141 +- .../check_package_boundaries_test.py | 75 +- .../sanitizers/tsan_suppressions_webrtc.cc | 13 +- tools_webrtc/sslroots/README.md | 12 +- tools_webrtc/sslroots/generate_sslroots.py | 292 +- .../version_updater/update_version.py | 200 +- tools_webrtc/vim/webrtc.ycm_extra_conf.py | 296 +- tools_webrtc/whitespace.txt | 1 + video/BUILD.gn | 235 +- video/adaptation/BUILD.gn | 3 + video/adaptation/OWNERS | 2 +- video/adaptation/balanced_constraint.cc | 4 +- video/adaptation/balanced_constraint.h | 6 +- .../bandwidth_quality_scaler_resource.cc | 85 + .../bandwidth_quality_scaler_resource.h | 64 + video/adaptation/overuse_frame_detector.h | 6 +- video/adaptation/pixel_limit_resource.cc | 8 +- .../quality_rampup_experiment_helper.cc | 18 +- .../quality_rampup_experiment_helper.h | 7 +- .../video_stream_encoder_resource.cc | 3 +- .../video_stream_encoder_resource_manager.cc | 115 +- .../video_stream_encoder_resource_manager.h | 20 +- video/buffered_frame_decryptor.cc | 5 +- video/buffered_frame_decryptor.h | 5 +- video/buffered_frame_decryptor_unittest.cc | 8 +- video/call_stats.h | 6 +- video/call_stats2.cc | 1 - video/call_stats2.h | 8 +- video/call_stats2_unittest.cc | 19 +- video/cpu_scaling_tests.cc | 2 +- video/decode_synchronizer.cc | 186 + video/decode_synchronizer.h | 137 + video/decode_synchronizer_unittest.cc | 232 + video/encoder_bitrate_adjuster.cc | 3 +- video/end_to_end_tests/bandwidth_tests.cc | 7 +- .../extended_reports_tests.cc | 3 +- .../multi_codec_receive_tests.cc | 6 +- video/end_to_end_tests/multi_stream_tester.cc | 2 +- video/end_to_end_tests/rtp_rtcp_tests.cc | 2 +- video/end_to_end_tests/stats_tests.cc | 111 +- video/frame_buffer_proxy.cc | 610 ++ video/frame_buffer_proxy.h | 70 + video/frame_buffer_proxy_unittest.cc | 854 +++ video/frame_cadence_adapter.cc | 749 +++ video/frame_cadence_adapter.h | 119 + video/frame_cadence_adapter_unittest.cc | 962 +++ video/frame_decode_scheduler.h | 51 + video/frame_decode_timing.cc | 56 + video/frame_decode_timing.h | 53 + video/frame_decode_timing_unittest.cc | 115 + video/frame_encode_metadata_writer.cc | 73 +- video/frame_encode_metadata_writer.h | 6 +- .../frame_encode_metadata_writer_unittest.cc | 61 +- video/pc_full_stack_tests.cc | 218 +- video/picture_id_tests.cc | 2 +- video/quality_limitation_reason_tracker.h | 2 + video/quality_scaling_tests.cc | 116 +- video/receive_statistics_proxy.cc | 33 +- video/receive_statistics_proxy.h | 14 +- video/receive_statistics_proxy2.cc | 97 +- video/receive_statistics_proxy2.h | 4 +- video/receive_statistics_proxy2_unittest.cc | 27 +- video/receive_statistics_proxy_unittest.cc | 30 +- video/rtp_streams_synchronizer.cc | 208 - video/rtp_streams_synchronizer.h | 67 - video/rtp_streams_synchronizer2.cc | 33 +- video/rtp_video_stream_receiver.cc | 63 +- video/rtp_video_stream_receiver.h | 11 +- video/rtp_video_stream_receiver2.cc | 59 +- video/rtp_video_stream_receiver2.h | 6 +- video/rtp_video_stream_receiver2_unittest.cc | 13 +- ...eam_receiver_frame_transformer_delegate.cc | 4 +- ...ver_frame_transformer_delegate_unittest.cc | 1 - video/rtp_video_stream_receiver_unittest.cc | 11 +- video/send_statistics_proxy.cc | 28 +- video/send_statistics_proxy.h | 4 +- video/send_statistics_proxy_unittest.cc | 41 +- video/stats_counter.cc | 6 +- video/stats_counter.h | 32 +- video/stream_synchronization.cc | 18 +- video/stream_synchronization_unittest.cc | 17 +- video/task_queue_frame_decode_scheduler.cc | 76 + video/task_queue_frame_decode_scheduler.h | 48 + ...k_queue_frame_decode_scheduler_unittest.cc | 135 + video/test/mock_video_stream_encoder.h | 1 - video/video_quality_test.cc | 12 +- video/video_receive_stream.cc | 771 --- video/video_receive_stream.h | 241 - video/video_receive_stream2.cc | 150 +- video/video_receive_stream2.h | 17 +- video/video_receive_stream2_unittest.cc | 93 +- video/video_receive_stream_timeout_tracker.cc | 81 + video/video_receive_stream_timeout_tracker.h | 65 + ...receive_stream_timeout_tracker_unittest.cc | 95 + video/video_receive_stream_unittest.cc | 515 -- video/video_send_stream.cc | 46 +- video/video_send_stream.h | 8 +- video/video_send_stream_impl.cc | 9 +- video/video_send_stream_impl.h | 8 +- video/video_send_stream_impl_unittest.cc | 19 +- video/video_send_stream_tests.cc | 425 +- video/video_source_sink_controller.cc | 9 +- video/video_source_sink_controller.h | 3 + .../video_source_sink_controller_unittest.cc | 15 + video/video_stream_decoder_impl.cc | 106 +- video/video_stream_decoder_impl.h | 3 +- video/video_stream_decoder_impl_unittest.cc | 8 +- video/video_stream_encoder.cc | 655 +- video/video_stream_encoder.h | 97 +- video/video_stream_encoder_unittest.cc | 2248 ++++--- webrtc.gni | 41 +- 2110 files changed, 77529 insertions(+), 40474 deletions(-) create mode 100644 .mailmap create mode 100644 .style.yapf create mode 100644 .vpython3 rename rtc_tools/loopback_test/run-server.sh => api/metronome/BUILD.gn (53%) mode change 100755 => 100644 create mode 100644 api/metronome/metronome.h create mode 100644 api/metronome/test/BUILD.gn create mode 100644 api/metronome/test/fake_metronome.cc create mode 100644 api/metronome/test/fake_metronome.h create mode 100644 api/task_queue/test/BUILD.gn create mode 100644 api/task_queue/test/mock_task_queue_base.h create mode 100644 api/test/peer_network_dependencies.h create mode 100644 api/video/i444_buffer.cc create mode 100644 api/video/i444_buffer.h create mode 100644 api/video/test/i444_buffer_unittest.cc create mode 100644 api/video_codecs/test/video_encoder_factory_template_tests.cc create mode 100644 api/video_codecs/video_encoder_factory_template.h create mode 100644 api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h create mode 100644 api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h create mode 100644 api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h create mode 100644 api/video_codecs/video_encoder_factory_template_open_h264_adapter.h create mode 100644 api/video_track_source_constraints.h create mode 100644 api/webrtc_key_value_config.h create mode 100644 api/wrapping_async_dns_resolver.cc create mode 100644 api/wrapping_async_dns_resolver.h create mode 100644 audio/test/OWNERS create mode 100644 docs/native-code/development/contributing.md delete mode 100644 examples/androidtests/gradle_project_test.py create mode 100644 infra/config/OWNERS create mode 100644 infra/config/PRESUBMIT.py create mode 100644 infra/config/README.md create mode 100644 infra/config/codereview.settings create mode 100644 infra/config/commit-queue.cfg create mode 100755 infra/config/config.star create mode 100644 infra/config/console-header.textpb create mode 100644 infra/config/cr-buildbucket.cfg create mode 100644 infra/config/luci-logdog.cfg create mode 100644 infra/config/luci-milo.cfg create mode 100644 infra/config/luci-notify.cfg create mode 100644 infra/config/luci-notify/email-templates/build_failure.template create mode 100644 infra/config/luci-notify/email-templates/cron.template create mode 100644 infra/config/luci-notify/email-templates/infra_failure.template create mode 100644 infra/config/luci-scheduler.cfg create mode 100644 infra/config/project.cfg create mode 100644 infra/config/realms.cfg create mode 100644 logging/rtc_event_log/encoder/bit_writer.cc create mode 100644 logging/rtc_event_log/encoder/bit_writer.h create mode 100644 logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc create mode 100644 logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h create mode 100644 logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc create mode 100644 logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h create mode 100644 logging/rtc_event_log/events/logged_rtp_rtcp.h create mode 100644 logging/rtc_event_log/events/rtc_event_begin_log.cc create mode 100644 logging/rtc_event_log/events/rtc_event_begin_log.h create mode 100644 logging/rtc_event_log/events/rtc_event_definition.h create mode 100644 logging/rtc_event_log/events/rtc_event_end_log.cc create mode 100644 logging/rtc_event_log/events/rtc_event_end_log.h create mode 100644 logging/rtc_event_log/events/rtc_event_field_encoding.cc create mode 100644 logging/rtc_event_log/events/rtc_event_field_encoding.h create mode 100644 logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc create mode 100644 logging/rtc_event_log/events/rtc_event_field_encoding_parser.h create mode 100644 logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc create mode 100644 logging/rtc_event_log/events/rtc_event_field_extraction.cc create mode 100644 logging/rtc_event_log/events/rtc_event_field_extraction.h create mode 100644 logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc delete mode 100644 logging/rtc_event_log/logged_events.cc create mode 100644 media/engine/internal_encoder_factory_unittest.cc delete mode 100644 modules/audio_coding/codecs/isac/fix/test/kenny.cc create mode 100644 modules/audio_processing/agc/analog_gain_stats_reporter.cc create mode 100644 modules/audio_processing/agc/analog_gain_stats_reporter.h create mode 100644 modules/audio_processing/agc/analog_gain_stats_reporter_unittest.cc delete mode 100644 modules/audio_processing/agc2/adaptive_agc.cc create mode 100644 modules/audio_processing/agc2/adaptive_digital_gain_controller.cc rename modules/audio_processing/agc2/{adaptive_agc.h => adaptive_digital_gain_controller.h} (57%) delete mode 100644 modules/audio_processing/agc2/vad_with_level.cc delete mode 100644 modules/audio_processing/agc2/vad_with_level.h delete mode 100644 modules/audio_processing/agc2/vad_with_level_unittest.cc create mode 100644 modules/audio_processing/agc2/vad_wrapper.cc create mode 100644 modules/audio_processing/agc2/vad_wrapper.h create mode 100644 modules/audio_processing/agc2/vad_wrapper_unittest.cc delete mode 100644 modules/audio_processing/common.h delete mode 100644 modules/audio_processing/config_unittest.cc delete mode 100644 modules/audio_processing/include/config.h delete mode 100644 modules/audio_processing/level_estimator.cc delete mode 100644 modules/audio_processing/level_estimator.h delete mode 100644 modules/audio_processing/level_estimator_unittest.cc delete mode 100644 modules/audio_processing/voice_detection.cc delete mode 100644 modules/audio_processing/voice_detection.h delete mode 100644 modules/audio_processing/voice_detection_unittest.cc delete mode 100644 modules/desktop_capture/linux/base_capturer_pipewire.cc delete mode 100644 modules/desktop_capture/linux/base_capturer_pipewire.h create mode 100644 modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc create mode 100644 modules/desktop_capture/linux/wayland/base_capturer_pipewire.h create mode 100644 modules/desktop_capture/linux/wayland/drm.sigs create mode 100644 modules/desktop_capture/linux/wayland/egl_dmabuf.cc create mode 100644 modules/desktop_capture/linux/wayland/egl_dmabuf.h create mode 100644 modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc create mode 100644 modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h rename modules/desktop_capture/linux/{pipewire03.sigs => wayland/pipewire.sigs} (86%) rename modules/desktop_capture/linux/{ => wayland}/pipewire_stub_header.fragment (87%) create mode 100644 modules/desktop_capture/linux/wayland/scoped_glib.cc create mode 100644 modules/desktop_capture/linux/wayland/scoped_glib.h create mode 100644 modules/desktop_capture/linux/wayland/screencast_portal.cc create mode 100644 modules/desktop_capture/linux/wayland/screencast_portal.h create mode 100644 modules/desktop_capture/linux/wayland/shared_screencast_stream.cc create mode 100644 modules/desktop_capture/linux/wayland/shared_screencast_stream.h create mode 100644 modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc create mode 100644 modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h rename modules/desktop_capture/linux/{ => x11}/mouse_cursor_monitor_x11.cc (97%) rename modules/desktop_capture/linux/{ => x11}/mouse_cursor_monitor_x11.h (87%) rename modules/desktop_capture/linux/{ => x11}/screen_capturer_x11.cc (90%) rename modules/desktop_capture/linux/{ => x11}/screen_capturer_x11.h (90%) rename modules/desktop_capture/linux/{ => x11}/shared_x_display.cc (94%) rename modules/desktop_capture/linux/{ => x11}/shared_x_display.h (88%) rename modules/desktop_capture/linux/{ => x11}/window_capturer_x11.cc (96%) rename modules/desktop_capture/linux/{ => x11}/window_capturer_x11.h (76%) rename modules/desktop_capture/linux/{ => x11}/window_finder_x11.cc (90%) rename modules/desktop_capture/linux/{ => x11}/window_finder_x11.h (81%) rename modules/desktop_capture/linux/{ => x11}/window_list_utils.cc (96%) rename modules/desktop_capture/linux/{ => x11}/window_list_utils.h (89%) rename modules/desktop_capture/linux/{ => x11}/x_atom_cache.cc (95%) rename modules/desktop_capture/linux/{ => x11}/x_atom_cache.h (85%) rename modules/desktop_capture/linux/{ => x11}/x_error_trap.cc (96%) rename modules/desktop_capture/linux/{ => x11}/x_error_trap.h (76%) rename modules/desktop_capture/linux/{ => x11}/x_server_pixel_buffer.cc (97%) rename modules/desktop_capture/linux/{ => x11}/x_server_pixel_buffer.h (88%) rename modules/desktop_capture/linux/{ => x11}/x_window_property.cc (95%) rename modules/desktop_capture/linux/{ => x11}/x_window_property.h (78%) create mode 100644 modules/video_coding/codecs/av1/dav1d_decoder.cc rename modules/video_coding/codecs/av1/{libaom_av1_encoder_absent.cc => dav1d_decoder.h} (55%) create mode 100644 modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc create mode 100644 modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h rename modules/video_coding/codecs/test/{videocodec_test_libaom.cc => videocodec_test_av1.cc} (73%) create mode 100644 modules/video_coding/frame_buffer3.cc create mode 100644 modules/video_coding/frame_buffer3.h create mode 100644 modules/video_coding/frame_buffer3_unittest.cc create mode 100644 modules/video_coding/frame_helpers.cc create mode 100644 modules/video_coding/frame_helpers.h create mode 100644 modules/video_coding/h264_packet_buffer.cc create mode 100644 modules/video_coding/h264_packet_buffer.h create mode 100644 modules/video_coding/h264_packet_buffer_unittest.cc create mode 100644 modules/video_coding/inter_frame_delay_unittest.cc create mode 100644 modules/video_coding/rtt_filter_unittest.cc create mode 100644 modules/video_coding/utility/bandwidth_quality_scaler.cc create mode 100644 modules/video_coding/utility/bandwidth_quality_scaler.h create mode 100644 modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc create mode 100644 modules/video_coding/utility/ivf_defines.h create mode 100644 net/dcsctp/common/handover_testing.cc create mode 100644 net/dcsctp/common/handover_testing.h create mode 100644 net/dcsctp/public/dcsctp_handover_state.cc create mode 100644 net/dcsctp/public/dcsctp_handover_state.h create mode 100644 net/dcsctp/socket/DEPS create mode 100644 net/dcsctp/socket/callback_deferrer.cc create mode 100644 net/dcsctp/socket/dcsctp_socket_network_test.cc create mode 100644 net/dcsctp/tx/outstanding_data.cc create mode 100644 net/dcsctp/tx/outstanding_data.h create mode 100644 net/dcsctp/tx/outstanding_data_test.cc create mode 100644 pc/audio_rtp_receiver_unittest.cc create mode 100644 pc/sdp_offer_answer_unittest.cc create mode 100644 pc/test/mock_voice_media_channel.h delete mode 100644 resources/speech_and_misc_wb.pcm.sha1 delete mode 100644 rtc_base/constructor_magic.h create mode 100644 rtc_base/experiments/bandwidth_quality_scaler_settings.cc create mode 100644 rtc_base/experiments/bandwidth_quality_scaler_settings.h create mode 100644 rtc_base/experiments/bandwidth_quality_scaler_settings_unittest.cc create mode 100644 rtc_base/memory/always_valid_pointer.h rename {net/dcsctp/public => rtc_base}/strong_alias.h (93%) rename net/dcsctp/public/strong_alias_test.cc => rtc_base/strong_alias_unittest.cc (99%) delete mode 100644 rtc_base/win32_socket_server.cc delete mode 100644 rtc_base/win32_socket_server.h delete mode 100644 rtc_base/win32_socket_server_unittest.cc create mode 100644 rtc_tools/data_channel_benchmark/BUILD.gn create mode 100644 rtc_tools/data_channel_benchmark/data_channel_benchmark.cc create mode 100644 rtc_tools/data_channel_benchmark/grpc_signaling.cc create mode 100644 rtc_tools/data_channel_benchmark/grpc_signaling.h create mode 100644 rtc_tools/data_channel_benchmark/peer_connection_client.cc create mode 100644 rtc_tools/data_channel_benchmark/peer_connection_client.h create mode 100644 rtc_tools/data_channel_benchmark/peer_connection_signaling.proto create mode 100644 rtc_tools/data_channel_benchmark/signaling_interface.h delete mode 100644 rtc_tools/loopback_test/README delete mode 100644 rtc_tools/loopback_test/adapter.js delete mode 100644 rtc_tools/loopback_test/loopback_test.html delete mode 100644 rtc_tools/loopback_test/loopback_test.js delete mode 100755 rtc_tools/loopback_test/record-test.sh delete mode 100644 rtc_tools/loopback_test/stat_tracker.js rename modules/audio_processing/include/config.cc => sdk/android/api/org/webrtc/Dav1dDecoder.java (54%) create mode 100644 sdk/android/api/org/webrtc/IceCandidateErrorEvent.java create mode 100644 sdk/android/api/org/webrtc/LibaomAv1EncoderIfSupported.java create mode 100644 sdk/android/src/jni/dav1d_codec.cc rename sdk/android/src/jni/{av1_codec.cc => libaom_av1_codec.cc} (65%) create mode 100644 sdk/android/src/jni/libaom_av1_encoder.cc create mode 100644 sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent+Private.h create mode 100644 sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h create mode 100644 sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.mm create mode 100644 test/fuzzers/audio_decoder_g722_fuzzer.cc create mode 100644 test/fuzzers/audio_decoder_pcm16b_fuzzer.cc create mode 100644 test/fuzzers/audio_decoder_pcm_fuzzer.cc create mode 100644 test/fuzzers/corpora/h264-depacketizer-fuzzer-corpus/h264-0 create mode 100644 test/fuzzers/corpora/h264-depacketizer-fuzzer-corpus/h264-1 create mode 100644 test/fuzzers/corpora/video_layers_allocation-corpus/vla-0 create mode 100644 test/fuzzers/corpora/vp9-encoder-references-corpus/0cee4d5fd2905dc1fb2979f10a9724265b7075e2 create mode 100644 test/fuzzers/corpora/vp9-encoder-references-corpus/a8b3fb7be82395c9462684c766841d668dc0029f create mode 100644 test/fuzzers/frame_buffer3_fuzzer.cc create mode 100644 test/fuzzers/rtp_video_layers_allocation_fuzzer.cc create mode 100644 test/scoped_key_value_config.cc create mode 100644 test/scoped_key_value_config.h delete mode 100755 tools_webrtc/iwyu/iwyu create mode 100644 tools_webrtc/iwyu/mappings.imp create mode 100644 tools_webrtc/perf/catapult_uploader_test.py create mode 100644 tools_webrtc/perf/process_perf_results.py create mode 100644 tools_webrtc/perf/process_perf_results_py2.py create mode 100644 tools_webrtc/perf/process_perf_results_test.py create mode 100644 video/adaptation/bandwidth_quality_scaler_resource.cc create mode 100644 video/adaptation/bandwidth_quality_scaler_resource.h create mode 100644 video/decode_synchronizer.cc create mode 100644 video/decode_synchronizer.h create mode 100644 video/decode_synchronizer_unittest.cc create mode 100644 video/frame_buffer_proxy.cc create mode 100644 video/frame_buffer_proxy.h create mode 100644 video/frame_buffer_proxy_unittest.cc create mode 100644 video/frame_cadence_adapter.cc create mode 100644 video/frame_cadence_adapter.h create mode 100644 video/frame_cadence_adapter_unittest.cc create mode 100644 video/frame_decode_scheduler.h create mode 100644 video/frame_decode_timing.cc create mode 100644 video/frame_decode_timing.h create mode 100644 video/frame_decode_timing_unittest.cc delete mode 100644 video/rtp_streams_synchronizer.cc delete mode 100644 video/rtp_streams_synchronizer.h create mode 100644 video/task_queue_frame_decode_scheduler.cc create mode 100644 video/task_queue_frame_decode_scheduler.h create mode 100644 video/task_queue_frame_decode_scheduler_unittest.cc delete mode 100644 video/video_receive_stream.cc delete mode 100644 video/video_receive_stream.h create mode 100644 video/video_receive_stream_timeout_tracker.cc create mode 100644 video/video_receive_stream_timeout_tracker.h create mode 100644 video/video_receive_stream_timeout_tracker_unittest.cc delete mode 100644 video/video_receive_stream_unittest.cc diff --git a/.gn b/.gn index f1a63754ac..77ebc2dea8 100644 --- a/.gn +++ b/.gn @@ -63,4 +63,8 @@ default_args = { enable_libaom = false gtest_enable_absl_printers = true + + # RingRTC change to support SDK >= 19. + # Differently from Chromium, WebRTC still support SDK 19. + default_min_sdk_version = 19 } diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..634f3a6ac5 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Tommi Tomas Gunnarsson diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000000..c34341d425 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,4 @@ +[style] +based_on_style = pep8 +indent_width = 2 +column_limit = 80 \ No newline at end of file diff --git a/.vpython3 b/.vpython3 new file mode 100644 index 0000000000..99b1a0d8e9 --- /dev/null +++ b/.vpython3 @@ -0,0 +1,82 @@ +# This is a vpython "spec" file. +# +# It describes patterns for python wheel dependencies of the python scripts in +# the chromium repo, particularly for dependencies that have compiled components +# (since pure-python dependencies can be easily vendored into third_party). +# +# When vpython is invoked, it finds this file and builds a python VirtualEnv, +# containing all of the dependencies described in this file, fetching them from +# CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`, +# this never requires the end-user machine to have a working python extension +# compilation environment. All of these packages are built using: +# https://chromium.googlesource.com/infra/infra/+/main/infra/tools/dockerbuild/ +# +# All python scripts in the repo share this same spec, to avoid dependency +# fragmentation. +# +# If you have depot_tools installed in your $PATH, you can invoke python scripts +# in this repo by running them as you normally would run them, except +# substituting `vpython` instead of `python` on the command line, e.g.: +# vpython path/to/script.py some --arguments +# +# Read more about `vpython` and how to modify this file here: +# https://chromium.googlesource.com/infra/infra/+/main/doc/users/vpython.md + +python_version: "3.8" + +# Used by: +# third_party/catapult +wheel: < + name: "infra/python/wheels/psutil/${vpython_platform}" + version: "version:5.8.0.chromium.2" +> + +# Used by tools_webrtc/perf/webrtc_dashboard_upload.py. +wheel: < + name: "infra/python/wheels/httplib2-py3" + version: "version:0.19.1" +> + +wheel: < + name: "infra/python/wheels/pyparsing-py2_py3" + version: "version:2.4.7" +> + + +# Used by: +# build/toolchain/win +wheel: < + name: "infra/python/wheels/pywin32/${vpython_platform}" + version: "version:300" + match_tag: < + platform: "win32" + > + match_tag: < + platform: "win_amd64" + > +> + +wheel: < + name: "infra/python/wheels/six-py2_py3" + version: "version:1.15.0" +> +wheel: < + name: "infra/python/wheels/pbr-py2_py3" + version: "version:3.0.0" +> +wheel: < + name: "infra/python/wheels/funcsigs-py2_py3" + version: "version:1.0.2" +> +wheel: < + name: "infra/python/wheels/mock-py2_py3" + version: "version:2.0.0" +> +wheel: < + name: "infra/python/wheels/protobuf-py2_py3" + version: "version:3.13.0" +> +wheel: < + name: "infra/python/wheels/requests-py2_py3" + version: "version:2.13.0" +> diff --git a/AUTHORS b/AUTHORS index 645b9f3f2e..212c990918 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,6 +58,7 @@ Jesús Leganés-Combarro Jiawei Ou Jie Mao Jiwon Kim +Johnny Wong Jose Antonio Olivera Ortega Keiichi Enomoto Kiran Thind @@ -69,6 +70,7 @@ Mallikarjuna Rao V Manish Jethani Martin Storsjo Matthias Liebig +Maksim Sisov Maxim Pavlov Maxim Potapov Michael Iedema @@ -77,6 +79,8 @@ Miguel Paris Mike Gilbert Min Wang Mo Zanaty +Niek van der Maas +Olivier Crête Pali Rohar Paul Kapustin Peng Yu @@ -88,16 +92,19 @@ Raman Budny Ramprakash Jelari Riku Voipio Robert Bares +Robert Mader Robert Nagy Ryan Yoakum Sarah Thompson Satender Saroha Saul Kravitz Sergio Garcia Murillo +Shuhai Peng Silviu Caragea Stefan Gula Stephan Hartmann Steve Reid +Takaaki Suzuki Tarun Chawla Todd Wong Tomas Popela @@ -108,6 +115,7 @@ Victor Costan Vladimir Beloborodov Xiaohong Xu Xiaolei Yu +Yaowen Guo Yura Yaroshevich Yuriy Pavlyshak Yusuke Suzuki @@ -115,6 +123,7 @@ Yusuke Suzuki # BEGIN organizations section. 8x8 Inc. <*@8x8.com> +8x8 Inc. <*@jitsi.org> 8x8 Inc. <*@sip-communicator.org> Agora IO <*@agora.io> ARM Holdings <*@arm.com> @@ -130,6 +139,7 @@ Microsoft Corporation <*@microsoft.com> MIPS Technologies <*@mips.com> Mozilla Foundation <*@mozilla.com> Netgem S.A. <*@netgem.com> +Nutanix Inc. <*@nutanix.com> NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> Optical Tone Ltd <*@opticaltone.com> diff --git a/BUILD.gn b/BUILD.gn index d836cb7248..6fbd1d977c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,6 +12,15 @@ # you add a new build file, there must be some path of dependencies from this # file to your new one or GN won't know about it. +# Use of visibility = clauses: +# The default visibility for all rtc_ targets is equivalent to "//*", or +# "all targets in webrtc can depend on this, nothing outside can". +# +# When overriding, the choices are: +# - visibility = [ "*" ] - public. Stuff outside webrtc can use this. +# - visibility = [ ":*" ] - directory private. +# As a general guideline, only targets in api/ should have public visibility. + import("//build/config/linux/pkg_config.gni") import("//build/config/sanitizers/sanitizers.gni") import("//third_party/google_benchmark/buildconfig.gni") @@ -39,7 +48,6 @@ if (!build_with_chromium) { if (rtc_include_tests) { deps += [ ":rtc_unittests", - ":slow_tests", ":video_engine_tests", ":voip_unittests", ":webrtc_nonparallel_tests", @@ -73,6 +81,13 @@ if (!build_with_chromium) { # see bugs.webrtc.org/11027#c5. deps += [ ":webrtc_lib_link_test" ] } + if (is_ios) { + deps += [ + "examples:apprtcmobile_tests", + "sdk:sdk_framework_unittests", + "sdk:sdk_unittests", + ] + } if (is_android) { deps += [ "examples:android_examples_junit_tests", @@ -84,7 +99,7 @@ if (!build_with_chromium) { } if (rtc_enable_protobuf) { deps += [ - "audio:low_bandwidth_audio_test", + "audio:low_bandwidth_audio_perf_test", "logging:rtc_event_log_rtp_dump", "tools_webrtc/perf:webrtc_dashboard_upload", ] @@ -118,15 +133,15 @@ config("common_inherited_config") { cflags = [] ldflags = [] - if (rtc_enable_symbol_export || is_component_build) { - defines = [ "WEBRTC_ENABLE_SYMBOL_EXPORT" ] - } - if (rtc_enable_objc_symbol_export) { - defines = [ "WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT" ] + if (rtc_dlog_always_on) { + defines += [ "DLOG_ALWAYS_ON" ] } - if (build_with_mozilla) { - defines += [ "WEBRTC_MOZILLA_BUILD" ] + if (rtc_enable_symbol_export || is_component_build) { + defines += [ "WEBRTC_ENABLE_SYMBOL_EXPORT" ] + } + if (rtc_enable_objc_symbol_export) { + defines += [ "WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT" ] } if (!rtc_builtin_ssl_root_certificates) { @@ -222,14 +237,6 @@ config("common_inherited_config") { } } -# TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning -# as soon as WebRTC compiles without it. -config("no_exit_time_destructors") { - if (is_clang) { - cflags = [ "-Wno-exit-time-destructors" ] - } -} - # TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning # as soon as WebRTC compiles without it. config("no_global_constructors") { @@ -269,6 +276,10 @@ config("common_config") { defines += [ "RTC_ENABLE_VP9" ] } + if (rtc_include_dav1d_in_internal_decoder_factory) { + defines += [ "RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY" ] + } + if (rtc_enable_sctp) { defines += [ "WEBRTC_HAVE_SCTP" ] } @@ -305,7 +316,10 @@ config("common_config") { defines += [ "WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE" ] } - cflags = [] + # TODO(webrtc:13219): Fix -Wshadow instances and enable. + if (is_clang) { + cflags += [ "-Wno-shadow" ] + } if (build_with_chromium) { defines += [ @@ -346,21 +360,10 @@ config("common_config") { "-Wundef", ] - # use_xcode_clang only refers to the iOS toolchain, host binaries use - # chromium's clang always. - if (!is_nacl && - (!use_xcode_clang || current_toolchain == host_toolchain)) { - # Flags NaCl (Clang 3.7) and Xcode 7.3 (Clang clang-703.0.31) do not - # recognize. + if (!is_nacl) { + # Flags NaCl (Clang 3.7) do not recognize. cflags += [ "-Wunused-lambda-capture" ] } - - if (use_xcode_clang) { - # This may be removed if the clang version in xcode > 12.4 includes the - # fix https://reviews.llvm.org/D73007. - # https://bugs.llvm.org/show_bug.cgi?id=44556 - cflags += [ "-Wno-range-loop-analysis" ] - } } if (is_win && !is_clang) { @@ -575,7 +578,9 @@ if (rtc_include_tests && !build_with_chromium) { "rtc_base/experiments:experiments_unittests", "rtc_base/system:file_wrapper_unittests", "rtc_base/task_utils:pending_task_safety_flag_unittests", + "rtc_base/task_utils:repeating_task_unittests", "rtc_base/task_utils:to_queued_task_unittests", + "rtc_base/units:units_unittests", "sdk:sdk_tests", "test:rtp_test_utils", "test:test_main", @@ -613,17 +618,6 @@ if (rtc_include_tests && !build_with_chromium) { } } - # This runs tests that must run in real time and therefore can take some - # time to execute. They are in a separate executable to avoid making the - # regular unittest suite too slow to run frequently. - rtc_test("slow_tests") { - testonly = true - deps = [ - "rtc_base/task_utils:repeating_task_unittests", - "test:test_main", - ] - } - # TODO(pbos): Rename test suite, this is no longer "just" for video targets. video_engine_tests_resources = [ "resources/foreman_cif_short.yuv", @@ -650,6 +644,7 @@ if (rtc_include_tests && !build_with_chromium) { "test:test_common", "test:test_main", "test:video_test_common", + "video:video_legacy_tests", "video:video_tests", "video/adaptation:video_adaptation_tests", ] @@ -746,6 +741,9 @@ group("poison_audio_codecs") { group("poison_default_task_queue") { } +group("poison_default_echo_detector") { +} + group("poison_rtc_json") { } diff --git a/DEPS b/DEPS index 506bbdbf36..e8f8a68070 100644 --- a/DEPS +++ b/DEPS @@ -10,40 +10,43 @@ vars = { # chromium waterfalls. More info at: crbug.com/570091. 'checkout_configuration': 'default', 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"', - 'chromium_revision': '48501b3f1824ea2f08759a0bcbe9408a7548bc7f', + 'chromium_revision': 'f55241d0144f3599fa90e4e6038b96634d2b4d42', # Keep the Chromium default of generating location tags. 'generate_location_tags': True, + + # ResultDB version + 'resultdb_version': 'git_revision:735a8a662d3874d8b1d795a40e46ea0f57b52758', } deps = { # TODO(kjellander): Move this to be Android-only once the libevent dependency # in base/third_party/libevent is solved. 'src/base': - 'https://chromium.googlesource.com/chromium/src/base@bdbd6f899ce50ac0efc87008384cf25a3eb0a03f', + 'https://chromium.googlesource.com/chromium/src/base@bee216d6736d6a471879c5fcd32c8aba863d4755', 'src/build': - 'https://chromium.googlesource.com/chromium/src/build@f90eed6a5e137d43526c24eecfd96382a3233ee3', + 'https://chromium.googlesource.com/chromium/src/build@b42b2033819f95b3e43f24e541938c671110966d', 'src/buildtools': - 'https://chromium.googlesource.com/chromium/src/buildtools@7ea3a871db68ae2cbbeaf5433a3192a799ef3c11', + 'https://chromium.googlesource.com/chromium/src/buildtools@d8c375426d8f7f4147f7d4109bb63c12655fb8d6', # Gradle 6.6.1. Used for testing Android Studio project generation for WebRTC. 'src/examples/androidtests/third_party/gradle': { 'url': 'https://chromium.googlesource.com/external/github.com/gradle/gradle.git@f2d1fb54a951d8b11d25748e4711bec8d128d7e3', 'condition': 'checkout_android', }, 'src/ios': { - 'url': 'https://chromium.googlesource.com/chromium/src/ios@1b17fd57e67672c041bab1fb04910bda42cb55bf', + 'url': 'https://chromium.googlesource.com/chromium/src/ios@043ae4b9e442fed9d4a53c88ab708775f8e079cd', 'condition': 'checkout_ios', }, 'src/testing': - 'https://chromium.googlesource.com/chromium/src/testing@59835db543d03c36bafe56fb10d6a8b68e377621', + 'https://chromium.googlesource.com/chromium/src/testing@c4769e51cb122096ea4cdc5ed6f7e57aa2315447', 'src/third_party': - 'https://chromium.googlesource.com/chromium/src/third_party@a299c990bc6b40774066cb84281975a0371e7a8b', + 'https://chromium.googlesource.com/chromium/src/third_party@7835795588c74a353beb1e03b74d45fabfdf295f', 'src/buildtools/linux64': { 'packages': [ { 'package': 'gn/gn/linux-amd64', - 'version': 'git_revision:69ec4fca1fa69ddadae13f9e6b7507efa0675263', + 'version': 'git_revision:bd99dbf98cbdefe18a4128189665c5761263bcfb', } ], 'dep_type': 'cipd', @@ -53,7 +56,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/mac-${{arch}}', - 'version': 'git_revision:69ec4fca1fa69ddadae13f9e6b7507efa0675263', + 'version': 'git_revision:bd99dbf98cbdefe18a4128189665c5761263bcfb', } ], 'dep_type': 'cipd', @@ -63,7 +66,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/windows-amd64', - 'version': 'git_revision:69ec4fca1fa69ddadae13f9e6b7507efa0675263', + 'version': 'git_revision:bd99dbf98cbdefe18a4128189665c5761263bcfb', } ], 'dep_type': 'cipd', @@ -71,13 +74,13 @@ deps = { }, 'src/buildtools/clang_format/script': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@99803d74e35962f63a775f29477882afd4d57d94', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@e435ad79c17b1888b34df88d6a30a094936e3836', 'src/buildtools/third_party/libc++/trunk': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@79a2e924d96e2fc1e4b937c42efd08898fa472d7', 'src/buildtools/third_party/libc++abi/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@7de86cbf37e0cf76ffe786ab6a21a728b1027bc2', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@93b8dcd57bd8ebe201ec24f7257339988ed2ef7c', 'src/buildtools/third_party/libunwind/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@edf77b2d2d2253052f1a3410c7a97e049fc70807', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@d1c7f92b8b0bff8d9f710ca40e44563a63db376e', 'src/tools/clang/dsymutil': { 'packages': [ @@ -100,12 +103,21 @@ deps = { 'condition': 'checkout_android', 'dep_type': 'cipd', }, + 'src/tools/resultdb': { + 'packages': [ + { + 'package': 'infra/tools/result_adapter/${{platform}}', + 'version': Var('resultdb_version'), + }, + ], + 'dep_type': 'cipd', + }, 'src/third_party/android_build_tools/aapt2': { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/aapt2', - 'version': 'O9eXFyC5ZkcYvDfHRLKPO1g1Xwf7M33wT3cuJtyfc0sC', + 'version': 'wicn5Ce1ay6ivbZ1GNFF0gRSS3NYv_7hJTPtVga3O-QC', }, ], 'condition': 'checkout_android', @@ -116,7 +128,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/bundletool', - 'version': 'nK0LSzIJHh_OqyIEMh3zye_Ad54jw57inqQF5jFJzdYC', + 'version': 'LoldiQDpZ0uTdAm5EPgZ8hBJ3La2KlTWLuaRxE7eDigC', }, ], 'condition': 'checkout_android', @@ -124,11 +136,11 @@ deps = { }, 'src/third_party/boringssl/src': - 'https://boringssl.googlesource.com/boringssl.git@dddb60eb9700110835ff6e2b429de40a17006429', + 'https://boringssl.googlesource.com/boringssl.git@4d955d20d27bcf3ae71df091ad17d95229a7eb56', 'src/third_party/breakpad/breakpad': - 'https://chromium.googlesource.com/breakpad/breakpad.git@7933ec0a69bac134b7cee4b60a5dc80743b2b1a9', + 'https://chromium.googlesource.com/breakpad/breakpad.git@08bd844599bf04c71707e8f59a8013a941264695', 'src/third_party/catapult': - 'https://chromium.googlesource.com/catapult.git@d9a9ebbe89eaa7f479612b4e23ee38a313197d5e', + 'https://chromium.googlesource.com/catapult.git@389f33bb40a3345b73a68613178c789476ceaecf', 'src/third_party/ced/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/compact_enc_det.git@ba412eaaacd3186085babcd901679a48863c7dd5', }, @@ -137,24 +149,27 @@ deps = { 'src/third_party/crc32c/src': 'https://chromium.googlesource.com/external/github.com/google/crc32c.git@fa5ade41ee480003d9c5af6f43567ba22e4e17e6', 'src/third_party/depot_tools': - 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@789dfc223b9dfd2fb377a7c4bcb351e63167554d', + 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@bc93924b3b2b84515d91ba49bb60ac0b89d928e1', 'src/third_party/ffmpeg': - 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@1f33e234be4e2823c663d02b3baa3e7a7cfc33cb', + 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@574c39cce3231c69bc9a02ac475c27d944bdb113', 'src/third_party/findbugs': { 'url': 'https://chromium.googlesource.com/chromium/deps/findbugs.git@4275d9ac8610db6b1bc9a5e887f97e41b33fac67', 'condition': 'checkout_android', }, + 'src/third_party/grpc/src': { + 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@2a0d6234cb2ccebb265c035ffd09ecc9a347b4bf', + }, # Used for embedded builds. CrOS & Linux use the system version. 'src/third_party/fontconfig/src': { 'url': 'https://chromium.googlesource.com/external/fontconfig.git@452be8125f0e2a18a7dfef469e05d19374d36307', 'condition': 'checkout_linux', }, 'src/third_party/freetype/src': - 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@7482c98f1533d1b685113a060f4588f703a81b79', + 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@53dfdcd8198d2b3201a23c4bad9190519ba918db', 'src/third_party/harfbuzz-ng/src': - 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@280366ba6af14fbcacbc49e6aa1c12d83e531ad1', + 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@965cf1d66589b0db60e75961cc58f5a65521078e', 'src/third_party/google_benchmark/src': { - 'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@e991355c02b93fe17713efe04cbc2e278e00fdbd', + 'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@f730846b0a3c0dc0699978846fb14ffb2fad0bdc', }, # WebRTC-only dependency (not present in Chromium). 'src/third_party/gtest-parallel': @@ -170,9 +185,9 @@ deps = { 'dep_type': 'cipd', }, 'src/third_party/googletest/src': - 'https://chromium.googlesource.com/external/github.com/google/googletest.git@955c7f837efad184ec63e771c42542d37545eaef', + 'https://chromium.googlesource.com/external/github.com/google/googletest.git@b007c54f2944e193ac44fba1bc997cb65826a0b9', 'src/third_party/icu': { - 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@ece15d049f2d360721716089372e3749fb89e0f4', + 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@a9359a84a3969b3019db7d62899afb19642eefcd', }, 'src/third_party/jdk': { 'packages': [ @@ -204,21 +219,23 @@ deps = { 'src/third_party/libFuzzer/src': 'https://chromium.googlesource.com/chromium/llvm-project/compiler-rt/lib/fuzzer.git@debe7d2d1982e540fbd6bd78604bf001753f9e74', 'src/third_party/libjpeg_turbo': - 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@ff19e5b2e176c61d552f68768e0e051867745321', + 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@22f1a22c99e9dde8cd3c72ead333f425c5a7aa77', 'src/third_party/libsrtp': 'https://chromium.googlesource.com/chromium/deps/libsrtp.git@5b7c744eb8310250ccc534f3f86a2015b3887a0a', + 'src/third_party/dav1d/libdav1d': + 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@56e7ffc0dbe44970a4d55ec2241824c67add9dd5', 'src/third_party/libaom/source/libaom': - 'https://aomedia.googlesource.com/aom.git@da0b537ee186143863ba7b41f004b2ecbb7b66b2', + 'https://aomedia.googlesource.com/aom.git@ee1ed1ccf2b9ecedd6aee438eafc7cc61c23342d', 'src/third_party/libunwindstack': { - 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@b34a0059a648f179ef05da2c0927f564bdaea2b3', + 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@6868358481bb1e5e20d155c1084dc436c88b5e6b', 'condition': 'checkout_android', }, 'src/third_party/perfetto': - 'https://android.googlesource.com/platform/external/perfetto.git@8420673b4cf236574656e59f429b1c38d7029ba6', + 'https://android.googlesource.com/platform/external/perfetto.git@6dadd2c3b267bd4776762dc5c0a4acb391abbc5e', 'src/third_party/libvpx/source/libvpx': - 'https://chromium.googlesource.com/webm/libvpx.git@15a75b45304248f746634b43763c496322bf8968', + 'https://chromium.googlesource.com/webm/libvpx.git@df0d06de6d3b64e35b9e75ad72c571af061bc7b3', 'src/third_party/libyuv': - 'https://chromium.googlesource.com/libyuv/libyuv.git@49ebc996aa8c4bdf89c1b5ea461eb677234c61cc', + 'https://chromium.googlesource.com/libyuv/libyuv.git@3aebf69d668177e7ee6dbbe0025e5c3dbb525ff2', 'src/third_party/lss': { 'url': 'https://chromium.googlesource.com/linux-syscall-support.git@92a65a8f5d705d1928874420c8d0d15bde8c89e5', 'condition': 'checkout_android or checkout_linux', @@ -230,16 +247,16 @@ deps = { # Used by boringssl. 'src/third_party/nasm': { - 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@4e6fe9d1549e4ffb6c804494573e404849dfe7de' + 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@9215e8e1d0fe474ffd3e16c1a07a0f97089e6224' }, 'src/third_party/openh264/src': - 'https://chromium.googlesource.com/external/github.com/cisco/openh264@3dd5b80bc4f172dd82925bb259cb7c82348409c5', + 'https://chromium.googlesource.com/external/github.com/cisco/openh264@b52786888ddce9d6bc06b7825ba9bffc65924e0c', 'src/third_party/r8': { 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'dvPOJ_8iAF6OHGO79d86VbJjyKj7Xn0SFxlVVC9LHdcC', + 'version': 'ovozeRSDDfERnEFpDo_WS6OYOcEF7oT1JzGxCSf-g0kC', }, ], 'condition': 'checkout_android', @@ -264,14 +281,14 @@ deps = { 'condition': 'checkout_android', }, 'src/third_party/usrsctp/usrsctplib': - 'https://chromium.googlesource.com/external/github.com/sctplab/usrsctp@bdf3dd3f284ebbd2a6fa16f12284d7ac8b3ad8b8', + 'https://chromium.googlesource.com/external/github.com/sctplab/usrsctp@62d7d0c928c9a040dce96aa2f16c00e7e67d59cb', # Dependency used by libjpeg-turbo. 'src/third_party/yasm/binaries': { 'url': 'https://chromium.googlesource.com/chromium/deps/yasm/binaries.git@52f9b3f4b0aa06da24ef8b123058bb61ee468881', 'condition': 'checkout_win', }, 'src/tools': - 'https://chromium.googlesource.com/chromium/src/tools@e3721e5cf9c0b510e25891d9b5915cbd67d607e2', + 'https://chromium.googlesource.com/chromium/src/tools@3c7dc0d25c91195a7d9b31947b14739d57f6c728', 'src/third_party/accessibility_test_framework': { 'packages': [ @@ -295,17 +312,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/bazel': { - 'packages': [ - { - 'package': 'chromium/third_party/bazel', - 'version': 'VjMsf48QUWw8n7XtJP2AuSjIGmbQeYdWdwyxVvIRLmAC', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - 'src/third_party/bouncycastle': { 'packages': [ { @@ -362,7 +368,7 @@ deps = { }, 'src/third_party/android_ndk': { - 'url': 'https://chromium.googlesource.com/android_ndk.git@401019bf85744311b26c88ced255cd53401af8b7', + 'url': 'https://chromium.googlesource.com/android_ndk.git@9644104c8cf85bf1bdce5b1c0691e9778572c3f8', 'condition': 'checkout_android', }, @@ -370,7 +376,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'TnotTDnWGUJDh0mSOMrgnIwzbfWFiwo2NTtr2SlUJ0MC', + 'version': '4144ITIgXUisP3mBnV8td3mdaIKKku5UW_bQjgoP9r8C', }, ], 'condition': 'checkout_android', @@ -397,19 +403,19 @@ deps = { }, { 'package': 'chromium/third_party/android_sdk/public/platform-tools', - 'version': 'qi_k82nm6j9nz4dQosOoqXew4_TFAy8rcGOHDLptx1sC', + 'version': 'g7n_-r6yJd_SGRklujGB1wEt8iyr77FZTUJVS9w6O34C', }, { 'package': 'chromium/third_party/android_sdk/public/platforms/android-31', 'version': 'lL3IGexKjYlwjO_1Ga-xwxgwbE_w-lmi2Zi1uOlWUIAC', }, { - 'package': 'chromium/third_party/android_sdk/public/sources/android-30', - 'version': 'n7svc8KYah-i4s8zwkVa85SI3_H0WFOniP0mpwNdFO0C', + 'package': 'chromium/third_party/android_sdk/public/sources/android-31', + 'version': '_a_BcnANjPYw5mSKlNHa7GFY8yc1kdqj2rmQgac7yUcC', }, { 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', - 'version': 'ZT3JmI6GMG4YVcZ1OtECRVMOLLJAWAdPbi-OclubJLMC', + 'version': 'PGPmqJtSIQ84If155ba7iTU846h5WJ-bL5d_OoUWEWYC', }, ], 'condition': 'checkout_android', @@ -442,7 +448,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/robolectric', - 'version': 'iC6RDM5EH3GEAzR-1shW_Mg0FeeNE5shq1okkFfuuNQC', + 'version': 'WZ96VJuhBM63xzHb-_E72Tf46M9yIbfia6basI1YG4EC', }, ], 'condition': 'checkout_android', @@ -464,7 +470,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/turbine', - 'version': 'Om6yIEXgJxuqghErK29h9RcMH6VaymMbxwScwXmcN6EC', + 'version': 'Go9J3Mz5ankZAgxmn5GxeXKdEDV73zaZp2ojNfGC1RQC', }, ], 'condition': 'checkout_android', @@ -475,34 +481,16 @@ deps = { 'packages': [ { 'package': 'infra/tools/luci/isolate/${{platform}}', - 'version': 'git_revision:7f42370cb3b75398bdb9ae0aabe215a70d40cd31', - }, - { - 'package': 'infra/tools/luci/isolated/${{platform}}', - 'version': 'git_revision:7f42370cb3b75398bdb9ae0aabe215a70d40cd31', + 'version': 'git_revision:cb424e70e75136736a86359ef070aa96425fe7a3', }, { 'package': 'infra/tools/luci/swarming/${{platform}}', - 'version': 'git_revision:7f42370cb3b75398bdb9ae0aabe215a70d40cd31', + 'version': 'git_revision:cb424e70e75136736a86359ef070aa96425fe7a3', }, ], 'dep_type': 'cipd', }, - # TODO(crbug.com/1184780) Move this back to ANDROID_DEPS Generated Code - # section once org_robolectric_shadows_multidex is updated to a new version - # that does not need jetify. - 'src/third_party/android_deps/libs/org_robolectric_shadows_multidex': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_multidex', - 'version': 'version:4.3.1-cr1', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - # Everything coming after this is automatically updated by the auto-roller. # === ANDROID_DEPS Generated Code Start === # Generated by //third_party/android_deps/fetch_all.py @@ -510,7 +498,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_core_common', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -521,7 +509,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_core_runtime', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -532,7 +520,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_common', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -543,7 +531,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_common_java8', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -554,7 +542,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_livedata', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -565,7 +553,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_livedata_core', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -576,7 +564,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_runtime', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -587,29 +575,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_viewmodel', - 'version': 'version:2@1.1.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/backport_util_concurrent_backport_util_concurrent': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/backport_util_concurrent_backport_util_concurrent', - 'version': 'version:2@3.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/classworlds_classworlds': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/classworlds_classworlds', - 'version': 'version:2@1.1-alpha-2.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -620,7 +586,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_animated_vector_drawable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -631,7 +597,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_appcompat_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -642,7 +608,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_asynclayoutinflater', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -653,7 +619,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_cardview_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -664,7 +630,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_collections', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -675,7 +641,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_coordinatorlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -686,7 +652,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_cursoradapter', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -697,7 +663,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_customview', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -708,7 +674,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_design', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -719,7 +685,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_documentfile', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -730,7 +696,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_drawerlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -741,7 +707,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_interpolator', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -752,7 +718,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_loader', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -763,7 +729,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_localbroadcastmanager', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -774,7 +740,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_multidex', - 'version': 'version:2@1.0.0.cr0', + 'version': 'version:2@1.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -785,7 +751,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_print', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -796,7 +762,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_recyclerview_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -807,7 +773,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_slidingpanelayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -818,7 +784,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_annotations', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -829,7 +795,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_compat', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -840,7 +806,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_core_ui', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -851,7 +817,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_core_utils', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -862,7 +828,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_fragment', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -873,7 +839,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_media_compat', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -884,7 +850,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_v4', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -895,7 +861,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_vector_drawable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -906,7 +872,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_swiperefreshlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -917,7 +883,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_transition', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -928,7 +894,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_versionedparcelable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -939,7 +905,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_viewpager', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -950,7 +916,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_common', - 'version': 'version:2@30.0.0-alpha10.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -961,7 +927,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.5.cr1', }, ], 'condition': 'checkout_android', @@ -972,7 +938,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.5.cr1', }, ], 'condition': 'checkout_android', @@ -983,7 +949,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api', - 'version': 'version:2@30.0.0-alpha10.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -994,7 +960,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_sdk_common', - 'version': 'version:2@30.0.0-alpha10.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -1005,7 +971,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_github_ben_manes_caffeine_caffeine', - 'version': 'version:2@2.8.8.cr0', + 'version': 'version:2@2.8.8.cr1', }, ], 'condition': 'checkout_android', @@ -1016,7 +982,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_github_kevinstern_software_and_algorithms', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1027,7 +993,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_datatransport_transport_api', - 'version': 'version:2@2.2.1.cr0', + 'version': 'version:2@2.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -1038,7 +1004,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1049,7 +1015,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth_api_phone', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@17.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -1060,7 +1026,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth_base', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1071,7 +1037,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_base', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@17.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -1082,7 +1048,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_basement', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@17.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -1093,7 +1059,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cast', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1104,7 +1070,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cast_framework', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1115,7 +1081,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_clearcut', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1126,7 +1092,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cloud_messaging', - 'version': 'version:2@16.0.0.cr0', + 'version': 'version:2@16.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1137,7 +1103,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_fido', - 'version': 'version:2@19.0.0-beta.cr0', + 'version': 'version:2@19.0.0-beta.cr1', }, ], 'condition': 'checkout_android', @@ -1148,7 +1114,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_flags', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1159,7 +1125,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_gcm', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1170,7 +1136,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_iid', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1181,7 +1147,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_instantapps', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1192,7 +1158,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_location', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1203,7 +1169,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_phenotype', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1214,7 +1180,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_places_placereport', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1225,7 +1191,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_stats', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1236,7 +1202,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_tasks', - 'version': 'version:2@17.2.0.cr0', + 'version': 'version:2@17.2.0.cr1', }, ], 'condition': 'checkout_android', @@ -1247,7 +1213,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_vision', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@18.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1258,7 +1224,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_vision_common', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@18.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1269,7 +1235,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_material_material', - 'version': 'version:2@1.5.0-alpha02.cr0', + 'version': 'version:2@1.6.0-alpha01.cr1', }, ], 'condition': 'checkout_android', @@ -1280,7 +1246,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core', - 'version': 'version:2@1.10.0.cr0', + 'version': 'version:2@1.10.0.cr1', }, ], 'condition': 'checkout_android', @@ -1291,7 +1257,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_auto_common', - 'version': 'version:2@0.10.cr0', + 'version': 'version:2@1.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -1302,7 +1268,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_service_auto_service', - 'version': 'version:2@1.0-rc6.cr0', + 'version': 'version:2@1.0-rc6.cr1', }, ], 'condition': 'checkout_android', @@ -1313,7 +1279,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_service_auto_service_annotations', - 'version': 'version:2@1.0-rc6.cr0', + 'version': 'version:2@1.0-rc6.cr1', }, ], 'condition': 'checkout_android', @@ -1324,18 +1290,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_value_auto_value_annotations', - 'version': 'version:2@1.7.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/com_google_code_findbugs_jformatstring': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/com_google_code_findbugs_jformatstring', - 'version': 'version:2@3.0.0.cr0', + 'version': 'version:2@1.9.cr1', }, ], 'condition': 'checkout_android', @@ -1346,7 +1301,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_code_findbugs_jsr305', - 'version': 'version:2@3.0.2.cr0', + 'version': 'version:2@3.0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1357,7 +1312,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_code_gson_gson', - 'version': 'version:2@2.8.0.cr0', + 'version': 'version:2@2.8.0.cr1', }, ], 'condition': 'checkout_android', @@ -1368,7 +1323,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1379,7 +1334,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_compiler', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1390,7 +1345,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_producers', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1401,7 +1356,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_spi', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1412,7 +1367,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation', - 'version': 'version:2@2.7.1.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1423,7 +1378,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations', - 'version': 'version:2@2.7.1.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1434,7 +1389,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_check_api', - 'version': 'version:2@2.7.1.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1445,7 +1400,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_core', - 'version': 'version:2@2.7.1.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1456,7 +1411,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations', - 'version': 'version:2@2.7.1.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1467,7 +1422,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_javac', - 'version': 'version:2@9+181-r4173-1.cr0', + 'version': 'version:2@9+181-r4173-1.cr1', }, ], 'condition': 'checkout_android', @@ -1478,7 +1433,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_javac_shaded', - 'version': 'version:2@9-dev-r4023-3.cr0', + 'version': 'version:2@9-dev-r4023-3.cr1', }, ], 'condition': 'checkout_android', @@ -1489,7 +1444,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_annotations', - 'version': 'version:2@16.0.0.cr0', + 'version': 'version:2@16.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1500,7 +1455,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_common', - 'version': 'version:2@19.5.0.cr0', + 'version': 'version:2@19.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -1511,7 +1466,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_components', - 'version': 'version:2@16.1.0.cr0', + 'version': 'version:2@16.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1522,7 +1477,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_encoders', - 'version': 'version:2@16.1.0.cr0', + 'version': 'version:2@16.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1533,7 +1488,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_encoders_json', - 'version': 'version:2@17.1.0.cr0', + 'version': 'version:2@17.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1544,7 +1499,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_iid', - 'version': 'version:2@21.0.1.cr0', + 'version': 'version:2@21.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1555,7 +1510,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_iid_interop', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1566,7 +1521,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_installations', - 'version': 'version:2@16.3.5.cr0', + 'version': 'version:2@16.3.5.cr1', }, ], 'condition': 'checkout_android', @@ -1577,7 +1532,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_installations_interop', - 'version': 'version:2@16.0.1.cr0', + 'version': 'version:2@16.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1588,7 +1543,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_measurement_connector', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@18.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1599,7 +1554,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_messaging', - 'version': 'version:2@21.0.1.cr0', + 'version': 'version:2@21.0.1.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/com_google_flatbuffers_flatbuffers_java': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/com_google_flatbuffers_flatbuffers_java', + 'version': 'version:2@2.0.3.cr1', }, ], 'condition': 'checkout_android', @@ -1610,7 +1576,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_googlejavaformat_google_java_format', - 'version': 'version:2@1.5.cr0', + 'version': 'version:2@1.5.cr1', }, ], 'condition': 'checkout_android', @@ -1621,7 +1587,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_failureaccess', - 'version': 'version:2@1.0.1.cr0', + 'version': 'version:2@1.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1632,7 +1598,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava', - 'version': 'version:2@30.1-jre.cr0', + 'version': 'version:2@31.0.1-jre.cr1', }, ], 'condition': 'checkout_android', @@ -1643,7 +1609,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava_android', - 'version': 'version:2@30.1-android.cr0', + 'version': 'version:2@31.0-android.cr1', }, ], 'condition': 'checkout_android', @@ -1654,7 +1620,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_listenablefuture', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1665,7 +1631,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations', - 'version': 'version:2@1.3.cr0', + 'version': 'version:2@1.3.cr1', }, ], 'condition': 'checkout_android', @@ -1676,7 +1642,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_java', - 'version': 'version:2@3.4.0.cr0', + 'version': 'version:2@3.19.2.cr1', }, ], 'condition': 'checkout_android', @@ -1687,7 +1653,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_javalite', - 'version': 'version:2@3.13.0.cr0', + 'version': 'version:2@3.19.3.cr1', }, ], 'condition': 'checkout_android', @@ -1698,7 +1664,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils', - 'version': 'version:2@1.3.0.cr0', + 'version': 'version:2@1.3.0.cr1', }, ], 'condition': 'checkout_android', @@ -1709,7 +1675,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_squareup_javapoet', - 'version': 'version:2@1.13.0.cr0', + 'version': 'version:2@1.13.0.cr1', }, ], 'condition': 'checkout_android', @@ -1720,7 +1686,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_squareup_javawriter', - 'version': 'version:2@2.1.1.cr0', + 'version': 'version:2@2.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -1731,7 +1697,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/io_github_java_diff_utils_java_diff_utils', - 'version': 'version:2@4.0.cr0', + 'version': 'version:2@4.0.cr1', }, ], 'condition': 'checkout_android', @@ -1742,7 +1708,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_annotation_javax_annotation_api', - 'version': 'version:2@1.3.2.cr0', + 'version': 'version:2@1.3.2.cr1', }, ], 'condition': 'checkout_android', @@ -1753,7 +1719,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_annotation_jsr250_api', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1764,29 +1730,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_inject_javax_inject', - 'version': 'version:2@1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/nekohtml_nekohtml': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/nekohtml_nekohtml', - 'version': 'version:2@1.9.6.2.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/nekohtml_xercesminimal': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/nekohtml_xercesminimal', - 'version': 'version:2@1.9.6.2.cr0', + 'version': 'version:2@1.cr1', }, ], 'condition': 'checkout_android', @@ -1797,7 +1741,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/net_ltgt_gradle_incap_incap', - 'version': 'version:2@0.2.cr0', + 'version': 'version:2@0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1808,183 +1752,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/net_sf_kxml_kxml2', - 'version': 'version:2@2.3.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_ant_ant': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_ant_ant', - 'version': 'version:2@1.8.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_ant_ant_launcher': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_ant_ant_launcher', - 'version': 'version:2@1.8.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_ant_tasks': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_ant_tasks', - 'version': 'version:2@2.1.3.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_artifact': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_artifact', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_artifact_manager': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_artifact_manager', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_error_diagnostics': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_error_diagnostics', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_model': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_model', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_plugin_registry': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_plugin_registry', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_profile': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_profile', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_project': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_project', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_repository_metadata': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_repository_metadata', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_settings': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_settings', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_file': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_file', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_lightweight': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_lightweight', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_shared': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_shared', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_provider_api': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_provider_api', - 'version': 'version:2@1.0-beta-6.cr0', + 'version': 'version:2@2.3.0.cr1', }, ], 'condition': 'checkout_android', @@ -1995,7 +1763,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup', - 'version': 'version:2@1.2.1.cr0', + 'version': 'version:2@1.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -2006,7 +1774,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual', - 'version': 'version:2@2.5.5.cr0', + 'version': 'version:2@2.5.5.cr1', }, ], 'condition': 'checkout_android', @@ -2017,18 +1785,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual', - 'version': 'version:2@3.8.0.cr0', + 'version': 'version:2@3.12.0.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_checkerframework_dataflow_shaded': { + 'src/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_dataflow_shaded', - 'version': 'version:2@3.11.0.cr0', + 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone', + 'version': 'version:2@3.15.0.cr1', }, ], 'condition': 'checkout_android', @@ -2039,40 +1807,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations', - 'version': 'version:2@1.17.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_container_default': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_container_default', - 'version': 'version:2@1.0-alpha-9-stable-1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_interpolation': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_interpolation', - 'version': 'version:2@1.11.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_utils': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_utils', - 'version': 'version:2@1.5.15.cr0', + 'version': 'version:2@1.17.cr1', }, ], 'condition': 'checkout_android', @@ -2083,7 +1818,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit', - 'version': 'version:2@4.4.1.201607150455-r.cr0', + 'version': 'version:2@4.4.1.201607150455-r.cr1', }, ], 'condition': 'checkout_android', @@ -2094,7 +1829,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_annotations', - 'version': 'version:2@13.0.cr0', + 'version': 'version:2@13.0.cr1', }, ], 'condition': 'checkout_android', @@ -2105,7 +1840,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib', - 'version': 'version:2@1.5.30.cr0', + 'version': 'version:2@1.6.10.cr1', }, ], 'condition': 'checkout_android', @@ -2116,7 +1851,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_common', - 'version': 'version:2@1.5.30.cr0', + 'version': 'version:2@1.6.10.cr1', }, ], 'condition': 'checkout_android', @@ -2127,7 +1862,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk7', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -2138,7 +1873,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk8', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -2149,7 +1884,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_android', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -2160,7 +1895,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -2171,7 +1906,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_metadata_jvm', - 'version': 'version:2@0.1.0.cr0', + 'version': 'version:2@0.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -2182,7 +1917,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2193,7 +1928,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_analysis', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2204,7 +1939,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_commons', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2215,7 +1950,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_tree', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2226,7 +1961,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_util', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2237,7 +1972,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_pcollections_pcollections', - 'version': 'version:2@2.1.2.cr0', + 'version': 'version:2@3.1.4.cr1', }, ], 'condition': 'checkout_android', @@ -2248,7 +1983,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_annotations', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2259,7 +1994,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_junit', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_robolectric_nativeruntime': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_robolectric_nativeruntime', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2270,7 +2016,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_pluginapi', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2281,7 +2027,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_plugins_maven_dependency_resolver', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2292,7 +2038,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_resources', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2303,7 +2049,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_robolectric', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2314,7 +2060,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_sandbox', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2325,7 +2071,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadowapi', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2336,7 +2082,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_framework', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_robolectric_shadows_multidex': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_multidex', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2347,7 +2104,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_playservices', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2358,7 +2115,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2369,7 +2126,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils_reflector', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2387,7 +2144,7 @@ hooks = [ 'name': 'landmines', 'pattern': '.', 'action': [ - 'python', + 'python3', 'src/build/landmines.py', '--landmine-scripts', 'src/tools_webrtc/get_landmines.py', @@ -2401,7 +2158,7 @@ hooks = [ 'name': 'disable_depot_tools_selfupdate', 'pattern': '.', 'action': [ - 'python', + 'python3', 'src/third_party/depot_tools/update_depot_tools_toggle.py', '--disable', ], @@ -2410,14 +2167,14 @@ hooks = [ 'name': 'sysroot_arm', 'pattern': '.', 'condition': 'checkout_linux and checkout_arm', - 'action': ['python', 'src/build/linux/sysroot_scripts/install-sysroot.py', + 'action': ['python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=arm'], }, { 'name': 'sysroot_arm64', 'pattern': '.', 'condition': 'checkout_linux and checkout_arm64', - 'action': ['python', 'src/build/linux/sysroot_scripts/install-sysroot.py', + 'action': ['python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=arm64'], }, { @@ -2425,7 +2182,7 @@ hooks = [ 'pattern': '.', 'condition': 'checkout_linux and (checkout_x86 or checkout_x64)', # TODO(mbonadei): change to --arch=x86. - 'action': ['python', 'src/build/linux/sysroot_scripts/install-sysroot.py', + 'action': ['python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=i386'], }, { @@ -2433,7 +2190,7 @@ hooks = [ 'pattern': '.', 'condition': 'checkout_linux and checkout_mips', # TODO(mbonadei): change to --arch=mips. - 'action': ['python', 'src/build/linux/sysroot_scripts/install-sysroot.py', + 'action': ['python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=mipsel'], }, { @@ -2441,7 +2198,7 @@ hooks = [ 'pattern': '.', 'condition': 'checkout_linux and checkout_x64', # TODO(mbonadei): change to --arch=x64. - 'action': ['python', 'src/build/linux/sysroot_scripts/install-sysroot.py', + 'action': ['python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=amd64'], }, { @@ -2449,7 +2206,7 @@ hooks = [ 'name': 'ciopfs_linux', 'pattern': '.', 'condition': 'checkout_win and host_os == "linux"', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--no_auth', @@ -2462,26 +2219,26 @@ hooks = [ 'name': 'win_toolchain', 'pattern': '.', 'condition': 'checkout_win', - 'action': ['python', 'src/build/vs_toolchain.py', 'update', '--force'], + 'action': ['python3', 'src/build/vs_toolchain.py', 'update', '--force'], }, { # Update the Mac toolchain if necessary. 'name': 'mac_toolchain', 'pattern': '.', 'condition': 'checkout_mac', - 'action': ['python', 'src/build/mac_toolchain.py'], + 'action': ['python3', 'src/build/mac_toolchain.py'], }, { # Note: On Win, this should run after win_toolchain, as it may use it. 'name': 'clang', 'pattern': '.', - 'action': ['python', 'src/tools/clang/scripts/update.py'], + 'action': ['python3', 'src/tools/clang/scripts/update.py'], }, { # Update LASTCHANGE. 'name': 'lastchange', 'pattern': '.', - 'action': ['python', 'src/build/util/lastchange.py', + 'action': ['python3', 'src/build/util/lastchange.py', '-o', 'src/build/util/LASTCHANGE'], }, # Pull clang-format binaries using checked-in hashes. @@ -2526,7 +2283,7 @@ hooks = [ 'name': 'rc_win', 'pattern': '.', 'condition': 'checkout_win and host_os == "win"', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--no_auth', @@ -2538,7 +2295,7 @@ hooks = [ 'name': 'rc_mac', 'pattern': '.', 'condition': 'checkout_win and host_os == "mac"', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--no_auth', @@ -2550,7 +2307,7 @@ hooks = [ 'name': 'rc_linux', 'pattern': '.', 'condition': 'checkout_win and host_os == "linux"', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--no_auth', @@ -2573,24 +2330,24 @@ hooks = [ 'name': 'msan_chained_origins', 'pattern': '.', 'condition': 'checkout_instrumented_libraries', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', "--no_resume", "--no_auth", "--bucket", "chromium-instrumented-libraries", - "-s", "src/third_party/instrumented_libraries/binaries/msan-chained-origins-trusty.tgz.sha1", + "-s", "src/third_party/instrumented_libraries/binaries/msan-chained-origins.tgz.sha1", ], }, { 'name': 'msan_no_origins', 'pattern': '.', 'condition': 'checkout_instrumented_libraries', - 'action': [ 'python', + 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', "--no_resume", "--no_auth", "--bucket", "chromium-instrumented-libraries", - "-s", "src/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1", + "-s", "src/third_party/instrumented_libraries/binaries/msan-no-origins.tgz.sha1", ], }, { @@ -2609,7 +2366,7 @@ hooks = [ 'name': 'Generate component metadata for tests', 'pattern': '.', 'action': [ - 'vpython', + 'vpython3', 'src/testing/generate_location_tags.py', '--out', 'src/testing/location_tags.json', @@ -2619,8 +2376,8 @@ hooks = [ { 'name': 'vpython_common', 'pattern': '.', - 'action': [ 'vpython', - '-vpython-spec', 'src/.vpython', + 'action': [ 'vpython3', + '-vpython-spec', 'src/.vpython3', '-vpython-tool', 'install', ], }, @@ -2654,6 +2411,7 @@ include_rules = [ "+absl/base/config.h", "+absl/base/const_init.h", "+absl/base/macros.h", + "+absl/cleanup/cleanup.h", "+absl/container/inlined_vector.h", "+absl/functional/bind_front.h", "+absl/memory/memory.h", diff --git a/ENG_REVIEW_OWNERS b/ENG_REVIEW_OWNERS index b06ad40d0f..c509a278c1 100644 --- a/ENG_REVIEW_OWNERS +++ b/ENG_REVIEW_OWNERS @@ -7,4 +7,3 @@ danilchap@webrtc.org mbonadei@webrtc.org -phoglund@webrtc.org diff --git a/OWNERS b/OWNERS index 98eef283b7..6ae4b59a95 100644 --- a/OWNERS +++ b/OWNERS @@ -10,9 +10,13 @@ per-file .../BUILD.gn=mbonadei@webrtc.org per-file *.gni=mbonadei@webrtc.org per-file .../*.gni=mbonadei@webrtc.org per-file .vpython=mbonadei@webrtc.org +per-file .vpython3=mbonadei@webrtc.org per-file AUTHORS=* per-file DEPS=* per-file pylintrc=mbonadei@webrtc.org per-file WATCHLISTS=* per-file native-api.md=mbonadei@webrtc.org per-file ....lua=titovartem@webrtc.org +per-file .style.yapf=jleconte@webrtc.org +per-file *.py=jansson@webrtc.org +per-file *.py=jleconte@webrtc.org diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 0591c1bab9..f01338f2ef 100755 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -1,3 +1,5 @@ +#!/usr/bin/env vpython3 + # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. # # Use of this source code is governed by a BSD-style license @@ -13,6 +15,9 @@ import sys from collections import defaultdict from contextlib import contextmanager +# Runs PRESUBMIT.py in py3 mode by git cl presubmit. +USE_PYTHON3 = True + # Files and directories that are *skipped* by cpplint in the presubmit script. CPPLINT_EXCEPTIONS = [ 'api/video_codecs/video_decoder.h', @@ -31,6 +36,9 @@ CPPLINT_EXCEPTIONS = [ 'modules/video_capture', 'p2p/base/pseudo_tcp.cc', 'p2p/base/pseudo_tcp.h', + 'PRESUBMIT.py', + 'presubmit_test_mocks.py', + 'presubmit_test.py', 'rtc_base', 'sdk/android/src/jni', 'sdk/objc', @@ -115,42 +123,42 @@ FILE_PATH_RE = re.compile(r'"(?P(\w|\/)+)(?P\.\w+)"') def FindSrcDirPath(starting_dir): - """Returns the abs path to the src/ dir of the project.""" - src_dir = starting_dir - while os.path.basename(src_dir) != 'src': - src_dir = os.path.normpath(os.path.join(src_dir, os.pardir)) - return src_dir + """Returns the abs path to the src/ dir of the project.""" + src_dir = starting_dir + while os.path.basename(src_dir) != 'src': + src_dir = os.path.normpath(os.path.join(src_dir, os.pardir)) + return src_dir @contextmanager def _AddToPath(*paths): - original_sys_path = sys.path - sys.path.extend(paths) - try: - yield - finally: - # Restore sys.path to what it was before. - sys.path = original_sys_path + original_sys_path = sys.path + sys.path.extend(paths) + try: + yield + finally: + # Restore sys.path to what it was before. + sys.path = original_sys_path def VerifyNativeApiHeadersListIsValid(input_api, output_api): - """Ensures the list of native API header directories is up to date.""" - non_existing_paths = [] - native_api_full_paths = [ - input_api.os_path.join(input_api.PresubmitLocalPath(), - *path.split('/')) for path in API_DIRS + """Ensures the list of native API header directories is up to date.""" + non_existing_paths = [] + native_api_full_paths = [ + input_api.os_path.join(input_api.PresubmitLocalPath(), *path.split('/')) + for path in API_DIRS + ] + for path in native_api_full_paths: + if not os.path.isdir(path): + non_existing_paths.append(path) + if non_existing_paths: + return [ + output_api.PresubmitError( + 'Directories to native API headers have changed which has made ' + 'the list in PRESUBMIT.py outdated.\nPlease update it to the ' + 'current location of our native APIs.', non_existing_paths) ] - for path in native_api_full_paths: - if not os.path.isdir(path): - non_existing_paths.append(path) - if non_existing_paths: - return [ - output_api.PresubmitError( - 'Directories to native API headers have changed which has made ' - 'the list in PRESUBMIT.py outdated.\nPlease update it to the ' - 'current location of our native APIs.', non_existing_paths) - ] - return [] + return [] API_CHANGE_MSG = """ @@ -174,610 +182,592 @@ Related files: def CheckNativeApiHeaderChanges(input_api, output_api): - """Checks to remind proper changing of native APIs.""" - files = [] - source_file_filter = lambda x: input_api.FilterSourceFile( - x, files_to_check=[r'.+\.(gn|gni|h)$']) - for f in input_api.AffectedSourceFiles(source_file_filter): - for path in API_DIRS: - dn = os.path.dirname(f.LocalPath()) - if path == 'api': - # Special case: Subdirectories included. - if dn == 'api' or dn.startswith('api/'): - files.append(f.LocalPath()) - else: - # Normal case: Subdirectories not included. - if dn == path: - files.append(f.LocalPath()) + """Checks to remind proper changing of native APIs.""" + files = [] + source_file_filter = lambda x: input_api.FilterSourceFile( + x, files_to_check=[r'.+\.(gn|gni|h)$']) + for f in input_api.AffectedSourceFiles(source_file_filter): + for path in API_DIRS: + dn = os.path.dirname(f.LocalPath()) + if path == 'api': + # Special case: Subdirectories included. + if dn == 'api' or dn.startswith('api/'): + files.append(f.LocalPath()) + else: + # Normal case: Subdirectories not included. + if dn == path: + files.append(f.LocalPath()) - if files: - return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)] - return [] + if files: + return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)] + return [] def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter): - """Checks to make sure no .h files include .""" - files = [] - pattern = input_api.re.compile(r'^#include\s*', - input_api.re.MULTILINE) - file_filter = lambda x: (input_api.FilterSourceFile(x) and - source_file_filter(x)) - for f in input_api.AffectedSourceFiles(file_filter): - if not f.LocalPath().endswith('.h'): - continue - contents = input_api.ReadFile(f) - if pattern.search(contents): - files.append(f) + """Checks to make sure no .h files include .""" + files = [] + pattern = input_api.re.compile(r'^#include\s*', + input_api.re.MULTILINE) + file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter( + x)) + for f in input_api.AffectedSourceFiles(file_filter): + if not f.LocalPath().endswith('.h'): + continue + contents = input_api.ReadFile(f) + if pattern.search(contents): + files.append(f) - if len(files): - return [ - output_api.PresubmitError( - 'Do not #include in header files, since it inserts ' - 'static initialization into every file including the header. ' - 'Instead, #include . See http://crbug.com/94794', - files) - ] - return [] + if len(files) > 0: + return [ + output_api.PresubmitError( + 'Do not #include in header files, since it inserts ' + 'static initialization into every file including the header. ' + 'Instead, #include . See http://crbug.com/94794', files) + ] + return [] def CheckNoPragmaOnce(input_api, output_api, source_file_filter): - """Make sure that banned functions are not used.""" - files = [] - pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE) - file_filter = lambda x: (input_api.FilterSourceFile(x) and - source_file_filter(x)) - for f in input_api.AffectedSourceFiles(file_filter): - if not f.LocalPath().endswith('.h'): - continue - contents = input_api.ReadFile(f) - if pattern.search(contents): - files.append(f) + """Make sure that banned functions are not used.""" + files = [] + pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE) + file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter( + x)) + for f in input_api.AffectedSourceFiles(file_filter): + if not f.LocalPath().endswith('.h'): + continue + contents = input_api.ReadFile(f) + if pattern.search(contents): + files.append(f) + + if files: + return [ + output_api.PresubmitError( + 'Do not use #pragma once in header files.\n' + 'See http://www.chromium.org/developers/coding-style' + '#TOC-File-headers', files) + ] + return [] - if files: - return [ - output_api.PresubmitError( - 'Do not use #pragma once in header files.\n' - 'See http://www.chromium.org/developers/coding-style' - '#TOC-File-headers', - files) - ] - return [] def CheckNoFRIEND_TEST(# pylint: disable=invalid-name input_api, output_api, source_file_filter): - """Make sure that gtest's FRIEND_TEST() macro is not used, the + """Make sure that gtest's FRIEND_TEST() macro is not used, the FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes.""" - problems = [] + problems = [] - file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and - source_file_filter(f)) - for f in input_api.AffectedFiles(file_filter=file_filter): - for line_num, line in f.ChangedContents(): - if 'FRIEND_TEST(' in line: - problems.append(' %s:%d' % (f.LocalPath(), line_num)) + file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and + source_file_filter(f)) + for f in input_api.AffectedFiles(file_filter=file_filter): + for line_num, line in f.ChangedContents(): + if 'FRIEND_TEST(' in line: + problems.append(' %s:%d' % (f.LocalPath(), line_num)) - if not problems: - return [] - return [ - output_api.PresubmitPromptWarning( - 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. ' - 'Include testsupport/gtest_prod_util.h and use ' - 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems)) - ] + if not problems: + return [] + return [ + output_api.PresubmitPromptWarning( + 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. ' + 'Include testsupport/gtest_prod_util.h and use ' + 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems)) + ] def IsLintDisabled(disabled_paths, file_path): - """ Checks if a file is disabled for lint check.""" - for path in disabled_paths: - if file_path == path or os.path.dirname(file_path).startswith(path): - return True - return False + """ Checks if a file is disabled for lint check.""" + for path in disabled_paths: + if file_path == path or os.path.dirname(file_path).startswith(path): + return True + return False def CheckApprovedFilesLintClean(input_api, output_api, source_file_filter=None): - """Checks that all new or non-exempt .cc and .h files pass cpplint.py. + """Checks that all new or non-exempt .cc and .h files pass cpplint.py. This check is based on CheckChangeLintsClean in depot_tools/presubmit_canned_checks.py but has less filters and only checks added files.""" - result = [] + result = [] - # Initialize cpplint. - import cpplint - # Access to a protected member _XX of a client class - # pylint: disable=W0212 - cpplint._cpplint_state.ResetErrorCounts() + # Initialize cpplint. + import cpplint + # Access to a protected member _XX of a client class + # pylint: disable=W0212 + cpplint._cpplint_state.ResetErrorCounts() - lint_filters = cpplint._Filters() - lint_filters.extend(DISABLED_LINT_FILTERS) - cpplint._SetFilters(','.join(lint_filters)) + lint_filters = cpplint._Filters() + lint_filters.extend(DISABLED_LINT_FILTERS) + cpplint._SetFilters(','.join(lint_filters)) - # Create a platform independent exempt list for cpplint. - disabled_paths = [ - input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS - ] + # Create a platform independent exempt list for cpplint. + disabled_paths = [ + input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS + ] - # Use the strictest verbosity level for cpplint.py (level 1) which is the - # default when running cpplint.py from command line. To make it possible to - # work with not-yet-converted code, we're only applying it to new (or - # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS. - verbosity_level = 1 - files = [] - for f in input_api.AffectedSourceFiles(source_file_filter): - # Note that moved/renamed files also count as added. - if f.Action() == 'A' or not IsLintDisabled(disabled_paths, - f.LocalPath()): - files.append(f.AbsoluteLocalPath()) + # Use the strictest verbosity level for cpplint.py (level 1) which is the + # default when running cpplint.py from command line. To make it possible to + # work with not-yet-converted code, we're only applying it to new (or + # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS. + verbosity_level = 1 + files = [] + for f in input_api.AffectedSourceFiles(source_file_filter): + # Note that moved/renamed files also count as added. + if f.Action() == 'A' or not IsLintDisabled(disabled_paths, f.LocalPath()): + files.append(f.AbsoluteLocalPath()) - for file_name in files: - cpplint.ProcessFile(file_name, verbosity_level) + for file_name in files: + cpplint.ProcessFile(file_name, verbosity_level) - if cpplint._cpplint_state.error_count > 0: - if input_api.is_committing: - res_type = output_api.PresubmitError - else: - res_type = output_api.PresubmitPromptWarning - result = [res_type('Changelist failed cpplint.py check.')] + if cpplint._cpplint_state.error_count > 0: + if input_api.is_committing: + res_type = output_api.PresubmitError + else: + res_type = output_api.PresubmitPromptWarning + result = [res_type('Changelist failed cpplint.py check.')] - return result + return result def CheckNoSourcesAbove(input_api, gn_files, output_api): - # Disallow referencing source files with paths above the GN file location. - source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', - re.MULTILINE | re.DOTALL) - file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"') - violating_gn_files = set() - violating_source_entries = [] - for gn_file in gn_files: - contents = input_api.ReadFile(gn_file) - for source_block_match in source_pattern.finditer(contents): - # Find all source list entries starting with ../ in the source block - # (exclude overrides entries). - for file_list_match in file_pattern.finditer( - source_block_match.group(1)): - source_file = file_list_match.group(1) - if 'overrides/' not in source_file: - violating_source_entries.append(source_file) - violating_gn_files.add(gn_file) - if violating_gn_files: - return [ - output_api.PresubmitError( - 'Referencing source files above the directory of the GN file ' - 'is not allowed. Please introduce new GN targets in the proper ' - 'location instead.\n' - 'Invalid source entries:\n' - '%s\n' - 'Violating GN files:' % '\n'.join(violating_source_entries), - items=violating_gn_files) - ] - return [] + # Disallow referencing source files with paths above the GN file location. + source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', + re.MULTILINE | re.DOTALL) + file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"') + violating_gn_files = set() + violating_source_entries = [] + for gn_file in gn_files: + contents = input_api.ReadFile(gn_file) + for source_block_match in source_pattern.finditer(contents): + # Find all source list entries starting with ../ in the source block + # (exclude overrides entries). + for file_list_match in file_pattern.finditer(source_block_match.group(1)): + source_file = file_list_match.group(1) + if 'overrides/' not in source_file: + violating_source_entries.append(source_file) + violating_gn_files.add(gn_file) + if violating_gn_files: + return [ + output_api.PresubmitError( + 'Referencing source files above the directory of the GN file ' + 'is not allowed. Please introduce new GN targets in the proper ' + 'location instead.\n' + 'Invalid source entries:\n' + '%s\n' + 'Violating GN files:' % '\n'.join(violating_source_entries), + items=violating_gn_files) + ] + return [] def CheckAbseilDependencies(input_api, gn_files, output_api): - """Checks that Abseil dependencies are declared in `absl_deps`.""" - absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL) - target_types_to_check = [ - 'rtc_library', - 'rtc_source_set', - 'rtc_static_library', - 'webrtc_fuzzer_test', - ] - error_msg = ('Abseil dependencies in target "%s" (file: %s) ' - 'should be moved to the "absl_deps" parameter.') - errors = [] + """Checks that Abseil dependencies are declared in `absl_deps`.""" + absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL) + target_types_to_check = [ + 'rtc_library', + 'rtc_source_set', + 'rtc_static_library', + 'webrtc_fuzzer_test', + ] + error_msg = ('Abseil dependencies in target "%s" (file: %s) ' + 'should be moved to the "absl_deps" parameter.') + errors = [] - for gn_file in gn_files: - gn_file_content = input_api.ReadFile(gn_file) - for target_match in TARGET_RE.finditer(gn_file_content): - target_type = target_match.group('target_type') - target_name = target_match.group('target_name') - target_contents = target_match.group('target_contents') - if target_type in target_types_to_check: - for deps_match in DEPS_RE.finditer(target_contents): - deps = deps_match.group('deps').splitlines() - for dep in deps: - if re.search(absl_re, dep): - errors.append( - output_api.PresubmitError( - error_msg % - (target_name, gn_file.LocalPath()))) - break # no need to warn more than once per target - return errors + # pylint: disable=too-many-nested-blocks + for gn_file in gn_files: + gn_file_content = input_api.ReadFile(gn_file) + for target_match in TARGET_RE.finditer(gn_file_content): + target_type = target_match.group('target_type') + target_name = target_match.group('target_name') + target_contents = target_match.group('target_contents') + if target_type in target_types_to_check: + for deps_match in DEPS_RE.finditer(target_contents): + deps = deps_match.group('deps').splitlines() + for dep in deps: + if re.search(absl_re, dep): + errors.append( + output_api.PresubmitError(error_msg % + (target_name, gn_file.LocalPath()))) + break # no need to warn more than once per target + return errors def CheckNoMixingSources(input_api, gn_files, output_api): - """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target. + """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target. See bugs.webrtc.org/7743 for more context. """ - def _MoreThanOneSourceUsed(*sources_lists): - sources_used = 0 - for source_list in sources_lists: - if len(source_list): - sources_used += 1 - return sources_used > 1 + def _MoreThanOneSourceUsed(*sources_lists): + sources_used = 0 + for source_list in sources_lists: + if len(source_list) > 0: + sources_used += 1 + return sources_used > 1 - errors = defaultdict(lambda: []) - for gn_file in gn_files: - gn_file_content = input_api.ReadFile(gn_file) - for target_match in TARGET_RE.finditer(gn_file_content): - # list_of_sources is a list of tuples of the form - # (c_files, cc_files, objc_files) that keeps track of all the - # sources defined in a target. A GN target can have more that - # on definition of sources (since it supports if/else statements). - # E.g.: - # rtc_static_library("foo") { - # if (is_win) { - # sources = [ "foo.cc" ] - # } else { - # sources = [ "foo.mm" ] - # } - # } - # This is allowed and the presubmit check should support this case. - list_of_sources = [] - c_files = [] - cc_files = [] - objc_files = [] - target_name = target_match.group('target_name') - target_contents = target_match.group('target_contents') - for sources_match in SOURCES_RE.finditer(target_contents): - if '+=' not in sources_match.group(0): - if c_files or cc_files or objc_files: - list_of_sources.append((c_files, cc_files, objc_files)) - c_files = [] - cc_files = [] - objc_files = [] - for file_match in FILE_PATH_RE.finditer( - sources_match.group(1)): - file_path = file_match.group('file_path') - extension = file_match.group('extension') - if extension == '.c': - c_files.append(file_path + extension) - if extension == '.cc': - cc_files.append(file_path + extension) - if extension in ['.m', '.mm']: - objc_files.append(file_path + extension) + errors = defaultdict(lambda: []) + for gn_file in gn_files: + gn_file_content = input_api.ReadFile(gn_file) + for target_match in TARGET_RE.finditer(gn_file_content): + # list_of_sources is a list of tuples of the form + # (c_files, cc_files, objc_files) that keeps track of all the + # sources defined in a target. A GN target can have more that + # on definition of sources (since it supports if/else statements). + # E.g.: + # rtc_static_library("foo") { + # if (is_win) { + # sources = [ "foo.cc" ] + # } else { + # sources = [ "foo.mm" ] + # } + # } + # This is allowed and the presubmit check should support this case. + list_of_sources = [] + c_files = [] + cc_files = [] + objc_files = [] + target_name = target_match.group('target_name') + target_contents = target_match.group('target_contents') + for sources_match in SOURCES_RE.finditer(target_contents): + if '+=' not in sources_match.group(0): + if c_files or cc_files or objc_files: list_of_sources.append((c_files, cc_files, objc_files)) - for c_files_list, cc_files_list, objc_files_list in list_of_sources: - if _MoreThanOneSourceUsed(c_files_list, cc_files_list, - objc_files_list): - all_sources = sorted(c_files_list + cc_files_list + - objc_files_list) - errors[gn_file.LocalPath()].append( - (target_name, all_sources)) - if errors: - return [ - output_api.PresubmitError( - 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n' - 'Please create a separate target for each collection of ' - 'sources.\n' - 'Mixed sources: \n' - '%s\n' - 'Violating GN files:\n%s\n' % - (json.dumps(errors, indent=2), '\n'.join(errors.keys()))) - ] - return [] + c_files = [] + cc_files = [] + objc_files = [] + for file_match in FILE_PATH_RE.finditer(sources_match.group(1)): + file_path = file_match.group('file_path') + extension = file_match.group('extension') + if extension == '.c': + c_files.append(file_path + extension) + if extension == '.cc': + cc_files.append(file_path + extension) + if extension in ['.m', '.mm']: + objc_files.append(file_path + extension) + list_of_sources.append((c_files, cc_files, objc_files)) + for c_files_list, cc_files_list, objc_files_list in list_of_sources: + if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list): + all_sources = sorted(c_files_list + cc_files_list + objc_files_list) + errors[gn_file.LocalPath()].append((target_name, all_sources)) + if errors: + return [ + output_api.PresubmitError( + 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n' + 'Please create a separate target for each collection of ' + 'sources.\n' + 'Mixed sources: \n' + '%s\n' + 'Violating GN files:\n%s\n' % + (json.dumps(errors, indent=2), '\n'.join(list(errors.keys())))) + ] + return [] def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api): - cwd = input_api.PresubmitLocalPath() - with _AddToPath( - input_api.os_path.join(cwd, 'tools_webrtc', - 'presubmit_checks_lib')): - from check_package_boundaries import CheckPackageBoundaries - build_files = [ - os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files + cwd = input_api.PresubmitLocalPath() + with _AddToPath( + input_api.os_path.join(cwd, 'tools_webrtc', 'presubmit_checks_lib')): + from check_package_boundaries import CheckPackageBoundaries + build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files] + errors = CheckPackageBoundaries(cwd, build_files)[:5] + if errors: + return [ + output_api.PresubmitError( + 'There are package boundary violations in the following GN ' + 'files:', + long_text='\n\n'.join(str(err) for err in errors)) ] - errors = CheckPackageBoundaries(cwd, build_files)[:5] - if errors: - return [ - output_api.PresubmitError( - 'There are package boundary violations in the following GN ' - 'files:', long_text='\n\n'.join(str(err) for err in errors)) - ] - return [] + return [] def _ReportFileAndLine(filename, line_num): - """Default error formatter for _FindNewViolationsOfRule.""" - return '%s (line %s)' % (filename, line_num) + """Default error formatter for _FindNewViolationsOfRule.""" + return '%s (line %s)' % (filename, line_num) def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api, error_formatter=_ReportFileAndLine): - """Ensure warning suppression flags are not added wihtout a reason.""" - msg = ('Usage of //build/config/clang:extra_warnings is discouraged ' - 'in WebRTC.\n' - 'If you are not adding this code (e.g. you are just moving ' - 'existing code) or you want to add an exception,\n' - 'you can add a comment on the line that causes the problem:\n\n' - '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n' - '\n' - 'Affected files:\n') - errors = [] # 2-element tuples with (file, line number) - clang_warn_re = input_api.re.compile( - r'//build/config/clang:extra_warnings') - no_presubmit_re = input_api.re.compile( - r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)') - for f in gn_files: - for line_num, line in f.ChangedContents(): - if clang_warn_re.search(line) and not no_presubmit_re.search(line): - errors.append(error_formatter(f.LocalPath(), line_num)) - if errors: - return [output_api.PresubmitError(msg, errors)] - return [] + """Ensure warning suppression flags are not added without a reason.""" + msg = ('Usage of //build/config/clang:extra_warnings is discouraged ' + 'in WebRTC.\n' + 'If you are not adding this code (e.g. you are just moving ' + 'existing code) or you want to add an exception,\n' + 'you can add a comment on the line that causes the problem:\n\n' + '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n' + '\n' + 'Affected files:\n') + errors = [] # 2-element tuples with (file, line number) + clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings') + # pylint: disable-next=fixme + no_presubmit_re = input_api.re.compile( + r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)') + for f in gn_files: + for line_num, line in f.ChangedContents(): + if clang_warn_re.search(line) and not no_presubmit_re.search(line): + errors.append(error_formatter(f.LocalPath(), line_num)) + if errors: + return [output_api.PresubmitError(msg, errors)] + return [] def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter, error_formatter=_ReportFileAndLine): - error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the ' - 'new API: https://github.com/google/googletest/blob/master/' - 'googletest/docs/primer.md#beware-of-the-nomenclature.\n' - 'Affected files:\n') - errors = [] # 2-element tuples with (file, line number) - test_case_re = input_api.re.compile(r'TEST_CASE') - file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith( - '.cc')) - for f in input_api.AffectedSourceFiles(file_filter): - for line_num, line in f.ChangedContents(): - if test_case_re.search(line): - errors.append(error_formatter(f.LocalPath(), line_num)) - if errors: - return [output_api.PresubmitError(error_msg, errors)] - return [] + error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the ' + 'new API: https://github.com/google/googletest/blob/master/' + 'googletest/docs/primer.md#beware-of-the-nomenclature.\n' + 'Affected files:\n') + errors = [] # 2-element tuples with (file, line number) + test_case_re = input_api.re.compile(r'TEST_CASE') + file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith( + '.cc')) + for f in input_api.AffectedSourceFiles(file_filter): + for line_num, line in f.ChangedContents(): + if test_case_re.search(line): + errors.append(error_formatter(f.LocalPath(), line_num)) + if errors: + return [output_api.PresubmitError(error_msg, errors)] + return [] def CheckNoStreamUsageIsAdded(input_api, output_api, source_file_filter, error_formatter=_ReportFileAndLine): - """Make sure that no more dependencies on stringstream are added.""" - error_msg = ( - 'Usage of , and in WebRTC is ' - 'deprecated.\n' - 'This includes the following types:\n' - 'std::istringstream, std::ostringstream, std::wistringstream, ' - 'std::wostringstream,\n' - 'std::wstringstream, std::ostream, std::wostream, std::istream,' - 'std::wistream,\n' - 'std::iostream, std::wiostream.\n' - 'If you are not adding this code (e.g. you are just moving ' - 'existing code),\n' - 'you can add a comment on the line that causes the problem:\n\n' - '#include // no-presubmit-check TODO(webrtc:8982)\n' - 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n' - '\n' - 'If you are adding new code, consider using ' - 'rtc::SimpleStringBuilder\n' - '(in rtc_base/strings/string_builder.h).\n' - 'Affected files:\n') - errors = [] # 2-element tuples with (file, line number) - include_re = input_api.re.compile(r'#include <(i|o|s)stream>') - usage_re = input_api.re.compile( - r'std::(w|i|o|io|wi|wo|wio)(string)*stream') - no_presubmit_re = input_api.re.compile( - r'// no-presubmit-check TODO\(webrtc:8982\)') - file_filter = lambda x: (input_api.FilterSourceFile(x) and - source_file_filter(x)) + """Make sure that no more dependencies on stringstream are added.""" + error_msg = ('Usage of , and in WebRTC is ' + 'deprecated.\n' + 'This includes the following types:\n' + 'std::istringstream, std::ostringstream, std::wistringstream, ' + 'std::wostringstream,\n' + 'std::wstringstream, std::ostream, std::wostream, std::istream,' + 'std::wistream,\n' + 'std::iostream, std::wiostream.\n' + 'If you are not adding this code (e.g. you are just moving ' + 'existing code),\n' + 'you can add a comment on the line that causes the problem:\n\n' + '#include // no-presubmit-check TODO(webrtc:8982)\n' + 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n' + '\n' + 'If you are adding new code, consider using ' + 'rtc::SimpleStringBuilder\n' + '(in rtc_base/strings/string_builder.h).\n' + 'Affected files:\n') + errors = [] # 2-element tuples with (file, line number) + include_re = input_api.re.compile(r'#include <(i|o|s)stream>') + usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream') + no_presubmit_re = input_api.re.compile( + r'// no-presubmit-check TODO\(webrtc:8982\)') + file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter( + x)) - def _IsException(file_path): - is_test = any( - file_path.endswith(x) for x in - ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc']) - return (file_path.startswith('examples') - or file_path.startswith('test') or is_test) + def _IsException(file_path): + is_test = any( + file_path.endswith(x) + for x in ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc']) + return (file_path.startswith('examples') or file_path.startswith('test') + or is_test) - for f in input_api.AffectedSourceFiles(file_filter): - # Usage of stringstream is allowed under examples/ and in tests. - if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()): - continue - for line_num, line in f.ChangedContents(): - if ((include_re.search(line) or usage_re.search(line)) - and not no_presubmit_re.search(line)): - errors.append(error_formatter(f.LocalPath(), line_num)) - if errors: - return [output_api.PresubmitError(error_msg, errors)] - return [] + for f in input_api.AffectedSourceFiles(file_filter): + # Usage of stringstream is allowed under examples/ and in tests. + if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()): + continue + for line_num, line in f.ChangedContents(): + if ((include_re.search(line) or usage_re.search(line)) + and not no_presubmit_re.search(line)): + errors.append(error_formatter(f.LocalPath(), line_num)) + if errors: + return [output_api.PresubmitError(error_msg, errors)] + return [] def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api): - """Checks that public_deps is not used without a good reason.""" - result = [] - no_presubmit_check_re = input_api.re.compile( - r'# no-presubmit-check TODO\(webrtc:\d+\)') - error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files ' - 'because it doesn\'t map well to downstream build systems.\n' - 'Used in: %s (line %d).\n' - 'If you are not adding this code (e.g. you are just moving ' - 'existing code) or you have a good reason, you can add this ' - 'comment (verbatim) on the line that causes the problem:\n\n' - 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n') - for affected_file in gn_files: - for (line_number, affected_line) in affected_file.ChangedContents(): - if 'public_deps' in affected_line: - surpressed = no_presubmit_check_re.search(affected_line) - if not surpressed: - result.append( - output_api.PresubmitError( - error_msg % - (affected_file.LocalPath(), line_number))) - return result + """Checks that public_deps is not used without a good reason.""" + result = [] + no_presubmit_check_re = input_api.re.compile( + r'# no-presubmit-check TODO\(webrtc:\d+\)') + error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files ' + 'because it doesn\'t map well to downstream build systems.\n' + 'Used in: %s (line %d).\n' + 'If you are not adding this code (e.g. you are just moving ' + 'existing code) or you have a good reason, you can add this ' + 'comment (verbatim) on the line that causes the problem:\n\n' + 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n') + for affected_file in gn_files: + for (line_number, affected_line) in affected_file.ChangedContents(): + if 'public_deps' in affected_line: + surpressed = no_presubmit_check_re.search(affected_line) + if not surpressed: + result.append( + output_api.PresubmitError( + error_msg % (affected_file.LocalPath(), line_number))) + return result def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api): - result = [] - error_msg = ( - 'check_includes overrides are not allowed since it can cause ' - 'incorrect dependencies to form. It effectively means that your ' - 'module can include any .h file without depending on its ' - 'corresponding target. There are some exceptional cases when ' - 'this is allowed: if so, get approval from a .gn owner in the ' - 'root OWNERS file.\n' - 'Used in: %s (line %d).') - no_presubmit_re = input_api.re.compile( - r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)') - for affected_file in gn_files: - for (line_number, affected_line) in affected_file.ChangedContents(): - if ('check_includes' in affected_line - and not no_presubmit_re.search(affected_line)): - result.append( - output_api.PresubmitError( - error_msg % (affected_file.LocalPath(), line_number))) - return result + result = [] + error_msg = ('check_includes overrides are not allowed since it can cause ' + 'incorrect dependencies to form. It effectively means that your ' + 'module can include any .h file without depending on its ' + 'corresponding target. There are some exceptional cases when ' + 'this is allowed: if so, get approval from a .gn owner in the ' + 'root OWNERS file.\n' + 'Used in: %s (line %d).') + # pylint: disable-next=fixme + no_presubmit_re = input_api.re.compile( + r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)') + for affected_file in gn_files: + for (line_number, affected_line) in affected_file.ChangedContents(): + if ('check_includes' in affected_line + and not no_presubmit_re.search(affected_line)): + result.append( + output_api.PresubmitError(error_msg % + (affected_file.LocalPath(), line_number))) + return result def CheckGnChanges(input_api, output_api): - file_filter = lambda x: (input_api.FilterSourceFile( - x, - files_to_check=(r'.+\.(gn|gni)$', ), - files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', ))) + file_filter = lambda x: (input_api.FilterSourceFile( + x, + files_to_check=(r'.+\.(gn|gni)$', ), + files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', ))) - gn_files = [] - for f in input_api.AffectedSourceFiles(file_filter): - gn_files.append(f) + gn_files = [] + for f in input_api.AffectedSourceFiles(file_filter): + gn_files.append(f) - result = [] - if gn_files: - result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api)) - result.extend(CheckNoMixingSources(input_api, gn_files, output_api)) - result.extend(CheckAbseilDependencies(input_api, gn_files, output_api)) - result.extend( - CheckNoPackageBoundaryViolations(input_api, gn_files, output_api)) - result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, - output_api)) - result.extend( - CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api)) - result.extend( - CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, - output_api)) - return result + result = [] + if gn_files: + result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api)) + result.extend(CheckNoMixingSources(input_api, gn_files, output_api)) + result.extend(CheckAbseilDependencies(input_api, gn_files, output_api)) + result.extend( + CheckNoPackageBoundaryViolations(input_api, gn_files, output_api)) + result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api)) + result.extend(CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api)) + result.extend( + CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api)) + return result def CheckGnGen(input_api, output_api): - """Runs `gn gen --check` with default args to detect mismatches between + """Runs `gn gen --check` with default args to detect mismatches between #includes and dependencies in the BUILD.gn files, as well as general build errors. """ - with _AddToPath( - input_api.os_path.join(input_api.PresubmitLocalPath(), - 'tools_webrtc', 'presubmit_checks_lib')): - from build_helpers import RunGnCheck - errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5] - if errors: - return [ - output_api.PresubmitPromptWarning( - 'Some #includes do not match the build dependency graph. ' - 'Please run:\n' - ' gn gen --check ', - long_text='\n\n'.join(errors)) - ] - return [] + with _AddToPath( + input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools_webrtc', + 'presubmit_checks_lib')): + from build_helpers import RunGnCheck + errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5] + if errors: + return [ + output_api.PresubmitPromptWarning( + 'Some #includes do not match the build dependency graph. ' + 'Please run:\n' + ' gn gen --check ', + long_text='\n\n'.join(errors)) + ] + return [] def CheckUnwantedDependencies(input_api, output_api, source_file_filter): - """Runs checkdeps on #include statements added in this + """Runs checkdeps on #include statements added in this change. Breaking - rules is an error, breaking ! rules is a warning. """ - # Copied from Chromium's src/PRESUBMIT.py. + # Copied from Chromium's src/PRESUBMIT.py. - # We need to wait until we have an input_api object and use this - # roundabout construct to import checkdeps because this file is - # eval-ed and thus doesn't have __file__. - src_path = FindSrcDirPath(input_api.PresubmitLocalPath()) - checkdeps_path = input_api.os_path.join(src_path, 'buildtools', - 'checkdeps') - if not os.path.exists(checkdeps_path): - return [ - output_api.PresubmitError( - 'Cannot find checkdeps at %s\nHave you run "gclient sync" to ' - 'download all the DEPS entries?' % checkdeps_path) - ] - with _AddToPath(checkdeps_path): - import checkdeps - from cpp_checker import CppChecker - from rules import Rule + # We need to wait until we have an input_api object and use this + # roundabout construct to import checkdeps because this file is + # eval-ed and thus doesn't have __file__. + src_path = FindSrcDirPath(input_api.PresubmitLocalPath()) + checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps') + if not os.path.exists(checkdeps_path): + return [ + output_api.PresubmitError( + 'Cannot find checkdeps at %s\nHave you run "gclient sync" to ' + 'download all the DEPS entries?' % checkdeps_path) + ] + with _AddToPath(checkdeps_path): + import checkdeps + from cpp_checker import CppChecker + from rules import Rule - added_includes = [] - for f in input_api.AffectedFiles(file_filter=source_file_filter): - if not CppChecker.IsCppFile(f.LocalPath()): - continue + added_includes = [] + for f in input_api.AffectedFiles(file_filter=source_file_filter): + if not CppChecker.IsCppFile(f.LocalPath()): + continue - changed_lines = [line for _, line in f.ChangedContents()] - added_includes.append([f.LocalPath(), changed_lines]) + changed_lines = [line for _, line in f.ChangedContents()] + added_includes.append([f.LocalPath(), changed_lines]) - deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath()) + deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath()) - error_descriptions = [] - warning_descriptions = [] - for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes( - added_includes): - description_with_path = '%s\n %s' % (path, rule_description) - if rule_type == Rule.DISALLOW: - error_descriptions.append(description_with_path) - else: - warning_descriptions.append(description_with_path) + error_descriptions = [] + warning_descriptions = [] + for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes( + added_includes): + description_with_path = '%s\n %s' % (path, rule_description) + if rule_type == Rule.DISALLOW: + error_descriptions.append(description_with_path) + else: + warning_descriptions.append(description_with_path) - results = [] - if error_descriptions: - results.append( - output_api.PresubmitError( - 'You added one or more #includes that violate checkdeps rules.' - '\nCheck that the DEPS files in these locations contain valid ' - 'rules.\nSee ' - 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ ' - 'for more details about checkdeps.', error_descriptions)) - if warning_descriptions: - results.append( - output_api.PresubmitPromptOrNotify( - 'You added one or more #includes of files that are temporarily' - '\nallowed but being removed. Can you avoid introducing the\n' - '#include? See relevant DEPS file(s) for details and contacts.' - '\nSee ' - 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ ' - 'for more details about checkdeps.', warning_descriptions)) - return results + results = [] + if error_descriptions: + results.append( + output_api.PresubmitError( + 'You added one or more #includes that violate checkdeps rules.' + '\nCheck that the DEPS files in these locations contain valid ' + 'rules.\nSee ' + 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ ' + 'for more details about checkdeps.', error_descriptions)) + if warning_descriptions: + results.append( + output_api.PresubmitPromptOrNotify( + 'You added one or more #includes of files that are temporarily' + '\nallowed but being removed. Can you avoid introducing the\n' + '#include? See relevant DEPS file(s) for details and contacts.' + '\nSee ' + 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ ' + 'for more details about checkdeps.', warning_descriptions)) + return results def CheckCommitMessageBugEntry(input_api, output_api): - """Check that bug entries are well-formed in commit message.""" - bogus_bug_msg = ( - 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the ' - 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.' - ) - results = [] - for bug in input_api.change.BugsFromDescription(): - bug = bug.strip() - if bug.lower() == 'none': - continue - if 'b/' not in bug and ':' not in bug: - try: - if int(bug) > 100000: - # Rough indicator for current chromium bugs. - prefix_guess = 'chromium' - else: - prefix_guess = 'webrtc' - results.append( - 'Bug entry requires issue tracker prefix, e.g. %s:%s' % - (prefix_guess, bug)) - except ValueError: - results.append(bogus_bug_msg % bug) - elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)): - results.append(bogus_bug_msg % bug) - return [output_api.PresubmitError(r) for r in results] + """Check that bug entries are well-formed in commit message.""" + bogus_bug_msg = ( + 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the ' + 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.') + results = [] + for bug in input_api.change.BugsFromDescription(): + bug = bug.strip() + if bug.lower() == 'none': + continue + if 'b/' not in bug and ':' not in bug: + try: + if int(bug) > 100000: + # Rough indicator for current chromium bugs. + prefix_guess = 'chromium' + else: + prefix_guess = 'webrtc' + results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' % + (prefix_guess, bug)) + except ValueError: + results.append(bogus_bug_msg % bug) + elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)): + results.append(bogus_bug_msg % bug) + return [output_api.PresubmitError(r) for r in results] def CheckChangeHasBugField(input_api, output_api): - """Requires that the changelist is associated with a bug. + """Requires that the changelist is associated with a bug. This check is stricter than the one in depot_tools/presubmit_canned_checks.py since it fails the presubmit if the bug field is missing or doesn't contain @@ -786,273 +776,280 @@ def CheckChangeHasBugField(input_api, output_api): This supports both 'BUG=' and 'Bug:' since we are in the process of migrating to Gerrit and it encourages the usage of 'Bug:'. """ - if input_api.change.BugsFromDescription(): - return [] - else: - return [ - output_api.PresubmitError( - 'The "Bug: [bug number]" footer is mandatory. Please create a ' - 'bug and reference it using either of:\n' - ' * https://bugs.webrtc.org - reference it using Bug: ' - 'webrtc:XXXX\n' - ' * https://crbug.com - reference it using Bug: chromium:XXXXXX' - ) - ] + if input_api.change.BugsFromDescription(): + return [] + return [ + output_api.PresubmitError( + 'The "Bug: [bug number]" footer is mandatory. Please create a ' + 'bug and reference it using either of:\n' + ' * https://bugs.webrtc.org - reference it using Bug: ' + 'webrtc:XXXX\n' + ' * https://crbug.com - reference it using Bug: chromium:XXXXXX') + ] def CheckJSONParseErrors(input_api, output_api, source_file_filter): - """Check that JSON files do not contain syntax errors.""" + """Check that JSON files do not contain syntax errors.""" - def FilterFile(affected_file): - return (input_api.os_path.splitext( - affected_file.LocalPath())[1] == '.json' - and source_file_filter(affected_file)) + def FilterFile(affected_file): + return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json' + and source_file_filter(affected_file)) - def GetJSONParseError(input_api, filename): - try: - contents = input_api.ReadFile(filename) - input_api.json.loads(contents) - except ValueError as e: - return e - return None + def GetJSONParseError(input_api, filename): + try: + contents = input_api.ReadFile(filename) + input_api.json.loads(contents) + except ValueError as e: + return e + return None - results = [] - for affected_file in input_api.AffectedFiles(file_filter=FilterFile, - include_deletes=False): - parse_error = GetJSONParseError(input_api, - affected_file.AbsoluteLocalPath()) - if parse_error: - results.append( - output_api.PresubmitError( - '%s could not be parsed: %s' % - (affected_file.LocalPath(), parse_error))) - return results + results = [] + for affected_file in input_api.AffectedFiles(file_filter=FilterFile, + include_deletes=False): + parse_error = GetJSONParseError(input_api, + affected_file.AbsoluteLocalPath()) + if parse_error: + results.append( + output_api.PresubmitError('%s could not be parsed: %s' % + (affected_file.LocalPath(), parse_error))) + return results def RunPythonTests(input_api, output_api): - def Join(*args): - return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) + def Join(*args): + return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) - test_directories = [ - input_api.PresubmitLocalPath(), - Join('rtc_tools', 'py_event_log_analyzer'), - Join('audio', 'test', 'unittests'), - ] + [ - root for root, _, files in os.walk(Join('tools_webrtc')) if any( - f.endswith('_test.py') for f in files) - ] + excluded_files = [ + # These tests should be run manually after webrtc_dashboard_upload target + # has been built. + 'catapult_uploader_test.py', + 'process_perf_results_test.py', + ] - tests = [] - for directory in test_directories: - tests.extend( - input_api.canned_checks.GetUnitTestsInDirectory( - input_api, - output_api, - directory, - files_to_check=[r'.+_test\.py$'])) - return input_api.RunTests(tests, parallel=True) + test_directories = [ + input_api.PresubmitLocalPath(), + Join('rtc_tools', 'py_event_log_analyzer'), + Join('audio', 'test', 'unittests'), + ] + [ + root for root, _, files in os.walk(Join('tools_webrtc')) if any( + f.endswith('_test.py') and f not in excluded_files for f in files) + ] + + tests = [] + + for directory in test_directories: + tests.extend( + input_api.canned_checks.GetUnitTestsInDirectory( + input_api, + output_api, + directory, + files_to_check=[r'.+_test\.py$'], + run_on_python2=False)) + return input_api.RunTests(tests, parallel=True) def CheckUsageOfGoogleProtobufNamespace(input_api, output_api, source_file_filter): - """Checks that the namespace google::protobuf has not been used.""" - files = [] - pattern = input_api.re.compile(r'google::protobuf') - proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h') - file_filter = lambda x: (input_api.FilterSourceFile(x) and - source_file_filter(x)) - for f in input_api.AffectedSourceFiles(file_filter): - if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']: - continue - contents = input_api.ReadFile(f) - if pattern.search(contents): - files.append(f) + """Checks that the namespace google::protobuf has not been used.""" + files = [] + pattern = input_api.re.compile(r'google::protobuf') + proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h') + file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter( + x)) + for f in input_api.AffectedSourceFiles(file_filter): + if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']: + continue + contents = input_api.ReadFile(f) + if pattern.search(contents): + files.append(f) - if files: - return [ - output_api.PresubmitError( - 'Please avoid to use namespace `google::protobuf` directly.\n' - 'Add a using directive in `%s` and include that header instead.' - % proto_utils_path, files) - ] - return [] + if files: + return [ + output_api.PresubmitError( + 'Please avoid to use namespace `google::protobuf` directly.\n' + 'Add a using directive in `%s` and include that header instead.' % + proto_utils_path, files) + ] + return [] def _LicenseHeader(input_api): - """Returns the license header regexp.""" - # Accept any year number from 2003 to the current year - current_year = int(input_api.time.strftime('%Y')) - allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1))) - years_re = '(' + '|'.join(allowed_years) + ')' - license_header = ( - r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. ' - r'All [Rr]ights [Rr]eserved\.\n' - r'.*?\n' - r'.*? Use of this source code is governed by a BSD-style license\n' - r'.*? that can be found in the LICENSE file in the root of the source\n' - r'.*? tree\. An additional intellectual property rights grant can be ' - r'found\n' - r'.*? in the file PATENTS\. All contributing project authors may\n' - r'.*? be found in the AUTHORS file in the root of the source tree\.\n' - ) % { - 'year': years_re, - } - return license_header + """Returns the license header regexp.""" + # Accept any year number from 2003 to the current year + current_year = int(input_api.time.strftime('%Y')) + allowed_years = (str(s) for s in reversed(range(2003, current_year + 1))) + years_re = '(' + '|'.join(allowed_years) + ')' + license_header = ( + r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. ' + r'All [Rr]ights [Rr]eserved\.\n' + r'.*?\n' + r'.*? Use of this source code is governed by a BSD-style license\n' + r'.*? that can be found in the LICENSE file in the root of the source\n' + r'.*? tree\. An additional intellectual property rights grant can be ' + r'found\n' + r'.*? in the file PATENTS\. All contributing project authors may\n' + r'.*? be found in the AUTHORS file in the root of the source tree\.\n' + ) % { + 'year': years_re, + } + return license_header def CommonChecks(input_api, output_api): - """Checks common to both upload and commit.""" - results = [] - # Filter out files that are in objc or ios dirs from being cpplint-ed since - # they do not follow C++ lint rules. - exception_list = input_api.DEFAULT_FILES_TO_SKIP + ( - r".*\bobjc[\\\/].*", - r".*objc\.[hcm]+$", - ) - source_file_filter = lambda x: input_api.FilterSourceFile( - x, None, exception_list) - results.extend( - CheckApprovedFilesLintClean(input_api, output_api, source_file_filter)) - results.extend( - input_api.canned_checks.CheckLicense(input_api, output_api, - _LicenseHeader(input_api))) + """Checks common to both upload and commit.""" + results = [] + # Filter out files that are in objc or ios dirs from being cpplint-ed since + # they do not follow C++ lint rules. + exception_list = input_api.DEFAULT_FILES_TO_SKIP + ( + r".*\bobjc[\\\/].*", + r".*objc\.[hcm]+$", + ) + source_file_filter = lambda x: input_api.FilterSourceFile( + x, None, exception_list) + results.extend( + CheckApprovedFilesLintClean(input_api, output_api, source_file_filter)) + results.extend( + input_api.canned_checks.CheckLicense(input_api, output_api, + _LicenseHeader(input_api))) - # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on - # all python files. This is a temporary solution. - python_file_filter = lambda f: (f.LocalPath().endswith('.py') and - source_file_filter(f)) - python_changed_files = [f.LocalPath() for f in input_api.AffectedFiles( - include_deletes=False, file_filter=python_file_filter)] + # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on + # all python files. This is a temporary solution. + python_file_filter = lambda f: (f.LocalPath().endswith('.py') and + source_file_filter(f)) + python_changed_files = [ + f.LocalPath() + for f in input_api.AffectedFiles(include_deletes=False, + file_filter=python_file_filter) + ] - results.extend( - input_api.canned_checks.RunPylint( - input_api, - output_api, - files_to_check=python_changed_files, - files_to_skip=( - r'^base[\\\/].*\.py$', - r'^build[\\\/].*\.py$', - r'^buildtools[\\\/].*\.py$', - r'^infra[\\\/].*\.py$', - r'^ios[\\\/].*\.py$', - r'^out.*[\\\/].*\.py$', - r'^testing[\\\/].*\.py$', - r'^third_party[\\\/].*\.py$', - r'^tools[\\\/].*\.py$', - # TODO(phoglund): should arguably be checked. - r'^tools_webrtc[\\\/]mb[\\\/].*\.py$', - r'^xcodebuild.*[\\\/].*\.py$', - ), - pylintrc='pylintrc')) + results.extend( + input_api.canned_checks.RunPylint( + input_api, + output_api, + files_to_check=python_changed_files, + files_to_skip=( + r'^base[\\\/].*\.py$', + r'^build[\\\/].*\.py$', + r'^buildtools[\\\/].*\.py$', + r'^infra[\\\/].*\.py$', + r'^ios[\\\/].*\.py$', + r'^out.*[\\\/].*\.py$', + r'^testing[\\\/].*\.py$', + r'^third_party[\\\/].*\.py$', + r'^tools[\\\/].*\.py$', + # TODO(bugs.webrtc.org/13605): should arguably be checked. + r'^tools_webrtc[\\\/]mb[\\\/].*\.py$', + r'^xcodebuild.*[\\\/].*\.py$', + ), + pylintrc='pylintrc', + version='2.7')) - # TODO(nisse): talk/ is no more, so make below checks simpler? - # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function - # since we need to have different license checks - # in talk/ and webrtc/directories. - # Instead, hand-picked checks are included below. + # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks simpler? + # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function + # since we need to have different license checks + # in talk/ and webrtc/directories. + # Instead, hand-picked checks are included below. - # .m and .mm files are ObjC files. For simplicity we will consider - # .h files in ObjC subdirectories ObjC headers. - objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$') - # Skip long-lines check for DEPS and GN files. - build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS') - # Also we will skip most checks for third_party directory. - third_party_filter_list = (r'^third_party[\\\/].+', ) - eighty_char_sources = lambda x: input_api.FilterSourceFile( - x, - files_to_skip=build_file_filter_list + objc_filter_list + - third_party_filter_list) - hundred_char_sources = lambda x: input_api.FilterSourceFile( - x, files_to_check=objc_filter_list) - non_third_party_sources = lambda x: input_api.FilterSourceFile( - x, files_to_skip=third_party_filter_list) + # .m and .mm files are ObjC files. For simplicity we will consider + # .h files in ObjC subdirectories ObjC headers. + objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$') + # Skip long-lines check for DEPS and GN files. + build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS') + # Also we will skip most checks for third_party directory. + third_party_filter_list = (r'^third_party[\\\/].+', ) + eighty_char_sources = lambda x: input_api.FilterSourceFile( + x, + files_to_skip=build_file_filter_list + objc_filter_list + + third_party_filter_list) + hundred_char_sources = lambda x: input_api.FilterSourceFile( + x, files_to_check=objc_filter_list) + non_third_party_sources = lambda x: input_api.FilterSourceFile( + x, files_to_skip=third_party_filter_list) - results.extend( - input_api.canned_checks.CheckLongLines( - input_api, - output_api, - maxlen=80, - source_file_filter=eighty_char_sources)) - results.extend( - input_api.canned_checks.CheckLongLines( - input_api, - output_api, - maxlen=100, - source_file_filter=hundred_char_sources)) - results.extend( - input_api.canned_checks.CheckChangeHasNoTabs( - input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend( - input_api.canned_checks.CheckChangeHasNoStrayWhitespace( - input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend( - input_api.canned_checks.CheckAuthorizedAuthor( - input_api, - output_api, - bot_allowlist=[ - 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com', - 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com', - ])) - results.extend( - input_api.canned_checks.CheckChangeTodoHasOwner( - input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend( - input_api.canned_checks.CheckPatchFormatted(input_api, output_api)) - results.extend(CheckNativeApiHeaderChanges(input_api, output_api)) - results.extend( - CheckNoIOStreamInHeaders(input_api, - output_api, - source_file_filter=non_third_party_sources)) - results.extend( - CheckNoPragmaOnce(input_api, - output_api, - source_file_filter=non_third_party_sources)) - results.extend( - CheckNoFRIEND_TEST(input_api, + results.extend( + input_api.canned_checks.CheckLongLines( + input_api, + output_api, + maxlen=80, + source_file_filter=eighty_char_sources)) + results.extend( + input_api.canned_checks.CheckLongLines( + input_api, + output_api, + maxlen=100, + source_file_filter=hundred_char_sources)) + results.extend( + input_api.canned_checks.CheckChangeHasNoTabs( + input_api, output_api, source_file_filter=non_third_party_sources)) + results.extend( + input_api.canned_checks.CheckChangeHasNoStrayWhitespace( + input_api, output_api, source_file_filter=non_third_party_sources)) + results.extend( + input_api.canned_checks.CheckAuthorizedAuthor( + input_api, + output_api, + bot_allowlist=[ + 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com', + 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com', + ])) + results.extend( + input_api.canned_checks.CheckChangeTodoHasOwner( + input_api, output_api, source_file_filter=non_third_party_sources)) + results.extend( + input_api.canned_checks.CheckPatchFormatted(input_api, output_api)) + results.extend(CheckNativeApiHeaderChanges(input_api, output_api)) + results.extend( + CheckNoIOStreamInHeaders(input_api, + output_api, + source_file_filter=non_third_party_sources)) + results.extend( + CheckNoPragmaOnce(input_api, + output_api, + source_file_filter=non_third_party_sources)) + results.extend( + CheckNoFRIEND_TEST(input_api, + output_api, + source_file_filter=non_third_party_sources)) + results.extend(CheckGnChanges(input_api, output_api)) + results.extend( + CheckUnwantedDependencies(input_api, + output_api, + source_file_filter=non_third_party_sources)) + results.extend( + CheckJSONParseErrors(input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend(CheckGnChanges(input_api, output_api)) - results.extend( - CheckUnwantedDependencies(input_api, - output_api, - source_file_filter=non_third_party_sources)) - results.extend( - CheckJSONParseErrors(input_api, - output_api, - source_file_filter=non_third_party_sources)) - results.extend(RunPythonTests(input_api, output_api)) - results.extend( - CheckUsageOfGoogleProtobufNamespace( - input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend( - CheckOrphanHeaders(input_api, - output_api, - source_file_filter=non_third_party_sources)) - results.extend( - CheckNewlineAtTheEndOfProtoFiles( - input_api, output_api, source_file_filter=non_third_party_sources)) - results.extend( - CheckNoStreamUsageIsAdded(input_api, output_api, + results.extend(RunPythonTests(input_api, output_api)) + results.extend( + CheckUsageOfGoogleProtobufNamespace( + input_api, output_api, source_file_filter=non_third_party_sources)) + results.extend( + CheckOrphanHeaders(input_api, + output_api, + source_file_filter=non_third_party_sources)) + results.extend( + CheckNewlineAtTheEndOfProtoFiles( + input_api, output_api, source_file_filter=non_third_party_sources)) + results.extend( + CheckNoStreamUsageIsAdded(input_api, output_api, non_third_party_sources)) + results.extend( + CheckNoTestCaseUsageIsAdded(input_api, output_api, non_third_party_sources)) - results.extend( - CheckNoTestCaseUsageIsAdded(input_api, output_api, - non_third_party_sources)) - results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api)) - results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api)) - results.extend( - CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources)) - results.extend( - CheckAssertUsage(input_api, output_api, non_third_party_sources)) - results.extend( - CheckBannedAbslMakeUnique(input_api, output_api, - non_third_party_sources)) - results.extend( - CheckObjcApiSymbols(input_api, output_api, non_third_party_sources)) - return results + results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api)) + results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api)) + results.extend( + CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources)) + results.extend( + CheckAssertUsage(input_api, output_api, non_third_party_sources)) + results.extend( + CheckBannedAbslMakeUnique(input_api, output_api, non_third_party_sources)) + results.extend( + CheckObjcApiSymbols(input_api, output_api, non_third_party_sources)) + return results def CheckApiDepsFileIsUpToDate(input_api, output_api): - """Check that 'include_rules' in api/DEPS is up to date. + """Check that 'include_rules' in api/DEPS is up to date. The file api/DEPS must be kept up to date in order to avoid to avoid to include internal header from WebRTC's api/ headers. @@ -1061,388 +1058,378 @@ def CheckApiDepsFileIsUpToDate(input_api, output_api): rule for each root level directory. More focused allow rules can be added to 'specific_include_rules'. """ - results = [] - api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS') - with open(api_deps) as f: - deps_content = _ParseDeps(f.read()) + results = [] + api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS') + with open(api_deps) as f: + deps_content = _ParseDeps(f.read()) - include_rules = deps_content.get('include_rules', []) - dirs_to_skip = set(['api', 'docs']) + include_rules = deps_content.get('include_rules', []) + dirs_to_skip = set(['api', 'docs']) - # Only check top level directories affected by the current CL. - dirs_to_check = set() - for f in input_api.AffectedFiles(): - path_tokens = [t for t in f.LocalPath().split(os.sep) if t] - if len(path_tokens) > 1: - if (path_tokens[0] not in dirs_to_skip and os.path.isdir( - os.path.join(input_api.PresubmitLocalPath(), - path_tokens[0]))): - dirs_to_check.add(path_tokens[0]) + # Only check top level directories affected by the current CL. + dirs_to_check = set() + for f in input_api.AffectedFiles(): + path_tokens = [t for t in f.LocalPath().split(os.sep) if t] + if len(path_tokens) > 1: + if (path_tokens[0] not in dirs_to_skip and os.path.isdir( + os.path.join(input_api.PresubmitLocalPath(), path_tokens[0]))): + dirs_to_check.add(path_tokens[0]) - missing_include_rules = set() - for p in dirs_to_check: - rule = '-%s' % p - if rule not in include_rules: - missing_include_rules.add(rule) + missing_include_rules = set() + for p in dirs_to_check: + rule = '-%s' % p + if rule not in include_rules: + missing_include_rules.add(rule) - if missing_include_rules: - error_msg = [ - 'include_rules = [\n', - ' ...\n', - ] + if missing_include_rules: + error_msg = [ + 'include_rules = [\n', + ' ...\n', + ] - for r in sorted(missing_include_rules): - error_msg.append(' "%s",\n' % str(r)) + for r in sorted(missing_include_rules): + error_msg.append(' "%s",\n' % str(r)) - error_msg.append(' ...\n') - error_msg.append(']\n') + error_msg.append(' ...\n') + error_msg.append(']\n') - results.append( - output_api.PresubmitError( - 'New root level directory detected! WebRTC api/ headers should ' - 'not #include headers from \n' - 'the new directory, so please update "include_rules" in file\n' - '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg)))) + results.append( + output_api.PresubmitError( + 'New root level directory detected! WebRTC api/ headers should ' + 'not #include headers from \n' + 'the new directory, so please update "include_rules" in file\n' + '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg)))) - return results + return results def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter): - file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and - source_file_filter(f)) + file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and + source_file_filter(f)) - files = [] - for f in input_api.AffectedFiles(include_deletes=False, - file_filter=file_filter): - for _, line in f.ChangedContents(): - if 'absl::make_unique' in line: - files.append(f) - break + files = [] + for f in input_api.AffectedFiles(include_deletes=False, + file_filter=file_filter): + for _, line in f.ChangedContents(): + if 'absl::make_unique' in line: + files.append(f) + break - if len(files): - return [ - output_api.PresubmitError( - 'Please use std::make_unique instead of absl::make_unique.\n' - 'Affected files:', files) - ] - return [] + if files: + return [ + output_api.PresubmitError( + 'Please use std::make_unique instead of absl::make_unique.\n' + 'Affected files:', files) + ] + return [] def CheckObjcApiSymbols(input_api, output_api, source_file_filter): - rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}', - re.MULTILINE | re.DOTALL) - file_filter = lambda f: (f.LocalPath().endswith(('.h')) and - source_file_filter(f)) + rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}', + re.MULTILINE | re.DOTALL) + file_filter = lambda f: (f.LocalPath().endswith(('.h')) and + source_file_filter(f)) - files = [] - file_filter = lambda x: (input_api.FilterSourceFile(x) and - source_file_filter(x)) - for f in input_api.AffectedSourceFiles(file_filter): - if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath(): - continue - if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'): - continue - contents = input_api.ReadFile(f) - for match in rtc_objc_export.finditer(contents): - export_block = match.group(0) - if 'RTC_OBJC_TYPE' not in export_block: - files.append(f.LocalPath()) + files = [] + file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter( + x)) + for f in input_api.AffectedSourceFiles(file_filter): + if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath(): + continue + if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'): + continue + contents = input_api.ReadFile(f) + for match in rtc_objc_export.finditer(contents): + export_block = match.group(0) + if 'RTC_OBJC_TYPE' not in export_block: + files.append(f.LocalPath()) - if len(files): - return [ - output_api.PresubmitError( - 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() ' - + 'macro.\n\n' + 'For example:\n' + - 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' + - 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' + - 'Please fix the following files:', files) - ] - return [] + if len(files) > 0: + return [ + output_api.PresubmitError( + 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() ' + + 'macro.\n\n' + 'For example:\n' + + 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' + + 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' + + 'Please fix the following files:', files) + ] + return [] def CheckAssertUsage(input_api, output_api, source_file_filter): - pattern = input_api.re.compile(r'\bassert\(') - file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h', '.m', '.mm')) - and source_file_filter(f)) + pattern = input_api.re.compile(r'\bassert\(') + file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h', '.m', '.mm')) + and source_file_filter(f)) - files = [] - for f in input_api.AffectedFiles(include_deletes=False, - file_filter=file_filter): - for _, line in f.ChangedContents(): - if pattern.search(line): - files.append(f.LocalPath()) - break + files = [] + for f in input_api.AffectedFiles(include_deletes=False, + file_filter=file_filter): + for _, line in f.ChangedContents(): + if pattern.search(line): + files.append(f.LocalPath()) + break - if len(files): - return [ - output_api.PresubmitError( - 'Usage of assert() has been detected in the following files, ' - 'please use RTC_DCHECK() instead.\n Files:', files) - ] - return [] + if len(files) > 0: + return [ + output_api.PresubmitError( + 'Usage of assert() has been detected in the following files, ' + 'please use RTC_DCHECK() instead.\n Files:', files) + ] + return [] def CheckAbslMemoryInclude(input_api, output_api, source_file_filter): - pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"', - input_api.re.MULTILINE) - file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and - source_file_filter(f)) + pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"', + input_api.re.MULTILINE) + file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and + source_file_filter(f)) - files = [] - for f in input_api.AffectedFiles(include_deletes=False, - file_filter=file_filter): - contents = input_api.ReadFile(f) - if pattern.search(contents): - continue - for _, line in f.ChangedContents(): - if 'absl::WrapUnique' in line: - files.append(f) - break + files = [] + for f in input_api.AffectedFiles(include_deletes=False, + file_filter=file_filter): + contents = input_api.ReadFile(f) + if pattern.search(contents): + continue + for _, line in f.ChangedContents(): + if 'absl::WrapUnique' in line: + files.append(f) + break - if len(files): - return [ - output_api.PresubmitError( - 'Please include "absl/memory/memory.h" header for ' - 'absl::WrapUnique.\nThis header may or may not be included ' - 'transitively depending on the C++ standard version.', files) - ] - return [] + if len(files) > 0: + return [ + output_api.PresubmitError( + 'Please include "absl/memory/memory.h" header for ' + 'absl::WrapUnique.\nThis header may or may not be included ' + 'transitively depending on the C++ standard version.', files) + ] + return [] def CheckChangeOnUpload(input_api, output_api): - results = [] - results.extend(CommonChecks(input_api, output_api)) - results.extend(CheckGnGen(input_api, output_api)) - results.extend( - input_api.canned_checks.CheckGNFormatted(input_api, output_api)) - return results + results = [] + results.extend(CommonChecks(input_api, output_api)) + results.extend(CheckGnGen(input_api, output_api)) + results.extend(input_api.canned_checks.CheckGNFormatted( + input_api, output_api)) + return results def CheckChangeOnCommit(input_api, output_api): - results = [] - results.extend(CommonChecks(input_api, output_api)) - results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api)) - results.extend(input_api.canned_checks.CheckOwners(input_api, output_api)) - results.extend( - input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api)) - results.extend( - input_api.canned_checks.CheckChangeHasDescription( - input_api, output_api)) - results.extend(CheckChangeHasBugField(input_api, output_api)) - results.extend(CheckCommitMessageBugEntry(input_api, output_api)) - results.extend( - input_api.canned_checks.CheckTreeIsOpen( - input_api, - output_api, - json_url='http://webrtc-status.appspot.com/current?format=json')) - return results + results = [] + results.extend(CommonChecks(input_api, output_api)) + results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api)) + results.extend(input_api.canned_checks.CheckOwners(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangeHasDescription(input_api, output_api)) + results.extend(CheckChangeHasBugField(input_api, output_api)) + results.extend(CheckCommitMessageBugEntry(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckTreeIsOpen( + input_api, + output_api, + json_url='http://webrtc-status.appspot.com/current?format=json')) + return results def CheckOrphanHeaders(input_api, output_api, source_file_filter): - # We need to wait until we have an input_api object and use this - # roundabout construct to import prebubmit_checks_lib because this file is - # eval-ed and thus doesn't have __file__. - error_msg = """{} should be listed in {}.""" - results = [] - exempt_paths = [ - os.path.join('tools_webrtc', 'ios', 'SDK'), - ] - with _AddToPath( - input_api.os_path.join(input_api.PresubmitLocalPath(), - 'tools_webrtc', 'presubmit_checks_lib')): - from check_orphan_headers import GetBuildGnPathFromFilePath - from check_orphan_headers import IsHeaderInBuildGn + # We need to wait until we have an input_api object and use this + # roundabout construct to import prebubmit_checks_lib because this file is + # eval-ed and thus doesn't have __file__. + error_msg = """{} should be listed in {}.""" + results = [] + exempt_paths = [re.escape(os.path.join('tools_webrtc', 'ios', 'SDK'))] - file_filter = lambda x: input_api.FilterSourceFile( - x, files_to_skip=exempt_paths) and source_file_filter(x) - for f in input_api.AffectedSourceFiles(file_filter): - if f.LocalPath().endswith('.h'): - file_path = os.path.abspath(f.LocalPath()) - root_dir = os.getcwd() - gn_file_path = GetBuildGnPathFromFilePath(file_path, - os.path.exists, root_dir) - in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path) - if not in_build_gn: - results.append( - output_api.PresubmitError( - error_msg.format(f.LocalPath(), - os.path.relpath(gn_file_path)))) - return results + with _AddToPath( + input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools_webrtc', + 'presubmit_checks_lib')): + from check_orphan_headers import GetBuildGnPathFromFilePath + from check_orphan_headers import IsHeaderInBuildGn + + file_filter = lambda x: input_api.FilterSourceFile( + x, files_to_skip=exempt_paths) and source_file_filter(x) + for f in input_api.AffectedSourceFiles(file_filter): + if f.LocalPath().endswith('.h'): + file_path = os.path.abspath(f.LocalPath()) + root_dir = os.getcwd() + gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists, + root_dir) + in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path) + if not in_build_gn: + results.append( + output_api.PresubmitError( + error_msg.format(f.LocalPath(), os.path.relpath(gn_file_path)))) + return results def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter): - """Checks that all .proto files are terminated with a newline.""" - error_msg = 'File {} must end with exactly one newline.' - results = [] - file_filter = lambda x: input_api.FilterSourceFile( - x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x) - for f in input_api.AffectedSourceFiles(file_filter): - file_path = f.LocalPath() - with open(file_path) as f: - lines = f.readlines() - if len(lines) > 0 and not lines[-1].endswith('\n'): - results.append( - output_api.PresubmitError(error_msg.format(file_path))) - return results + """Checks that all .proto files are terminated with a newline.""" + error_msg = 'File {} must end with exactly one newline.' + results = [] + file_filter = lambda x: input_api.FilterSourceFile( + x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x) + for f in input_api.AffectedSourceFiles(file_filter): + file_path = f.LocalPath() + with open(file_path) as f: + lines = f.readlines() + if len(lines) > 0 and not lines[-1].endswith('\n'): + results.append(output_api.PresubmitError(error_msg.format(file_path))) + return results def _ExtractAddRulesFromParsedDeps(parsed_deps): - """Extract the rules that add dependencies from a parsed DEPS file. + """Extract the rules that add dependencies from a parsed DEPS file. Args: parsed_deps: the locals dictionary from evaluating the DEPS file.""" - add_rules = set() + add_rules = set() + add_rules.update([ + rule[1:] for rule in parsed_deps.get('include_rules', []) + if rule.startswith('+') or rule.startswith('!') + ]) + for _, rules in parsed_deps.get('specific_include_rules', {}).items(): add_rules.update([ - rule[1:] for rule in parsed_deps.get('include_rules', []) + rule[1:] for rule in rules if rule.startswith('+') or rule.startswith('!') ]) - for _, rules in parsed_deps.get('specific_include_rules', {}).iteritems(): - add_rules.update([ - rule[1:] for rule in rules - if rule.startswith('+') or rule.startswith('!') - ]) - return add_rules + return add_rules def _ParseDeps(contents): - """Simple helper for parsing DEPS files.""" + """Simple helper for parsing DEPS files.""" - # Stubs for handling special syntax in the root DEPS file. - class VarImpl(object): - def __init__(self, local_scope): - self._local_scope = local_scope + # Stubs for handling special syntax in the root DEPS file. + class VarImpl: + def __init__(self, local_scope): + self._local_scope = local_scope - def Lookup(self, var_name): - """Implements the Var syntax.""" - try: - return self._local_scope['vars'][var_name] - except KeyError: - raise Exception('Var is not defined: %s' % var_name) + def Lookup(self, var_name): + """Implements the Var syntax.""" + try: + return self._local_scope['vars'][var_name] + except KeyError as var_not_defined: + raise Exception('Var is not defined: %s' % + var_name) from var_not_defined - local_scope = {} - global_scope = { - 'Var': VarImpl(local_scope).Lookup, - } - exec contents in global_scope, local_scope - return local_scope + local_scope = {} + global_scope = { + 'Var': VarImpl(local_scope).Lookup, + } + exec(contents, global_scope, local_scope) + return local_scope def _CalculateAddedDeps(os_path, old_contents, new_contents): - """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns + """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns a set of DEPS entries that we should look up. For a directory (rather than a specific filename) we fake a path to a specific filename by adding /DEPS. This is chosen as a file that will seldom or never be subject to per-file include_rules. """ - # We ignore deps entries on auto-generated directories. - auto_generated_dirs = ['grit', 'jni'] + # We ignore deps entries on auto-generated directories. + auto_generated_dirs = ['grit', 'jni'] - old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents)) - new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents)) + old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents)) + new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents)) - added_deps = new_deps.difference(old_deps) + added_deps = new_deps.difference(old_deps) - results = set() - for added_dep in added_deps: - if added_dep.split('/')[0] in auto_generated_dirs: - continue - # Assume that a rule that ends in .h is a rule for a specific file. - if added_dep.endswith('.h'): - results.add(added_dep) - else: - results.add(os_path.join(added_dep, 'DEPS')) - return results + results = set() + for added_dep in added_deps: + if added_dep.split('/')[0] in auto_generated_dirs: + continue + # Assume that a rule that ends in .h is a rule for a specific file. + if added_dep.endswith('.h'): + results.add(added_dep) + else: + results.add(os_path.join(added_dep, 'DEPS')) + return results def CheckAddedDepsHaveTargetApprovals(input_api, output_api): - """When a dependency prefixed with + is added to a DEPS file, we + """When a dependency prefixed with + is added to a DEPS file, we want to make sure that the change is reviewed by an OWNER of the target file or directory, to avoid layering violations from being introduced. This check verifies that this happens. """ - virtual_depended_on_files = set() + virtual_depended_on_files = set() - file_filter = lambda f: not input_api.re.match( - r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath()) - for f in input_api.AffectedFiles(include_deletes=False, - file_filter=file_filter): - filename = input_api.os_path.basename(f.LocalPath()) - if filename == 'DEPS': - virtual_depended_on_files.update( - _CalculateAddedDeps(input_api.os_path, - '\n'.join(f.OldContents()), - '\n'.join(f.NewContents()))) - - if not virtual_depended_on_files: - return [] - - if input_api.is_committing: - if input_api.tbr: - return [ - output_api.PresubmitNotifyResult( - '--tbr was specified, skipping OWNERS check for DEPS ' - 'additions' - ) - ] - if input_api.dry_run: - return [ - output_api.PresubmitNotifyResult( - 'This is a dry run, skipping OWNERS check for DEPS ' - 'additions' - ) - ] - if not input_api.change.issue: - return [ - output_api.PresubmitError( - "DEPS approval by OWNERS check failed: this change has " - "no change number, so we can't check it for approvals.") - ] - output = output_api.PresubmitError - else: - output = output_api.PresubmitNotifyResult - - owner_email, reviewers = ( - input_api.canned_checks.GetCodereviewOwnerAndReviewers( - input_api, - None, - approval_needed=input_api.is_committing)) - - owner_email = owner_email or input_api.change.author_email - - approval_status = input_api.owners_client.GetFilesApprovalStatus( - virtual_depended_on_files, reviewers.union([owner_email]), []) - missing_files = [ - f for f in virtual_depended_on_files - if approval_status[f] != input_api.owners_client.APPROVED] - - # We strip the /DEPS part that was added by - # _FilesToCheckForIncomingDeps to fake a path to a file in a - # directory. - def StripDeps(path): - start_deps = path.rfind('/DEPS') - if start_deps != -1: - return path[:start_deps] - else: - return path - - unapproved_dependencies = [ - "'+%s'," % StripDeps(path) for path in missing_files - ] - - if unapproved_dependencies: - output_list = [ - output( - 'You need LGTM from owners of depends-on paths in DEPS that ' - ' were modified in this CL:\n %s' % - '\n '.join(sorted(unapproved_dependencies))) - ] - suggested_owners = input_api.owners_client.SuggestOwners( - missing_files, exclude=[owner_email]) - output_list.append( - output('Suggested missing target path OWNERS:\n %s' % - '\n '.join(suggested_owners or []))) - return output_list + file_filter = lambda f: not input_api.re.match( + r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath()) + for f in input_api.AffectedFiles(include_deletes=False, + file_filter=file_filter): + filename = input_api.os_path.basename(f.LocalPath()) + if filename == 'DEPS': + virtual_depended_on_files.update( + _CalculateAddedDeps(input_api.os_path, '\n'.join(f.OldContents()), + '\n'.join(f.NewContents()))) + if not virtual_depended_on_files: return [] + + if input_api.is_committing: + if input_api.tbr: + return [ + output_api.PresubmitNotifyResult( + '--tbr was specified, skipping OWNERS check for DEPS ' + 'additions') + ] + if input_api.dry_run: + return [ + output_api.PresubmitNotifyResult( + 'This is a dry run, skipping OWNERS check for DEPS ' + 'additions') + ] + if not input_api.change.issue: + return [ + output_api.PresubmitError( + "DEPS approval by OWNERS check failed: this change has " + "no change number, so we can't check it for approvals.") + ] + output = output_api.PresubmitError + else: + output = output_api.PresubmitNotifyResult + + owner_email, reviewers = ( + input_api.canned_checks.GetCodereviewOwnerAndReviewers( + input_api, None, approval_needed=input_api.is_committing)) + + owner_email = owner_email or input_api.change.author_email + + approval_status = input_api.owners_client.GetFilesApprovalStatus( + virtual_depended_on_files, reviewers.union([owner_email]), []) + missing_files = [ + f for f in virtual_depended_on_files + if approval_status[f] != input_api.owners_client.APPROVED + ] + + # We strip the /DEPS part that was added by + # _FilesToCheckForIncomingDeps to fake a path to a file in a + # directory. + def StripDeps(path): + start_deps = path.rfind('/DEPS') + if start_deps != -1: + return path[:start_deps] + return path + + unapproved_dependencies = [ + "'+%s'," % StripDeps(path) for path in missing_files + ] + + if unapproved_dependencies: + output_list = [ + output('You need LGTM from owners of depends-on paths in DEPS that ' + ' were modified in this CL:\n %s' % + '\n '.join(sorted(unapproved_dependencies))) + ] + suggested_owners = input_api.owners_client.SuggestOwners( + missing_files, exclude=[owner_email]) + output_list.append( + output('Suggested missing target path OWNERS:\n %s' % + '\n '.join(suggested_owners or []))) + return output_list + + return [] diff --git a/api/BUILD.gn b/api/BUILD.gn index 091eb9f748..c5f8f00067 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -98,6 +98,12 @@ rtc_library("rtp_packet_info") { absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } +rtc_source_set("video_track_source_constraints") { + visibility = [ "*" ] + sources = [ "video_track_source_constraints.h" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + rtc_library("media_stream_interface") { visibility = [ "*" ] sources = [ @@ -110,9 +116,12 @@ rtc_library("media_stream_interface") { ":audio_options_api", ":rtp_parameters", ":scoped_refptr", + ":sequence_checker", + ":video_track_source_constraints", "../modules/audio_processing:audio_processing_statistics", "../rtc_base:checks", "../rtc_base:refcount", + "../rtc_base/system:no_unique_address", "../rtc_base/system:rtc_export", "video:recordable_encoded_frame", "video:video_frame", @@ -185,6 +194,7 @@ rtc_library("libjingle_peerconnection_api") { "crypto:frame_decryptor_interface", "crypto:frame_encryptor_interface", "crypto:options", + "metronome", "neteq:neteq_api", "rtc_event_log", "task_queue", @@ -257,6 +267,7 @@ rtc_source_set("packet_socket_factory") { ] deps = [ ":async_dns_resolver", + ":wrapping_async_dns_resolver", "../rtc_base:async_resolver_interface", "../rtc_base:rtc_base", "../rtc_base:socket_address", @@ -273,6 +284,28 @@ rtc_source_set("async_dns_resolver") { ] } +rtc_source_set("wrapping_async_dns_resolver") { + visibility = [ + ":*", + "../p2p:rtc_p2p", + ] + sources = [ + "wrapping_async_dns_resolver.cc", + "wrapping_async_dns_resolver.h", + ] + deps = [ + ":async_dns_resolver", + ":sequence_checker", + "../rtc_base:async_resolver_interface", + "../rtc_base:checks", + "../rtc_base:macromagic", + "../rtc_base:socket_address", + "../rtc_base:threading", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] +} + rtc_source_set("scoped_refptr") { visibility = [ "*" ] sources = [ "scoped_refptr.h" ] @@ -382,6 +415,16 @@ rtc_source_set("stats_observer_interface") { absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } +rtc_source_set("peer_network_dependencies") { + visibility = [ "*" ] + sources = [ "test/peer_network_dependencies.h" ] + deps = [ + ":packet_socket_factory", + "../rtc_base", + "../rtc_base:threading", + ] +} + rtc_source_set("peer_connection_quality_test_fixture_api") { visibility = [ "*" ] testonly = true @@ -397,14 +440,17 @@ rtc_source_set("peer_connection_quality_test_fixture_api") { ":media_stream_interface", ":network_state_predictor_api", ":packet_socket_factory", + ":peer_network_dependencies", ":rtp_parameters", ":simulated_network_api", ":stats_observer_interface", ":track_id_stream_info_map", ":video_quality_analyzer_api", "../media:rtc_media_base", + "../modules/audio_processing:api", "../rtc_base:rtc_base", "../rtc_base:threading", + "audio:audio_mixer_api", "rtc_event_log", "task_queue", "transport:network_control", @@ -625,6 +671,8 @@ rtc_source_set("network_emulation_manager_api") { ] deps = [ ":array_view", + ":packet_socket_factory", + ":peer_network_dependencies", ":simulated_network_api", ":time_controller", "../call:simulated_network", @@ -1177,3 +1225,10 @@ if (rtc_include_tests) { ] } } + +rtc_source_set("webrtc_key_value_config") { + visibility = [ "*" ] + sources = [ "webrtc_key_value_config.h" ] + deps = [ "../rtc_base/system:rtc_export" ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} diff --git a/api/DEPS b/api/DEPS index d201a4413a..06232d7890 100644 --- a/api/DEPS +++ b/api/DEPS @@ -112,14 +112,6 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], - "jsep_ice_candidate\.h": [ - "+rtc_base/constructor_magic.h", - ], - - "jsep_session_description\.h": [ - "+rtc_base/constructor_magic.h", - ], - "media_stream_interface\.h": [ "+modules/audio_processing/include/audio_processing_statistics.h", "+rtc_base/ref_count.h", @@ -156,7 +148,6 @@ specific_include_rules = { ], "ref_counted_base\.h": [ - "+rtc_base/constructor_magic.h", "+rtc_base/ref_count.h", "+rtc_base/ref_counter.h", ], @@ -168,6 +159,9 @@ specific_include_rules = { # For private member and constructor. "+rtc_base/system/file_wrapper.h", ], + "rtp_packet_infos\.h": [ + "+rtc_base/ref_counted_object.h", + ], "rtp_receiver_interface\.h": [ "+rtc_base/ref_count.h", ], @@ -192,7 +186,6 @@ specific_include_rules = { ], "stats_types\.h": [ - "+rtc_base/constructor_magic.h", "+rtc_base/ref_count.h", "+rtc_base/thread_checker.h", ], @@ -201,17 +194,12 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], - "audio_frame\.h": [ - "+rtc_base/constructor_magic.h", - ], - "audio_mixer\.h": [ "+rtc_base/ref_count.h", ], "audio_decoder\.h": [ "+rtc_base/buffer.h", - "+rtc_base/constructor_magic.h", ], "audio_decoder_factory\.h": [ @@ -267,6 +255,13 @@ specific_include_rules = { "+rtc_base/ref_counted_object.h", ], + "fake_metronome\.h": [ + "+rtc_base/synchronization/mutex.h", + "+rtc_base/task_queue.h", + "+rtc_base/task_utils/repeating_task.h", + "+rtc_base/thread_annotations.h", + ], + "mock.*\.h": [ "+test/gmock.h", ], @@ -279,6 +274,10 @@ specific_include_rules = { "+rtc_base/ref_counted_object.h", ], + "notifier\.h": [ + "+rtc_base/system/no_unique_address.h", + ], + "simulated_network\.h": [ "+rtc_base/random.h", "+rtc_base/thread_annotations.h", @@ -305,6 +304,18 @@ specific_include_rules = { "+rtc_base/thread_annotations.h", ], + "wrapping_async_dns_resolver\.h": [ + "+rtc_base/async_resolver.h", + "+rtc_base/async_resolver_interface.h", + "+rtc_base/socket_address.h", + "+rtc_base/third_party/sigslot/sigslot.h", + "+rtc_base/thread_annotations.h", + ], + + "video_encoder_factory_template.*\.h": [ + "+modules/video_coding", + ], + # .cc files in api/ should not be restricted in what they can #include, # so we re-add all the top-level directories here. (That's because .h # files leak their #includes to whoever's #including them, but .cc files diff --git a/api/async_dns_resolver.h b/api/async_dns_resolver.h index cbe921b012..138503b59f 100644 --- a/api/async_dns_resolver.h +++ b/api/async_dns_resolver.h @@ -51,6 +51,11 @@ class AsyncDnsResolverResult { 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; @@ -70,7 +75,7 @@ class AsyncDnsResolverFactoryInterface { // Creates an AsyncDnsResolver and starts resolving the name. The callback // will be called when resolution is finished. - // The callback will be called on the thread that the caller runs on. + // The callback will be called on the sequence that the caller runs on. virtual std::unique_ptr CreateAndResolve( const rtc::SocketAddress& addr, std::function callback) = 0; diff --git a/api/audio/BUILD.gn b/api/audio/BUILD.gn index d0465bbc40..49cf95dbce 100644 --- a/api/audio/BUILD.gn +++ b/api/audio/BUILD.gn @@ -95,6 +95,7 @@ rtc_source_set("echo_control") { rtc_source_set("echo_detector_creator") { visibility = [ "*" ] + allow_poison = [ "default_echo_detector" ] sources = [ "echo_detector_creator.cc", "echo_detector_creator.h", @@ -102,7 +103,7 @@ rtc_source_set("echo_detector_creator") { deps = [ "../../api:scoped_refptr", "../../modules/audio_processing:api", - "../../modules/audio_processing:audio_processing", + "../../modules/audio_processing:residual_echo_detector", "../../rtc_base:refcount", ] } diff --git a/api/audio/audio_frame.cc b/api/audio/audio_frame.cc index 0c39d51f11..3e12006386 100644 --- a/api/audio/audio_frame.cc +++ b/api/audio/audio_frame.cc @@ -11,8 +11,6 @@ #include "api/audio/audio_frame.h" #include -#include -#include #include "rtc_base/checks.h" #include "rtc_base/time_utils.h" @@ -24,28 +22,6 @@ AudioFrame::AudioFrame() { static_assert(sizeof(data_) == kMaxDataSizeBytes, "kMaxDataSizeBytes"); } -void swap(AudioFrame& a, AudioFrame& b) { - using std::swap; - swap(a.timestamp_, b.timestamp_); - swap(a.elapsed_time_ms_, b.elapsed_time_ms_); - swap(a.ntp_time_ms_, b.ntp_time_ms_); - swap(a.samples_per_channel_, b.samples_per_channel_); - swap(a.sample_rate_hz_, b.sample_rate_hz_); - swap(a.num_channels_, b.num_channels_); - swap(a.channel_layout_, b.channel_layout_); - swap(a.speech_type_, b.speech_type_); - swap(a.vad_activity_, b.vad_activity_); - swap(a.profile_timestamp_ms_, b.profile_timestamp_ms_); - swap(a.packet_infos_, b.packet_infos_); - const size_t length_a = a.samples_per_channel_ * a.num_channels_; - const size_t length_b = b.samples_per_channel_ * b.num_channels_; - RTC_DCHECK_LE(length_a, AudioFrame::kMaxDataSizeSamples); - RTC_DCHECK_LE(length_b, AudioFrame::kMaxDataSizeSamples); - std::swap_ranges(a.data_, a.data_ + std::max(length_a, length_b), b.data_); - swap(a.muted_, b.muted_); - swap(a.absolute_capture_timestamp_ms_, b.absolute_capture_timestamp_ms_); -} - void AudioFrame::Reset() { ResetWithoutMuting(); muted_ = true; diff --git a/api/audio/audio_frame.h b/api/audio/audio_frame.h index 726b9a98e3..d5dcb5f788 100644 --- a/api/audio/audio_frame.h +++ b/api/audio/audio_frame.h @@ -14,11 +14,8 @@ #include #include -#include - #include "api/audio/channel_layout.h" #include "api/rtp_packet_infos.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -60,7 +57,8 @@ class AudioFrame { AudioFrame(); - friend void swap(AudioFrame& a, AudioFrame& b); + AudioFrame(const AudioFrame&) = delete; + AudioFrame& operator=(const AudioFrame&) = delete; // Resets all members to their default state. void Reset(); @@ -168,8 +166,6 @@ class AudioFrame { // capture timestamp of a received frame is found in `packet_infos_`. // This timestamp MUST be based on the same clock as rtc::TimeMillis(). absl::optional absolute_capture_timestamp_ms_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioFrame); }; } // namespace webrtc diff --git a/api/audio/channel_layout.cc b/api/audio/channel_layout.cc index 567f4d9b26..e4ae356fab 100644 --- a/api/audio/channel_layout.cc +++ b/api/audio/channel_layout.cc @@ -275,7 +275,7 @@ const char* ChannelLayoutToString(ChannelLayout layout) { case CHANNEL_LAYOUT_BITSTREAM: return "BITSTREAM"; } - RTC_NOTREACHED() << "Invalid channel layout provided: " << layout; + RTC_DCHECK_NOTREACHED() << "Invalid channel layout provided: " << layout; return ""; } diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h index 0df6450872..1fd403652a 100644 --- a/api/audio/echo_canceller3_config.h +++ b/api/audio/echo_canceller3_config.h @@ -112,7 +112,7 @@ struct RTC_EXPORT EchoCanceller3Config { bool echo_can_saturate = true; bool bounded_erl = false; bool erle_onset_compensation_in_dominant_nearend = false; - bool use_conservative_tail_frequency_response = false; + bool use_conservative_tail_frequency_response = true; } ep_strength; struct EchoAudibility { @@ -209,6 +209,7 @@ struct RTC_EXPORT EchoCanceller3Config { int hold_duration = 50; int trigger_threshold = 12; bool use_during_initial_phase = true; + bool use_unbounded_echo_spectrum = true; } dominant_nearend_detection; struct SubbandNearendDetection { diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc index f6ffd575ea..71966c13b3 100644 --- a/api/audio/echo_canceller3_config_json.cc +++ b/api/audio/echo_canceller3_config_json.cc @@ -374,6 +374,9 @@ void Aec3ConfigFromJsonString(absl::string_view json_string, ReadParam( subsection, "use_during_initial_phase", &cfg.suppressor.dominant_nearend_detection.use_during_initial_phase); + ReadParam(subsection, "use_unbounded_echo_spectrum", + &cfg.suppressor.dominant_nearend_detection + .use_unbounded_echo_spectrum); } if (rtc::GetValueFromJsonObject(section, "subband_nearend_detection", @@ -684,20 +687,20 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) { << config.suppressor.last_lf_smoothing_band << ","; ost << "\"last_lf_band\": " << config.suppressor.last_lf_band << ","; ost << "\"first_hf_band\": " << config.suppressor.first_hf_band << ","; - ost << "\"dominant_nearend_detection\": {"; - ost << "\"enr_threshold\": " - << config.suppressor.dominant_nearend_detection.enr_threshold << ","; - ost << "\"enr_exit_threshold\": " - << config.suppressor.dominant_nearend_detection.enr_exit_threshold << ","; - ost << "\"snr_threshold\": " - << config.suppressor.dominant_nearend_detection.snr_threshold << ","; - ost << "\"hold_duration\": " - << config.suppressor.dominant_nearend_detection.hold_duration << ","; - ost << "\"trigger_threshold\": " - << config.suppressor.dominant_nearend_detection.trigger_threshold << ","; - ost << "\"use_during_initial_phase\": " - << config.suppressor.dominant_nearend_detection.use_during_initial_phase; - ost << "},"; + { + const auto& dnd = config.suppressor.dominant_nearend_detection; + ost << "\"dominant_nearend_detection\": {"; + ost << "\"enr_threshold\": " << dnd.enr_threshold << ","; + ost << "\"enr_exit_threshold\": " << dnd.enr_exit_threshold << ","; + ost << "\"snr_threshold\": " << dnd.snr_threshold << ","; + ost << "\"hold_duration\": " << dnd.hold_duration << ","; + ost << "\"trigger_threshold\": " << dnd.trigger_threshold << ","; + ost << "\"use_during_initial_phase\": " << dnd.use_during_initial_phase + << ","; + ost << "\"use_unbounded_echo_spectrum\": " + << dnd.use_unbounded_echo_spectrum; + ost << "},"; + } ost << "\"subband_nearend_detection\": {"; ost << "\"nearend_average_blocks\": " << config.suppressor.subband_nearend_detection.nearend_average_blocks diff --git a/api/audio/test/audio_frame_unittest.cc b/api/audio/test/audio_frame_unittest.cc index f8d3318274..dbf45ceabc 100644 --- a/api/audio/test/audio_frame_unittest.cc +++ b/api/audio/test/audio_frame_unittest.cc @@ -133,54 +133,4 @@ TEST(AudioFrameTest, CopyFrom) { EXPECT_EQ(0, memcmp(frame2.data(), frame1.data(), sizeof(samples))); } -TEST(AudioFrameTest, SwapFrames) { - AudioFrame frame1, frame2; - int16_t samples1[kNumChannelsMono * kSamplesPerChannel]; - for (size_t i = 0; i < kNumChannelsMono * kSamplesPerChannel; ++i) { - samples1[i] = i; - } - frame1.UpdateFrame(kTimestamp, samples1, kSamplesPerChannel, kSampleRateHz, - AudioFrame::kPLC, AudioFrame::kVadActive, - kNumChannelsMono); - frame1.set_absolute_capture_timestamp_ms(12345678); - const auto frame1_channel_layout = frame1.channel_layout(); - - int16_t samples2[(kNumChannelsMono + 1) * (kSamplesPerChannel + 1)]; - for (size_t i = 0; i < (kNumChannelsMono + 1) * (kSamplesPerChannel + 1); - ++i) { - samples2[i] = 1000 + i; - } - frame2.UpdateFrame(kTimestamp + 1, samples2, kSamplesPerChannel + 1, - kSampleRateHz + 1, AudioFrame::kNormalSpeech, - AudioFrame::kVadPassive, kNumChannelsMono + 1); - const auto frame2_channel_layout = frame2.channel_layout(); - - swap(frame1, frame2); - - EXPECT_EQ(kTimestamp + 1, frame1.timestamp_); - ASSERT_EQ(kSamplesPerChannel + 1, frame1.samples_per_channel_); - EXPECT_EQ(kSampleRateHz + 1, frame1.sample_rate_hz_); - EXPECT_EQ(AudioFrame::kNormalSpeech, frame1.speech_type_); - EXPECT_EQ(AudioFrame::kVadPassive, frame1.vad_activity_); - ASSERT_EQ(kNumChannelsMono + 1, frame1.num_channels_); - for (size_t i = 0; i < (kNumChannelsMono + 1) * (kSamplesPerChannel + 1); - ++i) { - EXPECT_EQ(samples2[i], frame1.data()[i]); - } - EXPECT_FALSE(frame1.absolute_capture_timestamp_ms()); - EXPECT_EQ(frame2_channel_layout, frame1.channel_layout()); - - EXPECT_EQ(kTimestamp, frame2.timestamp_); - ASSERT_EQ(kSamplesPerChannel, frame2.samples_per_channel_); - EXPECT_EQ(kSampleRateHz, frame2.sample_rate_hz_); - EXPECT_EQ(AudioFrame::kPLC, frame2.speech_type_); - EXPECT_EQ(AudioFrame::kVadActive, frame2.vad_activity_); - ASSERT_EQ(kNumChannelsMono, frame2.num_channels_); - for (size_t i = 0; i < kNumChannelsMono * kSamplesPerChannel; ++i) { - EXPECT_EQ(samples1[i], frame2.data()[i]); - } - EXPECT_EQ(12345678, frame2.absolute_capture_timestamp_ms()); - EXPECT_EQ(frame1_channel_layout, frame2.channel_layout()); -} - } // namespace webrtc diff --git a/api/audio_codecs/BUILD.gn b/api/audio_codecs/BUILD.gn index 5926f5ec2e..3c84af8d19 100644 --- a/api/audio_codecs/BUILD.gn +++ b/api/audio_codecs/BUILD.gn @@ -32,6 +32,7 @@ rtc_library("audio_codecs_api") { "..:array_view", "..:bitrate_allocation", "..:scoped_refptr", + "../../api:webrtc_key_value_config", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", "../../rtc_base:sanitizer", diff --git a/api/audio_codecs/L16/BUILD.gn b/api/audio_codecs/L16/BUILD.gn index 1f7a1e5a0b..9e4a0f1002 100644 --- a/api/audio_codecs/L16/BUILD.gn +++ b/api/audio_codecs/L16/BUILD.gn @@ -21,6 +21,7 @@ rtc_library("audio_encoder_L16") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:pcm16b", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", @@ -41,6 +42,7 @@ rtc_library("audio_decoder_L16") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:pcm16b", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", diff --git a/api/audio_codecs/L16/audio_decoder_L16.cc b/api/audio_codecs/L16/audio_decoder_L16.cc index 17b43c17bd..d2380d7334 100644 --- a/api/audio_codecs/L16/audio_decoder_L16.cc +++ b/api/audio_codecs/L16/audio_decoder_L16.cc @@ -24,9 +24,10 @@ absl::optional AudioDecoderL16::SdpToConfig( Config config; config.sample_rate_hz = format.clockrate_hz; config.num_channels = rtc::checked_cast(format.num_channels); - return absl::EqualsIgnoreCase(format.name, "L16") && config.IsOk() - ? absl::optional(config) - : absl::nullopt; + if (absl::EqualsIgnoreCase(format.name, "L16") && config.IsOk()) { + return config; + } + return absl::nullopt; } void AudioDecoderL16::AppendSupportedDecoders( @@ -37,10 +38,13 @@ void AudioDecoderL16::AppendSupportedDecoders( std::unique_ptr AudioDecoderL16::MakeAudioDecoder( const Config& config, - absl::optional /*codec_pair_id*/) { - return config.IsOk() ? std::make_unique( - config.sample_rate_hz, config.num_channels) - : nullptr; + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + return nullptr; + } + return std::make_unique(config.sample_rate_hz, + config.num_channels); } } // namespace webrtc diff --git a/api/audio_codecs/L16/audio_decoder_L16.h b/api/audio_codecs/L16/audio_decoder_L16.h index f0be03659c..ade8f98088 100644 --- a/api/audio_codecs/L16/audio_decoder_L16.h +++ b/api/audio_codecs/L16/audio_decoder_L16.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -29,7 +30,8 @@ struct RTC_EXPORT AudioDecoderL16 { 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 >= 1 && + num_channels <= AudioDecoder::kMaxNumberOfChannels); } int sample_rate_hz = 8000; int num_channels = 1; @@ -38,7 +40,8 @@ struct RTC_EXPORT AudioDecoderL16 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( const Config& config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/L16/audio_encoder_L16.cc b/api/audio_codecs/L16/audio_encoder_L16.cc index 249b39d684..6f2260d283 100644 --- a/api/audio_codecs/L16/audio_encoder_L16.cc +++ b/api/audio_codecs/L16/audio_encoder_L16.cc @@ -24,6 +24,7 @@ namespace webrtc { absl::optional AudioEncoderL16::SdpToConfig( const SdpAudioFormat& format) { if (!rtc::IsValueInRangeForNumericType(format.num_channels)) { + RTC_DCHECK_NOTREACHED(); return absl::nullopt; } Config config; @@ -36,9 +37,10 @@ absl::optional AudioEncoderL16::SdpToConfig( config.frame_size_ms = rtc::SafeClamp(10 * (*ptime / 10), 10, 60); } } - return absl::EqualsIgnoreCase(format.name, "L16") && config.IsOk() - ? absl::optional(config) - : absl::nullopt; + if (absl::EqualsIgnoreCase(format.name, "L16") && config.IsOk()) { + return config; + } + return absl::nullopt; } void AudioEncoderL16::AppendSupportedEncoders( @@ -58,13 +60,17 @@ AudioCodecInfo AudioEncoderL16::QueryAudioEncoder( std::unique_ptr AudioEncoderL16::MakeAudioEncoder( const AudioEncoderL16::Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { AudioEncoderPcm16B::Config c; c.sample_rate_hz = config.sample_rate_hz; c.num_channels = config.num_channels; c.frame_size_ms = config.frame_size_ms; c.payload_type = payload_type; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(c); } diff --git a/api/audio_codecs/L16/audio_encoder_L16.h b/api/audio_codecs/L16/audio_encoder_L16.h index b410286802..e0916dfb24 100644 --- a/api/audio_codecs/L16/audio_encoder_L16.h +++ b/api/audio_codecs/L16/audio_encoder_L16.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -29,7 +30,9 @@ struct RTC_EXPORT AudioEncoderL16 { bool IsOk() const { return (sample_rate_hz == 8000 || sample_rate_hz == 16000 || sample_rate_hz == 32000 || sample_rate_hz == 48000) && - num_channels >= 1 && frame_size_ms > 0 && frame_size_ms <= 120 && + 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; @@ -42,7 +45,8 @@ struct RTC_EXPORT AudioEncoderL16 { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/audio_decoder.cc b/api/audio_codecs/audio_decoder.cc index 4fc995172b..28f5b8aae8 100644 --- a/api/audio_codecs/audio_decoder.cc +++ b/api/audio_codecs/audio_decoder.cc @@ -161,9 +161,10 @@ AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) { case 2: return kComfortNoise; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kSpeech; } } +constexpr int AudioDecoder::kMaxNumberOfChannels; } // namespace webrtc diff --git a/api/audio_codecs/audio_decoder.h b/api/audio_codecs/audio_decoder.h index 51d20c4982..41138741bb 100644 --- a/api/audio_codecs/audio_decoder.h +++ b/api/audio_codecs/audio_decoder.h @@ -20,7 +20,6 @@ #include "absl/types/optional.h" #include "api/array_view.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -37,6 +36,9 @@ class AudioDecoder { AudioDecoder() = default; virtual ~AudioDecoder() = default; + AudioDecoder(const AudioDecoder&) = delete; + AudioDecoder& operator=(const AudioDecoder&) = delete; + class EncodedAudioFrame { public: struct DecodeResult { @@ -170,6 +172,9 @@ class AudioDecoder { // 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); @@ -184,9 +189,6 @@ class AudioDecoder { int sample_rate_hz, int16_t* decoded, SpeechType* speech_type); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoder); }; } // namespace webrtc diff --git a/api/audio_codecs/audio_decoder_factory_template.h b/api/audio_codecs/audio_decoder_factory_template.h index 976f9c62d7..4badd825b1 100644 --- a/api/audio_codecs/audio_decoder_factory_template.h +++ b/api/audio_codecs/audio_decoder_factory_template.h @@ -16,6 +16,7 @@ #include "api/audio_codecs/audio_decoder_factory.h" #include "api/scoped_refptr.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -32,7 +33,8 @@ struct Helper<> { static bool IsSupportedDecoder(const SdpAudioFormat& format) { return false; } static std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const WebRtcKeyValueConfig* field_trials) { return nullptr; } }; @@ -55,16 +57,22 @@ struct Helper { } static std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const WebRtcKeyValueConfig* field_trials) { auto opt_config = T::SdpToConfig(format); return opt_config ? T::MakeAudioDecoder(*opt_config, codec_pair_id) - : Helper::MakeAudioDecoder(format, codec_pair_id); + : Helper::MakeAudioDecoder(format, codec_pair_id, + field_trials); } }; template class AudioDecoderFactoryT : public AudioDecoderFactory { public: + explicit AudioDecoderFactoryT(const WebRtcKeyValueConfig* field_trials) { + field_trials_ = field_trials; + } + std::vector GetSupportedDecoders() override { std::vector specs; Helper::AppendSupportedDecoders(&specs); @@ -78,8 +86,11 @@ class AudioDecoderFactoryT : public AudioDecoderFactory { std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, absl::optional codec_pair_id) override { - return Helper::MakeAudioDecoder(format, codec_pair_id); + return Helper::MakeAudioDecoder(format, codec_pair_id, + field_trials_); } + + const WebRtcKeyValueConfig* field_trials_; }; } // namespace audio_decoder_factory_template_impl @@ -115,7 +126,8 @@ class AudioDecoderFactoryT : public AudioDecoderFactory { // TODO(kwiberg): Point at CreateBuiltinAudioDecoderFactory() for an example of // how it is used. template -rtc::scoped_refptr CreateAudioDecoderFactory() { +rtc::scoped_refptr CreateAudioDecoderFactory( + const WebRtcKeyValueConfig* field_trials = nullptr) { // There's no technical reason we couldn't allow zero template parameters, // but such a factory couldn't create any decoders, and callers can do this // by mistake by simply forgetting the <> altogether. So we forbid it in @@ -124,7 +136,8 @@ rtc::scoped_refptr CreateAudioDecoderFactory() { "Caller must give at least one template parameter"); return rtc::make_ref_counted< - audio_decoder_factory_template_impl::AudioDecoderFactoryT>(); + audio_decoder_factory_template_impl::AudioDecoderFactoryT>( + field_trials); } } // namespace webrtc diff --git a/api/audio_codecs/audio_encoder.cc b/api/audio_codecs/audio_encoder.cc index cd4d2000d2..31bb8739f7 100644 --- a/api/audio_codecs/audio_encoder.cc +++ b/api/audio_codecs/audio_encoder.cc @@ -83,7 +83,7 @@ void AudioEncoder::OnReceivedUplinkPacketLossFraction( void AudioEncoder::OnReceivedUplinkRecoverablePacketLossFraction( float uplink_recoverable_packet_loss_fraction) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } void AudioEncoder::OnReceivedTargetAudioBitrate(int target_audio_bitrate_bps) { @@ -110,4 +110,5 @@ ANAStats AudioEncoder::GetANAStats() const { return ANAStats(); } +constexpr int AudioEncoder::kMaxNumberOfChannels; } // namespace webrtc diff --git a/api/audio_codecs/audio_encoder.h b/api/audio_codecs/audio_encoder.h index ead24027f7..61ed89015e 100644 --- a/api/audio_codecs/audio_encoder.h +++ b/api/audio_codecs/audio_encoder.h @@ -276,6 +276,9 @@ class AudioEncoder { virtual absl::optional> GetFrameLengthRange() const = 0; + // The maximum number of audio channels supported by WebRTC encoders. + static constexpr int kMaxNumberOfChannels = 24; + // RingRTC Change to configure OPUS virtual bool Configure(const Config& config) { RTC_LOG(LS_WARNING) << "Default AudioEncoder::Configure(...) does nothing!"; diff --git a/api/audio_codecs/audio_encoder_factory_template.h b/api/audio_codecs/audio_encoder_factory_template.h index 4dc0672c46..ceefab2dd5 100644 --- a/api/audio_codecs/audio_encoder_factory_template.h +++ b/api/audio_codecs/audio_encoder_factory_template.h @@ -16,6 +16,7 @@ #include "api/audio_codecs/audio_encoder_factory.h" #include "api/scoped_refptr.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -36,7 +37,8 @@ struct Helper<> { static std::unique_ptr MakeAudioEncoder( int payload_type, const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const WebRtcKeyValueConfig* field_trials) { return nullptr; } }; @@ -63,13 +65,14 @@ struct Helper { static std::unique_ptr MakeAudioEncoder( int payload_type, const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const WebRtcKeyValueConfig* field_trials) { auto opt_config = T::SdpToConfig(format); if (opt_config) { return T::MakeAudioEncoder(*opt_config, payload_type, codec_pair_id); } else { return Helper::MakeAudioEncoder(payload_type, format, - codec_pair_id); + codec_pair_id, field_trials); } } }; @@ -77,6 +80,10 @@ struct Helper { template class AudioEncoderFactoryT : public AudioEncoderFactory { public: + explicit AudioEncoderFactoryT(const WebRtcKeyValueConfig* field_trials) { + field_trials_ = field_trials; + } + std::vector GetSupportedEncoders() override { std::vector specs; Helper::AppendSupportedEncoders(&specs); @@ -92,8 +99,11 @@ class AudioEncoderFactoryT : public AudioEncoderFactory { int payload_type, const SdpAudioFormat& format, absl::optional codec_pair_id) override { - return Helper::MakeAudioEncoder(payload_type, format, codec_pair_id); + return Helper::MakeAudioEncoder(payload_type, format, codec_pair_id, + field_trials_); } + + const WebRtcKeyValueConfig* field_trials_; }; } // namespace audio_encoder_factory_template_impl @@ -134,7 +144,8 @@ class AudioEncoderFactoryT : public AudioEncoderFactory { // TODO(kwiberg): Point at CreateBuiltinAudioEncoderFactory() for an example of // how it is used. template -rtc::scoped_refptr CreateAudioEncoderFactory() { +rtc::scoped_refptr CreateAudioEncoderFactory( + const WebRtcKeyValueConfig* field_trials = nullptr) { // There's no technical reason we couldn't allow zero template parameters, // but such a factory couldn't create any encoders, and callers can do this // by mistake by simply forgetting the <> altogether. So we forbid it in @@ -143,7 +154,8 @@ rtc::scoped_refptr CreateAudioEncoderFactory() { "Caller must give at least one template parameter"); return rtc::make_ref_counted< - audio_encoder_factory_template_impl::AudioEncoderFactoryT>(); + audio_encoder_factory_template_impl::AudioEncoderFactoryT>( + field_trials); } } // namespace webrtc diff --git a/api/audio_codecs/builtin_audio_encoder_factory.cc b/api/audio_codecs/builtin_audio_encoder_factory.cc index 99fac09a57..4433893c1b 100644 --- a/api/audio_codecs/builtin_audio_encoder_factory.cc +++ b/api/audio_codecs/builtin_audio_encoder_factory.cc @@ -47,8 +47,10 @@ struct NotAdvertised { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt) { - return T::MakeAudioEncoder(config, payload_type, codec_pair_id); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr) { + return T::MakeAudioEncoder(config, payload_type, codec_pair_id, + field_trials); } }; diff --git a/api/audio_codecs/g711/BUILD.gn b/api/audio_codecs/g711/BUILD.gn index 92d77bed9f..1f0b7dff7a 100644 --- a/api/audio_codecs/g711/BUILD.gn +++ b/api/audio_codecs/g711/BUILD.gn @@ -21,6 +21,7 @@ rtc_library("audio_encoder_g711") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:g711", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", @@ -41,6 +42,7 @@ rtc_library("audio_decoder_g711") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:g711", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", diff --git a/api/audio_codecs/g711/audio_decoder_g711.cc b/api/audio_codecs/g711/audio_decoder_g711.cc index 194baa2efb..50d1d54b9e 100644 --- a/api/audio_codecs/g711/audio_decoder_g711.cc +++ b/api/audio_codecs/g711/audio_decoder_g711.cc @@ -28,7 +28,10 @@ absl::optional AudioDecoderG711::SdpToConfig( Config config; config.type = is_pcmu ? Config::Type::kPcmU : Config::Type::kPcmA; config.num_channels = rtc::dchecked_cast(format.num_channels); - RTC_DCHECK(config.IsOk()); + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -45,14 +48,19 @@ void AudioDecoderG711::AppendSupportedDecoders( std::unique_ptr AudioDecoderG711::MakeAudioDecoder( const Config& config, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } switch (config.type) { case Config::Type::kPcmU: return std::make_unique(config.num_channels); case Config::Type::kPcmA: return std::make_unique(config.num_channels); default: + RTC_DCHECK_NOTREACHED(); return nullptr; } } diff --git a/api/audio_codecs/g711/audio_decoder_g711.h b/api/audio_codecs/g711/audio_decoder_g711.h index ccd1ee0480..62b0f880d5 100644 --- a/api/audio_codecs/g711/audio_decoder_g711.h +++ b/api/audio_codecs/g711/audio_decoder_g711.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -28,7 +29,9 @@ struct RTC_EXPORT AudioDecoderG711 { struct Config { enum class Type { kPcmU, kPcmA }; bool IsOk() const { - return (type == Type::kPcmU || type == Type::kPcmA) && num_channels >= 1; + return (type == Type::kPcmU || type == Type::kPcmA) && + num_channels >= 1 && + num_channels <= AudioDecoder::kMaxNumberOfChannels; } Type type; int num_channels; @@ -37,7 +40,8 @@ struct RTC_EXPORT AudioDecoderG711 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( const Config& config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g711/audio_encoder_g711.cc b/api/audio_codecs/g711/audio_encoder_g711.cc index a12335d2ed..bdd22f3185 100644 --- a/api/audio_codecs/g711/audio_encoder_g711.cc +++ b/api/audio_codecs/g711/audio_encoder_g711.cc @@ -38,7 +38,10 @@ absl::optional AudioEncoderG711::SdpToConfig( config.frame_size_ms = rtc::SafeClamp(10 * (*ptime / 10), 10, 60); } } - RTC_DCHECK(config.IsOk()); + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -62,8 +65,12 @@ AudioCodecInfo AudioEncoderG711::QueryAudioEncoder(const Config& config) { std::unique_ptr AudioEncoderG711::MakeAudioEncoder( const Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } switch (config.type) { case Config::Type::kPcmU: { AudioEncoderPcmU::Config impl_config; @@ -80,6 +87,7 @@ std::unique_ptr AudioEncoderG711::MakeAudioEncoder( return std::make_unique(impl_config); } default: { + RTC_DCHECK_NOTREACHED(); return nullptr; } } diff --git a/api/audio_codecs/g711/audio_encoder_g711.h b/api/audio_codecs/g711/audio_encoder_g711.h index 23ae18b5e3..c2750e26ec 100644 --- a/api/audio_codecs/g711/audio_encoder_g711.h +++ b/api/audio_codecs/g711/audio_encoder_g711.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -29,7 +30,9 @@ struct RTC_EXPORT AudioEncoderG711 { 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; + frame_size_ms > 0 && frame_size_ms % 10 == 0 && + num_channels >= 1 && + num_channels <= AudioEncoder::kMaxNumberOfChannels; } Type type = Type::kPcmU; int num_channels = 1; @@ -42,7 +45,8 @@ struct RTC_EXPORT AudioEncoderG711 { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g722/BUILD.gn b/api/audio_codecs/g722/BUILD.gn index a186eabbb7..fbce2c5e33 100644 --- a/api/audio_codecs/g722/BUILD.gn +++ b/api/audio_codecs/g722/BUILD.gn @@ -27,6 +27,7 @@ rtc_library("audio_encoder_g722") { deps = [ ":audio_encoder_g722_config", "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:g722", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", @@ -47,6 +48,7 @@ rtc_library("audio_decoder_g722") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:g722", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", diff --git a/api/audio_codecs/g722/audio_decoder_g722.cc b/api/audio_codecs/g722/audio_decoder_g722.cc index a1451f8b79..4c29cacf6d 100644 --- a/api/audio_codecs/g722/audio_decoder_g722.cc +++ b/api/audio_codecs/g722/audio_decoder_g722.cc @@ -21,12 +21,12 @@ namespace webrtc { absl::optional AudioDecoderG722::SdpToConfig( const SdpAudioFormat& format) { - return absl::EqualsIgnoreCase(format.name, "G722") && - format.clockrate_hz == 8000 && - (format.num_channels == 1 || format.num_channels == 2) - ? absl::optional( - Config{rtc::dchecked_cast(format.num_channels)}) - : absl::nullopt; + if (absl::EqualsIgnoreCase(format.name, "G722") && + format.clockrate_hz == 8000 && + (format.num_channels == 1 || format.num_channels == 2)) { + return Config{rtc::dchecked_cast(format.num_channels)}; + } + return absl::nullopt; } void AudioDecoderG722::AppendSupportedDecoders( @@ -37,13 +37,19 @@ void AudioDecoderG722::AppendSupportedDecoders( std::unique_ptr AudioDecoderG722::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } switch (config.num_channels) { case 1: return std::make_unique(); case 2: return std::make_unique(); default: + RTC_DCHECK_NOTREACHED(); return nullptr; } } diff --git a/api/audio_codecs/g722/audio_decoder_g722.h b/api/audio_codecs/g722/audio_decoder_g722.h index 2a674926db..05e613055a 100644 --- a/api/audio_codecs/g722/audio_decoder_g722.h +++ b/api/audio_codecs/g722/audio_decoder_g722.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -33,7 +34,8 @@ struct RTC_EXPORT AudioDecoderG722 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g722/audio_encoder_g722.cc b/api/audio_codecs/g722/audio_encoder_g722.cc index 4fb312ed5d..6707cf0249 100644 --- a/api/audio_codecs/g722/audio_encoder_g722.cc +++ b/api/audio_codecs/g722/audio_encoder_g722.cc @@ -38,8 +38,11 @@ absl::optional AudioEncoderG722::SdpToConfig( config.frame_size_ms = rtc::SafeClamp(whole_packets * 10, 10, 60); } } - return config.IsOk() ? absl::optional(config) - : absl::nullopt; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } + return config; } void AudioEncoderG722::AppendSupportedEncoders( @@ -60,8 +63,12 @@ AudioCodecInfo AudioEncoderG722::QueryAudioEncoder( std::unique_ptr AudioEncoderG722::MakeAudioEncoder( const AudioEncoderG722Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(config, payload_type); } diff --git a/api/audio_codecs/g722/audio_encoder_g722.h b/api/audio_codecs/g722/audio_encoder_g722.h index 327c0af04a..14ddff72bd 100644 --- a/api/audio_codecs/g722/audio_encoder_g722.h +++ b/api/audio_codecs/g722/audio_encoder_g722.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/g722/audio_encoder_g722_config.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioEncoderG722 { static std::unique_ptr MakeAudioEncoder( const AudioEncoderG722Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g722/audio_encoder_g722_config.h b/api/audio_codecs/g722/audio_encoder_g722_config.h index 287898589f..f85eef00a8 100644 --- a/api/audio_codecs/g722/audio_encoder_g722_config.h +++ b/api/audio_codecs/g722/audio_encoder_g722_config.h @@ -15,7 +15,8 @@ namespace webrtc { struct AudioEncoderG722Config { bool IsOk() const { - return frame_size_ms > 0 && frame_size_ms % 10 == 0 && num_channels >= 1; + 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; diff --git a/api/audio_codecs/ilbc/BUILD.gn b/api/audio_codecs/ilbc/BUILD.gn index b6a5045eaf..1cac7ed7b9 100644 --- a/api/audio_codecs/ilbc/BUILD.gn +++ b/api/audio_codecs/ilbc/BUILD.gn @@ -27,6 +27,7 @@ rtc_library("audio_encoder_ilbc") { deps = [ ":audio_encoder_ilbc_config", "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:ilbc", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", @@ -46,6 +47,7 @@ rtc_library("audio_decoder_ilbc") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:ilbc", "../../../rtc_base:rtc_base_approved", ] diff --git a/api/audio_codecs/ilbc/audio_decoder_ilbc.cc b/api/audio_codecs/ilbc/audio_decoder_ilbc.cc index 3b4ae69608..9393335f02 100644 --- a/api/audio_codecs/ilbc/audio_decoder_ilbc.cc +++ b/api/audio_codecs/ilbc/audio_decoder_ilbc.cc @@ -20,10 +20,11 @@ namespace webrtc { absl::optional AudioDecoderIlbc::SdpToConfig( const SdpAudioFormat& format) { - return absl::EqualsIgnoreCase(format.name, "ILBC") && - format.clockrate_hz == 8000 && format.num_channels == 1 - ? absl::optional(Config()) - : absl::nullopt; + if (absl::EqualsIgnoreCase(format.name, "ILBC") && + format.clockrate_hz == 8000 && format.num_channels == 1) { + return Config(); + } + return absl::nullopt; } void AudioDecoderIlbc::AppendSupportedDecoders( @@ -34,7 +35,8 @@ void AudioDecoderIlbc::AppendSupportedDecoders( std::unique_ptr AudioDecoderIlbc::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { return std::make_unique(); } diff --git a/api/audio_codecs/ilbc/audio_decoder_ilbc.h b/api/audio_codecs/ilbc/audio_decoder_ilbc.h index 9ab847977d..4d9a4b7d6a 100644 --- a/api/audio_codecs/ilbc/audio_decoder_ilbc.h +++ b/api/audio_codecs/ilbc/audio_decoder_ilbc.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" namespace webrtc { @@ -29,7 +30,8 @@ struct AudioDecoderIlbc { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/ilbc/audio_encoder_ilbc.cc b/api/audio_codecs/ilbc/audio_encoder_ilbc.cc index 60668d9d2c..f91575353c 100644 --- a/api/audio_codecs/ilbc/audio_encoder_ilbc.cc +++ b/api/audio_codecs/ilbc/audio_encoder_ilbc.cc @@ -53,8 +53,11 @@ absl::optional AudioEncoderIlbc::SdpToConfig( config.frame_size_ms = rtc::SafeClamp(whole_packets * 10, 20, 60); } } - return config.IsOk() ? absl::optional(config) - : absl::nullopt; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } + return config; } void AudioEncoderIlbc::AppendSupportedEncoders( @@ -74,8 +77,12 @@ AudioCodecInfo AudioEncoderIlbc::QueryAudioEncoder( std::unique_ptr AudioEncoderIlbc::MakeAudioEncoder( const AudioEncoderIlbcConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(config, payload_type); } diff --git a/api/audio_codecs/ilbc/audio_encoder_ilbc.h b/api/audio_codecs/ilbc/audio_encoder_ilbc.h index e4aeca70de..8dd4928558 100644 --- a/api/audio_codecs/ilbc/audio_encoder_ilbc.h +++ b/api/audio_codecs/ilbc/audio_encoder_ilbc.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/ilbc/audio_encoder_ilbc_config.h" +#include "api/webrtc_key_value_config.h" namespace webrtc { @@ -33,7 +34,8 @@ struct AudioEncoderIlbc { static std::unique_ptr MakeAudioEncoder( const AudioEncoderIlbcConfig& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/BUILD.gn b/api/audio_codecs/isac/BUILD.gn index 6ff6e5f092..b6aa810fb5 100644 --- a/api/audio_codecs/isac/BUILD.gn +++ b/api/audio_codecs/isac/BUILD.gn @@ -65,6 +65,7 @@ rtc_library("audio_encoder_isac_fix") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:isac_fix", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -84,6 +85,7 @@ rtc_library("audio_decoder_isac_fix") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:isac_fix", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -103,6 +105,7 @@ rtc_library("audio_encoder_isac_float") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:isac", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -122,6 +125,7 @@ rtc_library("audio_decoder_isac_float") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:isac", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.cc b/api/audio_codecs/isac/audio_decoder_isac_fix.cc index 50bc1e8743..83371b1d4b 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.cc +++ b/api/audio_codecs/isac/audio_decoder_isac_fix.cc @@ -19,10 +19,11 @@ namespace webrtc { absl::optional AudioDecoderIsacFix::SdpToConfig( const SdpAudioFormat& format) { - return absl::EqualsIgnoreCase(format.name, "ISAC") && - format.clockrate_hz == 16000 && format.num_channels == 1 - ? absl::optional(Config()) - : absl::nullopt; + if (absl::EqualsIgnoreCase(format.name, "ISAC") && + format.clockrate_hz == 16000 && format.num_channels == 1) { + return Config(); + } + return absl::nullopt; } void AudioDecoderIsacFix::AppendSupportedDecoders( @@ -33,7 +34,8 @@ void AudioDecoderIsacFix::AppendSupportedDecoders( std::unique_ptr AudioDecoderIsacFix::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { AudioDecoderIsacFixImpl::Config c; c.sample_rate_hz = 16000; return std::make_unique(c); diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.h b/api/audio_codecs/isac/audio_decoder_isac_fix.h index 200914adfe..11b87669c9 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.h +++ b/api/audio_codecs/isac/audio_decoder_isac_fix.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -30,7 +31,8 @@ struct RTC_EXPORT AudioDecoderIsacFix { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.cc b/api/audio_codecs/isac/audio_decoder_isac_float.cc index 6310316012..a5c2ced864 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_float.cc +++ b/api/audio_codecs/isac/audio_decoder_isac_float.cc @@ -24,6 +24,10 @@ AudioDecoderIsacFloat::SdpToConfig(const SdpAudioFormat& format) { format.num_channels == 1) { Config config; config.sample_rate_hz = format.clockrate_hz; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -39,10 +43,14 @@ void AudioDecoderIsacFloat::AppendSupportedDecoders( std::unique_ptr AudioDecoderIsacFloat::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { AudioDecoderIsacFloatImpl::Config c; c.sample_rate_hz = config.sample_rate_hz; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(c); } diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.h b/api/audio_codecs/isac/audio_decoder_isac_float.h index e78f8b81ee..501edfcab9 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_float.h +++ b/api/audio_codecs/isac/audio_decoder_isac_float.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -35,7 +36,8 @@ struct RTC_EXPORT AudioDecoderIsacFloat { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.cc b/api/audio_codecs/isac/audio_encoder_isac_fix.cc index 3aa8c0f5d5..5a13bce887 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.cc +++ b/api/audio_codecs/isac/audio_encoder_isac_fix.cc @@ -30,6 +30,10 @@ absl::optional AudioEncoderIsacFix::SdpToConfig( config.frame_size_ms = 60; } } + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -53,12 +57,16 @@ AudioCodecInfo AudioEncoderIsacFix::QueryAudioEncoder( std::unique_ptr AudioEncoderIsacFix::MakeAudioEncoder( AudioEncoderIsacFix::Config config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { AudioEncoderIsacFixImpl::Config c; c.frame_size_ms = config.frame_size_ms; c.bit_rate = config.bit_rate; c.payload_type = payload_type; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(c); } diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.h b/api/audio_codecs/isac/audio_encoder_isac_fix.h index e50d9f5112..e8ff0fc57b 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.h +++ b/api/audio_codecs/isac/audio_encoder_isac_fix.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -44,7 +45,8 @@ struct RTC_EXPORT AudioEncoderIsacFix { static std::unique_ptr MakeAudioEncoder( Config config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.cc b/api/audio_codecs/isac/audio_encoder_isac_float.cc index 5839a89334..1b0753b2f8 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_float.cc +++ b/api/audio_codecs/isac/audio_encoder_isac_float.cc @@ -37,6 +37,10 @@ AudioEncoderIsacFloat::SdpToConfig(const SdpAudioFormat& format) { } } } + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -65,13 +69,17 @@ AudioCodecInfo AudioEncoderIsacFloat::QueryAudioEncoder( std::unique_ptr AudioEncoderIsacFloat::MakeAudioEncoder( const AudioEncoderIsacFloat::Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { AudioEncoderIsacFloatImpl::Config c; c.payload_type = payload_type; c.sample_rate_hz = config.sample_rate_hz; c.frame_size_ms = config.frame_size_ms; c.bit_rate = config.bit_rate; + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(c); } diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.h b/api/audio_codecs/isac/audio_encoder_isac_float.h index 0cb9c17d71..8e1d505c31 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_float.h +++ b/api/audio_codecs/isac/audio_encoder_isac_float.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -58,7 +59,8 @@ struct RTC_EXPORT AudioEncoderIsacFloat { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/BUILD.gn b/api/audio_codecs/opus/BUILD.gn index 586e9b3dd8..fbb116b88a 100644 --- a/api/audio_codecs/opus/BUILD.gn +++ b/api/audio_codecs/opus/BUILD.gn @@ -46,6 +46,7 @@ rtc_library("audio_encoder_opus") { deps = [ ":audio_encoder_opus_config", "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:webrtc_opus", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -65,6 +66,7 @@ rtc_library("audio_decoder_opus") { ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:webrtc_opus", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -82,6 +84,7 @@ rtc_library("audio_encoder_multiopus") { sources = [ "audio_encoder_multi_channel_opus.cc" ] deps = [ "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:webrtc_multiopus", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", @@ -100,6 +103,7 @@ rtc_library("audio_decoder_multiopus") { deps = [ ":audio_decoder_opus_config", "..:audio_codecs_api", + "../../../api:webrtc_key_value_config", "../../../modules/audio_coding:webrtc_multiopus", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc index 6ba2b6d9d3..5a0b794430 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc @@ -64,7 +64,8 @@ void AudioDecoderMultiChannelOpus::AppendSupportedDecoders( std::unique_ptr AudioDecoderMultiChannelOpus::MakeAudioDecoder( AudioDecoderMultiChannelOpusConfig config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { return AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder(config); } } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h index b5ca0fe41b..2dcd26b102 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -32,7 +33,8 @@ struct RTC_EXPORT AudioDecoderMultiChannelOpus { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( AudioDecoderMultiChannelOpusConfig config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h index 30bc76e354..7350045bf5 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h @@ -30,7 +30,8 @@ struct AudioDecoderMultiChannelOpusConfig { std::vector channel_mapping; bool IsOk() const { - if (num_channels < 0 || num_streams < 0 || coupled_streams < 0) { + if (num_channels < 1 || num_channels > AudioDecoder::kMaxNumberOfChannels || + num_streams < 0 || coupled_streams < 0) { return false; } if (num_streams < coupled_streams) { diff --git a/api/audio_codecs/opus/audio_decoder_opus.cc b/api/audio_codecs/opus/audio_decoder_opus.cc index 6b4e0d3797..0fe3367a64 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.cc +++ b/api/audio_codecs/opus/audio_decoder_opus.cc @@ -51,7 +51,10 @@ absl::optional AudioDecoderOpus::SdpToConfig( num_channels) { Config config; config.num_channels = *num_channels; - RTC_DCHECK(config.IsOk()); + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } else { return absl::nullopt; @@ -70,8 +73,12 @@ void AudioDecoderOpus::AppendSupportedDecoders( std::unique_ptr AudioDecoderOpus::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { - RTC_DCHECK(config.IsOk()); + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(config.num_channels, config.sample_rate_hz); } diff --git a/api/audio_codecs/opus/audio_decoder_opus.h b/api/audio_codecs/opus/audio_decoder_opus.h index ec0f61d5bb..4e44309c24 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.h +++ b/api/audio_codecs/opus/audio_decoder_opus.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioDecoderOpus { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc index 758eaaeebe..b71a0003fd 100644 --- a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc +++ b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc @@ -66,7 +66,8 @@ AudioCodecInfo AudioEncoderMultiChannelOpus::QueryAudioEncoder( std::unique_ptr AudioEncoderMultiChannelOpus::MakeAudioEncoder( const AudioEncoderMultiChannelOpusConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { return AudioEncoderMultiChannelOpusImpl::MakeAudioEncoder(config, payload_type); } diff --git a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h index 977a3a4b9c..58b959a9b8 100644 --- a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h +++ b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -33,7 +34,8 @@ struct RTC_EXPORT AudioEncoderMultiChannelOpus { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_encoder_opus.cc b/api/audio_codecs/opus/audio_encoder_opus.cc index 36d82b3eff..26fe8cc786 100644 --- a/api/audio_codecs/opus/audio_encoder_opus.cc +++ b/api/audio_codecs/opus/audio_encoder_opus.cc @@ -32,7 +32,12 @@ AudioCodecInfo AudioEncoderOpus::QueryAudioEncoder( std::unique_ptr AudioEncoderOpus::MakeAudioEncoder( const AudioEncoderOpusConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const WebRtcKeyValueConfig* field_trials) { + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return AudioEncoderOpusImpl::MakeAudioEncoder(config, payload_type); } diff --git a/api/audio_codecs/opus/audio_encoder_opus.h b/api/audio_codecs/opus/audio_encoder_opus.h index 03cb0d6b38..4fb0337490 100644 --- a/api/audio_codecs/opus/audio_encoder_opus.h +++ b/api/audio_codecs/opus/audio_encoder_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_opus_config.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioEncoderOpus { static std::unique_ptr MakeAudioEncoder( const AudioEncoderOpusConfig& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus_audio_encoder_factory.cc b/api/audio_codecs/opus_audio_encoder_factory.cc index 5f0c7147f5..867d200e5f 100644 --- a/api/audio_codecs/opus_audio_encoder_factory.cc +++ b/api/audio_codecs/opus_audio_encoder_factory.cc @@ -37,8 +37,10 @@ struct NotAdvertised { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt) { - return T::MakeAudioEncoder(config, payload_type, codec_pair_id); + absl::optional codec_pair_id = absl::nullopt, + const WebRtcKeyValueConfig* field_trials = nullptr) { + return T::MakeAudioEncoder(config, payload_type, codec_pair_id, + field_trials); } }; diff --git a/api/audio_codecs/test/BUILD.gn b/api/audio_codecs/test/BUILD.gn index 575f062ce7..dabcc3da8d 100644 --- a/api/audio_codecs/test/BUILD.gn +++ b/api/audio_codecs/test/BUILD.gn @@ -23,6 +23,7 @@ if (rtc_include_tests) { "..:audio_codecs_api", "../../../rtc_base:rtc_base_approved", "../../../test:audio_codec_mocks", + "../../../test:scoped_key_value_config", "../../../test:test_support", "../L16:audio_decoder_L16", "../L16:audio_encoder_L16", diff --git a/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc b/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc index 464ecfd487..3662f3b76d 100644 --- a/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc @@ -22,6 +22,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_decoder.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -77,9 +78,11 @@ struct AudioDecoderFakeApi { } // namespace TEST(AudioDecoderFactoryTemplateTest, NoDecoderTypes) { + test::ScopedKeyValueConfig field_trials; rtc::scoped_refptr factory( rtc::make_ref_counted< - audio_decoder_factory_template_impl::AudioDecoderFactoryT<>>()); + audio_decoder_factory_template_impl::AudioDecoderFactoryT<>>( + &field_trials)); EXPECT_THAT(factory->GetSupportedDecoders(), ::testing::IsEmpty()); EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1})); EXPECT_EQ(nullptr, diff --git a/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc b/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc index 110f9930bd..67b6883583 100644 --- a/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc @@ -22,6 +22,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -77,9 +78,11 @@ struct AudioEncoderFakeApi { } // namespace TEST(AudioEncoderFactoryTemplateTest, NoEncoderTypes) { + test::ScopedKeyValueConfig field_trials; rtc::scoped_refptr factory( rtc::make_ref_counted< - audio_encoder_factory_template_impl::AudioEncoderFactoryT<>>()); + audio_encoder_factory_template_impl::AudioEncoderFactoryT<>>( + &field_trials)); EXPECT_THAT(factory->GetSupportedEncoders(), ::testing::IsEmpty()); EXPECT_EQ(absl::nullopt, factory->QueryAudioEncoder({"foo", 8000, 1})); EXPECT_EQ(nullptr, diff --git a/api/audio_options.cc b/api/audio_options.cc index 6832bbe294..fad35cb881 100644 --- a/api/audio_options.cc +++ b/api/audio_options.cc @@ -55,16 +55,11 @@ void AudioOptions::SetAll(const AudioOptions& change) { SetFrom(&audio_jitter_buffer_enable_rtx_handling, change.audio_jitter_buffer_enable_rtx_handling); SetFrom(&typing_detection, change.typing_detection); - SetFrom(&experimental_agc, change.experimental_agc); - SetFrom(&experimental_ns, change.experimental_ns); SetFrom(&residual_echo_detector, change.residual_echo_detector); - SetFrom(&tx_agc_target_dbov, change.tx_agc_target_dbov); - SetFrom(&tx_agc_digital_compression_gain, - change.tx_agc_digital_compression_gain); - SetFrom(&tx_agc_limiter, change.tx_agc_limiter); SetFrom(&combined_audio_video_bwe, change.combined_audio_video_bwe); SetFrom(&audio_network_adaptor, change.audio_network_adaptor); SetFrom(&audio_network_adaptor_config, change.audio_network_adaptor_config); + SetFrom(&init_recording_on_send, change.init_recording_on_send); } bool AudioOptions::operator==(const AudioOptions& o) const { @@ -84,15 +79,11 @@ bool AudioOptions::operator==(const AudioOptions& o) const { audio_jitter_buffer_enable_rtx_handling == o.audio_jitter_buffer_enable_rtx_handling && typing_detection == o.typing_detection && - experimental_agc == o.experimental_agc && - experimental_ns == o.experimental_ns && residual_echo_detector == o.residual_echo_detector && - tx_agc_target_dbov == o.tx_agc_target_dbov && - tx_agc_digital_compression_gain == o.tx_agc_digital_compression_gain && - tx_agc_limiter == o.tx_agc_limiter && combined_audio_video_bwe == o.combined_audio_video_bwe && audio_network_adaptor == o.audio_network_adaptor && - audio_network_adaptor_config == o.audio_network_adaptor_config; + audio_network_adaptor_config == o.audio_network_adaptor_config && + init_recording_on_send == o.init_recording_on_send; } std::string AudioOptions::ToString() const { @@ -117,15 +108,10 @@ std::string AudioOptions::ToString() const { ToStringIfSet(&result, "audio_jitter_buffer_enable_rtx_handling", audio_jitter_buffer_enable_rtx_handling); ToStringIfSet(&result, "typing", typing_detection); - ToStringIfSet(&result, "experimental_agc", experimental_agc); - ToStringIfSet(&result, "experimental_ns", experimental_ns); ToStringIfSet(&result, "residual_echo_detector", residual_echo_detector); - ToStringIfSet(&result, "tx_agc_target_dbov", tx_agc_target_dbov); - ToStringIfSet(&result, "tx_agc_digital_compression_gain", - tx_agc_digital_compression_gain); - ToStringIfSet(&result, "tx_agc_limiter", tx_agc_limiter); ToStringIfSet(&result, "combined_audio_video_bwe", combined_audio_video_bwe); ToStringIfSet(&result, "audio_network_adaptor", audio_network_adaptor); + ToStringIfSet(&result, "init_recording_on_send", init_recording_on_send); result << "}"; return result.str(); } diff --git a/api/audio_options.h b/api/audio_options.h index 1b0d1ad0bd..3fcc38d83f 100644 --- a/api/audio_options.h +++ b/api/audio_options.h @@ -60,15 +60,14 @@ struct RTC_EXPORT AudioOptions { absl::optional audio_jitter_buffer_min_delay_ms; // Audio receiver jitter buffer (NetEq) should handle retransmitted packets. absl::optional audio_jitter_buffer_enable_rtx_handling; + // Deprecated. + // TODO(bugs.webrtc.org/11226): Remove. // Audio processing to detect typing. absl::optional typing_detection; - absl::optional experimental_agc; - absl::optional experimental_ns; - // Note that tx_agc_* only applies to non-experimental AGC. + // TODO(bugs.webrtc.org/11539): Deprecated, replaced by + // webrtc::CreateEchoDetector() and injection when creating the audio + // processing module. absl::optional residual_echo_detector; - absl::optional tx_agc_target_dbov; - absl::optional tx_agc_digital_compression_gain; - absl::optional tx_agc_limiter; // Enable combined audio+bandwidth BWE. // TODO(pthatcher): This flag is set from the // "googCombinedAudioVideoBwe", but not used anywhere. So delete it, @@ -80,6 +79,10 @@ struct RTC_EXPORT AudioOptions { absl::optional audio_network_adaptor; // Config string for audio network adaptor. absl::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. + absl::optional init_recording_on_send; }; } // namespace cricket diff --git a/api/candidate.cc b/api/candidate.cc index ad65121bde..4d17256c2e 100644 --- a/api/candidate.cc +++ b/api/candidate.cc @@ -27,14 +27,14 @@ Candidate::Candidate() network_cost_(0) {} Candidate::Candidate(int component, - const std::string& protocol, + absl::string_view protocol, const rtc::SocketAddress& address, uint32_t priority, - const std::string& username, - const std::string& password, - const std::string& type, + absl::string_view username, + absl::string_view password, + absl::string_view type, uint32_t generation, - const std::string& foundation, + absl::string_view foundation, uint16_t network_id, uint16_t network_cost) : id_(rtc::CreateRandomString(8)), @@ -101,7 +101,9 @@ uint32_t Candidate::GetPriority(uint32_t type_preference, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired. // Addr Pref - Address preference value as per RFC 3484. - // local preference = (NIC Type << 8 | Addr_Pref) - relay preference. + // local preference = (NIC Type << 8 | Addr_Pref) + relay preference. + // The relay preference is based on the number of TURN servers, the + // first TURN server gets the highest preference. int addr_pref = IPAddressPrecedence(address_.ipaddr()); int local_preference = @@ -153,4 +155,11 @@ Candidate Candidate::ToSanitizedCopy(bool use_hostname_address, return copy; } +void Candidate::Assign(std::string& s, absl::string_view view) { + // Assigning via a temporary object, like s = std::string(view), results in + // binary size bloat. To avoid that, extract pointer and size from the + // string view, and use std::string::assign method. + s.assign(view.data(), view.size()); +} + } // namespace cricket diff --git a/api/candidate.h b/api/candidate.h index c9447bbf6c..b8aaebc14a 100644 --- a/api/candidate.h +++ b/api/candidate.h @@ -17,6 +17,7 @@ #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" @@ -33,32 +34,32 @@ class RTC_EXPORT Candidate { // TODO(pthatcher): Match the ordering and param list as per RFC 5245 // candidate-attribute syntax. http://tools.ietf.org/html/rfc5245#section-15.1 Candidate(int component, - const std::string& protocol, + absl::string_view protocol, const rtc::SocketAddress& address, uint32_t priority, - const std::string& username, - const std::string& password, - const std::string& type, + absl::string_view username, + absl::string_view password, + absl::string_view type, uint32_t generation, - const std::string& foundation, + absl::string_view foundation, uint16_t network_id = 0, uint16_t network_cost = 0); Candidate(const Candidate&); ~Candidate(); const std::string& id() const { return id_; } - void set_id(const std::string& id) { id_ = id; } + void set_id(absl::string_view id) { Assign(id_, id); } int component() const { return component_; } void set_component(int component) { component_ = component; } const std::string& protocol() const { return protocol_; } - void set_protocol(const std::string& protocol) { protocol_ = protocol; } + 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_; } - void set_relay_protocol(const std::string& protocol) { - relay_protocol_ = protocol; + void set_relay_protocol(absl::string_view protocol) { + Assign(relay_protocol_, protocol); } const rtc::SocketAddress& address() const { return address_; } @@ -90,17 +91,17 @@ class RTC_EXPORT Candidate { // TODO(honghaiz): Change to usernameFragment or ufrag. const std::string& username() const { return username_; } - void set_username(const std::string& username) { username_ = username; } + void set_username(absl::string_view username) { Assign(username_, username); } const std::string& password() const { return password_; } - void set_password(const std::string& password) { password_ = password; } + void set_password(absl::string_view password) { Assign(password_, password); } const std::string& type() const { return type_; } - void set_type(const std::string& type) { type_ = type; } + void set_type(absl::string_view type) { Assign(type_, type); } const std::string& network_name() const { return network_name_; } - void set_network_name(const std::string& network_name) { - network_name_ = network_name; + void set_network_name(absl::string_view network_name) { + Assign(network_name_, network_name); } rtc::AdapterType network_type() const { return network_type_; } @@ -108,6 +109,13 @@ class RTC_EXPORT Candidate { network_type_ = network_type; } + rtc::AdapterType underlying_type_for_vpn() const { + return underlying_type_for_vpn_; + } + void set_underlying_type_for_vpn(rtc::AdapterType network_type) { + underlying_type_for_vpn_ = network_type; + } + // Candidates in a new generation replace those in the old generation. uint32_t generation() const { return generation_; } void set_generation(uint32_t generation) { generation_ = generation; } @@ -126,8 +134,8 @@ class RTC_EXPORT Candidate { void set_network_id(uint16_t network_id) { network_id_ = network_id; } const std::string& foundation() const { return foundation_; } - void set_foundation(const std::string& foundation) { - foundation_ = foundation; + void set_foundation(absl::string_view foundation) { + Assign(foundation_, foundation); } const rtc::SocketAddress& related_address() const { return related_address_; } @@ -135,18 +143,18 @@ class RTC_EXPORT Candidate { related_address_ = related_address; } const std::string& tcptype() const { return tcptype_; } - void set_tcptype(const std::string& tcptype) { tcptype_ = 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(const std::string& transport_name) { - transport_name_ = 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(const std::string& url) { url_ = 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; @@ -177,6 +185,10 @@ class RTC_EXPORT Candidate { bool filter_related_address) const; 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_; @@ -190,6 +202,7 @@ class RTC_EXPORT Candidate { std::string type_; std::string network_name_; rtc::AdapterType network_type_; + rtc::AdapterType underlying_type_for_vpn_; uint32_t generation_; std::string foundation_; rtc::SocketAddress related_address_; diff --git a/api/create_peerconnection_factory.cc b/api/create_peerconnection_factory.cc index 008fce3e80..c41b6d6fb2 100644 --- a/api/create_peerconnection_factory.cc +++ b/api/create_peerconnection_factory.cc @@ -49,6 +49,10 @@ rtc::scoped_refptr CreatePeerConnectionFactory( dependencies.task_queue_factory.get()); dependencies.trials = std::make_unique(); + if (network_thread) { + // TODO(bugs.webrtc.org/13145): Add an rtc::SocketFactory* argument. + dependencies.socket_factory = network_thread->socketserver(); + } cricket::MediaEngineDependencies media_dependencies; media_dependencies.task_queue_factory = dependencies.task_queue_factory.get(); media_dependencies.adm = std::move(default_adm); diff --git a/api/data_channel_interface.cc b/api/data_channel_interface.cc index d299cedf45..bddb9d1b0a 100644 --- a/api/data_channel_interface.cc +++ b/api/data_channel_interface.cc @@ -40,4 +40,8 @@ bool DataChannelInterface::negotiated() const { return false; } +uint64_t DataChannelInterface::MaxSendQueueSize() { + return 16 * 1024 * 1024; // 16 MiB +} + } // namespace webrtc diff --git a/api/data_channel_interface.h b/api/data_channel_interface.h index 99ea551c2d..4f74918ff9 100644 --- a/api/data_channel_interface.h +++ b/api/data_channel_interface.h @@ -174,6 +174,7 @@ class RTC_EXPORT DataChannelInterface : public rtc::RefCountInterface { // 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: @@ -182,14 +183,16 @@ class RTC_EXPORT DataChannelInterface : public rtc::RefCountInterface { // 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 16MB. If Send is called while this buffer is full, the - // data channel will be closed abruptly. - // - // So, it's important to use buffered_amount() and OnBufferedAmountChange to - // ensure the data channel is used efficiently but without filling this - // buffer. + // 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. virtual bool Send(const DataBuffer& buffer) = 0; + // 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; }; diff --git a/api/dtls_transport_interface.cc b/api/dtls_transport_interface.cc index a68ff8feb0..faebc0972f 100644 --- a/api/dtls_transport_interface.cc +++ b/api/dtls_transport_interface.cc @@ -20,11 +20,27 @@ DtlsTransportInformation::DtlsTransportInformation(DtlsTransportState state) DtlsTransportInformation::DtlsTransportInformation( DtlsTransportState state, + absl::optional role, absl::optional tls_version, absl::optional ssl_cipher_suite, absl::optional srtp_cipher_suite, std::unique_ptr remote_ssl_certificates) : state_(state), + role_(role), + tls_version_(tls_version), + ssl_cipher_suite_(ssl_cipher_suite), + srtp_cipher_suite_(srtp_cipher_suite), + remote_ssl_certificates_(std::move(remote_ssl_certificates)) {} + +// Deprecated version +DtlsTransportInformation::DtlsTransportInformation( + DtlsTransportState state, + absl::optional tls_version, + absl::optional ssl_cipher_suite, + absl::optional srtp_cipher_suite, + std::unique_ptr remote_ssl_certificates) + : state_(state), + role_(absl::nullopt), tls_version_(tls_version), ssl_cipher_suite_(ssl_cipher_suite), srtp_cipher_suite_(srtp_cipher_suite), @@ -33,6 +49,7 @@ DtlsTransportInformation::DtlsTransportInformation( DtlsTransportInformation::DtlsTransportInformation( const DtlsTransportInformation& c) : state_(c.state()), + role_(c.role_), tls_version_(c.tls_version_), ssl_cipher_suite_(c.ssl_cipher_suite_), srtp_cipher_suite_(c.srtp_cipher_suite_), @@ -43,6 +60,7 @@ DtlsTransportInformation::DtlsTransportInformation( DtlsTransportInformation& DtlsTransportInformation::operator=( const DtlsTransportInformation& c) { state_ = c.state(); + role_ = c.role_; tls_version_ = c.tls_version_; ssl_cipher_suite_ = c.ssl_cipher_suite_; srtp_cipher_suite_ = c.srtp_cipher_suite_; diff --git a/api/dtls_transport_interface.h b/api/dtls_transport_interface.h index 86715b0400..7b0151249c 100644 --- a/api/dtls_transport_interface.h +++ b/api/dtls_transport_interface.h @@ -36,6 +36,11 @@ enum class DtlsTransportState { 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 { @@ -44,10 +49,19 @@ class RTC_EXPORT DtlsTransportInformation { explicit DtlsTransportInformation(DtlsTransportState state); DtlsTransportInformation( DtlsTransportState state, + absl::optional role, absl::optional tls_version, absl::optional ssl_cipher_suite, absl::optional srtp_cipher_suite, std::unique_ptr remote_ssl_certificates); + ABSL_DEPRECATED("Use version with role parameter") + DtlsTransportInformation( + DtlsTransportState state, + absl::optional tls_version, + absl::optional ssl_cipher_suite, + absl::optional srtp_cipher_suite, + std::unique_ptr remote_ssl_certificates); + // Copy and assign DtlsTransportInformation(const DtlsTransportInformation& c); DtlsTransportInformation& operator=(const DtlsTransportInformation& c); @@ -57,6 +71,7 @@ class RTC_EXPORT DtlsTransportInformation { default; DtlsTransportState state() const { return state_; } + absl::optional role() const { return role_; } absl::optional tls_version() const { return tls_version_; } absl::optional ssl_cipher_suite() const { return ssl_cipher_suite_; } absl::optional srtp_cipher_suite() const { return srtp_cipher_suite_; } @@ -67,6 +82,7 @@ class RTC_EXPORT DtlsTransportInformation { private: DtlsTransportState state_; + absl::optional role_; absl::optional tls_version_; absl::optional ssl_cipher_suite_; absl::optional srtp_cipher_suite_; diff --git a/api/ice_transport_factory.cc b/api/ice_transport_factory.cc index 26ef88bf1c..9e7e629a6f 100644 --- a/api/ice_transport_factory.cc +++ b/api/ice_transport_factory.cc @@ -58,18 +58,9 @@ rtc::scoped_refptr CreateIceTransport( rtc::scoped_refptr CreateIceTransport( IceTransportInit init) { - if (init.async_resolver_factory()) { - // Backwards compatibility mode - return rtc::make_ref_counted( - std::make_unique( - "", cricket::ICE_CANDIDATE_COMPONENT_RTP, init.port_allocator(), - init.async_resolver_factory(), init.event_log())); - } else { - return rtc::make_ref_counted( - cricket::P2PTransportChannel::Create( - "", cricket::ICE_CANDIDATE_COMPONENT_RTP, init.port_allocator(), - init.async_dns_resolver_factory(), init.event_log())); - } + return rtc::make_ref_counted( + cricket::P2PTransportChannel::Create( + "", cricket::ICE_CANDIDATE_COMPONENT_RTP, std::move(init))); } } // namespace webrtc diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h index a3b364c87a..c82027a427 100644 --- a/api/ice_transport_interface.h +++ b/api/ice_transport_interface.h @@ -23,9 +23,11 @@ namespace cricket { class IceTransportInternal; class PortAllocator; +class IceControllerFactoryInterface; } // namespace cricket namespace webrtc { +class WebRtcKeyValueConfig; // An ICE transport, as represented to the outside world. // This object is refcounted, and is therefore alive until the @@ -74,12 +76,27 @@ struct IceTransportInit final { RtcEventLog* event_log() { return event_log_; } void set_event_log(RtcEventLog* event_log) { event_log_ = event_log; } + void set_ice_controller_factory( + cricket::IceControllerFactoryInterface* ice_controller_factory) { + ice_controller_factory_ = ice_controller_factory; + } + cricket::IceControllerFactoryInterface* ice_controller_factory() { + return ice_controller_factory_; + } + + const WebRtcKeyValueConfig* field_trials() { return field_trials_; } + void set_field_trials(const WebRtcKeyValueConfig* field_trials) { + field_trials_ = field_trials; + } + private: cricket::PortAllocator* port_allocator_ = nullptr; AsyncDnsResolverFactoryInterface* async_dns_resolver_factory_ = nullptr; // For backwards compatibility. Only one resolver factory can be set. AsyncResolverFactory* async_resolver_factory_ = nullptr; RtcEventLog* event_log_ = nullptr; + cricket::IceControllerFactoryInterface* ice_controller_factory_ = nullptr; + const WebRtcKeyValueConfig* field_trials_ = nullptr; // TODO(https://crbug.com/webrtc/12657): Redesign to have const members. }; diff --git a/api/jsep_ice_candidate.h b/api/jsep_ice_candidate.h index 40e2783457..8f47a102e7 100644 --- a/api/jsep_ice_candidate.h +++ b/api/jsep_ice_candidate.h @@ -22,7 +22,6 @@ #include "api/candidate.h" #include "api/jsep.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -64,6 +63,10 @@ class JsepCandidateCollection : public IceCandidateCollection { // 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; @@ -80,8 +83,6 @@ class JsepCandidateCollection : public IceCandidateCollection { private: std::vector> candidates_; - - RTC_DISALLOW_COPY_AND_ASSIGN(JsepCandidateCollection); }; } // namespace webrtc diff --git a/api/jsep_session_description.h b/api/jsep_session_description.h index a4300eba98..0b65734ea9 100644 --- a/api/jsep_session_description.h +++ b/api/jsep_session_description.h @@ -22,7 +22,6 @@ #include "api/candidate.h" #include "api/jsep.h" #include "api/jsep_ice_candidate.h" -#include "rtc_base/constructor_magic.h" namespace cricket { class SessionDescription; @@ -43,6 +42,9 @@ class JsepSessionDescription : public SessionDescriptionInterface { 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, @@ -82,8 +84,6 @@ class JsepSessionDescription : public SessionDescriptionInterface { bool GetMediasectionIndex(const IceCandidateInterface* candidate, size_t* index); int GetMediasectionIndex(const cricket::Candidate& candidate); - - RTC_DISALLOW_COPY_AND_ASSIGN(JsepSessionDescription); }; } // namespace webrtc diff --git a/api/media_stream_interface.h b/api/media_stream_interface.h index 874b4dbf70..7e010289a0 100644 --- a/api/media_stream_interface.h +++ b/api/media_stream_interface.h @@ -28,6 +28,7 @@ #include "api/video/video_frame.h" #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" +#include "api/video_track_source_constraints.h" #include "modules/audio_processing/include/audio_processing_statistics.h" #include "rtc_base/ref_count.h" #include "rtc_base/system/rtc_export.h" @@ -146,8 +147,6 @@ class VideoTrackSourceInterface : public MediaSourceInterface, // Add an encoded video sink to the source and additionally cause // a key frame to be generated from the source. The sink will be // invoked from a decoder queue. - // TODO(bugs.webrtc.org/11114): make pure virtual once downstream project - // adapts. virtual void AddEncodedSink( rtc::VideoSinkInterface* sink) = 0; @@ -155,6 +154,13 @@ class VideoTrackSourceInterface : public MediaSourceInterface, virtual void RemoveEncodedSink( rtc::VideoSinkInterface* sink) = 0; + // Notify about constraints set on the source. The information eventually gets + // routed to attached sinks via VideoSinkInterface<>::OnConstraintsChanged. + // The call is expected to happen on the network thread. + // TODO(crbug/1255737): make pure virtual once downstream project adapts. + virtual void ProcessConstraints( + const webrtc::VideoTrackSourceConstraints& constraints) {} + protected: ~VideoTrackSourceInterface() override = default; }; @@ -197,7 +203,7 @@ class AudioTrackSinkInterface { int sample_rate, size_t number_of_channels, size_t number_of_frames) { - RTC_NOTREACHED() << "This method must be overridden, or not used."; + RTC_DCHECK_NOTREACHED() << "This method must be overridden, or not used."; } // In this method, `absolute_capture_timestamp_ms`, when available, is @@ -327,6 +333,8 @@ class MediaStreamInterface : public rtc::RefCountInterface, virtual rtc::scoped_refptr FindVideoTrack( const std::string& track_id) = 0; + // Takes ownership of added tracks. + // TODO(hta): Should take scoped_refptr rather than raw pointer. virtual bool AddTrack(AudioTrackInterface* track) = 0; virtual bool AddTrack(VideoTrackInterface* track) = 0; virtual bool RemoveTrack(AudioTrackInterface* track) = 0; diff --git a/api/media_types.cc b/api/media_types.cc index 3453ce3905..5c7d55b876 100644 --- a/api/media_types.cc +++ b/api/media_types.cc @@ -28,7 +28,7 @@ std::string MediaTypeToString(MediaType type) { return kMediaTypeData; case MEDIA_TYPE_UNSUPPORTED: // Unsupported media stores the m= differently. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } RTC_CHECK_NOTREACHED(); diff --git a/rtc_tools/loopback_test/run-server.sh b/api/metronome/BUILD.gn old mode 100755 new mode 100644 similarity index 53% rename from rtc_tools/loopback_test/run-server.sh rename to api/metronome/BUILD.gn index 35c0797c24..3d3d876df0 --- a/rtc_tools/loopback_test/run-server.sh +++ b/api/metronome/BUILD.gn @@ -1,15 +1,19 @@ -#!/bin/sh -# -# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. # # Use of this source code is governed by a BSD-style license # that can be found in the LICENSE file in the root of the source # tree. An additional intellectual property rights grant can 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 script is used to launch a simple http server for files in the same -# location as the script itself. -cd "`dirname \"$0\"`" -echo "Starting http server in port 8080." -exec python -m SimpleHTTPServer 8080 + +import("../../webrtc.gni") + +rtc_source_set("metronome") { + visibility = [ "*" ] + sources = [ "metronome.h" ] + deps = [ + "../../rtc_base/system:rtc_export", + "../task_queue", + "../units:time_delta", + ] +} diff --git a/api/metronome/metronome.h b/api/metronome/metronome.h new file mode 100644 index 0000000000..fc5f350db2 --- /dev/null +++ b/api/metronome/metronome.h @@ -0,0 +1,65 @@ +/* + * 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 "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// The Metronome posts OnTick() on task queues provided by its listeners' task +// queue periodically. The metronome can be used as an alternative to using +// PostDelayedTask on a thread or task queue for coalescing work and reducing +// the number of idle-wakeups. +// +// Listeners can be added and removed from any sequence, but it is illegal to +// remove a listener from an OnTick invocation. +// +// The metronome concept is still under experimentation, and may not be availble +// in all platforms or applications. See https://crbug.com/1253787 for more +// details. +// +// Metronome implementations must be thread-safe. +class RTC_EXPORT Metronome { + public: + class RTC_EXPORT TickListener { + public: + virtual ~TickListener() = default; + + // OnTick is run on the task queue provided by OnTickTaskQueue each time the + // metronome ticks. + virtual void OnTick() = 0; + + // The task queue that OnTick will run on. Must not be null. + virtual TaskQueueBase* OnTickTaskQueue() = 0; + }; + + virtual ~Metronome() = default; + + // Adds a tick listener to the metronome. Once this method has returned + // OnTick will be invoked on each metronome tick. A listener may + // only be added to the metronome once. + virtual void AddListener(TickListener* listener) = 0; + + // Removes the tick listener from the metronome. Once this method has returned + // OnTick will never be called again. This method must not be called from + // within OnTick. + virtual void RemoveListener(TickListener* listener) = 0; + + // Returns the current tick period of the metronome. + virtual TimeDelta TickPeriod() const = 0; +}; + +} // namespace webrtc + +#endif // API_METRONOME_METRONOME_H_ diff --git a/api/metronome/test/BUILD.gn b/api/metronome/test/BUILD.gn new file mode 100644 index 0000000000..d25d5a848a --- /dev/null +++ b/api/metronome/test/BUILD.gn @@ -0,0 +1,30 @@ +# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../../webrtc.gni") + +rtc_library("fake_metronome") { + testonly = true + sources = [ + "fake_metronome.cc", + "fake_metronome.h", + ] + deps = [ + "..:metronome", + "../..:priority", + "../..:sequence_checker", + "../../../rtc_base:macromagic", + "../../../rtc_base:rtc_event", + "../../../rtc_base:rtc_task_queue", + "../../../rtc_base/synchronization:mutex", + "../../../rtc_base/task_utils:repeating_task", + "../../../rtc_base/task_utils:to_queued_task", + "../../task_queue", + "../../units:time_delta", + ] +} diff --git a/api/metronome/test/fake_metronome.cc b/api/metronome/test/fake_metronome.cc new file mode 100644 index 0000000000..83b5ea7604 --- /dev/null +++ b/api/metronome/test/fake_metronome.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/metronome/test/fake_metronome.h" + +#include "api/priority.h" +#include "api/sequence_checker.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/units/time_delta.h" +#include "rtc_base/event.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/task_utils/to_queued_task.h" + +namespace webrtc::test { + +ForcedTickMetronome::ForcedTickMetronome(TimeDelta tick_period) + : tick_period_(tick_period) {} + +void ForcedTickMetronome::AddListener(TickListener* listener) { + listeners_.insert(listener); +} + +void ForcedTickMetronome::RemoveListener(TickListener* listener) { + listeners_.erase(listener); +} + +TimeDelta ForcedTickMetronome::TickPeriod() const { + return tick_period_; +} + +size_t ForcedTickMetronome::NumListeners() { + return listeners_.size(); +} + +void ForcedTickMetronome::Tick() { + for (auto* listener : listeners_) { + listener->OnTickTaskQueue()->PostTask( + ToQueuedTask([listener] { listener->OnTick(); })); + } +} + +FakeMetronome::FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period) + : tick_period_(tick_period), + queue_(factory->CreateTaskQueue("MetronomeQueue", + TaskQueueFactory::Priority::HIGH)) {} + +FakeMetronome::~FakeMetronome() { + RTC_DCHECK(listeners_.empty()); +} + +void FakeMetronome::AddListener(TickListener* listener) { + MutexLock lock(&mutex_); + listeners_.insert(listener); + if (!started_) { + tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] { + MutexLock lock(&mutex_); + // Stop if empty. + if (listeners_.empty()) + return TimeDelta::PlusInfinity(); + for (auto* listener : listeners_) { + listener->OnTickTaskQueue()->PostTask( + ToQueuedTask([listener] { listener->OnTick(); })); + } + return tick_period_; + }); + started_ = true; + } +} + +void FakeMetronome::RemoveListener(TickListener* listener) { + MutexLock lock(&mutex_); + listeners_.erase(listener); +} + +void FakeMetronome::Stop() { + MutexLock lock(&mutex_); + RTC_DCHECK(listeners_.empty()); + if (started_) + queue_.PostTask([this] { tick_task_.Stop(); }); +} + +TimeDelta FakeMetronome::TickPeriod() const { + return tick_period_; +} + +} // namespace webrtc::test diff --git a/api/metronome/test/fake_metronome.h b/api/metronome/test/fake_metronome.h new file mode 100644 index 0000000000..28a79e06ff --- /dev/null +++ b/api/metronome/test/fake_metronome.h @@ -0,0 +1,77 @@ +/* + * 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_TEST_FAKE_METRONOME_H_ +#define API_METRONOME_TEST_FAKE_METRONOME_H_ + +#include +#include + +#include "api/metronome/metronome.h" +#include "api/task_queue/task_queue_base.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/units/time_delta.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc::test { + +// ForcedTickMetronome is a Metronome that ticks when `Tick()` is invoked. +// The constructor argument `tick_period` returned in `TickPeriod()`. +class ForcedTickMetronome : public Metronome { + public: + explicit ForcedTickMetronome(TimeDelta tick_period); + + // Forces all TickListeners to run `OnTick`. + void Tick(); + size_t NumListeners(); + + // Metronome implementation. + void AddListener(TickListener* listener) override; + void RemoveListener(TickListener* listener) override; + TimeDelta TickPeriod() const override; + + private: + const TimeDelta tick_period_; + std::set listeners_; +}; + +// FakeMetronome is a metronome that ticks based on a repeating task at the +// `tick_period` provided in the constructor. It is designed for use with +// simulated task queues for unit tests. +// +// `Stop()` must be called before destruction, as it cancels the metronome tick +// on the proper task queue. +class FakeMetronome : public Metronome { + public: + FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period); + ~FakeMetronome() override; + + // Metronome implementation. + void AddListener(TickListener* listener) override; + void RemoveListener(TickListener* listener) override; + TimeDelta TickPeriod() const override; + + void Stop(); + + private: + const TimeDelta tick_period_; + RepeatingTaskHandle tick_task_; + bool started_ RTC_GUARDED_BY(mutex_) = false; + std::set listeners_ RTC_GUARDED_BY(mutex_); + Mutex mutex_; + rtc::TaskQueue queue_; +}; + +} // namespace webrtc::test + +#endif // API_METRONOME_TEST_FAKE_METRONOME_H_ diff --git a/api/neteq/neteq.cc b/api/neteq/neteq.cc index e8ef4dbd39..155ddf2cf3 100644 --- a/api/neteq/neteq.cc +++ b/api/neteq/neteq.cc @@ -30,8 +30,7 @@ std::string NetEq::Config::ToString() const { << ", min_delay_ms=" << min_delay_ms << ", enable_fast_accelerate=" << (enable_fast_accelerate ? "true" : "false") << ", enable_muted_state=" << (enable_muted_state ? "true" : "false") - << ", enable_rtx_handling=" << (enable_rtx_handling ? "true" : "false") - << ", extra_output_delay_ms=" << extra_output_delay_ms; + << ", enable_rtx_handling=" << (enable_rtx_handling ? "true" : "false"); return ss.str(); } diff --git a/api/neteq/neteq.h b/api/neteq/neteq.h index dbfa0718e4..675742a1ce 100644 --- a/api/neteq/neteq.h +++ b/api/neteq/neteq.h @@ -136,10 +136,6 @@ class NetEq { bool enable_rtx_handling = false; absl::optional codec_pair_id; bool for_test_no_time_stretching = false; // Use only for testing. - // Adds extra delay to the output of NetEq, without affecting jitter or - // loss behavior. This is mainly for testing. Value must be a non-negative - // multiple of 10 ms. - int extra_output_delay_ms = 0; }; enum ReturnCodes { kOK = 0, kFail = -1 }; @@ -183,14 +179,6 @@ class NetEq { SdpAudioFormat sdp_format; }; - // 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. - static NetEq* Create( - const NetEq::Config& config, - Clock* clock, - const rtc::scoped_refptr& decoder_factory); - virtual ~NetEq() {} // Inserts a new packet into NetEq. diff --git a/api/notifier.h b/api/notifier.h index c03b1049eb..fc2480e00a 100644 --- a/api/notifier.h +++ b/api/notifier.h @@ -14,7 +14,9 @@ #include #include "api/media_stream_interface.h" +#include "api/sequence_checker.h" #include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" namespace webrtc { @@ -23,14 +25,16 @@ namespace webrtc { template class Notifier : public T { public: - Notifier() {} + Notifier() { sequence_checker_.Detach(); } virtual void RegisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK(observer != nullptr); observers_.push_back(observer); } virtual void UnregisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); for (std::list::iterator it = observers_.begin(); it != observers_.end(); it++) { if (*it == observer) { @@ -41,6 +45,7 @@ class Notifier : public T { } void FireOnChanged() { + RTC_DCHECK_RUN_ON(&sequence_checker_); // Copy the list of observers to avoid a crash if the observer object // unregisters as a result of the OnChanged() call. If the same list is used // UnregisterObserver will affect the list make the iterator invalid. @@ -52,7 +57,10 @@ class Notifier : public T { } protected: - std::list observers_; + std::list observers_ RTC_GUARDED_BY(sequence_checker_); + + private: + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; }; } // namespace webrtc diff --git a/api/packet_socket_factory.h b/api/packet_socket_factory.h index 1e9f470357..29d2606b9b 100644 --- a/api/packet_socket_factory.h +++ b/api/packet_socket_factory.h @@ -11,9 +11,12 @@ #ifndef API_PACKET_SOCKET_FACTORY_H_ #define API_PACKET_SOCKET_FACTORY_H_ +#include #include #include +#include "api/async_dns_resolver.h" +#include "api/wrapping_async_dns_resolver.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/proxy_info.h" #include "rtc_base/system/rtc_export.h" @@ -56,7 +59,7 @@ class RTC_EXPORT PacketSocketFactory { virtual AsyncPacketSocket* CreateUdpSocket(const SocketAddress& address, uint16_t min_port, uint16_t max_port) = 0; - virtual AsyncPacketSocket* CreateServerTcpSocket( + virtual AsyncListenSocket* CreateServerTcpSocket( const SocketAddress& local_address, uint16_t min_port, uint16_t max_port, @@ -69,7 +72,23 @@ class RTC_EXPORT PacketSocketFactory { const std::string& user_agent, const PacketSocketTcpOptions& tcp_options) = 0; - virtual AsyncResolverInterface* CreateAsyncResolver() = 0; + // The AsyncResolverInterface is deprecated; users are encouraged + // to switch to the AsyncDnsResolverInterface. + // TODO(bugs.webrtc.org/12598): Remove once all downstream users + // are converted. + virtual AsyncResolverInterface* CreateAsyncResolver() { + // Default implementation, so that downstream users can remove this + // immediately after changing to CreateAsyncDnsResolver + RTC_DCHECK_NOTREACHED(); + return nullptr; + } + + virtual std::unique_ptr + CreateAsyncDnsResolver() { + // Default implementation, to aid in transition to AsyncDnsResolverInterface + return std::make_unique( + CreateAsyncResolver()); + } private: PacketSocketFactory(const PacketSocketFactory&) = delete; diff --git a/api/peer_connection_interface.cc b/api/peer_connection_interface.cc index 83a81ad8bc..eba5ad383d 100644 --- a/api/peer_connection_interface.cc +++ b/api/peer_connection_interface.cc @@ -41,12 +41,6 @@ PeerConnectionInterface::RTCConfiguration::RTCConfiguration( PeerConnectionInterface::RTCConfiguration::~RTCConfiguration() = default; -RTCError PeerConnectionInterface::RemoveTrackNew( - rtc::scoped_refptr sender) { - return RTCError(RemoveTrack(sender) ? RTCErrorType::NONE - : RTCErrorType::INTERNAL_ERROR); -} - RTCError PeerConnectionInterface::SetConfiguration( const PeerConnectionInterface::RTCConfiguration& config) { return RTCError(); diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index 8dd26bda4b..9778f59600 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -76,6 +76,7 @@ #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/adaptation/resource.h" #include "api/async_dns_resolver.h" @@ -95,6 +96,7 @@ #include "api/jsep.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" @@ -171,7 +173,12 @@ class StatsObserver : public rtc::RefCountInterface { ~StatsObserver() override = default; }; -enum class SdpSemantics { kPlanB, kUnifiedPlan }; +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 rtc::RefCountInterface { public: @@ -184,6 +191,7 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { kHaveRemotePrAnswer, kClosed, }; + static constexpr absl::string_view AsString(SignalingState); // See https://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate enum IceGatheringState { @@ -191,6 +199,7 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { kIceGatheringGathering, kIceGatheringComplete }; + static constexpr absl::string_view AsString(IceGatheringState state); // See https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectionstate enum class PeerConnectionState { @@ -201,6 +210,7 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { kFailed, kClosed, }; + static constexpr absl::string_view AsString(PeerConnectionState state); // See https://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate enum IceConnectionState { @@ -213,6 +223,7 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { kIceConnectionClosed, kIceConnectionMax, }; + static constexpr absl::string_view AsString(IceConnectionState state); // TLS certificate policy. enum TlsCertPolicy { @@ -294,6 +305,13 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { 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. @@ -371,6 +389,18 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { 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; @@ -598,28 +628,27 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // cost. absl::optional network_preference; - // Configure the SDP semantics used by this PeerConnection. Note that the - // WebRTC 1.0 specification requires kUnifiedPlan semantics. The - // RtpTransceiver API is only available with kUnifiedPlan semantics. + // 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. // - // kPlanB will cause PeerConnection to create offers and answers with at + // 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. - // - // kUnifiedPlan will cause 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 PeerConnection to ignore all but the first a=ssrc lines - // that form a Plan B stream. - // - // For users who wish to send multiple audio/video streams and need to stay - // interoperable with legacy WebRTC implementations or use legacy APIs, - // specify kPlanB. - // - // For all other users, specify kUnifiedPlan. - SdpSemantics sdp_semantics = SdpSemantics::kPlanB; + // the same media type (if the PeerConnection is given Unified Plan SDP to + // process). + // RingRTC Change to use "Plan B" + SdpSemantics sdp_semantics = SdpSemantics::kPlanB_DEPRECATED; // TODO(bugs.webrtc.org/9891) - Move to crypto_options or remove. // Actively reset the SRTP parameters whenever the DTLS transports @@ -669,6 +698,8 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // VPN (in case webrtc fails to auto detect them). std::vector vpn_list; + PortAllocatorConfig port_allocator_config; + // // Don't forget to update operator== if adding something. // @@ -780,23 +811,25 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { rtc::scoped_refptr track, const std::vector& stream_ids) = 0; - // Remove an RtpSender from this PeerConnection. - // Returns true on success. - // TODO(steveanton): Replace with signature that returns RTCError. - virtual bool RemoveTrack(RtpSenderInterface* sender) = 0; - - // Plan B semantics: Removes the RtpSender from this PeerConnection. - // Unified Plan semantics: Stop sending on the RtpSender and mark the + // 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. - virtual RTCError RemoveTrackNew( - rtc::scoped_refptr sender); + // is removed; remove default implementation once upstream is updated. + virtual RTCError RemoveTrackOrError( + rtc::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 @@ -1311,14 +1344,6 @@ class PeerConnectionObserver : public RtpPacketSinkInterface { // 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 - // `host_candidate` is a stringified socket address. - virtual void OnIceCandidateError(const std::string& host_candidate, - const std::string& url, - int error_code, - const std::string& error_text) {} - // Gathering of an ICE candidate failed. // See https://w3c.github.io/webrtc-pc/#event-icecandidateerror virtual void OnIceCandidateError(const std::string& address, @@ -1443,6 +1468,7 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final { rtc::Thread* network_thread = nullptr; rtc::Thread* worker_thread = nullptr; rtc::Thread* signaling_thread = nullptr; + rtc::SocketFactory* socket_factory = nullptr; std::unique_ptr task_queue_factory; std::unique_ptr media_engine; std::unique_ptr call_factory; @@ -1460,6 +1486,7 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final { std::unique_ptr trials; std::unique_ptr transport_controller_send_factory; + std::unique_ptr metronome; }; // PeerConnectionFactoryInterface is the factory interface used for creating @@ -1616,6 +1643,93 @@ RTC_EXPORT rtc::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/api/ref_counted_base.h b/api/ref_counted_base.h index 931cb20762..f20228b740 100644 --- a/api/ref_counted_base.h +++ b/api/ref_counted_base.h @@ -12,7 +12,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_counter.h" namespace rtc { @@ -21,6 +20,9 @@ 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(); @@ -39,8 +41,6 @@ class RefCountedBase { private: mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; - - RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedBase); }; // Template based version of `RefCountedBase` for simple implementations that do @@ -61,6 +61,9 @@ 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 @@ -88,8 +91,6 @@ class RefCountedNonVirtual { private: mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; - - RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedNonVirtual); }; } // namespace rtc diff --git a/api/rtc_error.h b/api/rtc_error.h index 8ca2249762..42ceed18d9 100644 --- a/api/rtc_error.h +++ b/api/rtc_error.h @@ -178,11 +178,11 @@ inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) // 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 << " (" << ToString(type) << ")"; \ - return webrtc::RTCError(type, message); \ +#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) \ @@ -244,7 +244,7 @@ class RTCErrorOr { // // REQUIRES: !error.ok(). This requirement is DCHECKed. RTCErrorOr(RTCError&& error) : error_(std::move(error)) { // NOLINT - RTC_DCHECK(!error.ok()); + RTC_DCHECK(!error_.ok()); } // Constructs a new RTCErrorOr with the given value. After calling this diff --git a/api/rtc_event_log/rtc_event.h b/api/rtc_event_log/rtc_event.h index 51db8f0b4d..8697a25a74 100644 --- a/api/rtc_event_log/rtc_event.h +++ b/api/rtc_event_log/rtc_event.h @@ -27,7 +27,7 @@ class RtcEvent { // of Type. This leaks the information of existing subclasses into the // superclass, but the *actual* information - rtclog::StreamConfig, etc. - // is kept separate. - enum class Type { + enum class Type : uint32_t { AlrStateEvent, RouteChangeEvent, RemoteEstimateEvent, @@ -53,7 +53,9 @@ class RtcEvent { GenericPacketSent, GenericPacketReceived, GenericAckReceived, - FrameDecoded + FrameDecoded, + BeginV3Log = 0x2501580, + EndV3Log = 0x2501581 }; RtcEvent(); @@ -63,6 +65,13 @@ class RtcEvent { virtual bool IsConfigEvent() const = 0; + // Events are grouped by Type before being encoded. + // Optionally, `GetGroupKey` can be overloaded to group the + // events by a secondary key (in addition to the event type.) + // This can, in some cases, improve compression efficiency + // e.g. by grouping events by SSRC. + virtual uint32_t GetGroupKey() const { return 0; } + int64_t timestamp_ms() const { return timestamp_us_ / 1000; } int64_t timestamp_us() const { return timestamp_us_; } diff --git a/api/rtc_event_log/rtc_event_log.h b/api/rtc_event_log/rtc_event_log.h index 86613ddd85..7b42cdc028 100644 --- a/api/rtc_event_log/rtc_event_log.h +++ b/api/rtc_event_log/rtc_event_log.h @@ -29,7 +29,7 @@ class RtcEventLog { // TODO(eladalon): Get rid of the legacy encoding and this enum once all // clients have migrated to the new format. - enum class EncodingType { Legacy, NewFormat }; + enum class EncodingType { Legacy, NewFormat, ProtoFree }; virtual ~RtcEventLog() = default; diff --git a/api/rtp_packet_infos.h b/api/rtp_packet_infos.h index 2ca3174037..031e33332e 100644 --- a/api/rtp_packet_infos.h +++ b/api/rtp_packet_infos.h @@ -18,6 +18,7 @@ #include "api/ref_counted_base.h" #include "api/rtp_packet_info.h" #include "api/scoped_refptr.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -79,7 +80,7 @@ class RTC_EXPORT RtpPacketInfos { size_type size() const { return entries().size(); } private: - class Data : public rtc::RefCountedBase { + class Data final : public rtc::RefCountedNonVirtual { public: static rtc::scoped_refptr Create(const vector_type& entries) { // Performance optimization for the empty case. @@ -87,7 +88,7 @@ class RTC_EXPORT RtpPacketInfos { return nullptr; } - return new Data(entries); + return rtc::make_ref_counted(entries); } static rtc::scoped_refptr Create(vector_type&& entries) { @@ -96,16 +97,16 @@ class RTC_EXPORT RtpPacketInfos { return nullptr; } - return new Data(std::move(entries)); + return rtc::make_ref_counted(std::move(entries)); } const vector_type& entries() const { return entries_; } - private: explicit Data(const vector_type& entries) : entries_(entries) {} explicit Data(vector_type&& entries) : entries_(std::move(entries)) {} - ~Data() override {} + ~Data() = default; + private: const vector_type entries_; }; diff --git a/api/rtp_parameters.cc b/api/rtp_parameters.cc index 5ce6780753..c48b8da02c 100644 --- a/api/rtp_parameters.cc +++ b/api/rtp_parameters.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include "api/array_view.h" @@ -131,6 +132,7 @@ constexpr char RtpExtension::kMidUri[]; constexpr char RtpExtension::kRidUri[]; constexpr char RtpExtension::kRepairedRidUri[]; constexpr char RtpExtension::kVideoFrameTrackingIdUri[]; +constexpr char RtpExtension::kCsrcAudioLevelsUri[]; constexpr int RtpExtension::kMinId; constexpr int RtpExtension::kMaxId; @@ -237,12 +239,6 @@ const RtpExtension* RtpExtension::FindHeaderExtensionByUri( return fallback_extension; } -const RtpExtension* RtpExtension::FindHeaderExtensionByUri( - const std::vector& extensions, - absl::string_view uri) { - return FindHeaderExtensionByUri(extensions, uri, kPreferEncryptedExtension); -} - const RtpExtension* RtpExtension::FindHeaderExtensionByUriAndEncryption( const std::vector& extensions, absl::string_view uri, @@ -285,6 +281,14 @@ const std::vector RtpExtension::DeduplicateHeaderExtensions( } } + // Sort the returned vector to make comparisons of header extensions reliable. + // In order of priority, we sort by uri first, then encrypt and id last. + std::sort(filtered.begin(), filtered.end(), + [](const RtpExtension& a, const RtpExtension& b) { + return std::tie(a.uri, a.encrypt, a.id) < + std::tie(b.uri, b.encrypt, b.id); + }); + return filtered; } } // namespace webrtc diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h index 2664ae734a..45cedfdd9c 100644 --- a/api/rtp_parameters.h +++ b/api/rtp_parameters.h @@ -277,11 +277,6 @@ struct RTC_EXPORT RtpExtension { const std::vector& extensions, absl::string_view uri, Filter filter); - ABSL_DEPRECATED( - "Use RtpExtension::FindHeaderExtensionByUri with filter argument") - static const RtpExtension* FindHeaderExtensionByUri( - const std::vector& extensions, - absl::string_view uri); // Returns the header extension with the given URI and encrypt parameter, // if found, otherwise nullptr. @@ -291,6 +286,9 @@ struct RTC_EXPORT RtpExtension { 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); diff --git a/api/rtp_parameters_unittest.cc b/api/rtp_parameters_unittest.cc index 51ad426748..234c3c9b6d 100644 --- a/api/rtp_parameters_unittest.cc +++ b/api/rtp_parameters_unittest.cc @@ -109,6 +109,38 @@ TEST(RtpExtensionTest, DeduplicateHeaderExtensions) { EXPECT_EQ((std::vector{kExtension1Encrypted}), filtered); } +// Test that the filtered vector is sorted so that for a given unsorted array of +// extensions, the filtered vector will always be laied out the same (for easy +// comparison). +TEST(RtpExtensionTest, DeduplicateHeaderExtensionsSorted) { + const std::vector extensions = { + RtpExtension("cde1", 11, false), RtpExtension("cde2", 12, true), + RtpExtension("abc1", 3, false), RtpExtension("abc2", 4, true), + RtpExtension("cde3", 9, true), RtpExtension("cde4", 10, false), + RtpExtension("abc3", 1, true), RtpExtension("abc4", 2, false), + RtpExtension("bcd3", 7, false), RtpExtension("bcd1", 8, true), + RtpExtension("bcd2", 5, true), RtpExtension("bcd4", 6, false), + }; + + auto encrypted = RtpExtension::DeduplicateHeaderExtensions( + extensions, RtpExtension::Filter::kRequireEncryptedExtension); + + const std::vector expected_sorted_encrypted = { + RtpExtension("abc2", 4, true), RtpExtension("abc3", 1, true), + RtpExtension("bcd1", 8, true), RtpExtension("bcd2", 5, true), + RtpExtension("cde2", 12, true), RtpExtension("cde3", 9, true)}; + EXPECT_EQ(expected_sorted_encrypted, encrypted); + + auto unencypted = RtpExtension::DeduplicateHeaderExtensions( + extensions, RtpExtension::Filter::kDiscardEncryptedExtension); + + const std::vector expected_sorted_unencrypted = { + RtpExtension("abc1", 3, false), RtpExtension("abc4", 2, false), + RtpExtension("bcd3", 7, false), RtpExtension("bcd4", 6, false), + RtpExtension("cde1", 11, false), RtpExtension("cde4", 10, false)}; + EXPECT_EQ(expected_sorted_unencrypted, unencypted); +} + TEST(RtpExtensionTest, FindHeaderExtensionByUriAndEncryption) { std::vector extensions; diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc index fd5085c336..7267b286be 100644 --- a/api/rtp_transceiver_interface.cc +++ b/api/rtp_transceiver_interface.cc @@ -34,39 +34,14 @@ void RtpTransceiverInterface::Stop() { } RTCError RtpTransceiverInterface::StopStandard() { - RTC_NOTREACHED() << "DEBUG: RtpTransceiverInterface::StopStandard called"; + RTC_DCHECK_NOTREACHED() + << "DEBUG: RtpTransceiverInterface::StopStandard called"; return RTCError::OK(); } void RtpTransceiverInterface::StopInternal() { - RTC_NOTREACHED() << "DEBUG: RtpTransceiverInterface::StopInternal called"; -} - -RTCError RtpTransceiverInterface::SetCodecPreferences( - rtc::ArrayView) { - RTC_NOTREACHED() << "Not implemented"; - return {}; -} - -std::vector RtpTransceiverInterface::codec_preferences() - const { - return {}; -} - -std::vector -RtpTransceiverInterface::HeaderExtensionsToOffer() const { - return {}; -} - -webrtc::RTCError RtpTransceiverInterface::SetOfferedRtpHeaderExtensions( - rtc::ArrayView - header_extensions_to_offer) { - return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION); -} - -std::vector -RtpTransceiverInterface::HeaderExtensionsNegotiated() const { - return {}; + RTC_DCHECK_NOTREACHED() + << "DEBUG: RtpTransceiverInterface::StopInternal called"; } // TODO(bugs.webrtc.org/11839) Remove default implementations when clients @@ -78,7 +53,7 @@ void RtpTransceiverInterface::SetDirection( RTCError RtpTransceiverInterface::SetDirectionWithError( RtpTransceiverDirection new_direction) { - RTC_NOTREACHED() << "Default implementation called"; + RTC_DCHECK_NOTREACHED() << "Default implementation called"; return RTCError::OK(); } diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h index 4799c4b153..c9d911fac1 100644 --- a/api/rtp_transceiver_interface.h +++ b/api/rtp_transceiver_interface.h @@ -97,8 +97,7 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface { // 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 - // TODO(hta): Remove default implementation. - virtual bool stopping() const; + 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. @@ -147,28 +146,28 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface { // by WebRTC for this transceiver. // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences virtual RTCError SetCodecPreferences( - rtc::ArrayView codecs); - virtual std::vector codec_preferences() const; + rtc::ArrayView codecs) = 0; + virtual std::vector codec_preferences() const = 0; // Readonly attribute which contains the set of header extensions that was set // with SetOfferedRtpHeaderExtensions, or a default set if it has not been // called. // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface virtual std::vector HeaderExtensionsToOffer() - const; + const = 0; // Readonly attribute which is either empty 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 HeaderExtensionsNegotiated() - const; + const = 0; // The SetOfferedRtpHeaderExtensions 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 SetOfferedRtpHeaderExtensions( rtc::ArrayView - header_extensions_to_offer); + header_extensions_to_offer) = 0; protected: ~RtpTransceiverInterface() override = default; diff --git a/api/scoped_refptr.h b/api/scoped_refptr.h index 5b3a08541e..8f45f89206 100644 --- a/api/scoped_refptr.h +++ b/api/scoped_refptr.h @@ -74,8 +74,9 @@ class scoped_refptr { typedef T element_type; scoped_refptr() : ptr_(nullptr) {} + scoped_refptr(std::nullptr_t) : ptr_(nullptr) {} // NOLINT(runtime/explicit) - scoped_refptr(T* p) : ptr_(p) { // NOLINT(runtime/explicit) + explicit scoped_refptr(T* p) : ptr_(p) { if (ptr_) ptr_->AddRef(); } diff --git a/api/scoped_refptr_unittest.cc b/api/scoped_refptr_unittest.cc index 75a202bccd..22b61209cd 100644 --- a/api/scoped_refptr_unittest.cc +++ b/api/scoped_refptr_unittest.cc @@ -48,7 +48,7 @@ class ScopedRefCounted { TEST(ScopedRefptrTest, IsCopyConstructable) { FunctionsCalled called; - scoped_refptr ptr = new ScopedRefCounted(&called); + scoped_refptr ptr(new ScopedRefCounted(&called)); scoped_refptr another_ptr = ptr; EXPECT_TRUE(ptr); @@ -59,7 +59,7 @@ TEST(ScopedRefptrTest, IsCopyConstructable) { TEST(ScopedRefptrTest, IsCopyAssignable) { FunctionsCalled called; scoped_refptr another_ptr; - scoped_refptr ptr = new ScopedRefCounted(&called); + scoped_refptr ptr(new ScopedRefCounted(&called)); another_ptr = ptr; EXPECT_TRUE(ptr); @@ -69,7 +69,7 @@ TEST(ScopedRefptrTest, IsCopyAssignable) { TEST(ScopedRefptrTest, IsMoveConstructableWithoutExtraAddRefRelease) { FunctionsCalled called; - scoped_refptr ptr = new ScopedRefCounted(&called); + scoped_refptr ptr(new ScopedRefCounted(&called)); scoped_refptr another_ptr = std::move(ptr); EXPECT_FALSE(ptr); @@ -81,7 +81,7 @@ TEST(ScopedRefptrTest, IsMoveConstructableWithoutExtraAddRefRelease) { TEST(ScopedRefptrTest, IsMoveAssignableWithoutExtraAddRefRelease) { FunctionsCalled called; scoped_refptr another_ptr; - scoped_refptr ptr = new ScopedRefCounted(&called); + scoped_refptr ptr(new ScopedRefCounted(&called)); another_ptr = std::move(ptr); EXPECT_FALSE(ptr); @@ -100,8 +100,8 @@ TEST(ScopedRefptrTest, MovableDuringVectorReallocation) { std::vector> ptrs; ptrs.reserve(1); // Insert more elements than reserved to provoke reallocation. - ptrs.push_back(new ScopedRefCounted(&called)); - ptrs.push_back(new ScopedRefCounted(&called)); + ptrs.emplace_back(new ScopedRefCounted(&called)); + ptrs.emplace_back(new ScopedRefCounted(&called)); EXPECT_EQ(called.addref, 2); EXPECT_EQ(called.release, 0); diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 849ef80a5d..ccaf095e3d 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -106,6 +106,20 @@ class RTC_EXPORT RTCCertificateStats final : public RTCStats { RTCStatsMember issuer_certificate_id; }; +// Non standard extension mapping to rtc::AdapterType +struct RTCNetworkAdapterType { + static constexpr char kUnknown[] = "unknown"; + static constexpr char kEthernet[] = "ethernet"; + static constexpr char kWifi[] = "wifi"; + static constexpr char kCellular[] = "cellular"; + static constexpr char kLoopback[] = "loopback"; + static constexpr char kAny[] = "any"; + static constexpr char kCellular2g[] = "cellular2g"; + static constexpr char kCellular3g[] = "cellular3g"; + static constexpr char kCellular4g[] = "cellular4g"; + static constexpr char kCellular5g[] = "cellular5g"; +}; + // https://w3c.github.io/webrtc-stats/#codec-dict* class RTC_EXPORT RTCCodecStats final : public RTCStats { public: @@ -170,6 +184,8 @@ class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { RTCStatsMember writable; // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 RTCStatsMember readable; + RTCStatsMember packets_sent; + RTCStatsMember packets_received; RTCStatsMember bytes_sent; RTCStatsMember bytes_received; RTCStatsMember total_round_trip_time; @@ -194,6 +210,8 @@ class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { RTCStatsMember consent_responses_received; // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 RTCStatsMember consent_responses_sent; + RTCStatsMember packets_discarded_on_send; + RTCStatsMember bytes_discarded_on_send; }; // https://w3c.github.io/webrtc-stats/#icecandidate-dict* @@ -221,9 +239,11 @@ class RTC_EXPORT RTCIceCandidateStats : public RTCStats { // TODO(hbos): Support enum types? "RTCStatsMember"? RTCStatsMember candidate_type; RTCStatsMember priority; - // TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/632723 RTCStatsMember url; + RTCNonStandardStatsMember vpn; + RTCNonStandardStatsMember network_adapter_type; + protected: RTCIceCandidateStats(const std::string& id, int64_t timestamp_us, @@ -525,7 +545,7 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { RTCStatsMember bytes_sent; RTCStatsMember header_bytes_sent; RTCStatsMember retransmitted_bytes_sent; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7066 + // TODO(https://crbug.com/webrtc/13394): Also collect this metric for video. RTCStatsMember target_bitrate; RTCStatsMember frames_encoded; RTCStatsMember key_frames_encoded; @@ -644,7 +664,7 @@ class RTC_EXPORT RTCVideoSourceStats final : public RTCMediaSourceStats { RTCStatsMember width; RTCStatsMember height; RTCStatsMember frames; - RTCStatsMember frames_per_second; + RTCStatsMember frames_per_second; }; // https://w3c.github.io/webrtc-stats/#transportstats-dict* diff --git a/api/stats_types.cc b/api/stats_types.cc index ddba593a1e..b044e4ab11 100644 --- a/api/stats_types.cc +++ b/api/stats_types.cc @@ -58,7 +58,7 @@ const char* InternalTypeToString(StatsReport::StatsType type) { case StatsReport::kStatsReportTypeDataChannel: return "datachannel"; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -291,7 +291,7 @@ bool StatsReport::Value::Equals(const Value& other) const { case kId: return (*value_.id_)->Equals(*other.value_.id_); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -648,12 +648,15 @@ const char* StatsReport::Value::display_name() const { return "googTrackId"; case kStatsValueNameTimingFrameInfo: return "googTimingFrameInfo"; + // TODO(bugs.webrtc.org/11226): Remove. case kStatsValueNameTypingNoiseState: return "googTypingNoiseState"; case kStatsValueNameWritable: return "googWritable"; case kStatsValueNameAudioDeviceUnderrunCounter: return "googAudioDeviceUnderrunCounter"; + case kStatsValueNameLocalCandidateRelayProtocol: + return "googLocalCandidateRelayProtocol"; } return nullptr; @@ -676,7 +679,7 @@ std::string StatsReport::Value::ToString() const { case kId: return (*value_.id_)->ToString(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return std::string(); } diff --git a/api/stats_types.h b/api/stats_types.h index 9a03db3e40..e7dd528e62 100644 --- a/api/stats_types.h +++ b/api/stats_types.h @@ -22,7 +22,6 @@ #include "api/scoped_refptr.h" #include "api/sequence_checker.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_count.h" #include "rtc_base/system/rtc_export.h" @@ -236,9 +235,11 @@ class RTC_EXPORT StatsReport { kStatsValueNameTrackId, kStatsValueNameTransmitBitrate, kStatsValueNameTransportType, + // TODO(bugs.webrtc.org/11226): Remove. kStatsValueNameTypingNoiseState, kStatsValueNameWritable, kStatsValueNameAudioDeviceUnderrunCounter, + kStatsValueNameLocalCandidateRelayProtocol, }; class RTC_EXPORT IdBase : public rtc::RefCountInterface { @@ -287,6 +288,9 @@ class RTC_EXPORT StatsReport { ~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 @@ -357,8 +361,6 @@ class RTC_EXPORT StatsReport { const char* static_string_; Id* id_; } value_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Value); }; typedef rtc::scoped_refptr ValuePtr; @@ -368,6 +370,9 @@ class RTC_EXPORT StatsReport { 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); @@ -407,8 +412,6 @@ class RTC_EXPORT StatsReport { const Id id_; double timestamp_; // Time since 1970-01-01T00:00:00Z in milliseconds. Values values_; - - RTC_DISALLOW_COPY_AND_ASSIGN(StatsReport); }; // Typedef for an array of const StatsReport pointers. diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h index d8af6e67db..b7c92f8647 100644 --- a/api/task_queue/task_queue_base.h +++ b/api/task_queue/task_queue_base.h @@ -11,6 +11,7 @@ #define API_TASK_QUEUE_TASK_QUEUE_BASE_H_ #include +#include #include "api/task_queue/queued_task.h" #include "rtc_base/system/rtc_export.h" @@ -24,6 +25,16 @@ namespace webrtc { // known task queue, use IsCurrent(). class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { public: + enum class DelayPrecision { + // This may include up to a 17 ms leeway in addition to OS timer precision. + // See PostDelayedTask() for more information. + kLow, + // This does not have the additional delay that kLow has, but it is still + // limited by OS timer precision. See PostDelayedHighPrecisionTask() for + // more information. + kHigh, + }; + // Starts destruction of the task queue. // On return ensures no task are running and no new tasks are able to start // on the task queue. @@ -48,14 +59,70 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // May be called on any thread or task queue, including this task queue. virtual void PostTask(std::unique_ptr task) = 0; + // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever + // possible. + // // Schedules a task to execute a specified number of milliseconds from when - // the call is made. The precision should be considered as "best effort" - // and in some cases, such as on Windows when all high precision timers have - // been used up, can be off by as much as 15 millseconds. + // 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. virtual void PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) = 0; + // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever + // possible. + // + // Schedules a task to execute a specified number of milliseconds from when + // the call is made, using "high" precision. All scheduling is affected by + // OS-specific leeway and current workloads which means that in terms of + // precision there are no hard guarantees. + // + // 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. + virtual void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) { + // Remove default implementation when dependencies have implemented this + // method. + PostDelayedTask(std::move(task), milliseconds); + } + + // As specified by |precision|, calls either PostDelayedTask() or + // PostDelayedHighPrecisionTask(). + void PostDelayedTaskWithPrecision(DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds) { + switch (precision) { + case DelayPrecision::kLow: + PostDelayedTask(std::move(task), milliseconds); + break; + case DelayPrecision::kHigh: + PostDelayedHighPrecisionTask(std::move(task), milliseconds); + 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. diff --git a/api/task_queue/test/BUILD.gn b/api/task_queue/test/BUILD.gn new file mode 100644 index 0000000000..fa82dd090e --- /dev/null +++ b/api/task_queue/test/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../../webrtc.gni") + +rtc_library("mock_task_queue_base") { + testonly = true + sources = [ "mock_task_queue_base.h" ] + deps = [ + "../../../api/task_queue:task_queue", + "../../../test:test_support", + ] +} diff --git a/api/task_queue/test/mock_task_queue_base.h b/api/task_queue/test/mock_task_queue_base.h new file mode 100644 index 0000000000..68c5c052ed --- /dev/null +++ b/api/task_queue/test/mock_task_queue_base.h @@ -0,0 +1,32 @@ +/* + * 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_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ +#define API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ + +#include + +#include "api/task_queue/task_queue_base.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockTaskQueueBase : public TaskQueueBase { + public: + MOCK_METHOD0(Delete, void()); + MOCK_METHOD1(PostTask, void(std::unique_ptr)); + MOCK_METHOD2(PostDelayedTask, void(std::unique_ptr, uint32_t)); + MOCK_METHOD2(PostDelayedHighPrecisionTask, + void(std::unique_ptr, uint32_t)); +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ diff --git a/api/test/DEPS b/api/test/DEPS index 329076830c..48889d528f 100644 --- a/api/test/DEPS +++ b/api/test/DEPS @@ -16,6 +16,10 @@ specific_include_rules = { "+rtc_base/network.h", "+rtc_base/network_constants.h", ], + "peer_network_dependencies\.h": [ + "+rtc_base/network.h", + "+rtc_base/thread.h", + ], "peerconnection_quality_test_fixture\.h": [ "+logging/rtc_event_log/rtc_event_log_factory_interface.h", "+rtc_base/network.h", @@ -23,6 +27,7 @@ specific_include_rules = { "+rtc_base/ssl_certificate.h", "+rtc_base/thread.h", "+media/base/media_constants.h", + "+modules/audio_processing/include/audio_processing.h", ], "time_controller\.h": [ "+modules/utility/include/process_thread.h", diff --git a/api/test/dummy_peer_connection.h b/api/test/dummy_peer_connection.h index 80ae20c3c7..4a262564a6 100644 --- a/api/test/dummy_peer_connection.h +++ b/api/test/dummy_peer_connection.h @@ -45,9 +45,7 @@ class DummyPeerConnection : public PeerConnectionInterface { return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); } - bool RemoveTrack(RtpSenderInterface* sender) override { return false; } - - RTCError RemoveTrackNew( + RTCError RemoveTrackOrError( rtc::scoped_refptr sender) override { return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); } diff --git a/api/test/frame_generator_interface.cc b/api/test/frame_generator_interface.cc index 356fe3af53..fe7b1e883d 100644 --- a/api/test/frame_generator_interface.cc +++ b/api/test/frame_generator_interface.cc @@ -26,7 +26,7 @@ const char* FrameGeneratorInterface::OutputTypeToString( case OutputType::kNV12: return "NV12"; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/api/test/mock_async_dns_resolver.h b/api/test/mock_async_dns_resolver.h index e863cac6e6..7cc17a8427 100644 --- a/api/test/mock_async_dns_resolver.h +++ b/api/test/mock_async_dns_resolver.h @@ -24,8 +24,8 @@ class MockAsyncDnsResolverResult : public AsyncDnsResolverResult { MOCK_METHOD(bool, GetResolvedAddress, (int, rtc::SocketAddress*), - (const override)); - MOCK_METHOD(int, GetError, (), (const override)); + (const, override)); + MOCK_METHOD(int, GetError, (), (const, override)); }; class MockAsyncDnsResolver : public AsyncDnsResolverInterface { @@ -34,7 +34,7 @@ class MockAsyncDnsResolver : public AsyncDnsResolverInterface { Start, (const rtc::SocketAddress&, std::function), (override)); - MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const override)); + MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const, override)); }; class MockAsyncDnsResolverFactory : public AsyncDnsResolverFactoryInterface { diff --git a/api/test/mock_data_channel.h b/api/test/mock_data_channel.h index 9346ffd638..40f7edb08a 100644 --- a/api/test/mock_data_channel.h +++ b/api/test/mock_data_channel.h @@ -22,7 +22,8 @@ class MockDataChannelInterface final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockDataChannelInterface(); + return rtc::scoped_refptr( + new MockDataChannelInterface()); } MOCK_METHOD(void, diff --git a/api/test/mock_media_stream_interface.h b/api/test/mock_media_stream_interface.h index 29521e6e23..17a30a877e 100644 --- a/api/test/mock_media_stream_interface.h +++ b/api/test/mock_media_stream_interface.h @@ -22,7 +22,7 @@ class MockAudioSource final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockAudioSource(); + return rtc::scoped_refptr(new MockAudioSource()); } MOCK_METHOD(void, @@ -55,7 +55,7 @@ class MockAudioSource final class MockAudioTrack final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockAudioTrack(); + return rtc::scoped_refptr(new MockAudioTrack()); } MOCK_METHOD(void, @@ -67,7 +67,7 @@ class MockAudioTrack final : public rtc::RefCountedObject { (ObserverInterface * observer), (override)); MOCK_METHOD(std::string, kind, (), (const, override)); - MOCK_METHOD(std::string, id, (), (const override)); + MOCK_METHOD(std::string, id, (), (const, override)); MOCK_METHOD(bool, enabled, (), (const, override)); MOCK_METHOD(bool, set_enabled, (bool enable), (override)); MOCK_METHOD(TrackState, state, (), (const, override)); diff --git a/api/test/mock_peer_connection_factory_interface.h b/api/test/mock_peer_connection_factory_interface.h index c2f2435fb8..6bab595b5a 100644 --- a/api/test/mock_peer_connection_factory_interface.h +++ b/api/test/mock_peer_connection_factory_interface.h @@ -23,7 +23,8 @@ class MockPeerConnectionFactoryInterface final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockPeerConnectionFactoryInterface(); + return rtc::scoped_refptr( + new MockPeerConnectionFactoryInterface()); } MOCK_METHOD(void, SetOptions, (const Options&), (override)); @@ -47,11 +48,11 @@ class MockPeerConnectionFactoryInterface final MOCK_METHOD(RtpCapabilities, GetRtpSenderCapabilities, (cricket::MediaType), - (const override)); + (const, override)); MOCK_METHOD(RtpCapabilities, GetRtpReceiverCapabilities, (cricket::MediaType), - (const override)); + (const, override)); MOCK_METHOD(rtc::scoped_refptr, CreateLocalMediaStream, (const std::string&), diff --git a/api/test/mock_peerconnectioninterface.h b/api/test/mock_peerconnectioninterface.h index 9ee67f5044..3761b1fc65 100644 --- a/api/test/mock_peerconnectioninterface.h +++ b/api/test/mock_peerconnectioninterface.h @@ -29,7 +29,7 @@ class MockPeerConnectionInterface : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockPeerConnectionInterface(); + return rtc::make_ref_counted(); } // PeerConnectionInterface @@ -48,9 +48,8 @@ class MockPeerConnectionInterface (rtc::scoped_refptr, const std::vector&), (override)); - MOCK_METHOD(bool, RemoveTrack, (RtpSenderInterface*), (override)); MOCK_METHOD(RTCError, - RemoveTrackNew, + RemoveTrackOrError, (rtc::scoped_refptr), (override)); MOCK_METHOD(RTCErrorOr>, @@ -77,15 +76,15 @@ class MockPeerConnectionInterface MOCK_METHOD(std::vector>, GetSenders, (), - (const override)); + (const, override)); MOCK_METHOD(std::vector>, GetReceivers, (), - (const override)); + (const, override)); MOCK_METHOD(std::vector>, GetTransceivers, (), - (const override)); + (const, override)); MOCK_METHOD(bool, GetStats, (StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel), @@ -105,7 +104,7 @@ class MockPeerConnectionInterface MOCK_METHOD(rtc::scoped_refptr, GetSctpTransport, (), - (const override)); + (const, override)); MOCK_METHOD(RTCErrorOr>, CreateDataChannelOrError, (const std::string&, const DataChannelInit*), @@ -113,27 +112,27 @@ class MockPeerConnectionInterface MOCK_METHOD(const SessionDescriptionInterface*, local_description, (), - (const override)); + (const, override)); MOCK_METHOD(const SessionDescriptionInterface*, remote_description, (), - (const override)); + (const, override)); MOCK_METHOD(const SessionDescriptionInterface*, current_local_description, (), - (const override)); + (const, override)); MOCK_METHOD(const SessionDescriptionInterface*, current_remote_description, (), - (const override)); + (const, override)); MOCK_METHOD(const SessionDescriptionInterface*, pending_local_description, (), - (const override)); + (const, override)); MOCK_METHOD(const SessionDescriptionInterface*, pending_remote_description, (), - (const override)); + (const, override)); MOCK_METHOD(void, RestartIce, (), (override)); MOCK_METHOD(void, CreateOffer, diff --git a/api/test/mock_rtp_transceiver.h b/api/test/mock_rtp_transceiver.h index a0a08c4772..5ea9028b77 100644 --- a/api/test/mock_rtp_transceiver.h +++ b/api/test/mock_rtp_transceiver.h @@ -23,7 +23,7 @@ class MockRtpTransceiver final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockRtpTransceiver(); + return rtc::scoped_refptr(new MockRtpTransceiver()); } MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); @@ -70,6 +70,10 @@ class MockRtpTransceiver final HeaderExtensionsToOffer, (), (const, override)); + MOCK_METHOD(std::vector, + HeaderExtensionsNegotiated, + (), + (const, override)); MOCK_METHOD(webrtc::RTCError, SetOfferedRtpHeaderExtensions, (rtc::ArrayView diff --git a/api/test/mock_rtpreceiver.h b/api/test/mock_rtpreceiver.h index a0b79e0bed..4bcf064b2a 100644 --- a/api/test/mock_rtpreceiver.h +++ b/api/test/mock_rtpreceiver.h @@ -24,20 +24,20 @@ class MockRtpReceiver : public rtc::RefCountedObject { MOCK_METHOD(rtc::scoped_refptr, track, (), - (const override)); + (const, override)); MOCK_METHOD(std::vector>, streams, (), - (const override)); - MOCK_METHOD(cricket::MediaType, media_type, (), (const override)); - MOCK_METHOD(std::string, id, (), (const override)); - MOCK_METHOD(RtpParameters, GetParameters, (), (const override)); + (const, override)); + MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); + MOCK_METHOD(std::string, id, (), (const, override)); + MOCK_METHOD(RtpParameters, GetParameters, (), (const, override)); MOCK_METHOD(void, SetObserver, (RtpReceiverObserverInterface*), (override)); MOCK_METHOD(void, SetJitterBufferMinimumDelay, (absl::optional), (override)); - MOCK_METHOD(std::vector, GetSources, (), (const override)); + MOCK_METHOD(std::vector, GetSources, (), (const, override)); }; } // namespace webrtc diff --git a/api/test/mock_rtpsender.h b/api/test/mock_rtpsender.h index f12a6185a6..e4d6399eed 100644 --- a/api/test/mock_rtpsender.h +++ b/api/test/mock_rtpsender.h @@ -25,21 +25,21 @@ class MockRtpSender : public rtc::RefCountedObject { MOCK_METHOD(rtc::scoped_refptr, track, (), - (const override)); - MOCK_METHOD(uint32_t, ssrc, (), (const override)); - MOCK_METHOD(cricket::MediaType, media_type, (), (const override)); - MOCK_METHOD(std::string, id, (), (const override)); - MOCK_METHOD(std::vector, stream_ids, (), (const override)); + (const, override)); + MOCK_METHOD(uint32_t, ssrc, (), (const, override)); + MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); + MOCK_METHOD(std::string, id, (), (const, override)); + MOCK_METHOD(std::vector, stream_ids, (), (const, override)); MOCK_METHOD(std::vector, init_send_encodings, (), - (const override)); - MOCK_METHOD(RtpParameters, GetParameters, (), (const override)); + (const, override)); + MOCK_METHOD(RtpParameters, GetParameters, (), (const, override)); MOCK_METHOD(RTCError, SetParameters, (const RtpParameters&), (override)); MOCK_METHOD(rtc::scoped_refptr, GetDtmfSender, (), - (const override)); + (const, override)); }; } // namespace webrtc diff --git a/api/test/mock_transformable_video_frame.h b/api/test/mock_transformable_video_frame.h index 36798b5d73..5cebcaba80 100644 --- a/api/test/mock_transformable_video_frame.h +++ b/api/test/mock_transformable_video_frame.h @@ -21,9 +21,9 @@ namespace webrtc { class MockTransformableVideoFrame : public webrtc::TransformableVideoFrameInterface { public: - MOCK_METHOD(rtc::ArrayView, GetData, (), (const override)); + MOCK_METHOD(rtc::ArrayView, GetData, (), (const, override)); MOCK_METHOD(void, SetData, (rtc::ArrayView data), (override)); - MOCK_METHOD(uint32_t, GetTimestamp, (), (const override)); + MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override)); MOCK_METHOD(uint32_t, GetSsrc, (), (const, override)); MOCK_METHOD(bool, IsKeyFrame, (), (const, override)); MOCK_METHOD(std::vector, GetAdditionalData, (), (const, override)); diff --git a/api/test/mock_video_encoder_factory.h b/api/test/mock_video_encoder_factory.h index 1aa14631be..79851096b7 100644 --- a/api/test/mock_video_encoder_factory.h +++ b/api/test/mock_video_encoder_factory.h @@ -28,10 +28,6 @@ class MockVideoEncoderFactory : public webrtc::VideoEncoderFactory { GetSupportedFormats, (), (const, override)); - MOCK_METHOD(CodecInfo, - QueryVideoEncoder, - (const SdpVideoFormat&), - (const, override)); MOCK_METHOD(std::unique_ptr, CreateVideoEncoder, (const SdpVideoFormat&), diff --git a/api/test/mock_video_track.h b/api/test/mock_video_track.h index 58a531bf42..705d13509b 100644 --- a/api/test/mock_video_track.h +++ b/api/test/mock_video_track.h @@ -24,7 +24,7 @@ class MockVideoTrack final : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { - return new MockVideoTrack(); + return rtc::scoped_refptr(new MockVideoTrack()); } // NotifierInterface diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h index 9fe4ad5eaf..b5c68af5f3 100644 --- a/api/test/network_emulation_manager.h +++ b/api/test/network_emulation_manager.h @@ -17,8 +17,10 @@ #include #include "api/array_view.h" +#include "api/packet_socket_factory.h" #include "api/test/network_emulation/cross_traffic.h" #include "api/test/network_emulation/network_emulation_interfaces.h" +#include "api/test/peer_network_dependencies.h" #include "api/test/simulated_network.h" #include "api/test/time_controller.h" #include "api/units/timestamp.h" @@ -125,6 +127,13 @@ class EmulatedNetworkManagerInterface { // WebRTC to properly setup network emulation. Returned manager is owned by // EmulatedNetworkManagerInterface implementation. virtual rtc::NetworkManager* network_manager() = 0; + // Returns non-null pointer to packet socket factory that have to be injected + // into WebRTC to properly setup network emulation. Returned factory is owned + // by EmulatedNetworkManagerInterface implementation. + virtual rtc::PacketSocketFactory* packet_socket_factory() = 0; + webrtc::webrtc_pc_e2e::PeerNetworkDependencies network_dependencies() { + return {network_thread(), network_manager(), packet_socket_factory()}; + } // Returns list of endpoints that are associated with this instance. Pointers // are guaranteed to be non-null and are owned by NetworkEmulationManager. virtual std::vector endpoints() const = 0; diff --git a/api/test/peer_network_dependencies.h b/api/test/peer_network_dependencies.h new file mode 100644 index 0000000000..6f85ad0a4d --- /dev/null +++ b/api/test/peer_network_dependencies.h @@ -0,0 +1,32 @@ +/* + * 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 API_TEST_PEER_NETWORK_DEPENDENCIES_H_ +#define API_TEST_PEER_NETWORK_DEPENDENCIES_H_ + +#include "api/packet_socket_factory.h" +#include "rtc_base/network.h" +#include "rtc_base/thread.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +// The network dependencies needed when adding a peer to tests using +// PeerConnectionE2EQualityTestFixture. +struct PeerNetworkDependencies { + rtc::Thread* network_thread; + rtc::NetworkManager* network_manager; + rtc::PacketSocketFactory* packet_socket_factory; +}; + +} // namespace webrtc_pc_e2e +} // namespace webrtc + +#endif // API_TEST_PEER_NETWORK_DEPENDENCIES_H_ diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h index 8d07ccb5ba..434a3a643f 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -20,6 +20,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/async_resolver_factory.h" +#include "api/audio/audio_mixer.h" #include "api/call/call_factory_interface.h" #include "api/fec_controller.h" #include "api/function_view.h" @@ -30,6 +31,7 @@ #include "api/task_queue/task_queue_factory.h" #include "api/test/audio_quality_analyzer_interface.h" #include "api/test/frame_generator_interface.h" +#include "api/test/peer_network_dependencies.h" #include "api/test/simulated_network.h" #include "api/test/stats_observer_interface.h" #include "api/test/track_id_stream_info_map.h" @@ -40,6 +42,7 @@ #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/base/media_constants.h" +#include "modules/audio_processing/include/audio_processing.h" #include "rtc_base/network.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_certificate.h" @@ -356,6 +359,10 @@ class PeerConnectionE2EQualityTestFixture { // Set a custom NetEqFactory to be used in the call. virtual PeerConfigurer* SetNetEqFactory( std::unique_ptr neteq_factory) = 0; + virtual PeerConfigurer* SetAudioProcessing( + rtc::scoped_refptr audio_processing) = 0; + virtual PeerConfigurer* SetAudioMixer( + rtc::scoped_refptr audio_mixer) = 0; // The parameters of the following 4 methods will be passed to the // PeerConnectionInterface implementation that will be created for this @@ -370,6 +377,11 @@ class PeerConnectionE2EQualityTestFixture { std::unique_ptr tls_cert_verifier) = 0; virtual PeerConfigurer* SetIceTransportFactory( std::unique_ptr factory) = 0; + // Flags to set on `cricket::PortAllocator`. These flags will be added + // to the default ones that are presented on the port allocator. + // For possible values check p2p/base/port_allocator.h. + virtual PeerConfigurer* SetPortAllocatorExtraFlags( + uint32_t extra_flags) = 0; // Add new video stream to the call that will be sent from this peer. // Default implementation of video frames generator will be used. @@ -395,6 +407,22 @@ class PeerConnectionE2EQualityTestFixture { // Set the audio stream for the call from this peer. If this method won't // be invoked, this peer will send no audio. virtual PeerConfigurer* SetAudioConfig(AudioConfig config) = 0; + + // Set if ULP FEC should be used or not. False by default. + virtual PeerConfigurer* SetUseUlpFEC(bool value) = 0; + // Set if Flex FEC should be used or not. False by default. + // Client also must enable `enable_flex_fec_support` in the `RunParams` to + // be able to use this feature. + virtual PeerConfigurer* SetUseFlexFEC(bool value) = 0; + // Specifies how much video encoder target bitrate should be different than + // target bitrate, provided by WebRTC stack. Must be greater than 0. Can be + // used to emulate overshooting of video encoders. This multiplier will + // be applied for all video encoder on both sides for all layers. Bitrate + // estimated by WebRTC stack will be multiplied by this multiplier and then + // provided into VideoEncoder::SetRates(...). 1.0 by default. + virtual PeerConfigurer* SetVideoEncoderBitrateMultiplier( + double multiplier) = 0; + // If is set, an RTCEventLog will be saved in that location and it will be // available for further analysis. virtual PeerConfigurer* SetRtcEventLogPath(std::string path) = 0; @@ -426,15 +454,9 @@ class PeerConnectionE2EQualityTestFixture { // it will be shut downed. TimeDelta run_duration; - bool use_ulp_fec = false; - bool use_flex_fec = false; - // Specifies how much video encoder target bitrate should be different than - // target bitrate, provided by WebRTC stack. Must be greater then 0. Can be - // used to emulate overshooting of video encoders. This multiplier will - // be applied for all video encoder on both sides for all layers. Bitrate - // estimated by WebRTC stack will be multiplied on this multiplier and then - // provided into VideoEncoder::SetRates(...). - double video_encoder_bitrate_multiplier = 1.0; + // If set to true peers will be able to use Flex FEC, otherwise they won't + // be able to negotiate it even if it's enabled on per peer level. + bool enable_flex_fec_support = false; // If true will set conference mode in SDP media section for all video // tracks for all peers. bool use_conference_mode = false; @@ -494,14 +516,13 @@ class PeerConnectionE2EQualityTestFixture { // Add a new peer to the call and return an object through which caller // can configure peer's behavior. - // `network_thread` will be used as network thread for peer's peer connection - // `network_manager` will be used to provide network interfaces for peer's - // peer connection. + // `network_dependencies` are used to provide networking for peer's peer + // connection. Members must be non-null. // `configurer` function will be used to configure peer in the call. virtual PeerHandle* AddPeer( - rtc::Thread* network_thread, - rtc::NetworkManager* network_manager, + const PeerNetworkDependencies& network_dependencies, rtc::FunctionView configurer) = 0; + // Runs the media quality test, which includes setting up the call with // configured participants, running it according to provided `run_params` and // terminating it properly at the end. During call duration media quality diff --git a/api/test/video/function_video_encoder_factory.h b/api/test/video/function_video_encoder_factory.h index a452eee7c4..9ae9719916 100644 --- a/api/test/video/function_video_encoder_factory.h +++ b/api/test/video/function_video_encoder_factory.h @@ -39,7 +39,7 @@ class FunctionVideoEncoderFactory final : public VideoEncoderFactory { // Unused by tests. std::vector GetSupportedFormats() const override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return {}; } diff --git a/api/test/video_quality_analyzer_interface.h b/api/test/video_quality_analyzer_interface.h index 97c9cc7af1..d27c9ea015 100644 --- a/api/test/video_quality_analyzer_interface.h +++ b/api/test/video_quality_analyzer_interface.h @@ -147,6 +147,9 @@ class VideoQualityAnalyzerInterface // statistics. virtual void Stop() {} + // Returns the last stream where this frame was captured. It means that if + // frame ids space wraps around, then stream label for frame id may change. + // It will crash, if the specified `frame_id` wasn't captured. virtual std::string GetStreamLabel(uint16_t frame_id) = 0; }; diff --git a/api/test/videocodec_test_fixture.h b/api/test/videocodec_test_fixture.h index c9b9ed4087..dbf20993e2 100644 --- a/api/test/videocodec_test_fixture.h +++ b/api/test/videocodec_test_fixture.h @@ -128,6 +128,14 @@ class VideoCodecTestFixture { // Name of the codec being tested. std::string codec_name; + // Encoder and decoder format and parameters. If provided, format is used to + // instantiate the codec. If not provided, the test creates and uses the + // default `SdpVideoFormat` based on `codec_name`. + // Encoder and decoder name (`SdpVideoFormat::name`) should be the same as + // `codec_name`. + absl::optional encoder_format; + absl::optional decoder_format; + // H.264 specific settings. struct H264CodecSettings { H264Profile profile = H264Profile::kProfileConstrainedBaseline; diff --git a/api/test/videocodec_test_stats.cc b/api/test/videocodec_test_stats.cc index 0cf00da85b..f082b1e935 100644 --- a/api/test/videocodec_test_stats.cc +++ b/api/test/videocodec_test_stats.cc @@ -86,6 +86,10 @@ std::map VideoCodecTestStats::VideoStatistics::ToMap() map["framerate_fps"] = std::to_string(framerate_fps); map["enc_speed_fps"] = std::to_string(enc_speed_fps); map["dec_speed_fps"] = std::to_string(dec_speed_fps); + map["avg_encode_latency_sec"] = std::to_string(avg_encode_latency_sec); + map["max_encode_latency_sec"] = std::to_string(max_encode_latency_sec); + map["avg_decode_latency_sec"] = std::to_string(avg_decode_latency_sec); + map["max_decode_latency_sec"] = std::to_string(max_decode_latency_sec); map["avg_delay_sec"] = std::to_string(avg_delay_sec); map["max_key_frame_delay_sec"] = std::to_string(max_key_frame_delay_sec); map["max_delta_frame_delay_sec"] = std::to_string(max_delta_frame_delay_sec); diff --git a/api/test/videocodec_test_stats.h b/api/test/videocodec_test_stats.h index 3f862338ee..a05985a665 100644 --- a/api/test/videocodec_test_stats.h +++ b/api/test/videocodec_test_stats.h @@ -101,6 +101,11 @@ class VideoCodecTestStats { float enc_speed_fps = 0.0f; float dec_speed_fps = 0.0f; + float avg_encode_latency_sec = 0.0f; + float max_encode_latency_sec = 0.0f; + float avg_decode_latency_sec = 0.0f; + float max_decode_latency_sec = 0.0f; + float avg_delay_sec = 0.0f; float max_key_frame_delay_sec = 0.0f; float max_delta_frame_delay_sec = 0.0f; diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn index 30955273b0..fb0fcf7a64 100644 --- a/api/transport/BUILD.gn +++ b/api/transport/BUILD.gn @@ -48,7 +48,10 @@ rtc_library("network_control") { rtc_source_set("webrtc_key_value_config") { visibility = [ "*" ] sources = [ "webrtc_key_value_config.h" ] - deps = [ "../../rtc_base/system:rtc_export" ] + deps = [ + "../../api:webrtc_key_value_config", + "../../rtc_base/system:rtc_export", + ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } diff --git a/api/transport/network_types.cc b/api/transport/network_types.cc index 7451940151..d6495ce490 100644 --- a/api/transport/network_types.cc +++ b/api/transport/network_types.cc @@ -103,8 +103,4 @@ bool PacedPacketInfo::operator==(const PacedPacketInfo& rhs) const { probe_cluster_min_bytes == rhs.probe_cluster_min_bytes; } -ProcessInterval::ProcessInterval() = default; -ProcessInterval::ProcessInterval(const ProcessInterval&) = default; -ProcessInterval::~ProcessInterval() = default; - } // namespace webrtc diff --git a/api/transport/network_types.h b/api/transport/network_types.h index 4e96b0f12e..29a7cf7705 100644 --- a/api/transport/network_types.h +++ b/api/transport/network_types.h @@ -241,9 +241,6 @@ struct NetworkControlUpdate { // Process control struct ProcessInterval { - ProcessInterval(); - ProcessInterval(const ProcessInterval&); - ~ProcessInterval(); Timestamp at_time = Timestamp::PlusInfinity(); absl::optional pacer_queue; }; diff --git a/api/transport/rtp/dependency_descriptor.cc b/api/transport/rtp/dependency_descriptor.cc index 2a9b6d9a71..e784853cd6 100644 --- a/api/transport/rtp/dependency_descriptor.cc +++ b/api/transport/rtp/dependency_descriptor.cc @@ -43,7 +43,7 @@ absl::InlinedVector StringToDecodeTargetIndications( indication = DecodeTargetIndication::kSwitch; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } dtis.push_back(indication); } diff --git a/api/transport/stun.cc b/api/transport/stun.cc index 0f86267ec2..87da0058d3 100644 --- a/api/transport/stun.cc +++ b/api/transport/stun.cc @@ -31,7 +31,6 @@ namespace cricket { namespace { const int k127Utf8CharactersLengthInBytes = 508; -const int kDefaultMaxAttributeLength = 508; const int kMessageIntegrityAttributeLength = 20; const int kTheoreticalMaximumAttributeLength = 65535; @@ -68,12 +67,6 @@ bool LengthValid(int type, int length) { case STUN_ATTR_SOFTWARE: return length <= k127Utf8CharactersLengthInBytes; // RFC 8489 section 14.14 - case STUN_ATTR_ORIGIN: - // 0x802F is unassigned by IANA. - // RESPONSE-ORIGIN is defined in RFC 5780 section 7.3, but does not - // specify a maximum length. It's an URL, so return an arbitrary - // restriction. - return length <= kDefaultMaxAttributeLength; case STUN_ATTR_DATA: // No length restriction in RFC; it's the content of an UDP datagram, // which in theory can be up to 65.535 bytes. @@ -83,7 +76,7 @@ bool LengthValid(int type, int length) { // Return an arbitrary restriction for all other types. return length <= kTheoreticalMaximumAttributeLength; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return true; } @@ -620,8 +613,6 @@ StunAttributeValueType StunMessage::GetAttributeValueType(int type) const { return STUN_VALUE_ADDRESS; case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32; - case STUN_ATTR_ORIGIN: - return STUN_VALUE_BYTE_STRING; case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32; case STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED: diff --git a/api/transport/stun.h b/api/transport/stun.h index b5f9ce1a6c..766b9ec368 100644 --- a/api/transport/stun.h +++ b/api/transport/stun.h @@ -62,7 +62,6 @@ enum StunAttributeType { STUN_ATTR_SOFTWARE = 0x8022, // ByteString STUN_ATTR_ALTERNATE_SERVER = 0x8023, // Address STUN_ATTR_FINGERPRINT = 0x8028, // UInt32 - STUN_ATTR_ORIGIN = 0x802F, // ByteString STUN_ATTR_RETRANSMIT_COUNT = 0xFF00 // UInt32 }; diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc index bf791f257d..e180703817 100644 --- a/api/transport/stun_unittest.cc +++ b/api/transport/stun_unittest.cc @@ -199,20 +199,6 @@ static const unsigned char kStunMessageWithErrorAttribute[] = { 0x69, 0x7a, 0x65, 0x64 }; -static const unsigned char kStunMessageWithOriginAttribute[] = { - 0x00, 0x01, 0x00, 0x18, // message header (binding request), length 24 - 0x21, 0x12, 0xA4, 0x42, // magic cookie - 0x29, 0x1f, 0xcd, 0x7c, // transaction id - 0xba, 0x58, 0xab, 0xd7, - 0xf2, 0x41, 0x01, 0x00, - 0x80, 0x2f, 0x00, 0x12, // origin attribute (length 18) - 0x68, 0x74, 0x74, 0x70, // http://example.com - 0x3A, 0x2F, 0x2F, 0x65, - 0x78, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x2e, 0x63, - 0x6f, 0x6d, 0x00, 0x00, -}; - // Sample messages with an invalid length Field // The actual length in bytes of the invalid messages (including STUN header) @@ -551,7 +537,6 @@ const in_addr kIPv4TestAddress1 = {{{0x0ac, 0x017, 0x044, 0x0e6}}}; const char kTestUserName1[] = "abcdefgh"; const char kTestUserName2[] = "abc"; const char kTestErrorReason[] = "Unauthorized"; -const char kTestOrigin[] = "http://example.com"; const int kTestErrorClass = 4; const int kTestErrorNumber = 1; const int kTestErrorCode = 401; @@ -1097,15 +1082,6 @@ TEST_F(StunTest, ReadMessageWithAnUnknownAttribute) { EXPECT_EQ(kTestUserName2, username->GetString()); } -TEST_F(StunTest, ReadMessageWithOriginAttribute) { - StunMessage msg; - size_t size = ReadStunMessage(&msg, kStunMessageWithOriginAttribute); - CheckStunHeader(msg, STUN_BINDING_REQUEST, size); - const StunByteStringAttribute* origin = msg.GetByteString(STUN_ATTR_ORIGIN); - ASSERT_TRUE(origin != NULL); - EXPECT_EQ(kTestOrigin, origin->GetString()); -} - TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) { StunMessage msg; size_t size = sizeof(kStunMessageWithErrorAttribute); @@ -1152,25 +1128,6 @@ TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) { memcmp(out.Data(), kStunMessageWithUInt16ListAttribute, size - 2)); } -TEST_F(StunTest, WriteMessageWithOriginAttribute) { - StunMessage msg; - size_t size = sizeof(kStunMessageWithOriginAttribute); - - msg.SetType(STUN_BINDING_REQUEST); - msg.SetTransactionID( - std::string(reinterpret_cast(kTestTransactionId1), - kStunTransactionIdLength)); - auto origin = - std::make_unique(STUN_ATTR_ORIGIN, kTestOrigin); - msg.AddAttribute(std::move(origin)); - - rtc::ByteBufferWriter out; - EXPECT_TRUE(msg.Write(&out)); - ASSERT_EQ(size, out.Length()); - // Check everything up to the padding - ASSERT_EQ(0, memcmp(out.Data(), kStunMessageWithOriginAttribute, size - 2)); -} - // Test that we fail to read messages with invalid lengths. void CheckFailureToRead(const unsigned char* testcase, size_t length) { StunMessage msg; diff --git a/api/transport/webrtc_key_value_config.h b/api/transport/webrtc_key_value_config.h index 5666a82783..f666873cfe 100644 --- a/api/transport/webrtc_key_value_config.h +++ b/api/transport/webrtc_key_value_config.h @@ -10,24 +10,8 @@ #ifndef API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ #define API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ -#include - -#include "absl/strings/string_view.h" -#include "rtc_base/system/rtc_export.h" - -namespace webrtc { - -// An interface that provides a key-value mapping for configuring internal -// details of WebRTC. Note that there's no guarantess that the meaning of a -// particular key value mapping will be preserved over time and no announcements -// will be made if they are changed. It's up to the library user to ensure that -// the behavior does not break. -class RTC_EXPORT WebRtcKeyValueConfig { - public: - virtual ~WebRtcKeyValueConfig() = default; - // The configured value for the given key. Defaults to an empty string. - virtual std::string Lookup(absl::string_view key) const = 0; -}; -} // namespace webrtc +// TODO(bugs.webrtc.org/10335): Remove once all migrated to +// "api/webrtc_key_value_config.h". +#include "api/webrtc_key_value_config.h" #endif // API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn index e358b1f41e..34b7f22575 100644 --- a/api/video/BUILD.gn +++ b/api/video/BUILD.gn @@ -31,6 +31,7 @@ rtc_library("video_rtp_headers") { "../../rtc_base:rtc_base_approved", "../../rtc_base/system:rtc_export", "../units:data_rate", + "../units:time_delta", ] absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector", @@ -43,6 +44,8 @@ rtc_library("video_frame") { sources = [ "i420_buffer.cc", "i420_buffer.h", + "i444_buffer.cc", + "i444_buffer.h", "nv12_buffer.cc", "nv12_buffer.h", "video_codec_type.h", @@ -60,6 +63,7 @@ rtc_library("video_frame") { "..:array_view", "..:rtp_packet_info", "..:scoped_refptr", + "..:video_track_source_constraints", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", "../../rtc_base/memory:aligned_malloc", @@ -145,7 +149,11 @@ rtc_library("encoded_frame") { "encoded_frame.h", ] - deps = [ "../../modules/video_coding:encoded_frame" ] + deps = [ + "../../modules/video_coding:encoded_frame", + "../units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("rtp_video_frame_assembler") { @@ -159,6 +167,7 @@ rtc_library("rtp_video_frame_assembler") { ":encoded_frame", "../../modules/rtp_rtcp:rtp_rtcp", "../../modules/rtp_rtcp:rtp_rtcp_format", + "../../modules/video_coding:packet_buffer", "../../modules/video_coding:video_coding", "../../rtc_base:logging", ] diff --git a/api/video/DEPS b/api/video/DEPS index cf6770dce0..5a3e496bcf 100644 --- a/api/video/DEPS +++ b/api/video/DEPS @@ -18,6 +18,10 @@ specific_include_rules = { "+rtc_base/memory/aligned_malloc.h", ], + "i444_buffer\.h": [ + "+rtc_base/memory/aligned_malloc.h", + ], + "nv12_buffer\.h": [ "+rtc_base/memory/aligned_malloc.h", ], diff --git a/api/video/encoded_frame.cc b/api/video/encoded_frame.cc index 42d6b06b84..c5e2abbbb4 100644 --- a/api/video/encoded_frame.cc +++ b/api/video/encoded_frame.cc @@ -10,10 +10,24 @@ #include "api/video/encoded_frame.h" +#include "absl/types/optional.h" + namespace webrtc { +absl::optional EncodedFrame::ReceivedTimestamp() const { + return ReceivedTime() >= 0 + ? absl::make_optional(Timestamp::Millis(ReceivedTime())) + : absl::nullopt; +} + +absl::optional EncodedFrame::RenderTimestamp() const { + return RenderTimeMs() >= 0 + ? absl::make_optional(Timestamp::Millis(RenderTimeMs())) + : absl::nullopt; +} + bool EncodedFrame::delayed_by_retransmission() const { - return 0; + return false; } } // namespace webrtc diff --git a/api/video/encoded_frame.h b/api/video/encoded_frame.h index 3ef26caf6e..66aee227bb 100644 --- a/api/video/encoded_frame.h +++ b/api/video/encoded_frame.h @@ -14,6 +14,8 @@ #include #include +#include "absl/types/optional.h" +#include "api/units/timestamp.h" #include "modules/video_coding/encoded_frame.h" namespace webrtc { @@ -30,10 +32,18 @@ class EncodedFrame : public webrtc::VCMEncodedFrame { virtual ~EncodedFrame() {} // When this frame was received. + // TODO(bugs.webrtc.org/13756): Use Timestamp instead of int. virtual int64_t ReceivedTime() const = 0; + // Returns a Timestamp from `ReceivedTime`, or nullopt if there is no receive + // time. + absl::optional ReceivedTimestamp() const; // When this frame should be rendered. + // TODO(bugs.webrtc.org/13756): Use Timestamp instead of int. virtual int64_t RenderTime() const = 0; + // Returns a Timestamp from `RenderTime`, or nullopt if there is no + // render time. + absl::optional RenderTimestamp() const; // This information is currently needed by the timing calculation class. // TODO(philipel): Remove this function when a new timing class has diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h index 987645b569..88df34916b 100644 --- a/api/video/encoded_image.h +++ b/api/video/encoded_image.h @@ -154,6 +154,16 @@ class RTC_EXPORT EncodedImage { return encoded_data_ ? encoded_data_->data() : nullptr; } + // Returns whether the encoded image can be considered to be of target + // quality. + bool IsAtTargetQuality() const { return at_target_quality_; } + + // Sets that the encoded image can be considered to be of target quality to + // true or false. + void SetAtTargetQuality(bool at_target_quality) { + at_target_quality_ = at_target_quality; + } + uint32_t _encodedWidth = 0; uint32_t _encodedHeight = 0; // NTP time of the capture time in local timebase in milliseconds. @@ -200,6 +210,8 @@ class RTC_EXPORT EncodedImage { // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources RtpPacketInfos packet_infos_; bool retransmission_allowed_ = true; + // True if the encoded image can be considered to be of target quality. + bool at_target_quality_ = false; }; } // namespace webrtc diff --git a/api/video/i010_buffer.cc b/api/video/i010_buffer.cc index 74d37d1b57..b98e586562 100644 --- a/api/video/i010_buffer.cc +++ b/api/video/i010_buffer.cc @@ -117,7 +117,7 @@ rtc::scoped_refptr I010Buffer::Rotate( switch (rotation) { // This case is covered by the early return. case webrtc::kVideoRotation_0: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case webrtc::kVideoRotation_90: dest_x = src.height() - y - 1; @@ -232,35 +232,4 @@ void I010Buffer::ScaleFrom(const I010BufferInterface& src) { CropAndScaleFrom(src, 0, 0, src.width(), src.height()); } -void I010Buffer::PasteFrom(const I010BufferInterface& picture, - int offset_col, - int offset_row) { - RTC_CHECK_LE(picture.width() + offset_col, width()); - RTC_CHECK_LE(picture.height() + offset_row, height()); - RTC_CHECK_GE(offset_col, 0); - RTC_CHECK_GE(offset_row, 0); - - // Pasted picture has to be aligned so subsumpled UV plane isn't corrupted. - RTC_CHECK(offset_col % 2 == 0); - RTC_CHECK(offset_row % 2 == 0); - RTC_CHECK(picture.width() % 2 == 0 || - picture.width() + offset_col == width()); - RTC_CHECK(picture.height() % 2 == 0 || - picture.height() + offset_row == height()); - - libyuv::CopyPlane_16(picture.DataY(), picture.StrideY(), - MutableDataY() + StrideY() * offset_row + offset_col, - StrideY(), picture.width(), picture.height()); - - libyuv::CopyPlane_16( - picture.DataU(), picture.StrideU(), - MutableDataU() + StrideU() * offset_row / 2 + offset_col / 2, StrideU(), - picture.width() / 2, picture.height() / 2); - - libyuv::CopyPlane_16( - picture.DataV(), picture.StrideV(), - MutableDataV() + StrideV() * offset_row / 2 + offset_col / 2, StrideV(), - picture.width() / 2, picture.height() / 2); -} - } // namespace webrtc diff --git a/api/video/i010_buffer.h b/api/video/i010_buffer.h index 776797521b..11e0879fec 100644 --- a/api/video/i010_buffer.h +++ b/api/video/i010_buffer.h @@ -66,12 +66,6 @@ class I010Buffer : public I010BufferInterface { // Scale all of `src` to the size of `this` buffer, with no cropping. void ScaleFrom(const I010BufferInterface& src); - // Pastes whole picture to canvas at (offset_row, offset_col). - // Offsets and picture dimensions must be even. - void PasteFrom(const I010BufferInterface& picture, - int offset_col, - int offset_row); - protected: I010Buffer(int width, int height, int stride_y, int stride_u, int stride_v); ~I010Buffer() override; diff --git a/api/video/i420_buffer.cc b/api/video/i420_buffer.cc index 8783a4a313..deecf1d71d 100644 --- a/api/video/i420_buffer.cc +++ b/api/video/i420_buffer.cc @@ -229,35 +229,4 @@ void I420Buffer::ScaleFrom(const I420BufferInterface& src) { CropAndScaleFrom(src, 0, 0, src.width(), src.height()); } -void I420Buffer::PasteFrom(const I420BufferInterface& picture, - int offset_col, - int offset_row) { - RTC_CHECK_LE(picture.width() + offset_col, width()); - RTC_CHECK_LE(picture.height() + offset_row, height()); - RTC_CHECK_GE(offset_col, 0); - RTC_CHECK_GE(offset_row, 0); - - // Pasted picture has to be aligned so subsumpled UV plane isn't corrupted. - RTC_CHECK(offset_col % 2 == 0); - RTC_CHECK(offset_row % 2 == 0); - RTC_CHECK(picture.width() % 2 == 0 || - picture.width() + offset_col == width()); - RTC_CHECK(picture.height() % 2 == 0 || - picture.height() + offset_row == height()); - - libyuv::CopyPlane(picture.DataY(), picture.StrideY(), - MutableDataY() + StrideY() * offset_row + offset_col, - StrideY(), picture.width(), picture.height()); - - libyuv::CopyPlane( - picture.DataU(), picture.StrideU(), - MutableDataU() + StrideU() * offset_row / 2 + offset_col / 2, StrideU(), - picture.width() / 2, picture.height() / 2); - - libyuv::CopyPlane( - picture.DataV(), picture.StrideV(), - MutableDataV() + StrideV() * offset_row / 2 + offset_col / 2, StrideV(), - picture.width() / 2, picture.height() / 2); -} - } // namespace webrtc diff --git a/api/video/i420_buffer.h b/api/video/i420_buffer.h index b60df09aba..af52c64fb4 100644 --- a/api/video/i420_buffer.h +++ b/api/video/i420_buffer.h @@ -98,12 +98,6 @@ class RTC_EXPORT I420Buffer : public I420BufferInterface { // Scale all of `src` to the size of `this` buffer, with no cropping. void ScaleFrom(const I420BufferInterface& src); - // Pastes whole picture to canvas at (offset_row, offset_col). - // Offsets and picture dimensions must be even. - void PasteFrom(const I420BufferInterface& picture, - int offset_col, - int offset_row); - protected: I420Buffer(int width, int height); I420Buffer(int width, int height, int stride_y, int stride_u, int stride_v); diff --git a/api/video/i444_buffer.cc b/api/video/i444_buffer.cc new file mode 100644 index 0000000000..8bf9f76625 --- /dev/null +++ b/api/video/i444_buffer.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/video/i444_buffer.h" + +#include + +#include +#include + +#include "api/video/i420_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/planar_functions.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. +static const int kBufferAlignment = 64; + +namespace webrtc { + +namespace { + +int I444DataSize(int height, int stride_y, int stride_u, int stride_v) { + return stride_y * height + stride_u * height + stride_v * height; +} + +} // namespace + +I444Buffer::I444Buffer(int width, int height) + : I444Buffer(width, height, width, (width), (width)) {} + +I444Buffer::I444Buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) + : width_(width), + height_(height), + stride_y_(stride_y), + stride_u_(stride_u), + stride_v_(stride_v), + data_(static_cast( + AlignedMalloc(I444DataSize(height, stride_y, stride_u, stride_v), + kBufferAlignment))) { + RTC_DCHECK_GT(width, 0); + RTC_DCHECK_GT(height, 0); + RTC_DCHECK_GE(stride_y, width); + RTC_DCHECK_GE(stride_u, (width)); + RTC_DCHECK_GE(stride_v, (width)); +} + +I444Buffer::~I444Buffer() {} + +// static +rtc::scoped_refptr I444Buffer::Create(int width, int height) { + return rtc::make_ref_counted(width, height); +} + +// static +rtc::scoped_refptr I444Buffer::Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return rtc::make_ref_counted(width, height, stride_y, stride_u, + stride_v); +} + +// static +rtc::scoped_refptr I444Buffer::Copy( + const I444BufferInterface& source) { + return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), + source.DataU(), source.StrideU(), source.DataV(), + source.StrideV()); +} + +// static +rtc::scoped_refptr I444Buffer::Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v) { + // Note: May use different strides than the input data. + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ(0, libyuv::I444Copy(data_y, stride_y, data_u, stride_u, data_v, + stride_v, buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), + buffer->StrideU(), buffer->MutableDataV(), + buffer->StrideV(), width, height)); + return buffer; +} + +// static +rtc::scoped_refptr I444Buffer::Rotate( + const I444BufferInterface& src, + VideoRotation rotation) { + RTC_CHECK(src.DataY()); + RTC_CHECK(src.DataU()); + RTC_CHECK(src.DataV()); + + int rotated_width = src.width(); + int rotated_height = src.height(); + if (rotation == webrtc::kVideoRotation_90 || + rotation == webrtc::kVideoRotation_270) { + std::swap(rotated_width, rotated_height); + } + + rtc::scoped_refptr buffer = + I444Buffer::Create(rotated_width, rotated_height); + + RTC_CHECK_EQ(0, + libyuv::I444Rotate( + src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), + src.DataV(), src.StrideV(), buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), src.width(), + src.height(), static_cast(rotation))); + + return buffer; +} + +rtc::scoped_refptr I444Buffer::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), + i420_buffer->MutableDataY(), i420_buffer->StrideY(), + i420_buffer->MutableDataU(), i420_buffer->StrideU(), + i420_buffer->MutableDataV(), i420_buffer->StrideV(), + width(), height()); + return i420_buffer; +} + +void I444Buffer::InitializeData() { + memset(data_.get(), 0, + I444DataSize(height_, stride_y_, stride_u_, stride_v_)); +} + +int I444Buffer::width() const { + return width_; +} + +int I444Buffer::height() const { + return height_; +} + +const uint8_t* I444Buffer::DataY() const { + return data_.get(); +} +const uint8_t* I444Buffer::DataU() const { + return data_.get() + stride_y_ * height_; +} +const uint8_t* I444Buffer::DataV() const { + return data_.get() + stride_y_ * height_ + stride_u_ * ((height_)); +} + +int I444Buffer::StrideY() const { + return stride_y_; +} +int I444Buffer::StrideU() const { + return stride_u_; +} +int I444Buffer::StrideV() const { + return stride_v_; +} + +uint8_t* I444Buffer::MutableDataY() { + return const_cast(DataY()); +} +uint8_t* I444Buffer::MutableDataU() { + return const_cast(DataU()); +} +uint8_t* I444Buffer::MutableDataV() { + return const_cast(DataV()); +} + +void I444Buffer::CropAndScaleFrom(const I444BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height) { + RTC_CHECK_LE(crop_width, src.width()); + RTC_CHECK_LE(crop_height, src.height()); + RTC_CHECK_LE(crop_width + offset_x, src.width()); + RTC_CHECK_LE(crop_height + offset_y, src.height()); + RTC_CHECK_GE(offset_x, 0); + RTC_CHECK_GE(offset_y, 0); + + const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; + const uint8_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; + const uint8_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; + int res = + libyuv::I444Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, + src.StrideV(), crop_width, crop_height, MutableDataY(), + StrideY(), MutableDataU(), StrideU(), MutableDataV(), + StrideV(), width(), height(), libyuv::kFilterBox); + + RTC_DCHECK_EQ(res, 0); +} + +} // namespace webrtc diff --git a/api/video/i444_buffer.h b/api/video/i444_buffer.h new file mode 100644 index 0000000000..557bf4f3e0 --- /dev/null +++ b/api/video/i444_buffer.h @@ -0,0 +1,104 @@ +/* + * 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 API_VIDEO_I444_BUFFER_H_ +#define API_VIDEO_I444_BUFFER_H_ + +#include + +#include + +#include "api/scoped_refptr.h" +#include "api/video/video_frame_buffer.h" +#include "api/video/video_rotation.h" +#include "rtc_base/memory/aligned_malloc.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Plain I444 buffer in standard memory. +// I444 represents an image with in YUV format withouth any chroma subsampling. +// https://en.wikipedia.org/wiki/Chroma_subsampling#4:4:4 +class RTC_EXPORT I444Buffer : public I444BufferInterface { + public: + static rtc::scoped_refptr Create(int width, int height); + static rtc::scoped_refptr Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + // Create a new buffer and copy the pixel data. + static rtc::scoped_refptr Copy(const I444BufferInterface& buffer); + + static rtc::scoped_refptr Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v); + + // Returns a rotated copy of |src|. + static rtc::scoped_refptr Rotate(const I444BufferInterface& src, + VideoRotation rotation); + + rtc::scoped_refptr ToI420() final; + const I420BufferInterface* GetI420() const final { return nullptr; } + + // Sets all three planes to all zeros. Used to work around for + // quirks in memory checkers + // (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and + // ffmpeg (http://crbug.com/390941). + // TODO(nisse): Deprecated. Should be deleted if/when those issues + // are resolved in a better way. Or in the mean time, use SetBlack. + void InitializeData(); + + int width() const override; + int height() const override; + const uint8_t* DataY() const override; + const uint8_t* DataU() const override; + const uint8_t* DataV() const override; + + int StrideY() const override; + int StrideU() const override; + int StrideV() const override; + + uint8_t* MutableDataY(); + uint8_t* MutableDataU(); + uint8_t* MutableDataV(); + + // Scale the cropped area of |src| to the size of |this| buffer, and + // write the result into |this|. + void CropAndScaleFrom(const I444BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height); + + protected: + I444Buffer(int width, int height); + I444Buffer(int width, int height, int stride_y, int stride_u, int stride_v); + + ~I444Buffer() override; + + private: + const int width_; + const int height_; + const int stride_y_; + const int stride_u_; + const int stride_v_; + const std::unique_ptr data_; +}; + +} // namespace webrtc + +#endif // API_VIDEO_I444_BUFFER_H_ diff --git a/api/video/rtp_video_frame_assembler.cc b/api/video/rtp_video_frame_assembler.cc index 8f3d04c30b..81c08389bb 100644 --- a/api/video/rtp_video_frame_assembler.cc +++ b/api/video/rtp_video_frame_assembler.cc @@ -51,7 +51,7 @@ std::unique_ptr CreateDepacketizer( case RtpVideoFrameAssembler::kGeneric: return std::make_unique(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } } // namespace @@ -92,6 +92,11 @@ RtpVideoFrameAssembler::Impl::Impl( RtpVideoFrameAssembler::FrameVector RtpVideoFrameAssembler::Impl::InsertPacket( const RtpPacketReceived& rtp_packet) { + if (rtp_packet.payload_size() == 0) { + ClearOldData(rtp_packet.SequenceNumber()); + return UpdateWithPadding(rtp_packet.SequenceNumber()); + } + absl::optional parsed_payload = depacketizer_->Parse(rtp_packet.PayloadBuffer()); @@ -99,11 +104,6 @@ RtpVideoFrameAssembler::FrameVector RtpVideoFrameAssembler::Impl::InsertPacket( return {}; } - if (parsed_payload->video_payload.size() == 0) { - ClearOldData(rtp_packet.SequenceNumber()); - return UpdateWithPadding(rtp_packet.SequenceNumber()); - } - if (rtp_packet.HasExtension()) { if (!ParseDependenciesDescriptorExtension(rtp_packet, parsed_payload->video_header)) { @@ -187,7 +187,10 @@ RtpVideoFrameAssembler::Impl::FindReferences(RtpFrameVector frames) { for (auto& frame : frames) { auto complete_frames = reference_finder_.ManageFrame(std::move(frame)); for (std::unique_ptr& complete_frame : complete_frames) { - res.push_back(std::move(complete_frame)); + uint16_t rtp_seq_num_start = complete_frame->first_seq_num(); + uint16_t rtp_seq_num_end = complete_frame->last_seq_num(); + res.emplace_back(rtp_seq_num_start, rtp_seq_num_end, + std::move(complete_frame)); } } return res; @@ -199,8 +202,12 @@ RtpVideoFrameAssembler::Impl::UpdateWithPadding(uint16_t seq_num) { FindReferences(AssembleFrames(packet_buffer_.InsertPadding(seq_num))); auto ref_finder_update = reference_finder_.PaddingReceived(seq_num); - res.insert(res.end(), std::make_move_iterator(ref_finder_update.begin()), - std::make_move_iterator(ref_finder_update.end())); + for (std::unique_ptr& complete_frame : ref_finder_update) { + uint16_t rtp_seq_num_start = complete_frame->first_seq_num(); + uint16_t rtp_seq_num_end = complete_frame->last_seq_num(); + res.emplace_back(rtp_seq_num_start, rtp_seq_num_end, + std::move(complete_frame)); + } return res; } diff --git a/api/video/rtp_video_frame_assembler.h b/api/video/rtp_video_frame_assembler.h index 353942bdc8..83162cb818 100644 --- a/api/video/rtp_video_frame_assembler.h +++ b/api/video/rtp_video_frame_assembler.h @@ -13,6 +13,7 @@ #include #include +#include #include "absl/container/inlined_vector.h" #include "api/video/encoded_frame.h" @@ -26,9 +27,31 @@ namespace webrtc { // monotonic in decode order, dependencies are expressed as frame IDs. class RtpVideoFrameAssembler { public: + // The RtpVideoFrameAssembler should return "RTP frames", but for now there + // is no good class for this purpose. For now return an EncodedFrame bundled + // with some minimal RTP information. + class AssembledFrame { + public: + AssembledFrame(uint16_t rtp_seq_num_start, + uint16_t rtp_seq_num_end, + std::unique_ptr frame) + : rtp_seq_num_start_(rtp_seq_num_start), + rtp_seq_num_end_(rtp_seq_num_end), + frame_(std::move(frame)) {} + + uint16_t RtpSeqNumStart() const { return rtp_seq_num_start_; } + uint16_t RtpSeqNumEnd() const { return rtp_seq_num_end_; } + std::unique_ptr ExtractFrame() { return std::move(frame_); } + + private: + uint16_t rtp_seq_num_start_; + uint16_t rtp_seq_num_end_; + std::unique_ptr frame_; + }; + // FrameVector is just a vector-like type of std::unique_ptr. // The vector type may change without notice. - using FrameVector = absl::InlinedVector, 3>; + using FrameVector = absl::InlinedVector; enum PayloadFormat { kRaw, kH264, kVp8, kVp9, kAv1, kGeneric }; explicit RtpVideoFrameAssembler(PayloadFormat payload_format); diff --git a/api/video/rtp_video_frame_assembler_unittests.cc b/api/video/rtp_video_frame_assembler_unittests.cc index 916a83cd73..82defb8399 100644 --- a/api/video/rtp_video_frame_assembler_unittests.cc +++ b/api/video/rtp_video_frame_assembler_unittests.cc @@ -93,7 +93,7 @@ class PacketBuilder { return kVideoCodecGeneric; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return absl::nullopt; } @@ -105,6 +105,13 @@ class PacketBuilder { RtpPacketToSend packet_to_send_; }; +RtpPacketReceived PaddingPacket(uint16_t seq_num) { + RtpPacketReceived padding_packet; + padding_packet.SetSequenceNumber(seq_num); + padding_packet.SetPadding(224); + return padding_packet; +} + void AppendFrames(RtpVideoFrameAssembler::FrameVector from, RtpVideoFrameAssembler::FrameVector& to) { to.insert(to.end(), std::make_move_iterator(from.begin()), @@ -155,13 +162,15 @@ TEST(RtpVideoFrameAssembler, Vp8Packetization) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(10)); - EXPECT_THAT(References(frames[0]), IsEmpty()); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kKeyframePayload)); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(References(first_frame), IsEmpty()); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kKeyframePayload)); - EXPECT_THAT(frames[1]->Id(), Eq(11)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(10)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kDeltaframePayload)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(11)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaframePayload)); } TEST(RtpVideoFrameAssembler, Vp9Packetization) { @@ -194,13 +203,15 @@ TEST(RtpVideoFrameAssembler, Vp9Packetization) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(10)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[1]->Id(), Eq(11)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(10)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(11)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); } TEST(RtpVideoFrameAssembler, Av1Packetization) { @@ -232,13 +243,15 @@ TEST(RtpVideoFrameAssembler, Av1Packetization) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(20)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kKeyframePayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(20)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kKeyframePayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[1]->Id(), Eq(21)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kDeltaframePayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(20)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(21)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaframePayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(20)); } TEST(RtpVideoFrameAssembler, RawPacketizationDependencyDescriptorExtension) { @@ -283,13 +296,15 @@ TEST(RtpVideoFrameAssembler, RawPacketizationDependencyDescriptorExtension) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(10)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[1]->Id(), Eq(20)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(10)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(20)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); } TEST(RtpVideoFrameAssembler, RawPacketizationGenericDescriptor00Extension) { @@ -322,13 +337,15 @@ TEST(RtpVideoFrameAssembler, RawPacketizationGenericDescriptor00Extension) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(100)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(100)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[1]->Id(), Eq(102)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(100)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(102)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(100)); } TEST(RtpVideoFrameAssembler, RawPacketizationGenericPayloadDescriptor) { @@ -356,13 +373,15 @@ TEST(RtpVideoFrameAssembler, RawPacketizationGenericPayloadDescriptor) { ASSERT_THAT(frames, SizeIs(2)); - EXPECT_THAT(frames[0]->Id(), Eq(123)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(123)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[1]->Id(), Eq(124)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(123)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(124)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(123)); } TEST(RtpVideoFrameAssembler, Padding) { @@ -389,29 +408,18 @@ TEST(RtpVideoFrameAssembler, Padding) { frames); ASSERT_THAT(frames, SizeIs(1)); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(123)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); - EXPECT_THAT(frames[0]->Id(), Eq(123)); - EXPECT_THAT(Payload(frames[0]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[0]), IsEmpty()); - - // Padding packets have no bitstream data. An easy way to generate one is to - // build a normal packet and then simply remove the bitstream portion of the - // payload. - RtpPacketReceived padding_packet = PacketBuilder(PayloadFormat::kGeneric) - .WithPayload(kPayload) - .WithVideoHeader(video_header) - .WithSeqNum(124) - .Build(); - // The payload descriptor is one byte, keep it. - padding_packet.SetPayloadSize(1); - - AppendFrames(assembler.InsertPacket(padding_packet), frames); + AppendFrames(assembler.InsertPacket(PaddingPacket(/*seq_num=*/124)), frames); ASSERT_THAT(frames, SizeIs(2)); - - EXPECT_THAT(frames[1]->Id(), Eq(125)); - EXPECT_THAT(Payload(frames[1]), ElementsAreArray(kPayload)); - EXPECT_THAT(References(frames[1]), UnorderedElementsAre(123)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(125)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(123)); } TEST(RtpVideoFrameAssembler, ClearOldPackets) { @@ -464,17 +472,8 @@ TEST(RtpVideoFrameAssembler, ClearOldPacketsWithPadding) { .Build()), SizeIs(1)); - // Padding packets have no bitstream data. An easy way to generate one is to - // build a normal packet and then simply remove the bitstream portion of the - // payload. - RtpPacketReceived padding_packet = PacketBuilder(PayloadFormat::kGeneric) - .WithPayload(kPayload) - .WithVideoHeader(video_header) - .WithSeqNum(2000) - .Build(); - // The payload descriptor is one byte, keep it. - padding_packet.SetPayloadSize(1); - EXPECT_THAT(assembler.InsertPacket(padding_packet), SizeIs(0)); + EXPECT_THAT(assembler.InsertPacket(PaddingPacket(/*seq_num=*/2000)), + SizeIs(0)); EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) .WithPayload(kPayload) @@ -491,5 +490,94 @@ TEST(RtpVideoFrameAssembler, ClearOldPacketsWithPadding) { SizeIs(1)); } +TEST(RtpVideoFrameAssembler, SeqNumStartAndSeqNumEndSet) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = + "Some payload that will get split into two when packetized."; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = sizeof(kPayload) - 1; + + auto packetizer = + RtpPacketizer::Create(kVideoCodecGeneric, kPayload, limits, video_header); + ASSERT_THAT(packetizer->NumPackets(), Eq(2U)); + + RtpPacketReceived::ExtensionManager extension_manager; + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(123); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(124); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + AppendFrames(assembler.InsertPacket(received_packet), frames); + } + + ASSERT_THAT(frames, SizeIs(1)); + EXPECT_THAT(frames[0].RtpSeqNumStart(), Eq(123)); + EXPECT_THAT(frames[0].RtpSeqNumEnd(), Eq(124)); +} + +TEST(RtpVideoFrameAssembler, SeqNumStartAndSeqNumEndSetWhenPaddingReceived) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = + "Some payload that will get split into two when packetized."; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(121) + .Build()), + SizeIs(1)); + + video_header.frame_type = VideoFrameType::kVideoFrameDelta; + RtpPacketReceived::ExtensionManager extension_manager; + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = sizeof(kPayload) - 1; + + auto packetizer = + RtpPacketizer::Create(kVideoCodecGeneric, kPayload, limits, video_header); + ASSERT_THAT(packetizer->NumPackets(), Eq(2U)); + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(123); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(124); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + AppendFrames(assembler.InsertPacket(PaddingPacket(/*seq_num=*/122)), frames); + + ASSERT_THAT(frames, SizeIs(1)); + EXPECT_THAT(frames[0].RtpSeqNumStart(), Eq(123)); + EXPECT_THAT(frames[0].RtpSeqNumEnd(), Eq(124)); +} + } // namespace } // namespace webrtc diff --git a/api/video/test/BUILD.gn b/api/video/test/BUILD.gn index 1573e7848f..5b0d57b3c6 100644 --- a/api/video/test/BUILD.gn +++ b/api/video/test/BUILD.gn @@ -12,6 +12,7 @@ rtc_library("rtc_api_video_unittests") { testonly = true sources = [ "color_space_unittest.cc", + "i444_buffer_unittest.cc", "nv12_buffer_unittest.cc", "video_adaptation_counters_unittest.cc", "video_bitrate_allocation_unittest.cc", diff --git a/api/video/test/i444_buffer_unittest.cc b/api/video/test/i444_buffer_unittest.cc new file mode 100644 index 0000000000..9a1a9315aa --- /dev/null +++ b/api/video/test/i444_buffer_unittest.cc @@ -0,0 +1,112 @@ + +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/video/i444_buffer.h" + +#include "api/video/i420_buffer.h" +#include "test/frame_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +int GetY(rtc::scoped_refptr buf, int col, int row) { + return buf->DataY()[row * buf->StrideY() + col]; +} + +int GetU(rtc::scoped_refptr buf, int col, int row) { + return buf->DataU()[row * buf->StrideU() + col]; +} + +int GetV(rtc::scoped_refptr buf, int col, int row) { + return buf->DataV()[row * buf->StrideV() + col]; +} + +void FillI444Buffer(rtc::scoped_refptr buf) { + const uint8_t Y = 1; + const uint8_t U = 2; + const uint8_t V = 3; + for (int row = 0; row < buf->height(); ++row) { + for (int col = 0; col < buf->width(); ++col) { + buf->MutableDataY()[row * buf->StrideY() + col] = Y; + buf->MutableDataU()[row * buf->StrideU() + col] = U; + buf->MutableDataV()[row * buf->StrideV() + col] = V; + } + } +} + +} // namespace + +TEST(I444BufferTest, InitialData) { + constexpr int stride = 3; + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + EXPECT_EQ(width, i444_buffer->width()); + EXPECT_EQ(height, i444_buffer->height()); + EXPECT_EQ(stride, i444_buffer->StrideY()); + EXPECT_EQ(stride, i444_buffer->StrideU()); + EXPECT_EQ(stride, i444_buffer->StrideV()); + EXPECT_EQ(3, i444_buffer->ChromaWidth()); + EXPECT_EQ(3, i444_buffer->ChromaHeight()); +} + +TEST(I444BufferTest, ReadPixels) { + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + // Y = 1, U = 2, V = 3. + FillI444Buffer(i444_buffer); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(1, GetY(i444_buffer, col, row)); + EXPECT_EQ(2, GetU(i444_buffer, col, row)); + EXPECT_EQ(3, GetV(i444_buffer, col, row)); + } + } +} + +TEST(I444BufferTest, ToI420) { + constexpr int width = 3; + constexpr int height = 3; + constexpr int size_y = width * height; + constexpr int size_u = (width + 1) / 2 * (height + 1) / 2; + constexpr int size_v = (width + 1) / 2 * (height + 1) / 2; + rtc::scoped_refptr reference(I420Buffer::Create(width, height)); + memset(reference->MutableDataY(), 8, size_y); + memset(reference->MutableDataU(), 4, size_u); + memset(reference->MutableDataV(), 2, size_v); + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + // Convert the reference buffer to I444. + memset(i444_buffer->MutableDataY(), 8, size_y); + memset(i444_buffer->MutableDataU(), 4, size_y); + memset(i444_buffer->MutableDataV(), 2, size_y); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(8, GetY(i444_buffer, col, row)); + EXPECT_EQ(4, GetU(i444_buffer, col, row)); + EXPECT_EQ(2, GetV(i444_buffer, col, row)); + } + } + + rtc::scoped_refptr i420_buffer(i444_buffer->ToI420()); + EXPECT_EQ(height, i420_buffer->height()); + EXPECT_EQ(width, i420_buffer->width()); + EXPECT_TRUE(test::FrameBufsEqual(reference, i420_buffer)); +} + +} // namespace webrtc diff --git a/api/video/video_frame_buffer.cc b/api/video/video_frame_buffer.cc index 7085010325..6c46f782a0 100644 --- a/api/video/video_frame_buffer.cc +++ b/api/video/video_frame_buffer.cc @@ -11,6 +11,7 @@ #include "api/video/video_frame_buffer.h" #include "api/video/i420_buffer.h" +#include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/checks.h" @@ -81,7 +82,7 @@ const char* VideoFrameBufferTypeToString(VideoFrameBuffer::Type type) { case VideoFrameBuffer::Type::kNV12: return "kNV12"; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -94,7 +95,7 @@ int I420BufferInterface::ChromaHeight() const { } rtc::scoped_refptr I420BufferInterface::ToI420() { - return this; + return rtc::scoped_refptr(this); } const I420BufferInterface* I420BufferInterface::GetI420() const { @@ -117,6 +118,19 @@ int I444BufferInterface::ChromaHeight() const { return height(); } +rtc::scoped_refptr I444BufferInterface::CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + rtc::scoped_refptr result = + I444Buffer::Create(scaled_width, scaled_height); + result->CropAndScaleFrom(*this, offset_x, offset_y, crop_width, crop_height); + return result; +} + VideoFrameBuffer::Type I010BufferInterface::type() const { return Type::kI010; } diff --git a/api/video/video_frame_buffer.h b/api/video/video_frame_buffer.h index 3e12c75a45..6098a48117 100644 --- a/api/video/video_frame_buffer.h +++ b/api/video/video_frame_buffer.h @@ -69,6 +69,8 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface { // in another format, a conversion will take place. All implementations must // provide a fallback to I420 for compatibility with e.g. the internal WebRTC // software encoders. + // Conversion may fail, for example if reading the pixel data from a texture + // fails. If the conversion fails, nullptr is returned. virtual rtc::scoped_refptr ToI420() = 0; // GetI420() methods should return I420 buffer if conversion is trivial, i.e @@ -182,6 +184,13 @@ class I444BufferInterface : public PlanarYuv8Buffer { int ChromaWidth() const final; int ChromaHeight() const final; + rtc::scoped_refptr CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + protected: ~I444BufferInterface() override {} }; diff --git a/api/video/video_sink_interface.h b/api/video/video_sink_interface.h index 88cf9d924f..9c1f5f3214 100644 --- a/api/video/video_sink_interface.h +++ b/api/video/video_sink_interface.h @@ -11,6 +11,8 @@ #ifndef API_VIDEO_VIDEO_SINK_INTERFACE_H_ #define API_VIDEO_VIDEO_SINK_INTERFACE_H_ +#include "absl/types/optional.h" +#include "api/video_track_source_constraints.h" #include "rtc_base/checks.h" namespace rtc { @@ -25,6 +27,11 @@ class VideoSinkInterface { // Should be called by the source when it discards the frame due to rate // limiting. virtual void OnDiscardedFrame() {} + + // Called on the network thread when video constraints change. + // TODO(crbug/1255737): make pure virtual once downstream project adapts. + virtual void OnConstraintsChanged( + const webrtc::VideoTrackSourceConstraints& constraints) {} }; } // namespace rtc diff --git a/api/video/video_source_interface.h b/api/video/video_source_interface.h index d66a235da0..5eb4ebfd75 100644 --- a/api/video/video_source_interface.h +++ b/api/video/video_source_interface.h @@ -97,6 +97,10 @@ class VideoSourceInterface { // RemoveSink must guarantee that at the time the method returns, // there is no current and no future calls to VideoSinkInterface::OnFrame. virtual void RemoveSink(VideoSinkInterface* sink) = 0; + + // Request underlying source to capture a new frame. + // TODO(crbug/1255737): make pure virtual once downstream projects adapt. + virtual void RequestRefreshFrame() {} }; } // namespace rtc diff --git a/api/video/video_stream_encoder_interface.h b/api/video/video_stream_encoder_interface.h index 69d0ad2363..f2d7e131e6 100644 --- a/api/video/video_stream_encoder_interface.h +++ b/api/video/video_stream_encoder_interface.h @@ -39,7 +39,7 @@ namespace webrtc { // // 2. Moving responsibility for simulcast and for software fallback into this // class. -class VideoStreamEncoderInterface : public rtc::VideoSinkInterface { +class VideoStreamEncoderInterface { public: // Interface for receiving encoded video frames and notifications about // configuration changes. @@ -58,6 +58,8 @@ class VideoStreamEncoderInterface : public rtc::VideoSinkInterface { VideoLayersAllocation allocation) = 0; }; + virtual ~VideoStreamEncoderInterface() = default; + // If the resource is overusing, the VideoStreamEncoder will try to reduce // resolution or frame rate until no resource is overusing. // TODO(https://crbug.com/webrtc/11565): When the ResourceAdaptationProcessor diff --git a/api/video/video_stream_encoder_settings.h b/api/video/video_stream_encoder_settings.h index 743524b352..3aee5b7050 100644 --- a/api/video/video_stream_encoder_settings.h +++ b/api/video/video_stream_encoder_settings.h @@ -23,19 +23,13 @@ class EncoderSwitchRequestCallback { public: virtual ~EncoderSwitchRequestCallback() {} - struct Config { - std::string codec_name; - absl::optional param; - absl::optional value; - }; - - // Requests that encoder fallback is performed. + // Requests switch to next negotiated encoder. virtual void RequestEncoderFallback() = 0; - // Requests that a switch to a specific encoder is performed. - virtual void RequestEncoderSwitch(const Config& conf) = 0; - - virtual void RequestEncoderSwitch(const SdpVideoFormat& format) = 0; + // Requests switch to a specific encoder. If the encoder is not available and + // `allow_default_fallback` is `true` the default fallback is invoked. + virtual void RequestEncoderSwitch(const SdpVideoFormat& format, + bool allow_default_fallback) = 0; }; struct VideoStreamEncoderSettings { diff --git a/api/video/video_timing.cc b/api/video/video_timing.cc index df1bc4857a..0483c20e66 100644 --- a/api/video/video_timing.cc +++ b/api/video/video_timing.cc @@ -11,6 +11,7 @@ #include "api/video/video_timing.h" #include "api/array_view.h" +#include "api/units/time_delta.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/strings/string_builder.h" @@ -25,6 +26,14 @@ uint16_t VideoSendTiming::GetDeltaCappedMs(int64_t base_ms, int64_t time_ms) { return rtc::saturated_cast(time_ms - base_ms); } +uint16_t VideoSendTiming::GetDeltaCappedMs(TimeDelta delta) { + if (delta < TimeDelta::Zero()) { + RTC_DLOG(LS_ERROR) << "Delta " << delta.ms() + << "ms expected to be positive"; + } + return rtc::saturated_cast(delta.ms()); +} + TimingFrameInfo::TimingFrameInfo() : rtp_timestamp(0), capture_time_ms(-1), diff --git a/api/video/video_timing.h b/api/video/video_timing.h index dd8febb3db..698477a81a 100644 --- a/api/video/video_timing.h +++ b/api/video/video_timing.h @@ -16,6 +16,8 @@ #include #include +#include "api/units/time_delta.h" + namespace webrtc { // Video timing timestamps in ms counted from capture_time_ms of a frame. @@ -34,6 +36,7 @@ struct VideoSendTiming { // https://webrtc.org/experiments/rtp-hdrext/video-timing/ extension stores // 16-bit deltas of timestamps from packet capture time. static uint16_t GetDeltaCappedMs(int64_t base_ms, int64_t time_ms); + static uint16_t GetDeltaCappedMs(TimeDelta delta); uint16_t encode_start_delta_ms; uint16_t encode_finish_delta_ms; diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn index cab0f7a049..198c7f44aa 100644 --- a/api/video_codecs/BUILD.gn +++ b/api/video_codecs/BUILD.gn @@ -112,6 +112,50 @@ rtc_library("builtin_video_encoder_factory") { absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } +rtc_source_set("video_encoder_factory_template") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template.h" ] + + deps = [ ":video_codecs_api" ] + absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ] +} + +rtc_source_set("video_encoder_factory_template_libvpx_vp8_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libvpx_vp8_adapter.h" ] + + deps = [ "../../modules/video_coding:webrtc_vp8" ] +} + +rtc_source_set("video_encoder_factory_template_libvpx_vp9_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libvpx_vp9_adapter.h" ] + + deps = [ "../../modules/video_coding:webrtc_vp9" ] +} + +rtc_source_set("video_encoder_factory_template_open_h264_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_open_h264_adapter.h" ] + + deps = [ "../../modules/video_coding:webrtc_h264" ] +} + +rtc_source_set("video_encoder_factory_template_libaom_av1_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libaom_av1_adapter.h" ] + + deps = [ + "../../modules/video_coding/codecs/av1:libaom_av1_encoder", + "../../modules/video_coding/svc:scalability_structures", + ] +} + rtc_library("vp8_temporal_layers_factory") { visibility = [ "*" ] allow_poison = [ "software_video_codecs" ] diff --git a/api/video_codecs/builtin_video_encoder_factory.cc b/api/video_codecs/builtin_video_encoder_factory.cc index 9463a9cdf2..78e5f8b8be 100644 --- a/api/video_codecs/builtin_video_encoder_factory.cc +++ b/api/video_codecs/builtin_video_encoder_factory.cc @@ -32,15 +32,6 @@ class BuiltinVideoEncoderFactory : public VideoEncoderFactory { BuiltinVideoEncoderFactory() : internal_encoder_factory_(new InternalEncoderFactory()) {} - VideoEncoderFactory::CodecInfo QueryVideoEncoder( - const SdpVideoFormat& format) const override { - // Format must be one of the internal formats. - RTC_DCHECK( - format.IsCodecInList(internal_encoder_factory_->GetSupportedFormats())); - VideoEncoderFactory::CodecInfo info; - return info; - } - std::unique_ptr CreateVideoEncoder( const SdpVideoFormat& format) override { // Try creating internal encoder. diff --git a/api/video_codecs/h264_profile_level_id.cc b/api/video_codecs/h264_profile_level_id.cc index fa47758189..02b43ba4f2 100644 --- a/api/video_codecs/h264_profile_level_id.cc +++ b/api/video_codecs/h264_profile_level_id.cc @@ -68,7 +68,8 @@ constexpr ProfilePattern kProfilePatterns[] = { {0x58, BitPattern("10xx0000"), H264Profile::kProfileBaseline}, {0x4D, BitPattern("0x0x0000"), H264Profile::kProfileMain}, {0x64, BitPattern("00000000"), H264Profile::kProfileHigh}, - {0x64, BitPattern("00001100"), H264Profile::kProfileConstrainedHigh}}; + {0x64, BitPattern("00001100"), H264Profile::kProfileConstrainedHigh}, + {0xF4, BitPattern("00000000"), H264Profile::kProfilePredictiveHigh444}}; struct LevelConstraint { const int max_macroblocks_per_second; @@ -228,6 +229,9 @@ absl::optional H264ProfileLevelIdToString( case H264Profile::kProfileHigh: profile_idc_iop_string = "6400"; break; + case H264Profile::kProfilePredictiveHigh444: + profile_idc_iop_string = "f400"; + break; // Unrecognized profile. default: return absl::nullopt; diff --git a/api/video_codecs/h264_profile_level_id.h b/api/video_codecs/h264_profile_level_id.h index 51d025cd7b..4b46ad329d 100644 --- a/api/video_codecs/h264_profile_level_id.h +++ b/api/video_codecs/h264_profile_level_id.h @@ -25,6 +25,7 @@ enum class H264Profile { kProfileMain, kProfileConstrainedHigh, kProfileHigh, + kProfilePredictiveHigh444, }; // All values are equal to ten times the level number, except level 1b which is diff --git a/api/video_codecs/test/BUILD.gn b/api/video_codecs/test/BUILD.gn index 14b54a1f99..3bee6b17ce 100644 --- a/api/video_codecs/test/BUILD.gn +++ b/api/video_codecs/test/BUILD.gn @@ -20,6 +20,7 @@ if (rtc_include_tests) { ] deps = [ + ":video_encoder_factory_template_tests", "..:builtin_video_encoder_factory", "..:rtc_software_fallback_wrappers", "..:video_codecs_api", @@ -43,4 +44,20 @@ if (rtc_include_tests) { ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } + + rtc_library("video_encoder_factory_template_tests") { + testonly = true + sources = [ "video_encoder_factory_template_tests.cc" ] + + deps = [ + "..:video_encoder_factory_template", + "..:video_encoder_factory_template_libaom_av1_adapter", + "..:video_encoder_factory_template_libvpx_vp8_adapter", + "..:video_encoder_factory_template_libvpx_vp9_adapter", + "..:video_encoder_factory_template_open_h264_adapter", + "../../:mock_video_encoder", + "../../../test:test_support", + "//testing/gtest", + ] + } } diff --git a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc index 3d0e56e5fc..73dedc8395 100644 --- a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc +++ b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc @@ -179,13 +179,13 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, int32_t Decoded(VideoFrame& decodedImage) override { return 0; } int32_t Decoded(webrtc::VideoFrame& decodedImage, int64_t decode_time_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } void Decoded(webrtc::VideoFrame& decodedImage, absl::optional decode_time_ms, absl::optional qp) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } callback; diff --git a/api/video_codecs/test/video_encoder_factory_template_tests.cc b/api/video_codecs/test/video_encoder_factory_template_tests.cc new file mode 100644 index 0000000000..e54b46c09f --- /dev/null +++ b/api/video_codecs/test/video_encoder_factory_template_tests.cc @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/test/mock_video_encoder.h" +#include "api/video_codecs/video_encoder_factory_template.h" +#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::Each; +using ::testing::Eq; +using ::testing::Field; +using ::testing::IsEmpty; +using ::testing::Ne; +using ::testing::Not; +using ::testing::UnorderedElementsAre; + +namespace webrtc { +namespace { +using CodecSupport = VideoEncoderFactory::CodecSupport; +const SdpVideoFormat kFooSdp("Foo"); +const SdpVideoFormat kBarLowSdp("Bar", {{"profile", "low"}}); +const SdpVideoFormat kBarHighSdp("Bar", {{"profile", "high"}}); + +struct FooEncoderTemplateAdapter { + static std::vector SupportedFormats() { return {kFooSdp}; } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + static bool IsScalabilityModeSupported( + const absl::string_view scalability_mode) { + return scalability_mode == "L1T2" || scalability_mode == "L1T3"; + } +}; + +struct BarEncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {kBarLowSdp, kBarHighSdp}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + static bool IsScalabilityModeSupported( + const absl::string_view scalability_mode) { + return scalability_mode == "L1T2" || scalability_mode == "L1T3" || + scalability_mode == "S2T2" || scalability_mode == "S2T3"; + } +}; + +TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCreateEncoder) { + VideoEncoderFactoryTemplate factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), Eq(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCodecSupport) { + VideoEncoderFactoryTemplate factory; + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S2T3"), + Field(&CodecSupport::is_supported, false)); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("FooX"), absl::nullopt), + Field(&CodecSupport::is_supported, false)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersNoDuplicates) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCreateEncoders) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.GetSupportedFormats(), + UnorderedElementsAre(kFooSdp, kBarLowSdp, kBarHighSdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(kBarLowSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(kBarHighSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), Eq(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("Bar")), Eq(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCodecSupport) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S2T3"), + Field(&CodecSupport::is_supported, false)); + EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, "S2T2"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, "S3T2"), + Field(&CodecSupport::is_supported, false)); +} + +TEST(VideoEncoderFactoryTemplate, LibvpxVp8) { + VideoEncoderFactoryTemplate factory; + const SdpVideoFormat kVp8Sdp("VP8"); + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kVp8Sdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kVp8Sdp), Ne(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, LibvpxVp9) { + VideoEncoderFactoryTemplate factory; + auto formats = factory.GetSupportedFormats(); + EXPECT_THAT(formats, Not(IsEmpty())); + EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "VP9"))); + EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), Ne(nullptr)); +} + +// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build +// target remove this #ifdef. +#if defined(WEBRTC_USE_H264) +TEST(VideoEncoderFactoryTemplate, OpenH264) { + VideoEncoderFactoryTemplate factory; + auto formats = factory.GetSupportedFormats(); + EXPECT_THAT(formats, Not(IsEmpty())); + EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "H264"))); + EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), Ne(nullptr)); +} +#endif // defined(WEBRTC_USE_H264) + +TEST(VideoEncoderFactoryTemplate, LibaomAv1) { + VideoEncoderFactoryTemplate factory; + const SdpVideoFormat kAv1Sdp("AV1"); + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kAv1Sdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kAv1Sdp), Ne(nullptr)); +} + +} // namespace +} // namespace webrtc diff --git a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc index a63a7c27fd..2150a767a3 100644 --- a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc +++ b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc @@ -71,13 +71,6 @@ VideoEncoder::EncoderInfo GetEncoderInfoWithHardwareAccelerated( return info; } -VideoEncoder::EncoderInfo GetEncoderInfoWithInternalSource( - bool internal_source) { - VideoEncoder::EncoderInfo info; - info.has_internal_source = internal_source; - return info; -} - class FakeEncodedImageCallback : public EncodedImageCallback { public: Result OnEncodedImage(const EncodedImage& encoded_image, @@ -803,35 +796,6 @@ TEST(SoftwareFallbackEncoderTest, ReportsHardwareAccelerated) { EXPECT_FALSE(wrapper->GetEncoderInfo().is_hardware_accelerated); } -TEST(SoftwareFallbackEncoderTest, ReportsInternalSource) { - auto* sw_encoder = new ::testing::NiceMock(); - auto* hw_encoder = new ::testing::NiceMock(); - EXPECT_CALL(*sw_encoder, GetEncoderInfo()) - .WillRepeatedly(Return(GetEncoderInfoWithInternalSource(false))); - EXPECT_CALL(*hw_encoder, GetEncoderInfo()) - .WillRepeatedly(Return(GetEncoderInfoWithInternalSource(true))); - - std::unique_ptr wrapper = - CreateVideoEncoderSoftwareFallbackWrapper( - std::unique_ptr(sw_encoder), - std::unique_ptr(hw_encoder)); - EXPECT_TRUE(wrapper->GetEncoderInfo().has_internal_source); - - VideoCodec codec_ = {}; - codec_.width = 100; - codec_.height = 100; - wrapper->InitEncode(&codec_, kSettings); - - // Trigger fallback to software. - EXPECT_CALL(*hw_encoder, Encode) - .WillOnce(Return(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)); - VideoFrame frame = VideoFrame::Builder() - .set_video_frame_buffer(I420Buffer::Create(100, 100)) - .build(); - wrapper->Encode(frame, nullptr); - EXPECT_FALSE(wrapper->GetEncoderInfo().has_internal_source); -} - class PreferTemporalLayersFallbackTest : public ::testing::Test { public: PreferTemporalLayersFallbackTest() {} @@ -982,7 +946,7 @@ TEST_F(PreferTemporalLayersFallbackTest, PrimesEncoderOnSwitch) { wrapper_->RegisterEncodeCompleteCallback(&callback1); EXPECT_CALL(*hw_, SetFecControllerOverride(&fec_controller_override1)); - EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0); + EXPECT_CALL(*sw_, SetFecControllerOverride).Times(1); wrapper_->SetFecControllerOverride(&fec_controller_override1); EXPECT_CALL(*hw_, SetRates(rate_params1)); @@ -1007,7 +971,7 @@ TEST_F(PreferTemporalLayersFallbackTest, PrimesEncoderOnSwitch) { EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback(&callback1)); EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback).Times(0); - EXPECT_CALL(*sw_, SetFecControllerOverride(&fec_controller_override1)); + EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0); EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0); // Rate control parameters are cleared on InitEncode. @@ -1041,7 +1005,7 @@ TEST_F(PreferTemporalLayersFallbackTest, PrimesEncoderOnSwitch) { wrapper_->RegisterEncodeCompleteCallback(&callback2); EXPECT_CALL(*sw_, SetFecControllerOverride(&fec_controller_override2)); - EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0); + EXPECT_CALL(*hw_, SetFecControllerOverride).Times(1); wrapper_->SetFecControllerOverride(&fec_controller_override2); EXPECT_CALL(*sw_, SetRates(rate_params2)); @@ -1066,7 +1030,7 @@ TEST_F(PreferTemporalLayersFallbackTest, PrimesEncoderOnSwitch) { EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback(&callback2)); EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback).Times(0); - EXPECT_CALL(*hw_, SetFecControllerOverride(&fec_controller_override2)); + EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0); EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0); // Rate control parameters are cleared on InitEncode. diff --git a/api/video_codecs/video_codec.cc b/api/video_codecs/video_codec.cc index d05eb456fc..75c89e6985 100644 --- a/api/video_codecs/video_codec.cc +++ b/api/video_codecs/video_codec.cc @@ -21,9 +21,10 @@ namespace webrtc { namespace { constexpr char kPayloadNameVp8[] = "VP8"; constexpr char kPayloadNameVp9[] = "VP9"; -// TODO(bugs.webrtc.org/11042): Rename to AV1 when rtp payload format for av1 is -// frozen. -constexpr char kPayloadNameAv1[] = "AV1X"; +constexpr char kPayloadNameAv1[] = "AV1"; +// TODO(bugs.webrtc.org/13166): Remove AV1X when backwards compatibility is not +// needed. +constexpr char kPayloadNameAv1x[] = "AV1X"; constexpr char kPayloadNameH264[] = "H264"; constexpr char kPayloadNameGeneric[] = "Generic"; constexpr char kPayloadNameMultiplex[] = "Multiplex"; @@ -128,7 +129,8 @@ VideoCodecType PayloadStringToCodecType(const std::string& name) { return kVideoCodecVP8; if (absl::EqualsIgnoreCase(name, kPayloadNameVp9)) return kVideoCodecVP9; - if (absl::EqualsIgnoreCase(name, kPayloadNameAv1)) + if (absl::EqualsIgnoreCase(name, kPayloadNameAv1) || + absl::EqualsIgnoreCase(name, kPayloadNameAv1x)) return kVideoCodecAV1; if (absl::EqualsIgnoreCase(name, kPayloadNameH264)) return kVideoCodecH264; @@ -137,4 +139,23 @@ VideoCodecType PayloadStringToCodecType(const std::string& name) { return kVideoCodecGeneric; } +VideoCodecComplexity VideoCodec::GetVideoEncoderComplexity() const { + if (complexity_.has_value()) { + return complexity_.value(); + } + switch (codecType) { + case kVideoCodecVP8: + return VP8().complexity; + case kVideoCodecVP9: + return VP9().complexity; + default: + return VideoCodecComplexity::kComplexityNormal; + } +} + +void VideoCodec::SetVideoEncoderComplexity( + VideoCodecComplexity complexity_setting) { + complexity_ = complexity_setting; +} + } // namespace webrtc diff --git a/api/video_codecs/video_codec.h b/api/video_codecs/video_codec.h index 1e6e21c0ec..f00176d3f9 100644 --- a/api/video_codecs/video_codec.h +++ b/api/video_codecs/video_codec.h @@ -109,6 +109,9 @@ class RTC_EXPORT VideoCodec { scalability_mode_ = std::string(scalability_mode); } + VideoCodecComplexity GetVideoEncoderComplexity() const; + void SetVideoEncoderComplexity(VideoCodecComplexity complexity_setting); + // Public variables. TODO(hta): Make them private with accessors. VideoCodecType codecType; @@ -169,6 +172,9 @@ class RTC_EXPORT VideoCodec { // This will allow removing the VideoCodec* types from this file. VideoCodecUnion codec_specific_; std::string scalability_mode_; + // 'complexity_' indicates the CPU capability of the client. It's used to + // determine encoder CPU complexity (e.g., cpu_used for VP8, VP9. and AV1). + absl::optional complexity_; }; } // namespace webrtc diff --git a/api/video_codecs/video_decoder_software_fallback_wrapper.cc b/api/video_codecs/video_decoder_software_fallback_wrapper.cc index 9d583881f5..6484616b81 100644 --- a/api/video_codecs/video_decoder_software_fallback_wrapper.cc +++ b/api/video_codecs/video_decoder_software_fallback_wrapper.cc @@ -16,7 +16,6 @@ #include #include -#include "absl/base/macros.h" #include "api/video/encoded_image.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/include/video_error_codes.h" @@ -209,13 +208,13 @@ int32_t VideoDecoderSoftwareFallbackWrapper::Decode( } // Fallback decoder initialized, fall-through. - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; } case DecoderType::kFallback: return fallback_decoder_->Decode(input_image, missing_frames, render_time_ms); default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return WEBRTC_VIDEO_CODEC_ERROR; } } @@ -240,7 +239,7 @@ int32_t VideoDecoderSoftwareFallbackWrapper::Release() { status = WEBRTC_VIDEO_CODEC_OK; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); status = WEBRTC_VIDEO_CODEC_ERROR; } diff --git a/api/video_codecs/video_encoder.cc b/api/video_codecs/video_encoder.cc index ac5d84b50a..83c329152e 100644 --- a/api/video_codecs/video_encoder.cc +++ b/api/video_codecs/video_encoder.cc @@ -99,7 +99,6 @@ VideoEncoder::EncoderInfo::EncoderInfo() implementation_name("unknown"), has_trusted_rate_controller(false), is_hardware_accelerated(true), - has_internal_source(false), fps_allocation{absl::InlinedVector( 1, kMaxFramerateFraction)}, @@ -133,7 +132,6 @@ std::string VideoEncoder::EncoderInfo::ToString() const { ", has_trusted_rate_controller = " << has_trusted_rate_controller << ", is_hardware_accelerated = " << is_hardware_accelerated - << ", has_internal_source = " << has_internal_source << ", fps_allocation = ["; size_t num_spatial_layer_with_fps_allocation = 0; for (size_t i = 0; i < kMaxSpatialLayers; ++i) { @@ -214,8 +212,7 @@ bool VideoEncoder::EncoderInfo::operator==(const EncoderInfo& rhs) const { if (supports_native_handle != rhs.supports_native_handle || implementation_name != rhs.implementation_name || has_trusted_rate_controller != rhs.has_trusted_rate_controller || - is_hardware_accelerated != rhs.is_hardware_accelerated || - has_internal_source != rhs.has_internal_source) { + is_hardware_accelerated != rhs.is_hardware_accelerated) { return false; } diff --git a/api/video_codecs/video_encoder.h b/api/video_codecs/video_encoder.h index 3035dd7209..94d7287f78 100644 --- a/api/video_codecs/video_encoder.h +++ b/api/video_codecs/video_encoder.h @@ -207,13 +207,6 @@ class RTC_EXPORT VideoEncoder { // thresholds will be used in CPU adaptation. bool is_hardware_accelerated; - // If this field is true, the encoder uses internal camera sources, meaning - // that it does not require/expect frames to be delivered via - // webrtc::VideoEncoder::Encode. - // Internal source encoders are deprecated and support for them will be - // phased out. - bool has_internal_source; - // For each spatial layer (simulcast stream or SVC layer), represented as an // element in `fps_allocation` a vector indicates how many temporal layers // the encoder is using for that spatial layer. diff --git a/api/video_codecs/video_encoder_config.cc b/api/video_codecs/video_encoder_config.cc index 0321da24da..206a1d3984 100644 --- a/api/video_codecs/video_encoder_config.cc +++ b/api/video_codecs/video_encoder_config.cc @@ -97,23 +97,24 @@ void VideoEncoderConfig::EncoderSpecificSettings::FillEncoderSpecificSettings( } else if (codec->codecType == kVideoCodecVP9) { FillVideoCodecVp9(codec->VP9()); } else { - RTC_NOTREACHED() << "Encoder specifics set/used for unknown codec type."; + RTC_DCHECK_NOTREACHED() + << "Encoder specifics set/used for unknown codec type."; } } void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecH264( VideoCodecH264* h264_settings) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecVp8( VideoCodecVP8* vp8_settings) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecVp9( VideoCodecVP9* vp9_settings) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } VideoEncoderConfig::H264EncoderSpecificSettings::H264EncoderSpecificSettings( diff --git a/api/video_codecs/video_encoder_factory.h b/api/video_codecs/video_encoder_factory.h index 2768079130..d7cea47909 100644 --- a/api/video_codecs/video_encoder_factory.h +++ b/api/video_codecs/video_encoder_factory.h @@ -27,16 +27,6 @@ class VideoEncoder; // NOTE: This class is still under development and may change without notice. class VideoEncoderFactory { public: - // TODO(magjed): Try to get rid of this struct. - struct CodecInfo { - // `has_internal_source` is true if encoders created by this factory of the - // given codec will use internal camera sources, meaning that they don't - // require/expect frames to be delivered via webrtc::VideoEncoder::Encode. - // This flag is used as the internal_source parameter to - // webrtc::ViEExternalCodec::RegisterExternalSendCodec. - bool has_internal_source = false; - }; - struct CodecSupport { bool is_supported = false; bool is_power_efficient = false; @@ -74,16 +64,6 @@ class VideoEncoderFactory { return GetSupportedFormats(); } - // Returns information about how this format will be encoded. The specified - // format must be one of the supported formats by this factory. - - // TODO(magjed): Try to get rid of this method. Since is_hardware_accelerated - // is unused, only factories producing internal source encoders (in itself a - // deprecated feature) needs to override this method. - virtual CodecInfo QueryVideoEncoder(const SdpVideoFormat& format) const { - return CodecInfo(); - } - // Query whether the specifed format is supported or not and if it will be // power efficient, which is currently interpreted as if there is support for // hardware acceleration. diff --git a/api/video_codecs/video_encoder_factory_template.h b/api/video_codecs/video_encoder_factory_template.h new file mode 100644 index 0000000000..f1581c5620 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ + +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_encoder_factory.h" + +namespace webrtc { +// The VideoEncoderFactoryTemplate supports encoders implementations given as +// template arguments. +// +// To include an encoder in the factory it requires three static members +// functions to be defined: +// +// // Returns the supported SdpVideoFormats this encoder can produce. +// static std::vector SupportedFormats(); +// +// // Creates an encoder instance for the given format. +// static std::unique_ptr +// CreateEncoder(const SdpVideoFormat& format); +// +// // Returns true if the encoder supports the given scalability mode. +// static bool +// IsScalabilityModeSupported(const absl::string_view scalability_mode); +// +// Note that the order of the template arguments matter as the factory will +// query/return the first encoder implementation supporting the given +// SdpVideoFormat. +template +class VideoEncoderFactoryTemplate : public VideoEncoderFactory { + public: + std::vector GetSupportedFormats() const override { + std::vector formats; + GetSupportedFormatsInternal(formats); + return formats; + } + + std::unique_ptr CreateVideoEncoder( + const SdpVideoFormat& format) override { + return CreateVideoEncoderInternal(format); + } + + CodecSupport QueryCodecSupport( + const SdpVideoFormat& format, + absl::optional scalability_mode) const override { + return QueryCodecSupportInternal(format, scalability_mode); + } + + private: + template + bool IsFormatSupported(const SdpVideoFormat& format) const { + return absl::c_count(V::SupportedFormats(), format) > 0; + } + + template + void GetSupportedFormatsInternal(std::vector& formats) const { + auto supported_formats = V::SupportedFormats(); + for (const auto& format : supported_formats) { + if (absl::c_count(formats, format) == 0) { + formats.push_back(format); + } + } + + if constexpr (sizeof...(Vs) > 0) { + return GetSupportedFormatsInternal(formats); + } + } + + template + std::unique_ptr CreateVideoEncoderInternal( + const SdpVideoFormat& format) { + if (IsFormatSupported(format)) { + return V::CreateEncoder(format); + } + + if constexpr (sizeof...(Vs) > 0) { + return CreateVideoEncoderInternal(format); + } + + return nullptr; + } + + template + CodecSupport QueryCodecSupportInternal( + const SdpVideoFormat& format, + const absl::optional& scalability_mode) const { + if (IsFormatSupported(format)) { + return {.is_supported = !scalability_mode || + V::IsScalabilityModeSupported(*scalability_mode)}; + } + + if constexpr (sizeof...(Vs) > 0) { + return QueryCodecSupportInternal(format, scalability_mode); + } + + return {.is_supported = false}; + } +}; + +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h b/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h new file mode 100644 index 0000000000..dcbdc82b86 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" +#include "modules/video_coding/svc/create_scalability_structure.h" + +namespace webrtc { +struct LibaomAv1EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {SdpVideoFormat("AV1")}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return CreateLibaomAv1Encoder(); + } + + static bool IsScalabilityModeSupported(absl::string_view scalability_mode) { + // For libaom AV1, the scalability mode is supported if we can create the + // scalability structure. + return ScalabilityStructureConfig(scalability_mode) != absl::nullopt; + } +}; + +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h b/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h new file mode 100644 index 0000000000..935a87a216 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/vp8/include/vp8.h" + +namespace webrtc { +struct LibvpxVp8EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {SdpVideoFormat("VP8")}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return VP8Encoder::Create(); + } + + static bool IsScalabilityModeSupported( + const absl::string_view scalability_mode) { + return VP8Encoder::SupportsScalabilityMode(scalability_mode); + } +}; +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h b/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h new file mode 100644 index 0000000000..e203b079c6 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/vp9/include/vp9.h" + +namespace webrtc { +struct LibvpxVp9EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return SupportedVP9Codecs(); + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return VP9Encoder::Create(); + } + + static bool IsScalabilityModeSupported( + const absl::string_view scalability_mode) { + return VP9Encoder::SupportsScalabilityMode(scalability_mode); + } +}; +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h b/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h new file mode 100644 index 0000000000..b127009f6e --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/h264/include/h264.h" + +namespace webrtc { +// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build +// target remove this #ifdef. +#if defined(WEBRTC_USE_H264) +struct OpenH264EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return SupportedH264Codecs(); + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return H264Encoder::Create(cricket::VideoCodec(format)); + } + + static bool IsScalabilityModeSupported( + const absl::string_view scalability_mode) { + return H264Encoder::SupportsScalabilityMode(scalability_mode); + } +}; +#endif // defined(WEBRTC_USE_H264) +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_software_fallback_wrapper.cc b/api/video_codecs/video_encoder_software_fallback_wrapper.cc index e95c088811..39c52a0081 100644 --- a/api/video_codecs/video_encoder_software_fallback_wrapper.cc +++ b/api/video_codecs/video_encoder_software_fallback_wrapper.cc @@ -155,7 +155,7 @@ class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder { RTC_LOG(LS_WARNING) << "Trying to access encoder in uninitialized fallback wrapper."; // Return main encoder to preserve previous behavior. - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; case EncoderState::kMainEncoderUsed: return encoder_.get(); case EncoderState::kFallbackDueToFailure: @@ -180,7 +180,6 @@ class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder { // The last channel parameters set. absl::optional packet_loss_; absl::optional rtt_; - FecControllerOverride* fec_controller_override_; absl::optional loss_notification_; enum class EncoderState { @@ -205,8 +204,7 @@ VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper( std::unique_ptr sw_encoder, std::unique_ptr hw_encoder, bool prefer_temporal_support) - : fec_controller_override_(nullptr), - encoder_state_(EncoderState::kUninitialized), + : encoder_state_(EncoderState::kUninitialized), encoder_(std::move(hw_encoder)), fallback_encoder_(std::move(sw_encoder)), callback_(nullptr), @@ -234,9 +232,7 @@ void VideoEncoderSoftwareFallbackWrapper::PrimeEncoder( if (packet_loss_.has_value()) { encoder->OnPacketLossRateUpdate(packet_loss_.value()); } - if (fec_controller_override_) { - encoder->SetFecControllerOverride(fec_controller_override_); - } + if (loss_notification_.has_value()) { encoder->OnLossNotification(loss_notification_.value()); } @@ -277,8 +273,8 @@ void VideoEncoderSoftwareFallbackWrapper::SetFecControllerOverride( // `fec_controller_override` at a given time. This is the responsibility // of `this` to maintain. - fec_controller_override_ = fec_controller_override; - current_encoder()->SetFecControllerOverride(fec_controller_override); + encoder_->SetFecControllerOverride(fec_controller_override); + fallback_encoder_->SetFecControllerOverride(fec_controller_override); } int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode( @@ -362,8 +358,8 @@ int32_t VideoEncoderSoftwareFallbackWrapper::EncodeWithMainEncoder( fallback_encoder_->GetEncoderInfo().supports_native_handle) { return fallback_encoder_->Encode(frame, frame_types); } else { - RTC_LOG(INFO) << "Fallback encoder does not support native handle - " - "converting frame to I420"; + RTC_LOG(LS_INFO) << "Fallback encoder does not support native handle - " + "converting frame to I420"; rtc::scoped_refptr src_buffer = frame.video_frame_buffer()->ToI420(); if (!src_buffer) { diff --git a/api/video_codecs/vp8_frame_config.cc b/api/video_codecs/vp8_frame_config.cc index 7253206bd1..05e1911bb7 100644 --- a/api/video_codecs/vp8_frame_config.cc +++ b/api/video_codecs/vp8_frame_config.cc @@ -56,7 +56,7 @@ bool Vp8FrameConfig::References(Buffer buffer) const { case Buffer::kCount: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -71,7 +71,7 @@ bool Vp8FrameConfig::Updates(Buffer buffer) const { case Buffer::kCount: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/api/video_track_source_constraints.h b/api/video_track_source_constraints.h new file mode 100644 index 0000000000..55e5396d62 --- /dev/null +++ b/api/video_track_source_constraints.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +// This file contains interfaces for MediaStream, MediaTrack and MediaSource. +// These interfaces are used for implementing MediaStream and MediaTrack as +// defined in http://dev.w3.org/2011/webrtc/editor/webrtc.html#stream-api. These +// interfaces must be used only with PeerConnection. + +#ifndef API_VIDEO_TRACK_SOURCE_CONSTRAINTS_H_ +#define API_VIDEO_TRACK_SOURCE_CONSTRAINTS_H_ + +#include "absl/types/optional.h" + +namespace webrtc { + +// This struct definition describes constraints on the video source that may be +// set with VideoTrackSourceInterface::ProcessConstraints. +struct VideoTrackSourceConstraints { + absl::optional min_fps; + absl::optional max_fps; +}; + +} // namespace webrtc + +#endif // API_VIDEO_TRACK_SOURCE_CONSTRAINTS_H_ diff --git a/api/voip/voip_engine_factory.cc b/api/voip/voip_engine_factory.cc index 88f63f9c92..8da53cef74 100644 --- a/api/voip/voip_engine_factory.cc +++ b/api/voip/voip_engine_factory.cc @@ -24,7 +24,7 @@ std::unique_ptr CreateVoipEngine(VoipEngineConfig config) { RTC_CHECK(config.audio_device_module); if (!config.audio_processing) { - RTC_DLOG(INFO) << "No audio processing functionality provided."; + RTC_DLOG(LS_INFO) << "No audio processing functionality provided."; } return std::make_unique(std::move(config.encoder_factory), diff --git a/api/webrtc_key_value_config.h b/api/webrtc_key_value_config.h new file mode 100644 index 0000000000..32a2e47eed --- /dev/null +++ b/api/webrtc_key_value_config.h @@ -0,0 +1,41 @@ +/* + * 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_ + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// An interface that provides a key-value mapping for configuring internal +// details of WebRTC. Note that there's no guarantess that the meaning of a +// particular key value mapping will be preserved over time and no announcements +// will be made if they are changed. It's up to the library user to ensure that +// the behavior does not break. +class RTC_EXPORT WebRtcKeyValueConfig { + public: + virtual ~WebRtcKeyValueConfig() = default; + // The configured value for the given key. Defaults to an empty string. + virtual std::string Lookup(absl::string_view key) const = 0; + + bool IsEnabled(absl::string_view key) const { + return Lookup(key).find("Enabled") == 0; + } + + bool IsDisabled(absl::string_view key) const { + return Lookup(key).find("Disabled") == 0; + } +}; +} // namespace webrtc + +#endif // API_WEBRTC_KEY_VALUE_CONFIG_H_ diff --git a/api/wrapping_async_dns_resolver.cc b/api/wrapping_async_dns_resolver.cc new file mode 100644 index 0000000000..866cb0076d --- /dev/null +++ b/api/wrapping_async_dns_resolver.cc @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#include "api/wrapping_async_dns_resolver.h" + +namespace webrtc { + +bool WrappingAsyncDnsResolverResult::GetResolvedAddress( + int family, + rtc::SocketAddress* addr) const { + if (!owner_->wrapped()) { + return false; + } + return owner_->wrapped()->GetResolvedAddress(family, addr); +} + +int WrappingAsyncDnsResolverResult::GetError() const { + if (!owner_->wrapped()) { + return -1; // FIXME: Find a code that makes sense. + } + return owner_->wrapped()->GetError(); +} + +} // namespace webrtc diff --git a/api/wrapping_async_dns_resolver.h b/api/wrapping_async_dns_resolver.h new file mode 100644 index 0000000000..80da206e75 --- /dev/null +++ b/api/wrapping_async_dns_resolver.h @@ -0,0 +1,117 @@ +/* + * 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_WRAPPING_ASYNC_DNS_RESOLVER_H_ +#define API_WRAPPING_ASYNC_DNS_RESOLVER_H_ + +#include +#include + +#include "absl/memory/memory.h" +#include "api/async_dns_resolver.h" +#include "api/sequence_checker.h" +#include "rtc_base/async_resolver.h" +#include "rtc_base/async_resolver_interface.h" +#include "rtc_base/checks.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread_annotations.h" + +// This file defines a DNS resolver that wraps an old-style +// AsyncResolver. +// It is part of the conversion to the newer interface, and will go away +// once conversion is finished. +// TODO(bugs.webrtc.org/12598): Delete this API. + +namespace webrtc { + +class WrappingAsyncDnsResolver; + +class RTC_EXPORT WrappingAsyncDnsResolverResult + : public AsyncDnsResolverResult { + public: + explicit WrappingAsyncDnsResolverResult(WrappingAsyncDnsResolver* owner) + : owner_(owner) {} + ~WrappingAsyncDnsResolverResult() {} + + // Note: Inline declaration not possible, since it refers to + // WrappingAsyncDnsResolver. + bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override; + int GetError() const override; + + private: + WrappingAsyncDnsResolver* const owner_; +}; + +class RTC_EXPORT WrappingAsyncDnsResolver : public AsyncDnsResolverInterface, + public sigslot::has_slots<> { + public: + explicit WrappingAsyncDnsResolver(rtc::AsyncResolverInterface* wrapped) + : wrapped_(absl::WrapUnique(wrapped)), result_(this) {} + + ~WrappingAsyncDnsResolver() override { + // Workaround to get around the fact that sigslot-using objects can't be + // destroyed from within their callback: Alert class users early. + // TODO(bugs.webrtc.org/12651): Delete this class once the sigslot users are + // gone. + RTC_CHECK(!within_resolve_result_); + wrapped_.release()->Destroy(false); + } + + void Start(const rtc::SocketAddress& addr, + std::function callback) override { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK_EQ(State::kNotStarted, state_); + state_ = State::kStarted; + callback_ = callback; + wrapped_->SignalDone.connect(this, + &WrappingAsyncDnsResolver::OnResolveResult); + wrapped_->Start(addr); + } + + const AsyncDnsResolverResult& result() const override { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK_EQ(State::kResolved, state_); + return result_; + } + + private: + enum class State { kNotStarted, kStarted, kResolved }; + + friend class WrappingAsyncDnsResolverResult; + // For use by WrappingAsyncDnsResolverResult + rtc::AsyncResolverInterface* wrapped() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return wrapped_.get(); + } + + void OnResolveResult(rtc::AsyncResolverInterface* ref) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(state_ == State::kStarted); + RTC_DCHECK_EQ(ref, wrapped_.get()); + state_ = State::kResolved; + within_resolve_result_ = true; + callback_(); + within_resolve_result_ = false; + } + + // The class variables need to be accessed on a single thread. + SequenceChecker sequence_checker_; + std::function callback_ RTC_GUARDED_BY(sequence_checker_); + std::unique_ptr wrapped_ + RTC_GUARDED_BY(sequence_checker_); + State state_ RTC_GUARDED_BY(sequence_checker_) = State::kNotStarted; + WrappingAsyncDnsResolverResult result_ RTC_GUARDED_BY(sequence_checker_); + bool within_resolve_result_ RTC_GUARDED_BY(sequence_checker_) = false; +}; + +} // namespace webrtc + +#endif // API_WRAPPING_ASYNC_DNS_RESOLVER_H_ diff --git a/audio/BUILD.gn b/audio/BUILD.gn index ea6b9d2d55..dcafe3bf7b 100644 --- a/audio/BUILD.gn +++ b/audio/BUILD.gn @@ -49,6 +49,7 @@ rtc_library("audio") { "../api:scoped_refptr", "../api:sequence_checker", "../api:transport_api", + "../api:webrtc_key_value_config", "../api/audio:aec3_factory", "../api/audio:audio_frame_api", "../api/audio:audio_frame_processor", @@ -83,7 +84,6 @@ rtc_library("audio") { "../modules/pacing", "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", - "../modules/utility", "../rtc_base", "../rtc_base:audio_format_to_string", "../rtc_base:checks", @@ -105,6 +105,7 @@ rtc_library("audio") { ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } @@ -190,6 +191,7 @@ if (rtc_include_tests) { "../test:mock_transformable_frame", "../test:mock_transport", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_common", "../test:test_support", "utility:utility_tests", @@ -226,7 +228,10 @@ if (rtc_include_tests) { "../test/pc/e2e:network_quality_metrics_reporter", "//testing/gtest", ] - absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/strings", + ] if (is_android) { deps += [ "//testing/android/native_test:native_test_native_code" ] } diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index 01bf3792fe..c662bdf3ac 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -458,9 +458,9 @@ void AudioReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) { channel_receive_->ReceivedRTCPPacket(packet, length); } -void AudioReceiveStream::SetSyncGroup(const std::string& sync_group) { +void AudioReceiveStream::SetSyncGroup(absl::string_view sync_group) { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - config_.sync_group = sync_group; + config_.sync_group = std::string(sync_group); } void AudioReceiveStream::SetLocalSsrc(uint32_t local_ssrc) { diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h index 42fb9ad554..3241e61229 100644 --- a/audio/audio_receive_stream.h +++ b/audio/audio_receive_stream.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/audio/audio_mixer.h" #include "api/neteq/neteq_factory.h" #include "api/rtp_headers.h" @@ -29,9 +30,7 @@ namespace webrtc { class PacketRouter; -class ProcessThread; class RtcEventLog; -class RtpPacketReceived; class RtpStreamReceiverControllerInterface; class RtpStreamReceiverInterface; @@ -123,7 +122,7 @@ class AudioReceiveStream final : public webrtc::AudioReceiveStream, void AssociateSendStream(AudioSendStream* send_stream); void DeliverRtcp(const uint8_t* packet, size_t length); - void SetSyncGroup(const std::string& sync_group); + void SetSyncGroup(absl::string_view sync_group); void SetLocalSsrc(uint32_t local_ssrc); diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc index 7b986b13e2..b7df370e87 100644 --- a/audio/audio_send_stream.cc +++ b/audio/audio_send_stream.cc @@ -39,7 +39,6 @@ #include "rtc_base/logging.h" #include "rtc_base/strings/audio_format_to_string.h" #include "rtc_base/task_queue.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -88,8 +87,9 @@ std::unique_ptr AudioAllocationConfig::Parser() { "rate_prio", &bitrate_priority); } -AudioAllocationConfig::AudioAllocationConfig() { - Parser()->Parse(field_trial::FindFullName(kKey)); +AudioAllocationConfig::AudioAllocationConfig( + const WebRtcKeyValueConfig& field_trials) { + Parser()->Parse(field_trials.Lookup(kKey)); if (priority_bitrate_raw && !priority_bitrate.IsZero()) { RTC_LOG(LS_WARNING) << "'priority_bitrate' and '_raw' are mutually " "exclusive but both were configured."; @@ -106,28 +106,31 @@ AudioSendStream::AudioSendStream( BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, RtcpRttStats* rtcp_rtt_stats, - const absl::optional& suspended_rtp_state) - : AudioSendStream(clock, - config, - audio_state, - task_queue_factory, - rtp_transport, - bitrate_allocator, - event_log, - suspended_rtp_state, - voe::CreateChannelSend( - clock, - task_queue_factory, - config.send_transport, - rtcp_rtt_stats, - event_log, - config.frame_encryptor, - config.crypto_options, - config.rtp.extmap_allow_mixed, - config.rtcp_report_interval_ms, - config.rtp.ssrc, - config.frame_transformer, - rtp_transport->transport_feedback_observer())) {} + const absl::optional& suspended_rtp_state, + const WebRtcKeyValueConfig& field_trials) + : AudioSendStream( + clock, + config, + audio_state, + task_queue_factory, + rtp_transport, + bitrate_allocator, + event_log, + suspended_rtp_state, + voe::CreateChannelSend(clock, + task_queue_factory, + config.send_transport, + rtcp_rtt_stats, + event_log, + config.frame_encryptor, + config.crypto_options, + config.rtp.extmap_allow_mixed, + config.rtcp_report_interval_ms, + config.rtp.ssrc, + config.frame_transformer, + rtp_transport->transport_feedback_observer(), + field_trials), + field_trials) {} AudioSendStream::AudioSendStream( Clock* clock, @@ -138,21 +141,24 @@ AudioSendStream::AudioSendStream( BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, const absl::optional& suspended_rtp_state, - std::unique_ptr channel_send) + std::unique_ptr channel_send, + const WebRtcKeyValueConfig& field_trials) : clock_(clock), + field_trials_(field_trials), rtp_transport_queue_(rtp_transport->GetWorkerQueue()), allocate_audio_without_feedback_( - field_trial::IsEnabled("WebRTC-Audio-ABWENoTWCC")), + field_trials_.IsEnabled("WebRTC-Audio-ABWENoTWCC")), enable_audio_alr_probing_( - !field_trial::IsDisabled("WebRTC-Audio-AlrProbing")), + !field_trials_.IsDisabled("WebRTC-Audio-AlrProbing")), send_side_bwe_with_overhead_( - !field_trial::IsDisabled("WebRTC-SendSideBwe-WithOverhead")), + !field_trials_.IsDisabled("WebRTC-SendSideBwe-WithOverhead")), + allocation_settings_(field_trials_), config_(Config(/*send_transport=*/nullptr)), audio_state_(audio_state), channel_send_(std::move(channel_send)), event_log_(event_log), use_legacy_overhead_calculation_( - field_trial::IsEnabled("WebRTC-Audio-LegacyOverhead")), + field_trials_.IsEnabled("WebRTC-Audio-LegacyOverhead")), bitrate_allocator_(bitrate_allocator), rtp_transport_(rtp_transport), rtp_rtcp_module_(channel_send_->GetRtpRtcp()), @@ -269,11 +275,10 @@ void AudioSendStream::ConfigureStream( } if (first_time || new_ids.abs_send_time != old_ids.abs_send_time) { - rtp_rtcp_module_->DeregisterSendRtpHeaderExtension( - kRtpExtensionAbsoluteSendTime); + absl::string_view uri = AbsoluteSendTime::Uri(); + rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(uri); if (new_ids.abs_send_time) { - rtp_rtcp_module_->RegisterRtpHeaderExtension(AbsoluteSendTime::kUri, - new_ids.abs_send_time); + rtp_rtcp_module_->RegisterRtpHeaderExtension(uri, new_ids.abs_send_time); } } @@ -290,7 +295,7 @@ void AudioSendStream::ConfigureStream( if (!allocate_audio_without_feedback_ && new_ids.transport_sequence_number != 0) { rtp_rtcp_module_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, new_ids.transport_sequence_number); + TransportSequenceNumber::Uri(), new_ids.transport_sequence_number); // Probing in application limited region is only used in combination with // send side congestion control, wich depends on feedback packets which // requires transport sequence numbers to be enabled. @@ -308,34 +313,16 @@ void AudioSendStream::ConfigureStream( if ((first_time || new_ids.mid != old_ids.mid || new_config.rtp.mid != old_config.rtp.mid) && new_ids.mid != 0 && !new_config.rtp.mid.empty()) { - rtp_rtcp_module_->RegisterRtpHeaderExtension(RtpMid::kUri, new_ids.mid); + rtp_rtcp_module_->RegisterRtpHeaderExtension(RtpMid::Uri(), new_ids.mid); rtp_rtcp_module_->SetMid(new_config.rtp.mid); } - // RID RTP header extension - if ((first_time || new_ids.rid != old_ids.rid || - new_ids.repaired_rid != old_ids.repaired_rid || - new_config.rtp.rid != old_config.rtp.rid)) { - if (new_ids.rid != 0 || new_ids.repaired_rid != 0) { - if (new_config.rtp.rid.empty()) { - rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(RtpStreamId::kUri); - } else if (new_ids.repaired_rid != 0) { - rtp_rtcp_module_->RegisterRtpHeaderExtension(RtpStreamId::kUri, - new_ids.repaired_rid); - } else { - rtp_rtcp_module_->RegisterRtpHeaderExtension(RtpStreamId::kUri, - new_ids.rid); - } - } - rtp_rtcp_module_->SetRid(new_config.rtp.rid); - } - if (first_time || new_ids.abs_capture_time != old_ids.abs_capture_time) { - rtp_rtcp_module_->DeregisterSendRtpHeaderExtension( - kRtpExtensionAbsoluteCaptureTime); + absl::string_view uri = AbsoluteCaptureTimeExtension::Uri(); + rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(uri); if (new_ids.abs_capture_time) { - rtp_rtcp_module_->RegisterRtpHeaderExtension( - AbsoluteCaptureTimeExtension::kUri, new_ids.abs_capture_time); + rtp_rtcp_module_->RegisterRtpHeaderExtension(uri, + new_ids.abs_capture_time); } } @@ -446,7 +433,7 @@ webrtc::AudioSendStream::Stats AudioSendStream::GetStats( RTC_DCHECK_RUN_ON(&worker_thread_checker_); webrtc::AudioSendStream::Stats stats; stats.local_ssrc = config_.rtp.ssrc; - stats.target_bitrate_bps = channel_send_->GetBitrate(); + stats.target_bitrate_bps = channel_send_->GetTargetBitrate(); webrtc::CallSendStatistics call_stats = channel_send_->GetRTCPStatistics(); stats.payload_bytes_sent = call_stats.payload_bytes_sent; @@ -659,7 +646,8 @@ bool AudioSendStream::SetupSendCodec(const Config& new_config) { AudioEncoderCopyRed::Config red_config; red_config.payload_type = *spec.red_payload_type; red_config.speech_encoder = std::move(encoder); - encoder = std::make_unique(std::move(red_config)); + encoder = std::make_unique(std::move(red_config), + field_trials_); } // Set currently known overhead (used in ANA, opus only). diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h index 94ea267b09..b13664a670 100644 --- a/audio/audio_send_stream.h +++ b/audio/audio_send_stream.h @@ -16,6 +16,7 @@ #include #include "api/sequence_checker.h" +#include "api/webrtc_key_value_config.h" #include "audio/audio_level.h" #include "audio/channel_send.h" #include "call/audio_send_stream.h" @@ -46,7 +47,7 @@ struct AudioAllocationConfig { absl::optional bitrate_priority; std::unique_ptr Parser(); - AudioAllocationConfig(); + explicit AudioAllocationConfig(const WebRtcKeyValueConfig& field_trials); }; namespace internal { class AudioState; @@ -62,7 +63,8 @@ class AudioSendStream final : public webrtc::AudioSendStream, BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, RtcpRttStats* rtcp_rtt_stats, - const absl::optional& suspended_rtp_state); + const absl::optional& suspended_rtp_state, + const WebRtcKeyValueConfig& field_trials); // For unit tests, which need to supply a mock ChannelSend. AudioSendStream(Clock* clock, const webrtc::AudioSendStream::Config& config, @@ -72,7 +74,8 @@ class AudioSendStream final : public webrtc::AudioSendStream, BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, const absl::optional& suspended_rtp_state, - std::unique_ptr channel_send); + std::unique_ptr channel_send, + const WebRtcKeyValueConfig& field_trials); AudioSendStream() = delete; AudioSendStream(const AudioSendStream&) = delete; @@ -169,6 +172,7 @@ class AudioSendStream final : public webrtc::AudioSendStream, RTC_RUN_ON(worker_thread_checker_); Clock* clock_; + const WebRtcKeyValueConfig& field_trials_; SequenceChecker worker_thread_checker_; SequenceChecker pacer_thread_checker_; diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc index 58db5254fe..2d2e64c155 100644 --- a/audio/audio_send_stream_unittest.cc +++ b/audio/audio_send_stream_unittest.cc @@ -32,10 +32,10 @@ #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "rtc_base/task_queue_for_test.h" #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" #include "test/mock_audio_encoder_factory.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace test { @@ -196,7 +196,8 @@ struct ConfigHelper { Clock::GetRealTimeClock(), stream_config_, audio_state_, task_queue_factory_.get(), &rtp_transport_, &bitrate_allocator_, &event_log_, absl::nullopt, - std::unique_ptr(channel_send_))); + std::unique_ptr(channel_send_), + field_trials)); } AudioSendStream::Config& config() { return stream_config_; } @@ -233,7 +234,7 @@ struct ConfigHelper { .WillRepeatedly(Return(&bandwidth_observer_)); if (audio_bwe_enabled) { EXPECT_CALL(rtp_rtcp_, - RegisterRtpHeaderExtension(TransportSequenceNumber::kUri, + RegisterRtpHeaderExtension(TransportSequenceNumber::Uri(), kTransportSequenceNumberId)) .Times(1); EXPECT_CALL(*channel_send_, @@ -246,7 +247,6 @@ struct ConfigHelper { .Times(1); } EXPECT_CALL(*channel_send_, ResetSenderCongestionControlObjects()).Times(1); - EXPECT_CALL(rtp_rtcp_, SetRid(std::string())).Times(1); } void SetupMockForSetupSendCodec(bool expect_set_encoder_call) { @@ -301,7 +301,7 @@ struct ConfigHelper { .WillRepeatedly(Return(report_blocks)); EXPECT_CALL(*channel_send_, GetANAStatistics()) .WillRepeatedly(Return(ANAStats())); - EXPECT_CALL(*channel_send_, GetBitrate()).WillRepeatedly(Return(0)); + EXPECT_CALL(*channel_send_, GetTargetBitrate()).WillRepeatedly(Return(0)); audio_processing_stats_.echo_return_loss = kEchoReturnLoss; audio_processing_stats_.echo_return_loss_enhancement = @@ -322,6 +322,8 @@ struct ConfigHelper { TaskQueueForTest* worker() { return &worker_queue_; } + test::ScopedKeyValueConfig field_trials; + private: SimulatedClock clock_; std::unique_ptr task_queue_factory_; @@ -660,10 +662,10 @@ TEST(AudioSendStreamTest, SSBweTargetInRangeRespected) { } TEST(AudioSendStreamTest, SSBweFieldTrialMinRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); auto send_stream = helper.CreateAudioSendStream(); EXPECT_CALL( *helper.channel_send(), @@ -677,10 +679,10 @@ TEST(AudioSendStreamTest, SSBweFieldTrialMinRespected) { } TEST(AudioSendStreamTest, SSBweFieldTrialMaxRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); auto send_stream = helper.CreateAudioSendStream(); EXPECT_CALL( *helper.channel_send(), @@ -694,10 +696,10 @@ TEST(AudioSendStreamTest, SSBweFieldTrialMaxRespected) { } TEST(AudioSendStreamTest, SSBweWithOverhead) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials(helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); @@ -715,11 +717,12 @@ TEST(AudioSendStreamTest, SSBweWithOverhead) { } TEST(AudioSendStreamTest, SSBweWithOverheadMinRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/" - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); @@ -735,11 +738,12 @@ TEST(AudioSendStreamTest, SSBweWithOverheadMinRespected) { } TEST(AudioSendStreamTest, SSBweWithOverheadMaxRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/" - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); @@ -802,7 +806,7 @@ TEST(AudioSendStreamTest, ReconfigureTransportCcResetsFirst) { ConfigHelper::AddBweToConfig(&new_config); EXPECT_CALL(*helper.rtp_rtcp(), - RegisterRtpHeaderExtension(TransportSequenceNumber::kUri, + RegisterRtpHeaderExtension(TransportSequenceNumber::Uri(), kTransportSequenceNumberId)) .Times(1); { diff --git a/audio/audio_state.cc b/audio/audio_state.cc index b70ec4297d..1ffe2257cd 100644 --- a/audio/audio_state.cc +++ b/audio/audio_state.cc @@ -123,7 +123,7 @@ void AudioState::RemoveSendingStream(webrtc::AudioSendStream* stream) { } void AudioState::SetPlayout(bool enabled) { - RTC_LOG(INFO) << "SetPlayout(" << enabled << ")"; + RTC_LOG(LS_INFO) << "SetPlayout(" << enabled << ")"; RTC_DCHECK(thread_checker_.IsCurrent()); if (playout_enabled_ != enabled) { playout_enabled_ = enabled; @@ -146,7 +146,7 @@ void AudioState::SetPlayout(bool enabled) { } void AudioState::SetRecording(bool enabled) { - RTC_LOG(INFO) << "SetRecording(" << enabled << ")"; + RTC_LOG(LS_INFO) << "SetRecording(" << enabled << ")"; RTC_DCHECK(thread_checker_.IsCurrent()); if (recording_enabled_ != enabled) { recording_enabled_ = enabled; diff --git a/audio/audio_transport_impl.cc b/audio/audio_transport_impl.cc index 2a80ea893d..194f09cf6c 100644 --- a/audio/audio_transport_impl.cc +++ b/audio/audio_transport_impl.cc @@ -102,6 +102,23 @@ AudioTransportImpl::AudioTransportImpl( AudioTransportImpl::~AudioTransportImpl() {} +int32_t AudioTransportImpl::RecordedDataIsAvailable( + const void* audio_data, + const size_t number_of_frames, + const size_t bytes_per_sample, + const size_t number_of_channels, + const uint32_t sample_rate, + const uint32_t audio_delay_milliseconds, + const int32_t clock_drift, + const uint32_t volume, + const bool key_pressed, + uint32_t& new_mic_volume) { // NOLINT: to avoid changing APIs + return RecordedDataIsAvailable( + audio_data, number_of_frames, bytes_per_sample, number_of_channels, + sample_rate, audio_delay_milliseconds, clock_drift, volume, key_pressed, + new_mic_volume, /* estimated_capture_time_ns */ 0); +} + // Not used in Chromium. Process captured audio and distribute to all sending // streams, and try to do this at the lowest possible sample rate. int32_t AudioTransportImpl::RecordedDataIsAvailable( @@ -114,7 +131,9 @@ int32_t AudioTransportImpl::RecordedDataIsAvailable( const int32_t /*clock_drift*/, const uint32_t /*volume*/, const bool key_pressed, - uint32_t& /*new_mic_volume*/) { // NOLINT: to avoid changing APIs + uint32_t& /*new_mic_volume*/, + const int64_t + estimated_capture_time_ns) { // NOLINT: to avoid changing APIs RTC_DCHECK(audio_data); RTC_DCHECK_GE(number_of_channels, 1); RTC_DCHECK_LE(number_of_channels, 2); @@ -144,25 +163,8 @@ int32_t AudioTransportImpl::RecordedDataIsAvailable( ProcessCaptureFrame(audio_delay_milliseconds, key_pressed, swap_stereo_channels, audio_processing_, audio_frame.get()); - - // Typing detection (utilizes the APM/VAD decision). We let the VAD determine - // if we're using this feature or not. - // TODO(solenberg): GetConfig() takes a lock. Work around that. - bool typing_detected = false; - if (audio_processing_ && - audio_processing_->GetConfig().voice_detection.enabled) { - if (audio_frame->vad_activity_ != AudioFrame::kVadUnknown) { - bool vad_active = audio_frame->vad_activity_ == AudioFrame::kVadActive; - typing_detected = typing_detection_.Process(key_pressed, vad_active); - } - } - - // Copy frame and push to each sending stream. The copy is required since an - // encoding task will be posted internally to each stream. - { - MutexLock lock(&capture_lock_); - typing_noise_detected_ = typing_detected; - } + audio_frame->set_absolute_capture_timestamp_ms(estimated_capture_time_ns / + 1000000); RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0); if (async_audio_processing_) @@ -270,8 +272,4 @@ void AudioTransportImpl::SetStereoChannelSwapping(bool enable) { swap_stereo_channels_ = enable; } -bool AudioTransportImpl::typing_noise_detected() const { - MutexLock lock(&capture_lock_); - return typing_noise_detected_; -} } // namespace webrtc diff --git a/audio/audio_transport_impl.h b/audio/audio_transport_impl.h index f3ca2fa848..89999560c6 100644 --- a/audio/audio_transport_impl.h +++ b/audio/audio_transport_impl.h @@ -41,21 +41,34 @@ class AudioTransportImpl : public AudioTransport { ~AudioTransportImpl() override; + // TODO(bugs.webrtc.org/13620) Deprecate this function int32_t RecordedDataIsAvailable(const void* audioSamples, - const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, - const uint32_t totalDelayMS, - const int32_t clockDrift, - const uint32_t currentMicLevel, - const bool keyPressed, + 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) override; - int32_t NeedMorePlayData(const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, + 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, + int64_t estimated_capture_time_ns) override; + + int32_t NeedMorePlayData(size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, void* audioSamples, size_t& nSamplesOut, int64_t* elapsed_time_ms, @@ -73,7 +86,9 @@ class AudioTransportImpl : public AudioTransport { int send_sample_rate_hz, size_t send_num_channels); void SetStereoChannelSwapping(bool enable); - bool typing_noise_detected() const; + // Deprecated. + // TODO(bugs.webrtc.org/11226): Remove. + bool typing_noise_detected() const { return false; } private: void SendProcessedData(std::unique_ptr audio_frame); @@ -90,7 +105,6 @@ class AudioTransportImpl : public AudioTransport { std::vector audio_senders_ RTC_GUARDED_BY(capture_lock_); int send_sample_rate_hz_ RTC_GUARDED_BY(capture_lock_) = 8000; size_t send_num_channels_ RTC_GUARDED_BY(capture_lock_) = 1; - bool typing_noise_detected_ RTC_GUARDED_BY(capture_lock_) = false; bool swap_stereo_channels_ RTC_GUARDED_BY(capture_lock_) = false; PushResampler capture_resampler_; TypingDetection typing_detection_; diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc index fd0bbe9ee2..58361fda26 100644 --- a/audio/channel_receive.cc +++ b/audio/channel_receive.cc @@ -39,7 +39,6 @@ #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/rtp_rtcp/source/rtp_rtcp_config.h" #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/format_macros.h" #include "rtc_base/location.h" @@ -848,14 +847,11 @@ CallReceiveStatistics ChannelReceive::GetRTCPStatistics() const { absl::optional rtcp_sr_stats = rtp_rtcp_->GetSenderReportStats(); if (rtcp_sr_stats.has_value()) { - // Number of seconds since 1900 January 1 00:00 GMT (see - // https://tools.ietf.org/html/rfc868). - constexpr int64_t kNtpJan1970Millisecs = - 2208988800 * rtc::kNumMillisecsPerSec; stats.last_sender_report_timestamp_ms = - rtcp_sr_stats->last_arrival_timestamp.ToMs() - kNtpJan1970Millisecs; + rtcp_sr_stats->last_arrival_timestamp.ToMs() - + rtc::kNtpJan1970Millisecs; stats.last_sender_report_remote_timestamp_ms = - rtcp_sr_stats->last_remote_timestamp.ToMs() - kNtpJan1970Millisecs; + rtcp_sr_stats->last_remote_timestamp.ToMs() - rtc::kNtpJan1970Millisecs; stats.sender_reports_packets_sent = rtcp_sr_stats->packets_sent; stats.sender_reports_bytes_sent = rtcp_sr_stats->bytes_sent; stats.sender_reports_reports_count = rtcp_sr_stats->reports_count; @@ -919,7 +915,7 @@ void ChannelReceive::SetDepacketizerToDecoderFrameTransformer( // Depending on when the channel is created, the transformer might be set // twice. Don't replace the delegate if it was already initialized. if (!frame_transformer || frame_transformer_delegate_) { - RTC_NOTREACHED() << "Not setting the transformer?"; + RTC_DCHECK_NOTREACHED() << "Not setting the transformer?"; return; } diff --git a/audio/channel_receive_frame_transformer_delegate.cc b/audio/channel_receive_frame_transformer_delegate.cc index c9e8a8b29d..c9865cbe19 100644 --- a/audio/channel_receive_frame_transformer_delegate.cc +++ b/audio/channel_receive_frame_transformer_delegate.cc @@ -79,7 +79,7 @@ void ChannelReceiveFrameTransformerDelegate::Transform( void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame( std::unique_ptr frame) { - rtc::scoped_refptr delegate = this; + rtc::scoped_refptr delegate(this); channel_receive_thread_->PostTask(ToQueuedTask( [delegate = std::move(delegate), frame = std::move(frame)]() mutable { delegate->ReceiveFrame(std::move(frame)); diff --git a/audio/channel_send.cc b/audio/channel_send.cc index 6fd216144a..d6b5823f3e 100644 --- a/audio/channel_send.cc +++ b/audio/channel_send.cc @@ -31,7 +31,6 @@ #include "modules/audio_processing/rms_level.h" #include "modules/pacing/packet_router.h" #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" #include "rtc_base/format_macros.h" @@ -44,7 +43,6 @@ #include "rtc_base/task_queue.h" #include "rtc_base/time_utils.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace webrtc { @@ -79,7 +77,8 @@ class ChannelSend : public ChannelSendInterface, int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer); + TransportFeedbackObserver* feedback_observer, + const WebRtcKeyValueConfig& field_trials); ~ChannelSend() override; @@ -96,7 +95,7 @@ class ChannelSend : public ChannelSendInterface, // Codecs void OnBitrateAllocation(BitrateAllocationUpdate update) override; - int GetBitrate() const override; + int GetTargetBitrate() const override; // Network void ReceivedRTCPPacket(const uint8_t* data, size_t length) override; @@ -238,9 +237,6 @@ class ChannelSend : public ChannelSendInterface, rtc::scoped_refptr frame_transformer_delegate_ RTC_GUARDED_BY(encoder_queue_); - mutable Mutex bitrate_mutex_; - int configured_bitrate_bps_ RTC_GUARDED_BY(bitrate_mutex_) = 0; - // Defined last to ensure that there are no running tasks when the other // members are destroyed. rtc::TaskQueue encoder_queue_; @@ -462,7 +458,8 @@ ChannelSend::ChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer) + TransportFeedbackObserver* feedback_observer, + const WebRtcKeyValueConfig& field_trials) : ssrc_(ssrc), event_log_(rtc_event_log), _timeStamp(0), // This is just an offset, RTP module will add it's own @@ -481,7 +478,7 @@ ChannelSend::ChannelSend( "AudioEncoder", TaskQueueFactory::Priority::NORMAL)), fixing_timestamp_stall_( - !field_trial::IsDisabled("WebRTC-Audio-FixTimestampStall")) { + field_trials.IsDisabled("WebRTC-Audio-FixTimestampStall")) { audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config())); RtpRtcpInterface::Configuration configuration; @@ -616,18 +613,14 @@ void ChannelSend::OnBitrateAllocation(BitrateAllocationUpdate update) { // rules. // RTC_DCHECK(worker_thread_checker_.IsCurrent() || // module_process_thread_checker_.IsCurrent()); - MutexLock lock(&bitrate_mutex_); - CallEncoder([&](AudioEncoder* encoder) { encoder->OnReceivedUplinkAllocation(update); }); retransmission_rate_limiter_->SetMaxRate(update.target_bitrate.bps()); - configured_bitrate_bps_ = update.target_bitrate.bps(); } -int ChannelSend::GetBitrate() const { - MutexLock lock(&bitrate_mutex_); - return configured_bitrate_bps_; +int ChannelSend::GetTargetBitrate() const { + return audio_coding_->GetTargetBitrate(); } void ChannelSend::OnUplinkPacketLossRate(float packet_loss_rate) { @@ -708,9 +701,9 @@ void ChannelSend::SetSendAudioLevelIndicationStatus(bool enable, int id) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); _includeAudioLevelIndication = enable; if (enable) { - rtp_rtcp_->RegisterRtpHeaderExtension(AudioLevel::kUri, id); + rtp_rtcp_->RegisterRtpHeaderExtension(AudioLevel::Uri(), id); } else { - rtp_rtcp_->DeregisterSendRtpHeaderExtension(AudioLevel::kUri); + rtp_rtcp_->DeregisterSendRtpHeaderExtension(AudioLevel::Uri()); } } @@ -956,12 +949,13 @@ std::unique_ptr CreateChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer) { + TransportFeedbackObserver* feedback_observer, + const WebRtcKeyValueConfig& field_trials) { return std::make_unique( clock, task_queue_factory, rtp_transport, rtcp_rtt_stats, rtc_event_log, frame_encryptor, crypto_options, extmap_allow_mixed, rtcp_report_interval_ms, ssrc, std::move(frame_transformer), - feedback_observer); + feedback_observer, field_trials); } } // namespace voe diff --git a/audio/channel_send.h b/audio/channel_send.h index 663b947036..bfbfbeedfa 100644 --- a/audio/channel_send.h +++ b/audio/channel_send.h @@ -21,6 +21,7 @@ #include "api/frame_transformer_interface.h" #include "api/function_view.h" #include "api/task_queue/task_queue_factory.h" +#include "api/webrtc_key_value_config.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/source/rtp_rtcp_interface.h" #include "modules/rtp_rtcp/source/rtp_sender_audio.h" @@ -91,7 +92,7 @@ class ChannelSendInterface { int payload_frequency) = 0; virtual bool SendTelephoneEventOutband(int event, int duration_ms) = 0; virtual void OnBitrateAllocation(BitrateAllocationUpdate update) = 0; - virtual int GetBitrate() const = 0; + virtual int GetTargetBitrate() const = 0; virtual void SetInputMute(bool muted) = 0; virtual void ProcessAndEncodeAudio( @@ -135,7 +136,8 @@ std::unique_ptr CreateChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer); + TransportFeedbackObserver* feedback_observer, + const WebRtcKeyValueConfig& field_trials); } // namespace voe } // namespace webrtc diff --git a/audio/channel_send_frame_transformer_delegate.cc b/audio/channel_send_frame_transformer_delegate.cc index eee4cd0d96..29bb0b81d8 100644 --- a/audio/channel_send_frame_transformer_delegate.cc +++ b/audio/channel_send_frame_transformer_delegate.cc @@ -102,7 +102,7 @@ void ChannelSendFrameTransformerDelegate::OnTransformedFrame( MutexLock lock(&send_lock_); if (!send_frame_callback_) return; - rtc::scoped_refptr delegate = this; + rtc::scoped_refptr delegate(this); encoder_queue_->PostTask( [delegate = std::move(delegate), frame = std::move(frame)]() mutable { delegate->SendFrame(std::move(frame)); diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h index d445b51312..a02bee38ad 100644 --- a/audio/mock_voe_channel_proxy.h +++ b/audio/mock_voe_channel_proxy.h @@ -166,7 +166,7 @@ class MockChannelSend : public voe::ChannelSendInterface { (std::unique_ptr), (override)); MOCK_METHOD(RtpRtcpInterface*, GetRtpRtcp, (), (const, override)); - MOCK_METHOD(int, GetBitrate, (), (const, override)); + MOCK_METHOD(int, GetTargetBitrate, (), (const, override)); MOCK_METHOD(int64_t, GetRTT, (), (const, override)); MOCK_METHOD(void, StartSend, (), (override)); MOCK_METHOD(void, StopSend, (), (override)); diff --git a/audio/test/OWNERS b/audio/test/OWNERS new file mode 100644 index 0000000000..3754d4823a --- /dev/null +++ b/audio/test/OWNERS @@ -0,0 +1,3 @@ +# Script to launch low_bandwidth_audio_test. +per-file low_bandwidth_audio_test.py=mbonadei@webrtc.org +per-file low_bandwidth_audio_test.py=jleconte@webrtc.org diff --git a/audio/test/low_bandwidth_audio_test.py b/audio/test/low_bandwidth_audio_test.py index 9aaf30f364..07065e2c8d 100755 --- a/audio/test/low_bandwidth_audio_test.py +++ b/audio/test/low_bandwidth_audio_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env vpython3 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. # # Use of this source code is governed by a BSD-style license @@ -15,6 +15,7 @@ output files will be performed. import argparse import collections +import json import logging import os import re @@ -31,108 +32,99 @@ NO_TOOLS_ERROR_MESSAGE = ( 'To fix this run:\n' ' python %s %s\n' '\n' - 'Note that these tools are Google-internal due to licensing, so in order to ' - 'use them you will have to get your own license and manually put them in the ' - 'right location.\n' + 'Note that these tools are Google-internal due to licensing, so in order ' + 'to use them you will have to get your own license and manually put them ' + 'in the right location.\n' 'See https://cs.chromium.org/chromium/src/third_party/webrtc/tools_webrtc/' 'download_tools.py?rcl=bbceb76f540159e2dba0701ac03c514f01624130&l=13') def _LogCommand(command): - logging.info('Running %r', command) - return command + logging.info('Running %r', command) + return command def _ParseArgs(): - parser = argparse.ArgumentParser( - description='Run low-bandwidth audio tests.') - parser.add_argument('build_dir', - help='Path to the build directory (e.g. out/Release).') - parser.add_argument('--remove', - action='store_true', - help='Remove output audio files after testing.') - parser.add_argument( - '--android', - action='store_true', - help='Perform the test on a connected Android device instead.') - parser.add_argument('--adb-path', - help='Path to adb binary.', - default='adb') - parser.add_argument('--num-retries', - default='0', - help='Number of times to retry the test on Android.') - parser.add_argument( - '--isolated-script-test-perf-output', - default=None, - help='Path to store perf results in histogram proto format.') - parser.add_argument('--extra-test-args', - default=[], - action='append', - help='Extra args to path to the test binary.') + parser = argparse.ArgumentParser(description='Run low-bandwidth audio tests.') + parser.add_argument('build_dir', + help='Path to the build directory (e.g. out/Release).') + parser.add_argument('--remove', + action='store_true', + help='Remove output audio files after testing.') + parser.add_argument( + '--android', + action='store_true', + help='Perform the test on a connected Android device instead.') + parser.add_argument('--adb-path', help='Path to adb binary.', default='adb') + parser.add_argument('--num-retries', + default='0', + help='Number of times to retry the test on Android.') + parser.add_argument( + '--isolated-script-test-perf-output', + default=None, + help='Path to store perf results in histogram proto format.') + parser.add_argument( + '--isolated-script-test-output', + default=None, + help='Path to output an empty JSON file which Chromium infra requires.') - # Ignore Chromium-specific flags - parser.add_argument('--test-launcher-summary-output', - type=str, - default=None) - args = parser.parse_args() - - return args + return parser.parse_known_args() def _GetPlatform(): - if sys.platform == 'win32': - return 'win' - elif sys.platform == 'darwin': - return 'mac' - elif sys.platform.startswith('linux'): - return 'linux' + if sys.platform == 'win32': + return 'win' + if sys.platform == 'darwin': + return 'mac' + if sys.platform.startswith('linux'): + return 'linux' + raise AssertionError('Unknown platform %s' % sys.platform) def _GetExtension(): - return '.exe' if sys.platform == 'win32' else '' + return '.exe' if sys.platform == 'win32' else '' def _GetPathToTools(): - tools_dir = os.path.join(SRC_DIR, 'tools_webrtc') - toolchain_dir = os.path.join(tools_dir, 'audio_quality') + tools_dir = os.path.join(SRC_DIR, 'tools_webrtc') + toolchain_dir = os.path.join(tools_dir, 'audio_quality') - platform = _GetPlatform() - ext = _GetExtension() + platform = _GetPlatform() + ext = _GetExtension() - pesq_path = os.path.join(toolchain_dir, platform, 'pesq' + ext) - if not os.path.isfile(pesq_path): - pesq_path = None + pesq_path = os.path.join(toolchain_dir, platform, 'pesq' + ext) + if not os.path.isfile(pesq_path): + pesq_path = None - polqa_path = os.path.join(toolchain_dir, platform, 'PolqaOem64' + ext) - if not os.path.isfile(polqa_path): - polqa_path = None + polqa_path = os.path.join(toolchain_dir, platform, 'PolqaOem64' + ext) + if not os.path.isfile(polqa_path): + polqa_path = None - if (platform != 'mac' and not polqa_path) or not pesq_path: - logging.error(NO_TOOLS_ERROR_MESSAGE, toolchain_dir, - os.path.join(tools_dir, 'download_tools.py'), - toolchain_dir) + if (platform != 'mac' and not polqa_path) or not pesq_path: + logging.error(NO_TOOLS_ERROR_MESSAGE, toolchain_dir, + os.path.join(tools_dir, 'download_tools.py'), toolchain_dir) - return pesq_path, polqa_path + return pesq_path, polqa_path def ExtractTestRuns(lines, echo=False): - """Extracts information about tests from the output of a test runner. + """Extracts information about tests from the output of a test runner. Produces tuples (android_device, test_name, reference_file, degraded_file, cur_perf_results). """ - for line in lines: - if echo: - sys.stdout.write(line) + for line in lines: + if echo: + sys.stdout.write(line) - # Output from Android has a prefix with the device name. - android_prefix_re = r'(?:I\b.+\brun_tests_on_device\((.+?)\)\s*)?' - test_re = r'^' + android_prefix_re + (r'TEST (\w+) ([^ ]+?) ([^\s]+)' - r' ?([^\s]+)?\s*$') + # Output from Android has a prefix with the device name. + android_prefix_re = r'(?:I\b.+\brun_tests_on_device\((.+?)\)\s*)?' + test_re = r'^' + android_prefix_re + (r'TEST (\w+) ([^ ]+?) ([^\s]+)' + r' ?([^\s]+)?\s*$') - match = re.search(test_re, line) - if match: - yield match.groups() + match = re.search(test_re, line) + if match: + yield match.groups() def _GetFile(file_path, @@ -140,97 +132,97 @@ def _GetFile(file_path, move=False, android=False, adb_prefix=('adb', )): - out_file_name = os.path.basename(file_path) - out_file_path = os.path.join(out_dir, out_file_name) + out_file_name = os.path.basename(file_path) + out_file_path = os.path.join(out_dir, out_file_name) - if android: - # Pull the file from the connected Android device. - adb_command = adb_prefix + ('pull', file_path, out_dir) - subprocess.check_call(_LogCommand(adb_command)) - if move: - # Remove that file. - adb_command = adb_prefix + ('shell', 'rm', file_path) - subprocess.check_call(_LogCommand(adb_command)) - elif os.path.abspath(file_path) != os.path.abspath(out_file_path): - if move: - shutil.move(file_path, out_file_path) - else: - shutil.copy(file_path, out_file_path) + if android: + # Pull the file from the connected Android device. + adb_command = adb_prefix + ('pull', file_path, out_dir) + subprocess.check_call(_LogCommand(adb_command)) + if move: + # Remove that file. + adb_command = adb_prefix + ('shell', 'rm', file_path) + subprocess.check_call(_LogCommand(adb_command)) + elif os.path.abspath(file_path) != os.path.abspath(out_file_path): + if move: + shutil.move(file_path, out_file_path) + else: + shutil.copy(file_path, out_file_path) - return out_file_path + return out_file_path def _RunPesq(executable_path, reference_file, degraded_file, sample_rate_hz=16000): - directory = os.path.dirname(reference_file) - assert os.path.dirname(degraded_file) == directory + directory = os.path.dirname(reference_file) + assert os.path.dirname(degraded_file) == directory - # Analyze audio. - command = [ - executable_path, - '+%d' % sample_rate_hz, - os.path.basename(reference_file), - os.path.basename(degraded_file) - ] - # Need to provide paths in the current directory due to a bug in PESQ: - # On Mac, for some 'path/to/file.wav', if 'file.wav' is longer than - # 'path/to', PESQ crashes. - out = subprocess.check_output(_LogCommand(command), - cwd=directory, - stderr=subprocess.STDOUT) + # Analyze audio. + command = [ + executable_path, + '+%d' % sample_rate_hz, + os.path.basename(reference_file), + os.path.basename(degraded_file) + ] + # Need to provide paths in the current directory due to a bug in PESQ: + # On Mac, for some 'path/to/file.wav', if 'file.wav' is longer than + # 'path/to', PESQ crashes. + out = subprocess.check_output(_LogCommand(command), + cwd=directory, + universal_newlines=True, + stderr=subprocess.STDOUT) - # Find the scores in stdout of PESQ. - match = re.search( - r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)', out) - if match: - raw_mos, _ = match.groups() - - return {'pesq_mos': (raw_mos, 'unitless')} - else: - logging.error('PESQ: %s', out.splitlines()[-1]) - return {} + # Find the scores in stdout of PESQ. + match = re.search( + r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)', out) + if match: + raw_mos, _ = match.groups() + return {'pesq_mos': (raw_mos, 'unitless')} + logging.error('PESQ: %s', out.splitlines()[-1]) + return {} def _RunPolqa(executable_path, reference_file, degraded_file): - # Analyze audio. - command = [ - executable_path, '-q', '-LC', 'NB', '-Ref', reference_file, '-Test', - degraded_file - ] - process = subprocess.Popen(_LogCommand(command), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = process.communicate() + # Analyze audio. + command = [ + executable_path, '-q', '-LC', 'NB', '-Ref', reference_file, '-Test', + degraded_file + ] + process = subprocess.Popen(_LogCommand(command), + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = process.communicate() - # Find the scores in stdout of POLQA. - match = re.search(r'\bMOS-LQO:\s+([\d.]+)', out) + # Find the scores in stdout of POLQA. + match = re.search(r'\bMOS-LQO:\s+([\d.]+)', out) - if process.returncode != 0 or not match: - if process.returncode == 2: - logging.warning('%s (2)', err.strip()) - logging.warning('POLQA license error, skipping test.') - else: - logging.error('%s (%d)', err.strip(), process.returncode) - return {} + if process.returncode != 0 or not match: + if process.returncode == 2: + logging.warning('%s (2)', err.strip()) + logging.warning('POLQA license error, skipping test.') + else: + logging.error('%s (%d)', err.strip(), process.returncode) + return {} - mos_lqo, = match.groups() - return {'polqa_mos_lqo': (mos_lqo, 'unitless')} + mos_lqo, = match.groups() + return {'polqa_mos_lqo': (mos_lqo, 'unitless')} def _MergeInPerfResultsFromCcTests(histograms, run_perf_results_file): - from tracing.value import histogram_set + from tracing.value import histogram_set - cc_histograms = histogram_set.HistogramSet() - with open(run_perf_results_file, 'rb') as f: - contents = f.read() - if not contents: - return + cc_histograms = histogram_set.HistogramSet() + with open(run_perf_results_file, 'rb') as f: + contents = f.read() + if not contents: + return - cc_histograms.ImportProto(contents) + cc_histograms.ImportProto(contents) - histograms.Merge(cc_histograms) + histograms.Merge(cc_histograms) Analyzer = collections.namedtuple( @@ -238,136 +230,136 @@ Analyzer = collections.namedtuple( def _ConfigurePythonPath(args): - script_dir = os.path.dirname(os.path.realpath(__file__)) - checkout_root = os.path.abspath( - os.path.join(script_dir, os.pardir, os.pardir)) + script_dir = os.path.dirname(os.path.realpath(__file__)) + checkout_root = os.path.abspath(os.path.join(script_dir, os.pardir, + os.pardir)) - # TODO(https://crbug.com/1029452): Use a copy rule and add these from the out - # dir like for the third_party/protobuf code. - sys.path.insert( - 0, os.path.join(checkout_root, 'third_party', 'catapult', 'tracing')) + # TODO(https://crbug.com/1029452): Use a copy rule and add these from the + # out dir like for the third_party/protobuf code. + sys.path.insert( + 0, os.path.join(checkout_root, 'third_party', 'catapult', 'tracing')) - # The low_bandwidth_audio_perf_test gn rule will build the protobuf stub for - # python, so put it in the path for this script before we attempt to import - # it. - histogram_proto_path = os.path.join(os.path.abspath(args.build_dir), - 'pyproto', 'tracing', 'tracing', - 'proto') - sys.path.insert(0, histogram_proto_path) - proto_stub_path = os.path.join(os.path.abspath(args.build_dir), 'pyproto') - sys.path.insert(0, proto_stub_path) + # The low_bandwidth_audio_perf_test gn rule will build the protobuf stub + # for python, so put it in the path for this script before we attempt to + # import it. + histogram_proto_path = os.path.join(os.path.abspath(args.build_dir), + 'pyproto', 'tracing', 'tracing', 'proto') + sys.path.insert(0, histogram_proto_path) + proto_stub_path = os.path.join(os.path.abspath(args.build_dir), 'pyproto') + sys.path.insert(0, proto_stub_path) - # Fail early in case the proto hasn't been built. - try: - import histogram_pb2 - except ImportError as e: - logging.exception(e) - raise ImportError( - 'Could not import histogram_pb2. You need to build the ' - 'low_bandwidth_audio_perf_test target before invoking ' - 'this script. Expected to find ' - 'histogram_pb2.py in %s.' % histogram_proto_path) + # Fail early in case the proto hasn't been built. + try: + #pylint: disable=unused-import + import histogram_pb2 + except ImportError as e: + raise ImportError('Could not import histogram_pb2. You need to build the ' + 'low_bandwidth_audio_perf_test target before invoking ' + 'this script. Expected to find ' + 'histogram_pb2.py in %s.' % histogram_proto_path) from e def main(): - # pylint: disable=W0101 - logging.basicConfig(level=logging.INFO) - logging.info('Invoked with %s', str(sys.argv)) + logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', + level=logging.INFO, + datefmt='%Y-%m-%d %H:%M:%S') + logging.info('Invoked with %s', str(sys.argv)) - args = _ParseArgs() + args, extra_test_args = _ParseArgs() - _ConfigurePythonPath(args) + _ConfigurePythonPath(args) - # Import catapult modules here after configuring the pythonpath. - from tracing.value import histogram_set - from tracing.value.diagnostics import reserved_infos - from tracing.value.diagnostics import generic_set + # Import catapult modules here after configuring the pythonpath. + from tracing.value import histogram_set + from tracing.value.diagnostics import reserved_infos + from tracing.value.diagnostics import generic_set - pesq_path, polqa_path = _GetPathToTools() - if pesq_path is None: - return 1 + pesq_path, polqa_path = _GetPathToTools() + if pesq_path is None: + return 1 - out_dir = os.path.join(args.build_dir, '..') - if args.android: - test_command = [ - os.path.join(args.build_dir, 'bin', - 'run_low_bandwidth_audio_test'), '-v', - '--num-retries', args.num_retries - ] - else: - test_command = [ - os.path.join(args.build_dir, 'low_bandwidth_audio_test') - ] + out_dir = os.path.join(args.build_dir, '..') + if args.android: + test_command = [ + os.path.join(args.build_dir, 'bin', 'run_low_bandwidth_audio_test'), + '-v', '--num-retries', args.num_retries + ] + else: + test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')] - analyzers = [Analyzer('pesq', _RunPesq, pesq_path, 16000)] - # Check if POLQA can run at all, or skip the 48 kHz tests entirely. - example_path = os.path.join(SRC_DIR, 'resources', 'voice_engine', - 'audio_tiny48.wav') - if polqa_path and _RunPolqa(polqa_path, example_path, example_path): - analyzers.append(Analyzer('polqa', _RunPolqa, polqa_path, 48000)) + analyzers = [Analyzer('pesq', _RunPesq, pesq_path, 16000)] + # Check if POLQA can run at all, or skip the 48 kHz tests entirely. + example_path = os.path.join(SRC_DIR, 'resources', 'voice_engine', + 'audio_tiny48.wav') + if polqa_path and _RunPolqa(polqa_path, example_path, example_path): + analyzers.append(Analyzer('polqa', _RunPolqa, polqa_path, 48000)) - histograms = histogram_set.HistogramSet() - for analyzer in analyzers: - # Start the test executable that produces audio files. - test_process = subprocess.Popen(_LogCommand(test_command + [ - '--sample_rate_hz=%d' % analyzer.sample_rate_hz, - '--test_case_prefix=%s' % analyzer.name, - ] + args.extra_test_args), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - perf_results_file = None - try: - lines = iter(test_process.stdout.readline, '') - for result in ExtractTestRuns(lines, echo=True): - (android_device, test_name, reference_file, degraded_file, - perf_results_file) = result + histograms = histogram_set.HistogramSet() + for analyzer in analyzers: + # Start the test executable that produces audio files. + test_process = subprocess.Popen(_LogCommand(test_command + [ + '--sample_rate_hz=%d' % analyzer.sample_rate_hz, + '--test_case_prefix=%s' % analyzer.name, + ] + extra_test_args), + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + perf_results_file = None + try: + lines = iter(test_process.stdout.readline, '') + for result in ExtractTestRuns(lines, echo=True): + (android_device, test_name, reference_file, degraded_file, + perf_results_file) = result - adb_prefix = (args.adb_path, ) - if android_device: - adb_prefix += ('-s', android_device) + adb_prefix = (args.adb_path, ) + if android_device: + adb_prefix += ('-s', android_device) - reference_file = _GetFile(reference_file, - out_dir, - android=args.android, - adb_prefix=adb_prefix) - degraded_file = _GetFile(degraded_file, - out_dir, - move=True, - android=args.android, - adb_prefix=adb_prefix) + reference_file = _GetFile(reference_file, + out_dir, + android=args.android, + adb_prefix=adb_prefix) + degraded_file = _GetFile(degraded_file, + out_dir, + move=True, + android=args.android, + adb_prefix=adb_prefix) - analyzer_results = analyzer.func(analyzer.executable, - reference_file, degraded_file) - for metric, (value, units) in analyzer_results.items(): - hist = histograms.CreateHistogram(metric, units, [value]) - user_story = generic_set.GenericSet([test_name]) - hist.diagnostics[reserved_infos.STORIES.name] = user_story + analyzer_results = analyzer.func(analyzer.executable, reference_file, + degraded_file) + for metric, (value, units) in list(analyzer_results.items()): + hist = histograms.CreateHistogram(metric, units, [value]) + user_story = generic_set.GenericSet([test_name]) + hist.diagnostics[reserved_infos.STORIES.name] = user_story - # Output human readable results. - print 'RESULT %s: %s= %s %s' % (metric, test_name, value, - units) + # Output human readable results. + print('RESULT %s: %s= %s %s' % (metric, test_name, value, units)) - if args.remove: - os.remove(reference_file) - os.remove(degraded_file) - finally: - test_process.terminate() - if perf_results_file: - perf_results_file = _GetFile(perf_results_file, - out_dir, - move=True, - android=args.android, - adb_prefix=adb_prefix) - _MergeInPerfResultsFromCcTests(histograms, perf_results_file) - if args.remove: - os.remove(perf_results_file) + if args.remove: + os.remove(reference_file) + os.remove(degraded_file) + finally: + test_process.terminate() + if perf_results_file: + perf_results_file = _GetFile(perf_results_file, + out_dir, + move=True, + android=args.android, + adb_prefix=adb_prefix) + _MergeInPerfResultsFromCcTests(histograms, perf_results_file) + if args.remove: + os.remove(perf_results_file) - if args.isolated_script_test_perf_output: - with open(args.isolated_script_test_perf_output, 'wb') as f: - f.write(histograms.AsProto().SerializeToString()) + if args.isolated_script_test_perf_output: + with open(args.isolated_script_test_perf_output, 'wb') as f: + f.write(histograms.AsProto().SerializeToString()) - return test_process.wait() + if args.isolated_script_test_output: + with open(args.isolated_script_test_output, 'w') as f: + json.dump({"version": 3}, f) + + return test_process.wait() if __name__ == '__main__': - sys.exit(main()) + sys.exit(main()) diff --git a/audio/test/pc_low_bandwidth_audio_test.cc b/audio/test/pc_low_bandwidth_audio_test.cc index 95a32238c5..0364670b91 100644 --- a/audio/test/pc_low_bandwidth_audio_test.cc +++ b/audio/test/pc_low_bandwidth_audio_test.cc @@ -12,6 +12,7 @@ #include "absl/flags/declare.h" #include "absl/flags/flag.h" +#include "absl/strings/string_view.h" #include "api/test/create_network_emulation_manager.h" #include "api/test/create_peerconnection_quality_test_fixture.h" #include "api/test/network_emulation_manager.h" @@ -71,19 +72,20 @@ CreateTwoNetworkLinks(NetworkEmulationManager* emulation, } std::unique_ptr -CreateTestFixture(const std::string& test_case_name, +CreateTestFixture(absl::string_view test_case_name, TimeController& time_controller, std::pair network_links, rtc::FunctionView alice_configurer, rtc::FunctionView bob_configurer) { auto fixture = webrtc_pc_e2e::CreatePeerConnectionE2EQualityTestFixture( - test_case_name, time_controller, /*audio_quality_analyzer=*/nullptr, + std::string(test_case_name), time_controller, + /*audio_quality_analyzer=*/nullptr, /*video_quality_analyzer=*/nullptr); - fixture->AddPeer(network_links.first->network_thread(), - network_links.first->network_manager(), alice_configurer); - fixture->AddPeer(network_links.second->network_thread(), - network_links.second->network_manager(), bob_configurer); + fixture->AddPeer(network_links.first->network_dependencies(), + alice_configurer); + fixture->AddPeer(network_links.second->network_dependencies(), + bob_configurer); fixture->AddQualityMetricsReporter( std::make_unique( network_links.first, network_links.second)); diff --git a/audio/utility/audio_frame_operations.cc b/audio/utility/audio_frame_operations.cc index 8f3f37a037..1b936c239b 100644 --- a/audio/utility/audio_frame_operations.cc +++ b/audio/utility/audio_frame_operations.cc @@ -131,8 +131,8 @@ void AudioFrameOperations::DownmixChannels(const int16_t* src_audio, return; } - RTC_NOTREACHED() << "src_channels: " << src_channels - << ", dst_channels: " << dst_channels; + RTC_DCHECK_NOTREACHED() << "src_channels: " << src_channels + << ", dst_channels: " << dst_channels; } void AudioFrameOperations::DownmixChannels(size_t dst_channels, @@ -149,8 +149,8 @@ void AudioFrameOperations::DownmixChannels(size_t dst_channels, int err = QuadToStereo(frame); RTC_DCHECK_EQ(err, 0); } else { - RTC_NOTREACHED() << "src_channels: " << frame->num_channels_ - << ", dst_channels: " << dst_channels; + RTC_DCHECK_NOTREACHED() << "src_channels: " << frame->num_channels_ + << ", dst_channels: " << dst_channels; } } diff --git a/audio/voip/test/audio_egress_unittest.cc b/audio/voip/test/audio_egress_unittest.cc index 0692ef2df4..d114253c15 100644 --- a/audio/voip/test/audio_egress_unittest.cc +++ b/audio/voip/test/audio_egress_unittest.cc @@ -57,7 +57,6 @@ class AudioEgressTest : public ::testing::Test { AudioEgressTest() : fake_clock_(kStartTime), wave_generator_(1000.0, kAudioLevel) { - rtp_rtcp_ = CreateRtpStack(&fake_clock_, &transport_, kRemoteSsrc); task_queue_factory_ = CreateDefaultTaskQueueFactory(); encoder_factory_ = CreateBuiltinAudioEncoderFactory(); } @@ -65,6 +64,7 @@ class AudioEgressTest : public ::testing::Test { // Prepare test on audio egress by using PCMu codec with specific // sequence number and its status to be running. void SetUp() override { + rtp_rtcp_ = CreateRtpStack(&fake_clock_, &transport_, kRemoteSsrc); egress_ = std::make_unique(rtp_rtcp_.get(), &fake_clock_, task_queue_factory_.get()); constexpr int kPcmuPayload = 0; @@ -81,6 +81,7 @@ class AudioEgressTest : public ::testing::Test { egress_->StopSend(); rtp_rtcp_->SetSendingStatus(false); egress_.reset(); + rtp_rtcp_.reset(); } // Create an audio frame prepared for pcmu encoding. Timestamp is diff --git a/audio/voip/voip_core.h b/audio/voip/voip_core.h index 439393585c..6c3aec6fa2 100644 --- a/audio/voip/voip_core.h +++ b/audio/voip/voip_core.h @@ -53,9 +53,6 @@ class VoipCore : public VoipEngine, public VoipVolumeControl { public: // Construct VoipCore with provided arguments. - // ProcessThread implementation can be injected by `process_thread` - // (mainly for testing purpose) and when set to nullptr, default - // implementation will be used. VoipCore(rtc::scoped_refptr encoder_factory, rtc::scoped_refptr decoder_factory, std::unique_ptr task_queue_factory, diff --git a/call/BUILD.gn b/call/BUILD.gn index 87c0ec028a..cb85febcaf 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -55,6 +55,7 @@ rtc_library("call_interfaces") { "../api/audio_codecs:audio_codecs_api", "../api/crypto:frame_encryptor_interface", "../api/crypto:options", + "../api/metronome", "../api/neteq:neteq_api", "../api/task_queue", "../api/transport:bitrate_settings", @@ -150,7 +151,10 @@ rtc_library("rtp_receiver") { "../rtc_base/containers:flat_map", "../rtc_base/containers:flat_set", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("rtp_sender") { @@ -174,11 +178,11 @@ rtc_library("rtp_sender") { "../api:rtp_parameters", "../api:sequence_checker", "../api:transport_api", + "../api:webrtc_key_value_config", "../api/rtc_event_log", "../api/transport:field_trial_based_config", "../api/transport:goog_cc", "../api/transport:network_control", - "../api/transport:webrtc_key_value_config", "../api/units:data_rate", "../api/units:time_delta", "../api/units:timestamp", @@ -287,6 +291,7 @@ rtc_library("call") { "../api:sequence_checker", "../api:simulated_network_api", "../api:transport_api", + "../api:webrtc_key_value_config", "../api/rtc_event_log", "../api/transport:network_control", "../api/units:time_delta", @@ -315,6 +320,7 @@ rtc_library("call") { "../system_wrappers:field_trial", "../system_wrappers:metrics", "../video", + "../video:decode_synchronizer", "adaptation:resource_adaptation", ] absl_deps = [ @@ -362,6 +368,7 @@ rtc_library("video_stream_api") { "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", "../rtc_base:rtc_base_approved", + "../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -485,6 +492,7 @@ if (rtc_include_tests) { "../test:field_trial", "../test:mock_frame_transformer", "../test:mock_transport", + "../test:scoped_key_value_config", "../test:test_common", "../test:test_support", "../test:video_test_common", @@ -498,6 +506,7 @@ if (rtc_include_tests) { absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", "//third_party/abseil-cpp/absl/types:variant", ] diff --git a/call/OWNERS b/call/OWNERS index f863b939bc..48b403d95a 100644 --- a/call/OWNERS +++ b/call/OWNERS @@ -3,3 +3,5 @@ stefan@webrtc.org srte@webrtc.org terelius@webrtc.org sprang@webrtc.org + +per-file version.cc=webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com diff --git a/call/adaptation/BUILD.gn b/call/adaptation/BUILD.gn index 10a46a3d43..4661c3e2f6 100644 --- a/call/adaptation/BUILD.gn +++ b/call/adaptation/BUILD.gn @@ -35,6 +35,7 @@ rtc_library("resource_adaptation") { "../../api:rtp_parameters", "../../api:scoped_refptr", "../../api:sequence_checker", + "../../api:webrtc_key_value_config", "../../api/adaptation:resource_adaptation_api", "../../api/task_queue:task_queue", "../../api/video:video_adaptation", @@ -86,6 +87,7 @@ if (rtc_include_tests) { "../../rtc_base/synchronization:mutex", "../../test:field_trial", "../../test:rtc_expect_death", + "../../test:scoped_key_value_config", "../../test:test_support", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] diff --git a/call/adaptation/OWNERS b/call/adaptation/OWNERS index b65c763efc..bd56595d2e 100644 --- a/call/adaptation/OWNERS +++ b/call/adaptation/OWNERS @@ -1,3 +1,3 @@ -eshr@google.com +eshr@webrtc.org hbos@webrtc.org ilnik@webrtc.org diff --git a/call/adaptation/broadcast_resource_listener.cc b/call/adaptation/broadcast_resource_listener.cc index 876d4c0bf6..5a1250ae80 100644 --- a/call/adaptation/broadcast_resource_listener.cc +++ b/call/adaptation/broadcast_resource_listener.cc @@ -32,7 +32,8 @@ class BroadcastResourceListener::AdapterResource : public Resource { MutexLock lock(&lock_); if (!listener_) return; - listener_->OnResourceUsageStateMeasured(this, usage_state); + listener_->OnResourceUsageStateMeasured(rtc::scoped_refptr(this), + usage_state); } // Resource implementation. diff --git a/call/adaptation/resource_adaptation_processor.cc b/call/adaptation/resource_adaptation_processor.cc index 3c06675a16..66e6f0c36e 100644 --- a/call/adaptation/resource_adaptation_processor.cc +++ b/call/adaptation/resource_adaptation_processor.cc @@ -27,14 +27,8 @@ namespace webrtc { ResourceAdaptationProcessor::ResourceListenerDelegate::ResourceListenerDelegate( ResourceAdaptationProcessor* processor) - : task_queue_(nullptr), processor_(processor) {} - -void ResourceAdaptationProcessor::ResourceListenerDelegate::SetTaskQueue( - TaskQueueBase* task_queue) { - RTC_DCHECK(!task_queue_); - RTC_DCHECK(task_queue); - task_queue_ = task_queue; - RTC_DCHECK_RUN_ON(task_queue_); + : task_queue_(TaskQueueBase::Current()), processor_(processor) { + RTC_DCHECK(task_queue_); } void ResourceAdaptationProcessor::ResourceListenerDelegate:: @@ -70,14 +64,15 @@ ResourceAdaptationProcessor::MitigationResultAndLogMessage:: ResourceAdaptationProcessor::ResourceAdaptationProcessor( VideoStreamAdapter* stream_adapter) - : task_queue_(nullptr), + : task_queue_(TaskQueueBase::Current()), resource_listener_delegate_( rtc::make_ref_counted(this)), resources_(), stream_adapter_(stream_adapter), last_reported_source_restrictions_(), previous_mitigation_results_() { - RTC_DCHECK(stream_adapter_); + RTC_DCHECK(task_queue_); + stream_adapter_->AddRestrictionsListener(this); } ResourceAdaptationProcessor::~ResourceAdaptationProcessor() { @@ -89,16 +84,6 @@ ResourceAdaptationProcessor::~ResourceAdaptationProcessor() { resource_listener_delegate_->OnProcessorDestroyed(); } -void ResourceAdaptationProcessor::SetTaskQueue(TaskQueueBase* task_queue) { - RTC_DCHECK(!task_queue_); - RTC_DCHECK(task_queue); - task_queue_ = task_queue; - resource_listener_delegate_->SetTaskQueue(task_queue); - RTC_DCHECK_RUN_ON(task_queue_); - // Now that we have the queue we can attach as adaptation listener. - stream_adapter_->AddRestrictionsListener(this); -} - void ResourceAdaptationProcessor::AddResourceLimitationsListener( ResourceLimitationsListener* limitations_listener) { RTC_DCHECK_RUN_ON(task_queue_); @@ -129,7 +114,7 @@ void ResourceAdaptationProcessor::AddResource( resources_.push_back(resource); } resource->SetResourceListener(resource_listener_delegate_); - RTC_LOG(INFO) << "Registered resource \"" << resource->Name() << "\"."; + RTC_LOG(LS_INFO) << "Registered resource \"" << resource->Name() << "\"."; } std::vector> @@ -141,7 +126,7 @@ ResourceAdaptationProcessor::GetResources() const { void ResourceAdaptationProcessor::RemoveResource( rtc::scoped_refptr resource) { RTC_DCHECK(resource); - RTC_LOG(INFO) << "Removing resource \"" << resource->Name() << "\"."; + RTC_LOG(LS_INFO) << "Removing resource \"" << resource->Name() << "\"."; resource->SetResourceListener(nullptr); { MutexLock crit(&resources_lock_); @@ -188,10 +173,11 @@ void ResourceAdaptationProcessor::RemoveLimitationsImposedByResource( RTC_DCHECK_EQ(adapt_to.status(), Adaptation::Status::kValid); stream_adapter_->ApplyAdaptation(adapt_to, nullptr); - RTC_LOG(INFO) << "Most limited resource removed. Restoring restrictions to " - "next most limited restrictions: " - << most_limited.restrictions.ToString() << " with counters " - << most_limited.counters.ToString(); + RTC_LOG(LS_INFO) + << "Most limited resource removed. Restoring restrictions to " + "next most limited restrictions: " + << most_limited.restrictions.ToString() << " with counters " + << most_limited.counters.ToString(); } } @@ -204,8 +190,8 @@ void ResourceAdaptationProcessor::OnResourceUsageStateMeasured( { MutexLock crit(&resources_lock_); if (absl::c_find(resources_, resource) == resources_.end()) { - RTC_LOG(INFO) << "Ignoring signal from removed resource \"" - << resource->Name() << "\"."; + RTC_LOG(LS_INFO) << "Ignoring signal from removed resource \"" + << resource->Name() << "\"."; return; } } @@ -226,9 +212,9 @@ void ResourceAdaptationProcessor::OnResourceUsageStateMeasured( // successfully adapted since - don't log to avoid spam. return; } - RTC_LOG(INFO) << "Resource \"" << resource->Name() << "\" signalled " - << ResourceUsageStateToString(usage_state) << ". " - << result_and_message.message; + RTC_LOG(LS_INFO) << "Resource \"" << resource->Name() << "\" signalled " + << ResourceUsageStateToString(usage_state) << ". " + << result_and_message.message; if (result_and_message.result == MitigationResult::kAdaptationApplied) { previous_mitigation_results_.clear(); } else { diff --git a/call/adaptation/resource_adaptation_processor.h b/call/adaptation/resource_adaptation_processor.h index 3e273081f8..ca2fec08c3 100644 --- a/call/adaptation/resource_adaptation_processor.h +++ b/call/adaptation/resource_adaptation_processor.h @@ -58,8 +58,6 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface, VideoStreamAdapter* video_stream_adapter); ~ResourceAdaptationProcessor() override; - void SetTaskQueue(TaskQueueBase* task_queue) override; - // ResourceAdaptationProcessorInterface implementation. void AddResourceLimitationsListener( ResourceLimitationsListener* limitations_listener) override; @@ -90,7 +88,6 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface, public: explicit ResourceListenerDelegate(ResourceAdaptationProcessor* processor); - void SetTaskQueue(TaskQueueBase* task_queue); void OnProcessorDestroyed(); // ResourceListener implementation. diff --git a/call/adaptation/resource_adaptation_processor_interface.h b/call/adaptation/resource_adaptation_processor_interface.h index 8b1f94b73a..4729488150 100644 --- a/call/adaptation/resource_adaptation_processor_interface.h +++ b/call/adaptation/resource_adaptation_processor_interface.h @@ -47,8 +47,6 @@ class ResourceAdaptationProcessorInterface { public: virtual ~ResourceAdaptationProcessorInterface(); - virtual void SetTaskQueue(TaskQueueBase* task_queue) = 0; - virtual void AddResourceLimitationsListener( ResourceLimitationsListener* limitations_listener) = 0; virtual void RemoveResourceLimitationsListener( diff --git a/call/adaptation/resource_adaptation_processor_unittest.cc b/call/adaptation/resource_adaptation_processor_unittest.cc index 7020b22daf..da2bc947b9 100644 --- a/call/adaptation/resource_adaptation_processor_unittest.cc +++ b/call/adaptation/resource_adaptation_processor_unittest.cc @@ -23,6 +23,7 @@ #include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_queue_for_test.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -90,10 +91,10 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { other_resource_(FakeResource::Create("OtherFakeResource")), video_stream_adapter_( std::make_unique(&input_state_provider_, - &frame_rate_provider_)), + &frame_rate_provider_, + field_trials_)), processor_(std::make_unique( video_stream_adapter_.get())) { - processor_->SetTaskQueue(TaskQueueBase::Current()); video_stream_adapter_->AddRestrictionsListener(&restrictions_listener_); processor_->AddResource(resource_); processor_->AddResource(other_resource_); @@ -134,6 +135,7 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { } protected: + webrtc::test::ScopedKeyValueConfig field_trials_; FakeFrameRateProvider frame_rate_provider_; VideoStreamInputStateProvider input_state_provider_; rtc::scoped_refptr resource_; diff --git a/call/adaptation/test/fake_resource.cc b/call/adaptation/test/fake_resource.cc index d125468cb6..70df058a70 100644 --- a/call/adaptation/test/fake_resource.cc +++ b/call/adaptation/test/fake_resource.cc @@ -29,7 +29,8 @@ FakeResource::~FakeResource() {} void FakeResource::SetUsageState(ResourceUsageState usage_state) { if (listener_) { - listener_->OnResourceUsageStateMeasured(this, usage_state); + listener_->OnResourceUsageStateMeasured(rtc::scoped_refptr(this), + usage_state); } } diff --git a/call/adaptation/video_stream_adapter.cc b/call/adaptation/video_stream_adapter.cc index 49a4d6a1ce..313bcc33de 100644 --- a/call/adaptation/video_stream_adapter.cc +++ b/call/adaptation/video_stream_adapter.cc @@ -23,7 +23,6 @@ #include "call/adaptation/video_source_restrictions.h" #include "call/adaptation/video_stream_input_state.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -204,9 +203,11 @@ const VideoAdaptationCounters& Adaptation::counters() const { VideoStreamAdapter::VideoStreamAdapter( VideoStreamInputStateProvider* input_state_provider, - VideoStreamEncoderObserver* encoder_stats_observer) + VideoStreamEncoderObserver* encoder_stats_observer, + const WebRtcKeyValueConfig& field_trials) : input_state_provider_(input_state_provider), encoder_stats_observer_(encoder_stats_observer), + balanced_settings_(field_trials), adaptation_validation_id_(0), degradation_preference_(DegradationPreference::DISABLED), awaiting_frame_size_change_(absl::nullopt) { @@ -234,7 +235,7 @@ const VideoAdaptationCounters& VideoStreamAdapter::adaptation_counters() const { void VideoStreamAdapter::ClearRestrictions() { RTC_DCHECK_RUN_ON(&sequence_checker_); // Invalidate any previously returned Adaptation. - RTC_LOG(INFO) << "Resetting restrictions"; + RTC_LOG(LS_INFO) << "Resetting restrictions"; ++adaptation_validation_id_; current_restrictions_ = {VideoSourceRestrictions(), VideoAdaptationCounters()}; @@ -333,8 +334,8 @@ Adaptation VideoStreamAdapter::GetAdaptationUp( if (!constraint->IsAdaptationUpAllowed(input_state, current_restrictions_.restrictions, restrictions.restrictions)) { - RTC_LOG(INFO) << "Not adapting up because constraint \"" - << constraint->Name() << "\" disallowed it"; + RTC_LOG(LS_INFO) << "Not adapting up because constraint \"" + << constraint->Name() << "\" disallowed it"; step = Adaptation::Status::kRejectedByConstraint; } } @@ -375,7 +376,7 @@ VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::GetAdaptationUpStep( return increase_frame_rate; } // else, increase resolution. - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; } case DegradationPreference::MAINTAIN_FRAMERATE: { // Attempt to increase pixel count. @@ -459,7 +460,7 @@ VideoStreamAdapter::GetAdaptationDownStep( return decrease_frame_rate; } // else, decrease resolution. - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; } case DegradationPreference::MAINTAIN_FRAMERATE: { return DecreaseResolution(input_state, current_restrictions); @@ -509,7 +510,7 @@ VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::DecreaseFramerate( max_frame_rate = balanced_settings_.MinFps(input_state.video_codec_type(), frame_size_pixels); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); max_frame_rate = GetLowerFrameRateThan(input_state.frames_per_second()); } if (!CanDecreaseFrameRateTo(max_frame_rate, @@ -584,7 +585,7 @@ VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseFramerate( return Adaptation::Status::kLimitReached; } } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); max_frame_rate = GetHigherFrameRateThan(input_state.frames_per_second()); } if (current_restrictions.counters.fps_adaptations == 1) { diff --git a/call/adaptation/video_stream_adapter.h b/call/adaptation/video_stream_adapter.h index 7bf424a17e..a4a52f400a 100644 --- a/call/adaptation/video_stream_adapter.h +++ b/call/adaptation/video_stream_adapter.h @@ -21,6 +21,7 @@ #include "api/rtp_parameters.h" #include "api/video/video_adaptation_counters.h" #include "api/video/video_stream_encoder_observer.h" +#include "api/webrtc_key_value_config.h" #include "call/adaptation/adaptation_constraint.h" #include "call/adaptation/degradation_preference_provider.h" #include "call/adaptation/video_source_restrictions.h" @@ -123,7 +124,8 @@ class Adaptation final { class VideoStreamAdapter { public: VideoStreamAdapter(VideoStreamInputStateProvider* input_state_provider, - VideoStreamEncoderObserver* encoder_stats_observer); + VideoStreamEncoderObserver* encoder_stats_observer, + const WebRtcKeyValueConfig& field_trials); ~VideoStreamAdapter(); VideoSourceRestrictions source_restrictions() const; diff --git a/call/adaptation/video_stream_adapter_unittest.cc b/call/adaptation/video_stream_adapter_unittest.cc index aba9cf1f29..8f29194254 100644 --- a/call/adaptation/video_stream_adapter_unittest.cc +++ b/call/adaptation/video_stream_adapter_unittest.cc @@ -27,9 +27,9 @@ #include "call/adaptation/video_source_restrictions.h" #include "call/adaptation/video_stream_input_state.h" #include "rtc_base/string_encode.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "test/testsupport/rtc_expect_death.h" namespace webrtc { @@ -153,10 +153,12 @@ class VideoStreamAdapterTest : public ::testing::Test { VideoStreamAdapterTest() : field_trials_(BalancedFieldTrialConfig()), resource_(FakeResource::Create("FakeResource")), - adapter_(&input_state_provider_, &encoder_stats_observer_) {} + adapter_(&input_state_provider_, + &encoder_stats_observer_, + field_trials_) {} protected: - webrtc::test::ScopedFieldTrials field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; FakeVideoStreamInputStateProvider input_state_provider_; rtc::scoped_refptr resource_; testing::StrictMock encoder_stats_observer_; @@ -919,9 +921,11 @@ TEST_F(VideoStreamAdapterTest, AdaptationConstraintDisallowsAdaptationsUp) { TEST(VideoStreamAdapterDeathTest, SetDegradationPreferenceInvalidatesAdaptations) { + webrtc::test::ScopedKeyValueConfig field_trials; FakeVideoStreamInputStateProvider input_state_provider; testing::StrictMock encoder_stats_observer_; - VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_); + VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, + field_trials); adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); Adaptation adaptation = adapter.GetAdaptationDown(); @@ -930,9 +934,11 @@ TEST(VideoStreamAdapterDeathTest, } TEST(VideoStreamAdapterDeathTest, AdaptDownInvalidatesAdaptations) { + webrtc::test::ScopedKeyValueConfig field_trials; FakeVideoStreamInputStateProvider input_state_provider; testing::StrictMock encoder_stats_observer_; - VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_); + VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, + field_trials); adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); Adaptation adaptation = adapter.GetAdaptationDown(); diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h index 0fde4d4f8a..3399902b5a 100644 --- a/call/audio_receive_stream.h +++ b/call/audio_receive_stream.h @@ -23,6 +23,7 @@ #include "api/rtp_parameters.h" #include "call/receive_stream.h" #include "call/rtp_config.h" +#include "rtc_base/logging.h" namespace webrtc { class AudioSinkInterface; @@ -195,7 +196,10 @@ class AudioReceiveStream : public MediaReceiveStream { virtual int GetBaseMinimumPlayoutDelayMs() const = 0; // RingRTC change to get recv audio levels - virtual uint16_t GetAudioLevel() = 0; + virtual uint16_t GetAudioLevel() { + RTC_LOG(LS_WARNING) << "Default AudioReceiveStream::GetAudioLevel() does nothing!"; + return 0; + } protected: virtual ~AudioReceiveStream() {} diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc index c789d672d1..7414bff9ad 100644 --- a/call/bitrate_allocator.cc +++ b/call/bitrate_allocator.cc @@ -24,7 +24,6 @@ #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_minmax.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace webrtc { diff --git a/call/bitrate_estimator_tests.cc b/call/bitrate_estimator_tests.cc index 4634f6e147..424cf0b038 100644 --- a/call/bitrate_estimator_tests.cc +++ b/call/bitrate_estimator_tests.cc @@ -12,6 +12,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/test/create_frame_generator.h" #include "call/call.h" #include "call/fake_network_pipe.h" @@ -50,13 +51,17 @@ class LogObserver { class Callback : public rtc::LogSink { public: void OnLogMessage(const std::string& message) override { + OnLogMessage(absl::string_view(message)); + } + + void OnLogMessage(absl::string_view message) override { MutexLock lock(&mutex_); // Ignore log lines that are due to missing AST extensions, these are // logged when we switch back from AST to TOF until the wrapping bitrate // estimator gives up on using AST. - if (message.find("BitrateEstimator") != std::string::npos && - message.find("packet is missing") == std::string::npos) { - received_log_lines_.push_back(message); + if (message.find("BitrateEstimator") != absl::string_view::npos && + message.find("packet is missing") == absl::string_view::npos) { + received_log_lines_.push_back(std::string(message)); } int num_popped = 0; @@ -66,7 +71,7 @@ class LogObserver { received_log_lines_.pop_front(); expected_log_lines_.pop_front(); num_popped++; - EXPECT_TRUE(a.find(b) != std::string::npos) << a << " != " << b; + EXPECT_TRUE(a.find(b) != absl::string_view::npos) << a << " != " << b; } if (expected_log_lines_.empty()) { if (num_popped > 0) { diff --git a/call/call.cc b/call/call.cc index 39fb3cfac9..f2101cfc6b 100644 --- a/call/call.cc +++ b/call/call.cc @@ -51,7 +51,6 @@ #include "modules/utility/include/process_thread.h" #include "modules/video_coding/fec_controller_default.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" @@ -62,7 +61,6 @@ #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/cpu_info.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" #include "video/call_stats2.h" #include "video/send_delay_stats.h" @@ -209,6 +207,9 @@ class Call final : public webrtc::Call, TaskQueueFactory* task_queue_factory); ~Call() override; + Call(const Call&) = delete; + Call& operator=(const Call&) = delete; + // Implements webrtc::Call. PacketReceiver* Receiver() override; @@ -344,12 +345,21 @@ class Call final : public webrtc::Call, DeliveryStatus DeliverRtp(MediaType media_type, rtc::CopyOnWriteBuffer packet, int64_t packet_time_us) RTC_RUN_ON(worker_thread_); + + AudioReceiveStream* FindAudioStreamForSyncGroup(const std::string& sync_group) + RTC_RUN_ON(worker_thread_); void ConfigureSync(const std::string& sync_group) RTC_RUN_ON(worker_thread_); void NotifyBweOfReceivedPacket(const RtpPacketReceived& packet, - MediaType media_type) + MediaType media_type, + bool use_send_side_bwe) RTC_RUN_ON(worker_thread_); + bool IdentifyReceivedPacket(RtpPacketReceived& packet, + bool* use_send_side_bwe = nullptr); + bool RegisterReceiveStream(uint32_t ssrc, ReceiveStream* stream); + bool UnregisterReceiveStream(uint32_t ssrc); + void UpdateAggregateNetworkState(); // Ensure that necessary process threads are started, and any required @@ -360,6 +370,7 @@ class Call final : public webrtc::Call, TaskQueueFactory* const task_queue_factory_; TaskQueueBase* const worker_thread_; TaskQueueBase* const network_thread_; + const std::unique_ptr decode_sync_; RTC_NO_UNIQUE_ADDRESS SequenceChecker send_transport_sequence_checker_; const int num_cpu_cores_; @@ -382,14 +393,11 @@ class Call final : public webrtc::Call, // Audio, Video, and FlexFEC receive streams are owned by the client that // creates them. // TODO(bugs.webrtc.org/11993): Move audio_receive_streams_, - // video_receive_streams_ and sync_stream_mapping_ over to the network thread. + // video_receive_streams_ over to the network thread. std::set audio_receive_streams_ RTC_GUARDED_BY(worker_thread_); std::set video_receive_streams_ RTC_GUARDED_BY(worker_thread_); - std::map sync_stream_mapping_ - RTC_GUARDED_BY(worker_thread_); - // TODO(nisse): Should eventually be injected at creation, // with a single object in the bundled case. RtpStreamReceiverController audio_receiver_controller_ @@ -400,10 +408,12 @@ class Call final : public webrtc::Call, // This extra map is used for receive processing which is // independent of media type. + RTC_NO_UNIQUE_ADDRESS SequenceChecker receive_11993_checker_; + // TODO(bugs.webrtc.org/11993): Move receive_rtp_config_ over to the // network thread. std::map receive_rtp_config_ - RTC_GUARDED_BY(worker_thread_); + RTC_GUARDED_BY(&receive_11993_checker_); // Audio and Video send streams are owned by the client that creates them. std::map audio_send_ssrcs_ @@ -466,7 +476,11 @@ class Call final : public webrtc::Call, bool is_started_ RTC_GUARDED_BY(worker_thread_) = false; - RTC_DISALLOW_COPY_AND_ASSIGN(Call); + // Sequence checker for outgoing network traffic. Could be the network thread. + // Could also be a pacer owned thread or TQ such as the TaskQueuePacedSender. + RTC_NO_UNIQUE_ADDRESS SequenceChecker sent_packet_sequence_checker_; + absl::optional last_sent_packet_ + RTC_GUARDED_BY(sent_packet_sequence_checker_); }; } // namespace internal @@ -587,8 +601,9 @@ SharedModuleThread::~SharedModuleThread() = default; rtc::scoped_refptr SharedModuleThread::Create( std::unique_ptr process_thread, std::function on_one_ref_remaining) { - return new SharedModuleThread(std::move(process_thread), - std::move(on_one_ref_remaining)); + // Using `new` to access a non-public constructor. + return rtc::scoped_refptr(new SharedModuleThread( + std::move(process_thread), std::move(on_one_ref_remaining))); } void SharedModuleThread::EnsureStarted() { @@ -785,6 +800,11 @@ Call::Call(Clock* clock, // must be made on `worker_thread_` (i.e. they're one and the same). network_thread_(config.network_task_queue_ ? config.network_task_queue_ : worker_thread_), + decode_sync_(config.metronome + ? std::make_unique(clock_, + config.metronome, + worker_thread_) + : nullptr), num_cpu_cores_(CpuInfo::DetectNumberOfCores()), module_process_thread_(std::move(module_process_thread)), call_stats_(new CallStats(clock_, worker_thread_)), @@ -803,7 +823,8 @@ Call::Call(Clock* clock, absl::bind_front(&PacketRouter::SendRemb, transport_send->packet_router()), /*network_state_estimator=*/nullptr), - receive_time_calculator_(ReceiveTimeCalculator::CreateFromFieldTrial()), + receive_time_calculator_( + ReceiveTimeCalculator::CreateFromFieldTrial(*config.trials)), video_send_delay_stats_(new SendDelayStats(clock_)), start_of_call_(clock_->CurrentTime()), transport_send_ptr_(transport_send.get()), @@ -813,7 +834,9 @@ Call::Call(Clock* clock, RTC_DCHECK(network_thread_); RTC_DCHECK(worker_thread_->IsCurrent()); + receive_11993_checker_.Detach(); send_transport_sequence_checker_.Detach(); + sent_packet_sequence_checker_.Detach(); // Do not remove this call; it is here to convince the compiler that the // WebRTC source timestamp string needs to be in the final binary. @@ -892,7 +915,7 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream( AudioSendStream* send_stream = new AudioSendStream( clock_, config, config_.audio_state, task_queue_factory_, transport_send_.get(), bitrate_allocator_.get(), event_log_, - call_stats_->AsRtcpRttStats(), suspended_rtp_state); + call_stats_->AsRtcpRttStats(), suspended_rtp_state, trials()); RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) == audio_send_ssrcs_.end()); audio_send_ssrcs_[config.rtp.ssrc] = send_stream; @@ -959,7 +982,7 @@ webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream( // TODO(bugs.webrtc.org/11993): Update the below on the network thread. // We could possibly set up the audio_receiver_controller_ association up // as part of the async setup. - receive_rtp_config_.emplace(config.rtp.remote_ssrc, receive_stream); + RegisterReceiveStream(config.rtp.remote_ssrc, receive_stream); ConfigureSync(config.sync_group); @@ -992,12 +1015,12 @@ void Call::DestroyAudioReceiveStream( audio_receive_streams_.erase(audio_receive_stream); - const auto it = sync_stream_mapping_.find(config.sync_group); - if (it != sync_stream_mapping_.end() && it->second == audio_receive_stream) { - sync_stream_mapping_.erase(it); - ConfigureSync(config.sync_group); - } - receive_rtp_config_.erase(ssrc); + // After calling erase(), call ConfigureSync. This will clear associated + // video streams or associate them with a different audio stream if one exists + // for this sync_group. + ConfigureSync(audio_receive_stream->config().sync_group); + + UnregisterReceiveStream(ssrc); UpdateAggregateNetworkState(); // TODO(bugs.webrtc.org/11993): Consider if deleting `audio_receive_stream` @@ -1029,11 +1052,12 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( std::vector ssrcs = config.rtp.ssrcs; VideoSendStream* send_stream = new VideoSendStream( - clock_, num_cpu_cores_, task_queue_factory_, + clock_, num_cpu_cores_, task_queue_factory_, network_thread_, call_stats_->AsRtcpRttStats(), transport_send_.get(), bitrate_allocator_.get(), video_send_delay_stats_.get(), event_log_, std::move(config), std::move(encoder_config), suspended_video_send_ssrcs_, - suspended_video_payload_states_, std::move(fec_controller)); + suspended_video_payload_states_, std::move(fec_controller), + *config_.trials); for (uint32_t ssrc : ssrcs) { RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); @@ -1074,10 +1098,6 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { VideoSendStream* send_stream_impl = static_cast(send_stream); - VideoSendStream::RtpStateMap rtp_states; - VideoSendStream::RtpPayloadStateMap rtp_payload_states; - send_stream_impl->StopPermanentlyAndGetRtpStates(&rtp_states, - &rtp_payload_states); auto it = video_send_ssrcs_.begin(); while (it != video_send_ssrcs_.end()) { @@ -1097,6 +1117,10 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { if (video_send_streams_.empty()) video_send_streams_empty_.store(true, std::memory_order_relaxed); + VideoSendStream::RtpStateMap rtp_states; + VideoSendStream::RtpPayloadStateMap rtp_payload_states; + send_stream_impl->StopPermanentlyAndGetRtpStates(&rtp_states, + &rtp_payload_states); for (const auto& kv : rtp_states) { suspended_video_send_ssrcs_[kv.first] = kv.second; } @@ -1131,7 +1155,7 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( task_queue_factory_, this, num_cpu_cores_, transport_send_->packet_router(), std::move(configuration), call_stats_.get(), clock_, new VCMTiming(clock_), - &nack_periodic_processor_); + &nack_periodic_processor_, decode_sync_.get()); // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // thread. receive_stream->RegisterWithTransport(&video_receiver_controller_); @@ -1142,9 +1166,9 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( // stream. Since the transport_send_cc negotiation is per payload // type, we may get an incorrect value for the rtx stream, but // that is unlikely to matter in practice. - receive_rtp_config_.emplace(rtp.rtx_ssrc, receive_stream); + RegisterReceiveStream(rtp.rtx_ssrc, receive_stream); } - receive_rtp_config_.emplace(rtp.remote_ssrc, receive_stream); + RegisterReceiveStream(rtp.remote_ssrc, receive_stream); video_receive_streams_.insert(receive_stream); ConfigureSync(receive_stream->sync_group()); @@ -1169,9 +1193,9 @@ void Call::DestroyVideoReceiveStream( // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a // separate SSRC there can be either one or two. - receive_rtp_config_.erase(rtp.remote_ssrc); + UnregisterReceiveStream(rtp.remote_ssrc); if (rtp.rtx_ssrc) { - receive_rtp_config_.erase(rtp.rtx_ssrc); + UnregisterReceiveStream(rtp.rtx_ssrc); } video_receive_streams_.erase(receive_stream_impl); ConfigureSync(receive_stream_impl->sync_group()); @@ -1204,10 +1228,7 @@ FlexfecReceiveStream* Call::CreateFlexfecReceiveStream( // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // thread. receive_stream->RegisterWithTransport(&video_receiver_controller_); - - RTC_DCHECK(receive_rtp_config_.find(config.rtp.remote_ssrc) == - receive_rtp_config_.end()); - receive_rtp_config_.emplace(config.rtp.remote_ssrc, receive_stream); + RegisterReceiveStream(config.rtp.remote_ssrc, receive_stream); // TODO(brandtr): Store config in RtcEventLog here. @@ -1225,7 +1246,7 @@ void Call::DestroyFlexfecReceiveStream(FlexfecReceiveStream* receive_stream) { RTC_DCHECK(receive_stream != nullptr); const FlexfecReceiveStream::RtpConfig& rtp = receive_stream->rtp_config(); - receive_rtp_config_.erase(rtp.remote_ssrc); + UnregisterReceiveStream(rtp.remote_ssrc); // Remove all SSRCs pointing to the FlexfecReceiveStreamImpl to be // destroyed. @@ -1382,6 +1403,20 @@ void Call::OnUpdateSyncGroup(webrtc::AudioReceiveStream& stream, } void Call::OnSentPacket(const rtc::SentPacket& sent_packet) { + RTC_DCHECK_RUN_ON(&sent_packet_sequence_checker_); + // When bundling is in effect, multiple senders may be sharing the same + // transport. It means every |sent_packet| will be multiply notified from + // different channels, WebRtcVoiceMediaChannel or WebRtcVideoChannel. Record + // |last_sent_packet_| to deduplicate redundant notifications to downstream. + // (https://crbug.com/webrtc/13437): Pass all packets without a |packet_id| to + // downstream. + if (last_sent_packet_.has_value() && last_sent_packet_->packet_id != -1 && + last_sent_packet_->packet_id == sent_packet.packet_id && + last_sent_packet_->send_time_ms == sent_packet.send_time_ms) { + return; + } + last_sent_packet_ = sent_packet; + // In production and with most tests, this method will be called on the // network thread. However some test classes such as DirectTransport don't // incorporate a network thread. This means that tests for RtpSenderEgress @@ -1431,51 +1466,37 @@ void Call::OnAllocationLimitsChanged(BitrateAllocationLimits limits) { } // RTC_RUN_ON(worker_thread_) -void Call::ConfigureSync(const std::string& sync_group) { - // TODO(bugs.webrtc.org/11993): Expect to be called on the network thread. - // Set sync only if there was no previous one. - if (sync_group.empty()) - return; - - AudioReceiveStream* sync_audio_stream = nullptr; - // Find existing audio stream. - const auto it = sync_stream_mapping_.find(sync_group); - if (it != sync_stream_mapping_.end()) { - sync_audio_stream = it->second; - } else { - // No configured audio stream, see if we can find one. +AudioReceiveStream* Call::FindAudioStreamForSyncGroup( + const std::string& sync_group) { + RTC_DCHECK_RUN_ON(&receive_11993_checker_); + if (!sync_group.empty()) { for (AudioReceiveStream* stream : audio_receive_streams_) { - if (stream->config().sync_group == sync_group) { - if (sync_audio_stream != nullptr) { - RTC_LOG(LS_WARNING) - << "Attempting to sync more than one audio stream " - "within the same sync group. This is not " - "supported in the current implementation."; - break; - } - sync_audio_stream = stream; - } + if (stream->config().sync_group == sync_group) + return stream; } } - if (sync_audio_stream) - sync_stream_mapping_[sync_group] = sync_audio_stream; + + return nullptr; +} + +// TODO(bugs.webrtc.org/11993): Expect to be called on the network thread. +// RTC_RUN_ON(worker_thread_) +void Call::ConfigureSync(const std::string& sync_group) { + // `audio_stream` may be nullptr when clearing the audio stream for a group. + AudioReceiveStream* audio_stream = FindAudioStreamForSyncGroup(sync_group); + size_t num_synced_streams = 0; for (VideoReceiveStream2* video_stream : video_receive_streams_) { if (video_stream->sync_group() != sync_group) continue; ++num_synced_streams; - if (num_synced_streams > 1) { - // TODO(pbos): Support synchronizing more than one A/V pair. - // https://code.google.com/p/webrtc/issues/detail?id=4762 - RTC_LOG(LS_WARNING) - << "Attempting to sync more than one audio/video pair " - "within the same sync group. This is not supported in " - "the current implementation."; - } + // TODO(bugs.webrtc.org/4762): Support synchronizing more than one A/V pair. + // Attempting to sync more than one audio/video pair within the same sync + // group is not supported in the current implementation. // Only sync the first A/V pair within this sync group. if (num_synced_streams == 1) { // sync_audio_stream may be null and that's ok. - video_stream->SetSync(sync_audio_stream); + video_stream->SetSync(audio_stream); } else { video_stream->SetSync(nullptr); } @@ -1563,22 +1584,11 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO || is_keep_alive_packet); - auto it = receive_rtp_config_.find(parsed_packet.Ssrc()); - if (it == receive_rtp_config_.end()) { - RTC_LOG(LS_ERROR) << "receive_rtp_config_ lookup failed for ssrc " - << parsed_packet.Ssrc(); - // Destruction of the receive stream, including deregistering from the - // RtpDemuxer, is not protected by the `worker_thread_`. - // But deregistering in the `receive_rtp_config_` map is. So by not passing - // the packet on to demuxing in this case, we prevent incoming packets to be - // passed on via the demuxer to a receive stream which is being torned down. + bool use_send_side_bwe = false; + if (!IdentifyReceivedPacket(parsed_packet, &use_send_side_bwe)) return DELIVERY_UNKNOWN_SSRC; - } - parsed_packet.IdentifyExtensions( - RtpHeaderExtensionMap(it->second->rtp_config().extensions)); - - NotifyBweOfReceivedPacket(parsed_packet, media_type); + NotifyBweOfReceivedPacket(parsed_packet, media_type, use_send_side_bwe); // RateCounters expect input parameter as int, save it as int, // instead of converting each time it is passed to RateCounter::Add below. @@ -1629,20 +1639,8 @@ void Call::OnRecoveredPacket(const uint8_t* packet, size_t length) { parsed_packet.set_recovered(true); - auto it = receive_rtp_config_.find(parsed_packet.Ssrc()); - if (it == receive_rtp_config_.end()) { - RTC_LOG(LS_ERROR) << "receive_rtp_config_ lookup failed for ssrc " - << parsed_packet.Ssrc(); - // Destruction of the receive stream, including deregistering from the - // RtpDemuxer, is not protected by the `worker_thread_`. - // But deregistering in the `receive_rtp_config_` map is. - // So by not passing the packet on to demuxing in this case, we prevent - // incoming packets to be passed on via the demuxer to a receive stream - // which is being torn down. + if (!IdentifyReceivedPacket(parsed_packet)) return; - } - parsed_packet.IdentifyExtensions( - RtpHeaderExtensionMap(it->second->rtp_config().extensions)); // TODO(brandtr): Update here when we support protecting audio packets too. parsed_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency); @@ -1651,11 +1649,8 @@ void Call::OnRecoveredPacket(const uint8_t* packet, size_t length) { // RTC_RUN_ON(worker_thread_) void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet, - MediaType media_type) { - auto it = receive_rtp_config_.find(packet.Ssrc()); - bool use_send_side_bwe = (it != receive_rtp_config_.end()) && - UseSendSideBwe(it->second->rtp_config()); - + MediaType media_type, + bool use_send_side_bwe) { RTPHeader header; packet.GetHeader(&header); @@ -1686,6 +1681,45 @@ void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet, } } +bool Call::IdentifyReceivedPacket(RtpPacketReceived& packet, + bool* use_send_side_bwe /*= nullptr*/) { + RTC_DCHECK_RUN_ON(&receive_11993_checker_); + auto it = receive_rtp_config_.find(packet.Ssrc()); + if (it == receive_rtp_config_.end()) { + RTC_DLOG(LS_WARNING) << "receive_rtp_config_ lookup failed for ssrc " + << packet.Ssrc(); + return false; + } + + packet.IdentifyExtensions( + RtpHeaderExtensionMap(it->second->rtp_config().extensions)); + + if (use_send_side_bwe) { + *use_send_side_bwe = UseSendSideBwe(it->second->rtp_config()); + } + + return true; +} + +bool Call::RegisterReceiveStream(uint32_t ssrc, ReceiveStream* stream) { + RTC_DCHECK_RUN_ON(&receive_11993_checker_); + RTC_DCHECK(stream); + auto inserted = receive_rtp_config_.emplace(ssrc, stream); + if (!inserted.second) { + RTC_DLOG(LS_WARNING) << "ssrc already registered: " << ssrc; + } + return inserted.second; +} + +bool Call::UnregisterReceiveStream(uint32_t ssrc) { + RTC_DCHECK_RUN_ON(&receive_11993_checker_); + size_t erased = receive_rtp_config_.erase(ssrc); + if (!erased) { + RTC_DLOG(LS_WARNING) << "ssrc wasn't registered: " << ssrc; + } + return erased != 0u; +} + } // namespace internal } // namespace webrtc diff --git a/call/call.h b/call/call.h index f6388c3c78..11451c5c82 100644 --- a/call/call.h +++ b/call/call.h @@ -40,13 +40,7 @@ namespace webrtc { // SharedModuleThread supports a callback that is issued when only one reference // remains, which is used to indicate to the original owner that the thread may // be discarded. -class SharedModuleThread : public rtc::RefCountInterface { - protected: - SharedModuleThread(std::unique_ptr process_thread, - std::function on_one_ref_remaining); - friend class rtc::scoped_refptr; - ~SharedModuleThread() override; - +class SharedModuleThread final { public: // Allows injection of an externally created process thread. static rtc::scoped_refptr Create( @@ -58,16 +52,28 @@ class SharedModuleThread : public rtc::RefCountInterface { ProcessThread* process_thread(); private: - void AddRef() const override; - rtc::RefCountReleaseStatus Release() const override; + friend class rtc::scoped_refptr; + SharedModuleThread(std::unique_ptr process_thread, + std::function on_one_ref_remaining); + ~SharedModuleThread(); + + void AddRef() const; + rtc::RefCountReleaseStatus Release() const; class Impl; mutable std::unique_ptr impl_; }; +// A Call represents a two-way connection carrying zero or more outgoing +// and incoming media streams, transported over one or more RTP transports. + // A Call instance can contain several send and/or receive streams. All streams // are assumed to have the same remote endpoint and will share bitrate estimates // etc. + +// When using the PeerConnection API, there is an one to one relationship +// between the PeerConnection and the Call. + class Call { public: using Config = CallConfig; diff --git a/call/call_config.h b/call/call_config.h index f149790150..ef505a4b0a 100644 --- a/call/call_config.h +++ b/call/call_config.h @@ -11,6 +11,7 @@ #define CALL_CALL_CONFIG_H_ #include "api/fec_controller.h" +#include "api/metronome/metronome.h" #include "api/neteq/neteq_factory.h" #include "api/network_state_predictor.h" #include "api/rtc_error.h" @@ -75,6 +76,8 @@ struct CallConfig { // RtpTransportControllerSend to use for this call. RtpTransportControllerSendFactoryInterface* rtp_transport_controller_send_factory = nullptr; + + Metronome* metronome = nullptr; }; } // namespace webrtc diff --git a/call/call_factory.cc b/call/call_factory.cc index aeb3cbdaa7..40357850a1 100644 --- a/call/call_factory.cc +++ b/call/call_factory.cc @@ -15,26 +15,34 @@ #include #include #include +#include #include "absl/types/optional.h" #include "api/test/simulated_network.h" +#include "api/units/time_delta.h" #include "call/call.h" #include "call/degraded_call.h" #include "call/rtp_transport_config.h" #include "rtc_base/checks.h" -#include "system_wrappers/include/field_trial.h" +#include "rtc_base/experiments/field_trial_list.h" +#include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { namespace { -bool ParseConfigParam(std::string exp_name, int* field) { - std::string group = field_trial::FindFullName(exp_name); +using TimeScopedNetworkConfig = DegradedCall::TimeScopedNetworkConfig; + +bool ParseConfigParam(const WebRtcKeyValueConfig& trials, + absl::string_view exp_name, + int* field) { + std::string group = trials.Lookup(exp_name); if (group.empty()) return false; return (sscanf(group.c_str(), "%d", field) == 1); } -absl::optional ParseDegradationConfig( +absl::optional ParseDegradationConfig( + const WebRtcKeyValueConfig& trials, bool send) { std::string exp_prefix = "WebRTCFakeNetwork"; if (send) { @@ -43,33 +51,92 @@ absl::optional ParseDegradationConfig( exp_prefix += "Receive"; } - webrtc::BuiltInNetworkBehaviorConfig config; + TimeScopedNetworkConfig config; bool configured = false; configured |= - ParseConfigParam(exp_prefix + "DelayMs", &config.queue_delay_ms); - configured |= ParseConfigParam(exp_prefix + "DelayStdDevMs", + ParseConfigParam(trials, exp_prefix + "DelayMs", &config.queue_delay_ms); + configured |= ParseConfigParam(trials, exp_prefix + "DelayStdDevMs", &config.delay_standard_deviation_ms); int queue_length = 0; - if (ParseConfigParam(exp_prefix + "QueueLength", &queue_length)) { + if (ParseConfigParam(trials, exp_prefix + "QueueLength", &queue_length)) { RTC_CHECK_GE(queue_length, 0); config.queue_length_packets = queue_length; configured = true; } - configured |= - ParseConfigParam(exp_prefix + "CapacityKbps", &config.link_capacity_kbps); - configured |= - ParseConfigParam(exp_prefix + "LossPercent", &config.loss_percent); + configured |= ParseConfigParam(trials, exp_prefix + "CapacityKbps", + &config.link_capacity_kbps); + configured |= ParseConfigParam(trials, exp_prefix + "LossPercent", + &config.loss_percent); int allow_reordering = 0; - if (ParseConfigParam(exp_prefix + "AllowReordering", &allow_reordering)) { + if (ParseConfigParam(trials, exp_prefix + "AllowReordering", + &allow_reordering)) { config.allow_reordering = true; configured = true; } - configured |= ParseConfigParam(exp_prefix + "AvgBurstLossLength", + configured |= ParseConfigParam(trials, exp_prefix + "AvgBurstLossLength", &config.avg_burst_loss_length); - return configured - ? absl::optional(config) - : absl::nullopt; + return configured ? absl::optional(config) + : absl::nullopt; } + +std::vector GetNetworkConfigs( + const WebRtcKeyValueConfig& trials, + bool send) { + FieldTrialStructList trials_list( + {FieldTrialStructMember("queue_length_packets", + [](TimeScopedNetworkConfig* p) { + // FieldTrialParser does not natively support + // size_t type, so use this ugly cast as + // workaround. + return reinterpret_cast( + &p->queue_length_packets); + }), + FieldTrialStructMember( + "queue_delay_ms", + [](TimeScopedNetworkConfig* p) { return &p->queue_delay_ms; }), + FieldTrialStructMember("delay_standard_deviation_ms", + [](TimeScopedNetworkConfig* p) { + return &p->delay_standard_deviation_ms; + }), + FieldTrialStructMember( + "link_capacity_kbps", + [](TimeScopedNetworkConfig* p) { return &p->link_capacity_kbps; }), + FieldTrialStructMember( + "loss_percent", + [](TimeScopedNetworkConfig* p) { return &p->loss_percent; }), + FieldTrialStructMember( + "allow_reordering", + [](TimeScopedNetworkConfig* p) { return &p->allow_reordering; }), + FieldTrialStructMember("avg_burst_loss_length", + [](TimeScopedNetworkConfig* p) { + return &p->avg_burst_loss_length; + }), + FieldTrialStructMember( + "packet_overhead", + [](TimeScopedNetworkConfig* p) { return &p->packet_overhead; }), + FieldTrialStructMember("codel_active_queue_management", + [](TimeScopedNetworkConfig* p) { + return &p->codel_active_queue_management; + }), + FieldTrialStructMember( + "duration", + [](TimeScopedNetworkConfig* p) { return &p->duration; })}, + {}); + ParseFieldTrial({&trials_list}, + trials.Lookup(send ? "WebRTC-FakeNetworkSendConfig" + : "WebRTC-FakeNetworkReceiveConfig")); + std::vector configs = trials_list.Get(); + if (configs.empty()) { + // Try legacy fallback trials. + absl::optional fallback_config = + ParseDegradationConfig(trials, send); + if (fallback_config.has_value()) { + configs.push_back(*fallback_config); + } + } + return configs; +} + } // namespace CallFactory::CallFactory() { @@ -78,14 +145,18 @@ CallFactory::CallFactory() { Call* CallFactory::CreateCall(const Call::Config& config) { RTC_DCHECK_RUN_ON(&call_thread_); - absl::optional send_degradation_config = - ParseDegradationConfig(true); - absl::optional - receive_degradation_config = ParseDegradationConfig(false); + RTC_DCHECK(config.trials); + + std::vector send_degradation_configs = + GetNetworkConfigs(*config.trials, /*send=*/true); + std::vector + receive_degradation_configs = + GetNetworkConfigs(*config.trials, /*send=*/false); RtpTransportConfig transportConfig = config.ExtractTransportConfig(); - if (send_degradation_config || receive_degradation_config) { + if (!send_degradation_configs.empty() || + !receive_degradation_configs.empty()) { return new DegradedCall( std::unique_ptr(Call::Create( config, Clock::GetRealTimeClock(), @@ -94,8 +165,7 @@ Call* CallFactory::CreateCall(const Call::Config& config) { config.rtp_transport_controller_send_factory->Create( transportConfig, Clock::GetRealTimeClock(), ProcessThread::Create("PacerThread")))), - send_degradation_config, receive_degradation_config, - config.task_queue_factory); + send_degradation_configs, receive_degradation_configs); } if (!module_thread_) { diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc index f4a20b82fe..6acebf2bb7 100644 --- a/call/call_perf_tests.cc +++ b/call/call_perf_tests.cc @@ -766,8 +766,8 @@ TEST_F(CallPerfTest, MAYBE_KeepsHighBitrateWhenReconfiguringSender) { // We get lower bitrate than expected by this test if the following field // trial is enabled. - test::ScopedFieldTrials field_trials( - "WebRTC-SendSideBwe-WithOverhead/Disabled/"); + test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-SendSideBwe-WithOverhead/Disabled/"); class VideoStreamFactory : public VideoEncoderConfig::VideoStreamFactoryInterface { diff --git a/call/degraded_call.cc b/call/degraded_call.cc index 5462085490..0d01e8696d 100644 --- a/call/degraded_call.cc +++ b/call/degraded_call.cc @@ -18,13 +18,13 @@ namespace webrtc { DegradedCall::FakeNetworkPipeOnTaskQueue::FakeNetworkPipeOnTaskQueue( - TaskQueueFactory* task_queue_factory, + TaskQueueBase* task_queue, + const ScopedTaskSafety& task_safety, Clock* clock, std::unique_ptr network_behavior) : clock_(clock), - task_queue_(task_queue_factory->CreateTaskQueue( - "DegradedSendQueue", - TaskQueueFactory::Priority::NORMAL)), + task_queue_(task_queue), + task_safety_(task_safety), pipe_(clock, std::move(network_behavior)) {} void DegradedCall::FakeNetworkPipeOnTaskQueue::SendRtp( @@ -61,21 +61,22 @@ bool DegradedCall::FakeNetworkPipeOnTaskQueue::Process() { return false; } - task_queue_.PostTask([this, time_to_next]() { - RTC_DCHECK_RUN_ON(&task_queue_); + task_queue_->PostTask(ToQueuedTask(task_safety_, [this, time_to_next] { + RTC_DCHECK_RUN_ON(task_queue_); int64_t next_process_time = *time_to_next + clock_->TimeInMilliseconds(); if (!next_process_ms_ || next_process_time < *next_process_ms_) { next_process_ms_ = next_process_time; - task_queue_.PostDelayedTask( - [this]() { - RTC_DCHECK_RUN_ON(&task_queue_); - if (!Process()) { - next_process_ms_.reset(); - } - }, + task_queue_->PostDelayedHighPrecisionTask( + ToQueuedTask(task_safety_, + [this] { + RTC_DCHECK_RUN_ON(task_queue_); + if (!Process()) { + next_process_ms_.reset(); + } + }), *time_to_next); } - }); + })); return true; } @@ -127,27 +128,37 @@ bool DegradedCall::FakeNetworkPipeTransportAdapter::SendRtcp( DegradedCall::DegradedCall( std::unique_ptr call, - absl::optional send_config, - absl::optional receive_config, - TaskQueueFactory* task_queue_factory) + const std::vector& send_configs, + const std::vector& receive_configs) : clock_(Clock::GetRealTimeClock()), call_(std::move(call)), - task_queue_factory_(task_queue_factory), - send_config_(send_config), + send_config_index_(0), + send_configs_(send_configs), send_simulated_network_(nullptr), - receive_config_(receive_config) { - if (receive_config_) { - auto network = std::make_unique(*receive_config_); + receive_config_index_(0), + receive_configs_(receive_configs) { + if (!receive_configs_.empty()) { + auto network = std::make_unique(receive_configs_[0]); receive_simulated_network_ = network.get(); receive_pipe_ = std::make_unique(clock_, std::move(network)); receive_pipe_->SetReceiver(call_->Receiver()); + if (receive_configs_.size() > 1) { + call_->network_thread()->PostDelayedTask( + ToQueuedTask(task_safety_, [this] { UpdateReceiveNetworkConfig(); }), + receive_configs_[0].duration.ms()); + } } - if (send_config_) { - auto network = std::make_unique(*send_config_); + if (!send_configs_.empty()) { + auto network = std::make_unique(send_configs_[0]); send_simulated_network_ = network.get(); send_pipe_ = std::make_unique( - task_queue_factory_, clock_, std::move(network)); + call_->network_thread(), task_safety_, clock_, std::move(network)); + if (send_configs_.size() > 1) { + call_->network_thread()->PostDelayedTask( + ToQueuedTask(task_safety_, [this] { UpdateSendNetworkConfig(); }), + send_configs_[0].duration.ms()); + } } } @@ -155,7 +166,7 @@ DegradedCall::~DegradedCall() = default; AudioSendStream* DegradedCall::CreateAudioSendStream( const AudioSendStream::Config& config) { - if (send_config_) { + if (!send_configs_.empty()) { auto transport_adapter = std::make_unique( send_pipe_.get(), call_.get(), clock_, config.send_transport); AudioSendStream::Config degrade_config = config; @@ -189,7 +200,7 @@ VideoSendStream* DegradedCall::CreateVideoSendStream( VideoSendStream::Config config, VideoEncoderConfig encoder_config) { std::unique_ptr transport_adapter; - if (send_config_) { + if (!send_configs_.empty()) { transport_adapter = std::make_unique( send_pipe_.get(), call_.get(), clock_, config.send_transport); config.send_transport = transport_adapter.get(); @@ -207,7 +218,7 @@ VideoSendStream* DegradedCall::CreateVideoSendStream( VideoEncoderConfig encoder_config, std::unique_ptr fec_controller) { std::unique_ptr transport_adapter; - if (send_config_) { + if (!send_configs_.empty()) { transport_adapter = std::make_unique( send_pipe_.get(), call_.get(), clock_, config.send_transport); config.send_transport = transport_adapter.get(); @@ -251,7 +262,7 @@ void DegradedCall::AddAdaptationResource( } PacketReceiver* DegradedCall::Receiver() { - if (receive_config_) { + if (!receive_configs_.empty()) { return this; } return call_->Receiver(); @@ -299,7 +310,7 @@ void DegradedCall::OnUpdateSyncGroup(AudioReceiveStream& stream, } void DegradedCall::OnSentPacket(const rtc::SentPacket& sent_packet) { - if (send_config_) { + if (!send_configs_.empty()) { // If we have a degraded send-transport, we have already notified call // about the supposed network send time. Discard the actual network send // time in order to properly fool the BWE. @@ -325,4 +336,21 @@ PacketReceiver::DeliveryStatus DegradedCall::DeliverPacket( receive_pipe_->Process(); return status; } + +void DegradedCall::UpdateSendNetworkConfig() { + send_config_index_ = (send_config_index_ + 1) % send_configs_.size(); + send_simulated_network_->SetConfig(send_configs_[send_config_index_]); + call_->network_thread()->PostDelayedTask( + ToQueuedTask(task_safety_, [this] { UpdateSendNetworkConfig(); }), + send_configs_[send_config_index_].duration.ms()); +} + +void DegradedCall::UpdateReceiveNetworkConfig() { + receive_config_index_ = (receive_config_index_ + 1) % receive_configs_.size(); + receive_simulated_network_->SetConfig( + receive_configs_[receive_config_index_]); + call_->network_thread()->PostDelayedTask( + ToQueuedTask(task_safety_, [this] { UpdateReceiveNetworkConfig(); }), + receive_configs_[receive_config_index_].duration.ms()); +} } // namespace webrtc diff --git a/call/degraded_call.h b/call/degraded_call.h index 70dc126807..dd80a0c5dd 100644 --- a/call/degraded_call.h +++ b/call/degraded_call.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "absl/types/optional.h" #include "api/call/transport.h" @@ -35,20 +36,23 @@ #include "call/simulated_network.h" #include "call/video_receive_stream.h" #include "call/video_send_stream.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/task_queue.h" +#include "rtc_base/task_utils/pending_task_safety_flag.h" #include "system_wrappers/include/clock.h" namespace webrtc { class DegradedCall : public Call, private PacketReceiver { public: + struct TimeScopedNetworkConfig : public BuiltInNetworkBehaviorConfig { + TimeDelta duration = TimeDelta::PlusInfinity(); + }; + explicit DegradedCall( std::unique_ptr call, - absl::optional send_config, - absl::optional receive_config, - TaskQueueFactory* task_queue_factory); + const std::vector& send_configs, + const std::vector& receive_configs); ~DegradedCall() override; // Implements Call. @@ -110,7 +114,8 @@ class DegradedCall : public Call, private PacketReceiver { class FakeNetworkPipeOnTaskQueue { public: FakeNetworkPipeOnTaskQueue( - TaskQueueFactory* task_queue_factory, + TaskQueueBase* task_queue, + const ScopedTaskSafety& task_safety, Clock* clock, std::unique_ptr network_behavior); @@ -129,7 +134,8 @@ class DegradedCall : public Call, private PacketReceiver { bool Process(); Clock* const clock_; - rtc::TaskQueue task_queue_; + TaskQueueBase* const task_queue_; + const ScopedTaskSafety& task_safety_; FakeNetworkPipe pipe_; absl::optional next_process_ms_ RTC_GUARDED_BY(&task_queue_); }; @@ -158,14 +164,16 @@ class DegradedCall : public Call, private PacketReceiver { Transport* const real_transport_; }; - Clock* const clock_; - const std::unique_ptr call_; - TaskQueueFactory* const task_queue_factory_; - void SetClientBitratePreferences( const webrtc::BitrateSettings& preferences) override {} + void UpdateSendNetworkConfig(); + void UpdateReceiveNetworkConfig(); - const absl::optional send_config_; + Clock* const clock_; + const std::unique_ptr call_; + ScopedTaskSafety task_safety_; + size_t send_config_index_; + const std::vector send_configs_; SimulatedNetwork* send_simulated_network_; std::unique_ptr send_pipe_; std::map> @@ -173,7 +181,8 @@ class DegradedCall : public Call, private PacketReceiver { std::map> video_send_transport_adapters_; - const absl::optional receive_config_; + size_t receive_config_index_; + const std::vector receive_configs_; SimulatedNetwork* receive_simulated_network_; std::unique_ptr receive_pipe_; }; diff --git a/call/fake_network_pipe.cc b/call/fake_network_pipe.cc index 4b5579dfc8..8a03e0ce7a 100644 --- a/call/fake_network_pipe.cc +++ b/call/fake_network_pipe.cc @@ -18,7 +18,6 @@ #include #include "api/media_types.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "system_wrappers/include/clock.h" diff --git a/call/fake_network_pipe.h b/call/fake_network_pipe.h index fadae337f5..be72e91637 100644 --- a/call/fake_network_pipe.h +++ b/call/fake_network_pipe.h @@ -23,7 +23,6 @@ #include "api/test/simulated_network.h" #include "call/call.h" #include "call/simulated_packet_receiver.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -109,6 +108,9 @@ class FakeNetworkPipe : public SimulatedPacketReceiverInterface { ~FakeNetworkPipe() override; + FakeNetworkPipe(const FakeNetworkPipe&) = delete; + FakeNetworkPipe& operator=(const FakeNetworkPipe&) = delete; + void SetClockOffset(int64_t offset_ms); // Must not be called in parallel with DeliverPacket or Process. @@ -228,8 +230,6 @@ class FakeNetworkPipe : public SimulatedPacketReceiverInterface { int64_t last_log_time_us_; std::map active_transports_ RTC_GUARDED_BY(config_lock_); - - RTC_DISALLOW_COPY_AND_ASSIGN(FakeNetworkPipe); }; } // namespace webrtc diff --git a/call/flexfec_receive_stream_impl.cc b/call/flexfec_receive_stream_impl.cc index eda5c7f05d..a78448170f 100644 --- a/call/flexfec_receive_stream_impl.cc +++ b/call/flexfec_receive_stream_impl.cc @@ -23,7 +23,6 @@ #include "modules/rtp_rtcp/include/flexfec_receiver.h" #include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" diff --git a/call/flexfec_receive_stream_unittest.cc b/call/flexfec_receive_stream_unittest.cc index 312fe0c907..9885971a8b 100644 --- a/call/flexfec_receive_stream_unittest.cc +++ b/call/flexfec_receive_stream_unittest.cc @@ -66,7 +66,7 @@ TEST(FlexfecReceiveStreamConfigTest, IsCompleteAndEnabled) { config.rtp.local_ssrc = 18374743; config.rtcp_mode = RtcpMode::kCompound; config.rtp.transport_cc = true; - config.rtp.extensions.emplace_back(TransportSequenceNumber::kUri, 7); + config.rtp.extensions.emplace_back(TransportSequenceNumber::Uri(), 7); EXPECT_FALSE(config.IsCompleteAndEnabled()); config.payload_type = 123; diff --git a/call/rampup_tests.cc b/call/rampup_tests.cc index bf136a5df9..999bb50815 100644 --- a/call/rampup_tests.cc +++ b/call/rampup_tests.cc @@ -26,7 +26,6 @@ #include "rtc_base/task_queue_for_test.h" #include "rtc_base/time_utils.h" #include "test/encoder_settings.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/testsupport/perf_test.h" @@ -182,20 +181,16 @@ void RampUpTester::ModifyVideoConfigs( send_config->rtp.extensions.clear(); - bool remb; bool transport_cc; if (extension_type_ == RtpExtension::kAbsSendTimeUri) { - remb = true; transport_cc = false; send_config->rtp.extensions.push_back( RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId)); } else if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) { - remb = false; transport_cc = true; send_config->rtp.extensions.push_back(RtpExtension( extension_type_.c_str(), kTransportSequenceNumberExtensionId)); } else { - remb = true; transport_cc = false; send_config->rtp.extensions.push_back(RtpExtension( extension_type_.c_str(), kTransmissionTimeOffsetExtensionId)); @@ -333,9 +328,11 @@ void RampUpTester::PollStats() { } } -void RampUpTester::ReportResult(const std::string& measurement, - size_t value, - const std::string& units) const { +void RampUpTester::ReportResult( + const std::string& measurement, + size_t value, + const std::string& units, + test::ImproveDirection improve_direction) const { webrtc::test::PrintResult( measurement, "", ::testing::UnitTest::GetInstance()->current_test_info()->name(), value, @@ -395,16 +392,21 @@ void RampUpTester::TriggerTestDone() { } if (report_perf_stats_) { - ReportResult("ramp-up-media-sent", media_sent, "bytes"); - ReportResult("ramp-up-padding-sent", padding_sent, "bytes"); - ReportResult("ramp-up-rtx-media-sent", rtx_media_sent, "bytes"); - ReportResult("ramp-up-rtx-padding-sent", rtx_padding_sent, "bytes"); + ReportResult("ramp-up-media-sent", media_sent, "bytes", + test::ImproveDirection::kBiggerIsBetter); + ReportResult("ramp-up-padding-sent", padding_sent, "bytes", + test::ImproveDirection::kSmallerIsBetter); + ReportResult("ramp-up-rtx-media-sent", rtx_media_sent, "bytes", + test::ImproveDirection::kBiggerIsBetter); + ReportResult("ramp-up-rtx-padding-sent", rtx_padding_sent, "bytes", + test::ImproveDirection::kSmallerIsBetter); if (ramp_up_finished_ms_ >= 0) { ReportResult("ramp-up-time", ramp_up_finished_ms_ - test_start_ms_, - "milliseconds"); + "milliseconds", test::ImproveDirection::kSmallerIsBetter); } ReportResult("ramp-up-average-network-latency", - send_transport_->GetAverageDelayMs(), "milliseconds"); + send_transport_->GetAverageDelayMs(), "milliseconds", + test::ImproveDirection::kSmallerIsBetter); } } @@ -531,7 +533,8 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) { if (report_perf_stats_) { webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), "first_rampup", now - state_start_ms_, "ms", - false); + false, + test::ImproveDirection::kSmallerIsBetter); } // Apply loss during the transition between states if FEC is enabled. forward_transport_config_.loss_percent = loss_rates_[test_state_]; @@ -547,7 +550,8 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) { if (report_perf_stats_) { webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), "rampdown", now - state_start_ms_, "ms", - false); + false, + test::ImproveDirection::kSmallerIsBetter); } // Apply loss during the transition between states if FEC is enabled. forward_transport_config_.loss_percent = loss_rates_[test_state_]; @@ -561,9 +565,11 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) { if (report_perf_stats_) { webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), "second_rampup", now - state_start_ms_, - "ms", false); + "ms", false, + test::ImproveDirection::kSmallerIsBetter); ReportResult("ramp-up-down-up-average-network-latency", - send_transport_->GetAverageDelayMs(), "milliseconds"); + send_transport_->GetAverageDelayMs(), "milliseconds", + test::ImproveDirection::kSmallerIsBetter); } // Apply loss during the transition between states if FEC is enabled. forward_transport_config_.loss_percent = loss_rates_[test_state_]; diff --git a/call/rampup_tests.h b/call/rampup_tests.h index 075de6d888..0432e662ba 100644 --- a/call/rampup_tests.h +++ b/call/rampup_tests.h @@ -25,6 +25,7 @@ #include "rtc_base/event.h" #include "rtc_base/task_utils/repeating_task.h" #include "test/call_test.h" +#include "test/testsupport/perf_test.h" namespace webrtc { @@ -66,7 +67,8 @@ class RampUpTester : public test::EndToEndTest { void ReportResult(const std::string& measurement, size_t value, - const std::string& units) const; + const std::string& units, + test::ImproveDirection improve_direction) const; void TriggerTestDone(); Clock* const clock_; diff --git a/call/receive_time_calculator.cc b/call/receive_time_calculator.cc index 94d1fd18cc..d6ffd39d6d 100644 --- a/call/receive_time_calculator.cc +++ b/call/receive_time_calculator.cc @@ -16,22 +16,20 @@ #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/numerics/safe_minmax.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { -using ::webrtc::field_trial::IsEnabled; const char kBweReceiveTimeCorrection[] = "WebRTC-Bwe-ReceiveTimeFix"; } // namespace -ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig() +ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig( + const WebRtcKeyValueConfig& field_trials) : max_packet_time_repair("maxrep", TimeDelta::Millis(2000)), stall_threshold("stall", TimeDelta::Millis(5)), tolerance("tol", TimeDelta::Millis(1)), max_stall("maxstall", TimeDelta::Seconds(5)) { - std::string trial_string = - field_trial::FindFullName(kBweReceiveTimeCorrection); + std::string trial_string = field_trials.Lookup(kBweReceiveTimeCorrection); ParseFieldTrial( {&max_packet_time_repair, &stall_threshold, &tolerance, &max_stall}, trial_string); @@ -40,14 +38,16 @@ ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig( const ReceiveTimeCalculatorConfig&) = default; ReceiveTimeCalculatorConfig::~ReceiveTimeCalculatorConfig() = default; -ReceiveTimeCalculator::ReceiveTimeCalculator() - : config_(ReceiveTimeCalculatorConfig()) {} +ReceiveTimeCalculator::ReceiveTimeCalculator( + const WebRtcKeyValueConfig& field_trials) + : config_(field_trials) {} std::unique_ptr -ReceiveTimeCalculator::CreateFromFieldTrial() { - if (!IsEnabled(kBweReceiveTimeCorrection)) +ReceiveTimeCalculator::CreateFromFieldTrial( + const WebRtcKeyValueConfig& field_trials) { + if (!field_trials.IsEnabled(kBweReceiveTimeCorrection)) return nullptr; - return std::make_unique(); + return std::make_unique(field_trials); } int64_t ReceiveTimeCalculator::ReconcileReceiveTimes(int64_t packet_time_us, diff --git a/call/receive_time_calculator.h b/call/receive_time_calculator.h index 0bd3a82afc..276ddda720 100644 --- a/call/receive_time_calculator.h +++ b/call/receive_time_calculator.h @@ -15,12 +15,14 @@ #include #include "api/units/time_delta.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { struct ReceiveTimeCalculatorConfig { - ReceiveTimeCalculatorConfig(); + explicit ReceiveTimeCalculatorConfig( + const WebRtcKeyValueConfig& field_trials); ReceiveTimeCalculatorConfig(const ReceiveTimeCalculatorConfig&); ReceiveTimeCalculatorConfig& operator=(const ReceiveTimeCalculatorConfig&) = default; @@ -41,8 +43,9 @@ struct ReceiveTimeCalculatorConfig { // is received. class ReceiveTimeCalculator { public: - static std::unique_ptr CreateFromFieldTrial(); - ReceiveTimeCalculator(); + static std::unique_ptr CreateFromFieldTrial( + const WebRtcKeyValueConfig& field_trials); + explicit ReceiveTimeCalculator(const WebRtcKeyValueConfig& field_trials); int64_t ReconcileReceiveTimes(int64_t packet_time_us_, int64_t system_time_us_, int64_t safe_time_us_); diff --git a/call/receive_time_calculator_unittest.cc b/call/receive_time_calculator_unittest.cc index d18fb1be8b..f2e3d54f0c 100644 --- a/call/receive_time_calculator_unittest.cc +++ b/call/receive_time_calculator_unittest.cc @@ -21,6 +21,7 @@ #include "rtc_base/random.h" #include "rtc_base/time_utils.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace test { @@ -168,6 +169,7 @@ class EmulatedNonMonotoneousClock : public EmulatedClock { }; TEST(ClockRepair, NoClockDrift) { + webrtc::test::ScopedKeyValueConfig field_trials; const int kSeeds = 10; const int kFirstSeed = 1; const int64_t kRuntimeUs = 10 * rtc::kNumMicrosecsPerSec; @@ -177,7 +179,7 @@ TEST(ClockRepair, NoClockDrift) { EmulatedMonotoneousClock monotone_clock(seed); EmulatedNonMonotoneousClock non_monotone_clock( seed + 1, kRuntimeUs + rtc::kNumMicrosecsPerSec, kDrift); - ReceiveTimeCalculator reception_time_tracker; + ReceiveTimeCalculator reception_time_tracker(field_trials); int64_t corrected_clock_0 = 0; int64_t reset_during_stall_tol_us = 0; bool initial_clock_stall = true; diff --git a/call/rtp_bitrate_configurator.h b/call/rtp_bitrate_configurator.h index 7ad83f8b0b..5cb779a3b3 100644 --- a/call/rtp_bitrate_configurator.h +++ b/call/rtp_bitrate_configurator.h @@ -14,7 +14,6 @@ #include "absl/types/optional.h" #include "api/transport/bitrate_settings.h" #include "api/units/data_rate.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -24,6 +23,10 @@ class RtpBitrateConfigurator { public: explicit RtpBitrateConfigurator(const BitrateConstraints& bitrate_config); ~RtpBitrateConfigurator(); + + RtpBitrateConfigurator(const RtpBitrateConfigurator&) = delete; + RtpBitrateConfigurator& operator=(const RtpBitrateConfigurator&) = delete; + BitrateConstraints GetConfig() const; // The greater min and smaller max set by this and SetClientBitratePreferences @@ -68,8 +71,6 @@ class RtpBitrateConfigurator { // Bandwidth cap applied for relayed calls. DataRate max_bitrate_over_relay_ = DataRate::PlusInfinity(); - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpBitrateConfigurator); }; } // namespace webrtc diff --git a/call/rtp_config.cc b/call/rtp_config.cc index c84a63ee4e..5457a94696 100644 --- a/call/rtp_config.cc +++ b/call/rtp_config.cc @@ -29,7 +29,7 @@ uint32_t FindAssociatedSsrc(uint32_t ssrc, if (ssrcs[i] == ssrc) return associated_ssrcs[i]; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } diff --git a/call/rtp_demuxer.cc b/call/rtp_demuxer.cc index da12b75be7..910ea1ca56 100644 --- a/call/rtp_demuxer.cc +++ b/call/rtp_demuxer.cc @@ -39,15 +39,32 @@ size_t RemoveFromMapByValue(Map* map, const Value& value) { return EraseIf(*map, [&](const auto& elem) { return elem.second == value; }); } +// Temp fix: MID in SDP is allowed to be slightly longer than what's allowed +// in the RTP demuxer. Truncate if needed; this won't match, but it only +// makes sense in places that wouldn't use this for matching anyway. +// TODO(bugs.webrtc.org/12517): remove when length 16 is policed by parser. +std::string CheckMidLength(absl::string_view mid) { + std::string new_mid(mid); + if (new_mid.length() > BaseRtpStringExtension::kMaxValueSizeBytes) { + RTC_LOG(LS_WARNING) << "`mid` attribute too long. Truncating."; + new_mid.resize(BaseRtpStringExtension::kMaxValueSizeBytes); + } + return new_mid; +} + } // namespace +RtpDemuxerCriteria::RtpDemuxerCriteria( + absl::string_view mid, + absl::string_view rsid /*= absl::string_view()*/) + : mid_(CheckMidLength(mid)), rsid_(rsid) {} + RtpDemuxerCriteria::RtpDemuxerCriteria() = default; RtpDemuxerCriteria::~RtpDemuxerCriteria() = default; bool RtpDemuxerCriteria::operator==(const RtpDemuxerCriteria& other) const { - return this->mid == other.mid && this->rsid == other.rsid && - this->ssrcs == other.ssrcs && - this->payload_types == other.payload_types; + return mid_ == other.mid_ && rsid_ == other.rsid_ && ssrcs_ == other.ssrcs_ && + payload_types_ == other.payload_types_; } bool RtpDemuxerCriteria::operator!=(const RtpDemuxerCriteria& other) const { @@ -56,16 +73,16 @@ bool RtpDemuxerCriteria::operator!=(const RtpDemuxerCriteria& other) const { std::string RtpDemuxerCriteria::ToString() const { rtc::StringBuilder sb; - sb << "{mid: " << (mid.empty() ? "" : mid) - << ", rsid: " << (rsid.empty() ? "" : rsid) << ", ssrcs: ["; + sb << "{mid: " << (mid_.empty() ? "" : mid_) + << ", rsid: " << (rsid_.empty() ? "" : rsid_) << ", ssrcs: ["; - for (auto ssrc : ssrcs) { + for (auto ssrc : ssrcs_) { sb << ssrc << ", "; } sb << "], payload_types = ["; - for (auto pt : payload_types) { + for (auto pt : payload_types_) { sb << pt << ", "; } @@ -105,60 +122,60 @@ RtpDemuxer::~RtpDemuxer() { bool RtpDemuxer::AddSink(const RtpDemuxerCriteria& criteria, RtpPacketSinkInterface* sink) { - RTC_DCHECK(!criteria.payload_types.empty() || !criteria.ssrcs.empty() || - !criteria.mid.empty() || !criteria.rsid.empty()); - RTC_DCHECK(criteria.mid.empty() || IsLegalMidName(criteria.mid)); - RTC_DCHECK(criteria.rsid.empty() || IsLegalRsidName(criteria.rsid)); + RTC_DCHECK(!criteria.payload_types().empty() || !criteria.ssrcs().empty() || + !criteria.mid().empty() || !criteria.rsid().empty()); + RTC_DCHECK(criteria.mid().empty() || IsLegalMidName(criteria.mid())); + RTC_DCHECK(criteria.rsid().empty() || IsLegalRsidName(criteria.rsid())); RTC_DCHECK(sink); // We return false instead of DCHECKing for logical conflicts with the new // criteria because new sinks are created according to user-specified SDP and // we do not want to crash due to a data validation error. if (CriteriaWouldConflict(criteria)) { - RTC_LOG(LS_ERROR) << "Unable to add sink = " << sink - << " due conflicting criteria " << criteria.ToString(); + RTC_LOG(LS_ERROR) << "Unable to add sink=" << sink + << " due to conflicting criteria " << criteria.ToString(); return false; } - if (!criteria.mid.empty()) { - if (criteria.rsid.empty()) { - sink_by_mid_.emplace(criteria.mid, sink); + if (!criteria.mid().empty()) { + if (criteria.rsid().empty()) { + sink_by_mid_.emplace(criteria.mid(), sink); } else { - sink_by_mid_and_rsid_.emplace(std::make_pair(criteria.mid, criteria.rsid), - sink); + sink_by_mid_and_rsid_.emplace( + std::make_pair(criteria.mid(), criteria.rsid()), sink); } } else { - if (!criteria.rsid.empty()) { - sink_by_rsid_.emplace(criteria.rsid, sink); + if (!criteria.rsid().empty()) { + sink_by_rsid_.emplace(criteria.rsid(), sink); } } - for (uint32_t ssrc : criteria.ssrcs) { + for (uint32_t ssrc : criteria.ssrcs()) { sink_by_ssrc_.emplace(ssrc, sink); } - for (uint8_t payload_type : criteria.payload_types) { + for (uint8_t payload_type : criteria.payload_types()) { sinks_by_pt_.emplace(payload_type, sink); } RefreshKnownMids(); - RTC_LOG(LS_INFO) << "Added sink = " << sink << " for criteria " - << criteria.ToString(); + RTC_DLOG(LS_INFO) << "Added sink = " << sink << " for criteria " + << criteria.ToString(); return true; } bool RtpDemuxer::CriteriaWouldConflict( const RtpDemuxerCriteria& criteria) const { - if (!criteria.mid.empty()) { - if (criteria.rsid.empty()) { + if (!criteria.mid().empty()) { + if (criteria.rsid().empty()) { // If the MID is in the known_mids_ set, then there is already a sink // added for this MID directly, or there is a sink already added with a // MID, RSID pair for our MID and some RSID. // Adding this criteria would cause one of these rules to be shadowed, so // reject this new criteria. - if (known_mids_.find(criteria.mid) != known_mids_.end()) { + if (known_mids_.find(criteria.mid()) != known_mids_.end()) { RTC_LOG(LS_INFO) << criteria.ToString() << " would conflict with known mid"; return true; @@ -166,7 +183,7 @@ bool RtpDemuxer::CriteriaWouldConflict( } else { // If the exact rule already exists, then reject this duplicate. const auto sink_by_mid_and_rsid = sink_by_mid_and_rsid_.find( - std::make_pair(criteria.mid, criteria.rsid)); + std::make_pair(criteria.mid(), criteria.rsid())); if (sink_by_mid_and_rsid != sink_by_mid_and_rsid_.end()) { RTC_LOG(LS_INFO) << criteria.ToString() << " would conflict with existing sink = " @@ -177,7 +194,7 @@ bool RtpDemuxer::CriteriaWouldConflict( // If there is already a sink registered for the bare MID, then this // criteria will never receive any packets because they will just be // directed to that MID sink, so reject this new criteria. - const auto sink_by_mid = sink_by_mid_.find(criteria.mid); + const auto sink_by_mid = sink_by_mid_.find(criteria.mid()); if (sink_by_mid != sink_by_mid_.end()) { RTC_LOG(LS_INFO) << criteria.ToString() << " would conflict with existing sink = " @@ -187,7 +204,7 @@ bool RtpDemuxer::CriteriaWouldConflict( } } - for (uint32_t ssrc : criteria.ssrcs) { + for (uint32_t ssrc : criteria.ssrcs()) { const auto sink_by_ssrc = sink_by_ssrc_.find(ssrc); if (sink_by_ssrc != sink_by_ssrc_.end()) { RTC_LOG(LS_INFO) << criteria.ToString() @@ -218,14 +235,13 @@ void RtpDemuxer::RefreshKnownMids() { bool RtpDemuxer::AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink) { RtpDemuxerCriteria criteria; - criteria.ssrcs.insert(ssrc); + criteria.ssrcs().insert(ssrc); return AddSink(criteria, sink); } void RtpDemuxer::AddSink(const std::string& rsid, RtpPacketSinkInterface* sink) { - RtpDemuxerCriteria criteria; - criteria.rsid = rsid; + RtpDemuxerCriteria criteria(absl::string_view() /* mid */, rsid); AddSink(criteria, sink); } @@ -237,11 +253,7 @@ bool RtpDemuxer::RemoveSink(const RtpPacketSinkInterface* sink) { RemoveFromMapByValue(&sink_by_mid_and_rsid_, sink) + RemoveFromMapByValue(&sink_by_rsid_, sink); RefreshKnownMids(); - bool removed = num_removed > 0; - if (removed) { - RTC_LOG(LS_INFO) << "Removed sink = " << sink << " bindings"; - } - return removed; + return num_removed > 0; } bool RtpDemuxer::OnRtpPacket(const RtpPacketReceived& packet) { @@ -416,11 +428,11 @@ void RtpDemuxer::AddSsrcSinkBinding(uint32_t ssrc, auto it = result.first; bool inserted = result.second; if (inserted) { - RTC_LOG(LS_INFO) << "Added sink = " << sink - << " binding with SSRC=" << ssrc; + RTC_DLOG(LS_INFO) << "Added sink = " << sink + << " binding with SSRC=" << ssrc; } else if (it->second != sink) { - RTC_LOG(LS_INFO) << "Updated sink = " << sink - << " binding with SSRC=" << ssrc; + RTC_DLOG(LS_INFO) << "Updated sink = " << sink + << " binding with SSRC=" << ssrc; it->second = sink; } } diff --git a/call/rtp_demuxer.h b/call/rtp_demuxer.h index fb65fce368..5fd37b4613 100644 --- a/call/rtp_demuxer.h +++ b/call/rtp_demuxer.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/containers/flat_map.h" #include "rtc_base/containers/flat_set.h" @@ -26,7 +27,10 @@ class RtpPacketSinkInterface; // This struct describes the criteria that will be used to match packets to a // specific sink. -struct RtpDemuxerCriteria { +class RtpDemuxerCriteria { + public: + explicit RtpDemuxerCriteria(absl::string_view mid, + absl::string_view rsid = absl::string_view()); RtpDemuxerCriteria(); ~RtpDemuxerCriteria(); @@ -34,23 +38,37 @@ struct RtpDemuxerCriteria { bool operator!=(const RtpDemuxerCriteria& other) const; // If not the empty string, will match packets with this MID. - std::string mid; + const std::string& mid() const { return mid_; } + + // Return string representation of demux criteria to facilitate logging + std::string ToString() const; // If not the empty string, will match packets with this as their RTP stream // ID or repaired RTP stream ID. // Note that if both MID and RSID are specified, this will only match packets // that have both specified (either through RTP header extensions, SSRC // latching or RTCP). - std::string rsid; + const std::string& rsid() const { return rsid_; } - // Will match packets with any of these SSRCs. - flat_set ssrcs; + // The criteria will match packets with any of these SSRCs. + const flat_set& ssrcs() const { return ssrcs_; } - // Will match packets with any of these payload types. - flat_set payload_types; + // Writable accessor for directly modifying the list of ssrcs. + flat_set& ssrcs() { return ssrcs_; } - // Return string representation of demux criteria to facilitate logging - std::string ToString() const; + // The criteria will match packets with any of these payload types. + const flat_set& payload_types() const { return payload_types_; } + + // Writable accessor for directly modifying the list of payload types. + flat_set& payload_types() { return payload_types_; } + + private: + // Intentionally private member variables to encourage specifying them via the + // constructor and consider them to be const as much as possible. + const std::string mid_; + const std::string rsid_; + flat_set ssrcs_; + flat_set payload_types_; }; // This class represents the RTP demuxing, for a single RTP session (i.e., one diff --git a/call/rtp_demuxer_unittest.cc b/call/rtp_demuxer_unittest.cc index 0c50b81035..4dbee91785 100644 --- a/call/rtp_demuxer_unittest.cc +++ b/call/rtp_demuxer_unittest.cc @@ -56,28 +56,24 @@ class RtpDemuxerTest : public ::testing::Test { bool AddSinkOnlySsrc(uint32_t ssrc, RtpPacketSinkInterface* sink) { RtpDemuxerCriteria criteria; - criteria.ssrcs = {ssrc}; + criteria.ssrcs().insert(ssrc); return AddSink(criteria, sink); } bool AddSinkOnlyRsid(const std::string& rsid, RtpPacketSinkInterface* sink) { - RtpDemuxerCriteria criteria; - criteria.rsid = rsid; + RtpDemuxerCriteria criteria(absl::string_view(), rsid); return AddSink(criteria, sink); } bool AddSinkOnlyMid(const std::string& mid, RtpPacketSinkInterface* sink) { - RtpDemuxerCriteria criteria; - criteria.mid = mid; + RtpDemuxerCriteria criteria(mid); return AddSink(criteria, sink); } bool AddSinkBothMidRsid(const std::string& mid, const std::string& rsid, RtpPacketSinkInterface* sink) { - RtpDemuxerCriteria criteria; - criteria.mid = mid; - criteria.rsid = rsid; + RtpDemuxerCriteria criteria(mid, rsid); return AddSink(criteria, sink); } @@ -199,15 +195,13 @@ TEST_F(RtpDemuxerTest, AllowAddSinkWithOverlappingPayloadTypesIfDifferentMid) { constexpr uint8_t pt2 = 31; constexpr uint8_t pt3 = 32; - RtpDemuxerCriteria pt1_pt2; - pt1_pt2.mid = mid1; - pt1_pt2.payload_types = {pt1, pt2}; + RtpDemuxerCriteria pt1_pt2(mid1); + pt1_pt2.payload_types() = {pt1, pt2}; MockRtpPacketSink sink1; AddSink(pt1_pt2, &sink1); - RtpDemuxerCriteria pt1_pt3; - pt1_pt2.mid = mid2; - pt1_pt3.payload_types = {pt1, pt3}; + RtpDemuxerCriteria pt1_pt3(mid2); + pt1_pt3.payload_types() = {pt1, pt3}; MockRtpPacketSink sink2; EXPECT_TRUE(AddSink(pt1_pt3, &sink2)); } @@ -280,12 +274,12 @@ TEST_F(RtpDemuxerTest, DISABLED_RejectAddSinkForSamePayloadTypes) { constexpr uint8_t pt2 = 31; RtpDemuxerCriteria pt1_pt2; - pt1_pt2.payload_types = {pt1, pt2}; + pt1_pt2.payload_types() = {pt1, pt2}; MockRtpPacketSink sink1; AddSink(pt1_pt2, &sink1); RtpDemuxerCriteria pt2_pt1; - pt2_pt1.payload_types = {pt2, pt1}; + pt2_pt1.payload_types() = {pt2, pt1}; MockRtpPacketSink sink2; EXPECT_FALSE(AddSink(pt2_pt1, &sink2)); } @@ -367,7 +361,7 @@ TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByPayloadType) { MockRtpPacketSink sink; RtpDemuxerCriteria criteria; - criteria.payload_types = {payload_type}; + criteria.payload_types() = {payload_type}; AddSink(criteria, &sink); auto packet = CreatePacketWithSsrc(ssrc); @@ -964,15 +958,13 @@ TEST_F(RtpDemuxerTest, DropByPayloadTypeIfAddedInMultipleSinks) { constexpr uint8_t payload_type = 30; constexpr uint32_t ssrc = 10; - RtpDemuxerCriteria mid1_pt; - mid1_pt.mid = mid1; - mid1_pt.payload_types = {payload_type}; + RtpDemuxerCriteria mid1_pt(mid1); + mid1_pt.payload_types() = {payload_type}; MockRtpPacketSink sink1; AddSink(mid1_pt, &sink1); - RtpDemuxerCriteria mid2_pt; - mid2_pt.mid = mid2; - mid2_pt.payload_types = {payload_type}; + RtpDemuxerCriteria mid2_pt(mid2); + mid2_pt.payload_types() = {payload_type}; MockRtpPacketSink sink2; AddSink(mid2_pt, &sink2); @@ -992,15 +984,13 @@ TEST_F(RtpDemuxerTest, RoutedByPayloadTypeIfAmbiguousSinkRemoved) { constexpr uint8_t payload_type = 30; constexpr uint32_t ssrc = 10; - RtpDemuxerCriteria mid1_pt; - mid1_pt.mid = mid1; - mid1_pt.payload_types = {payload_type}; + RtpDemuxerCriteria mid1_pt(mid1); + mid1_pt.payload_types().insert(payload_type); MockRtpPacketSink sink1; AddSink(mid1_pt, &sink1); - RtpDemuxerCriteria mid2_pt; - mid2_pt.mid = mid2; - mid2_pt.payload_types = {payload_type}; + RtpDemuxerCriteria mid2_pt(mid2); + mid2_pt.payload_types().insert(payload_type); MockRtpPacketSink sink2; AddSink(mid2_pt, &sink2); @@ -1020,7 +1010,7 @@ TEST_F(RtpDemuxerTest, RoutedByPayloadTypeLatchesSsrc) { constexpr uint32_t ssrc = 10; RtpDemuxerCriteria pt; - pt.payload_types = {payload_type}; + pt.payload_types().insert(payload_type); NiceMock sink; AddSink(pt, &sink); @@ -1107,7 +1097,7 @@ TEST_F(RtpDemuxerTest, RouteByPayloadTypeMultipleMatch) { MockRtpPacketSink sink; RtpDemuxerCriteria criteria; - criteria.payload_types = {pt1, pt2}; + criteria.payload_types() = {pt1, pt2}; AddSink(criteria, &sink); auto packet_with_pt1 = CreatePacketWithSsrc(ssrc); @@ -1140,10 +1130,8 @@ TEST_F(RtpDemuxerTest, DemuxBySsrcEvenWithMidAndRsid) { const std::string rsid = "1"; constexpr uint32_t ssrc = 10; - RtpDemuxerCriteria criteria; - criteria.rsid = rsid; - criteria.mid = mid; - criteria.ssrcs = {ssrc}; + RtpDemuxerCriteria criteria(mid, rsid); + criteria.ssrcs().insert(ssrc); MockRtpPacketSink sink; AddSink(criteria, &sink); @@ -1161,8 +1149,8 @@ TEST_F(RtpDemuxerTest, DoNotCheckPayloadTypeIfMatchedByOtherCriteria) { constexpr uint8_t different_payload_type = payload_type + 1; RtpDemuxerCriteria criteria; - criteria.ssrcs = {ssrc}; - criteria.payload_types = {payload_type}; + criteria.ssrcs().insert(ssrc); + criteria.payload_types().insert(payload_type); MockRtpPacketSink sink; AddSink(criteria, &sink); @@ -1220,10 +1208,8 @@ TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedBySsrc) { const std::string rsid = "1"; const std::string wrong_rsid = "2"; - RtpDemuxerCriteria criteria; - criteria.mid = mid; - criteria.rsid = rsid; - criteria.ssrcs = {ssrc}; + RtpDemuxerCriteria criteria(mid, rsid); + criteria.ssrcs().insert(ssrc); MockRtpPacketSink sink; AddSink(criteria, &sink); @@ -1241,10 +1227,8 @@ TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedByPayloadType) { const std::string wrong_rsid = "2"; constexpr uint8_t payload_type = 30; - RtpDemuxerCriteria criteria; - criteria.mid = mid; - criteria.rsid = rsid; - criteria.payload_types = {payload_type}; + RtpDemuxerCriteria criteria(mid, rsid); + criteria.payload_types().insert(payload_type); MockRtpPacketSink sink; AddSink(criteria, &sink); @@ -1254,6 +1238,24 @@ TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedByPayloadType) { EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); } +TEST_F(RtpDemuxerTest, MidMustNotExceedMaximumLength) { + MockRtpPacketSink sink1; + std::string mid1(BaseRtpStringExtension::kMaxValueSizeBytes + 1, 'a'); + // Adding the sink should pass even though the supplied mid is too long. + // The mid will be truncated though. + EXPECT_TRUE(AddSinkOnlyMid(mid1, &sink1)); + + // Adding a second sink with a mid that matches the truncated mid that was + // just added, should fail. + MockRtpPacketSink sink2; + std::string mid2(mid1.substr(0, BaseRtpStringExtension::kMaxValueSizeBytes)); + EXPECT_FALSE(AddSinkOnlyMid(mid2, &sink2)); + EXPECT_FALSE(RemoveSink(&sink2)); + + // Remove the original sink. + EXPECT_TRUE(RemoveSink(&sink1)); +} + #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST_F(RtpDemuxerDeathTest, CriteriaMustBeNonEmpty) { @@ -1278,12 +1280,6 @@ TEST_F(RtpDemuxerDeathTest, RsidMustNotExceedMaximumLength) { EXPECT_DEATH(AddSinkOnlyRsid(rsid, &sink), ""); } -TEST_F(RtpDemuxerDeathTest, MidMustNotExceedMaximumLength) { - MockRtpPacketSink sink; - std::string mid(BaseRtpStringExtension::kMaxValueSizeBytes + 1, 'a'); - EXPECT_DEATH(AddSinkOnlyMid(mid, &sink), ""); -} - #endif } // namespace diff --git a/call/rtp_payload_params.cc b/call/rtp_payload_params.cc index f010d2ea10..2c7cedc68a 100644 --- a/call/rtp_payload_params.cc +++ b/call/rtp_payload_params.cc @@ -131,9 +131,6 @@ RtpPayloadParams::RtpPayloadParams(const uint32_t ssrc, : ssrc_(ssrc), generic_picture_id_experiment_( absl::StartsWith(trials.Lookup("WebRTC-GenericPictureId"), - "Enabled")), - simulate_generic_vp9_( - absl::StartsWith(trials.Lookup("WebRTC-Vp9DependencyDescriptor"), "Enabled")) { for (auto& spatial_layer : last_shared_frame_id_) spatial_layer.fill(-1); @@ -286,7 +283,7 @@ void RtpPayloadParams::SetGeneric(const CodecSpecificInfo* codec_specific_info, } return; case VideoCodecType::kVideoCodecVP9: - if (simulate_generic_vp9_ && codec_specific_info != nullptr) { + if (codec_specific_info != nullptr) { Vp9ToGeneric(codec_specific_info->codecSpecific.VP9, frame_id, *rtp_video_header); } @@ -303,7 +300,7 @@ void RtpPayloadParams::SetGeneric(const CodecSpecificInfo* codec_specific_info, case VideoCodecType::kVideoCodecMultiplex: return; } - RTC_NOTREACHED() << "Unsupported codec."; + RTC_DCHECK_NOTREACHED() << "Unsupported codec."; } void RtpPayloadParams::GenericToGeneric(int64_t shared_frame_id, diff --git a/call/rtp_payload_params.h b/call/rtp_payload_params.h index 9ac52cde2c..1a3e11178e 100644 --- a/call/rtp_payload_params.h +++ b/call/rtp_payload_params.h @@ -15,8 +15,8 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/video_codecs/video_encoder.h" +#include "api/webrtc_key_value_config.h" #include "call/rtp_config.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" @@ -32,7 +32,7 @@ class RtpRtcp; // TODO(nisse): Make these properties not codec specific. class RtpPayloadParams final { public: - RtpPayloadParams(const uint32_t ssrc, + RtpPayloadParams(uint32_t ssrc, const RtpPayloadState* state, const WebRtcKeyValueConfig& trials); RtpPayloadParams(const RtpPayloadParams& other); @@ -137,7 +137,6 @@ class RtpPayloadParams final { RtpPayloadState state_; const bool generic_picture_id_experiment_; - const bool simulate_generic_vp9_; }; } // namespace webrtc #endif // CALL_RTP_PAYLOAD_PARAMS_H_ diff --git a/call/rtp_payload_params_unittest.cc b/call/rtp_payload_params_unittest.cc index 386430bff7..b155c4c204 100644 --- a/call/rtp_payload_params_unittest.cc +++ b/call/rtp_payload_params_unittest.cc @@ -27,9 +27,9 @@ #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" #include "modules/video_coding/include/video_codec_interface.h" #include "test/explicit_key_value_config.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using ::testing::Each; using ::testing::ElementsAre; @@ -275,8 +275,7 @@ TEST(RtpPayloadParamsTest, Tl0PicIdxUpdatedForVp9) { } TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { - test::ScopedFieldTrials generic_picture_id( - "WebRTC-GenericPictureId/Enabled/"); + test::ScopedKeyValueConfig field_trials("WebRTC-GenericPictureId/Enabled/"); RtpPayloadState state{}; EncodedImage encoded_image; @@ -284,7 +283,7 @@ TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { codec_info.codecType = kVideoCodecGeneric; encoded_image._frameType = VideoFrameType::kVideoFrameKey; - RtpPayloadParams params(kSsrc1, &state, FieldTrialBasedConfig()); + RtpPayloadParams params(kSsrc1, &state, field_trials); RTPVideoHeader header = params.GetRtpVideoHeader(encoded_image, &codec_info, 10); @@ -475,17 +474,9 @@ TEST_F(RtpPayloadParamsVp8ToGenericTest, FrameIdGaps) { ConvertAndCheck(1, 20, VideoFrameType::kVideoFrameDelta, kNoSync, {10, 15}); } -class RtpPayloadParamsVp9ToGenericTest : public ::testing::Test { - protected: - RtpPayloadParamsVp9ToGenericTest() - : field_trials_("WebRTC-Vp9DependencyDescriptor/Enabled/") {} - - test::ExplicitKeyValueConfig field_trials_; - RtpPayloadState state_; -}; - -TEST_F(RtpPayloadParamsVp9ToGenericTest, NoScalability) { - RtpPayloadParams params(/*ssrc=*/123, &state_, field_trials_); +TEST(RtpPayloadParamsVp9ToGenericTest, NoScalability) { + RtpPayloadState state; + RtpPayloadParams params(/*ssrc=*/123, &state, FieldTrialBasedConfig()); EncodedImage encoded_image; CodecSpecificInfo codec_info; @@ -532,12 +523,13 @@ TEST_F(RtpPayloadParamsVp9ToGenericTest, NoScalability) { EXPECT_THAT(header.generic->chain_diffs, ElementsAre(3 - 1)); } -TEST_F(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith2Layers) { +TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith2Layers) { // Test with 2 temporal layers structure that is not used by webrtc: // 1---3 5 // / / / ... // 0---2---4--- - RtpPayloadParams params(/*ssrc=*/123, &state_, field_trials_); + RtpPayloadState state; + RtpPayloadParams params(/*ssrc=*/123, &state, FieldTrialBasedConfig()); EncodedImage image; CodecSpecificInfo info; @@ -636,10 +628,11 @@ TEST_F(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith2Layers) { EXPECT_THAT(headers[5].generic->chain_diffs, ElementsAre(2)); } -TEST_F(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith3Layers) { +TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith3Layers) { // Test with 3 temporal layers structure that is not used by webrtc, but used // by chromium: https://imgur.com/pURAGvp - RtpPayloadParams params(/*ssrc=*/123, &state_, field_trials_); + RtpPayloadState state; + RtpPayloadParams params(/*ssrc=*/123, &state, FieldTrialBasedConfig()); EncodedImage image; CodecSpecificInfo info; @@ -780,11 +773,12 @@ TEST_F(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith3Layers) { EXPECT_THAT(headers[8].generic->chain_diffs, ElementsAre(8)); } -TEST_F(RtpPayloadParamsVp9ToGenericTest, SpatialScalabilityKSvc) { +TEST(RtpPayloadParamsVp9ToGenericTest, SpatialScalabilityKSvc) { // 1---3-- // | ... // 0---2-- - RtpPayloadParams params(/*ssrc=*/123, &state_, field_trials_); + RtpPayloadState state; + RtpPayloadParams params(/*ssrc=*/123, &state, FieldTrialBasedConfig()); EncodedImage image; CodecSpecificInfo info; diff --git a/call/rtp_transport_config.h b/call/rtp_transport_config.h index 9aa9f14c16..3a2c76b3d7 100644 --- a/call/rtp_transport_config.h +++ b/call/rtp_transport_config.h @@ -18,7 +18,6 @@ #include "api/transport/bitrate_settings.h" #include "api/transport/network_control.h" #include "api/transport/webrtc_key_value_config.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/task_queue.h" namespace webrtc { diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index f7b6b11fd7..9b3f3cc0ab 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -59,14 +59,12 @@ TargetRateConstraints ConvertConstraints(const BitrateConstraints& contraints, contraints.start_bitrate_bps, clock); } -bool IsEnabled(const WebRtcKeyValueConfig* trials, absl::string_view key) { - RTC_DCHECK(trials != nullptr); - return absl::StartsWith(trials->Lookup(key), "Enabled"); +bool IsEnabled(const WebRtcKeyValueConfig& trials, absl::string_view key) { + return absl::StartsWith(trials.Lookup(key), "Enabled"); } -bool IsDisabled(const WebRtcKeyValueConfig* trials, absl::string_view key) { - RTC_DCHECK(trials != nullptr); - return absl::StartsWith(trials->Lookup(key), "Disabled"); +bool IsDisabled(const WebRtcKeyValueConfig& trials, absl::string_view key) { + return absl::StartsWith(trials.Lookup(key), "Disabled"); } bool IsRelayed(const rtc::NetworkRoute& route) { @@ -75,6 +73,15 @@ bool IsRelayed(const rtc::NetworkRoute& route) { } // namespace +RtpTransportControllerSend::PacerSettings::PacerSettings( + const WebRtcKeyValueConfig& trials) + : tq_disabled("Disabled"), + holdback_window("holdback_window", PacingController::kMinSleepTime), + holdback_packets("holdback_packets", -1) { + ParseFieldTrial({&tq_disabled, &holdback_window, &holdback_packets}, + trials.Lookup("WebRTC-TaskQueuePacer")); +} + RtpTransportControllerSend::RtpTransportControllerSend( Clock* clock, webrtc::RtcEventLog* event_log, @@ -83,29 +90,27 @@ RtpTransportControllerSend::RtpTransportControllerSend( const BitrateConstraints& bitrate_config, std::unique_ptr process_thread, TaskQueueFactory* task_queue_factory, - const WebRtcKeyValueConfig* trials) + const WebRtcKeyValueConfig& trials) : clock_(clock), event_log_(event_log), bitrate_configurator_(bitrate_config), pacer_started_(false), process_thread_(std::move(process_thread)), - use_task_queue_pacer_(IsEnabled(trials, "WebRTC-TaskQueuePacer")), - process_thread_pacer_(use_task_queue_pacer_ + pacer_settings_(trials), + process_thread_pacer_(pacer_settings_.use_task_queue_pacer() ? nullptr : new PacedSender(clock, &packet_router_, - event_log, trials, process_thread_.get())), task_queue_pacer_( - use_task_queue_pacer_ - ? new TaskQueuePacedSender( - clock, - &packet_router_, - event_log, - trials, - task_queue_factory, - /*hold_back_window = */ PacingController::kMinSleepTime) + pacer_settings_.use_task_queue_pacer() + ? new TaskQueuePacedSender(clock, + &packet_router_, + trials, + task_queue_factory, + pacer_settings_.holdback_window.Get(), + pacer_settings_.holdback_packets.Get()) : nullptr), observer_(nullptr), controller_factory_override_(controller_factory), @@ -122,21 +127,24 @@ RtpTransportControllerSend::RtpTransportControllerSend( relay_bandwidth_cap_("relay_cap", DataRate::PlusInfinity()), transport_overhead_bytes_per_packet_(0), network_available_(false), + congestion_window_size_(DataSize::PlusInfinity()), + is_congested_(false), retransmission_rate_limiter_(clock, kRetransmitWindowSizeMs), task_queue_(task_queue_factory->CreateTaskQueue( "rtp_send_controller", - TaskQueueFactory::Priority::NORMAL)) { + TaskQueueFactory::Priority::NORMAL)), + field_trials_(trials) { ParseFieldTrial({&relay_bandwidth_cap_}, - trials->Lookup("WebRTC-Bwe-NetworkRouteConstraints")); + trials.Lookup("WebRTC-Bwe-NetworkRouteConstraints")); initial_config_.constraints = ConvertConstraints(bitrate_config, clock_); initial_config_.event_log = event_log; - initial_config_.key_value_config = trials; + initial_config_.key_value_config = &trials; RTC_DCHECK(bitrate_config.start_bitrate_bps > 0); pacer()->SetPacingRates( DataRate::BitsPerSec(bitrate_config.start_bitrate_bps), DataRate::Zero()); - if (absl::StartsWith(trials->Lookup("WebRTC-LazyPacerStart"), "Disabled")) { + if (absl::StartsWith(trials.Lookup("WebRTC-LazyPacerStart"), "Disabled")) { EnsureStarted(); } } @@ -147,7 +155,7 @@ RtpTransportControllerSend::~RtpTransportControllerSend() { } RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( - std::map suspended_ssrcs, + const std::map& suspended_ssrcs, const std::map& states, const RtpConfig& rtp_config, int rtcp_report_interval_ms, @@ -165,7 +173,8 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( // the parts of RtpTransportControllerSendInterface that are really used. this, event_log, &retransmission_rate_limiter_, std::move(fec_controller), frame_encryption_config.frame_encryptor, - frame_encryption_config.crypto_options, std::move(frame_transformer))); + frame_encryption_config.crypto_options, std::move(frame_transformer), + field_trials_)); return video_rtp_senders_.back().get(); } @@ -193,15 +202,24 @@ void RtpTransportControllerSend::UpdateControlState() { observer_->OnTargetTransferRate(*update); } +void RtpTransportControllerSend::UpdateCongestedState() { + bool congested = transport_feedback_adapter_.GetOutstandingData() >= + congestion_window_size_; + if (congested != is_congested_) { + is_congested_ = congested; + pacer()->SetCongested(congested); + } +} + RtpPacketPacer* RtpTransportControllerSend::pacer() { - if (use_task_queue_pacer_) { + if (pacer_settings_.use_task_queue_pacer()) { return task_queue_pacer_.get(); } return process_thread_pacer_.get(); } const RtpPacketPacer* RtpTransportControllerSend::pacer() const { - if (use_task_queue_pacer_) { + if (pacer_settings_.use_task_queue_pacer()) { return task_queue_pacer_.get(); } return process_thread_pacer_.get(); @@ -226,7 +244,7 @@ RtpTransportControllerSend::transport_feedback_observer() { } RtpPacketSender* RtpTransportControllerSend::packet_sender() { - if (use_task_queue_pacer_) { + if (pacer_settings_.use_task_queue_pacer()) { return task_queue_pacer_.get(); } return process_thread_pacer_.get(); @@ -352,7 +370,8 @@ void RtpTransportControllerSend::OnNetworkRouteChanged( } else { UpdateInitialConstraints(msg.constraints); } - pacer()->UpdateOutstandingData(DataSize::Zero()); + is_congested_ = false; + pacer()->SetCongested(false); }); } } @@ -373,7 +392,8 @@ void RtpTransportControllerSend::OnNetworkAvailability(bool network_available) { } else { pacer()->Pause(); } - pacer()->UpdateOutstandingData(DataSize::Zero()); + is_congested_ = false; + pacer()->SetCongested(false); if (controller_) { control_handler_->SetNetworkAvailability(network_available_); @@ -411,10 +431,15 @@ void RtpTransportControllerSend::OnSentPacket( RTC_DCHECK_RUN_ON(&task_queue_); absl::optional packet_msg = transport_feedback_adapter_.ProcessSentPacket(sent_packet); - pacer()->UpdateOutstandingData( - transport_feedback_adapter_.GetOutstandingData()); - if (packet_msg && controller_) - PostUpdates(controller_->OnSentPacket(*packet_msg)); + if (packet_msg) { + // Only update outstanding data if: + // 1. Packet feadback is used. + // 2. The packet has not yet received an acknowledgement. + // 3. It is not a retransmission of an earlier packet. + UpdateCongestedState(); + if (controller_) + PostUpdates(controller_->OnSentPacket(*packet_msg)); + } }); } @@ -503,7 +528,7 @@ void RtpTransportControllerSend::IncludeOverheadInPacedSender() { void RtpTransportControllerSend::EnsureStarted() { if (!pacer_started_) { pacer_started_ = true; - if (use_task_queue_pacer_) { + if (pacer_settings_.use_task_queue_pacer()) { task_queue_pacer_->EnsureStarted(); } else { process_thread_->Start(); @@ -544,11 +569,10 @@ void RtpTransportControllerSend::OnReceivedRtcpReceiverReport( void RtpTransportControllerSend::OnAddPacket( const RtpPacketSendInfo& packet_info) { - feedback_demuxer_.AddPacket(packet_info); - Timestamp creation_time = Timestamp::Millis(clock_->TimeInMilliseconds()); task_queue_.PostTask([this, packet_info, creation_time]() { RTC_DCHECK_RUN_ON(&task_queue_); + feedback_demuxer_.AddPacket(packet_info); transport_feedback_adapter_.AddPacket( packet_info, send_side_bwe_with_overhead_ ? transport_overhead_bytes_per_packet_ : 0, @@ -558,18 +582,20 @@ void RtpTransportControllerSend::OnAddPacket( void RtpTransportControllerSend::OnTransportFeedback( const rtcp::TransportFeedback& feedback) { - feedback_demuxer_.OnTransportFeedback(feedback); auto feedback_time = Timestamp::Millis(clock_->TimeInMilliseconds()); task_queue_.PostTask([this, feedback, feedback_time]() { RTC_DCHECK_RUN_ON(&task_queue_); + feedback_demuxer_.OnTransportFeedback(feedback); absl::optional feedback_msg = transport_feedback_adapter_.ProcessTransportFeedback(feedback, feedback_time); - if (feedback_msg && controller_) { - PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg)); + if (feedback_msg) { + if (controller_) + PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg)); + + // Only update outstanding data if any packet is first time acked. + UpdateCongestedState(); } - pacer()->UpdateOutstandingData( - transport_feedback_adapter_.GetOutstandingData()); }); } @@ -660,7 +686,8 @@ void RtpTransportControllerSend::UpdateStreamsConfig() { void RtpTransportControllerSend::PostUpdates(NetworkControlUpdate update) { if (update.congestion_window) { - pacer()->SetCongestionWindow(*update.congestion_window); + congestion_window_size_ = *update.congestion_window; + UpdateCongestedState(); } if (update.pacer_config) { pacer()->SetPacingRates(update.pacer_config->data_rate(), diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index f1b90c7f52..ba14fdd24f 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -32,7 +32,6 @@ #include "modules/pacing/rtp_packet_pacer.h" #include "modules/pacing/task_queue_paced_sender.h" #include "modules/utility/include/process_thread.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/network_route.h" #include "rtc_base/race_checker.h" #include "rtc_base/task_queue.h" @@ -60,12 +59,16 @@ class RtpTransportControllerSend final const BitrateConstraints& bitrate_config, std::unique_ptr process_thread, TaskQueueFactory* task_queue_factory, - const WebRtcKeyValueConfig* trials); + const WebRtcKeyValueConfig& trials); ~RtpTransportControllerSend() override; + RtpTransportControllerSend(const RtpTransportControllerSend&) = delete; + RtpTransportControllerSend& operator=(const RtpTransportControllerSend&) = + delete; + // TODO(tommi): Change to std::unique_ptr<>. RtpVideoSenderInterface* CreateRtpVideoSender( - std::map suspended_ssrcs, + const std::map& suspended_ssrcs, const std::map& states, // move states into RtpTransportControllerSend const RtpConfig& rtp_config, @@ -128,6 +131,16 @@ class RtpTransportControllerSend final void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override; private: + struct PacerSettings { + explicit PacerSettings(const WebRtcKeyValueConfig& trials); + + bool use_task_queue_pacer() const { return !tq_disabled.Get(); } + + FieldTrialFlag tq_disabled; // Kill-switch not normally used. + FieldTrialParameter holdback_window; + FieldTrialParameter holdback_packets; + }; + void MaybeCreateControllers() RTC_RUN_ON(task_queue_); void UpdateInitialConstraints(TargetRateConstraints new_contraints) RTC_RUN_ON(task_queue_); @@ -145,6 +158,7 @@ class RtpTransportControllerSend final RTC_RUN_ON(task_queue_); void PostUpdates(NetworkControlUpdate update) RTC_RUN_ON(task_queue_); void UpdateControlState() RTC_RUN_ON(task_queue_); + void UpdateCongestedState() RTC_RUN_ON(task_queue_); RtpPacketPacer* pacer(); const RtpPacketPacer* pacer() const; @@ -158,7 +172,7 @@ class RtpTransportControllerSend final std::map network_routes_; bool pacer_started_; const std::unique_ptr process_thread_; - const bool use_task_queue_pacer_; + const PacerSettings pacer_settings_; std::unique_ptr process_thread_pacer_; std::unique_ptr task_queue_pacer_; @@ -198,6 +212,9 @@ class RtpTransportControllerSend final RepeatingTaskHandle pacer_queue_update_task_ RTC_GUARDED_BY(task_queue_); RepeatingTaskHandle controller_task_ RTC_GUARDED_BY(task_queue_); + DataSize congestion_window_size_ RTC_GUARDED_BY(task_queue_); + bool is_congested_ RTC_GUARDED_BY(task_queue_); + // Protected by internal locks. RateLimiter retransmission_rate_limiter_; @@ -205,7 +222,8 @@ class RtpTransportControllerSend final // `task_queue_` is defined last to ensure all pending tasks are cancelled // and deleted before any other members. rtc::TaskQueue task_queue_; - RTC_DISALLOW_COPY_AND_ASSIGN(RtpTransportControllerSend); + + const WebRtcKeyValueConfig& field_trials_; }; } // namespace webrtc diff --git a/call/rtp_transport_controller_send_factory.h b/call/rtp_transport_controller_send_factory.h index a857ca7e6f..bda0be04af 100644 --- a/call/rtp_transport_controller_send_factory.h +++ b/call/rtp_transport_controller_send_factory.h @@ -25,10 +25,11 @@ class RtpTransportControllerSendFactory const RtpTransportConfig& config, Clock* clock, std::unique_ptr process_thread) override { + RTC_CHECK(config.trials); return std::make_unique( clock, config.event_log, config.network_state_predictor_factory, config.network_controller_factory, config.bitrate_config, - std::move(process_thread), config.task_queue_factory, config.trials); + std::move(process_thread), config.task_queue_factory, *config.trials); } virtual ~RtpTransportControllerSendFactory() {} diff --git a/call/rtp_transport_controller_send_interface.h b/call/rtp_transport_controller_send_interface.h index 2aa6d739da..f68c4bf3dd 100644 --- a/call/rtp_transport_controller_send_interface.h +++ b/call/rtp_transport_controller_send_interface.h @@ -96,7 +96,7 @@ class RtpTransportControllerSendInterface { virtual PacketRouter* packet_router() = 0; virtual RtpVideoSenderInterface* CreateRtpVideoSender( - std::map suspended_ssrcs, + const std::map& suspended_ssrcs, // TODO(holmer): Move states into RtpTransportControllerSend. const std::map& states, const RtpConfig& rtp_config, diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index db8c505d54..40f4638c6b 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -25,7 +25,6 @@ #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" #include "modules/rtp_rtcp/source/rtp_sender.h" -#include "modules/utility/include/process_thread.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" @@ -347,7 +346,7 @@ bool IsFirstFrameOfACodedVideoSequence( RtpVideoSender::RtpVideoSender( Clock* clock, - std::map suspended_ssrcs, + const std::map& suspended_ssrcs, const std::map& states, const RtpConfig& rtp_config, int rtcp_report_interval_ms, @@ -359,22 +358,20 @@ RtpVideoSender::RtpVideoSender( std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, const CryptoOptions& crypto_options, - rtc::scoped_refptr frame_transformer) - : send_side_bwe_with_overhead_(!absl::StartsWith( + rtc::scoped_refptr frame_transformer, + const WebRtcKeyValueConfig& field_trials) + : field_trials_(field_trials), + send_side_bwe_with_overhead_(!absl::StartsWith( field_trials_.Lookup("WebRTC-SendSideBwe-WithOverhead"), "Disabled")), use_frame_rate_for_overhead_(absl::StartsWith( field_trials_.Lookup("WebRTC-Video-UseFrameRateForOverhead"), "Enabled")), has_packet_feedback_(TransportSeqNumExtensionConfigured(rtp_config)), - simulate_vp9_structure_(absl::StartsWith( - field_trials_.Lookup("WebRTC-Vp9DependencyDescriptor"), - "Enabled")), simulate_generic_structure_(absl::StartsWith( field_trials_.Lookup("WebRTC-GenericCodecDependencyDescriptor"), "Enabled")), active_(false), - suspended_ssrcs_(std::move(suspended_ssrcs)), fec_controller_(std::move(fec_controller)), fec_allowed_(true), rtp_streams_(CreateRtpStreamSenders(clock, @@ -384,7 +381,7 @@ RtpVideoSender::RtpVideoSender( send_transport, transport->GetBandwidthObserver(), transport, - suspended_ssrcs_, + suspended_ssrcs, event_log, retransmission_limiter, frame_encryptor, @@ -398,6 +395,7 @@ RtpVideoSender::RtpVideoSender( encoder_target_rate_bps_(0), frame_counts_(rtp_config.ssrcs.size()), frame_count_observer_(observers.frame_count_observer) { + transport_checker_.Detach(); RTC_DCHECK_EQ(rtp_config_.ssrcs.size(), rtp_streams_.size()); if (send_side_bwe_with_overhead_ && has_packet_feedback_) transport_->IncludeOverheadInPacedSender(); @@ -424,7 +422,7 @@ RtpVideoSender::RtpVideoSender( } } - ConfigureSsrcs(); + ConfigureSsrcs(suspended_ssrcs); ConfigureRids(); if (!rtp_config_.mid.empty()) { @@ -449,9 +447,6 @@ RtpVideoSender::RtpVideoSender( fec_controller_->SetProtectionMethod(fec_enabled, NackEnabled()); fec_controller_->SetProtectionCallback(this); - // Signal congestion controller this object is ready for OnPacket* callbacks. - transport_->GetStreamFeedbackProvider()->RegisterStreamFeedbackObserver( - rtp_config_.ssrcs, this); // Construction happens on the worker thread (see Call::CreateVideoSendStream) // but subseqeuent calls to the RTP state will happen on one of two threads: @@ -464,27 +459,44 @@ RtpVideoSender::RtpVideoSender( } RtpVideoSender::~RtpVideoSender() { + // TODO(bugs.webrtc.org/13517): Remove once RtpVideoSender gets deleted on the + // transport task queue. + transport_checker_.Detach(); + SetActiveModulesLocked( std::vector(rtp_streams_.size(), /*active=*/false)); - transport_->GetStreamFeedbackProvider()->DeRegisterStreamFeedbackObserver( - this); + + RTC_DCHECK(!registered_for_feedback_); } void RtpVideoSender::SetActive(bool active) { + RTC_DCHECK_RUN_ON(&transport_checker_); MutexLock lock(&mutex_); if (active_ == active) return; + const std::vector active_modules(rtp_streams_.size(), active); SetActiveModulesLocked(active_modules); + + auto* feedback_provider = transport_->GetStreamFeedbackProvider(); + if (active && !registered_for_feedback_) { + feedback_provider->RegisterStreamFeedbackObserver(rtp_config_.ssrcs, this); + registered_for_feedback_ = true; + } else if (!active && registered_for_feedback_) { + feedback_provider->DeRegisterStreamFeedbackObserver(this); + registered_for_feedback_ = false; + } } void RtpVideoSender::SetActiveModules(const std::vector active_modules) { + RTC_DCHECK_RUN_ON(&transport_checker_); MutexLock lock(&mutex_); return SetActiveModulesLocked(active_modules); } void RtpVideoSender::SetActiveModulesLocked( const std::vector active_modules) { + RTC_DCHECK_RUN_ON(&transport_checker_); RTC_DCHECK_EQ(rtp_streams_.size(), active_modules.size()); active_ = false; for (size_t i = 0; i < active_modules.size(); ++i) { @@ -518,6 +530,7 @@ void RtpVideoSender::SetActiveModulesLocked( } bool RtpVideoSender::IsActive() { + RTC_DCHECK_RUN_ON(&transport_checker_); MutexLock lock(&mutex_); return IsActiveLocked(); } @@ -576,7 +589,7 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( RTPSenderVideo& sender_video = *rtp_streams_[stream_index].sender_video; if (codec_specific_info && codec_specific_info->template_structure) { sender_video.SetVideoStructure(&*codec_specific_info->template_structure); - } else if (simulate_vp9_structure_ && codec_specific_info && + } else if (codec_specific_info && codec_specific_info->codecType == kVideoCodecVP9) { const CodecSpecificInfoVP9& vp9 = codec_specific_info->codecSpecific.VP9; @@ -626,6 +639,7 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( void RtpVideoSender::OnBitrateAllocationUpdated( const VideoBitrateAllocation& bitrate) { + RTC_DCHECK_RUN_ON(&transport_checker_); MutexLock lock(&mutex_); if (IsActiveLocked()) { if (rtp_streams_.size() == 1) { @@ -686,7 +700,8 @@ void RtpVideoSender::DeliverRtcp(const uint8_t* packet, size_t length) { stream.rtp_rtcp->IncomingRtcpPacket(packet, length); } -void RtpVideoSender::ConfigureSsrcs() { +void RtpVideoSender::ConfigureSsrcs( + const std::map& suspended_ssrcs) { // Configure regular SSRCs. RTC_CHECK(ssrc_to_rtp_module_.empty()); for (size_t i = 0; i < rtp_config_.ssrcs.size(); ++i) { @@ -694,8 +709,8 @@ void RtpVideoSender::ConfigureSsrcs() { RtpRtcpInterface* const rtp_rtcp = rtp_streams_[i].rtp_rtcp.get(); // Restore RTP state if previous existed. - auto it = suspended_ssrcs_.find(ssrc); - if (it != suspended_ssrcs_.end()) + auto it = suspended_ssrcs.find(ssrc); + if (it != suspended_ssrcs.end()) rtp_rtcp->SetRtpState(it->second); ssrc_to_rtp_module_[ssrc] = rtp_rtcp; @@ -709,8 +724,8 @@ void RtpVideoSender::ConfigureSsrcs() { for (size_t i = 0; i < rtp_config_.rtx.ssrcs.size(); ++i) { uint32_t ssrc = rtp_config_.rtx.ssrcs[i]; RtpRtcpInterface* const rtp_rtcp = rtp_streams_[i].rtp_rtcp.get(); - auto it = suspended_ssrcs_.find(ssrc); - if (it != suspended_ssrcs_.end()) + auto it = suspended_ssrcs.find(ssrc); + if (it != suspended_ssrcs.end()) rtp_rtcp->SetRtxState(it->second); } diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index d7e1d7527c..1fa9a8bd1e 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -23,8 +23,8 @@ #include "api/fec_controller_override.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/sequence_checker.h" -#include "api/transport/field_trial_based_config.h" #include "api/video_codecs/video_encoder.h" +#include "api/webrtc_key_value_config.h" #include "call/rtp_config.h" #include "call/rtp_payload_params.h" #include "call/rtp_transport_controller_send_interface.h" @@ -35,7 +35,6 @@ #include "modules/rtp_rtcp/source/rtp_sender_video.h" #include "modules/rtp_rtcp/source/rtp_sequence_number_map.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/rate_limiter.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -74,7 +73,7 @@ class RtpVideoSender : public RtpVideoSenderInterface, // Rtp modules are assumed to be sorted in simulcast index order. RtpVideoSender( Clock* clock, - std::map suspended_ssrcs, + const std::map& suspended_ssrcs, const std::map& states, const RtpConfig& rtp_config, int rtcp_report_interval_ms, @@ -86,15 +85,19 @@ class RtpVideoSender : public RtpVideoSenderInterface, std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, const CryptoOptions& crypto_options, // move inside RtpTransport - rtc::scoped_refptr frame_transformer); + rtc::scoped_refptr frame_transformer, + const WebRtcKeyValueConfig& field_trials); ~RtpVideoSender() override; + RtpVideoSender(const RtpVideoSender&) = delete; + RtpVideoSender& operator=(const RtpVideoSender&) = delete; + // RtpVideoSender will only route packets if being active, all packets will be // dropped otherwise. void SetActive(bool active) RTC_LOCKS_EXCLUDED(mutex_) override; // Sets the sending status of the rtp modules and appropriately sets the // payload router to active if any rtp modules are active. - void SetActiveModules(const std::vector active_modules) + void SetActiveModules(std::vector active_modules) RTC_LOCKS_EXCLUDED(mutex_) override; bool IsActive() RTC_LOCKS_EXCLUDED(mutex_) override; @@ -151,11 +154,11 @@ class RtpVideoSender : public RtpVideoSenderInterface, private: bool IsActiveLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void SetActiveModulesLocked(const std::vector active_modules) + void SetActiveModulesLocked(std::vector active_modules) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); void UpdateModuleSendingState() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); void ConfigureProtection(); - void ConfigureSsrcs(); + void ConfigureSsrcs(const std::map& suspended_ssrcs); void ConfigureRids(); bool NackEnabled() const; uint32_t GetPacketizationOverheadRate() const; @@ -164,19 +167,21 @@ class RtpVideoSender : public RtpVideoSenderInterface, DataSize overhead_per_packet, Frequency framerate) const; - const FieldTrialBasedConfig field_trials_; + const WebRtcKeyValueConfig& field_trials_; const bool send_side_bwe_with_overhead_; const bool use_frame_rate_for_overhead_; const bool has_packet_feedback_; - const bool simulate_vp9_structure_; const bool simulate_generic_structure_; - // TODO(holmer): Remove mutex_ once RtpVideoSender runs on the + // Semantically equivalent to checking for `transport_->GetWorkerQueue()` + // but some tests need to be updated to call from the correct context. + RTC_NO_UNIQUE_ADDRESS SequenceChecker transport_checker_; + + // TODO(bugs.webrtc.org/13517): Remove mutex_ once RtpVideoSender runs on the // transport task queue. mutable Mutex mutex_; bool active_ RTC_GUARDED_BY(mutex_); - - std::map suspended_ssrcs_; + bool registered_for_feedback_ RTC_GUARDED_BY(transport_checker_) = false; const std::unique_ptr fec_controller_; bool fec_allowed_ RTC_GUARDED_BY(mutex_); @@ -208,8 +213,6 @@ class RtpVideoSender : public RtpVideoSenderInterface, // This map is set at construction time and never changed, but it's // non-trivial to make it properly const. std::map ssrc_to_rtp_module_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpVideoSender); }; } // namespace webrtc diff --git a/call/rtp_video_sender_interface.h b/call/rtp_video_sender_interface.h index a0b4baccb4..acb68e3ae2 100644 --- a/call/rtp_video_sender_interface.h +++ b/call/rtp_video_sender_interface.h @@ -36,7 +36,7 @@ class RtpVideoSenderInterface : public EncodedImageCallback, virtual void SetActive(bool active) = 0; // Sets the sending status of the rtp modules and appropriately sets the // RtpVideoSender to active if any rtp modules are active. - virtual void SetActiveModules(const std::vector active_modules) = 0; + virtual void SetActiveModules(std::vector active_modules) = 0; virtual bool IsActive() = 0; virtual void OnNetworkAvailability(bool network_available) = 0; diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index e58ee3c5c0..7962ce29c9 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include "call/rtp_transport_controller_send.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -23,12 +24,12 @@ #include "modules/video_coding/fec_controller_default.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/rate_limiter.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_frame_transformer.h" #include "test/mock_transport.h" #include "test/scenario/scenario.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" #include "video/send_delay_stats.h" #include "video/send_statistics_proxy.h" @@ -103,7 +104,7 @@ VideoSendStream::Config CreateVideoSendStreamConfig( config.rtp.nack.rtp_history_ms = 1000; config.rtp.extensions.emplace_back(RtpExtension::kTransportSequenceNumberUri, kTransportsSequenceExtensionId); - config.rtp.extensions.emplace_back(RtpDependencyDescriptorExtension::kUri, + config.rtp.extensions.emplace_back(RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorExtensionId); config.rtp.extmap_allow_mixed = true; return config; @@ -117,7 +118,8 @@ class RtpVideoSenderTestFixture { int payload_type, const std::map& suspended_payload_states, FrameCountObserver* frame_count_observer, - rtc::scoped_refptr frame_transformer) + rtc::scoped_refptr frame_transformer, + const WebRtcKeyValueConfig* field_trials = nullptr) : time_controller_(Timestamp::Millis(1000000)), config_(CreateVideoSendStreamConfig(&transport_, ssrcs, @@ -133,10 +135,11 @@ class RtpVideoSenderTestFixture { bitrate_config_, time_controller_.CreateProcessThread("PacerThread"), time_controller_.GetTaskQueueFactory(), - &field_trials_), + field_trials ? *field_trials : field_trials_), stats_proxy_(time_controller_.GetClock(), config_, - VideoEncoderConfig::ContentType::kRealtimeVideo), + VideoEncoderConfig::ContentType::kRealtimeVideo, + field_trials ? *field_trials : field_trials_), retransmission_rate_limiter_(time_controller_.GetClock(), kRetransmitWindowSizeMs) { transport_controller_.EnsureStarted(); @@ -149,7 +152,8 @@ class RtpVideoSenderTestFixture { &stats_proxy_, &stats_proxy_, &send_delay_stats_), &transport_controller_, &event_log_, &retransmission_rate_limiter_, std::make_unique(time_controller_.GetClock()), - nullptr, CryptoOptions{}, frame_transformer); + nullptr, CryptoOptions{}, frame_transformer, + field_trials ? *field_trials : field_trials_); } RtpVideoSenderTestFixture( @@ -157,31 +161,59 @@ class RtpVideoSenderTestFixture { const std::vector& rtx_ssrcs, int payload_type, const std::map& suspended_payload_states, - FrameCountObserver* frame_count_observer) + FrameCountObserver* frame_count_observer, + const WebRtcKeyValueConfig* field_trials = nullptr) : RtpVideoSenderTestFixture(ssrcs, rtx_ssrcs, payload_type, suspended_payload_states, frame_count_observer, - /*frame_transformer=*/nullptr) {} + /*frame_transformer=*/nullptr, + field_trials) {} RtpVideoSenderTestFixture( const std::vector& ssrcs, const std::vector& rtx_ssrcs, int payload_type, - const std::map& suspended_payload_states) + const std::map& suspended_payload_states, + const WebRtcKeyValueConfig* field_trials = nullptr) : RtpVideoSenderTestFixture(ssrcs, rtx_ssrcs, payload_type, suspended_payload_states, /*frame_count_observer=*/nullptr, - /*frame_transformer=*/nullptr) {} + /*frame_transformer=*/nullptr, + field_trials) {} + + ~RtpVideoSenderTestFixture() { SetActive(false); } RtpVideoSender* router() { return router_.get(); } MockTransport& transport() { return transport_; } void AdvanceTime(TimeDelta delta) { time_controller_.AdvanceTime(delta); } + void SetActive(bool active) { + RunOnTransportQueue([&]() { router_->SetActive(active); }); + } + + void SetActiveModules(const std::vector& active_modules) { + RunOnTransportQueue([&]() { router_->SetActiveModules(active_modules); }); + } + + // Several RtpVideoSender methods expect to be called on the task queue as + // owned by the send transport. While the SequenceChecker may pick up the + // default thread as the transport queue, explicit checks for the transport + // queue (not just using a SequenceChecker) aren't possible unless such a + // queue is actually active. So RunOnTransportQueue is a convenience function + // that allow for running a closure on the transport queue, similar to + // SendTask(). + template + void RunOnTransportQueue(Closure&& task) { + transport_controller_.GetWorkerQueue()->PostTask(std::move(task)); + AdvanceTime(TimeDelta::Millis(0)); + } + private: + test::ScopedKeyValueConfig field_trials_; NiceMock transport_; NiceMock encoder_feedback_; GlobalSimulatedTimeController time_controller_; @@ -189,7 +221,6 @@ class RtpVideoSenderTestFixture { VideoSendStream::Config config_; SendDelayStats send_delay_stats_; BitrateConstraints bitrate_config_; - const FieldTrialBasedConfig field_trials_; RtpTransportControllerSend transport_controller_; SendStatisticsProxy stats_proxy_; RateLimiter retransmission_rate_limiter_; @@ -217,15 +248,15 @@ TEST(RtpVideoSenderTest, SendOnOneModule) { EXPECT_NE(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image, nullptr).error); - test.router()->SetActive(true); + test.SetActive(true); EXPECT_EQ(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image, nullptr).error); - test.router()->SetActive(false); + test.SetActive(false); EXPECT_NE(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image, nullptr).error); - test.router()->SetActive(true); + test.SetActive(true); EXPECT_EQ(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image, nullptr).error); } @@ -244,7 +275,7 @@ TEST(RtpVideoSenderTest, SendSimulcastSetActive) { CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecVP8; - test.router()->SetActive(true); + test.SetActive(true); EXPECT_EQ(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); @@ -254,7 +285,7 @@ TEST(RtpVideoSenderTest, SendSimulcastSetActive) { test.router()->OnEncodedImage(encoded_image_2, &codec_info).error); // Inactive. - test.router()->SetActive(false); + test.SetActive(false); EXPECT_NE(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); EXPECT_NE(EncodedImageCallback::Result::OK, @@ -284,14 +315,14 @@ TEST(RtpVideoSenderTest, SendSimulcastSetActiveModules) { // Only setting one stream to active will still set the payload router to // active and allow sending data on the active stream. std::vector active_modules({true, false}); - test.router()->SetActiveModules(active_modules); + test.SetActiveModules(active_modules); EXPECT_EQ(EncodedImageCallback::Result::OK, test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); // Setting both streams to inactive will turn the payload router to // inactive. active_modules = {false, false}; - test.router()->SetActiveModules(active_modules); + test.SetActiveModules(active_modules); // An incoming encoded image will not ask the module to send outgoing data // because the payload router is inactive. EXPECT_NE(EncodedImageCallback::Result::OK, @@ -303,7 +334,7 @@ TEST(RtpVideoSenderTest, SendSimulcastSetActiveModules) { TEST(RtpVideoSenderTest, CreateWithNoPreviousStates) { RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); std::map initial_states = test.router()->GetRtpPayloadStates(); @@ -328,7 +359,7 @@ TEST(RtpVideoSenderTest, CreateWithPreviousStates) { RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, kPayloadType, states); - test.router()->SetActive(true); + test.SetActive(true); std::map initial_states = test.router()->GetRtpPayloadStates(); @@ -368,7 +399,7 @@ TEST(RtpVideoSenderTest, FrameCountCallbacks) { test.router()->OnEncodedImage(encoded_image, nullptr).error); ::testing::Mock::VerifyAndClearExpectations(&callback); - test.router()->SetActive(true); + test.SetActive(true); FrameCounts frame_counts; EXPECT_CALL(callback, FrameCountUpdated(_, kSsrc1)) @@ -397,7 +428,7 @@ TEST(RtpVideoSenderTest, FrameCountCallbacks) { TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); constexpr uint8_t kPayload = 'a'; EncodedImage encoded_image; @@ -496,8 +527,8 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { } // This tests that we utilize transport wide feedback to retransmit lost -// packets. This is tested by dropping all ordirary packets from a "lossy" -// stream send along with an secondary untouched stream. The transport wide +// packets. This is tested by dropping all ordinary packets from a "lossy" +// stream sent along with a secondary untouched stream. The transport wide // feedback packets from the secondary stream allows the sending side to // detect and retreansmit the lost packets from the lossy stream. TEST(RtpVideoSenderTest, RetransmitsOnTransportWideLossInfo) { @@ -562,7 +593,7 @@ TEST(RtpVideoSenderTest, RetransmitsOnTransportWideLossInfo) { TEST(RtpVideoSenderTest, EarlyRetransmits) { RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); const uint8_t kPayload[1] = {'a'}; EncodedImage encoded_image; @@ -657,7 +688,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -717,7 +748,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -772,10 +803,8 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9NotProvidedByEncoder) { - test::ScopedFieldTrials field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -827,10 +856,10 @@ TEST(RtpVideoSenderTest, } TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { - test::ScopedFieldTrials field_trials( + test::ScopedKeyValueConfig field_trials( "WebRTC-GenericCodecDependencyDescriptor/Enabled/"); - RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); - test.router()->SetActive(true); + RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -876,7 +905,7 @@ TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -934,7 +963,7 @@ TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptorForVp8Simulcast) { RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {}, kPayloadType, {}); - test.router()->SetActive(true); + test.SetActive(true); RtpHeaderExtensionMap extensions; extensions.Register( @@ -1009,7 +1038,7 @@ TEST(RtpVideoSenderTest, SimulcastSenderRegistersFrameTransformers) { } TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) { - test::ScopedFieldTrials field_trials( + test::ScopedKeyValueConfig field_trials( "WebRTC-Video-UseFrameRateForOverhead/Enabled/"); // TODO(jakobi): RTP header size should not be hard coded. @@ -1017,9 +1046,9 @@ TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) { constexpr uint32_t kTransportPacketOverheadBytes = 40; constexpr uint32_t kOverheadPerPacketBytes = kRtpHeaderSizeBytes + kTransportPacketOverheadBytes; - RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); + RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); test.router()->OnTransportOverheadChanged(kTransportPacketOverheadBytes); - test.router()->SetActive(true); + test.SetActive(true); { test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(300000), diff --git a/call/test/mock_rtp_transport_controller_send.h b/call/test/mock_rtp_transport_controller_send.h index b468aa6cb2..8d637a3b5c 100644 --- a/call/test/mock_rtp_transport_controller_send.h +++ b/call/test/mock_rtp_transport_controller_send.h @@ -34,7 +34,7 @@ class MockRtpTransportControllerSend public: MOCK_METHOD(RtpVideoSenderInterface*, CreateRtpVideoSender, - ((std::map), + ((const std::map&), (const std::map&), const RtpConfig&, int rtcp_report_interval_ms, diff --git a/call/version.cc b/call/version.cc index b9a23ad4af..a315d82397 100644 --- a/call/version.cc +++ b/call/version.cc @@ -13,7 +13,7 @@ namespace webrtc { // The timestamp is always in UTC. -const char* const kSourceTimestamp = "WebRTC source stamp 2021-09-09T04:03:59"; +const char* const kSourceTimestamp = "WebRTC source stamp 2022-03-18T04:05:46"; void LoadWebRTCVersionInRegister() { // Using volatile to instruct the compiler to not optimize `p` away even diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index 7c70acddd3..614d5dba76 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -191,6 +191,10 @@ class VideoReceiveStream : public MediaReceiveStream { bool receiver_reference_time_report = false; } rtcp_xr; + // How to request keyframes from a remote sender. Applies only if lntf is + // disabled. + KeyFrameReqMethod keyframe_method = KeyFrameReqMethod::kPliRtcp; + // See LntfConfig for description. LntfConfig lntf; @@ -284,16 +288,6 @@ class VideoReceiveStream : public MediaReceiveStream { virtual ~VideoReceiveStream() {} }; -class DEPRECATED_VideoReceiveStream : public VideoReceiveStream { - public: - // RtpDemuxer only forwards a given RTP packet to one sink. However, some - // sinks, such as FlexFEC, might wish to be informed of all of the packets - // a given sink receives (or any set of sinks). They may do so by registering - // themselves as secondary sinks. - virtual void AddSecondarySink(RtpPacketSinkInterface* sink) = 0; - virtual void RemoveSecondarySink(const RtpPacketSinkInterface* sink) = 0; -}; - } // namespace webrtc #endif // CALL_VIDEO_RECEIVE_STREAM_H_ diff --git a/call/video_send_stream.cc b/call/video_send_stream.cc index 25513e4e4c..241d44a230 100644 --- a/call/video_send_stream.cc +++ b/call/video_send_stream.cc @@ -14,6 +14,7 @@ #include "api/crypto/frame_encryptor_interface.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/strings/string_format.h" namespace webrtc { @@ -71,7 +72,7 @@ std::string VideoSendStream::Stats::ToString(int64_t time_ms) const { char buf[2048]; rtc::SimpleStringBuilder ss(buf); ss << "VideoSendStream stats: " << time_ms << ", {"; - ss << "input_fps: " << input_frame_rate << ", "; + ss << "input_fps: " << rtc::StringFormat("%.1f", input_frame_rate) << ", "; ss << "encode_fps: " << encode_frame_rate << ", "; ss << "encode_ms: " << avg_encode_time_ms << ", "; ss << "encode_usage_perc: " << encode_usage_percent << ", "; diff --git a/call/video_send_stream.h b/call/video_send_stream.h index 487f3042a8..356d8c8099 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -98,7 +98,7 @@ class VideoSendStream { ~Stats(); std::string ToString(int64_t time_ms) const; std::string encoder_implementation_name = "unknown"; - int input_frame_rate = 0; + double input_frame_rate = 0; int encode_frame_rate = 0; int avg_encode_time_ms = 0; int encode_usage_percent = 0; @@ -208,8 +208,7 @@ class VideoSendStream { // Note: This starts stream activity if it is inactive and one of the layers // is active. This stops stream activity if it is active and all layers are // inactive. - virtual void UpdateActiveSimulcastLayers( - const std::vector active_layers) = 0; + virtual void UpdateActiveSimulcastLayers(std::vector active_layers) = 0; // Starts stream activity. // When a stream is active, it can receive, process and deliver packets. diff --git a/common_audio/audio_converter.h b/common_audio/audio_converter.h index e12e601b24..4afbb6d0fd 100644 --- a/common_audio/audio_converter.h +++ b/common_audio/audio_converter.h @@ -15,8 +15,6 @@ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { // Format conversion (remixing and resampling) for audio. Only simple remixing @@ -35,6 +33,9 @@ class AudioConverter { 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 @@ -64,8 +65,6 @@ class AudioConverter { const size_t src_frames_; const size_t dst_channels_; const size_t dst_frames_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioConverter); }; } // namespace webrtc diff --git a/common_audio/fir_filter_factory.cc b/common_audio/fir_filter_factory.cc index 4bcf05245f..2ecef6501f 100644 --- a/common_audio/fir_filter_factory.cc +++ b/common_audio/fir_filter_factory.cc @@ -28,7 +28,7 @@ FIRFilter* CreateFirFilter(const float* coefficients, size_t coefficients_length, size_t max_input_length) { if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } diff --git a/common_audio/resampler/push_sinc_resampler.h b/common_audio/resampler/push_sinc_resampler.h index 88792d427a..7946ef8f82 100644 --- a/common_audio/resampler/push_sinc_resampler.h +++ b/common_audio/resampler/push_sinc_resampler.h @@ -17,7 +17,6 @@ #include #include "common_audio/resampler/sinc_resampler.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -33,6 +32,9 @@ class PushSincResampler : public SincResamplerCallback { 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 @@ -72,8 +74,6 @@ class PushSincResampler : public SincResamplerCallback { // Used to assert we are only requested for as much data as is available. size_t source_available_; - - RTC_DISALLOW_COPY_AND_ASSIGN(PushSincResampler); }; } // namespace webrtc diff --git a/common_audio/resampler/sinc_resampler.h b/common_audio/resampler/sinc_resampler.h index d071e96f4f..b89bba7ab4 100644 --- a/common_audio/resampler/sinc_resampler.h +++ b/common_audio/resampler/sinc_resampler.h @@ -18,7 +18,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/gtest_prod_util.h" #include "rtc_base/memory/aligned_malloc.h" #include "rtc_base/system/arch.h" @@ -64,6 +63,9 @@ class SincResampler { 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); @@ -172,8 +174,6 @@ class SincResampler { float* const r2_; float* r3_; float* r4_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SincResampler); }; } // namespace webrtc diff --git a/common_audio/resampler/sinusoidal_linear_chirp_source.h b/common_audio/resampler/sinusoidal_linear_chirp_source.h index 8534119e5c..a57cbfef02 100644 --- a/common_audio/resampler/sinusoidal_linear_chirp_source.h +++ b/common_audio/resampler/sinusoidal_linear_chirp_source.h @@ -15,7 +15,6 @@ #define COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ #include "common_audio/resampler/sinc_resampler.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -33,6 +32,10 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback { ~SinusoidalLinearChirpSource() override {} + SinusoidalLinearChirpSource(const SinusoidalLinearChirpSource&) = delete; + SinusoidalLinearChirpSource& operator=(const SinusoidalLinearChirpSource&) = + delete; + void Run(size_t frames, float* destination) override; double Frequency(size_t position); @@ -46,8 +49,6 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback { double k_; size_t current_index_; double delay_samples_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource); }; } // namespace webrtc diff --git a/common_audio/vad/vad.cc b/common_audio/vad/vad.cc index 987ed526c0..1647246590 100644 --- a/common_audio/vad/vad.cc +++ b/common_audio/vad/vad.cc @@ -38,7 +38,7 @@ class VadImpl final : public Vad { case 1: return kActive; default: - RTC_NOTREACHED() << "WebRtcVad_Process returned an error."; + RTC_DCHECK_NOTREACHED() << "WebRtcVad_Process returned an error."; return kError; } } diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index 081b1c8104..30eddc6bf6 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -42,6 +42,7 @@ rtc_library("common_video") { ] deps = [ + "../api:array_view", "../api:scoped_refptr", "../api:sequence_checker", "../api/task_queue", @@ -55,6 +56,7 @@ rtc_library("common_video") { "../api/video_codecs:bitstream_parser_api", "../api/video_codecs:video_codecs_api", "../rtc_base", + "../rtc_base:bitstream_reader", "../rtc_base:checks", "../rtc_base:rtc_task_queue", "../rtc_base:safe_minmax", @@ -63,7 +65,10 @@ rtc_library("common_video") { "../system_wrappers:metrics", "//third_party/libyuv", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/numeric:bits", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_source_set("frame_counts") { diff --git a/common_video/h264/h264_bitstream_parser.cc b/common_video/h264/h264_bitstream_parser.cc index bc2104c9ef..2311d0d2ee 100644 --- a/common_video/h264/h264_bitstream_parser.cc +++ b/common_video/h264/h264_bitstream_parser.cc @@ -15,31 +15,20 @@ #include #include "common_video/h264/h264_common.h" -#include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.h" #include "rtc_base/logging.h" +namespace webrtc { namespace { -const int kMaxAbsQpDeltaValue = 51; -const int kMinQpValue = 0; -const int kMaxQpValue = 51; +constexpr int kMaxAbsQpDeltaValue = 51; +constexpr int kMinQpValue = 0; +constexpr int kMaxQpValue = 51; } // namespace -namespace webrtc { - -#define RETURN_ON_FAIL(x, res) \ - do { \ - if (!(x)) { \ - RTC_LOG_F(LS_ERROR) << "FAILED: " #x; \ - return res; \ - } \ - } while (0) - -#define RETURN_INV_ON_FAIL(x) RETURN_ON_FAIL(x, kInvalidStream) - -H264BitstreamParser::H264BitstreamParser() {} -H264BitstreamParser::~H264BitstreamParser() {} +H264BitstreamParser::H264BitstreamParser() = default; +H264BitstreamParser::~H264BitstreamParser() = default; H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( const uint8_t* source, @@ -54,94 +43,90 @@ H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( if (slice_rbsp.size() < H264::kNaluTypeSize) return kInvalidStream; - rtc::BitBuffer slice_reader(slice_rbsp.data() + H264::kNaluTypeSize, - slice_rbsp.size() - H264::kNaluTypeSize); + BitstreamReader slice_reader(slice_rbsp); + slice_reader.ConsumeBits(H264::kNaluTypeSize * 8); + // Check to see if this is an IDR slice, which has an extra field to parse // out. bool is_idr = (source[0] & 0x0F) == H264::NaluType::kIdr; uint8_t nal_ref_idc = (source[0] & 0x60) >> 5; - uint32_t golomb_tmp; - uint32_t bits_tmp; // first_mb_in_slice: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); // slice_type: ue(v) - uint32_t slice_type; - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(slice_type)); + uint32_t slice_type = slice_reader.ReadExponentialGolomb(); // slice_type's 5..9 range is used to indicate that all slices of a picture // have the same value of slice_type % 5, we don't care about that, so we map // to the corresponding 0..4 range. slice_type %= 5; // pic_parameter_set_id: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); if (sps_->separate_colour_plane_flag == 1) { // colour_plane_id - RETURN_INV_ON_FAIL(slice_reader.ReadBits(2, bits_tmp)); + slice_reader.ConsumeBits(2); } // frame_num: u(v) // Represented by log2_max_frame_num bits. - RETURN_INV_ON_FAIL(slice_reader.ReadBits(sps_->log2_max_frame_num, bits_tmp)); - uint32_t field_pic_flag = 0; + slice_reader.ConsumeBits(sps_->log2_max_frame_num); + bool field_pic_flag = false; if (sps_->frame_mbs_only_flag == 0) { // field_pic_flag: u(1) - RETURN_INV_ON_FAIL(slice_reader.ReadBits(1, field_pic_flag)); - if (field_pic_flag != 0) { + field_pic_flag = slice_reader.Read(); + if (field_pic_flag) { // bottom_field_flag: u(1) - RETURN_INV_ON_FAIL(slice_reader.ReadBits(1, bits_tmp)); + slice_reader.ConsumeBits(1); } } if (is_idr) { // idr_pic_id: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } // pic_order_cnt_lsb: u(v) // Represented by sps_.log2_max_pic_order_cnt_lsb bits. if (sps_->pic_order_cnt_type == 0) { - RETURN_INV_ON_FAIL( - slice_reader.ReadBits(sps_->log2_max_pic_order_cnt_lsb, bits_tmp)); - if (pps_->bottom_field_pic_order_in_frame_present_flag && - field_pic_flag == 0) { + slice_reader.ConsumeBits(sps_->log2_max_pic_order_cnt_lsb); + if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) { // delta_pic_order_cnt_bottom: se(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } } if (sps_->pic_order_cnt_type == 1 && !sps_->delta_pic_order_always_zero_flag) { // delta_pic_order_cnt[0]: se(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) { // delta_pic_order_cnt[1]: se(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } } if (pps_->redundant_pic_cnt_present_flag) { // redundant_pic_cnt: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } if (slice_type == H264::SliceType::kB) { // direct_spatial_mv_pred_flag: u(1) - RETURN_INV_ON_FAIL(slice_reader.ReadBits(1, bits_tmp)); + slice_reader.ConsumeBits(1); } switch (slice_type) { case H264::SliceType::kP: case H264::SliceType::kB: case H264::SliceType::kSp: - uint32_t num_ref_idx_active_override_flag; // num_ref_idx_active_override_flag: u(1) - RETURN_INV_ON_FAIL( - slice_reader.ReadBits(1, num_ref_idx_active_override_flag)); - if (num_ref_idx_active_override_flag != 0) { + if (slice_reader.Read()) { // num_ref_idx_l0_active_minus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); if (slice_type == H264::SliceType::kB) { // num_ref_idx_l1_active_minus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } } break; default: break; } + if (!slice_reader.Ok()) { + return kInvalidStream; + } // assume nal_unit_type != 20 && nal_unit_type != 21: if (nalu_type == 20 || nalu_type == 21) { RTC_LOG(LS_ERROR) << "Unsupported nal unit type."; @@ -157,49 +142,44 @@ H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( // verify that they are both the same. if (slice_type % 5 != 2 && slice_type % 5 != 4) { // ref_pic_list_modification_flag_l0: u(1) - uint32_t ref_pic_list_modification_flag_l0; - RETURN_INV_ON_FAIL( - slice_reader.ReadBits(1, ref_pic_list_modification_flag_l0)); - if (ref_pic_list_modification_flag_l0) { + if (slice_reader.Read()) { uint32_t modification_of_pic_nums_idc; do { // modification_of_pic_nums_idc: ue(v) - RETURN_INV_ON_FAIL( - slice_reader.ReadExponentialGolomb(modification_of_pic_nums_idc)); + modification_of_pic_nums_idc = slice_reader.ReadExponentialGolomb(); if (modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1) { // abs_diff_pic_num_minus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } else if (modification_of_pic_nums_idc == 2) { // long_term_pic_num: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } - } while (modification_of_pic_nums_idc != 3); + } while (modification_of_pic_nums_idc != 3 && slice_reader.Ok()); } } if (slice_type % 5 == 1) { // ref_pic_list_modification_flag_l1: u(1) - uint32_t ref_pic_list_modification_flag_l1; - RETURN_INV_ON_FAIL( - slice_reader.ReadBits(1, ref_pic_list_modification_flag_l1)); - if (ref_pic_list_modification_flag_l1) { + if (slice_reader.Read()) { uint32_t modification_of_pic_nums_idc; do { // modification_of_pic_nums_idc: ue(v) - RETURN_INV_ON_FAIL( - slice_reader.ReadExponentialGolomb(modification_of_pic_nums_idc)); + modification_of_pic_nums_idc = slice_reader.ReadExponentialGolomb(); if (modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1) { // abs_diff_pic_num_minus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } else if (modification_of_pic_nums_idc == 2) { // long_term_pic_num: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } - } while (modification_of_pic_nums_idc != 3); + } while (modification_of_pic_nums_idc != 3 && slice_reader.Ok()); } } } + if (!slice_reader.Ok()) { + return kInvalidStream; + } // TODO(pbos): Do we need support for pred_weight_table()? if ((pps_->weighted_pred_flag && (slice_type == H264::SliceType::kP || slice_type == H264::SliceType::kSp)) || @@ -216,49 +196,47 @@ H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( if (is_idr) { // no_output_of_prior_pics_flag: u(1) // long_term_reference_flag: u(1) - RETURN_INV_ON_FAIL(slice_reader.ReadBits(2, bits_tmp)); + slice_reader.ConsumeBits(2); } else { // adaptive_ref_pic_marking_mode_flag: u(1) - uint32_t adaptive_ref_pic_marking_mode_flag; - RETURN_INV_ON_FAIL( - slice_reader.ReadBits(1, adaptive_ref_pic_marking_mode_flag)); - if (adaptive_ref_pic_marking_mode_flag) { + if (slice_reader.Read()) { uint32_t memory_management_control_operation; do { // memory_management_control_operation: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb( - memory_management_control_operation)); + memory_management_control_operation = + slice_reader.ReadExponentialGolomb(); if (memory_management_control_operation == 1 || memory_management_control_operation == 3) { // difference_of_pic_nums_minus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } if (memory_management_control_operation == 2) { // long_term_pic_num: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } if (memory_management_control_operation == 3 || memory_management_control_operation == 6) { // long_term_frame_idx: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } if (memory_management_control_operation == 4) { // max_long_term_frame_idx_plus1: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } - } while (memory_management_control_operation != 0); + } while (memory_management_control_operation != 0 && slice_reader.Ok()); } } } if (pps_->entropy_coding_mode_flag && slice_type != H264::SliceType::kI && slice_type != H264::SliceType::kSi) { // cabac_init_idc: ue(v) - RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(golomb_tmp)); + slice_reader.ReadExponentialGolomb(); } - int32_t last_slice_qp_delta; - RETURN_INV_ON_FAIL( - slice_reader.ReadSignedExponentialGolomb(last_slice_qp_delta)); + int last_slice_qp_delta = slice_reader.ReadSignedExponentialGolomb(); + if (!slice_reader.Ok()) { + return kInvalidStream; + } if (abs(last_slice_qp_delta) > kMaxAbsQpDeltaValue) { // Something has gone wrong, and the parsed value is invalid. RTC_LOG(LS_WARNING) << "Parsed QP value out of range."; diff --git a/common_video/h264/pps_parser.cc b/common_video/h264/pps_parser.cc index 3d3725f95a..2fc9749e8c 100644 --- a/common_video/h264/pps_parser.cc +++ b/common_video/h264/pps_parser.cc @@ -11,25 +11,19 @@ #include "common_video/h264/pps_parser.h" #include +#include #include +#include "absl/numeric/bits.h" #include "common_video/h264/h264_common.h" -#include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.h" #include "rtc_base/checks.h" -#define RETURN_EMPTY_ON_FAIL(x) \ - do { \ - if (!(x)) { \ - return absl::nullopt; \ - } \ - } while (0) - -namespace { -const int kMaxPicInitQpDeltaValue = 25; -const int kMinPicInitQpDeltaValue = -26; -} // namespace - namespace webrtc { +namespace { +constexpr int kMaxPicInitQpDeltaValue = 25; +constexpr int kMinPicInitQpDeltaValue = -26; +} // namespace // General note: this is based off the 02/2014 version of the H.264 standard. // You can find it on this page: @@ -40,9 +34,7 @@ absl::optional PpsParser::ParsePps(const uint8_t* data, // First, parse out rbsp, which is basically the source buffer minus emulation // bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in // section 7.3.1 of the H.264 standard. - std::vector unpacked_buffer = H264::ParseRbsp(data, length); - rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size()); - return ParseInternal(&bit_buffer); + return ParseInternal(H264::ParseRbsp(data, length)); } bool PpsParser::ParsePpsIds(const uint8_t* data, @@ -55,150 +47,114 @@ bool PpsParser::ParsePpsIds(const uint8_t* data, // bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in // section 7.3.1 of the H.264 standard. std::vector unpacked_buffer = H264::ParseRbsp(data, length); - rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size()); - return ParsePpsIdsInternal(&bit_buffer, pps_id, sps_id); + BitstreamReader reader(unpacked_buffer); + *pps_id = reader.ReadExponentialGolomb(); + *sps_id = reader.ReadExponentialGolomb(); + return reader.Ok(); } absl::optional PpsParser::ParsePpsIdFromSlice(const uint8_t* data, size_t length) { std::vector unpacked_buffer = H264::ParseRbsp(data, length); - rtc::BitBuffer slice_reader(unpacked_buffer.data(), unpacked_buffer.size()); + BitstreamReader slice_reader(unpacked_buffer); - uint32_t golomb_tmp; // first_mb_in_slice: ue(v) - if (!slice_reader.ReadExponentialGolomb(golomb_tmp)) - return absl::nullopt; + slice_reader.ReadExponentialGolomb(); // slice_type: ue(v) - if (!slice_reader.ReadExponentialGolomb(golomb_tmp)) - return absl::nullopt; + slice_reader.ReadExponentialGolomb(); // pic_parameter_set_id: ue(v) - uint32_t slice_pps_id; - if (!slice_reader.ReadExponentialGolomb(slice_pps_id)) + uint32_t slice_pps_id = slice_reader.ReadExponentialGolomb(); + if (!slice_reader.Ok()) { return absl::nullopt; + } return slice_pps_id; } absl::optional PpsParser::ParseInternal( - rtc::BitBuffer* bit_buffer) { + rtc::ArrayView buffer) { + BitstreamReader reader(buffer); PpsState pps; + pps.id = reader.ReadExponentialGolomb(); + pps.sps_id = reader.ReadExponentialGolomb(); - RETURN_EMPTY_ON_FAIL(ParsePpsIdsInternal(bit_buffer, &pps.id, &pps.sps_id)); - - uint32_t bits_tmp; - uint32_t golomb_ignored; // entropy_coding_mode_flag: u(1) - uint32_t entropy_coding_mode_flag; - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(1, entropy_coding_mode_flag)); - pps.entropy_coding_mode_flag = entropy_coding_mode_flag != 0; + pps.entropy_coding_mode_flag = reader.Read(); // bottom_field_pic_order_in_frame_present_flag: u(1) - uint32_t bottom_field_pic_order_in_frame_present_flag; - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadBits(1, bottom_field_pic_order_in_frame_present_flag)); - pps.bottom_field_pic_order_in_frame_present_flag = - bottom_field_pic_order_in_frame_present_flag != 0; + pps.bottom_field_pic_order_in_frame_present_flag = reader.Read(); // num_slice_groups_minus1: ue(v) - uint32_t num_slice_groups_minus1; - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadExponentialGolomb(num_slice_groups_minus1)); + uint32_t num_slice_groups_minus1 = reader.ReadExponentialGolomb(); if (num_slice_groups_minus1 > 0) { - uint32_t slice_group_map_type; // slice_group_map_type: ue(v) - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadExponentialGolomb(slice_group_map_type)); + uint32_t slice_group_map_type = reader.ReadExponentialGolomb(); if (slice_group_map_type == 0) { - for (uint32_t i_group = 0; i_group <= num_slice_groups_minus1; - ++i_group) { + for (uint32_t i_group = 0; + i_group <= num_slice_groups_minus1 && reader.Ok(); ++i_group) { // run_length_minus1[iGroup]: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); } } else if (slice_group_map_type == 1) { // TODO(sprang): Implement support for dispersed slice group map type. // See 8.2.2.2 Specification for dispersed slice group map type. } else if (slice_group_map_type == 2) { - for (uint32_t i_group = 0; i_group <= num_slice_groups_minus1; - ++i_group) { + for (uint32_t i_group = 0; + i_group <= num_slice_groups_minus1 && reader.Ok(); ++i_group) { // top_left[iGroup]: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // bottom_right[iGroup]: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); } } else if (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5) { // slice_group_change_direction_flag: u(1) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(1, bits_tmp)); + reader.ConsumeBits(1); // slice_group_change_rate_minus1: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); } else if (slice_group_map_type == 6) { // pic_size_in_map_units_minus1: ue(v) - uint32_t pic_size_in_map_units_minus1; - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadExponentialGolomb(pic_size_in_map_units_minus1)); - uint32_t slice_group_id_bits = 0; - uint32_t num_slice_groups = num_slice_groups_minus1 + 1; - // If num_slice_groups is not a power of two an additional bit is required - // to account for the ceil() of log2() below. - if ((num_slice_groups & (num_slice_groups - 1)) != 0) - ++slice_group_id_bits; - while (num_slice_groups > 0) { - num_slice_groups >>= 1; - ++slice_group_id_bits; - } - for (uint32_t i = 0; i <= pic_size_in_map_units_minus1; i++) { - // slice_group_id[i]: u(v) - // Represented by ceil(log2(num_slice_groups_minus1 + 1)) bits. - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadBits(slice_group_id_bits, bits_tmp)); + uint32_t pic_size_in_map_units = reader.ReadExponentialGolomb() + 1; + int slice_group_id_bits = 1 + absl::bit_width(num_slice_groups_minus1); + + // slice_group_id: array of size pic_size_in_map_units, each element + // is represented by ceil(log2(num_slice_groups_minus1 + 1)) bits. + int64_t bits_to_consume = + int64_t{slice_group_id_bits} * pic_size_in_map_units; + if (!reader.Ok() || bits_to_consume > std::numeric_limits::max()) { + return absl::nullopt; } + reader.ConsumeBits(bits_to_consume); } } // num_ref_idx_l0_default_active_minus1: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // num_ref_idx_l1_default_active_minus1: ue(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // weighted_pred_flag: u(1) - uint32_t weighted_pred_flag; - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(1, weighted_pred_flag)); - pps.weighted_pred_flag = weighted_pred_flag != 0; + pps.weighted_pred_flag = reader.Read(); // weighted_bipred_idc: u(2) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(2, pps.weighted_bipred_idc)); + pps.weighted_bipred_idc = reader.ReadBits(2); // pic_init_qp_minus26: se(v) - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadSignedExponentialGolomb(pps.pic_init_qp_minus26)); + pps.pic_init_qp_minus26 = reader.ReadSignedExponentialGolomb(); // Sanity-check parsed value - if (pps.pic_init_qp_minus26 > kMaxPicInitQpDeltaValue || + if (!reader.Ok() || pps.pic_init_qp_minus26 > kMaxPicInitQpDeltaValue || pps.pic_init_qp_minus26 < kMinPicInitQpDeltaValue) { - RETURN_EMPTY_ON_FAIL(false); + return absl::nullopt; } // pic_init_qs_minus26: se(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // chroma_qp_index_offset: se(v) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // deblocking_filter_control_present_flag: u(1) // constrained_intra_pred_flag: u(1) - RETURN_EMPTY_ON_FAIL(bit_buffer->ReadBits(2, bits_tmp)); + reader.ConsumeBits(2); // redundant_pic_cnt_present_flag: u(1) - RETURN_EMPTY_ON_FAIL( - bit_buffer->ReadBits(1, pps.redundant_pic_cnt_present_flag)); + pps.redundant_pic_cnt_present_flag = reader.ReadBit(); + if (!reader.Ok()) { + return absl::nullopt; + } return pps; } -bool PpsParser::ParsePpsIdsInternal(rtc::BitBuffer* bit_buffer, - uint32_t* pps_id, - uint32_t* sps_id) { - if (pps_id == nullptr) - return false; - // pic_parameter_set_id: ue(v) - if (!bit_buffer->ReadExponentialGolomb(*pps_id)) - return false; - if (sps_id == nullptr) - return false; - // seq_parameter_set_id: ue(v) - if (!bit_buffer->ReadExponentialGolomb(*sps_id)) - return false; - return true; -} - } // namespace webrtc diff --git a/common_video/h264/pps_parser.h b/common_video/h264/pps_parser.h index d6c31b0688..52717dcc26 100644 --- a/common_video/h264/pps_parser.h +++ b/common_video/h264/pps_parser.h @@ -11,11 +11,11 @@ #ifndef COMMON_VIDEO_H264_PPS_PARSER_H_ #define COMMON_VIDEO_H264_PPS_PARSER_H_ -#include "absl/types/optional.h" +#include +#include -namespace rtc { -class BitBuffer; -} +#include "absl/types/optional.h" +#include "api/array_view.h" namespace webrtc { @@ -49,12 +49,10 @@ class PpsParser { size_t length); protected: - // Parse the PPS state, for a bit buffer where RBSP decoding has already been + // Parse the PPS state, for a buffer where RBSP decoding has already been // performed. - static absl::optional ParseInternal(rtc::BitBuffer* bit_buffer); - static bool ParsePpsIdsInternal(rtc::BitBuffer* bit_buffer, - uint32_t* pps_id, - uint32_t* sps_id); + static absl::optional ParseInternal( + rtc::ArrayView buffer); }; } // namespace webrtc diff --git a/common_video/h264/pps_parser_unittest.cc b/common_video/h264/pps_parser_unittest.cc index a279563f85..652f4c7ce0 100644 --- a/common_video/h264/pps_parser_unittest.cc +++ b/common_video/h264/pps_parser_unittest.cc @@ -99,7 +99,7 @@ void WritePps(const PpsParser::PpsState& pps, break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -174,7 +174,7 @@ class PpsParserTest : public ::testing::Test { WritePps(pps, slice_group_map_type, num_slice_groups, pic_size_in_map_units, &buffer_); parsed_pps_ = PpsParser::ParsePps(buffer_.data(), buffer_.size()); - EXPECT_TRUE(static_cast(parsed_pps_)); + ASSERT_TRUE(parsed_pps_); EXPECT_EQ(pps.bottom_field_pic_order_in_frame_present_flag, parsed_pps_->bottom_field_pic_order_in_frame_present_flag); EXPECT_EQ(pps.weighted_pred_flag, parsed_pps_->weighted_pred_flag); diff --git a/common_video/h264/sps_parser.cc b/common_video/h264/sps_parser.cc index f505928f29..cfb0f24ff2 100644 --- a/common_video/h264/sps_parser.cc +++ b/common_video/h264/sps_parser.cc @@ -14,16 +14,9 @@ #include #include "common_video/h264/h264_common.h" -#include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.h" namespace { -typedef absl::optional OptionalSps; - -#define RETURN_EMPTY_ON_FAIL(x) \ - if (!(x)) { \ - return OptionalSps(); \ - } - constexpr int kScalingDeltaMin = -128; constexpr int kScaldingDeltaMax = 127; } // namespace @@ -42,13 +35,13 @@ SpsParser::SpsState::~SpsState() = default; absl::optional SpsParser::ParseSps(const uint8_t* data, size_t length) { std::vector unpacked_buffer = H264::ParseRbsp(data, length); - rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size()); - return ParseSpsUpToVui(&bit_buffer); + BitstreamReader reader(unpacked_buffer); + return ParseSpsUpToVui(reader); } absl::optional SpsParser::ParseSpsUpToVui( - rtc::BitBuffer* buffer) { - // Now, we need to use a bit buffer to parse through the actual AVC SPS + BitstreamReader& reader) { + // Now, we need to use a bitstream reader to parse through the actual AVC SPS // format. See Section 7.3.2.1.1 ("Sequence parameter set data syntax") of the // H.264 standard for a complete description. // Since we only care about resolution, we ignore the majority of fields, but @@ -61,24 +54,18 @@ absl::optional SpsParser::ParseSpsUpToVui( SpsState sps; - // The golomb values we have to read, not just consume. - uint32_t golomb_ignored; - // chroma_format_idc will be ChromaArrayType if separate_colour_plane_flag is // 0. It defaults to 1, when not specified. uint32_t chroma_format_idc = 1; // profile_idc: u(8). We need it to determine if we need to read/skip chroma // formats. - uint8_t profile_idc; - RETURN_EMPTY_ON_FAIL(buffer->ReadUInt8(profile_idc)); + uint8_t profile_idc = reader.Read(); // constraint_set0_flag through constraint_set5_flag + reserved_zero_2bits - // 1 bit each for the flags + 2 bits = 8 bits = 1 byte. - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1)); - // level_idc: u(8) - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBytes(1)); + // 1 bit each for the flags + 2 bits + 8 bits for level_idc = 16 bits. + reader.ConsumeBits(16); // seq_parameter_set_id: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(sps.id)); + sps.id = reader.ReadExponentialGolomb(); sps.separate_colour_plane_flag = 0; // See if profile_idc has chroma format information. if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || @@ -86,42 +73,37 @@ absl::optional SpsParser::ParseSpsUpToVui( profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || profile_idc == 138 || profile_idc == 139 || profile_idc == 134) { // chroma_format_idc: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(chroma_format_idc)); + chroma_format_idc = reader.ReadExponentialGolomb(); if (chroma_format_idc == 3) { // separate_colour_plane_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ReadBits(1, sps.separate_colour_plane_flag)); + sps.separate_colour_plane_flag = reader.ReadBit(); } // bit_depth_luma_minus8: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // bit_depth_chroma_minus8: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // qpprime_y_zero_transform_bypass_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1)); + reader.ConsumeBits(1); // seq_scaling_matrix_present_flag: u(1) - uint32_t seq_scaling_matrix_present_flag; - RETURN_EMPTY_ON_FAIL(buffer->ReadBits(1, seq_scaling_matrix_present_flag)); - if (seq_scaling_matrix_present_flag) { + if (reader.Read()) { // Process the scaling lists just enough to be able to properly // skip over them, so we can still read the resolution on streams // where this is included. int scaling_list_count = (chroma_format_idc == 3 ? 12 : 8); for (int i = 0; i < scaling_list_count; ++i) { // seq_scaling_list_present_flag[i] : u(1) - uint32_t seq_scaling_list_present_flags; - RETURN_EMPTY_ON_FAIL( - buffer->ReadBits(1, seq_scaling_list_present_flags)); - if (seq_scaling_list_present_flags != 0) { + if (reader.Read()) { int last_scale = 8; int next_scale = 8; int size_of_scaling_list = i < 6 ? 16 : 64; for (int j = 0; j < size_of_scaling_list; j++) { if (next_scale != 0) { - int32_t delta_scale; // delta_scale: se(v) - RETURN_EMPTY_ON_FAIL( - buffer->ReadSignedExponentialGolomb(delta_scale)); - RETURN_EMPTY_ON_FAIL(delta_scale >= kScalingDeltaMin && - delta_scale <= kScaldingDeltaMax); + int delta_scale = reader.ReadSignedExponentialGolomb(); + if (!reader.Ok() || delta_scale < kScalingDeltaMin || + delta_scale > kScaldingDeltaMax) { + return absl::nullopt; + } next_scale = (last_scale + delta_scale + 256) % 256; } if (next_scale != 0) @@ -132,50 +114,49 @@ absl::optional SpsParser::ParseSpsUpToVui( } } // log2_max_frame_num and log2_max_pic_order_cnt_lsb are used with - // BitBuffer::ReadBits, which can read at most 32 bits at a time. We also have - // to avoid overflow when adding 4 to the on-wire golomb value, e.g., for evil - // input data, ReadExponentialGolomb might return 0xfffc. + // BitstreamReader::ReadBits, which can read at most 64 bits at a time. We + // also have to avoid overflow when adding 4 to the on-wire golomb value, + // e.g., for evil input data, ReadExponentialGolomb might return 0xfffc. const uint32_t kMaxLog2Minus4 = 32 - 4; // log2_max_frame_num_minus4: ue(v) - uint32_t log2_max_frame_num_minus4; - if (!buffer->ReadExponentialGolomb(log2_max_frame_num_minus4) || - log2_max_frame_num_minus4 > kMaxLog2Minus4) { - return OptionalSps(); + uint32_t log2_max_frame_num_minus4 = reader.ReadExponentialGolomb(); + if (!reader.Ok() || log2_max_frame_num_minus4 > kMaxLog2Minus4) { + return absl::nullopt; } sps.log2_max_frame_num = log2_max_frame_num_minus4 + 4; // pic_order_cnt_type: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(sps.pic_order_cnt_type)); + sps.pic_order_cnt_type = reader.ReadExponentialGolomb(); if (sps.pic_order_cnt_type == 0) { // log2_max_pic_order_cnt_lsb_minus4: ue(v) - uint32_t log2_max_pic_order_cnt_lsb_minus4; - if (!buffer->ReadExponentialGolomb(log2_max_pic_order_cnt_lsb_minus4) || - log2_max_pic_order_cnt_lsb_minus4 > kMaxLog2Minus4) { - return OptionalSps(); + uint32_t log2_max_pic_order_cnt_lsb_minus4 = reader.ReadExponentialGolomb(); + if (!reader.Ok() || log2_max_pic_order_cnt_lsb_minus4 > kMaxLog2Minus4) { + return absl::nullopt; } sps.log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4; } else if (sps.pic_order_cnt_type == 1) { // delta_pic_order_always_zero_flag: u(1) - RETURN_EMPTY_ON_FAIL( - buffer->ReadBits(1, sps.delta_pic_order_always_zero_flag)); + sps.delta_pic_order_always_zero_flag = reader.ReadBit(); // offset_for_non_ref_pic: se(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // offset_for_top_to_bottom_field: se(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); // num_ref_frames_in_pic_order_cnt_cycle: ue(v) - uint32_t num_ref_frames_in_pic_order_cnt_cycle; - RETURN_EMPTY_ON_FAIL( - buffer->ReadExponentialGolomb(num_ref_frames_in_pic_order_cnt_cycle)); + uint32_t num_ref_frames_in_pic_order_cnt_cycle = + reader.ReadExponentialGolomb(); for (size_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) { // offset_for_ref_frame[i]: se(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(golomb_ignored)); + reader.ReadExponentialGolomb(); + if (!reader.Ok()) { + return absl::nullopt; + } } } // max_num_ref_frames: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(sps.max_num_ref_frames)); + sps.max_num_ref_frames = reader.ReadExponentialGolomb(); // gaps_in_frame_num_value_allowed_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1)); + reader.ConsumeBits(1); // // IMPORTANT ONES! Now we're getting to resolution. First we read the pic // width/height in macroblocks (16x16), which gives us the base resolution, @@ -183,48 +164,41 @@ absl::optional SpsParser::ParseSpsUpToVui( // to signify resolutions that aren't multiples of 16. // // pic_width_in_mbs_minus1: ue(v) - uint32_t pic_width_in_mbs_minus1; - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(pic_width_in_mbs_minus1)); + sps.width = 16 * (reader.ReadExponentialGolomb() + 1); // pic_height_in_map_units_minus1: ue(v) - uint32_t pic_height_in_map_units_minus1; - RETURN_EMPTY_ON_FAIL( - buffer->ReadExponentialGolomb(pic_height_in_map_units_minus1)); + uint32_t pic_height_in_map_units_minus1 = reader.ReadExponentialGolomb(); // frame_mbs_only_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ReadBits(1, sps.frame_mbs_only_flag)); + sps.frame_mbs_only_flag = reader.ReadBit(); if (!sps.frame_mbs_only_flag) { // mb_adaptive_frame_field_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1)); + reader.ConsumeBits(1); } + sps.height = + 16 * (2 - sps.frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1); // direct_8x8_inference_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ConsumeBits(1)); + reader.ConsumeBits(1); // // MORE IMPORTANT ONES! Now we're at the frame crop information. // - // frame_cropping_flag: u(1) - uint32_t frame_cropping_flag; uint32_t frame_crop_left_offset = 0; uint32_t frame_crop_right_offset = 0; uint32_t frame_crop_top_offset = 0; uint32_t frame_crop_bottom_offset = 0; - RETURN_EMPTY_ON_FAIL(buffer->ReadBits(1, frame_cropping_flag)); - if (frame_cropping_flag) { + // frame_cropping_flag: u(1) + if (reader.Read()) { // frame_crop_{left, right, top, bottom}_offset: ue(v) - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(frame_crop_left_offset)); - RETURN_EMPTY_ON_FAIL( - buffer->ReadExponentialGolomb(frame_crop_right_offset)); - RETURN_EMPTY_ON_FAIL(buffer->ReadExponentialGolomb(frame_crop_top_offset)); - RETURN_EMPTY_ON_FAIL( - buffer->ReadExponentialGolomb(frame_crop_bottom_offset)); + frame_crop_left_offset = reader.ReadExponentialGolomb(); + frame_crop_right_offset = reader.ReadExponentialGolomb(); + frame_crop_top_offset = reader.ReadExponentialGolomb(); + frame_crop_bottom_offset = reader.ReadExponentialGolomb(); } // vui_parameters_present_flag: u(1) - RETURN_EMPTY_ON_FAIL(buffer->ReadBits(1, sps.vui_params_present)); + sps.vui_params_present = reader.ReadBit(); // Far enough! We don't use the rest of the SPS. - - // Start with the resolution determined by the pic_width/pic_height fields. - sps.width = 16 * (pic_width_in_mbs_minus1 + 1); - sps.height = - 16 * (2 - sps.frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1); + if (!reader.Ok()) { + return absl::nullopt; + } // Figure out the crop units in pixels. That's based on the chroma format's // sampling, which is indicated by chroma_format_idc. @@ -247,7 +221,7 @@ absl::optional SpsParser::ParseSpsUpToVui( sps.width -= (frame_crop_left_offset + frame_crop_right_offset); sps.height -= (frame_crop_top_offset + frame_crop_bottom_offset); - return OptionalSps(sps); + return sps; } } // namespace webrtc diff --git a/common_video/h264/sps_parser.h b/common_video/h264/sps_parser.h index 76e627d27a..da328b48b0 100644 --- a/common_video/h264/sps_parser.h +++ b/common_video/h264/sps_parser.h @@ -12,10 +12,7 @@ #define COMMON_VIDEO_H264_SPS_PARSER_H_ #include "absl/types/optional.h" - -namespace rtc { -class BitBuffer; -} +#include "rtc_base/bitstream_reader.h" namespace webrtc { @@ -46,9 +43,9 @@ class SpsParser { static absl::optional ParseSps(const uint8_t* data, size_t length); protected: - // Parse the SPS state, up till the VUI part, for a bit buffer where RBSP + // Parse the SPS state, up till the VUI part, for a buffer where RBSP // decoding has already been performed. - static absl::optional ParseSpsUpToVui(rtc::BitBuffer* buffer); + static absl::optional ParseSpsUpToVui(BitstreamReader& reader); }; } // namespace webrtc diff --git a/common_video/h264/sps_vui_rewriter.cc b/common_video/h264/sps_vui_rewriter.cc index 856b012b32..117e92a1e5 100644 --- a/common_video/h264/sps_vui_rewriter.cc +++ b/common_video/h264/sps_vui_rewriter.cc @@ -13,6 +13,7 @@ #include +#include #include #include @@ -20,9 +21,9 @@ #include "common_video/h264/h264_common.h" #include "common_video/h264/sps_parser.h" #include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.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 { @@ -53,46 +54,55 @@ enum SpsValidEvent { } \ } while (0) -#define COPY_UINT8(src, dest, tmp) \ - do { \ - RETURN_FALSE_ON_FAIL((src)->ReadUInt8(tmp)); \ - if (dest) \ - RETURN_FALSE_ON_FAIL((dest)->WriteUInt8(tmp)); \ - } while (0) +uint8_t CopyUInt8(BitstreamReader& source, rtc::BitBufferWriter& destination) { + uint8_t tmp = source.Read(); + if (!destination.WriteUInt8(tmp)) { + source.Invalidate(); + } + return tmp; +} -#define COPY_EXP_GOLOMB(src, dest, tmp) \ - do { \ - RETURN_FALSE_ON_FAIL((src)->ReadExponentialGolomb(tmp)); \ - if (dest) \ - RETURN_FALSE_ON_FAIL((dest)->WriteExponentialGolomb(tmp)); \ - } while (0) +uint32_t CopyExpGolomb(BitstreamReader& source, + rtc::BitBufferWriter& destination) { + uint32_t tmp = source.ReadExponentialGolomb(); + if (!destination.WriteExponentialGolomb(tmp)) { + source.Invalidate(); + } + return tmp; +} -#define COPY_BITS(src, dest, tmp, bits) \ - do { \ - RETURN_FALSE_ON_FAIL((src)->ReadBits(bits, tmp)); \ - if (dest) \ - RETURN_FALSE_ON_FAIL((dest)->WriteBits(tmp, bits)); \ - } while (0) +uint32_t CopyBits(int bits, + BitstreamReader& source, + rtc::BitBufferWriter& destination) { + RTC_DCHECK_GT(bits, 0); + RTC_DCHECK_LE(bits, 32); + uint64_t tmp = source.ReadBits(bits); + if (!destination.WriteBits(tmp, bits)) { + source.Invalidate(); + } + return tmp; +} bool CopyAndRewriteVui(const SpsParser::SpsState& sps, - rtc::BitBuffer* source, - rtc::BitBufferWriter* destination, + BitstreamReader& source, + rtc::BitBufferWriter& destination, const webrtc::ColorSpace* color_space, - SpsVuiRewriter::ParseResult* out_vui_rewritten); -bool CopyHrdParameters(rtc::BitBuffer* source, - rtc::BitBufferWriter* destination); + SpsVuiRewriter::ParseResult& out_vui_rewritten); + +void CopyHrdParameters(BitstreamReader& source, + rtc::BitBufferWriter& destination); bool AddBitstreamRestriction(rtc::BitBufferWriter* destination, uint32_t max_num_ref_frames); bool IsDefaultColorSpace(const ColorSpace& color_space); -bool AddVideoSignalTypeInfo(rtc::BitBufferWriter* destination, +bool AddVideoSignalTypeInfo(rtc::BitBufferWriter& destination, const ColorSpace& color_space); bool CopyOrRewriteVideoSignalTypeInfo( - rtc::BitBuffer* source, - rtc::BitBufferWriter* destination, + BitstreamReader& source, + rtc::BitBufferWriter& destination, const ColorSpace* color_space, - SpsVuiRewriter::ParseResult* out_vui_rewritten); -bool CopyRemainingBits(rtc::BitBuffer* source, - rtc::BitBufferWriter* destination); + SpsVuiRewriter::ParseResult& out_vui_rewritten); +bool CopyRemainingBits(BitstreamReader& source, + rtc::BitBufferWriter& destination); } // namespace void SpsVuiRewriter::UpdateStats(ParseResult result, Direction direction) { @@ -133,23 +143,25 @@ SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( // Create temporary RBSP decoded buffer of the payload (exlcuding the // leading nalu type header byte (the SpsParser uses only the payload). std::vector rbsp_buffer = H264::ParseRbsp(buffer, length); - rtc::BitBuffer source_buffer(rbsp_buffer.data(), rbsp_buffer.size()); + BitstreamReader source_buffer(rbsp_buffer); absl::optional sps_state = - SpsParser::ParseSpsUpToVui(&source_buffer); + SpsParser::ParseSpsUpToVui(source_buffer); if (!sps_state) return ParseResult::kFailure; *sps = sps_state; - // We're going to completely muck up alignment, so we need a BitBuffer to - // write with. + // We're going to completely muck up alignment, so we need a BitBufferWriter + // to write with. rtc::Buffer out_buffer(length + kMaxVuiSpsIncrease); rtc::BitBufferWriter sps_writer(out_buffer.data(), out_buffer.size()); // Check how far the SpsParser has read, and copy that data in bulk. - size_t byte_offset; - size_t bit_offset; - source_buffer.GetCurrentOffset(&byte_offset, &bit_offset); + RTC_DCHECK(source_buffer.Ok()); + size_t total_bit_offset = + rbsp_buffer.size() * 8 - source_buffer.RemainingBitCount(); + size_t byte_offset = total_bit_offset / 8; + size_t bit_offset = total_bit_offset % 8; memcpy(out_buffer.data(), rbsp_buffer.data(), byte_offset + (bit_offset > 0 ? 1 : 0)); // OK to copy the last bits. @@ -164,8 +176,8 @@ SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( sps_writer.Seek(byte_offset, bit_offset); ParseResult vui_updated; - if (!CopyAndRewriteVui(*sps_state, &source_buffer, &sps_writer, color_space, - &vui_updated)) { + if (!CopyAndRewriteVui(*sps_state, source_buffer, sps_writer, color_space, + vui_updated)) { RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI."; return ParseResult::kFailure; } @@ -175,7 +187,7 @@ SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( return vui_updated; } - if (!CopyRemainingBits(&source_buffer, &sps_writer)) { + if (!CopyRemainingBits(source_buffer, sps_writer)) { RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI."; return ParseResult::kFailure; } @@ -271,19 +283,16 @@ rtc::Buffer SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite( namespace { bool CopyAndRewriteVui(const SpsParser::SpsState& sps, - rtc::BitBuffer* source, - rtc::BitBufferWriter* destination, + BitstreamReader& source, + rtc::BitBufferWriter& destination, const webrtc::ColorSpace* color_space, - SpsVuiRewriter::ParseResult* out_vui_rewritten) { - uint32_t golomb_tmp; - uint32_t bits_tmp; - - *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk; + SpsVuiRewriter::ParseResult& out_vui_rewritten) { + out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk; // // vui_parameters_present_flag: u(1) // - RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1)); // ********* IMPORTANT! ********** // Now we're at the VUI, so we want to (1) add it if it isn't present, and @@ -292,154 +301,140 @@ bool CopyAndRewriteVui(const SpsParser::SpsState& sps, // Write a simple VUI with the parameters we want and 0 for all other flags. // aspect_ratio_info_present_flag, overscan_info_present_flag. Both u(1). - RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 2)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(0, 2)); uint32_t video_signal_type_present_flag = (color_space && !IsDefaultColorSpace(*color_space)) ? 1 : 0; RETURN_FALSE_ON_FAIL( - destination->WriteBits(video_signal_type_present_flag, 1)); + destination.WriteBits(video_signal_type_present_flag, 1)); if (video_signal_type_present_flag) { RETURN_FALSE_ON_FAIL(AddVideoSignalTypeInfo(destination, *color_space)); } // chroma_loc_info_present_flag, timing_info_present_flag, // nal_hrd_parameters_present_flag, vcl_hrd_parameters_present_flag, // pic_struct_present_flag, All u(1) - RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 5)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(0, 5)); // bitstream_restriction_flag: u(1) - RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1)); RETURN_FALSE_ON_FAIL( - AddBitstreamRestriction(destination, sps.max_num_ref_frames)); + AddBitstreamRestriction(&destination, sps.max_num_ref_frames)); - *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; + out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; } else { // Parse out the full VUI. // aspect_ratio_info_present_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); - if (bits_tmp == 1) { + uint32_t aspect_ratio_info_present_flag = CopyBits(1, source, destination); + if (aspect_ratio_info_present_flag) { // aspect_ratio_idc: u(8) - COPY_BITS(source, destination, bits_tmp, 8); - if (bits_tmp == 255u) { // Extended_SAR + uint8_t aspect_ratio_idc = CopyUInt8(source, destination); + if (aspect_ratio_idc == 255u) { // Extended_SAR // sar_width/sar_height: u(16) each. - COPY_BITS(source, destination, bits_tmp, 32); + CopyBits(32, source, destination); } } // overscan_info_present_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); - if (bits_tmp == 1) { + uint32_t overscan_info_present_flag = CopyBits(1, source, destination); + if (overscan_info_present_flag) { // overscan_appropriate_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); } CopyOrRewriteVideoSignalTypeInfo(source, destination, color_space, out_vui_rewritten); // chroma_loc_info_present_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); - if (bits_tmp == 1) { + uint32_t chroma_loc_info_present_flag = CopyBits(1, source, destination); + if (chroma_loc_info_present_flag == 1) { // chroma_sample_loc_type_(top|bottom)_field: ue(v) each. - COPY_EXP_GOLOMB(source, destination, golomb_tmp); - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); + CopyExpGolomb(source, destination); } // timing_info_present_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); - if (bits_tmp == 1) { + uint32_t timing_info_present_flag = CopyBits(1, source, destination); + if (timing_info_present_flag == 1) { // num_units_in_tick, time_scale: u(32) each - COPY_BITS(source, destination, bits_tmp, 32); - COPY_BITS(source, destination, bits_tmp, 32); + CopyBits(32, source, destination); + CopyBits(32, source, destination); // fixed_frame_rate_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); } // nal_hrd_parameters_present_flag: u(1) - uint32_t nal_hrd_parameters_present_flag; - COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1); + uint32_t nal_hrd_parameters_present_flag = CopyBits(1, source, destination); if (nal_hrd_parameters_present_flag == 1) { - RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination)); + CopyHrdParameters(source, destination); } // vcl_hrd_parameters_present_flag: u(1) - uint32_t vcl_hrd_parameters_present_flag; - COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1); + uint32_t vcl_hrd_parameters_present_flag = CopyBits(1, source, destination); if (vcl_hrd_parameters_present_flag == 1) { - RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination)); + CopyHrdParameters(source, destination); } if (nal_hrd_parameters_present_flag == 1 || vcl_hrd_parameters_present_flag == 1) { // low_delay_hrd_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); } // pic_struct_present_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); // bitstream_restriction_flag: u(1) - uint32_t bitstream_restriction_flag; - RETURN_FALSE_ON_FAIL(source->ReadBits(1, bitstream_restriction_flag)); - RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); + uint32_t bitstream_restriction_flag = source.ReadBit(); + RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1)); if (bitstream_restriction_flag == 0) { // We're adding one from scratch. RETURN_FALSE_ON_FAIL( - AddBitstreamRestriction(destination, sps.max_num_ref_frames)); - *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; + AddBitstreamRestriction(&destination, sps.max_num_ref_frames)); + out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; } else { // We're replacing. // motion_vectors_over_pic_boundaries_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); // max_bytes_per_pic_denom: ue(v) - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); // max_bits_per_mb_denom: ue(v) - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); // log2_max_mv_length_horizontal: ue(v) - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); // log2_max_mv_length_vertical: ue(v) - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); // ********* IMPORTANT! ********** // The next two are the ones we need to set to low numbers: // max_num_reorder_frames: ue(v) // max_dec_frame_buffering: ue(v) // However, if they are already set to no greater than the numbers we // want, then we don't need to be rewriting. - uint32_t max_num_reorder_frames, max_dec_frame_buffering; + uint32_t max_num_reorder_frames = source.ReadExponentialGolomb(); + uint32_t max_dec_frame_buffering = source.ReadExponentialGolomb(); + RETURN_FALSE_ON_FAIL(destination.WriteExponentialGolomb(0)); RETURN_FALSE_ON_FAIL( - source->ReadExponentialGolomb(max_num_reorder_frames)); - RETURN_FALSE_ON_FAIL( - source->ReadExponentialGolomb(max_dec_frame_buffering)); - RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0)); - RETURN_FALSE_ON_FAIL( - destination->WriteExponentialGolomb(sps.max_num_ref_frames)); + destination.WriteExponentialGolomb(sps.max_num_ref_frames)); if (max_num_reorder_frames != 0 || max_dec_frame_buffering > sps.max_num_ref_frames) { - *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; + out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; } } } - return true; + return source.Ok(); } // Copies a VUI HRD parameters segment. -bool CopyHrdParameters(rtc::BitBuffer* source, - rtc::BitBufferWriter* destination) { - uint32_t golomb_tmp; - uint32_t bits_tmp; - +void CopyHrdParameters(BitstreamReader& source, + rtc::BitBufferWriter& destination) { // cbp_cnt_minus1: ue(v) - uint32_t cbp_cnt_minus1; - COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1); + uint32_t cbp_cnt_minus1 = CopyExpGolomb(source, destination); // bit_rate_scale and cbp_size_scale: u(4) each - COPY_BITS(source, destination, bits_tmp, 8); - for (size_t i = 0; i <= cbp_cnt_minus1; ++i) { + CopyBits(8, source, destination); + for (size_t i = 0; source.Ok() && i <= cbp_cnt_minus1; ++i) { // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each - COPY_EXP_GOLOMB(source, destination, golomb_tmp); - COPY_EXP_GOLOMB(source, destination, golomb_tmp); + CopyExpGolomb(source, destination); + CopyExpGolomb(source, destination); // cbr_flag: u(1) - COPY_BITS(source, destination, bits_tmp, 1); + CopyBits(1, source, destination); } // initial_cbp_removal_delay_length_minus1: u(5) - COPY_BITS(source, destination, bits_tmp, 5); // cbp_removal_delay_length_minus1: u(5) - COPY_BITS(source, destination, bits_tmp, 5); // dbp_output_delay_length_minus1: u(5) - COPY_BITS(source, destination, bits_tmp, 5); // time_offset_length: u(5) - COPY_BITS(source, destination, bits_tmp, 5); - return true; + CopyBits(5 * 4, source, destination); } // These functions are similar to webrtc::H264SpsParser::Parse, and based on the @@ -479,51 +474,51 @@ bool IsDefaultColorSpace(const ColorSpace& color_space) { color_space.matrix() == ColorSpace::MatrixID::kUnspecified; } -bool AddVideoSignalTypeInfo(rtc::BitBufferWriter* destination, +bool AddVideoSignalTypeInfo(rtc::BitBufferWriter& destination, const ColorSpace& color_space) { // video_format: u(3). - RETURN_FALSE_ON_FAIL(destination->WriteBits(5, 3)); // 5 = Unspecified + RETURN_FALSE_ON_FAIL(destination.WriteBits(5, 3)); // 5 = Unspecified // video_full_range_flag: u(1) - RETURN_FALSE_ON_FAIL(destination->WriteBits( + RETURN_FALSE_ON_FAIL(destination.WriteBits( color_space.range() == ColorSpace::RangeID::kFull ? 1 : 0, 1)); // colour_description_present_flag: u(1) - RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(1, 1)); // colour_primaries: u(8) RETURN_FALSE_ON_FAIL( - destination->WriteUInt8(static_cast(color_space.primaries()))); + destination.WriteUInt8(static_cast(color_space.primaries()))); // transfer_characteristics: u(8) RETURN_FALSE_ON_FAIL( - destination->WriteUInt8(static_cast(color_space.transfer()))); + destination.WriteUInt8(static_cast(color_space.transfer()))); // matrix_coefficients: u(8) RETURN_FALSE_ON_FAIL( - destination->WriteUInt8(static_cast(color_space.matrix()))); + destination.WriteUInt8(static_cast(color_space.matrix()))); return true; } bool CopyOrRewriteVideoSignalTypeInfo( - rtc::BitBuffer* source, - rtc::BitBufferWriter* destination, + BitstreamReader& source, + rtc::BitBufferWriter& destination, const ColorSpace* color_space, - SpsVuiRewriter::ParseResult* out_vui_rewritten) { + SpsVuiRewriter::ParseResult& out_vui_rewritten) { // Read. - uint32_t video_signal_type_present_flag; uint32_t video_format = 5; // H264 default: unspecified uint32_t video_full_range_flag = 0; // H264 default: limited uint32_t colour_description_present_flag = 0; uint8_t colour_primaries = 3; // H264 default: unspecified uint8_t transfer_characteristics = 3; // H264 default: unspecified uint8_t matrix_coefficients = 3; // H264 default: unspecified - RETURN_FALSE_ON_FAIL(source->ReadBits(1, video_signal_type_present_flag)); + uint32_t video_signal_type_present_flag = source.ReadBit(); if (video_signal_type_present_flag) { - RETURN_FALSE_ON_FAIL(source->ReadBits(3, video_format)); - RETURN_FALSE_ON_FAIL(source->ReadBits(1, video_full_range_flag)); - RETURN_FALSE_ON_FAIL(source->ReadBits(1, colour_description_present_flag)); + video_format = source.ReadBits(3); + video_full_range_flag = source.ReadBit(); + colour_description_present_flag = source.ReadBit(); if (colour_description_present_flag) { - RETURN_FALSE_ON_FAIL(source->ReadUInt8(colour_primaries)); - RETURN_FALSE_ON_FAIL(source->ReadUInt8(transfer_characteristics)); - RETURN_FALSE_ON_FAIL(source->ReadUInt8(matrix_coefficients)); + colour_primaries = source.Read(); + transfer_characteristics = source.Read(); + matrix_coefficients = source.Read(); } } + RETURN_FALSE_ON_FAIL(source.Ok()); // Update. uint32_t video_signal_type_present_flag_override = @@ -564,19 +559,19 @@ bool CopyOrRewriteVideoSignalTypeInfo( // Write. RETURN_FALSE_ON_FAIL( - destination->WriteBits(video_signal_type_present_flag_override, 1)); + destination.WriteBits(video_signal_type_present_flag_override, 1)); if (video_signal_type_present_flag_override) { - RETURN_FALSE_ON_FAIL(destination->WriteBits(video_format_override, 3)); + RETURN_FALSE_ON_FAIL(destination.WriteBits(video_format_override, 3)); RETURN_FALSE_ON_FAIL( - destination->WriteBits(video_full_range_flag_override, 1)); + destination.WriteBits(video_full_range_flag_override, 1)); RETURN_FALSE_ON_FAIL( - destination->WriteBits(colour_description_present_flag_override, 1)); + destination.WriteBits(colour_description_present_flag_override, 1)); if (colour_description_present_flag_override) { - RETURN_FALSE_ON_FAIL(destination->WriteUInt8(colour_primaries_override)); + RETURN_FALSE_ON_FAIL(destination.WriteUInt8(colour_primaries_override)); RETURN_FALSE_ON_FAIL( - destination->WriteUInt8(transfer_characteristics_override)); + destination.WriteUInt8(transfer_characteristics_override)); RETURN_FALSE_ON_FAIL( - destination->WriteUInt8(matrix_coefficients_override)); + destination.WriteUInt8(matrix_coefficients_override)); } } @@ -589,27 +584,26 @@ bool CopyOrRewriteVideoSignalTypeInfo( colour_primaries_override != colour_primaries || transfer_characteristics_override != transfer_characteristics || matrix_coefficients_override != matrix_coefficients) { - *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; + out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten; } return true; } -bool CopyRemainingBits(rtc::BitBuffer* source, - rtc::BitBufferWriter* destination) { - uint32_t bits_tmp; +bool CopyRemainingBits(BitstreamReader& source, + rtc::BitBufferWriter& destination) { // Try to get at least the destination aligned. - if (source->RemainingBitCount() > 0 && source->RemainingBitCount() % 8 != 0) { - size_t misaligned_bits = source->RemainingBitCount() % 8; - COPY_BITS(source, destination, bits_tmp, misaligned_bits); + if (source.RemainingBitCount() > 0 && source.RemainingBitCount() % 8 != 0) { + size_t misaligned_bits = source.RemainingBitCount() % 8; + CopyBits(misaligned_bits, source, destination); } - while (source->RemainingBitCount() > 0) { - auto count = rtc::SafeMin(32u, source->RemainingBitCount()); - COPY_BITS(source, destination, bits_tmp, count); + while (source.RemainingBitCount() > 0) { + int count = std::min(32, source.RemainingBitCount()); + CopyBits(count, source, destination); } // TODO(noahric): The last byte could be all zeroes now, which we should just // strip. - return true; + return source.Ok(); } } // namespace diff --git a/common_video/include/video_frame_buffer_pool.h b/common_video/include/video_frame_buffer_pool.h index 539a6cc0f3..f26a9f7be7 100644 --- a/common_video/include/video_frame_buffer_pool.h +++ b/common_video/include/video_frame_buffer_pool.h @@ -17,6 +17,7 @@ #include "api/scoped_refptr.h" #include "api/video/i420_buffer.h" +#include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/race_checker.h" #include "rtc_base/ref_counted_object.h" @@ -43,6 +44,7 @@ class VideoFrameBufferPool { // and there are less than `max_number_of_buffers` pending, a buffer is // created. Returns null otherwise. rtc::scoped_refptr CreateI420Buffer(int width, int height); + rtc::scoped_refptr CreateI444Buffer(int width, int height); rtc::scoped_refptr CreateNV12Buffer(int width, int height); // Changes the max amount of buffers in the pool to the new value. diff --git a/common_video/incoming_video_stream.cc b/common_video/incoming_video_stream.cc index 15c668e78e..1511c9f30c 100644 --- a/common_video/incoming_video_stream.cc +++ b/common_video/incoming_video_stream.cc @@ -57,7 +57,8 @@ void IncomingVideoStream::Dequeue() { if (render_buffers_.HasPendingFrames()) { uint32_t wait_time = render_buffers_.TimeToNextFrameRelease(); - incoming_render_queue_.PostDelayedTask([this]() { Dequeue(); }, wait_time); + incoming_render_queue_.PostDelayedHighPrecisionTask([this]() { Dequeue(); }, + wait_time); } } diff --git a/common_video/libyuv/webrtc_libyuv.cc b/common_video/libyuv/webrtc_libyuv.cc index cc6a71a61c..2e10a60776 100644 --- a/common_video/libyuv/webrtc_libyuv.cc +++ b/common_video/libyuv/webrtc_libyuv.cc @@ -45,7 +45,7 @@ size_t CalcBufferSize(VideoType type, int width, int height) { buffer_size = width * height * 4; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } return buffer_size; @@ -106,7 +106,7 @@ int ConvertVideoType(VideoType video_type) { case VideoType::kBGRA: return libyuv::FOURCC_BGRA; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return libyuv::FOURCC_ANY; } diff --git a/common_video/video_frame_buffer_pool.cc b/common_video/video_frame_buffer_pool.cc index a450bd1e4b..267cab1a71 100644 --- a/common_video/video_frame_buffer_pool.cc +++ b/common_video/video_frame_buffer_pool.cc @@ -20,18 +20,23 @@ namespace { bool HasOneRef(const rtc::scoped_refptr& buffer) { // Cast to rtc::RefCountedObject is safe because this function is only called // on locally created VideoFrameBuffers, which are either - // `rtc::RefCountedObject` or `rtc::RefCountedObject`. + // `rtc::RefCountedObject`, `rtc::RefCountedObject` or + // `rtc::RefCountedObject`. switch (buffer->type()) { case VideoFrameBuffer::Type::kI420: { return static_cast*>(buffer.get()) ->HasOneRef(); } + case VideoFrameBuffer::Type::kI444: { + return static_cast*>(buffer.get()) + ->HasOneRef(); + } case VideoFrameBuffer::Type::kNV12: { return static_cast*>(buffer.get()) ->HasOneRef(); } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return false; } @@ -116,6 +121,37 @@ rtc::scoped_refptr VideoFrameBufferPool::CreateI420Buffer( return buffer; } +rtc::scoped_refptr VideoFrameBufferPool::CreateI444Buffer( + int width, + int height) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); + + rtc::scoped_refptr existing_buffer = + GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI444); + if (existing_buffer) { + // Cast is safe because the only way kI444 buffer is created is + // in the same function below, where |RefCountedObject| + // is created. + rtc::RefCountedObject* raw_buffer = + static_cast*>(existing_buffer.get()); + // Creates a new scoped_refptr, which is also pointing to the same + // RefCountedObject as buffer, increasing ref count. + return rtc::scoped_refptr(raw_buffer); + } + + if (buffers_.size() >= max_number_of_buffers_) + return nullptr; + // Allocate new buffer. + rtc::scoped_refptr buffer = + rtc::make_ref_counted(width, height); + + if (zero_initialize_) + buffer->InitializeData(); + + buffers_.push_back(buffer); + return buffer; +} + rtc::scoped_refptr VideoFrameBufferPool::CreateNV12Buffer( int width, int height) { diff --git a/common_video/video_frame_unittest.cc b/common_video/video_frame_unittest.cc index b82c14716c..b14ec599b7 100644 --- a/common_video/video_frame_unittest.cc +++ b/common_video/video_frame_unittest.cc @@ -15,6 +15,7 @@ #include "api/video/i010_buffer.h" #include "api/video/i420_buffer.h" +#include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/time_utils.h" #include "test/fake_texture_frame.h" @@ -25,114 +26,11 @@ namespace webrtc { namespace { -// Helper class to delegate calls to appropriate container. -class PlanarYuvBufferFactory { - public: - static rtc::scoped_refptr Create(VideoFrameBuffer::Type type, - int width, - int height) { - switch (type) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Create(width, height); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Create(width, height); - default: - RTC_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr Copy(const VideoFrameBuffer& src) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Copy(src); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Copy(*src.GetI010()); - default: - RTC_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr Rotate(const VideoFrameBuffer& src, - VideoRotation rotation) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Rotate(src, rotation); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Rotate(*src.GetI010(), rotation); - default: - RTC_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr CropAndScaleFrom( - const VideoFrameBuffer& src, - int offset_x, - int offset_y, - int crop_width, - int crop_height) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: { - rtc::scoped_refptr buffer = - I420Buffer::Create(crop_width, crop_height); - buffer->CropAndScaleFrom(*src.GetI420(), offset_x, offset_y, crop_width, - crop_height); - return buffer; - } - case VideoFrameBuffer::Type::kI010: { - rtc::scoped_refptr buffer = - I010Buffer::Create(crop_width, crop_height); - buffer->CropAndScaleFrom(*src.GetI010(), offset_x, offset_y, crop_width, - crop_height); - return buffer; - } - default: - RTC_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr CropAndScaleFrom( - const VideoFrameBuffer& src, - int crop_width, - int crop_height) { - const int out_width = - std::min(src.width(), crop_width * src.height() / crop_height); - const int out_height = - std::min(src.height(), crop_height * src.width() / crop_width); - return CropAndScaleFrom(src, (src.width() - out_width) / 2, - (src.height() - out_height) / 2, out_width, - out_height); - } - - static rtc::scoped_refptr - ScaleFrom(const VideoFrameBuffer& src, int crop_width, int crop_height) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: { - rtc::scoped_refptr buffer = - I420Buffer::Create(crop_width, crop_height); - buffer->ScaleFrom(*src.GetI420()); - return buffer; - } - case VideoFrameBuffer::Type::kI010: { - rtc::scoped_refptr buffer = - I010Buffer::Create(crop_width, crop_height); - buffer->ScaleFrom(*src.GetI010()); - return buffer; - } - default: - RTC_NOTREACHED(); - } - return nullptr; - } -}; - -rtc::scoped_refptr CreateGradient(VideoFrameBuffer::Type type, - int width, - int height) { - rtc::scoped_refptr buffer(I420Buffer::Create(width, height)); +// Helper function to create a buffer and fill it with a gradient for +// PlanarYuvBuffer based buffers. +template +rtc::scoped_refptr CreateGradient(int width, int height) { + rtc::scoped_refptr buffer(T::Create(width, height)); // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { @@ -150,13 +48,10 @@ rtc::scoped_refptr CreateGradient(VideoFrameBuffer::Type type, 255 * y / (chroma_height - 1); } } - if (type == VideoFrameBuffer::Type::kI420) - return buffer; - - RTC_DCHECK(type == VideoFrameBuffer::Type::kI010); - return I010Buffer::Copy(*buffer); + return buffer; } +// Helper function to create a buffer and fill it with a gradient. rtc::scoped_refptr CreateNV12Gradient(int width, int height) { rtc::scoped_refptr buffer(NV12Buffer::Create(width, height)); @@ -183,13 +78,16 @@ rtc::scoped_refptr CreateNV12Gradient(int width, // The offsets and sizes describe the rectangle extracted from the // original (gradient) frame, in relative coordinates where the // original frame correspond to the unit square, 0.0 <= x, y < 1.0. -void CheckCrop(const webrtc::I420BufferInterface& frame, +template +void CheckCrop(const T& frame, double offset_x, double offset_y, double rel_width, double rel_height) { int width = frame.width(); int height = frame.height(); + int plane_divider = frame.type() == VideoFrameBuffer::Type::kI444 ? 1 : 2; + // Check that pixel values in the corners match the gradient used // for initialization. for (int i = 0; i < 2; i++) { @@ -204,18 +102,23 @@ void CheckCrop(const webrtc::I420BufferInterface& frame, EXPECT_NEAR(frame.DataY()[x + y * frame.StrideY()] / 256.0, (orig_x + orig_y) / 2, 0.02); - EXPECT_NEAR(frame.DataU()[x / 2 + (y / 2) * frame.StrideU()] / 256.0, + EXPECT_NEAR(frame.DataU()[x / plane_divider + + (y / plane_divider) * frame.StrideU()] / + 256.0, orig_x, 0.02); - EXPECT_NEAR(frame.DataV()[x / 2 + (y / 2) * frame.StrideV()] / 256.0, + EXPECT_NEAR(frame.DataV()[x / plane_divider + + (y / plane_divider) * frame.StrideV()] / + 256.0, orig_y, 0.02); } } } +template void CheckRotate(int width, int height, webrtc::VideoRotation rotation, - const webrtc::I420BufferInterface& rotated) { + const T& rotated) { int rotated_width = width; int rotated_height = height; @@ -238,56 +141,18 @@ void CheckRotate(int width, } colors[] = {{0, 0, 0}, {127, 255, 0}, {255, 255, 255}, {127, 0, 255}}; int corner_offset = static_cast(rotation) / 90; + int plane_divider = rotated.type() == VideoFrameBuffer::Type::kI444 ? 1 : 2; for (int i = 0; i < 4; i++) { int j = (i + corner_offset) % 4; int x = corners[j].x * (rotated_width - 1); int y = corners[j].y * (rotated_height - 1); EXPECT_EQ(colors[i].y, rotated.DataY()[x + y * rotated.StrideY()]); EXPECT_EQ(colors[i].u, - rotated.DataU()[(x / 2) + (y / 2) * rotated.StrideU()]); + rotated.DataU()[(x / plane_divider) + + (y / plane_divider) * rotated.StrideU()]); EXPECT_EQ(colors[i].v, - rotated.DataV()[(x / 2) + (y / 2) * rotated.StrideV()]); - } -} - -int GetU(rtc::scoped_refptr buf, int col, int row) { - if (buf->type() == VideoFrameBuffer::Type::kI420) { - return buf->GetI420() - ->DataU()[row / 2 * buf->GetI420()->StrideU() + col / 2]; - } else { - return buf->GetI010() - ->DataU()[row / 2 * buf->GetI010()->StrideU() + col / 2]; - } -} - -int GetV(rtc::scoped_refptr buf, int col, int row) { - if (buf->type() == VideoFrameBuffer::Type::kI420) { - return buf->GetI420() - ->DataV()[row / 2 * buf->GetI420()->StrideV() + col / 2]; - } else { - return buf->GetI010() - ->DataV()[row / 2 * buf->GetI010()->StrideV() + col / 2]; - } -} - -int GetY(rtc::scoped_refptr buf, int col, int row) { - if (buf->type() == VideoFrameBuffer::Type::kI420) { - return buf->GetI420()->DataY()[row * buf->GetI420()->StrideY() + col]; - } else { - return buf->GetI010()->DataY()[row * buf->GetI010()->StrideY() + col]; - } -} - -void PasteFromBuffer(PlanarYuvBuffer* canvas, - const PlanarYuvBuffer& picture, - int offset_col, - int offset_row) { - if (canvas->type() == VideoFrameBuffer::Type::kI420) { - I420Buffer* buf = static_cast(canvas); - buf->PasteFrom(*picture.GetI420(), offset_col, offset_row); - } else { - I010Buffer* buf = static_cast(canvas); - buf->PasteFrom(*picture.GetI010(), offset_col, offset_row); + rotated.DataV()[(x / plane_divider) + + (y / plane_divider) * rotated.StrideV()]); } } @@ -385,138 +250,142 @@ TEST(TestVideoFrame, TextureInitialValues) { EXPECT_EQ(20, frame.timestamp_us()); } -class TestPlanarYuvBuffer - : public ::testing::TestWithParam {}; +template +class TestPlanarYuvBuffer : public ::testing::Test {}; +TYPED_TEST_SUITE_P(TestPlanarYuvBuffer); -rtc::scoped_refptr CreateAndFillBuffer() { - auto buf = I420Buffer::Create(20, 10); +template +rtc::scoped_refptr CreateAndFillBuffer() { + auto buf = T::Create(20, 10); memset(buf->MutableDataY(), 1, 200); - memset(buf->MutableDataU(), 2, 50); - memset(buf->MutableDataV(), 3, 50); + + if (buf->type() == VideoFrameBuffer::Type::kI444) { + memset(buf->MutableDataU(), 2, 200); + memset(buf->MutableDataV(), 3, 200); + } else { + memset(buf->MutableDataU(), 2, 50); + memset(buf->MutableDataV(), 3, 50); + } + return buf; } -TEST_P(TestPlanarYuvBuffer, Copy) { - rtc::scoped_refptr buf1; - switch (GetParam()) { - case VideoFrameBuffer::Type::kI420: { - buf1 = CreateAndFillBuffer(); - break; - } - case VideoFrameBuffer::Type::kI010: { - buf1 = I010Buffer::Copy(*CreateAndFillBuffer()); - break; - } - default: - RTC_NOTREACHED(); - } - - rtc::scoped_refptr buf2 = - PlanarYuvBufferFactory::Copy(*buf1); - EXPECT_TRUE(test::FrameBufsEqual(buf1->ToI420(), buf2->ToI420())); +TYPED_TEST_P(TestPlanarYuvBuffer, Copy) { + rtc::scoped_refptr buf1 = CreateAndFillBuffer(); + rtc::scoped_refptr buf2 = TypeParam::Copy(*buf1); + EXPECT_TRUE(test::FrameBufsEqual(buf1, buf2)); } -TEST_P(TestPlanarYuvBuffer, Scale) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); - - // Pure scaling, no cropping. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::ScaleFrom(*buf, 150, 75); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.0, 1.0, 1.0); -} - -TEST_P(TestPlanarYuvBuffer, CropXCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); +TYPED_TEST_P(TestPlanarYuvBuffer, CropXCenter) { + rtc::scoped_refptr buf = CreateGradient(200, 100); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 50, 0, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.25, 0.0, 0.5, 1.0); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 50, 0, 100, 100); + CheckCrop(*scaled_buffer, 0.25, 0.0, 0.5, 1.0); } -TEST_P(TestPlanarYuvBuffer, CropXNotCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); +TYPED_TEST_P(TestPlanarYuvBuffer, CropXNotCenter) { + rtc::scoped_refptr buf = CreateGradient(200, 100); // Non-center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 25, 0, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.125, 0.0, 0.5, 1.0); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 25, 0, 100, 100); + CheckCrop(*scaled_buffer, 0.125, 0.0, 0.5, 1.0); } -TEST_P(TestPlanarYuvBuffer, CropYCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 100, 200); +TYPED_TEST_P(TestPlanarYuvBuffer, CropYCenter) { + rtc::scoped_refptr buf = CreateGradient(100, 200); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 0, 50, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.25, 1.0, 0.5); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 0, 50, 100, 100); + CheckCrop(*scaled_buffer, 0.0, 0.25, 1.0, 0.5); } -TEST_P(TestPlanarYuvBuffer, CropYNotCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 100, 200); +TYPED_TEST_P(TestPlanarYuvBuffer, CropYNotCenter) { + rtc::scoped_refptr buf = CreateGradient(100, 200); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 0, 25, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.125, 1.0, 0.5); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 0, 25, 100, 100); + CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.5); } -TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 640, 480); +TYPED_TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) { + const int buffer_width = 640; + const int buffer_height = 480; + const int crop_width = 320; + const int crop_height = 180; + rtc::scoped_refptr buf = CreateGradient(640, 480); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 320, 180); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.125, 1.0, 0.75); + const int out_width = + std::min(buffer_width, crop_width * buffer_height / crop_height); + const int out_height = + std::min(buffer_height, crop_height * buffer_width / crop_width); + rtc::scoped_refptr scaled_buffer = + TypeParam::Create(out_width, out_height); + scaled_buffer->CropAndScaleFrom(*buf, (buffer_width - out_width) / 2, + (buffer_height - out_height) / 2, out_width, + out_height); + CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.75); } -TEST_P(TestPlanarYuvBuffer, PastesIntoBuffer) { - const int kOffsetx = 20; - const int kOffsety = 30; - const int kPicSize = 20; - const int kWidth = 160; - const int kHeight = 80; - rtc::scoped_refptr buf = - CreateGradient(GetParam(), kWidth, kHeight); +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBuffer, + Copy, + CropXCenter, + CropXNotCenter, + CropYCenter, + CropYNotCenter, + CropAndScale16x9); - rtc::scoped_refptr original = - CreateGradient(GetParam(), kWidth, kHeight); +using TestTypesAll = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBuffer, TestTypesAll); - rtc::scoped_refptr picture = - CreateGradient(GetParam(), kPicSize, kPicSize); +template +class TestPlanarYuvBufferScale : public ::testing::Test {}; +TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale); - rtc::scoped_refptr odd_picture = - CreateGradient(GetParam(), kPicSize + 1, kPicSize - 1); +TYPED_TEST_P(TestPlanarYuvBufferScale, Scale) { + rtc::scoped_refptr buf = CreateGradient(200, 100); - PasteFromBuffer(buf.get(), *picture, kOffsetx, kOffsety); + // Pure scaling, no cropping. + rtc::scoped_refptr scaled_buffer = TypeParam::Create(150, 75); + scaled_buffer->ScaleFrom(*buf); + CheckCrop(*scaled_buffer, 0.0, 0.0, 1.0, 1.0); +} - for (int i = 0; i < kWidth; ++i) { - for (int j = 0; j < kHeight; ++j) { - bool is_inside = i >= kOffsetx && i < kOffsetx + kPicSize && - j >= kOffsety && j < kOffsety + kPicSize; - if (!is_inside) { - EXPECT_EQ(GetU(original, i, j), GetU(buf, i, j)); - EXPECT_EQ(GetV(original, i, j), GetV(buf, i, j)); - EXPECT_EQ(GetY(original, i, j), GetY(buf, i, j)); - } else { - EXPECT_EQ(GetU(picture, i - kOffsetx, j - kOffsety), GetU(buf, i, j)); - EXPECT_EQ(GetV(picture, i - kOffsetx, j - kOffsety), GetV(buf, i, j)); - EXPECT_EQ(GetY(picture, i - kOffsetx, j - kOffsety), GetY(buf, i, j)); - } - } +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale, Scale); + +using TestTypesScale = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBufferScale, TestTypesScale); + +template +class TestPlanarYuvBufferRotate : public ::testing::Test { + public: + std::vector RotationParams = { + kVideoRotation_0, kVideoRotation_90, kVideoRotation_180, + kVideoRotation_270}; +}; + +TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate); + +TYPED_TEST_P(TestPlanarYuvBufferRotate, Rotates) { + for (const webrtc::VideoRotation& rotation : this->RotationParams) { + rtc::scoped_refptr buffer = CreateGradient(640, 480); + rtc::scoped_refptr rotated_buffer = + TypeParam::Rotate(*buffer, rotation); + CheckRotate(640, 480, rotation, *rotated_buffer); } } -INSTANTIATE_TEST_SUITE_P(All, - TestPlanarYuvBuffer, - ::testing::Values(VideoFrameBuffer::Type::kI420, - VideoFrameBuffer::Type::kI010)); +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate, Rotates); + +using TestTypesRotate = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(Rotate, + TestPlanarYuvBufferRotate, + TestTypesRotate); TEST(TestNV12Buffer, CropAndScale) { const int kSourceWidth = 640; @@ -547,29 +416,6 @@ TEST(TestNV12Buffer, CropAndScale) { kRelativeHeight); } -class TestPlanarYuvBufferRotate - : public ::testing::TestWithParam< - std::tuple> {}; - -TEST_P(TestPlanarYuvBufferRotate, Rotates) { - const webrtc::VideoRotation rotation = std::get<0>(GetParam()); - const VideoFrameBuffer::Type type = std::get<1>(GetParam()); - rtc::scoped_refptr buffer = CreateGradient(type, 640, 480); - rtc::scoped_refptr rotated_buffer = - PlanarYuvBufferFactory::Rotate(*buffer, rotation); - CheckRotate(640, 480, rotation, *rotated_buffer->ToI420()); -} - -INSTANTIATE_TEST_SUITE_P( - Rotate, - TestPlanarYuvBufferRotate, - ::testing::Combine(::testing::Values(kVideoRotation_0, - kVideoRotation_90, - kVideoRotation_180, - kVideoRotation_270), - ::testing::Values(VideoFrameBuffer::Type::kI420, - VideoFrameBuffer::Type::kI010))); - TEST(TestUpdateRect, CanCompare) { VideoFrame::UpdateRect a = {0, 0, 100, 200}; VideoFrame::UpdateRect b = {0, 0, 100, 200}; diff --git a/docs/native-code/development/contributing.md b/docs/native-code/development/contributing.md new file mode 100644 index 0000000000..9aa7b0a4e8 --- /dev/null +++ b/docs/native-code/development/contributing.md @@ -0,0 +1,99 @@ +# Contributing to the WebRTC project + +## License Agreement + +WebRTC welcomes patches for features and bug fixes! + +For contributors external to Google, follow the instructions given in the +[Google Individual Contributor License Agreement][Google individual CLA]. +In all cases, contributors must sign a contributor license agreement before +a contribution can be accepted. Please complete the agreement for an +[individual][individual] or a [corporation][corporation] as appropriate. + +[Google Individual CLA]: https://cla.developers.google.com/about/google-individual. +[individual]: https://developers.google.com/open-source/cla/individual +[corporation]: https://developers.google.com/open-source/cla/corporate + + +## Instructions + +### Contributing your First Patch +You must do some preparation in order to upload your first CL: + +* [Check out and build the code][check out and build the code] +* Fill in the Contributor agreement (see above) +* If you’ve never submitted code before, you must add your + (or your organization’s in the case the contributor agreement is signed by + your organization) name and contact info to the + [AUTHORS][AUTHORS] file +* Go to [https://webrtc.googlesource.com/new-password](new-password) + and login with your email account. This should be the same account as + returned by `git config user.email` +* Then, run: `git cl creds-check`. If you get any errors, ask for help on + [discuss-webrtc][discuss-webrtc] + +You will not have to repeat the above. After all that, you’re ready to upload: + +[Check out and the build code]: https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/development/index.md +[AUTHORS]: https://webrtc.googlesource.com/src/+/refs/heads/main/AUTHORS +[new-password]: https://webrtc.googlesource.com/new-password +[discuss-webrtc]: https://groups.google.com/forum/#!forum/discuss-webrtc + +### Uploading your First Patch +Now that you have your account set up, you can do the actual upload: + +* Do this: + * Assuming you're on the main branch: + * `git checkout -b my-work-branch` + * Make changes, build locally, run tests locally + * `git commit -am "Changed x, and it is working"` + * `git cl upload` + + This will open a text editor showing all local commit messages, allowing you + to modify it before it becomes the CL description. + + Fill out the bug entry properly. Please specify the issue tracker prefix and + the issue number, separated by a colon, e.g. `webrtc:123` or `chromium:12345`. + If you do not have an issue tracker prefix and an issue number just add `None`. + + Save and close the file to proceed with the upload to the WebRTC + [code review server](https://webrtc-review.googlesource.com/q/status:open). + + The command will print a link like + [https://webrtc-review.googlesource.com/c/src/+/53121][example CL link]. + if everything goes well. + +* Click this CL Link +* If you’re not signed in, click the Sign In button in the top right and sign + in with your email +* Click Start Review and add a reviewer. You can find reviewers in OWNERS files + around the repository (take the one closest to your changes) +* Address any reviewer feedback: + * Make changes, build locally, run tests locally + * `git commit -am "Fixed X and Y"` + * `git cl upload` +* Once the reviewer LGTMs (approves) the patch, ask them to put it into the + commit queue + +NOTICE: On Windows, you’ll need to run the above in a Git bash shell in order +for gclient to find the `.gitcookies` file. + +[example CL link]: https://webrtc-review.googlesource.com/c/src/+/53121 + +### Trybots + +If you're working a lot in WebRTC, you can apply for *try rights*. This means you +can run the *trybots*, which run all the tests on all platforms. To do this, +file a bug using this [template][template-access] and the WebRTC EngProd team +will review your request. + +To run a tryjob, upload a CL as described above and click either CQ dry run or +Choose Trybots in the Gerrit UI. You need to have try rights for this. Otherwise, +ask your reviewer to kick off the bots for you. + +If you encounter any issues with the bots (flakiness, failing unrelated to your change etc), +please file a bug using this [template][template-issue]. + +[template-access]: https://bugs.chromium.org/p/webrtc/issues/entry?template=Get+tryjob+access +[template-issue]: https://bugs.chromium.org/p/webrtc/issues/entry?template=trybot+issue + diff --git a/docs/native-code/development/index.md b/docs/native-code/development/index.md index 3b3940d489..f3cfd556bc 100644 --- a/docs/native-code/development/index.md +++ b/docs/native-code/development/index.md @@ -184,10 +184,13 @@ To find the branch corresponding to a Chrome release check the ## Contributing Patches -Please see [Contributing Fixes][webrtc-contributing] for information on how to run +Please see [Contributing Fixes][contributing] for information on how to run `git cl upload`, getting your patch reviewed, and getting it submitted. You can also find info on how to run trybots and applying for try rights. +[contributing]: https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/development/contributing.md + + ## Chromium Committers Many WebRTC committers are also Chromium committers. To make sure to use the @@ -277,7 +280,6 @@ Target name `turnserver`. Used for unit tests. [webrtc-ios-development]: https://webrtc.googlesource.com/src/+/main/docs/native-code/ios/index.md [chromium-work-branches]: https://www.chromium.org/developers/how-tos/get-the-code/working-with-branches [chromium-work-release-branches]: https://www.chromium.org/developers/how-tos/get-the-code/working-with-release-branches -[webrtc-contributing]: https://webrtc.org/support/contributing/ [depot-tools]: http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up [rfc-5389]: https://tools.ietf.org/html/rfc5389 [rfc-5766]: https://tools.ietf.org/html/rfc5766 diff --git a/examples/BUILD.gn b/examples/BUILD.gn index 8dd998cc3b..d0571c9bfd 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn @@ -894,7 +894,6 @@ if (is_android) { deps = [ ":AppRTCMobile_javalib", "../sdk/android:peerconnection_java", - "//base:base_java_test_support", "//third_party/androidx:androidx_test_core_java", "//third_party/google-truth:google_truth_java", ] diff --git a/examples/aarproject/.idea/misc.xml b/examples/aarproject/.idea/misc.xml index 635999df1e..eab8644d41 100644 --- a/examples/aarproject/.idea/misc.xml +++ b/examples/aarproject/.idea/misc.xml @@ -1,15 +1,15 @@ - @@ -19,7 +19,7 @@ - + @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java b/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java index 5c73b4395c..604e2863d9 100644 --- a/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java +++ b/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java @@ -149,16 +149,10 @@ public class AppRTCProximitySensor implements SensorEventListener { info.append(", resolution: ").append(proximitySensor.getResolution()); info.append(", max range: ").append(proximitySensor.getMaximumRange()); info.append(", min delay: ").append(proximitySensor.getMinDelay()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { - // Added in API level 20. - info.append(", type: ").append(proximitySensor.getStringType()); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Added in API level 21. - info.append(", max delay: ").append(proximitySensor.getMaxDelay()); - info.append(", reporting mode: ").append(proximitySensor.getReportingMode()); - info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); - } + info.append(", type: ").append(proximitySensor.getStringType()); + info.append(", max delay: ").append(proximitySensor.getMaxDelay()); + info.append(", reporting mode: ").append(proximitySensor.getReportingMode()); + info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); Log.d(TAG, info.toString()); } } diff --git a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java index 2da2073e2b..eb5ee8289e 100644 --- a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java +++ b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java @@ -384,7 +384,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven } } - @TargetApi(17) private DisplayMetrics getDisplayMetrics() { DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager windowManager = @@ -393,16 +392,11 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven return displayMetrics; } - @TargetApi(19) private static int getSystemUiVisibility() { - int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - return flags; + return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } - @TargetApi(21) private void startScreenCapture() { MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getApplication().getSystemService( @@ -460,7 +454,6 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven return null; } - @TargetApi(21) private @Nullable VideoCapturer createScreenCapturer() { if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) { reportError("User didn't give permission to capture the screen."); diff --git a/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java b/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java index dd51ab2561..1c64621864 100644 --- a/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java +++ b/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java @@ -73,7 +73,6 @@ import java.util.concurrent.TimeUnit; * correct value, and then returns to back to correct reading. Both when * jumping up and back down we might create faulty CPU load readings. */ -@TargetApi(Build.VERSION_CODES.KITKAT) class CpuMonitor { private static final String TAG = "CpuMonitor"; private static final int MOVING_AVERAGE_SAMPLES = 5; @@ -159,8 +158,7 @@ class CpuMonitor { } public static boolean isSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT - && Build.VERSION.SDK_INT < Build.VERSION_CODES.N; + return Build.VERSION.SDK_INT < Build.VERSION_CODES.N; } public CpuMonitor(Context context) { diff --git a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java index c5e9059b74..7bdce00b2f 100644 --- a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java @@ -46,6 +46,7 @@ import org.webrtc.DefaultVideoDecoderFactory; import org.webrtc.DefaultVideoEncoderFactory; import org.webrtc.EglBase; import org.webrtc.IceCandidate; +import org.webrtc.IceCandidateErrorEvent; import org.webrtc.Logging; import org.webrtc.MediaConstraints; import org.webrtc.MediaStream; @@ -96,7 +97,6 @@ public class PeerConnectionClient { private static final String VIDEO_CODEC_H264_BASELINE = "H264 Baseline"; private static final String VIDEO_CODEC_H264_HIGH = "H264 High"; private static final String VIDEO_CODEC_AV1 = "AV1"; - private static final String VIDEO_CODEC_AV1_SDP_CODEC_NAME = "AV1X"; private static final String AUDIO_CODEC_OPUS = "opus"; private static final String AUDIO_CODEC_ISAC = "ISAC"; private static final String VIDEO_CODEC_PARAM_START_BITRATE = "x-google-start-bitrate"; @@ -443,6 +443,10 @@ public class PeerConnectionClient { decoderFactory = new SoftwareVideoDecoderFactory(); } + // Disable encryption for loopback calls. + if (peerConnectionParameters.loopback) { + options.disableEncryption = true; + } factory = PeerConnectionFactory.builder() .setOptions(options) .setAudioDeviceModule(adm) @@ -601,8 +605,6 @@ public class PeerConnectionClient { rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY; // Use ECDSA encryption. rtcConfig.keyType = PeerConnection.KeyType.ECDSA; - // Enable DTLS for normal calls and disable for loopback calls. - rtcConfig.enableDtlsSrtp = !peerConnectionParameters.loopback; rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN; peerConnection = factory.createPeerConnection(rtcConfig, pcObserver); @@ -985,7 +987,7 @@ public class PeerConnectionClient { case VIDEO_CODEC_VP9: return VIDEO_CODEC_VP9; case VIDEO_CODEC_AV1: - return VIDEO_CODEC_AV1_SDP_CODEC_NAME; + return VIDEO_CODEC_AV1; case VIDEO_CODEC_H264_HIGH: case VIDEO_CODEC_H264_BASELINE: return VIDEO_CODEC_H264; @@ -1212,6 +1214,13 @@ public class PeerConnectionClient { executor.execute(() -> events.onIceCandidate(candidate)); } + @Override + public void onIceCandidateError(final IceCandidateErrorEvent event) { + Log.d(TAG, + "IceCandidateError address: " + event.address + ", port: " + event.port + ", url: " + + event.url + ", errorCode: " + event.errorCode + ", errorText: " + event.errorText); + } + @Override public void onIceCandidatesRemoved(final IceCandidate[] candidates) { executor.execute(() -> events.onIceCandidatesRemoved(candidates)); diff --git a/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java b/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java index 2ad109306b..6a0f235528 100644 --- a/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java +++ b/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java @@ -159,6 +159,7 @@ public class RoomParametersFetcher { // Requests & returns a TURN ICE Server based on a request URL. Must be run // off the main thread! + @SuppressWarnings("UseNetworkAnnotations") private List requestTurnServers(String url) throws IOException, JSONException { List turnServers = new ArrayList<>(); diff --git a/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java b/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java index 3a721b6229..93028ae783 100644 --- a/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java +++ b/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java @@ -53,6 +53,7 @@ public class AsyncHttpURLConnection { new Thread(this ::sendHttpMessage).start(); } + @SuppressWarnings("UseNetworkAnnotations") private void sendHttpMessage() { try { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); diff --git a/examples/androidnativeapi/jni/android_call_client.cc b/examples/androidnativeapi/jni/android_call_client.cc index f38de24a3f..2c5e1af108 100644 --- a/examples/androidnativeapi/jni/android_call_client.cc +++ b/examples/androidnativeapi/jni/android_call_client.cc @@ -177,8 +177,10 @@ void AndroidCallClient::CreatePeerConnection() { webrtc::MutexLock lock(&pc_mutex_); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - // DTLS SRTP has to be disabled for loopback to work. - config.enable_dtls_srtp = false; + // Encryption has to be disabled for loopback to work. + webrtc::PeerConnectionFactoryInterface::Options options; + options.disable_encryption = true; + pcf_->SetOptions(options); webrtc::PeerConnectionDependencies deps(pc_observer_.get()); pc_ = pcf_->CreatePeerConnectionOrError(config, std::move(deps)).MoveValue(); @@ -206,7 +208,7 @@ void AndroidCallClient::CreatePeerConnection() { void AndroidCallClient::Connect() { webrtc::MutexLock lock(&pc_mutex_); - pc_->CreateOffer(new rtc::RefCountedObject(pc_), + pc_->CreateOffer(rtc::make_ref_counted(pc_), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -256,14 +258,14 @@ void CreateOfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) { // Ownership of desc was transferred to us, now we transfer it forward. pc_->SetLocalDescription( - new rtc::RefCountedObject(), desc); + rtc::make_ref_counted(), desc); // Generate a fake answer. std::unique_ptr answer( webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp)); pc_->SetRemoteDescription( std::move(answer), - new rtc::RefCountedObject()); + rtc::make_ref_counted()); } void CreateOfferObserver::OnFailure(webrtc::RTCError error) { diff --git a/examples/androidtests/gradle_project_test.py b/examples/androidtests/gradle_project_test.py deleted file mode 100644 index 097232d07f..0000000000 --- a/examples/androidtests/gradle_project_test.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# 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. -""" -This scripts tests creating an Android Studio project using the -generate_gradle.py script and making a debug build using it. - -It expect to be given the webrtc output build directory as the first argument -all other arguments are optional. -""" - -import argparse -import logging -import os -import shutil -import subprocess -import sys -import tempfile - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir)) -GENERATE_GRADLE_SCRIPT = os.path.join( - SRC_DIR, 'build/android/gradle/generate_gradle.py') -GRADLEW_BIN = os.path.join(SCRIPT_DIR, 'third_party/gradle/gradlew') - - -def _RunCommand(argv, cwd=SRC_DIR, **kwargs): - logging.info('Running %r', argv) - subprocess.check_call(argv, cwd=cwd, **kwargs) - - -def _ParseArgs(): - parser = argparse.ArgumentParser( - description='Test generating Android gradle project.') - parser.add_argument('build_dir_android', - help='The path to the build directory for Android.') - parser.add_argument('--project_dir', - help='A temporary directory to put the output.') - - args = parser.parse_args() - return args - - -def main(): - logging.basicConfig(level=logging.INFO) - args = _ParseArgs() - - project_dir = args.project_dir - if not project_dir: - project_dir = tempfile.mkdtemp() - - output_dir = os.path.abspath(args.build_dir_android) - project_dir = os.path.abspath(project_dir) - - try: - env = os.environ.copy() - env['PATH'] = os.pathsep.join([ - os.path.join(SRC_DIR, 'third_party', 'depot_tools'), - env.get('PATH', '') - ]) - _RunCommand([ - GENERATE_GRADLE_SCRIPT, '--output-directory', output_dir, - '--target', '//examples:AppRTCMobile', '--project-dir', - project_dir, '--use-gradle-process-resources', '--split-projects' - ], - env=env) - _RunCommand([GRADLEW_BIN, 'assembleDebug'], project_dir) - finally: - # Do not delete temporary directory if user specified it manually. - if not args.project_dir: - shutil.rmtree(project_dir, True) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java index ac3fb23a6e..051d7379bd 100644 --- a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java +++ b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java @@ -453,10 +453,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents { @Test @SmallTest public void testLoopbackVp8DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP8), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -464,10 +460,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents { @Test @SmallTest public void testLoopbackVp9DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP9), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -475,10 +467,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents { @Test @SmallTest public void testLoopbackH264DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_H264), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -486,10 +474,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents { @Test @SmallTest public void testLoopbackVp8CaptureToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Encode to textures is not supported. Requires SDK version 19"); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP8), createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */); } @@ -497,10 +481,6 @@ public class PeerConnectionClientTest implements PeerConnectionEvents { @Test @SmallTest public void testLoopbackH264CaptureToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Encode to textures is not supported. Requires KITKAT"); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_H264), createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */); } diff --git a/examples/androidvoip/jni/android_voip_client.cc b/examples/androidvoip/jni/android_voip_client.cc index 9f859cba28..9f17ed0913 100644 --- a/examples/androidvoip/jni/android_voip_client.cc +++ b/examples/androidvoip/jni/android_voip_client.cc @@ -41,7 +41,6 @@ namespace { #define RUN_ON_VOIP_THREAD(method, ...) \ if (!voip_thread_->IsCurrent()) { \ voip_thread_->PostTask( \ - RTC_FROM_HERE, \ std::bind(&AndroidVoipClient::method, this, ##__VA_ARGS__)); \ return; \ } \ @@ -64,7 +63,7 @@ rtc::IPAddress QueryDefaultLocalAddress(int family) { std::unique_ptr socket( thread->socketserver()->CreateSocket(family, SOCK_DGRAM)); if (!socket) { - RTC_LOG_ERR(LERROR) << "Socket creation failed"; + RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; return rtc::IPAddress(); } @@ -112,7 +111,7 @@ int GetPayloadType(const std::string& codec_name) { return static_cast(PayloadType::kIlbc); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } @@ -228,7 +227,7 @@ void AndroidVoipClient::SetEncoder( const std::string& chosen_encoder = webrtc::JavaToNativeString(env, j_encoder_string); voip_thread_->PostTask( - RTC_FROM_HERE, [this, chosen_encoder] { SetEncoder(chosen_encoder); }); + [this, chosen_encoder] { SetEncoder(chosen_encoder); }); } void AndroidVoipClient::SetDecoders(const std::vector& decoders) { @@ -258,7 +257,7 @@ void AndroidVoipClient::SetDecoders( webrtc::JavaListToNativeVector( env, j_decoder_strings, &webrtc::JavaToNativeString); voip_thread_->PostTask( - RTC_FROM_HERE, [this, chosen_decoders] { SetDecoders(chosen_decoders); }); + [this, chosen_decoders] { SetDecoders(chosen_decoders); }); } void AndroidVoipClient::SetLocalAddress(const std::string& ip_address, @@ -275,7 +274,7 @@ void AndroidVoipClient::SetLocalAddress( jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); - voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] { + voip_thread_->PostTask([this, ip_address, j_port_number_int] { SetLocalAddress(ip_address, j_port_number_int); }); } @@ -294,7 +293,7 @@ void AndroidVoipClient::SetRemoteAddress( jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); - voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] { + voip_thread_->PostTask([this, ip_address, j_port_number_int] { SetRemoteAddress(ip_address, j_port_number_int); }); } @@ -308,7 +307,7 @@ void AndroidVoipClient::StartSession(JNIEnv* env) { rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), rtp_local_address_)); if (!rtp_socket_) { - RTC_LOG_ERR(LERROR) << "Socket creation failed"; + RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; @@ -319,7 +318,7 @@ void AndroidVoipClient::StartSession(JNIEnv* env) { rtcp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), rtcp_local_address_)); if (!rtcp_socket_) { - RTC_LOG_ERR(LERROR) << "Socket creation failed"; + RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; @@ -431,10 +430,9 @@ bool AndroidVoipClient::SendRtp(const uint8_t* packet, size_t length, const webrtc::PacketOptions& options) { std::vector packet_copy(packet, packet + length); - voip_thread_->PostTask(RTC_FROM_HERE, - [this, packet_copy = std::move(packet_copy)] { - SendRtpPacket(packet_copy); - }); + voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { + SendRtpPacket(packet_copy); + }); return true; } @@ -450,10 +448,9 @@ void AndroidVoipClient::SendRtcpPacket( bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { std::vector packet_copy(packet, packet + length); - voip_thread_->PostTask(RTC_FROM_HERE, - [this, packet_copy = std::move(packet_copy)] { - SendRtcpPacket(packet_copy); - }); + voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { + SendRtcpPacket(packet_copy); + }); return true; } @@ -476,10 +473,9 @@ void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket, const rtc::SocketAddress& addr, const int64_t& timestamp) { std::vector packet_copy(rtp_packet, rtp_packet + size); - voip_thread_->PostTask(RTC_FROM_HERE, - [this, packet_copy = std::move(packet_copy)] { - ReadRTPPacket(packet_copy); - }); + voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { + ReadRTPPacket(packet_copy); + }); } void AndroidVoipClient::ReadRTCPPacket( @@ -502,10 +498,9 @@ void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, const rtc::SocketAddress& addr, const int64_t& timestamp) { std::vector packet_copy(rtcp_packet, rtcp_packet + size); - voip_thread_->PostTask(RTC_FROM_HERE, - [this, packet_copy = std::move(packet_copy)] { - ReadRTCPPacket(packet_copy); - }); + voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { + ReadRTCPPacket(packet_copy); + }); } static jlong JNI_VoipClient_CreateClient( diff --git a/examples/androidvoip/jni/android_voip_client.h b/examples/androidvoip/jni/android_voip_client.h index bfca7e8b79..8e1edd5ef9 100644 --- a/examples/androidvoip/jni/android_voip_client.h +++ b/examples/androidvoip/jni/android_voip_client.h @@ -147,8 +147,8 @@ class AndroidVoipClient : public webrtc::Transport, // Overloaded methods having native C++ variables as arguments. void SetEncoder(const std::string& encoder); void SetDecoders(const std::vector& decoders); - void SetLocalAddress(const std::string& ip_address, const int port_number); - void SetRemoteAddress(const std::string& ip_address, const int port_number); + void SetLocalAddress(const std::string& ip_address, int port_number); + void SetRemoteAddress(const std::string& ip_address, int port_number); // Methods to send and receive RTP/RTCP packets. Takes in a // copy of a packet as a vector to prolong the lifetime of diff --git a/examples/objc/AppRTCMobile/ARDAppClient.m b/examples/objc/AppRTCMobile/ARDAppClient.m index 04e17ea07f..4420972598 100644 --- a/examples/objc/AppRTCMobile/ARDAppClient.m +++ b/examples/objc/AppRTCMobile/ARDAppClient.m @@ -13,6 +13,7 @@ #import "sdk/objc/api/peerconnection/RTCAudioTrack.h" #import "sdk/objc/api/peerconnection/RTCConfiguration.h" #import "sdk/objc/api/peerconnection/RTCFileLogger.h" +#import "sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h" #import "sdk/objc/api/peerconnection/RTCIceServer.h" #import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" #import "sdk/objc/api/peerconnection/RTCMediaStream.h" @@ -425,6 +426,17 @@ static int const kKbpsMultiplier = 1000; }); } +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didFailToGatherIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) *)event { + RTCLog(@"Failed to gather ICE candidate. address: %@, port: %d, url: %@, errorCode: %d, " + @"errorText: %@", + event.address, + event.port, + event.url, + event.errorCode, + event.errorText); +} + - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection didRemoveIceCandidates:(NSArray *)candidates { dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/examples/objcnativeapi/objc/objc_call_client.mm b/examples/objcnativeapi/objc/objc_call_client.mm index 419203eb62..081b5bc44b 100644 --- a/examples/objcnativeapi/objc/objc_call_client.mm +++ b/examples/objcnativeapi/objc/objc_call_client.mm @@ -141,8 +141,10 @@ void ObjCCallClient::CreatePeerConnection() { webrtc::MutexLock lock(&pc_mutex_); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - // DTLS SRTP has to be disabled for loopback to work. - config.enable_dtls_srtp = false; + // Encryption has to be disabled for loopback to work. + webrtc::PeerConnectionFactoryInterface::Options options; + options.disable_encryption = true; + pcf_->SetOptions(options); webrtc::PeerConnectionDependencies pc_dependencies(pc_observer_.get()); pc_ = pcf_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)).MoveValue(); RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_; @@ -166,7 +168,7 @@ void ObjCCallClient::CreatePeerConnection() { void ObjCCallClient::Connect() { webrtc::MutexLock lock(&pc_mutex_); - pc_->CreateOffer(new rtc::RefCountedObject(pc_), + pc_->CreateOffer(rtc::make_ref_counted(pc_), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -212,13 +214,13 @@ void CreateOfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) { RTC_LOG(LS_INFO) << "Created offer: " << sdp; // Ownership of desc was transferred to us, now we transfer it forward. - pc_->SetLocalDescription(new rtc::RefCountedObject(), desc); + pc_->SetLocalDescription(rtc::make_ref_counted(), desc); // Generate a fake answer. std::unique_ptr answer( webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp)); pc_->SetRemoteDescription(std::move(answer), - new rtc::RefCountedObject()); + rtc::make_ref_counted()); } void CreateOfferObserver::OnFailure(webrtc::RTCError error) { diff --git a/examples/peerconnection/client/conductor.cc b/examples/peerconnection/client/conductor.cc index 005a9d6ddf..93e95b6583 100644 --- a/examples/peerconnection/client/conductor.cc +++ b/examples/peerconnection/client/conductor.cc @@ -59,12 +59,12 @@ class DummySetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { public: static DummySetSessionDescriptionObserver* Create() { - return new rtc::RefCountedObject(); + return rtc::make_ref_counted(); } - virtual void OnSuccess() { RTC_LOG(INFO) << __FUNCTION__; } + virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; } virtual void OnFailure(webrtc::RTCError error) { - RTC_LOG(INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " - << error.message(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " + << error.message(); } }; @@ -85,8 +85,7 @@ class CapturerTrackSource : public webrtc::VideoTrackSource { capturer = absl::WrapUnique( webrtc::test::VcmCapturer::Create(kWidth, kHeight, kFps, i)); if (capturer) { - return new rtc::RefCountedObject( - std::move(capturer)); + return rtc::make_ref_counted(std::move(capturer)); } } @@ -130,9 +129,13 @@ bool Conductor::InitializePeerConnection() { RTC_DCHECK(!peer_connection_factory_); RTC_DCHECK(!peer_connection_); + if (!signaling_thread_.get()) { + signaling_thread_ = rtc::Thread::CreateWithSocketServer(); + signaling_thread_->Start(); + } peer_connection_factory_ = webrtc::CreatePeerConnectionFactory( nullptr /* network_thread */, nullptr /* worker_thread */, - nullptr /* signaling_thread */, nullptr /* default_adm */, + signaling_thread_.get(), nullptr /* default_adm */, webrtc::CreateBuiltinAudioEncoderFactory(), webrtc::CreateBuiltinAudioDecoderFactory(), webrtc::CreateBuiltinVideoEncoderFactory(), @@ -146,7 +149,7 @@ bool Conductor::InitializePeerConnection() { return false; } - if (!CreatePeerConnection(/*dtls=*/true)) { + if (!CreatePeerConnection()) { main_wnd_->MessageBox("Error", "CreatePeerConnection failed", true); DeletePeerConnection(); } @@ -161,23 +164,28 @@ bool Conductor::ReinitializePeerConnectionForLoopback() { std::vector> senders = peer_connection_->GetSenders(); peer_connection_ = nullptr; - if (CreatePeerConnection(/*dtls=*/false)) { + // Loopback is only possible if encryption is disabled. + webrtc::PeerConnectionFactoryInterface::Options options; + options.disable_encryption = true; + peer_connection_factory_->SetOptions(options); + if (CreatePeerConnection()) { for (const auto& sender : senders) { peer_connection_->AddTrack(sender->track(), sender->stream_ids()); } peer_connection_->CreateOffer( this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } + options.disable_encryption = false; + peer_connection_factory_->SetOptions(options); return peer_connection_ != nullptr; } -bool Conductor::CreatePeerConnection(bool dtls) { +bool Conductor::CreatePeerConnection() { RTC_DCHECK(peer_connection_factory_); RTC_DCHECK(!peer_connection_); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - config.enable_dtls_srtp = dtls; webrtc::PeerConnectionInterface::IceServer server; server.uri = GetPeerConnectionString(); config.servers.push_back(server); @@ -212,23 +220,23 @@ void Conductor::OnAddTrack( rtc::scoped_refptr receiver, const std::vector>& streams) { - RTC_LOG(INFO) << __FUNCTION__ << " " << receiver->id(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); main_wnd_->QueueUIThreadCallback(NEW_TRACK_ADDED, receiver->track().release()); } void Conductor::OnRemoveTrack( rtc::scoped_refptr receiver) { - RTC_LOG(INFO) << __FUNCTION__ << " " << receiver->id(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); main_wnd_->QueueUIThreadCallback(TRACK_REMOVED, receiver->track().release()); } void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { - RTC_LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); // For loopback test. To save some connecting delay. if (loopback_) { if (!peer_connection_->AddIceCandidate(candidate)) { - RTC_LOG(WARNING) << "Failed to apply the received candidate"; + RTC_LOG(LS_WARNING) << "Failed to apply the received candidate"; } return; } @@ -252,12 +260,12 @@ void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { // void Conductor::OnSignedIn() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; main_wnd_->SwitchToPeerList(client_->peers()); } void Conductor::OnDisconnected() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; DeletePeerConnection(); @@ -266,16 +274,16 @@ void Conductor::OnDisconnected() { } void Conductor::OnPeerConnected(int id, const std::string& name) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; // Refresh the list if we're showing it. if (main_wnd_->current_ui() == MainWindow::LIST_PEERS) main_wnd_->SwitchToPeerList(client_->peers()); } void Conductor::OnPeerDisconnected(int id) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; if (id == peer_id_) { - RTC_LOG(INFO) << "Our peer disconnected"; + RTC_LOG(LS_INFO) << "Our peer disconnected"; main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL); } else { // Refresh the list if we're showing it. @@ -299,7 +307,7 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { } } else if (peer_id != peer_id_) { RTC_DCHECK(peer_id_ != -1); - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "Received a message from unknown peer while already in a " "conversation with a different peer."; return; @@ -308,7 +316,7 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { Json::Reader reader; Json::Value jmessage; if (!reader.parse(message, jmessage)) { - RTC_LOG(WARNING) << "Received unknown message. " << message; + RTC_LOG(LS_WARNING) << "Received unknown message. " << message; return; } std::string type_str; @@ -337,19 +345,21 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { std::string sdp; if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, &sdp)) { - RTC_LOG(WARNING) << "Can't parse received session description message."; + RTC_LOG(LS_WARNING) + << "Can't parse received session description message."; return; } webrtc::SdpParseError error; std::unique_ptr session_description = webrtc::CreateSessionDescription(type, sdp, &error); if (!session_description) { - RTC_LOG(WARNING) << "Can't parse received session description message. " - "SdpParseError was: " - << error.description; + RTC_LOG(LS_WARNING) + << "Can't parse received session description message. " + "SdpParseError was: " + << error.description; return; } - RTC_LOG(INFO) << " Received session description :" << message; + RTC_LOG(LS_INFO) << " Received session description :" << message; peer_connection_->SetRemoteDescription( DummySetSessionDescriptionObserver::Create(), session_description.release()); @@ -366,23 +376,23 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { !rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName, &sdp_mlineindex) || !rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) { - RTC_LOG(WARNING) << "Can't parse received message."; + RTC_LOG(LS_WARNING) << "Can't parse received message."; return; } webrtc::SdpParseError error; std::unique_ptr candidate( webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error)); if (!candidate.get()) { - RTC_LOG(WARNING) << "Can't parse received candidate message. " - "SdpParseError was: " - << error.description; + RTC_LOG(LS_WARNING) << "Can't parse received candidate message. " + "SdpParseError was: " + << error.description; return; } if (!peer_connection_->AddIceCandidate(candidate.get())) { - RTC_LOG(WARNING) << "Failed to apply the received candidate"; + RTC_LOG(LS_WARNING) << "Failed to apply the received candidate"; return; } - RTC_LOG(INFO) << " Received candidate :" << message; + RTC_LOG(LS_INFO) << " Received candidate :" << message; } } @@ -466,7 +476,7 @@ void Conductor::AddTracks() { } void Conductor::DisconnectFromCurrentPeer() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; if (peer_connection_.get()) { client_->SendHangUp(peer_id_); DeletePeerConnection(); @@ -479,7 +489,7 @@ void Conductor::DisconnectFromCurrentPeer() { void Conductor::UIThreadCallback(int msg_id, void* data) { switch (msg_id) { case PEER_CONNECTION_CLOSED: - RTC_LOG(INFO) << "PEER_CONNECTION_CLOSED"; + RTC_LOG(LS_INFO) << "PEER_CONNECTION_CLOSED"; DeletePeerConnection(); if (main_wnd_->IsWindow()) { @@ -494,7 +504,7 @@ void Conductor::UIThreadCallback(int msg_id, void* data) { break; case SEND_MESSAGE_TO_PEER: { - RTC_LOG(INFO) << "SEND_MESSAGE_TO_PEER"; + RTC_LOG(LS_INFO) << "SEND_MESSAGE_TO_PEER"; std::string* msg = reinterpret_cast(data); if (msg) { // For convenience, we always run the message through the queue. @@ -538,7 +548,7 @@ void Conductor::UIThreadCallback(int msg_id, void* data) { } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } @@ -570,7 +580,7 @@ void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) { } void Conductor::OnFailure(webrtc::RTCError error) { - RTC_LOG(LERROR) << ToString(error.type()) << ": " << error.message(); + RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); } void Conductor::SendMessage(const std::string& json_object) { diff --git a/examples/peerconnection/client/conductor.h b/examples/peerconnection/client/conductor.h index 3c06857a05..80617d3cf4 100644 --- a/examples/peerconnection/client/conductor.h +++ b/examples/peerconnection/client/conductor.h @@ -21,6 +21,7 @@ #include "api/peer_connection_interface.h" #include "examples/peerconnection/client/main_wnd.h" #include "examples/peerconnection/client/peer_connection_client.h" +#include "rtc_base/thread.h" namespace webrtc { class VideoCaptureModule; @@ -53,7 +54,7 @@ class Conductor : public webrtc::PeerConnectionObserver, ~Conductor(); bool InitializePeerConnection(); bool ReinitializePeerConnectionForLoopback(); - bool CreatePeerConnection(bool dtls); + bool CreatePeerConnection(); void DeletePeerConnection(); void EnsureStreamingUI(); void AddTracks(); @@ -122,6 +123,7 @@ class Conductor : public webrtc::PeerConnectionObserver, int peer_id_; bool loopback_; + std::unique_ptr signaling_thread_; rtc::scoped_refptr peer_connection_; rtc::scoped_refptr peer_connection_factory_; diff --git a/examples/peerconnection/client/linux/main.cc b/examples/peerconnection/client/linux/main.cc index ccca7b1c7c..47f4f3618e 100644 --- a/examples/peerconnection/client/linux/main.cc +++ b/examples/peerconnection/client/linux/main.cc @@ -101,8 +101,7 @@ int main(int argc, char* argv[]) { rtc::InitializeSSL(); // Must be constructed after we set the socketserver. PeerConnectionClient client; - rtc::scoped_refptr conductor( - new rtc::RefCountedObject(&client, &wnd)); + auto conductor = rtc::make_ref_counted(&client, &wnd); socket_server.set_client(&client); socket_server.set_conductor(conductor); diff --git a/examples/peerconnection/client/linux/main_wnd.cc b/examples/peerconnection/client/linux/main_wnd.cc index 7dcfa89d6a..e9b6a514b1 100644 --- a/examples/peerconnection/client/linux/main_wnd.cc +++ b/examples/peerconnection/client/linux/main_wnd.cc @@ -252,7 +252,7 @@ bool GtkMainWnd::Destroy() { } void GtkMainWnd::SwitchToConnectUI() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(IsWindow()); RTC_DCHECK(vbox_ == NULL); @@ -308,7 +308,7 @@ void GtkMainWnd::SwitchToConnectUI() { } void GtkMainWnd::SwitchToPeerList(const Peers& peers) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; if (!peer_list_) { gtk_container_set_border_width(GTK_CONTAINER(window_), 0); @@ -345,7 +345,7 @@ void GtkMainWnd::SwitchToPeerList(const Peers& peers) { } void GtkMainWnd::SwitchToStreamingUI() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(draw_area_ == NULL); @@ -514,7 +514,7 @@ void GtkMainWnd::Draw(GtkWidget* widget, cairo_t* cr) { cairo_fill(cr); cairo_surface_destroy(surface); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); #endif } diff --git a/examples/peerconnection/client/main.cc b/examples/peerconnection/client/main.cc index e209171116..32bc52bda4 100644 --- a/examples/peerconnection/client/main.cc +++ b/examples/peerconnection/client/main.cc @@ -23,7 +23,6 @@ #include "examples/peerconnection/client/main_wnd.h" #include "examples/peerconnection/client/peer_connection_client.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_adapter.h" #include "rtc_base/string_utils.h" // For ToUtf8 #include "rtc_base/win32_socket_init.h" @@ -40,6 +39,9 @@ class WindowsCommandLineArguments { public: WindowsCommandLineArguments(); + WindowsCommandLineArguments(const WindowsCommandLineArguments&) = delete; + WindowsCommandLineArguments& operator=(WindowsCommandLineArguments&) = delete; + int argc() { return argv_.size(); } char** argv() { return argv_.data(); } @@ -48,9 +50,6 @@ class WindowsCommandLineArguments { std::vector args_; // Pointers, to get layout compatible with char** argv. std::vector argv_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(WindowsCommandLineArguments); }; WindowsCommandLineArguments::WindowsCommandLineArguments() { @@ -101,14 +100,13 @@ int PASCAL wWinMain(HINSTANCE instance, MainWnd wnd(server.c_str(), absl::GetFlag(FLAGS_port), absl::GetFlag(FLAGS_autoconnect), absl::GetFlag(FLAGS_autocall)); if (!wnd.Create()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } rtc::InitializeSSL(); PeerConnectionClient client; - rtc::scoped_refptr conductor( - new rtc::RefCountedObject(&client, &wnd)); + auto conductor = rtc::make_ref_counted(&client, &wnd); // Main loop. MSG msg; diff --git a/examples/peerconnection/client/peer_connection_client.cc b/examples/peerconnection/client/peer_connection_client.cc index 713259360c..c0de4ffb5d 100644 --- a/examples/peerconnection/client/peer_connection_client.cc +++ b/examples/peerconnection/client/peer_connection_client.cc @@ -15,10 +15,6 @@ #include "rtc_base/logging.h" #include "rtc_base/net_helpers.h" -#ifdef WIN32 -#include "rtc_base/win32_socket_server.h" -#endif - namespace { // This is our magical hangup signal. @@ -27,17 +23,9 @@ const char kByeMessage[] = "BYE"; const int kReconnectDelay = 2000; rtc::Socket* CreateClientSocket(int family) { -#ifdef WIN32 - rtc::Win32Socket* sock = new rtc::Win32Socket(); - sock->CreateT(family, SOCK_STREAM); - return sock; -#elif defined(WEBRTC_POSIX) rtc::Thread* thread = rtc::Thread::Current(); RTC_DCHECK(thread != NULL); return thread->socketserver()->CreateSocket(family, SOCK_STREAM); -#else -#error Platform not supported. -#endif } } // namespace @@ -89,7 +77,7 @@ void PeerConnectionClient::Connect(const std::string& server, RTC_DCHECK(!client_name.empty()); if (state_ != NOT_CONNECTED) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "The client must not be connected before you can call Connect()"; callback_->OnServerConnectionFailure(); return; @@ -297,7 +285,7 @@ bool PeerConnectionClient::ReadIntoBuffer(rtc::Socket* socket, bool ret = false; size_t i = data->find("\r\n\r\n"); if (i != std::string::npos) { - RTC_LOG(INFO) << "Headers received"; + RTC_LOG(LS_INFO) << "Headers received"; if (GetHeaderValue(*data, i, "\r\nContent-Length: ", content_length)) { size_t total_response_size = (i + 4) + *content_length; if (data->length() >= total_response_size) { @@ -374,7 +362,7 @@ void PeerConnectionClient::OnRead(rtc::Socket* socket) { } void PeerConnectionClient::OnHangingGetRead(rtc::Socket* socket) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; size_t content_length = 0; if (ReadIntoBuffer(socket, ¬ification_data_, &content_length)) { size_t peer_id = 0, eoh = 0; @@ -472,7 +460,7 @@ bool PeerConnectionClient::ParseServerResponse(const std::string& response, } void PeerConnectionClient::OnClose(rtc::Socket* socket, int err) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; socket->Close(); @@ -491,7 +479,7 @@ void PeerConnectionClient::OnClose(rtc::Socket* socket, int err) { } } else { if (socket == control_socket_.get()) { - RTC_LOG(WARNING) << "Connection refused; retrying in 2 seconds"; + RTC_LOG(LS_WARNING) << "Connection refused; retrying in 2 seconds"; rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, kReconnectDelay, this, 0); } else { diff --git a/examples/stunprober/main.cc b/examples/stunprober/main.cc index fa5825c82e..d0ed92cc34 100644 --- a/examples/stunprober/main.cc +++ b/examples/stunprober/main.cc @@ -128,7 +128,7 @@ int main(int argc, char* argv[]) { auto socket_factory = std::make_unique(&socket_server); std::unique_ptr network_manager( - new rtc::BasicNetworkManager()); + new rtc::BasicNetworkManager(&socket_server)); rtc::NetworkManager::NetworkList networks; network_manager->GetNetworks(&networks); auto prober = std::make_unique(socket_factory.get(), diff --git a/examples/unityplugin/simple_peer_connection.cc b/examples/unityplugin/simple_peer_connection.cc index c7e5185bdc..16c580e767 100644 --- a/examples/unityplugin/simple_peer_connection.cc +++ b/examples/unityplugin/simple_peer_connection.cc @@ -61,7 +61,7 @@ class CapturerTrackSource : public webrtc::VideoTrackSource { if (!capturer) { return nullptr; } - return new rtc::RefCountedObject(std::move(capturer)); + return rtc::make_ref_counted(std::move(capturer)); } protected: @@ -99,12 +99,12 @@ class DummySetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { public: static DummySetSessionDescriptionObserver* Create() { - return new rtc::RefCountedObject(); + return rtc::make_ref_counted(); } - virtual void OnSuccess() { RTC_LOG(INFO) << __FUNCTION__; } + virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; } virtual void OnFailure(webrtc::RTCError error) { - RTC_LOG(INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " - << error.message(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " + << error.message(); } protected: @@ -190,7 +190,6 @@ bool SimplePeerConnection::CreatePeerConnection(const char** turn_urls, webrtc::PeerConnectionInterface::IceServer stun_server; stun_server.uri = GetPeerConnectionString(); config_.servers.push_back(stun_server); - config_.enable_dtls_srtp = false; auto result = g_peer_connection_factory->CreatePeerConnectionOrError( config_, webrtc::PeerConnectionDependencies(this)); @@ -270,7 +269,7 @@ void SimplePeerConnection::OnSuccess( } void SimplePeerConnection::OnFailure(webrtc::RTCError error) { - RTC_LOG(LERROR) << ToString(error.type()) << ": " << error.message(); + RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); // TODO(hta): include error.type in the message if (OnFailureMessage) @@ -279,7 +278,7 @@ void SimplePeerConnection::OnFailure(webrtc::RTCError error) { void SimplePeerConnection::OnIceCandidate( const webrtc::IceCandidateInterface* candidate) { - RTC_LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); std::string sdp; if (!candidate->ToString(&sdp)) { @@ -344,12 +343,12 @@ bool SimplePeerConnection::SetRemoteDescription(const char* type, webrtc::SessionDescriptionInterface* session_description( webrtc::CreateSessionDescription(desc_type, remote_desc, &error)); if (!session_description) { - RTC_LOG(WARNING) << "Can't parse received session description message. " - "SdpParseError was: " - << error.description; + RTC_LOG(LS_WARNING) << "Can't parse received session description message. " + "SdpParseError was: " + << error.description; return false; } - RTC_LOG(INFO) << " Received session description :" << remote_desc; + RTC_LOG(LS_INFO) << " Received session description :" << remote_desc; peer_connection_->SetRemoteDescription( DummySetSessionDescriptionObserver::Create(), session_description); @@ -366,16 +365,16 @@ bool SimplePeerConnection::AddIceCandidate(const char* candidate, std::unique_ptr ice_candidate( webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate, &error)); if (!ice_candidate.get()) { - RTC_LOG(WARNING) << "Can't parse received candidate message. " - "SdpParseError was: " - << error.description; + RTC_LOG(LS_WARNING) << "Can't parse received candidate message. " + "SdpParseError was: " + << error.description; return false; } if (!peer_connection_->AddIceCandidate(ice_candidate.get())) { - RTC_LOG(WARNING) << "Failed to apply the received candidate"; + RTC_LOG(LS_WARNING) << "Failed to apply the received candidate"; return false; } - RTC_LOG(INFO) << " Received candidate :" << candidate; + RTC_LOG(LS_INFO) << " Received candidate :" << candidate; return true; } @@ -410,7 +409,7 @@ void SimplePeerConnection::SetAudioControl() { void SimplePeerConnection::OnAddStream( rtc::scoped_refptr stream) { - RTC_LOG(INFO) << __FUNCTION__ << " " << stream->id(); + RTC_LOG(LS_INFO) << __FUNCTION__ << " " << stream->id(); remote_stream_ = stream; if (remote_video_observer_ && !remote_stream_->GetVideoTracks().empty()) { remote_stream_->GetVideoTracks()[0]->AddOrUpdateSink( @@ -447,10 +446,9 @@ void SimplePeerConnection::AddStreams(bool audio_only) { RTC_DCHECK(texture_helper != nullptr) << "Cannot get the Surface Texture Helper."; - rtc::scoped_refptr source( - new rtc::RefCountedObject( - g_signaling_thread.get(), env, /* is_screencast= */ false, - /* align_timestamps= */ true)); + auto source = rtc::make_ref_counted( + g_signaling_thread.get(), env, /*is_screencast=*/false, + /*align_timestamps=*/true); // link with VideoCapturer (Camera); jmethodID link_camera_method = webrtc::GetStaticMethodID( diff --git a/examples/unityplugin/simple_peer_connection.h b/examples/unityplugin/simple_peer_connection.h index b99bde930e..d5cebc9940 100644 --- a/examples/unityplugin/simple_peer_connection.h +++ b/examples/unityplugin/simple_peer_connection.h @@ -31,7 +31,7 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver, ~SimplePeerConnection() {} bool InitializePeerConnection(const char** turn_urls, - const int no_of_urls, + int no_of_urls, const char* username, const char* credential, bool is_receiver); @@ -56,13 +56,13 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver, ICECANDIDATEREADYTOSEND_CALLBACK callback); bool SetRemoteDescription(const char* type, const char* sdp); bool AddIceCandidate(const char* sdp, - const int sdp_mlineindex, + int sdp_mlineindex, const char* sdp_mid); protected: // create a peerconneciton and add the turn servers info to the configuration. bool CreatePeerConnection(const char** turn_urls, - const int no_of_urls, + int no_of_urls, const char* username, const char* credential); void CloseDataChannel(); diff --git a/examples/unityplugin/unity_plugin_apis.cc b/examples/unityplugin/unity_plugin_apis.cc index 34c28d926a..672330faec 100644 --- a/examples/unityplugin/unity_plugin_apis.cc +++ b/examples/unityplugin/unity_plugin_apis.cc @@ -27,7 +27,7 @@ int CreatePeerConnection(const char** turn_urls, const char* credential, bool mandatory_receive_video) { g_peer_connection_map[g_peer_connection_id] = - new rtc::RefCountedObject(); + rtc::make_ref_counted(); if (!g_peer_connection_map[g_peer_connection_id]->InitializePeerConnection( turn_urls, no_of_urls, username, credential, mandatory_receive_video)) diff --git a/examples/unityplugin/unity_plugin_apis.h b/examples/unityplugin/unity_plugin_apis.h index b32f9e2caf..8b8fe0fe80 100644 --- a/examples/unityplugin/unity_plugin_apis.h +++ b/examples/unityplugin/unity_plugin_apis.h @@ -31,7 +31,7 @@ typedef void (*DATAFROMEDATECHANNELREADY_CALLBACK)(const char* msg); typedef void (*FAILURE_CALLBACK)(const char* msg); typedef void (*LOCALSDPREADYTOSEND_CALLBACK)(const char* type, const char* sdp); typedef void (*ICECANDIDATEREADYTOSEND_CALLBACK)(const char* candidate, - const int sdp_mline_index, + int sdp_mline_index, const char* sdp_mid); typedef void (*AUDIOBUSREADY_CALLBACK)(const void* audio_data, int bits_per_sample, @@ -47,7 +47,7 @@ typedef void (*AUDIOBUSREADY_CALLBACK)(const void* audio_data, extern "C" { // Create a peerconnection and return a unique peer connection id. WEBRTC_PLUGIN_API int CreatePeerConnection(const char** turn_urls, - const int no_of_urls, + int no_of_urls, const char* username, const char* credential, bool mandatory_receive_video); @@ -75,9 +75,9 @@ WEBRTC_PLUGIN_API bool SetRemoteDescription(int peer_connection_id, const char* type, const char* sdp); // Add ice candidate. -WEBRTC_PLUGIN_API bool AddIceCandidate(const int peer_connection_id, +WEBRTC_PLUGIN_API bool AddIceCandidate(int peer_connection_id, const char* candidate, - const int sdp_mlineindex, + int sdp_mlineindex, const char* sdp_mid); // Register callback functions. diff --git a/g3doc/abseil-in-webrtc.md b/g3doc/abseil-in-webrtc.md index 9f7b4ab34f..6f0b4812e8 100644 --- a/g3doc/abseil-in-webrtc.md +++ b/g3doc/abseil-in-webrtc.md @@ -26,6 +26,7 @@ will generate a shared library. ## **Allowed** * `absl::bind_front` +* `absl::Cleanup` * `absl::InlinedVector` * `absl::WrapUnique` * `absl::optional` and related stuff from `absl/types/optional.h`. diff --git a/g3doc/become_a_committer.md b/g3doc/become_a_committer.md index f2c9844e6c..8045c067f3 100644 --- a/g3doc/become_a_committer.md +++ b/g3doc/become_a_committer.md @@ -1,7 +1,26 @@ -# How to become WebRTC committer +# How to get tryjob access or become WebRTC committer - + + +## Overview + +There are two levels of WebRTC contributors access: + +1. Tryjob access - permits contributor to run tests for their changes using + WebRTC infrastructure +2. WebRTC committer rights - permits to submit changes to the WebRTC code base. + This includes tryjob access. + +## Getting tryjob access + +To get tryjob access applicant has to do a contribution around 10-20 CLs to the +WebRTC code base. After that, they should file a bug using +[Get tryjob access template][7], specifying the email which was used for the +contributions and to which access will be granted, and listing contributed CLs. + +The access will be granted when the ticket is resolved by one of the project +members. In case of rejection the explanation will be provided. ## WebRTC committer duties @@ -62,3 +81,4 @@ recommended to apply for WebRTC committer rights obtaining process. [4]: https://developers.google.com/open-source/cla/individual?hl=en [5]: https://google.github.io/styleguide/cppguide.html [6]: https://bugs.chromium.org/p/webrtc/issues/entry?template=Become+WebRTC+committer +[7]: https://bugs.chromium.org/p/webrtc/issues/entry?template=Get+tryjob+access diff --git a/g3doc/implementation_basics.md b/g3doc/implementation_basics.md index 933941a0d1..fd906d03c3 100644 --- a/g3doc/implementation_basics.md +++ b/g3doc/implementation_basics.md @@ -82,7 +82,15 @@ in the (slow) process of being removed from the codebase. * RecursiveCriticalSection. Try to use [webrtc::Mutex][6] instead, and don't recurse. +## Enum-To-String functions +If there is a need to convert an enum to a string representation, such as for +enums exposed at the Javascript API interface, the recommended way is to write +a function named AsString, declared "static constexpr" and returning an +absl::string_view. The declaration should be right after the enum declaration, +in the same scope; the implementation (which must be marked "inline") should +be at the end of the same header file. +If the enum is not defined within a class, the "static" keyword is not needed. [1]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/units/timestamp.h;drc=b95d90b78a3491ef8e8aa0640dd521515ec881ca;l=29 [2]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/thread.h;drc=1107751b6f11c35259a1c5c8a0f716e227b7e3b4;l=194 diff --git a/g3doc/style-guide.md b/g3doc/style-guide.md index 06f8e0e0ae..62c99fc73b 100644 --- a/g3doc/style-guide.md +++ b/g3doc/style-guide.md @@ -1,16 +1,12 @@ # WebRTC coding style guide - + ## General advice Some older parts of the code violate the style guide in various ways. - -* If making small changes to such code, follow the style guide when it's - reasonable to do so, but in matters of formatting etc., it is often better to - be consistent with the surrounding code. -* If making large changes to such code, consider first cleaning it up in a +If making large changes to such code, consider first cleaning it up in a separate CL. ## C++ @@ -25,18 +21,14 @@ both. ### C++ version -WebRTC is written in C++14, but with some restrictions: +WebRTC is written in C++17, but with some restrictions: -* We only allow the subset of C++14 (language and library) that is not banned by - Chromium; see the [list of banned C++ features in Chromium][chromium-cpp]. -* We only allow the subset of C++14 that is also valid C++17; otherwise, users - would not be able to compile WebRTC in C++17 mode. +* We only allow the subset of C++17 (language and library) that is not banned by + Chromium; see the [list of banned C++ features in Chromium][chr-style-cpp]. +* We only allow the subset of C++17 that is also valid C++20; otherwise, users + would not be able to compile WebRTC in C++20 mode. -[chromium-cpp]: https://chromium-cpp.appspot.com/ - -Unlike the Chromium and Google C++ style guides, we do not allow C++20-style -designated initializers, because we want to stay compatible with compilers that -do not yet support them. +[chr-style-cpp]: https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++-features.md ### Abseil @@ -141,9 +133,7 @@ The following smart pointer types are recommended: * `rtc::scoped_refptr` for all objects with shared ownership Use of `std::shared_ptr` is *not permitted*. It is banned in the Chromium style -guide (overriding the Google style guide), and offers no compelling advantage -over `rtc::scoped_refptr` (which is cloned from the corresponding Chromium -type). See the +guide (overriding the Google style guide). See the [list of banned C++ library features in Chromium][chr-std-shared-ptr] for more information. @@ -152,7 +142,7 @@ In most cases, one will want to explicitly control lifetimes, and therefore use exist both from the API users and internally, with no way to invalidate pointers held by the API user, `rtc::scoped_refptr` can be appropriate. -[chr-std-shared-ptr]: https://chromium-cpp.appspot.com/#library-blocklist +[chr-std-shared-ptr]: https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++-features.md#shared-pointers-banned ### `std::bind` @@ -180,11 +170,8 @@ headers you need. There's a substantial chunk of legacy C code in WebRTC, and a lot of it is old enough that it violates the parts of the C++ style guide that also applies to C (naming etc.) for the simple reason that it pre-dates the use of the current C++ -style guide for this code base. - -* If making small changes to C code, mimic the style of the surrounding code. -* If making large changes to C code, consider converting the whole thing to C++ - first. +style guide for this code base. If making large changes to C code, consider +converting the whole thing to C++ first. ## Java diff --git a/infra/config/OWNERS b/infra/config/OWNERS new file mode 100644 index 0000000000..eae8171db5 --- /dev/null +++ b/infra/config/OWNERS @@ -0,0 +1,6 @@ +mbonadei@webrtc.org +jleconte@webrtc.org +titovartem@webrtc.org +jansson@webrtc.org +terelius@webrtc.org +landrey@webrtc.org diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py new file mode 100644 index 0000000000..6aa75c7df5 --- /dev/null +++ b/infra/config/PRESUBMIT.py @@ -0,0 +1,20 @@ +# Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + + +def CheckChangeOnUpload(input_api, output_api): + return input_api.RunTests( + input_api.canned_checks.CheckLucicfgGenOutput( + input_api, output_api, + 'config.star')) + input_api.canned_checks.CheckChangedLUCIConfigs( + input_api, output_api) + return res + + +def CheckChangeOnCommit(input_api, output_api): + return CheckChangeOnUpload(input_api, output_api) diff --git a/infra/config/README.md b/infra/config/README.md new file mode 100644 index 0000000000..ef60c763db --- /dev/null +++ b/infra/config/README.md @@ -0,0 +1,52 @@ +# infra/config folder + +This folder contains WebRTC project-wide configurations for Chrome infra +services, mainly the CI system ([console][]). + +`*.cfg` files are the actual configuration that [LUCI][luci-config] looks at. +They are in *protocol buffer text format*. For example, +[cr-buildbucket.cfg](cr-buildbucket.cfg) defines builders. + +However, they are all automatically generated from the [Starlark][] script +[config.star](config.star) that defines a unified config using **[lucicfg][]**. +The main body of the config is at the bottom of the file, following all the +helper definitions. + +`lucicfg` should be available as part of depot_tools. After editing +[config.star](config.star) you should run `lucicfg generate config.star` to +re-generate `*.cfg` files. Check the diffs in generated files to confirm that +your change worked as expected. Both the code change and the generated changes +need to be committed together. + +## Uploading changes + +It is recommended to have a separate checkout for this branch, so switching +to/from it does not populate/delete all files in the master branch. + +Initial setup: + +```bash +git clone https://webrtc.googlesource.com/src/ +``` + +Now you can create a new branch to make changes: + +```bash +git new-branch add-new-builder +# edit/generate files +git commit -a +git cl upload +``` + +Changes can be reviewed on Gerrit and submitted with commit queue as usual. + +### Activating the changes + +Any changes to this directory go live soon after landing, without any additional +steps. You can see the status or force a refresh of the config at +[luci-config][]. + +[console]: https://ci.chromium.org/p/webrtc/g/ci/console +[luci-config]: https://luci-config.appspot.com/#/projects/webrtc +[starlark]: https://github.com/google/starlark-go +[lucicfg]: https://chromium.googlesource.com/infra/luci/luci-go/+/master/lucicfg/doc/ diff --git a/infra/config/codereview.settings b/infra/config/codereview.settings new file mode 100644 index 0000000000..df15400f01 --- /dev/null +++ b/infra/config/codereview.settings @@ -0,0 +1,6 @@ +# This file is used by git-cl to get repository specific information. +CODE_REVIEW_SERVER: codereview.webrtc.org +CC_LIST: webrtc-reviews@webrtc.org +GERRIT_HOST: True +PROJECT: webrtc +VIEW_VC: https://webrtc.googlesource.com/src/+/ diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg new file mode 100644 index 0000000000..ac8c474e27 --- /dev/null +++ b/infra/config/commit-queue.cfg @@ -0,0 +1,365 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Config message: +# https://luci-config.appspot.com/schemas/projects:commit-queue.cfg + +cq_status_host: "chromium-cq-status.appspot.com" +submit_options { + max_burst: 1 + burst_delay { + seconds: 60 + } +} +config_groups { + name: "cq" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/heads/master" + ref_regexp: "refs/heads/main" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-committers" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tree_status { + url: "https://webrtc-status.appspot.com" + } + tryjob { + builders { + name: "webrtc-internal/g3.webrtc-internal.try/internal_compile_lite" + owner_whitelist_group: "project-webrtc-internal-tryjob-access" + } + builders { + name: "webrtc/try/android_arm64_rel" + } + builders { + name: "webrtc/try/android_arm_dbg" + } + builders { + name: "webrtc/try/android_arm_more_configs" + } + builders { + name: "webrtc/try/android_arm_rel" + } + builders { + name: "webrtc/try/android_chromium_compile" + } + builders { + name: "webrtc/try/android_compile_arm64_rel" + } + builders { + name: "webrtc/try/android_compile_arm_rel" + } + builders { + name: "webrtc/try/android_compile_x64_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_rel" + } + builders { + name: "webrtc/try/ios_api_framework" + } + builders { + name: "webrtc/try/ios_compile_arm64_dbg" + } + builders { + name: "webrtc/try/ios_compile_arm64_rel" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios12" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios13" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios14" + } + builders { + name: "webrtc/try/linux_asan" + } + builders { + name: "webrtc/try/linux_chromium_compile" + } + builders { + name: "webrtc/try/linux_chromium_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_rel" + } + builders { + name: "webrtc/try/linux_compile_arm_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm_rel" + } + builders { + name: "webrtc/try/linux_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_rel" + } + builders { + name: "webrtc/try/linux_libfuzzer_rel" + } + builders { + name: "webrtc/try/linux_more_configs" + } + builders { + name: "webrtc/try/linux_msan" + } + builders { + name: "webrtc/try/linux_rel" + } + builders { + name: "webrtc/try/linux_tsan2" + } + builders { + name: "webrtc/try/linux_ubsan" + } + builders { + name: "webrtc/try/linux_ubsan_vptr" + } + builders { + name: "webrtc/try/linux_x86_dbg" + } + builders { + name: "webrtc/try/linux_x86_rel" + } + builders { + name: "webrtc/try/mac_asan" + } + builders { + name: "webrtc/try/mac_chromium_compile" + } + builders { + name: "webrtc/try/mac_compile_dbg" + } + builders { + name: "webrtc/try/mac_rel" + } + builders { + name: "webrtc/try/presubmit" + disable_reuse: true + } + builders { + name: "webrtc/try/win_asan" + } + builders { + name: "webrtc/try/win_chromium_compile" + } + builders { + name: "webrtc/try/win_chromium_compile_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_rel" + } + builders { + name: "webrtc/try/win_compile_x86_clang_dbg" + } + builders { + name: "webrtc/try/win_x86_clang_rel" + } + builders { + name: "webrtc/try/win_x86_more_configs" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} +config_groups { + name: "cq_branch" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/branch-heads/.+" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-committers" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tryjob { + builders { + name: "webrtc/try/android_arm64_rel" + } + builders { + name: "webrtc/try/android_arm_dbg" + } + builders { + name: "webrtc/try/android_arm_more_configs" + } + builders { + name: "webrtc/try/android_arm_rel" + } + builders { + name: "webrtc/try/android_compile_arm64_rel" + } + builders { + name: "webrtc/try/android_compile_arm_rel" + } + builders { + name: "webrtc/try/android_compile_x64_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_rel" + } + builders { + name: "webrtc/try/ios_api_framework" + } + builders { + name: "webrtc/try/ios_compile_arm64_dbg" + } + builders { + name: "webrtc/try/ios_compile_arm64_rel" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios12" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios13" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios14" + } + builders { + name: "webrtc/try/linux_asan" + } + builders { + name: "webrtc/try/linux_compile_arm64_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_rel" + } + builders { + name: "webrtc/try/linux_compile_arm_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm_rel" + } + builders { + name: "webrtc/try/linux_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_rel" + } + builders { + name: "webrtc/try/linux_libfuzzer_rel" + } + builders { + name: "webrtc/try/linux_more_configs" + } + builders { + name: "webrtc/try/linux_msan" + } + builders { + name: "webrtc/try/linux_rel" + } + builders { + name: "webrtc/try/linux_tsan2" + } + builders { + name: "webrtc/try/linux_ubsan" + } + builders { + name: "webrtc/try/linux_ubsan_vptr" + } + builders { + name: "webrtc/try/linux_x86_dbg" + } + builders { + name: "webrtc/try/linux_x86_rel" + } + builders { + name: "webrtc/try/mac_asan" + } + builders { + name: "webrtc/try/mac_compile_dbg" + } + builders { + name: "webrtc/try/mac_rel" + } + builders { + name: "webrtc/try/presubmit" + disable_reuse: true + } + builders { + name: "webrtc/try/win_asan" + } + builders { + name: "webrtc/try/win_compile_x64_clang_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_rel" + } + builders { + name: "webrtc/try/win_compile_x86_clang_dbg" + } + builders { + name: "webrtc/try/win_x86_clang_rel" + } + builders { + name: "webrtc/try/win_x86_more_configs" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} +config_groups { + name: "cq_infra" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/heads/infra/config" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-admins" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tryjob { + builders { + name: "webrtc/try/presubmit" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} diff --git a/infra/config/config.star b/infra/config/config.star new file mode 100755 index 0000000000..f089c656ba --- /dev/null +++ b/infra/config/config.star @@ -0,0 +1,856 @@ +#!/usr/bin/env lucicfg + +# Copyright (c) 2019 The WebRTC project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# https://chromium.googlesource.com/infra/luci/luci-go/+/main/lucicfg/doc/ + +"""LUCI project configuration for WebRTC CQ and CI.""" + +lucicfg.check_version("1.30.9") + +WEBRTC_GIT = "https://webrtc.googlesource.com/src" +WEBRTC_GERRIT = "https://webrtc-review.googlesource.com/src" +WEBRTC_TROOPER_EMAIL = "webrtc-troopers-robots@google.com" +WEBRTC_IOS_XCODE_VERSION = "12a7209" +WEBRTC_XCODE13 = "13a233" +DEFAULT_CPU = "x86-64" + +# Helpers: + +def make_goma_properties(enable_ats = True, jobs = None): + """Makes a default goma property with the specified argument. + + Args: + enable_ats: True if the ATS should be enabled. + jobs: Number of jobs to be used by the builder. + Returns: + A dictonary with the goma properties. + """ + goma_properties = { + "server_host": "goma.chromium.org", + "use_luci_auth": True, + } + if not enable_ats: + goma_properties["enable_ats"] = enable_ats + if jobs: + goma_properties["jobs"] = jobs + return {"$build/goma": goma_properties} + +# Add names of builders to remove from LKGR finder to this list. This is +# useful when a failure can be safely ignored while fixing it without +# blocking the LKGR finder on it. +skipped_lkgr_bots = [ +] + +# Use LUCI Scheduler BBv2 names and add Scheduler realms configs. +lucicfg.enable_experiment("crbug.com/1182002") + +luci.builder.defaults.experiments.set( + { + "luci.recipes.use_python3": 100, + }, +) +luci.builder.defaults.test_presentation.set( + resultdb.test_presentation(grouping_keys = ["status", "v.test_suite"]), +) + +lucicfg.config( + config_dir = ".", + tracked_files = [ + "commit-queue.cfg", + "cr-buildbucket.cfg", + "luci-logdog.cfg", + "luci-milo.cfg", + "luci-notify.cfg", + "luci-notify/**/*", + "luci-scheduler.cfg", + "project.cfg", + "realms.cfg", + ], + lint_checks = ["default"], +) + +luci.project( + name = "webrtc", + buildbucket = "cr-buildbucket.appspot.com", + logdog = "luci-logdog.appspot.com", + milo = "luci-milo.appspot.com", + notify = "luci-notify.appspot.com", + scheduler = "luci-scheduler.appspot.com", + swarming = "chromium-swarm.appspot.com", + acls = [ + acl.entry( + [acl.BUILDBUCKET_READER, acl.LOGDOG_READER, acl.PROJECT_CONFIGS_READER, acl.SCHEDULER_READER], + groups = ["all"], + ), + acl.entry(acl.LOGDOG_WRITER, groups = ["luci-logdog-chromium-writers"]), + acl.entry(acl.SCHEDULER_OWNER, groups = ["project-webrtc-admins"]), + ], + bindings = [ + luci.binding( + roles = "role/configs.validator", + users = [ + "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com", + ], + ), + luci.binding( + roles = "role/swarming.poolOwner", + groups = "project-webrtc-admins", + ), + luci.binding( + roles = "role/swarming.poolViewer", + groups = "all", + ), + # Allow any WebRTC build to trigger a test ran under chromium-tester@ + # task service account. + luci.binding( + roles = "role/swarming.taskServiceAccount", + users = [ + "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + ], + ), + ], +) + +luci.logdog( + gs_bucket = "chromium-luci-logdog", +) + +luci.milo( + logo = "https://storage.googleapis.com/chrome-infra/webrtc-logo-vert-retro-255x305.png", +) + +luci.notify(tree_closing_enabled = True) + +luci.cq( + status_host = "chromium-cq-status.appspot.com", + submit_max_burst = 1, + submit_burst_delay = 1 * time.minute, +) + +luci.gitiles_poller( + name = "webrtc-gitiles-trigger-main", + bucket = "ci", + repo = WEBRTC_GIT, + refs = ["refs/heads/main"], +) + +# Swarming permissions: + +luci.realm(name = "pools/cron", bindings = [ + # Unlike WebRTC's own builders, other projects need an explicit grant to use this pool. + luci.binding( + roles = "role/swarming.poolUser", + projects = "libyuv", + ), +]) + +luci.realm(name = "pools/ci") +luci.realm(name = "pools/ci-tests", bindings = [ + # Allow task service accounts of .ci pool/bucket to trigger tasks here. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-ci-task-accounts", + ), + # Allow tasks here to use .ci task service accounts. + luci.binding( + roles = "role/swarming.taskServiceAccount", + groups = "project-webrtc-ci-task-accounts", + ), +]) +luci.realm(name = "ci", bindings = [ + # Allow CI builders to create invocations in their own builds. + luci.binding( + roles = "role/resultdb.invocationCreator", + groups = "project-webrtc-ci-task-accounts", + ), +]) + +luci.realm(name = "pools/try", bindings = [ + # Allow to use LED & Swarming "Debug" feature to a larger group but only on try bots / builders. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-led-users", + ), +]) +luci.realm(name = "pools/try-tests", bindings = [ + # Allow task service accounts of .try pool/bucket to trigger tasks here. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-try-task-accounts", + ), + # Allow tasks here to use .try task service accounts. + luci.binding( + roles = "role/swarming.taskServiceAccount", + groups = "project-webrtc-try-task-accounts", + ), +]) +luci.realm(name = "try", bindings = [ + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-led-users", + ), + # Allow try builders to create invocations in their own builds. + luci.binding( + roles = "role/resultdb.invocationCreator", + groups = "project-webrtc-try-task-accounts", + ), +]) + +luci.realm(name = "pools/perf", bindings = [ + # Allow to use LED & Swarming "Debug" feature to a larger group but only on perf bots / builders. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-led-users", + ), +]) +luci.realm(name = "perf", bindings = [ + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-led-users", + ), +]) + +luci.realm(name = "@root", bindings = [ + # Allow admins to use LED & Swarming "Debug" feature on all WebRTC bots. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-admins", + ), + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-admins", + ), +]) + +# Bucket definitions: + +luci.bucket( + name = "try", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "service-account-cq", + "project-webrtc-tryjob-access", + ]), + ], +) + +luci.bucket( + name = "ci", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "project-webrtc-ci-schedulers", + ]), + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + # Allow Pinpoint to trigger builds for bisection + "service-account-chromeperf", + ]), + ], +) + +luci.bucket( + name = "perf", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, users = [ + "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + ]), + ], +) + +luci.bucket( + name = "cron", +) + +# Commit queue definitions: + +luci.cq_group( + name = "cq", + tree_status_host = "webrtc-status.appspot.com", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/heads/master", "refs/heads/main"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-committers"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_group( + name = "cq_branch", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/branch-heads/.+"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-committers"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_group( + name = "cq_infra", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/heads/infra/config"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-admins"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_tryjob_verifier( + builder = "presubmit", + cq_group = "cq_infra", +) + +luci.cq_tryjob_verifier( + builder = "webrtc-internal:g3.webrtc-internal.try/internal_compile_lite", + owner_whitelist = ["project-webrtc-internal-tryjob-access"], + cq_group = "cq", +) + +# Notifier definitions: + +luci.notifier( + name = "post_submit_failure_notifier", + on_new_status = ["FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + notify_blamelist = True, + template = luci.notifier_template( + name = "build_failure", + body = io.read_file("luci-notify/email-templates/build_failure.template"), + ), +) + +luci.notifier( + name = "cron_notifier", + on_new_status = ["FAILURE", "INFRA_FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + template = luci.notifier_template( + name = "cron", + body = io.read_file("luci-notify/email-templates/cron.template"), + ), +) + +luci.notifier( + name = "infra_failure_notifier", + on_new_status = ["INFRA_FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + template = luci.notifier_template( + name = "infra_failure", + body = io.read_file("luci-notify/email-templates/infra_failure.template"), + ), +) + +# Tree closer definitions: + +luci.tree_closer( + name = "webrtc_tree_closer", + tree_status_host = "webrtc-status.appspot.com", + # TODO: These step filters are copied verbatim from Gatekeeper, for testing + # that LUCI-Notify would take the exact same actions. Once we've switched + # over, this should be updated - several of these steps don't exist in + # WebRTC recipes. + failed_step_regexp = [ + "bot_update", + "compile", + "gclient runhooks", + "runhooks", + "update", + "extract build", + "cleanup_temp", + "taskkill", + "compile", + "gn", + ], + failed_step_regexp_exclude = ".*\\(experimental\\).*", +) + +# Recipe definitions: + +def recipe(recipe, pkg = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"): + return luci.recipe( + name = recipe.split("/")[-1], + cipd_package = pkg, + cipd_version = "refs/heads/main", + recipe = recipe, + use_python3 = True, + ) + +recipe("chromium_trybot") +recipe("run_presubmit") +recipe("webrtc/auto_roll_webrtc_deps") +recipe("webrtc/ios_api_framework") +recipe("webrtc/libfuzzer") +recipe("webrtc/standalone") +recipe("webrtc/update_webrtc_binary_version") +recipe("lkgr_finder", pkg = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build") + +# Console definitions: + +luci.console_view(name = "ci", title = "Main", repo = WEBRTC_GIT, header = "console-header.textpb", refs = ["refs/heads/master", "refs/heads/main"]) +luci.console_view(name = "perf", title = "Perf", repo = WEBRTC_GIT, header = "console-header.textpb", refs = ["refs/heads/master", "refs/heads/main"]) +luci.list_view(name = "cron", title = "Cron") +luci.list_view(name = "try", title = "Tryserver") + +def add_milo(builder, views): + """Add Milo console entries for the builder. + + Args: + builder: builder name (str). + views: dict where keys are names of consoles and values are either a + category for the console (str, pipe-separated) or True, which means + adding to a list view rather than a console. + """ + for view_name, category in views.items(): + if category == None: + continue + elif type(category) == "string": + category, _, short_name = category.rpartition("|") + luci.console_view_entry( + console_view = view_name, + builder = builder, + category = category or None, + short_name = short_name or None, + ) + elif category == True: + luci.list_view_entry( + list_view = view_name, + builder = builder, + ) + else: + fail("Unexpected value for category: %r" % category) + +lkgr_builders = [] + +# Builder-defining functions: + +def webrtc_builder( + name, + bucket, + dimensions, + properties = None, + recipe = "standalone", + priority = 30, + execution_timeout = 2 * time.hour, + **kwargs): + """WebRTC specific wrapper around luci.builder. + + Args: + name: builder name (str). + bucket: The name of the bucket the builder belongs to. + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + recipe: string with the name of the recipe to run. + priority: int [1-255] or None, indicating swarming task priority, lower is + more important. If None, defer the decision to Buildbucket service. + execution_timeout: int or None, how long to wait for a running build to finish before + forcefully aborting it and marking the build as timed out. If None, + defer the decision to Buildbucket service. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + """ + properties = properties or {} + properties["$recipe_engine/isolated"] = { + "server": "https://isolateserver.appspot.com", + } + resultdb_bq_table = "webrtc-ci.resultdb." + bucket + "_test_results" + return luci.builder( + name = name, + bucket = bucket, + executable = recipe, + dimensions = dimensions, + properties = properties, + execution_timeout = execution_timeout, + priority = priority, + build_numbers = True, + swarming_tags = ["vpython:native-python-wrapper"], + resultdb_settings = resultdb.settings( + enable = True, + bq_exports = [ + resultdb.export_test_results(bq_table = resultdb_bq_table), + ], + ), + **kwargs + ) + +def ci_builder( + name, + ci_cat, + dimensions, + properties = None, + perf_cat = None, + prioritized = False, + enabled = True, + **kwargs): + """Add a post-submit builder. + + Args: + name: builder name (str). + ci_cat: the category + name for the /ci/ console, or None to omit from the console. + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + perf_cat: the category + name for the /perf/ console, or None to omit from the console. + prioritized: True to make this builder have a higher priority and never batch builds. + enabled: False to exclude this builder from consoles and failure notifications. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + + Notifications are also disabled if a builder is not on either of /ci/ or /perf/ consoles. + """ + if prioritized: + kwargs["triggering_policy"] = scheduler.greedy_batching( + max_batch_size = 1, + max_concurrent_invocations = 3, + ) + kwargs["priority"] = 29 + + if enabled: + add_milo(name, {"ci": ci_cat, "perf": perf_cat}) + if ci_cat: + lkgr_builders.append(name) + dimensions.update({"pool": "luci.webrtc.ci", "cpu": kwargs.pop("cpu", DEFAULT_CPU)}) + properties = properties or {} + properties["builder_group"] = "client.webrtc" + properties.update(make_goma_properties()) + notifies = ["post_submit_failure_notifier", "infra_failure_notifier"] + notifies += ["webrtc_tree_closer"] if name not in skipped_lkgr_bots else [] + return webrtc_builder( + name = name, + dimensions = dimensions, + properties = properties, + bucket = "ci", + service_account = "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + triggered_by = ["webrtc-gitiles-trigger-main"] if enabled else None, + repo = WEBRTC_GIT, + notifies = notifies if enabled else None, + **kwargs + ) + +def try_builder( + name, + dimensions, + properties = None, + try_cat = True, + cq = {}, + branch_cq = True, + goma_enable_ats = True, + goma_jobs = None, + **kwargs): + """Add a pre-submit builder. + + Args: + name: builder name (str). + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + try_cat: boolean, whether to include this builder in the /try/ console. See also: `add_milo`. + cq: None to exclude this from all commit queues, or a dict of kwargs for cq_tryjob_verifier. + branch_cq: False to exclude this builder just from the release-branch CQ. + goma_enable_ats: True if the ATS should be enabled by the builder. + goma_jobs: Number of jobs to be used by the builder. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + """ + add_milo(name, {"try": try_cat}) + dimensions.update({"pool": "luci.webrtc.try", "cpu": DEFAULT_CPU}) + properties = properties or {} + properties["builder_group"] = "tryserver.webrtc" + properties.update(make_goma_properties(enable_ats = goma_enable_ats, jobs = goma_jobs)) + if cq != None: + luci.cq_tryjob_verifier(name, cq_group = "cq", **cq) + if branch_cq: + luci.cq_tryjob_verifier(name, cq_group = "cq_branch", **cq) + + return webrtc_builder( + name = name, + dimensions = dimensions, + properties = properties, + bucket = "try", + service_account = "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com", + notifies = ["infra_failure_notifier"], + **kwargs + ) + +def perf_builder(name, perf_cat, **kwargs): + add_milo(name, {"perf": perf_cat}) + properties = make_goma_properties() + properties["builder_group"] = "client.webrtc.perf" + return webrtc_builder( + name = name, + dimensions = {"pool": "luci.webrtc.perf", "os": "Linux"}, + properties = properties, + bucket = "perf", + service_account = "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + # log_base of 1.7 means: + # when there are P pending builds, LUCI will batch the first B builds. + # P: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... + # B: 1 1 2 2 3 3 3 3 4 4 4 4 4 4 5 ... + triggering_policy = scheduler.logarithmic_batching(log_base = 1.7), + repo = WEBRTC_GIT, + execution_timeout = 3 * time.hour, + notifies = ["post_submit_failure_notifier", "infra_failure_notifier"], + **kwargs + ) + +def cron_builder(name, service_account = None, **kwargs): + if service_account == None: + service_account = "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + add_milo(name, {"cron": True}) + return webrtc_builder( + name = name, + dimensions = {"pool": "luci.webrtc.cron", "os": "Linux", "cpu": DEFAULT_CPU}, + bucket = "cron", + service_account = service_account, + notifies = ["cron_notifier"], + **kwargs + ) + +def normal_builder_factory(**common_kwargs): + def builder(*args, **kwargs): + kwargs.update(common_kwargs) + return ci_builder(*args, **kwargs) + + def try_job(name, **kwargs): + kwargs.update(common_kwargs) + return try_builder(name, **kwargs) + + return builder, try_job + +# Mixins: + +linux_builder, linux_try_job = normal_builder_factory( + dimensions = {"os": "Linux", "inside_docker": "0"}, +) + +android_builder, android_try_job = normal_builder_factory( + dimensions = {"os": "Linux"}, +) + +win_builder = normal_builder_factory( + dimensions = {"os": "Windows"}, +)[0] + +win_try_job = normal_builder_factory( + dimensions = {"os": "Windows"}, + goma_enable_ats = False, +)[1] + +mac_builder, mac_try_job = normal_builder_factory( + dimensions = {"os": "Mac"}, +) + +ios_builder, ios_try_job = normal_builder_factory( + dimensions = {"os": "Mac-10.15"}, + properties = {"xcode_build_version": WEBRTC_IOS_XCODE_VERSION}, + caches = [swarming.cache( + name = "xcode_ios_" + WEBRTC_IOS_XCODE_VERSION, + path = "xcode_ios_" + WEBRTC_IOS_XCODE_VERSION + ".app", + )], +) + +ios_builder_macos11, ios_try_job_macos11 = normal_builder_factory( + dimensions = {"os": "Mac-11"}, + properties = {"xcode_build_version": WEBRTC_XCODE13}, + caches = [swarming.cache( + name = "xcode_ios_" + WEBRTC_XCODE13, + path = "xcode_ios_" + WEBRTC_XCODE13 + ".app", + )], +) + +# Actual builder configuration: + +android_builder("Android32 (M Nexus5X)(dbg)", "Android|arm|dbg") +android_try_job("android_compile_arm_dbg", cq = None) +android_try_job("android_arm_dbg") +android_builder("Android32 (M Nexus5X)", "Android|arm|rel") +android_try_job("android_arm_rel") +android_builder("Android32 Builder arm", "Android|arm|size", perf_cat = "Android|arm|Builder|", prioritized = True) +android_try_job("android_compile_arm_rel") +perf_builder("Perf Android32 (M Nexus5)", "Android|arm|Tester|M Nexus5", triggered_by = ["Android32 Builder arm"]) +perf_builder("Perf Android32 (M AOSP Nexus6)", "Android|arm|Tester|M AOSP Nexus6", triggered_by = ["Android32 Builder arm"]) +android_try_job("android_compile_arm64_dbg", cq = None) +android_try_job("android_arm64_dbg", cq = None) +android_builder("Android64 (M Nexus5X)", "Android|arm64|rel") +android_try_job("android_arm64_rel") +android_builder("Android64 Builder arm64", "Android|arm64|size", perf_cat = "Android|arm64|Builder|", prioritized = True) +perf_builder("Perf Android64 (M Nexus5X)", "Android|arm64|Tester|M Nexus5X", triggered_by = ["Android64 Builder arm64"]) +perf_builder("Perf Android64 (O Pixel2)", "Android|arm64|Tester|O Pixel2", triggered_by = ["Android64 Builder arm64"]) +android_try_job("android_compile_arm64_rel") +android_builder("Android64 Builder x64 (dbg)", "Android|x64|dbg") +android_try_job("android_compile_x64_dbg") +android_try_job("android_compile_x64_rel", cq = None) +android_builder("Android32 Builder x86 (dbg)", "Android|x86|dbg") +android_try_job("android_compile_x86_dbg") +android_builder("Android32 Builder x86", "Android|x86|rel") +android_try_job("android_compile_x86_rel") +android_builder("Android32 (more configs)", "Android|arm|more") +android_try_job("android_arm_more_configs") +android_try_job("android_chromium_compile", recipe = "chromium_trybot", branch_cq = False) + +ios_builder("iOS64 Debug", "iOS|arm64|dbg") +ios_try_job("ios_compile_arm64_dbg") +ios_builder("iOS64 Release", "iOS|arm64|rel") +ios_try_job("ios_compile_arm64_rel") +ios_builder("iOS64 Sim Debug (iOS 14.0)", "iOS|x64|14") +ios_try_job("ios_sim_x64_dbg_ios14") +ios_builder("iOS64 Sim Debug (iOS 13)", "iOS|x64|13") +ios_try_job("ios_sim_x64_dbg_ios13") +ios_builder("iOS64 Sim Debug (iOS 12)", "iOS|x64|12") +ios_try_job("ios_sim_x64_dbg_ios12") +ios_builder_macos11("iOS API Framework Builder", "iOS|fat|size", recipe = "ios_api_framework", prioritized = True) +ios_try_job_macos11("ios_api_framework", recipe = "ios_api_framework") + +linux_builder("Linux32 Debug", "Linux|x86|dbg") +linux_try_job("linux_x86_dbg") +linux_builder("Linux32 Release", "Linux|x86|rel") +linux_try_job("linux_x86_rel") +linux_builder("Linux64 Debug", "Linux|x64|dbg") +linux_try_job("linux_dbg", cq = None) +linux_try_job("linux_compile_dbg") +linux_builder("Linux64 Release", "Linux|x64|rel") +linux_try_job("linux_rel") +linux_builder("Linux64 Builder", "Linux|x64|size", perf_cat = "Linux|x64|Builder|", prioritized = True) +linux_try_job("linux_compile_rel") +perf_builder("Perf Linux Trusty", "Linux|x64|Tester|Trusty", triggered_by = ["Linux64 Builder"]) +perf_builder("Perf Linux Bionic", "Linux|x64|Tester|Bionic", triggered_by = ["Linux64 Builder"]) +linux_builder("Linux32 Debug (ARM)", "Linux|arm|dbg") +linux_try_job("linux_compile_arm_dbg") +linux_builder("Linux32 Release (ARM)", "Linux|arm|rel") +linux_try_job("linux_compile_arm_rel") +linux_builder("Linux64 Debug (ARM)", "Linux|arm64|dbg") +linux_try_job("linux_compile_arm64_dbg") +linux_builder("Linux64 Release (ARM)", "Linux|arm64|rel") +linux_try_job("linux_compile_arm64_rel") +linux_builder("Linux Asan", "Linux|x64|asan") +linux_try_job("linux_asan") +linux_builder("Linux MSan", "Linux|x64|msan") +linux_try_job("linux_msan") +linux_builder("Linux Tsan v2", "Linux|x64|tsan") +linux_try_job("linux_tsan2") +linux_builder("Linux UBSan", "Linux|x64|ubsan") +linux_try_job("linux_ubsan") +linux_builder("Linux UBSan vptr", "Linux|x64|ubsan") +linux_try_job("linux_ubsan_vptr") +linux_builder("Linux64 Release (Libfuzzer)", "Linux|x64|fuzz", recipe = "libfuzzer") +linux_try_job("linux_libfuzzer_rel", recipe = "libfuzzer") +linux_builder("Linux (more configs)", "Linux|x64|more") +linux_try_job("linux_more_configs") +linux_try_job("linux_chromium_compile", recipe = "chromium_trybot", branch_cq = False) +linux_try_job("linux_chromium_compile_dbg", recipe = "chromium_trybot", branch_cq = False) + +mac_builder("Mac64 Debug", "Mac|x64|dbg") +mac_try_job("mac_dbg", cq = None) +mac_try_job("mac_compile_dbg") +mac_builder("Mac64 Release", "Mac|x64|rel") +mac_try_job("mac_rel") +mac_try_job("mac_compile_rel", cq = None) +mac_builder("Mac64 Builder", ci_cat = None, perf_cat = "Mac|x64|Builder|") +perf_builder("Perf Mac 10.11", "Mac|x64|Tester|10.11", triggered_by = ["Mac64 Builder"]) +mac_builder("Mac Asan", "Mac|x64|asan") +mac_try_job("mac_asan") +mac_try_job("mac_chromium_compile", recipe = "chromium_trybot", branch_cq = False) +mac_builder("MacARM64 M1 Release", "Mac|arm64M1|rel", cpu = "arm64-64-Apple_M1") +mac_try_job("mac_rel_m1", try_cat = None, cq = None) +mac_try_job("mac_dbg_m1", try_cat = None, cq = None) + +win_builder("Win32 Debug (Clang)", "Win Clang|x86|dbg") +win_try_job("win_x86_clang_dbg", cq = None) +win_try_job("win_compile_x86_clang_dbg") +win_builder("Win32 Release (Clang)", "Win Clang|x86|rel") +win_try_job("win_x86_clang_rel") +win_try_job("win_compile_x86_clang_rel", cq = None) +win_builder("Win32 Builder (Clang)", ci_cat = None, perf_cat = "Win|x86|Builder|") +perf_builder("Perf Win7", "Win|x86|Tester|7", triggered_by = ["Win32 Builder (Clang)"]) +win_builder("Win64 Debug (Clang)", "Win Clang|x64|dbg") +win_try_job("win_x64_clang_dbg", cq = None) +win_try_job("win_x64_clang_dbg_win10", cq = None) +win_try_job("win_compile_x64_clang_dbg") +win_builder("Win64 Release (Clang)", "Win Clang|x64|rel") +win_try_job("win_x64_clang_rel", cq = None) +win_try_job("win_compile_x64_clang_rel") +win_builder("Win64 ASan", "Win Clang|x64|asan") +win_try_job("win_asan") +win_builder("Win (more configs)", "Win Clang|x86|more") +win_try_job("win_x86_more_configs") +win_try_job("win_chromium_compile", recipe = "chromium_trybot", branch_cq = False, goma_jobs = 150) +win_try_job("win_chromium_compile_dbg", recipe = "chromium_trybot", branch_cq = False, goma_jobs = 150) + +linux_try_job( + "presubmit", + recipe = "run_presubmit", + properties = {"repo_name": "webrtc", "runhooks": True}, + priority = 28, + cq = {"disable_reuse": True}, +) + +cron_builder( + "Auto-roll - WebRTC DEPS", + recipe = "auto_roll_webrtc_deps", + schedule = "0 */2 * * *", # Every 2 hours. +) + +cron_builder( + "WebRTC version update", + recipe = "update_webrtc_binary_version", + schedule = "0 4 * * *", # Every day at 4am. + service_account = "webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com", +) + +lkgr_config = { + "project": "webrtc", + "source_url": WEBRTC_GIT, + "status_url": "https://webrtc-status.appspot.com", + "allowed_lag": 150, # hours + "allowed_gap": 4, # commits behind + "error_recipients": WEBRTC_TROOPER_EMAIL, + "buckets": { + "webrtc/ci": { + # bucket alias: luci.webrtc.ci + "builders": [ + b + for b in sorted(lkgr_builders) + if b not in skipped_lkgr_bots + ], + }, + "chromium/webrtc.fyi": { + # bucket alias: luci.chromium.webrtc.fyi + "builders": [ + "WebRTC Chromium FYI Android Builder (dbg)", + "WebRTC Chromium FYI Android Builder ARM64 (dbg)", + "WebRTC Chromium FYI Android Builder", + "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)", + "WebRTC Chromium FYI Android Tests (dbg) (N Nexus5X)", + "WebRTC Chromium FYI Linux Builder (dbg)", + "WebRTC Chromium FYI Linux Builder", + "WebRTC Chromium FYI Linux Tester", + "WebRTC Chromium FYI Mac Builder (dbg)", + "WebRTC Chromium FYI Mac Builder", + "WebRTC Chromium FYI Mac Tester", + "WebRTC Chromium FYI Win Builder (dbg)", + "WebRTC Chromium FYI Win Builder", + "WebRTC Chromium FYI Win10 Tester", + "WebRTC Chromium FYI Win7 Tester", + "WebRTC Chromium FYI ios-device", + "WebRTC Chromium FYI ios-simulator", + ], + }, + }, +} + +cron_builder( + "WebRTC lkgr finder", + recipe = "lkgr_finder", + properties = { + "project": "webrtc", + "repo": WEBRTC_GIT, + "ref": "refs/heads/lkgr", + "src_ref": "refs/heads/main", + "lkgr_status_gs_path": "chromium-webrtc/lkgr-status", + "config": lkgr_config, + }, + schedule = "*/10 * * * *", # Every 10 minutes. +) diff --git a/infra/config/console-header.textpb b/infra/config/console-header.textpb new file mode 100644 index 0000000000..79e5d2bf21 --- /dev/null +++ b/infra/config/console-header.textpb @@ -0,0 +1,59 @@ +tree_status_host: "webrtc-status.appspot.com" +links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } +} +links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } +} +console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" +} diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg new file mode 100644 index 0000000000..55aeece8db --- /dev/null +++ b/infra/config/cr-buildbucket.cfg @@ -0,0 +1,5864 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see BuildbucketCfg message: +# https://luci-config.appspot.com/schemas/projects:buildbucket.cfg + +buckets { + name: "ci" + acls { + group: "all" + } + acls { + role: SCHEDULER + group: "project-webrtc-ci-schedulers" + } + acls { + role: SCHEDULER + group: "service-account-chromeperf" + } + swarming { + builders { + name: "Android32 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 (M Nexus5X)(dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 Builder arm" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 Builder x86" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 Builder x86 (dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android64 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android64 Builder arm64" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android64 Builder x64 (dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux Asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux MSan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux Tsan v2" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux UBSan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux UBSan vptr" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Debug (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Release (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Debug (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release (Libfuzzer)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/libfuzzer"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac Asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "MacARM64 M1 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:arm64-64-Apple_M1" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Builder (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Debug (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Release (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 ASan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 Debug (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 Release (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS API Framework Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/ios_api_framework",' + ' "xcode_build_version": "13a233"' + '}' + priority: 29 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13a233" + path: "xcode_ios_13a233.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 12)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 13)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 14.0)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "cron" + acls { + group: "all" + } + swarming { + builders { + name: "Auto-roll - WebRTC DEPS" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "recipe": "webrtc/auto_roll_webrtc_deps"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + builders { + name: "WebRTC lkgr finder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "config": {' + ' "allowed_gap": 4,' + ' "allowed_lag": 150,' + ' "buckets": {' + ' "chromium/webrtc.fyi": {' + ' "builders": [' + ' "WebRTC Chromium FYI Android Builder (dbg)",' + ' "WebRTC Chromium FYI Android Builder ARM64 (dbg)",' + ' "WebRTC Chromium FYI Android Builder",' + ' "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)",' + ' "WebRTC Chromium FYI Android Tests (dbg) (N Nexus5X)",' + ' "WebRTC Chromium FYI Linux Builder (dbg)",' + ' "WebRTC Chromium FYI Linux Builder",' + ' "WebRTC Chromium FYI Linux Tester",' + ' "WebRTC Chromium FYI Mac Builder (dbg)",' + ' "WebRTC Chromium FYI Mac Builder",' + ' "WebRTC Chromium FYI Mac Tester",' + ' "WebRTC Chromium FYI Win Builder (dbg)",' + ' "WebRTC Chromium FYI Win Builder",' + ' "WebRTC Chromium FYI Win10 Tester",' + ' "WebRTC Chromium FYI Win7 Tester",' + ' "WebRTC Chromium FYI ios-device",' + ' "WebRTC Chromium FYI ios-simulator"' + ' ]' + ' },' + ' "webrtc/ci": {' + ' "builders": [' + ' "Android32 (M Nexus5X)",' + ' "Android32 (M Nexus5X)(dbg)",' + ' "Android32 (more configs)",' + ' "Android32 Builder arm",' + ' "Android32 Builder x86",' + ' "Android32 Builder x86 (dbg)",' + ' "Android64 (M Nexus5X)",' + ' "Android64 Builder arm64",' + ' "Android64 Builder x64 (dbg)",' + ' "Linux (more configs)",' + ' "Linux Asan",' + ' "Linux MSan",' + ' "Linux Tsan v2",' + ' "Linux UBSan",' + ' "Linux UBSan vptr",' + ' "Linux32 Debug",' + ' "Linux32 Debug (ARM)",' + ' "Linux32 Release",' + ' "Linux32 Release (ARM)",' + ' "Linux64 Builder",' + ' "Linux64 Debug",' + ' "Linux64 Debug (ARM)",' + ' "Linux64 Release",' + ' "Linux64 Release (ARM)",' + ' "Linux64 Release (Libfuzzer)",' + ' "Mac Asan",' + ' "Mac64 Debug",' + ' "Mac64 Release",' + ' "MacARM64 M1 Release",' + ' "Win (more configs)",' + ' "Win32 Debug (Clang)",' + ' "Win32 Release (Clang)",' + ' "Win64 ASan",' + ' "Win64 Debug (Clang)",' + ' "Win64 Release (Clang)",' + ' "iOS API Framework Builder",' + ' "iOS64 Debug",' + ' "iOS64 Release",' + ' "iOS64 Sim Debug (iOS 12)",' + ' "iOS64 Sim Debug (iOS 13)",' + ' "iOS64 Sim Debug (iOS 14.0)"' + ' ]' + ' }' + ' },' + ' "error_recipients": "webrtc-troopers-robots@google.com",' + ' "project": "webrtc",' + ' "source_url": "https://webrtc.googlesource.com/src",' + ' "status_url": "https://webrtc-status.appspot.com"' + ' },' + ' "lkgr_status_gs_path": "chromium-webrtc/lkgr-status",' + ' "project": "webrtc",' + ' "recipe": "lkgr_finder",' + ' "ref": "refs/heads/lkgr",' + ' "repo": "https://webrtc.googlesource.com/src",' + ' "src_ref": "refs/heads/main"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + builders { + name: "WebRTC version update" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "recipe": "webrtc/update_webrtc_binary_version"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "perf" + acls { + group: "all" + } + acls { + role: SCHEDULER + identity: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + swarming { + builders { + name: "Perf Android32 (M AOSP Nexus6)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android32 (M Nexus5)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android64 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android64 (O Pixel2)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Linux Bionic" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Linux Trusty" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Mac 10.11" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Win7" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "try" + acls { + group: "all" + } + acls { + role: SCHEDULER + group: "project-webrtc-tryjob-access" + } + acls { + role: SCHEDULER + group: "service-account-cq" + } + swarming { + builders { + name: "android_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x86_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x86_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_api_framework" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/ios_api_framework",' + ' "xcode_build_version": "13a233"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13a233" + path: "xcode_ios_13a233.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios12" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios13" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios14" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-10.15" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "12a7209"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_12a7209" + path: "xcode_ios_12a7209.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_chromium_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_libfuzzer_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/libfuzzer"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_msan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_tsan2" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_ubsan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_ubsan_vptr" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_x86_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_x86_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_compile_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_dbg_m1" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_rel_m1" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "presubmit" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "run_presubmit",' + ' "repo_name": "webrtc",' + ' "runhooks": true' + '}' + priority: 28 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "jobs": 150,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_chromium_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "jobs": 150,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x64_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x64_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x86_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x86_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_dbg_win10" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + } +} diff --git a/infra/config/luci-logdog.cfg b/infra/config/luci-logdog.cfg new file mode 100644 index 0000000000..adc75bef49 --- /dev/null +++ b/infra/config/luci-logdog.cfg @@ -0,0 +1,9 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg + +reader_auth_groups: "all" +writer_auth_groups: "luci-logdog-chromium-writers" +archive_gs_bucket: "chromium-luci-logdog" diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg new file mode 100644 index 0000000000..c598665644 --- /dev/null +++ b/infra/config/luci-milo.cfg @@ -0,0 +1,606 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Project message: +# https://luci-config.appspot.com/schemas/projects:luci-milo.cfg + +consoles { + id: "ci" + name: "Main" + repo_url: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/master" + refs: "regexp:refs/heads/main" + manifest_name: "REVISION" + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (M Nexus5X)(dbg)" + category: "Android|arm" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (M Nexus5X)" + category: "Android|arm" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder arm" + category: "Android|arm" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 (M Nexus5X)" + category: "Android|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 Builder arm64" + category: "Android|arm64" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 Builder x64 (dbg)" + category: "Android|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder x86 (dbg)" + category: "Android|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder x86" + category: "Android|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (more configs)" + category: "Android|arm" + short_name: "more" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Debug" + category: "iOS|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Release" + category: "iOS|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 14.0)" + category: "iOS|x64" + short_name: "14" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 13)" + category: "iOS|x64" + short_name: "13" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 12)" + category: "iOS|x64" + short_name: "12" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS API Framework Builder" + category: "iOS|fat" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Debug" + category: "Linux|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Release" + category: "Linux|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Debug" + category: "Linux|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release" + category: "Linux|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Builder" + category: "Linux|x64" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Debug (ARM)" + category: "Linux|arm" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Release (ARM)" + category: "Linux|arm" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Debug (ARM)" + category: "Linux|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release (ARM)" + category: "Linux|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux Asan" + category: "Linux|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux MSan" + category: "Linux|x64" + short_name: "msan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux Tsan v2" + category: "Linux|x64" + short_name: "tsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux UBSan" + category: "Linux|x64" + short_name: "ubsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux UBSan vptr" + category: "Linux|x64" + short_name: "ubsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release (Libfuzzer)" + category: "Linux|x64" + short_name: "fuzz" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux (more configs)" + category: "Linux|x64" + short_name: "more" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac64 Debug" + category: "Mac|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac64 Release" + category: "Mac|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac Asan" + category: "Mac|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/MacARM64 M1 Release" + category: "Mac|arm64M1" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win32 Debug (Clang)" + category: "Win Clang|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win32 Release (Clang)" + category: "Win Clang|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 Debug (Clang)" + category: "Win Clang|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 Release (Clang)" + category: "Win Clang|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 ASan" + category: "Win Clang|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win (more configs)" + category: "Win Clang|x86" + short_name: "more" + } + header { + links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } + } + links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } + } + console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" + } + tree_status_host: "webrtc-status.appspot.com" + } +} +consoles { + id: "perf" + name: "Perf" + repo_url: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/master" + refs: "regexp:refs/heads/main" + manifest_name: "REVISION" + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder arm" + category: "Android|arm|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android32 (M Nexus5)" + category: "Android|arm|Tester" + short_name: "M Nexus5" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android32 (M AOSP Nexus6)" + category: "Android|arm|Tester" + short_name: "M AOSP Nexus6" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 Builder arm64" + category: "Android|arm64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android64 (M Nexus5X)" + category: "Android|arm64|Tester" + short_name: "M Nexus5X" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android64 (O Pixel2)" + category: "Android|arm64|Tester" + short_name: "O Pixel2" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Builder" + category: "Linux|x64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Linux Trusty" + category: "Linux|x64|Tester" + short_name: "Trusty" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Linux Bionic" + category: "Linux|x64|Tester" + short_name: "Bionic" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac64 Builder" + category: "Mac|x64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Mac 10.11" + category: "Mac|x64|Tester" + short_name: "10.11" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win32 Builder (Clang)" + category: "Win|x86|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Win7" + category: "Win|x86|Tester" + short_name: "7" + } + header { + links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } + } + links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } + } + console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" + } + tree_status_host: "webrtc-status.appspot.com" + } +} +consoles { + id: "cron" + name: "Cron" + builders { + name: "buildbucket/luci.webrtc.cron/Auto-roll - WebRTC DEPS" + } + builders { + name: "buildbucket/luci.webrtc.cron/WebRTC version update" + } + builders { + name: "buildbucket/luci.webrtc.cron/WebRTC lkgr finder" + } + builder_view_only: true +} +consoles { + id: "try" + name: "Tryserver" + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x86_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x86_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/android_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios14" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios13" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios12" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_api_framework" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_x86_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_x86_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_msan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_tsan2" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_ubsan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_ubsan_vptr" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_libfuzzer_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_chromium_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_compile_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x86_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x86_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_dbg_win10" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x64_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x64_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/win_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/win_chromium_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/presubmit" + } + builder_view_only: true +} +logo_url: "https://storage.googleapis.com/chrome-infra/webrtc-logo-vert-retro-255x305.png" diff --git a/infra/config/luci-notify.cfg b/infra/config/luci-notify.cfg new file mode 100644 index 0000000000..4d20b9fd3a --- /dev/null +++ b/infra/config/luci-notify.cfg @@ -0,0 +1,2179 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-notify.cfg + +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (M Nexus5X)(dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 Builder arm" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 Builder x86" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 Builder x86 (dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android64 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android64 Builder arm64" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android64 Builder x64 (dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux Asan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux MSan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux Tsan v2" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux UBSan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux UBSan vptr" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Debug (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Release (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Debug (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release (Libfuzzer)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac Asan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac64 Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "MacARM64 M1 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win32 Builder (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win32 Debug (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win32 Release (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 ASan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 Debug (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 Release (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS API Framework Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 12)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 13)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 14.0)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "Auto-roll - WebRTC DEPS" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "WebRTC lkgr finder" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "WebRTC version update" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android32 (M AOSP Nexus6)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android32 (M Nexus5)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android64 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android64 (O Pixel2)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Linux Bionic" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Linux Trusty" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Mac 10.11" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Win7" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_more_configs" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x86_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x86_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_api_framework" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios12" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios13" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios14" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_chromium_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_libfuzzer_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_more_configs" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_msan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_tsan2" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_ubsan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_ubsan_vptr" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_x86_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_x86_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_compile_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_dbg_m1" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_rel_m1" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "presubmit" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_chromium_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x64_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x64_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x86_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x86_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_dbg_win10" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_more_configs" + } +} +tree_closing_enabled: true diff --git a/infra/config/luci-notify/email-templates/build_failure.template b/infra/config/luci-notify/email-templates/build_failure.template new file mode 100644 index 0000000000..dc321cbc1f --- /dev/null +++ b/infra/config/luci-notify/email-templates/build_failure.template @@ -0,0 +1,24 @@ +Test failure in WebRTC on {{ .Build.Builder.Builder }} + +

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

+ +

Full details are available here.

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

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

+ +

Full details are available here.

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

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

+ +

Full details are available here.

+ + + + + + + + + + + + + + + + + + +
New status:{{ .Build.Status }}
Previous status:{{ .OldStatus }}
Created at:{{ .Build.CreateTime | time }}
Finished at:{{ .Build.EndTime | time }}
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg new file mode 100644 index 0000000000..019db39aea --- /dev/null +++ b/infra/config/luci-scheduler.cfg @@ -0,0 +1,715 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-scheduler.cfg + +job { + id: "Android32 (M Nexus5X)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (M Nexus5X)" + } +} +job { + id: "Android32 (M Nexus5X)(dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (M Nexus5X)(dbg)" + } +} +job { + id: "Android32 (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (more configs)" + } +} +job { + id: "Android32 Builder arm" + realm: "ci" + acl_sets: "ci" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 Builder arm" + } +} +job { + id: "Android32 Builder x86" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 Builder x86" + } +} +job { + id: "Android32 Builder x86 (dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 Builder x86 (dbg)" + } +} +job { + id: "Android64 (M Nexus5X)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android64 (M Nexus5X)" + } +} +job { + id: "Android64 Builder arm64" + realm: "ci" + acl_sets: "ci" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android64 Builder arm64" + } +} +job { + id: "Android64 Builder x64 (dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android64 Builder x64 (dbg)" + } +} +job { + id: "Auto-roll - WebRTC DEPS" + realm: "cron" + schedule: "0 */2 * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "Auto-roll - WebRTC DEPS" + } +} +job { + id: "Linux (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux (more configs)" + } +} +job { + id: "Linux Asan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux Asan" + } +} +job { + id: "Linux MSan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux MSan" + } +} +job { + id: "Linux Tsan v2" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux Tsan v2" + } +} +job { + id: "Linux UBSan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux UBSan" + } +} +job { + id: "Linux UBSan vptr" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux UBSan vptr" + } +} +job { + id: "Linux32 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Debug" + } +} +job { + id: "Linux32 Debug (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Debug (ARM)" + } +} +job { + id: "Linux32 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Release" + } +} +job { + id: "Linux32 Release (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Release (ARM)" + } +} +job { + id: "Linux64 Builder" + realm: "ci" + acl_sets: "ci" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Builder" + } +} +job { + id: "Linux64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Debug" + } +} +job { + id: "Linux64 Debug (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Debug (ARM)" + } +} +job { + id: "Linux64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release" + } +} +job { + id: "Linux64 Release (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release (ARM)" + } +} +job { + id: "Linux64 Release (Libfuzzer)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release (Libfuzzer)" + } +} +job { + id: "Mac Asan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac Asan" + } +} +job { + id: "Mac64 Builder" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac64 Builder" + } +} +job { + id: "Mac64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac64 Debug" + } +} +job { + id: "Mac64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac64 Release" + } +} +job { + id: "MacARM64 M1 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "MacARM64 M1 Release" + } +} +job { + id: "Perf Android32 (M AOSP Nexus6)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android32 (M AOSP Nexus6)" + } +} +job { + id: "Perf Android32 (M Nexus5)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android32 (M Nexus5)" + } +} +job { + id: "Perf Android64 (M Nexus5X)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android64 (M Nexus5X)" + } +} +job { + id: "Perf Android64 (O Pixel2)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android64 (O Pixel2)" + } +} +job { + id: "Perf Linux Bionic" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Linux Bionic" + } +} +job { + id: "Perf Linux Trusty" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Linux Trusty" + } +} +job { + id: "Perf Mac 10.11" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Mac 10.11" + } +} +job { + id: "Perf Win7" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Win7" + } +} +job { + id: "WebRTC lkgr finder" + realm: "cron" + schedule: "*/10 * * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "WebRTC lkgr finder" + } +} +job { + id: "WebRTC version update" + realm: "cron" + schedule: "0 4 * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "WebRTC version update" + } +} +job { + id: "Win (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win (more configs)" + } +} +job { + id: "Win32 Builder (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win32 Builder (Clang)" + } +} +job { + id: "Win32 Debug (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win32 Debug (Clang)" + } +} +job { + id: "Win32 Release (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win32 Release (Clang)" + } +} +job { + id: "Win64 ASan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 ASan" + } +} +job { + id: "Win64 Debug (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 Debug (Clang)" + } +} +job { + id: "Win64 Release (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 Release (Clang)" + } +} +job { + id: "iOS API Framework Builder" + realm: "ci" + acl_sets: "ci" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS API Framework Builder" + } +} +job { + id: "iOS64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Debug" + } +} +job { + id: "iOS64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Release" + } +} +job { + id: "iOS64 Sim Debug (iOS 12)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 12)" + } +} +job { + id: "iOS64 Sim Debug (iOS 13)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 13)" + } +} +job { + id: "iOS64 Sim Debug (iOS 14.0)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 14.0)" + } +} +trigger { + id: "webrtc-gitiles-trigger-main" + realm: "ci" + acl_sets: "ci" + triggers: "Android32 (M Nexus5X)" + triggers: "Android32 (M Nexus5X)(dbg)" + triggers: "Android32 (more configs)" + triggers: "Android32 Builder arm" + triggers: "Android32 Builder x86" + triggers: "Android32 Builder x86 (dbg)" + triggers: "Android64 (M Nexus5X)" + triggers: "Android64 Builder arm64" + triggers: "Android64 Builder x64 (dbg)" + triggers: "Linux (more configs)" + triggers: "Linux Asan" + triggers: "Linux MSan" + triggers: "Linux Tsan v2" + triggers: "Linux UBSan" + triggers: "Linux UBSan vptr" + triggers: "Linux32 Debug" + triggers: "Linux32 Debug (ARM)" + triggers: "Linux32 Release" + triggers: "Linux32 Release (ARM)" + triggers: "Linux64 Builder" + triggers: "Linux64 Debug" + triggers: "Linux64 Debug (ARM)" + triggers: "Linux64 Release" + triggers: "Linux64 Release (ARM)" + triggers: "Linux64 Release (Libfuzzer)" + triggers: "Mac Asan" + triggers: "Mac64 Builder" + triggers: "Mac64 Debug" + triggers: "Mac64 Release" + triggers: "MacARM64 M1 Release" + triggers: "Win (more configs)" + triggers: "Win32 Builder (Clang)" + triggers: "Win32 Debug (Clang)" + triggers: "Win32 Release (Clang)" + triggers: "Win64 ASan" + triggers: "Win64 Debug (Clang)" + triggers: "Win64 Release (Clang)" + triggers: "iOS API Framework Builder" + triggers: "iOS64 Debug" + triggers: "iOS64 Release" + triggers: "iOS64 Sim Debug (iOS 12)" + triggers: "iOS64 Sim Debug (iOS 13)" + triggers: "iOS64 Sim Debug (iOS 14.0)" + gitiles { + repo: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/main" + } +} +acl_sets { + name: "ci" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} +acl_sets { + name: "cron" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} +acl_sets { + name: "perf" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} diff --git a/infra/config/project.cfg b/infra/config/project.cfg new file mode 100644 index 0000000000..8f83365d86 --- /dev/null +++ b/infra/config/project.cfg @@ -0,0 +1,15 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectCfg message: +# https://luci-config.appspot.com/schemas/projects:project.cfg + +name: "webrtc" +access: "group:all" +lucicfg { + version: "1.30.9" + package_dir: "." + config_dir: "." + entry_point: "config.star" + experiments: "crbug.com/1182002" +} diff --git a/infra/config/realms.cfg b/infra/config/realms.cfg new file mode 100644 index 0000000000..36f32e4d2b --- /dev/null +++ b/infra/config/realms.cfg @@ -0,0 +1,179 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see RealmsCfg message: +# https://luci-config.appspot.com/schemas/projects:realms.cfg + +realms { + name: "@root" + bindings { + role: "role/buildbucket.reader" + principals: "group:all" + } + bindings { + role: "role/configs.reader" + principals: "group:all" + } + bindings { + role: "role/configs.validator" + principals: "user:webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/logdog.reader" + principals: "group:all" + } + bindings { + role: "role/logdog.writer" + principals: "group:luci-logdog-chromium-writers" + } + bindings { + role: "role/scheduler.owner" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/scheduler.reader" + principals: "group:all" + } + bindings { + role: "role/swarming.poolOwner" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/swarming.poolViewer" + principals: "group:all" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "user:chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-admins" + } +} +realms { + name: "ci" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-ci-schedulers" + principals: "group:service-account-chromeperf" + } + bindings { + role: "role/resultdb.invocationCreator" + principals: "group:project-webrtc-ci-task-accounts" + } +} +realms { + name: "cron" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + principals: "user:webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com" + } +} +realms { + name: "perf" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-led-users" + } + bindings { + role: "role/scheduler.triggerer" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + conditions { + restrict { + attribute: "scheduler.job.name" + values: "Perf Android32 (M AOSP Nexus6)" + values: "Perf Android32 (M Nexus5)" + values: "Perf Android64 (M Nexus5X)" + values: "Perf Android64 (O Pixel2)" + values: "Perf Linux Bionic" + values: "Perf Linux Trusty" + values: "Perf Mac 10.11" + values: "Perf Win7" + } + } + } +} +realms { + name: "pools/ci" +} +realms { + name: "pools/ci-tests" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-ci-task-accounts" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "group:project-webrtc-ci-task-accounts" + } +} +realms { + name: "pools/cron" + bindings { + role: "role/swarming.poolUser" + principals: "project:libyuv" + } +} +realms { + name: "pools/perf" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-led-users" + } +} +realms { + name: "pools/try" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-led-users" + } +} +realms { + name: "pools/try-tests" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-try-task-accounts" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "group:project-webrtc-try-task-accounts" + } +} +realms { + name: "try" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-tryjob-access" + principals: "group:service-account-cq" + } + bindings { + role: "role/resultdb.invocationCreator" + principals: "group:project-webrtc-try-task-accounts" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-led-users" + } +} diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 200a37795e..1b7993104e 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -18,6 +18,7 @@ if (is_android) { group("logging") { deps = [ ":rtc_event_audio", + ":rtc_event_begin_end", ":rtc_event_bwe", ":rtc_event_log_impl_encoder", ":rtc_event_pacing", @@ -32,6 +33,35 @@ rtc_source_set("rtc_event_log_api") { deps = [ "../api/rtc_event_log" ] } +rtc_library("rtc_event_field") { + sources = [ + "rtc_event_log/events/fixed_length_encoding_parameters_v3.cc", + "rtc_event_log/events/fixed_length_encoding_parameters_v3.h", + "rtc_event_log/events/rtc_event_definition.h", + "rtc_event_log/events/rtc_event_field_encoding.cc", + "rtc_event_log/events/rtc_event_field_encoding.h", + "rtc_event_log/events/rtc_event_field_encoding_parser.cc", + "rtc_event_log/events/rtc_event_field_encoding_parser.h", + "rtc_event_log/events/rtc_event_field_extraction.cc", + "rtc_event_log/events/rtc_event_field_extraction.h", + ] + + deps = [ + ":rtc_event_number_encodings", + "../api:array_view", + "../api/rtc_event_log", + "../api/units:timestamp", + "../rtc_base:bitstream_reader", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:rtc_base_approved", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + rtc_library("rtc_stream_config") { sources = [ "rtc_event_log/rtc_stream_config.cc", @@ -51,11 +81,14 @@ rtc_library("rtc_event_pacing") { ] deps = [ - "../api:scoped_refptr", + ":rtc_event_field", "../api/rtc_event_log", "../api/units:timestamp", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] } rtc_library("rtc_event_audio") { @@ -71,14 +104,33 @@ rtc_library("rtc_event_audio") { ] deps = [ + ":rtc_event_field", ":rtc_stream_config", - "../api:scoped_refptr", "../api/rtc_event_log", "../api/units:timestamp", "../modules/audio_coding:audio_network_adaptor_config", "../rtc_base:checks", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] +} + +rtc_library("rtc_event_begin_end") { + sources = [ + "rtc_event_log/events/rtc_event_begin_log.cc", + "rtc_event_log/events/rtc_event_begin_log.h", + "rtc_event_log/events/rtc_event_end_log.cc", + "rtc_event_log/events/rtc_event_end_log.h", + ] + deps = [ + ":rtc_event_field", + "../api:array_view", + "../api/rtc_event_log", + "../api/units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("rtc_event_bwe") { @@ -99,14 +151,15 @@ rtc_library("rtc_event_bwe") { ] deps = [ + ":rtc_event_field", "../api:network_state_predictor_api", - "../api:scoped_refptr", "../api/rtc_event_log", "../api/units:data_rate", "../api/units:timestamp", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } @@ -117,6 +170,7 @@ rtc_library("rtc_event_frame_events") { "rtc_event_log/events/rtc_event_frame_decoded.h", ] deps = [ + ":rtc_event_field", "../api/rtc_event_log", "../api/units:timestamp", "../api/video:video_frame", @@ -124,6 +178,7 @@ rtc_library("rtc_event_frame_events") { ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } @@ -139,18 +194,21 @@ rtc_library("rtc_event_generic_packet_events") { "rtc_event_log/events/rtc_event_generic_packet_sent.h", ] deps = [ + ":rtc_event_field", "../api/rtc_event_log", "../api/units:timestamp", "../rtc_base:timeutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } rtc_library("rtc_event_rtp_rtcp") { sources = [ + "rtc_event_log/events/logged_rtp_rtcp.h", "rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc", "rtc_event_log/events/rtc_event_rtcp_packet_incoming.h", "rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc", @@ -162,14 +220,19 @@ rtc_library("rtc_event_rtp_rtcp") { ] deps = [ + ":rtc_event_field", "../api:array_view", - "../api:scoped_refptr", + "../api:rtp_headers", "../api/rtc_event_log", + "../api/units:timestamp", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", "../rtc_base:rtc_base_approved", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] } rtc_library("rtc_event_video") { @@ -181,13 +244,42 @@ rtc_library("rtc_event_video") { ] deps = [ + ":rtc_event_field", ":rtc_stream_config", - "../api:scoped_refptr", "../api/rtc_event_log", "../api/units:timestamp", "../rtc_base:checks", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] +} + +rtc_library("rtc_event_number_encodings") { + sources = [ + "rtc_event_log/encoder/bit_writer.cc", + "rtc_event_log/encoder/bit_writer.h", + "rtc_event_log/encoder/rtc_event_log_encoder_common.cc", + "rtc_event_log/encoder/rtc_event_log_encoder_common.h", + "rtc_event_log/encoder/var_int.cc", + "rtc_event_log/encoder/var_int.h", + ] + + defines = [] + + deps = [ + "../rtc_base:bitstream_reader", + "../rtc_base:checks", + "../rtc_base:ignore_wundef", + "../rtc_base:macromagic", + "../rtc_base:rtc_base_approved", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } # TODO(eladalon): Break down into (1) encoder and (2) decoder; we don't need @@ -198,15 +290,12 @@ rtc_library("rtc_event_log_impl_encoder") { "rtc_event_log/encoder/blob_encoding.h", "rtc_event_log/encoder/delta_encoding.cc", "rtc_event_log/encoder/delta_encoding.h", - "rtc_event_log/encoder/rtc_event_log_encoder_common.cc", - "rtc_event_log/encoder/rtc_event_log_encoder_common.h", - "rtc_event_log/encoder/var_int.cc", - "rtc_event_log/encoder/var_int.h", ] defines = [] deps = [ + ":rtc_event_number_encodings", "../api:rtp_headers", "../api:rtp_parameters", "../api/transport:network_control", @@ -225,7 +314,9 @@ rtc_library("rtc_event_log_impl_encoder") { deps += [ ":ice_log", ":rtc_event_audio", + ":rtc_event_begin_end", ":rtc_event_bwe", + ":rtc_event_field", ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", @@ -245,6 +336,8 @@ rtc_library("rtc_event_log_impl_encoder") { "rtc_event_log/encoder/rtc_event_log_encoder_legacy.h", "rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc", "rtc_event_log/encoder/rtc_event_log_encoder_new_format.h", + "rtc_event_log/encoder/rtc_event_log_encoder_v3.cc", + "rtc_event_log/encoder/rtc_event_log_encoder_v3.h", ] } } @@ -313,7 +406,6 @@ if (rtc_enable_protobuf) { rtc_library("rtc_event_log_parser") { visibility = [ "*" ] sources = [ - "rtc_event_log/logged_events.cc", "rtc_event_log/logged_events.h", "rtc_event_log/rtc_event_log_parser.cc", "rtc_event_log/rtc_event_log_parser.h", @@ -324,12 +416,14 @@ if (rtc_enable_protobuf) { deps = [ ":ice_log", ":rtc_event_audio", + ":rtc_event_begin_end", ":rtc_event_bwe", ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", ":rtc_event_log_impl_encoder", ":rtc_event_log_proto", + ":rtc_event_number_encodings", ":rtc_event_pacing", ":rtc_event_rtp_rtcp", ":rtc_event_video", @@ -369,6 +463,8 @@ if (rtc_enable_protobuf) { "rtc_event_log/encoder/delta_encoding_unittest.cc", "rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc", "rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc", + "rtc_event_log/events/rtc_event_field_encoding_unittest.cc", + "rtc_event_log/events/rtc_event_field_extraction_unittest.cc", "rtc_event_log/rtc_event_log_unittest.cc", "rtc_event_log/rtc_event_log_unittest_helper.cc", "rtc_event_log/rtc_event_log_unittest_helper.h", @@ -378,12 +474,14 @@ if (rtc_enable_protobuf) { ":ice_log", ":rtc_event_audio", ":rtc_event_bwe", + ":rtc_event_field", ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", ":rtc_event_log_impl_encoder", ":rtc_event_log_parser", ":rtc_event_log_proto", + ":rtc_event_number_encodings", ":rtc_event_pacing", ":rtc_event_rtp_rtcp", ":rtc_event_video", @@ -406,6 +504,7 @@ if (rtc_enable_protobuf) { "../system_wrappers", "../test:fileutils", "../test:test_support", + "../test/logging:log_writer", "//testing/gtest", ] absl_deps = [ @@ -455,13 +554,17 @@ rtc_library("ice_log") { ] deps = [ + ":rtc_event_field", "../api:libjingle_logging_api", "../api:libjingle_peerconnection_api", # For api/dtls_transport_interface.h "../api/rtc_event_log", "../api/units:timestamp", "../rtc_base:rtc_base_approved", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] } if (rtc_include_tests) { diff --git a/logging/rtc_event_log/encoder/bit_writer.cc b/logging/rtc_event_log/encoder/bit_writer.cc new file mode 100644 index 0000000000..e8748d3db3 --- /dev/null +++ b/logging/rtc_event_log/encoder/bit_writer.cc @@ -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. + */ + +#include "logging/rtc_event_log/encoder/bit_writer.h" + +namespace webrtc { + +namespace { +size_t BitsToBytes(size_t bits) { + return (bits / 8) + (bits % 8 > 0 ? 1 : 0); +} +} // namespace + +void BitWriter::WriteBits(uint64_t val, size_t bit_count) { + RTC_DCHECK(valid_); + const bool success = bit_writer_.WriteBits(val, bit_count); + RTC_DCHECK(success); + written_bits_ += bit_count; +} + +void BitWriter::WriteBits(absl::string_view input) { + RTC_DCHECK(valid_); + for (char c : input) { + WriteBits(static_cast(c), CHAR_BIT); + } +} + +// Returns everything that was written so far. +// Nothing more may be written after this is called. +std::string BitWriter::GetString() { + RTC_DCHECK(valid_); + valid_ = false; + + buffer_.resize(BitsToBytes(written_bits_)); + written_bits_ = 0; + + std::string result; + std::swap(buffer_, result); + return result; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/encoder/bit_writer.h b/logging/rtc_event_log/encoder/bit_writer.h new file mode 100644 index 0000000000..421e7c4370 --- /dev/null +++ b/logging/rtc_event_log/encoder/bit_writer.h @@ -0,0 +1,61 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/bit_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Wrap BitBufferWriter and extend its functionality by (1) keeping track of +// the number of bits written and (2) owning its buffer. +class BitWriter final { + public: + explicit BitWriter(size_t byte_count) + : buffer_(byte_count, '\0'), + bit_writer_(reinterpret_cast(&buffer_[0]), buffer_.size()), + written_bits_(0), + valid_(true) { + RTC_DCHECK_GT(byte_count, 0); + } + + BitWriter(const BitWriter&) = delete; + BitWriter& operator=(const BitWriter&) = delete; + + void WriteBits(uint64_t val, size_t bit_count); + + void WriteBits(absl::string_view input); + + // Returns everything that was written so far. + // Nothing more may be written after this is called. + std::string GetString(); + + private: + std::string buffer_; + rtc::BitBufferWriter bit_writer_; + // Note: Counting bits instead of bytes wraps around earlier than it has to, + // which means the maximum length is lower than it could be. We don't expect + // to go anywhere near the limit, though, so this is good enough. + size_t written_bits_; + bool valid_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ diff --git a/logging/rtc_event_log/encoder/delta_encoding.cc b/logging/rtc_event_log/encoder/delta_encoding.cc index 437392f929..3a2bee1d31 100644 --- a/logging/rtc_event_log/encoder/delta_encoding.cc +++ b/logging/rtc_event_log/encoder/delta_encoding.cc @@ -16,11 +16,11 @@ #include #include "absl/memory/memory.h" +#include "logging/rtc_event_log/encoder/bit_writer.h" #include "logging/rtc_event_log/encoder/var_int.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -107,58 +107,6 @@ constexpr bool kDefaultSignedDeltas = false; constexpr bool kDefaultValuesOptional = false; constexpr uint64_t kDefaultValueWidthBits = 64; -// Wrap BitBufferWriter and extend its functionality by (1) keeping track of -// the number of bits written and (2) owning its buffer. -class BitWriter final { - public: - explicit BitWriter(size_t byte_count) - : buffer_(byte_count, '\0'), - bit_writer_(reinterpret_cast(&buffer_[0]), buffer_.size()), - written_bits_(0), - valid_(true) { - RTC_DCHECK_GT(byte_count, 0); - } - - void WriteBits(uint64_t val, size_t bit_count) { - RTC_DCHECK(valid_); - const bool success = bit_writer_.WriteBits(val, bit_count); - RTC_DCHECK(success); - written_bits_ += bit_count; - } - - void WriteBits(const std::string& input) { - RTC_DCHECK(valid_); - for (std::string::value_type c : input) { - WriteBits(c, 8 * sizeof(std::string::value_type)); - } - } - - // Returns everything that was written so far. - // Nothing more may be written after this is called. - std::string GetString() { - RTC_DCHECK(valid_); - valid_ = false; - - buffer_.resize(BitsToBytes(written_bits_)); - written_bits_ = 0; - - std::string result; - std::swap(buffer_, result); - return result; - } - - private: - std::string buffer_; - rtc::BitBufferWriter bit_writer_; - // Note: Counting bits instead of bytes wraps around earlier than it has to, - // which means the maximum length is lower than it could be. We don't expect - // to go anywhere near the limit, though, so this is good enough. - size_t written_bits_; - bool valid_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BitWriter); -}; - // Parameters for fixed-size delta-encoding/decoding. // These are tailored for the sequence which will be encoded (e.g. widths). class FixedLengthEncodingParameters final { @@ -238,6 +186,9 @@ class FixedLengthDeltaEncoder final { absl::optional base, const std::vector>& values); + FixedLengthDeltaEncoder(const FixedLengthDeltaEncoder&) = delete; + FixedLengthDeltaEncoder& operator=(const FixedLengthDeltaEncoder&) = delete; + private: // Calculate min/max values of unsigned/signed deltas, given the bit width // of all the values in the series. @@ -300,8 +251,6 @@ class FixedLengthDeltaEncoder final { // ctor has finished running when this is constructed, so that the lower // bound on the buffer size would be guaranteed correct. std::unique_ptr writer_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FixedLengthDeltaEncoder); }; // TODO(eladalon): Reduce the number of passes. @@ -617,6 +566,9 @@ class FixedLengthDeltaDecoder final { absl::optional base, size_t num_of_deltas); + FixedLengthDeltaDecoder(const FixedLengthDeltaDecoder&) = delete; + FixedLengthDeltaDecoder& operator=(const FixedLengthDeltaDecoder&) = delete; + private: // Reads the encoding header in `input` and returns a FixedLengthDeltaDecoder // with the corresponding configuration, that can be used to decode the @@ -670,8 +622,6 @@ class FixedLengthDeltaDecoder final { // The number of values to be known to be decoded. const size_t num_of_deltas_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FixedLengthDeltaDecoder); }; bool FixedLengthDeltaDecoder::IsSuitableDecoderFor(const std::string& input) { diff --git a/logging/rtc_event_log/encoder/delta_encoding_unittest.cc b/logging/rtc_event_log/encoder/delta_encoding_unittest.cc index 3d91362f4d..d0f7fb93db 100644 --- a/logging/rtc_event_log/encoder/delta_encoding_unittest.cc +++ b/logging/rtc_event_log/encoder/delta_encoding_unittest.cc @@ -44,7 +44,7 @@ void MaybeSetSignedness(DeltaSignedness signedness) { SetFixedLengthEncoderDeltaSignednessForTesting(true); return; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } uint64_t RandomWithMaxBitWidth(Random* prng, uint64_t max_width) { diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc index 2bd7507853..add42ad15b 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc @@ -78,9 +78,9 @@ rtclog::DelayBasedBweUpdate::DetectorState ConvertDetectorState( case BandwidthUsage::kBwOverusing: return rtclog::DelayBasedBweUpdate::BWE_OVERUSING; case BandwidthUsage::kLast: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::DelayBasedBweUpdate::BWE_NORMAL; } @@ -94,9 +94,9 @@ rtclog::BweProbeResult::ResultType ConvertProbeResultType( case ProbeFailureReason::kTimeout: return rtclog::BweProbeResult::TIMEOUT; case ProbeFailureReason::kLast: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::BweProbeResult::SUCCESS; } @@ -107,9 +107,9 @@ rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) { case RtcpMode::kReducedSize: return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE; case RtcpMode::kOff: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::VideoReceiveConfig::RTCP_COMPOUND; } @@ -125,9 +125,9 @@ ConvertIceCandidatePairConfigType(IceCandidatePairConfigType type) { case IceCandidatePairConfigType::kSelected: return rtclog::IceCandidatePairConfig::SELECTED; case IceCandidatePairConfigType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairConfig::ADDED; } @@ -145,9 +145,9 @@ rtclog::IceCandidatePairConfig::IceCandidateType ConvertIceCandidateType( case IceCandidateType::kRelay: return rtclog::IceCandidatePairConfig::RELAY; case IceCandidateType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; } @@ -165,9 +165,9 @@ rtclog::IceCandidatePairConfig::Protocol ConvertIceCandidatePairProtocol( case IceCandidatePairProtocol::kTls: return rtclog::IceCandidatePairConfig::TLS; case IceCandidatePairProtocol::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL; } @@ -182,9 +182,9 @@ ConvertIceCandidatePairAddressFamily( case IceCandidatePairAddressFamily::kIpv6: return rtclog::IceCandidatePairConfig::IPV6; case IceCandidatePairAddressFamily::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; } @@ -204,9 +204,9 @@ rtclog::IceCandidatePairConfig::NetworkType ConvertIceCandidateNetworkType( case IceCandidateNetworkType::kCellular: return rtclog::IceCandidatePairConfig::CELLULAR; case IceCandidateNetworkType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; } @@ -222,9 +222,9 @@ ConvertIceCandidatePairEventType(IceCandidatePairEventType type) { case IceCandidatePairEventType::kCheckResponseReceived: return rtclog::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED; case IceCandidatePairEventType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog::IceCandidatePairEvent::CHECK_SENT; } @@ -363,6 +363,12 @@ std::string RtcEventLogEncoderLegacy::Encode(const RtcEvent& event) { static_cast(event); return EncodeVideoSendStreamConfig(rtc_event); } + case RtcEvent::Type::BeginV3Log: + case RtcEvent::Type::EndV3Log: + // These special events are written as part of starting + // and stopping the log, and only as part of version 3 of the format. + RTC_DCHECK_NOTREACHED(); + break; case RtcEvent::Type::RouteChangeEvent: case RtcEvent::Type::RemoteEstimateEvent: case RtcEvent::Type::GenericPacketReceived: @@ -374,7 +380,7 @@ std::string RtcEventLogEncoderLegacy::Encode(const RtcEvent& event) { } int event_type = static_cast(event.GetType()); - RTC_NOTREACHED() << "Unknown event type (" << event_type << ")"; + RTC_DCHECK_NOTREACHED() << "Unknown event type (" << event_type << ")"; return ""; } @@ -674,7 +680,7 @@ std::string RtcEventLogEncoderLegacy::EncodeVideoSendStreamConfig( encoder->set_payload_type(codec.payload_type); if (event.config().codecs.size() > 1) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "LogVideoSendStreamConfig currently only supports one " "codec. Logging codec :" << codec.payload_name; diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc index 236aea7754..d88f124f9e 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc @@ -86,9 +86,9 @@ rtclog2::DelayBasedBweUpdates::DetectorState ConvertToProtoFormat( case BandwidthUsage::kBwOverusing: return rtclog2::DelayBasedBweUpdates::BWE_OVERUSING; case BandwidthUsage::kLast: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE; } @@ -108,7 +108,7 @@ rtclog2::FrameDecodedEvents::Codec ConvertToProtoFormat(VideoCodecType codec) { // This codec type is afaik not used. return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; } @@ -122,9 +122,9 @@ rtclog2::BweProbeResultFailure::FailureReason ConvertToProtoFormat( case ProbeFailureReason::kTimeout: return rtclog2::BweProbeResultFailure::TIMEOUT; case ProbeFailureReason::kLast: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::BweProbeResultFailure::UNKNOWN; } @@ -167,9 +167,9 @@ rtclog2::DtlsTransportStateEvent::DtlsTransportState ConvertToProtoFormat( case webrtc::DtlsTransportState::kFailed: return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED; case webrtc::DtlsTransportState::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE; } @@ -185,9 +185,9 @@ ConvertToProtoFormat(IceCandidatePairConfigType type) { case IceCandidatePairConfigType::kSelected: return rtclog2::IceCandidatePairConfig::SELECTED; case IceCandidatePairConfigType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE; } @@ -205,9 +205,9 @@ rtclog2::IceCandidatePairConfig::IceCandidateType ConvertToProtoFormat( case IceCandidateType::kRelay: return rtclog2::IceCandidatePairConfig::RELAY; case IceCandidateType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; } @@ -225,9 +225,9 @@ rtclog2::IceCandidatePairConfig::Protocol ConvertToProtoFormat( case IceCandidatePairProtocol::kTls: return rtclog2::IceCandidatePairConfig::TLS; case IceCandidatePairProtocol::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL; } @@ -241,9 +241,9 @@ rtclog2::IceCandidatePairConfig::AddressFamily ConvertToProtoFormat( case IceCandidatePairAddressFamily::kIpv6: return rtclog2::IceCandidatePairConfig::IPV6; case IceCandidatePairAddressFamily::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; } @@ -263,9 +263,9 @@ rtclog2::IceCandidatePairConfig::NetworkType ConvertToProtoFormat( case IceCandidateNetworkType::kCellular: return rtclog2::IceCandidatePairConfig::CELLULAR; case IceCandidateNetworkType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; } @@ -281,9 +281,9 @@ rtclog2::IceCandidatePairEvent::IceCandidatePairEventType ConvertToProtoFormat( case IceCandidatePairEventType::kCheckResponseReceived: return rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED; case IceCandidatePairEventType::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE; } @@ -880,6 +880,12 @@ std::string RtcEventLogEncoderNewFormat::EncodeBatch( frames_decoded[rtc_event->ssrc()].emplace_back(rtc_event); break; } + case RtcEvent::Type::BeginV3Log: + case RtcEvent::Type::EndV3Log: + // These special events are written as part of starting + // and stopping the log, and only as part of version 3 of the format. + RTC_DCHECK_NOTREACHED(); + break; } } diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc index 8eddc8fcd5..be9352ab94 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -16,6 +16,7 @@ #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h" #include "logging/rtc_event_log/events/rtc_event_alr_state.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" @@ -61,6 +62,9 @@ class RtcEventLogEncoderTest case RtcEventLog::EncodingType::NewFormat: encoder_ = std::make_unique(); break; + case RtcEventLog::EncodingType::ProtoFree: + encoder_ = std::make_unique(); + break; } encoded_ = encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis()); @@ -1285,6 +1289,9 @@ class RtcEventLogEncoderSimpleTest case RtcEventLog::EncodingType::NewFormat: encoder_ = std::make_unique(); break; + case RtcEventLog::EncodingType::ProtoFree: + encoder_ = std::make_unique(); + break; } encoded_ = encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis()); diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc new file mode 100644 index 0000000000..131aae1de8 --- /dev/null +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc @@ -0,0 +1,164 @@ +/* + * 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h" + +#include +#include + +#include "absl/types/optional.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_alr_state.h" +#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" +#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" +#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_begin_log.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_end_log.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" +#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +std::string RtcEventLogEncoderV3::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + std::unique_ptr begin_log = + std::make_unique(Timestamp::Micros(timestamp_us), + Timestamp::Micros(utc_time_us)); + std::vector batch; + batch.push_back(begin_log.get()); + + std::string encoded_event = RtcEventBeginLog::Encode(batch); + + return encoded_event; +} + +std::string RtcEventLogEncoderV3::EncodeLogEnd(int64_t timestamp_us) { + std::unique_ptr end_log = + std::make_unique(Timestamp::Micros(timestamp_us)); + std::vector batch; + batch.push_back(end_log.get()); + + std::string encoded_event = RtcEventEndLog::Encode(batch); + + return encoded_event; +} + +RtcEventLogEncoderV3::RtcEventLogEncoderV3() { + encoders_[RtcEvent::Type::AlrStateEvent] = RtcEventAlrState::Encode; + encoders_[RtcEvent::Type::AudioNetworkAdaptation] = + RtcEventAudioNetworkAdaptation::Encode; + encoders_[RtcEvent::Type::AudioPlayout] = RtcEventAudioPlayout::Encode; + encoders_[RtcEvent::Type::AudioReceiveStreamConfig] = + RtcEventAudioReceiveStreamConfig::Encode; + encoders_[RtcEvent::Type::AudioSendStreamConfig] = + RtcEventAudioSendStreamConfig::Encode; + encoders_[RtcEvent::Type::BweUpdateDelayBased] = + RtcEventBweUpdateDelayBased::Encode; + encoders_[RtcEvent::Type::BweUpdateLossBased] = + RtcEventBweUpdateLossBased::Encode; + encoders_[RtcEvent::Type::DtlsTransportState] = + RtcEventDtlsTransportState::Encode; + encoders_[RtcEvent::Type::DtlsWritableState] = + RtcEventDtlsWritableState::Encode; + encoders_[RtcEvent::Type::FrameDecoded] = RtcEventFrameDecoded::Encode; + encoders_[RtcEvent::Type::GenericAckReceived] = + RtcEventGenericAckReceived::Encode; + encoders_[RtcEvent::Type::GenericPacketReceived] = + RtcEventGenericPacketReceived::Encode; + encoders_[RtcEvent::Type::GenericPacketSent] = + RtcEventGenericPacketSent::Encode; + encoders_[RtcEvent::Type::IceCandidatePairConfig] = + RtcEventIceCandidatePairConfig::Encode; + encoders_[RtcEvent::Type::IceCandidatePairEvent] = + RtcEventIceCandidatePair::Encode; + encoders_[RtcEvent::Type::ProbeClusterCreated] = + RtcEventProbeClusterCreated::Encode; + encoders_[RtcEvent::Type::ProbeResultFailure] = + RtcEventProbeResultFailure::Encode; + encoders_[RtcEvent::Type::ProbeResultSuccess] = + RtcEventProbeResultSuccess::Encode; + encoders_[RtcEvent::Type::RemoteEstimateEvent] = + RtcEventRemoteEstimate::Encode; + encoders_[RtcEvent::Type::RouteChangeEvent] = RtcEventRouteChange::Encode; + encoders_[RtcEvent::Type::RtcpPacketIncoming] = + RtcEventRtcpPacketIncoming::Encode; + encoders_[RtcEvent::Type::RtcpPacketOutgoing] = + RtcEventRtcpPacketOutgoing::Encode; + encoders_[RtcEvent::Type::RtpPacketIncoming] = + RtcEventRtpPacketIncoming::Encode; + encoders_[RtcEvent::Type::RtpPacketOutgoing] = + RtcEventRtpPacketOutgoing::Encode; + encoders_[RtcEvent::Type::VideoReceiveStreamConfig] = + RtcEventVideoReceiveStreamConfig::Encode; + encoders_[RtcEvent::Type::VideoSendStreamConfig] = + RtcEventVideoSendStreamConfig::Encode; +} + +std::string RtcEventLogEncoderV3::EncodeBatch( + std::deque>::const_iterator begin, + std::deque>::const_iterator end) { + struct EventGroupKey { + // Events are grouped by event type. For compression efficiency, + // events can optionally have a secondary key, in most cases the + // SSRC. + RtcEvent::Type type; + uint32_t secondary_group_key; + + bool operator<(EventGroupKey other) const { + return type < other.type || + (type == other.type && + secondary_group_key < other.secondary_group_key); + } + }; + + std::map> event_groups; + + for (auto it = begin; it != end; ++it) { + event_groups[{(*it)->GetType(), (*it)->GetGroupKey()}].push_back(it->get()); + } + + std::string encoded_output; + for (auto& kv : event_groups) { + auto it = encoders_.find(kv.first.type); + RTC_DCHECK(it != encoders_.end()); + if (it != encoders_.end()) { + auto& encoder = it->second; + // TODO(terelius): Use some "string builder" or preallocate? + encoded_output += encoder(kv.second); + } + } + + return encoded_output; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h new file mode 100644 index 0000000000..cb796ec562 --- /dev/null +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.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 LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +class RtcEventLogEncoderV3 final : public RtcEventLogEncoder { + public: + RtcEventLogEncoderV3(); + ~RtcEventLogEncoderV3() override = default; + + std::string EncodeBatch( + std::deque>::const_iterator begin, + std::deque>::const_iterator end) override; + + std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) override; + std::string EncodeLogEnd(int64_t timestamp_us) override; + + private: + std::map)>> + encoders_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ diff --git a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc new file mode 100644 index 0000000000..0c93e6226d --- /dev/null +++ b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc @@ -0,0 +1,137 @@ +/* + * 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 "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" + +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +using webrtc_event_logging::MaxUnsignedValueOfBitWidth; +using webrtc_event_logging::SignedBitWidth; +using webrtc_event_logging::UnsignedBitWidth; +using webrtc_event_logging::UnsignedDelta; + +namespace webrtc { + +FixedLengthEncodingParametersV3 +FixedLengthEncodingParametersV3::CalculateParameters( + uint64_t base, + const rtc::ArrayView values, + uint64_t value_bit_width, + bool values_optional) { + // As a special case, if all of the elements are identical to the base + // we just encode the base value with a special delta header. + if (std::all_of(values.cbegin(), values.cend(), + [base](uint64_t val) { return val == base; })) { + // Delta header with signed=true and delta_bitwidth=64 + return FixedLengthEncodingParametersV3(/*delta_bit_width=*/64, + /*signed_deltas=*/true, + values_optional, value_bit_width); + } + + const uint64_t bit_mask = MaxUnsignedValueOfBitWidth(value_bit_width); + + // Calculate the bitwidth required to encode all deltas when using a + // unsigned or signed represenation, respectively. For the unsigned + // representation, we just track the largest delta. For the signed + // representation, we have two possibilities for each delta; either + // going "forward" (i.e. current - previous) or "backwards" + // (i.e. previous - current) where both values are calculated with + // wrap around. We then track the largest positive and negative + // magnitude across the batch, assuming that we choose the smaller + // delta for each element. + uint64_t max_unsigned_delta = 0; + uint64_t max_positive_signed_delta = 0; + uint64_t min_negative_signed_delta = 0; + uint64_t prev = base; + for (uint64_t current : values) { + uint64_t positive_delta = UnsignedDelta(prev, current, bit_mask); + uint64_t negative_delta = UnsignedDelta(current, prev, bit_mask); + + max_unsigned_delta = std::max(max_unsigned_delta, positive_delta); + + if (positive_delta < negative_delta) { + max_positive_signed_delta = + std::max(max_positive_signed_delta, positive_delta); + } else { + min_negative_signed_delta = + std::max(min_negative_signed_delta, negative_delta); + } + + prev = current; + } + + // We now know the largest unsigned delta and the largest magnitudes of + // positive and negative signed deltas. Get the bitwidths required for + // each of the two encodings. + const uint64_t unsigned_delta_bit_width = + UnsignedBitWidth(max_unsigned_delta); + const uint64_t signed_delta_bit_width = + SignedBitWidth(max_positive_signed_delta, min_negative_signed_delta); + + // Note: Preference for unsigned if the two have the same width (efficiency). + bool use_signed_deltas = signed_delta_bit_width < unsigned_delta_bit_width; + uint64_t delta_bit_width = + use_signed_deltas ? signed_delta_bit_width : unsigned_delta_bit_width; + + // use_signed_deltas && delta_bit_width==64 is reserved for "all values + // equal". + RTC_DCHECK(!use_signed_deltas || delta_bit_width < 64); + + RTC_DCHECK(ValidParameters(delta_bit_width, use_signed_deltas, + values_optional, value_bit_width)); + return FixedLengthEncodingParametersV3(delta_bit_width, use_signed_deltas, + values_optional, value_bit_width); +} + +uint64_t FixedLengthEncodingParametersV3::DeltaHeaderAsInt() const { + uint64_t header = delta_bit_width_ - 1; + RTC_CHECK_LT(header, 1u << 6); + if (signed_deltas_) { + header += 1u << 6; + } + RTC_CHECK_LT(header, 1u << 7); + if (values_optional_) { + header += 1u << 7; + } + return header; +} + +absl::optional +FixedLengthEncodingParametersV3::ParseDeltaHeader(uint64_t header, + uint64_t value_bit_width) { + uint64_t delta_bit_width = (header & ((1u << 6) - 1)) + 1; + bool signed_deltas = header & (1u << 6); + bool values_optional = header & (1u << 7); + + if (header >= (1u << 8)) { + RTC_LOG(LS_ERROR) << "Failed to parse delta header; unread bits remaining."; + return absl::nullopt; + } + + if (!ValidParameters(delta_bit_width, signed_deltas, values_optional, + value_bit_width)) { + RTC_LOG(LS_ERROR) << "Failed to parse delta header. Invalid combination of " + "values: delta_bit_width=" + << delta_bit_width << " signed_deltas=" << signed_deltas + << " values_optional=" << values_optional + << " value_bit_width=" << value_bit_width; + return absl::nullopt; + } + + return FixedLengthEncodingParametersV3(delta_bit_width, signed_deltas, + values_optional, value_bit_width); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h new file mode 100644 index 0000000000..666fae1c63 --- /dev/null +++ b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h @@ -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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +// Parameters for fixed-size delta-encoding/decoding. +// These are tailored for the sequence which will be encoded (e.g. widths). +class FixedLengthEncodingParametersV3 final { + public: + static bool ValidParameters(uint64_t delta_bit_width, + bool signed_deltas, + bool values_optional, + uint64_t value_bit_width) { + return (1 <= delta_bit_width && delta_bit_width <= 64 && + 1 <= value_bit_width && value_bit_width <= 64 && + (delta_bit_width <= value_bit_width || + (signed_deltas && delta_bit_width == 64))); + } + + static FixedLengthEncodingParametersV3 CalculateParameters( + uint64_t base, + rtc::ArrayView values, + uint64_t value_bit_width, + bool values_optional); + static absl::optional ParseDeltaHeader( + uint64_t header, + uint64_t value_bit_width); + + uint64_t DeltaHeaderAsInt() const; + + // Number of bits necessary to hold the widest(*) of the deltas between the + // values in the sequence. + // (*) - Widest might not be the largest, if signed deltas are used. + uint64_t delta_bit_width() const { return delta_bit_width_; } + + // Whether deltas are signed. + bool signed_deltas() const { return signed_deltas_; } + + // Whether the values of the sequence are optional. That is, it may be + // that some of them do not have a value (not even a sentinel value indicating + // invalidity). + bool values_optional() const { return values_optional_; } + + // Whether all values are equal. 64-bit signed deltas are assumed to not + // occur, since those could equally well be represented using 64 bit unsigned + // deltas. + bool values_equal() const { + return delta_bit_width() == 64 && signed_deltas(); + } + + // Number of bits necessary to hold the largest value in the sequence. + uint64_t value_bit_width() const { return value_bit_width_; } + + // Masks where only the bits relevant to the deltas/values are turned on. + uint64_t delta_mask() const { return delta_mask_; } + uint64_t value_mask() const { return value_mask_; } + + private: + FixedLengthEncodingParametersV3(uint64_t delta_bit_width, + bool signed_deltas, + bool values_optional, + uint64_t value_bit_width) + : delta_bit_width_(delta_bit_width), + signed_deltas_(signed_deltas), + values_optional_(values_optional), + value_bit_width_(value_bit_width), + delta_mask_( + webrtc_event_logging::MaxUnsignedValueOfBitWidth(delta_bit_width_)), + value_mask_(webrtc_event_logging::MaxUnsignedValueOfBitWidth( + value_bit_width_)) {} + + uint64_t delta_bit_width_; + bool signed_deltas_; + bool values_optional_; + uint64_t value_bit_width_; + + uint64_t delta_mask_; + uint64_t value_mask_; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ diff --git a/logging/rtc_event_log/events/logged_rtp_rtcp.h b/logging/rtc_event_log/events/logged_rtp_rtcp.h new file mode 100644 index 0000000000..053a16371d --- /dev/null +++ b/logging/rtc_event_log/events/logged_rtp_rtcp.h @@ -0,0 +1,259 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ + +#include +#include + +#include "api/rtp_headers.h" +#include "api/units/timestamp.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/fir.h" +#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h" +#include "modules/rtp_rtcp/source/rtcp_packet/nack.h" +#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" + +namespace webrtc { + +struct LoggedRtpPacket { + LoggedRtpPacket(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : timestamp(timestamp), + header(header), + header_length(header_length), + total_length(total_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp; + // TODO(terelius): This allocates space for 15 CSRCs even if none are used. + RTPHeader header; + size_t header_length; + size_t total_length; +}; + +struct LoggedRtpPacketIncoming { + LoggedRtpPacketIncoming(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp, header, header_length, total_length) {} + int64_t log_time_us() const { return rtp.timestamp.us(); } + int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } + + LoggedRtpPacket rtp; +}; + +struct LoggedRtpPacketOutgoing { + LoggedRtpPacketOutgoing(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp, header, header_length, total_length) {} + int64_t log_time_us() const { return rtp.timestamp.us(); } + int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } + + LoggedRtpPacket rtp; +}; + +struct LoggedRtcpPacket { + LoggedRtcpPacket(Timestamp timestamp, const std::vector& packet) + : timestamp(timestamp), raw_data(packet) {} + LoggedRtcpPacket(Timestamp timestamp, const std::string& packet) + : timestamp(timestamp), raw_data(packet.size()) { + memcpy(raw_data.data(), packet.data(), packet.size()); + } + + LoggedRtcpPacket(const LoggedRtcpPacket& rhs) = default; + + ~LoggedRtcpPacket() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp; + std::vector raw_data; +}; + +struct LoggedRtcpPacketIncoming { + LoggedRtcpPacketIncoming(Timestamp timestamp, + const std::vector& packet) + : rtcp(timestamp, packet) {} + LoggedRtcpPacketIncoming(Timestamp timestamp, const std::string& packet) + : rtcp(timestamp, packet) {} + + int64_t log_time_us() const { return rtcp.timestamp.us(); } + int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } + + LoggedRtcpPacket rtcp; +}; + +struct LoggedRtcpPacketOutgoing { + LoggedRtcpPacketOutgoing(Timestamp timestamp, + const std::vector& packet) + : rtcp(timestamp, packet) {} + LoggedRtcpPacketOutgoing(Timestamp timestamp, const std::string& packet) + : rtcp(timestamp, packet) {} + + int64_t log_time_us() const { return rtcp.timestamp.us(); } + int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } + + LoggedRtcpPacket rtcp; +}; + +struct LoggedRtcpPacketReceiverReport { + LoggedRtcpPacketReceiverReport() = default; + LoggedRtcpPacketReceiverReport(Timestamp timestamp, + const rtcp::ReceiverReport& rr) + : timestamp(timestamp), rr(rr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::ReceiverReport rr; +}; + +struct LoggedRtcpPacketSenderReport { + LoggedRtcpPacketSenderReport() = default; + LoggedRtcpPacketSenderReport(Timestamp timestamp, + const rtcp::SenderReport& sr) + : timestamp(timestamp), sr(sr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::SenderReport sr; +}; + +struct LoggedRtcpPacketExtendedReports { + LoggedRtcpPacketExtendedReports() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::ExtendedReports xr; +}; + +struct LoggedRtcpPacketRemb { + LoggedRtcpPacketRemb() = default; + LoggedRtcpPacketRemb(Timestamp timestamp, const rtcp::Remb& remb) + : timestamp(timestamp), remb(remb) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Remb remb; +}; + +struct LoggedRtcpPacketNack { + LoggedRtcpPacketNack() = default; + LoggedRtcpPacketNack(Timestamp timestamp, const rtcp::Nack& nack) + : timestamp(timestamp), nack(nack) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Nack nack; +}; + +struct LoggedRtcpPacketFir { + LoggedRtcpPacketFir() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Fir fir; +}; + +struct LoggedRtcpPacketPli { + LoggedRtcpPacketPli() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Pli pli; +}; + +struct LoggedRtcpPacketTransportFeedback { + LoggedRtcpPacketTransportFeedback() + : transport_feedback(/*include_timestamps=*/true, /*include_lost*/ true) { + } + LoggedRtcpPacketTransportFeedback( + Timestamp timestamp, + const rtcp::TransportFeedback& transport_feedback) + : timestamp(timestamp), transport_feedback(transport_feedback) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::TransportFeedback transport_feedback; +}; + +struct LoggedRtcpPacketLossNotification { + LoggedRtcpPacketLossNotification() = default; + LoggedRtcpPacketLossNotification( + Timestamp timestamp, + const rtcp::LossNotification& loss_notification) + : timestamp(timestamp), loss_notification(loss_notification) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::LossNotification loss_notification; +}; + +struct LoggedRtcpPacketBye { + LoggedRtcpPacketBye() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Bye bye; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.cc b/logging/rtc_event_log/events/rtc_event_alr_state.cc index 3c307b9ca0..25941eb16b 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.cc +++ b/logging/rtc_event_log/events/rtc_event_alr_state.cc @@ -13,6 +13,9 @@ #include "absl/memory/memory.h" namespace webrtc { +constexpr RtcEvent::Type RtcEventAlrState::kType; +constexpr RtcEventDefinition + RtcEventAlrState::definition_; RtcEventAlrState::RtcEventAlrState(bool in_alr) : in_alr_(in_alr) {} @@ -25,4 +28,11 @@ std::unique_ptr RtcEventAlrState::Copy() const { return absl::WrapUnique(new RtcEventAlrState(*this)); } +RtcEventLogParseStatus RtcEventAlrState::Parse( + absl::string_view s, + bool batched, + std::vector& output) { + return RtcEventAlrState::definition_.ParseBatch(s, batched, output); +} + } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.h b/logging/rtc_event_log/events/rtc_event_alr_state.h index 74d66015ef..9f595ecd90 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.h +++ b/logging/rtc_event_log/events/rtc_event_alr_state.h @@ -12,12 +12,32 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ALR_STATE_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" namespace webrtc { +struct LoggedAlrStateEvent { + LoggedAlrStateEvent() = default; + LoggedAlrStateEvent(Timestamp timestamp, bool in_alr) + : timestamp(timestamp), in_alr(in_alr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool in_alr; +}; + class RtcEventAlrState final : public RtcEvent { public: static constexpr Type kType = Type::AlrStateEvent; @@ -32,22 +52,26 @@ class RtcEventAlrState final : public RtcEvent { bool in_alr() const { return in_alr_; } + static std::string Encode(rtc::ArrayView batch) { + return RtcEventAlrState::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse(absl::string_view s, + bool batched, + std::vector& output); + private: RtcEventAlrState(const RtcEventAlrState& other); const bool in_alr_; -}; -struct LoggedAlrStateEvent { - LoggedAlrStateEvent() = default; - LoggedAlrStateEvent(Timestamp timestamp, bool in_alr) - : timestamp(timestamp), in_alr(in_alr) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - bool in_alr; + static constexpr RtcEventDefinition + definition_{{"AlrState", RtcEventAlrState::kType}, + {&RtcEventAlrState::in_alr_, + &LoggedAlrStateEvent::in_alr, + {"in_alr", /*id=*/1, FieldType::kFixed8, /*width=*/1}}}; }; } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h index aeeb28e218..d4cae3abfa 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h +++ b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h @@ -12,13 +12,31 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_NETWORK_ADAPTATION_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" namespace webrtc { +struct LoggedAudioNetworkAdaptationEvent { + LoggedAudioNetworkAdaptationEvent() = default; + LoggedAudioNetworkAdaptationEvent(Timestamp timestamp, + const AudioEncoderRuntimeConfig& config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + AudioEncoderRuntimeConfig config; +}; + struct AudioEncoderRuntimeConfig; class RtcEventAudioNetworkAdaptation final : public RtcEvent { @@ -36,25 +54,25 @@ class RtcEventAudioNetworkAdaptation final : public RtcEvent { const AudioEncoderRuntimeConfig& config() const { return *config_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventAudioNetworkAdaptation(const RtcEventAudioNetworkAdaptation& other); const std::unique_ptr config_; }; -struct LoggedAudioNetworkAdaptationEvent { - LoggedAudioNetworkAdaptationEvent() = default; - LoggedAudioNetworkAdaptationEvent(Timestamp timestamp, - const AudioEncoderRuntimeConfig& config) - : timestamp(timestamp), config(config) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - AudioEncoderRuntimeConfig config; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_NETWORK_ADAPTATION_H_ diff --git a/logging/rtc_event_log/events/rtc_event_audio_playout.cc b/logging/rtc_event_log/events/rtc_event_audio_playout.cc index dae61c4df3..21a3f9266c 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_playout.cc +++ b/logging/rtc_event_log/events/rtc_event_audio_playout.cc @@ -14,6 +14,11 @@ namespace webrtc { +constexpr RtcEventDefinition + RtcEventAudioPlayout::definition_; + RtcEventAudioPlayout::RtcEventAudioPlayout(uint32_t ssrc) : ssrc_(ssrc) {} RtcEventAudioPlayout::RtcEventAudioPlayout(const RtcEventAudioPlayout& other) diff --git a/logging/rtc_event_log/events/rtc_event_audio_playout.h b/logging/rtc_event_log/events/rtc_event_audio_playout.h index 00d07a65bf..196c3ca247 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_playout.h +++ b/logging/rtc_event_log/events/rtc_event_audio_playout.h @@ -13,13 +13,31 @@ #include +#include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" namespace webrtc { +struct LoggedAudioPlayoutEvent { + LoggedAudioPlayoutEvent() = default; + LoggedAudioPlayoutEvent(Timestamp timestamp, uint32_t ssrc) + : timestamp(timestamp), ssrc(ssrc) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + uint32_t ssrc; +}; + class RtcEventAudioPlayout final : public RtcEvent { public: static constexpr Type kType = Type::AudioPlayout; @@ -34,22 +52,35 @@ class RtcEventAudioPlayout final : public RtcEvent { uint32_t ssrc() const { return ssrc_; } + static std::string Encode(rtc::ArrayView batch) { + return RtcEventAudioPlayout::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map>& output) { + std::vector temp_output; + auto status = RtcEventAudioPlayout::definition_.ParseBatch( + encoded_bytes, batched, temp_output); + for (const LoggedAudioPlayoutEvent& event : temp_output) { + output[event.ssrc].push_back(event); + } + return status; + } + private: RtcEventAudioPlayout(const RtcEventAudioPlayout& other); const uint32_t ssrc_; -}; -struct LoggedAudioPlayoutEvent { - LoggedAudioPlayoutEvent() = default; - LoggedAudioPlayoutEvent(Timestamp timestamp, uint32_t ssrc) - : timestamp(timestamp), ssrc(ssrc) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - uint32_t ssrc; + static constexpr RtcEventDefinition + definition_{{"AudioPlayout", RtcEventAudioPlayout::kType}, + {&RtcEventAudioPlayout::ssrc_, + &LoggedAudioPlayoutEvent::ssrc, + {"ssrc", /*id=*/1, FieldType::kFixed32, /*width=*/32}}}; }; } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h index ccf76025e6..9863e235af 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h @@ -12,13 +12,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_RECEIVE_STREAM_CONFIG_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "logging/rtc_event_log/rtc_stream_config.h" namespace webrtc { +struct LoggedAudioRecvConfig { + LoggedAudioRecvConfig() = default; + LoggedAudioRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + class RtcEventAudioReceiveStreamConfig final : public RtcEvent { public: static constexpr Type kType = Type::AudioReceiveStreamConfig; @@ -34,6 +51,19 @@ class RtcEventAudioReceiveStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventAudioReceiveStreamConfig( const RtcEventAudioReceiveStreamConfig& other); @@ -41,18 +71,6 @@ class RtcEventAudioReceiveStreamConfig final : public RtcEvent { const std::unique_ptr config_; }; -struct LoggedAudioRecvConfig { - LoggedAudioRecvConfig() = default; - LoggedAudioRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) - : timestamp(timestamp), config(config) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtclog::StreamConfig config; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_RECEIVE_STREAM_CONFIG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h index 4e93871ae8..550723bcf0 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h @@ -12,12 +12,29 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_SEND_STREAM_CONFIG_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "logging/rtc_event_log/rtc_stream_config.h" namespace webrtc { +struct LoggedAudioSendConfig { + LoggedAudioSendConfig() = default; + LoggedAudioSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + class RtcEventAudioSendStreamConfig final : public RtcEvent { public: static constexpr Type kType = Type::AudioSendStreamConfig; @@ -33,23 +50,25 @@ class RtcEventAudioSendStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventAudioSendStreamConfig(const RtcEventAudioSendStreamConfig& other); const std::unique_ptr config_; }; -struct LoggedAudioSendConfig { - LoggedAudioSendConfig() = default; - LoggedAudioSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) - : timestamp(timestamp), config(config) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtclog::StreamConfig config; -}; } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_SEND_STREAM_CONFIG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_begin_log.cc b/logging/rtc_event_log/events/rtc_event_begin_log.cc new file mode 100644 index 0000000000..49b9effa9e --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_begin_log.cc @@ -0,0 +1,73 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_begin_log.h" + +#include "absl/strings/string_view.h" + +namespace webrtc { +constexpr RtcEvent::Type RtcEventBeginLog::kType; +constexpr EventParameters RtcEventBeginLog::event_params_; +constexpr FieldParameters RtcEventBeginLog::utc_start_time_params_; + +RtcEventBeginLog::RtcEventBeginLog(Timestamp timestamp, + Timestamp utc_start_time) + : RtcEvent(timestamp.us()), utc_start_time_ms_(utc_start_time.ms()) {} + +RtcEventBeginLog::RtcEventBeginLog(const RtcEventBeginLog& other) + : RtcEvent(other.timestamp_us_) {} + +RtcEventBeginLog::~RtcEventBeginLog() = default; + +std::string RtcEventBeginLog::Encode(rtc::ArrayView batch) { + EventEncoder encoder(event_params_, batch); + + encoder.EncodeField( + utc_start_time_params_, + ExtractRtcEventMember(batch, &RtcEventBeginLog::utc_start_time_ms_)); + + return encoder.AsString(); +} + +RtcEventLogParseStatus RtcEventBeginLog::Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + EventParser parser; + auto status = parser.Initialize(encoded_bytes, batched); + if (!status.ok()) + return status; + + rtc::ArrayView output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp( + result.value(), &LoggedStartEvent::timestamp, output_batch); + if (!status.ok()) + return status; + + result = parser.ParseNumericField(utc_start_time_params_); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp( + result.value(), &LoggedStartEvent::utc_start_time, output_batch); + if (!status.ok()) + return status; + + return RtcEventLogParseStatus::Success(); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_begin_log.h b/logging/rtc_event_log/events/rtc_event_begin_log.h new file mode 100644 index 0000000000..f3b74c117e --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_begin_log.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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +struct LoggedStartEvent { + LoggedStartEvent() = default; + + explicit LoggedStartEvent(Timestamp timestamp) + : LoggedStartEvent(timestamp, timestamp) {} + + LoggedStartEvent(Timestamp timestamp, Timestamp utc_start_time) + : timestamp(timestamp), utc_start_time(utc_start_time) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp utc_time() const { return utc_start_time; } + + Timestamp timestamp = Timestamp::PlusInfinity(); + Timestamp utc_start_time = Timestamp::PlusInfinity(); +}; + +class RtcEventBeginLog final : public RtcEvent { + public: + static constexpr Type kType = Type::BeginV3Log; + + RtcEventBeginLog(Timestamp timestamp, Timestamp utc_start_time); + ~RtcEventBeginLog() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + static std::string Encode(rtc::ArrayView batch); + + static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, + bool batched, + std::vector& output); + + private: + RtcEventBeginLog(const RtcEventBeginLog& other); + + int64_t utc_start_time_ms_; + + static constexpr EventParameters event_params_{"BeginLog", + RtcEventBeginLog::kType}; + static constexpr FieldParameters utc_start_time_params_{ + "utc_start_time_ms", /*id=*/1, FieldType::kVarInt, /*width=*/64}; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc index f3f12192c4..0e98b2ff11 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc @@ -15,6 +15,12 @@ namespace webrtc { +constexpr RtcEventDefinition + RtcEventBweUpdateDelayBased::definition_; + RtcEventBweUpdateDelayBased::RtcEventBweUpdateDelayBased( int32_t bitrate_bps, BandwidthUsage detector_state) diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h index 522f98fd8d..796f119388 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h @@ -13,14 +13,76 @@ #include +#include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" namespace webrtc { +// Separate the event log encoding from the enum values. +// As long as the enum values are the same as the encodings, +// the two conversion functions can be compiled to (roughly) +// a range check each. +template <> +class RtcEventLogEnum { + static constexpr uint64_t kBwNormal = 0; + static constexpr uint64_t kBwUnderusing = 1; + static constexpr uint64_t kBwOverusing = 2; + + public: + static uint64_t Encode(BandwidthUsage x) { + switch (x) { + case BandwidthUsage::kBwNormal: + return kBwNormal; + case BandwidthUsage::kBwUnderusing: + return kBwUnderusing; + case BandwidthUsage::kBwOverusing: + return kBwOverusing; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return std::numeric_limits::max(); + } + static RtcEventLogParseStatusOr Decode(uint64_t x) { + switch (x) { + case kBwNormal: + return BandwidthUsage::kBwNormal; + case kBwUnderusing: + return BandwidthUsage::kBwUnderusing; + case kBwOverusing: + return BandwidthUsage::kBwOverusing; + } + return RtcEventLogParseStatus::Error("Failed to decode BandwidthUsage enum", + __FILE__, __LINE__); + } +}; + +struct LoggedBweDelayBasedUpdate { + LoggedBweDelayBasedUpdate() = default; + LoggedBweDelayBasedUpdate(Timestamp timestamp, + int32_t bitrate_bps, + BandwidthUsage detector_state) + : timestamp(timestamp), + bitrate_bps(bitrate_bps), + detector_state(detector_state) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t bitrate_bps; + BandwidthUsage detector_state; +}; + class RtcEventBweUpdateDelayBased final : public RtcEvent { public: static constexpr Type kType = Type::BweUpdateDelayBased; @@ -37,28 +99,36 @@ class RtcEventBweUpdateDelayBased final : public RtcEvent { int32_t bitrate_bps() const { return bitrate_bps_; } BandwidthUsage detector_state() const { return detector_state_; } + static std::string Encode(rtc::ArrayView batch) { + return RtcEventBweUpdateDelayBased::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + return RtcEventBweUpdateDelayBased::definition_.ParseBatch(encoded_bytes, + batched, output); + } + private: RtcEventBweUpdateDelayBased(const RtcEventBweUpdateDelayBased& other); const int32_t bitrate_bps_; const BandwidthUsage detector_state_; -}; -struct LoggedBweDelayBasedUpdate { - LoggedBweDelayBasedUpdate() = default; - LoggedBweDelayBasedUpdate(Timestamp timestamp, - int32_t bitrate_bps, - BandwidthUsage detector_state) - : timestamp(timestamp), - bitrate_bps(bitrate_bps), - detector_state(detector_state) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int32_t bitrate_bps; - BandwidthUsage detector_state; + static constexpr RtcEventDefinition + definition_{ + {"BweDelayBased", RtcEventBweUpdateDelayBased::kType}, + {&RtcEventBweUpdateDelayBased::bitrate_bps_, + &LoggedBweDelayBasedUpdate::bitrate_bps, + {"bitrate_bps", /*id=*/1, FieldType::kVarInt, /*width=*/32}}, + {&RtcEventBweUpdateDelayBased::detector_state_, + &LoggedBweDelayBasedUpdate::detector_state, + {"detector_state", /*id=*/2, FieldType::kVarInt, /*width=*/64}}}; }; } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h index b031658ea2..fd41b316e0 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h @@ -14,12 +14,37 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedBweLossBasedUpdate { + LoggedBweLossBasedUpdate() = default; + LoggedBweLossBasedUpdate(Timestamp timestamp, + int32_t bitrate_bps, + uint8_t fraction_lost, + int32_t expected_packets) + : timestamp(timestamp), + bitrate_bps(bitrate_bps), + fraction_lost(fraction_lost), + expected_packets(expected_packets) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t bitrate_bps; + uint8_t fraction_lost; + int32_t expected_packets; +}; + class RtcEventBweUpdateLossBased final : public RtcEvent { public: static constexpr Type kType = Type::BweUpdateLossBased; @@ -38,6 +63,19 @@ class RtcEventBweUpdateLossBased final : public RtcEvent { uint8_t fraction_loss() const { return fraction_loss_; } int32_t total_packets() const { return total_packets_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventBweUpdateLossBased(const RtcEventBweUpdateLossBased& other); @@ -46,26 +84,6 @@ class RtcEventBweUpdateLossBased final : public RtcEvent { const int32_t total_packets_; }; -struct LoggedBweLossBasedUpdate { - LoggedBweLossBasedUpdate() = default; - LoggedBweLossBasedUpdate(Timestamp timestamp, - int32_t bitrate_bps, - uint8_t fraction_lost, - int32_t expected_packets) - : timestamp(timestamp), - bitrate_bps(bitrate_bps), - fraction_lost(fraction_lost), - expected_packets(expected_packets) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int32_t bitrate_bps; - uint8_t fraction_lost; - int32_t expected_packets; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_LOSS_BASED_H_ diff --git a/logging/rtc_event_log/events/rtc_event_definition.h b/logging/rtc_event_log/events/rtc_event_definition.h new file mode 100644 index 0000000000..8688c5fc7b --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_definition.h @@ -0,0 +1,152 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +template +struct RtcEventFieldDefinition { + const T EventType::*event_member; + T LoggedType::*logged_member; + FieldParameters params; +}; + +// Base case +template +class RtcEventDefinitionImpl { + public: + void EncodeImpl(EventEncoder&, rtc::ArrayView) const {} + RtcEventLogParseStatus ParseImpl(EventParser&, + rtc::ArrayView) const { + return RtcEventLogParseStatus::Success(); + } +}; + +// Recursive case +template +class RtcEventDefinitionImpl { + public: + constexpr RtcEventDefinitionImpl( + RtcEventFieldDefinition field, + RtcEventFieldDefinition... rest) + : field_(field), rest_(rest...) {} + + void EncodeImpl(EventEncoder& encoder, + rtc::ArrayView batch) const { + auto values = ExtractRtcEventMember(batch, field_.event_member); + encoder.EncodeField(field_.params, values); + rest_.EncodeImpl(encoder, batch); + } + + RtcEventLogParseStatus ParseImpl( + EventParser& parser, + rtc::ArrayView output_batch) const { + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(field_.params); + if (!result.ok()) + return result.status(); + auto status = PopulateRtcEventMember(result.value(), field_.logged_member, + output_batch); + if (!status.ok()) + return status; + + return rest_.ParseImpl(parser, output_batch); + } + + private: + RtcEventFieldDefinition field_; + RtcEventDefinitionImpl rest_; +}; + +// The RtcEventDefinition sets up a mapping between the fields +// in an RtcEvent and the corresponding fields in the parsed struct. +// For example, an RtcFoo class containing two fields; `uint32_t bar` +// and `bool baz` (a log timestamp is always implicitly added) +// might have a definition +// RtcEventDefinition( +// {"foo", RtcFoo::Type}, +// {&RtcFoo::bar_, &LoggedFoo::bar, {"bar", 1, FieldType::kVarInt, 32}}, +// {&RtcFoo::baz_, &LoggedFoo::baz, {"baz", 2, FieldType::kFixed8, 1}}, +// ); +// In addition to defining string names to aid debugging, +// this specifies that +// * RtcFoo::Type uniquely identifies an RtcFoo in the encoded stream +// * The `bar` field has ID 1, is encoded as a VarInt +// (when not delta compressed), and wraps around after 32 bits. +// * The `baz` field has ID 2, is encoded as an 8-bit field +// (when not delta compressed), and wraps around after 1 bit. +// Note that the numerical field and event IDs can't be changed since +// that would break compatibility with old logs. +// In most cases (including all cases where wrap around isn't +// expected), the wrap around should be equal to the bitwidth of +// the field. +template +class RtcEventDefinition { + public: + constexpr RtcEventDefinition( + EventParameters params, + RtcEventFieldDefinition... fields) + : params_(params), fields_(fields...) {} + + std::string EncodeBatch(rtc::ArrayView batch) const { + EventEncoder encoder(params_, batch); + fields_.EncodeImpl(encoder, batch); + return encoder.AsString(); + } + + RtcEventLogParseStatus ParseBatch(absl::string_view s, + bool batched, + std::vector& output) const { + EventParser parser; + auto status = parser.Initialize(s, batched); + if (!status.ok()) + return status; + + rtc::ArrayView output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{"timestamp_ms", + FieldParameters::kTimestampField, + FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp(result.value(), &LoggedType::timestamp, + output_batch); + if (!status.ok()) + return status; + + return fields_.ParseImpl(parser, output_batch); + } + + private: + EventParameters params_; + RtcEventDefinitionImpl fields_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h index 9a3eecb3d3..b9af213256 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h @@ -12,13 +12,26 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/dtls_transport_interface.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedDtlsTransportState { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + DtlsTransportState dtls_transport_state; +}; + class RtcEventDtlsTransportState : public RtcEvent { public: static constexpr Type kType = Type::DtlsTransportState; @@ -35,20 +48,25 @@ class RtcEventDtlsTransportState : public RtcEvent { return dtls_transport_state_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventDtlsTransportState(const RtcEventDtlsTransportState& other); const DtlsTransportState dtls_transport_state_; }; -struct LoggedDtlsTransportState { - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - DtlsTransportState dtls_transport_state; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_ diff --git a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h index c0cc5b87ef..c820f184d7 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h @@ -12,12 +12,28 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_WRITABLE_STATE_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedDtlsWritableState { + LoggedDtlsWritableState() = default; + explicit LoggedDtlsWritableState(bool writable) : writable(writable) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool writable; +}; + class RtcEventDtlsWritableState : public RtcEvent { public: static constexpr Type kType = Type::DtlsWritableState; @@ -32,23 +48,25 @@ class RtcEventDtlsWritableState : public RtcEvent { bool writable() const { return writable_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventDtlsWritableState(const RtcEventDtlsWritableState& other); const bool writable_; }; -struct LoggedDtlsWritableState { - LoggedDtlsWritableState() = default; - explicit LoggedDtlsWritableState(bool writable) : writable(writable) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - bool writable; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_WRITABLE_STATE_H_ diff --git a/logging/rtc_event_log/events/rtc_event_end_log.cc b/logging/rtc_event_log/events/rtc_event_end_log.cc new file mode 100644 index 0000000000..52abf9e842 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_end_log.cc @@ -0,0 +1,58 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_end_log.h" + +#include "absl/strings/string_view.h" + +namespace webrtc { +constexpr RtcEvent::Type RtcEventEndLog::kType; +constexpr EventParameters RtcEventEndLog::event_params_; + +RtcEventEndLog::RtcEventEndLog(Timestamp timestamp) + : RtcEvent(timestamp.us()) {} + +RtcEventEndLog::RtcEventEndLog(const RtcEventEndLog& other) + : RtcEvent(other.timestamp_us_) {} + +RtcEventEndLog::~RtcEventEndLog() = default; + +std::string RtcEventEndLog::Encode(rtc::ArrayView batch) { + EventEncoder encoder(event_params_, batch); + return encoder.AsString(); +} + +RtcEventLogParseStatus RtcEventEndLog::Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + EventParser parser; + auto status = parser.Initialize(encoded_bytes, batched); + if (!status.ok()) + return status; + + rtc::ArrayView output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp(result.value(), + &LoggedStopEvent::timestamp, output_batch); + if (!status.ok()) + return status; + + return RtcEventLogParseStatus::Success(); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_end_log.h b/logging/rtc_event_log/events/rtc_event_end_log.h new file mode 100644 index 0000000000..79648bdb8d --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_end_log.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +struct LoggedStopEvent { + LoggedStopEvent() = default; + + explicit LoggedStopEvent(Timestamp timestamp) : timestamp(timestamp) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::PlusInfinity(); +}; + +class RtcEventEndLog final : public RtcEvent { + public: + static constexpr Type kType = Type::EndV3Log; + + explicit RtcEventEndLog(Timestamp timestamp); + ~RtcEventEndLog() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + static std::string Encode(rtc::ArrayView batch); + + static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, + bool batched, + std::vector& output); + + private: + RtcEventEndLog(const RtcEventEndLog& other); + + static constexpr EventParameters event_params_{"EndLog", + RtcEventEndLog::kType}; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.cc b/logging/rtc_event_log/events/rtc_event_field_encoding.cc new file mode 100644 index 0000000000..68188ce856 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_encoding.cc @@ -0,0 +1,299 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_field_encoding.h" + +#include +#include +#include +#include + +#include "logging/rtc_event_log/encoder/bit_writer.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +using webrtc_event_logging::UnsignedDelta; + +namespace { + +std::string SerializeLittleEndian(uint64_t value, uint8_t bytes) { + RTC_DCHECK_LE(bytes, sizeof(uint64_t)); + RTC_DCHECK_GE(bytes, 1); + if (bytes < sizeof(uint64_t)) { + // Note that shifting a 64-bit value by 64 (or more) bits is undefined. + RTC_DCHECK_EQ(value >> (8 * bytes), 0); + } + std::string output(bytes, 0); + // Getting a non-const pointer to the representation. See e.g. + // https://en.cppreference.com/w/cpp/string/basic_string: + // "The elements of a basic_string are stored contiguously, + // that is, [...] a pointer to s[0] can be passed to functions + // that expect a pointer to the first element of a null-terminated + // CharT[] array." + uint8_t* p = reinterpret_cast(&output[0]); +#ifdef WEBRTC_ARCH_LITTLE_ENDIAN + memcpy(p, &value, bytes); +#else + while (bytes > 0) { + *p = static_cast(value & 0xFF); + value >>= 8; + ++p; + --bytes; + } +#endif // WEBRTC_ARCH_LITTLE_ENDIAN + return output; +} + +} // namespace + +namespace webrtc { + +std::string EncodeOptionalValuePositions(std::vector positions) { + BitWriter writer((positions.size() + 7) / 8); + for (bool position : positions) { + writer.WriteBits(position ? 1u : 0u, 1); + } + return writer.GetString(); +} + +std::string EncodeSingleValue(uint64_t value, FieldType field_type) { + switch (field_type) { + case FieldType::kFixed8: + return SerializeLittleEndian(value, /*bytes=*/1); + case FieldType::kFixed32: + return SerializeLittleEndian(value, /*bytes=*/4); + case FieldType::kFixed64: + return SerializeLittleEndian(value, /*bytes=*/8); + case FieldType::kVarInt: + return EncodeVarInt(value); + case FieldType::kString: + RTC_DCHECK_NOTREACHED(); + return std::string(); + } + RTC_DCHECK_NOTREACHED(); + return std::string(); +} + +absl::optional ConvertFieldType(uint64_t value) { + switch (value) { + case static_cast(FieldType::kFixed8): + return FieldType::kFixed8; + case static_cast(FieldType::kFixed32): + return FieldType::kFixed32; + case static_cast(FieldType::kFixed64): + return FieldType::kFixed64; + case static_cast(FieldType::kVarInt): + return FieldType::kVarInt; + case static_cast(FieldType::kString): + return FieldType::kString; + default: + return absl::nullopt; + } +} + +std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, + uint64_t base, + rtc::ArrayView values) { + size_t outputbound = (values.size() * params.delta_bit_width() + 7) / 8; + BitWriter writer(outputbound); + + uint64_t previous = base; + for (uint64_t value : values) { + if (params.signed_deltas()) { + uint64_t positive_delta = + UnsignedDelta(previous, value, params.value_mask()); + uint64_t negative_delta = + UnsignedDelta(value, previous, params.value_mask()); + uint64_t delta; + if (positive_delta <= negative_delta) { + delta = positive_delta; + } else { + // Compute the two's complement representation of a negative + // delta, in a field width params_.delta_mask(). + RTC_DCHECK_GE(params.delta_mask(), negative_delta); + RTC_DCHECK_LT(params.delta_mask() - negative_delta, + params.delta_mask()); + delta = params.delta_mask() - negative_delta + 1; + RTC_DCHECK_LE(delta, params.delta_mask()); + } + writer.WriteBits(delta, params.delta_bit_width()); + } else { + uint64_t delta = UnsignedDelta(previous, value, params.value_mask()); + writer.WriteBits(delta, params.delta_bit_width()); + } + previous = value; + } + + return writer.GetString(); +} + +EventEncoder::EventEncoder(EventParameters params, + rtc::ArrayView batch) { + batch_size_ = batch.size(); + if (!batch.empty()) { + // Encode event type. + uint32_t batched = batch.size() > 1 ? 1 : 0; + event_tag_ = (static_cast(params.id) << 1) + batched; + + // Event tag and number of encoded bytes will be filled in when the + // encoding is finalized in AsString(). + + // Encode number of events in batch + if (batched) { + encoded_fields_.push_back(EncodeVarInt(batch.size())); + } + + // Encode timestamp + std::vector timestamps; + timestamps.reserve(batch.size()); + for (const RtcEvent* event : batch) { + timestamps.push_back(EncodeAsUnsigned(event->timestamp_ms())); + } + constexpr FieldParameters timestamp_params{"timestamp_ms", + FieldParameters::kTimestampField, + FieldType::kVarInt, 64}; + EncodeField(timestamp_params, timestamps); + } +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const ValuesWithPositions& values) { + return EncodeField(params, values.values, &values.position_mask); +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const std::vector& values, + const std::vector* positions) { + if (positions) { + RTC_DCHECK_EQ(positions->size(), batch_size_); + RTC_DCHECK_LE(values.size(), batch_size_); + } else { + RTC_DCHECK_EQ(values.size(), batch_size_); + } + + if (values.size() == 0) { + // If all values for a particular field is empty/nullopt, + // then we completely skip the field even if the the batch is non-empty. + return; + } + + // We know that each event starts with the varint encoded timestamp, + // so we omit that field tag (field id + field type). In all other + // cases, we write the field tag. + if (params.field_id != FieldParameters::kTimestampField) { + RTC_DCHECK_LE(params.field_id, std::numeric_limits::max() >> 3); + uint64_t field_tag = params.field_id << 3; + field_tag += static_cast(params.field_type); + encoded_fields_.push_back(EncodeVarInt(field_tag)); + } + + RTC_CHECK_GE(values.size(), 1); + if (batch_size_ == 1) { + encoded_fields_.push_back(EncodeSingleValue(values[0], params.field_type)); + return; + } + + const bool values_optional = values.size() != batch_size_; + + // Compute delta parameters + rtc::ArrayView all_values(values); + uint64_t base = values[0]; + rtc::ArrayView remaining_values(all_values.subview(1)); + + FixedLengthEncodingParametersV3 delta_params = + FixedLengthEncodingParametersV3::CalculateParameters( + base, remaining_values, params.value_width, values_optional); + + encoded_fields_.push_back(EncodeVarInt(delta_params.DeltaHeaderAsInt())); + + if (values_optional) { + RTC_CHECK(positions); + encoded_fields_.push_back(EncodeOptionalValuePositions(*positions)); + } + // Base element, encoded as uint8, uint32, uint64 or varint + encoded_fields_.push_back(EncodeSingleValue(base, params.field_type)); + + // If all (existing) values are equal to the base, then we can skip + // writing the all-zero deltas, and instead infer those from the delta + // header. + if (!delta_params.values_equal()) { + encoded_fields_.push_back( + EncodeDeltasV3(delta_params, base, remaining_values)); + } +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const std::vector& values) { + RTC_DCHECK_EQ(values.size(), batch_size_); + + if (values.size() == 0) { + // If all values for a particular field is empty/nullopt, + // then we completely skip the field even if the the batch is non-empty. + return; + } + + // Write the field tag. + RTC_CHECK_NE(params.field_id, FieldParameters::kTimestampField); + RTC_DCHECK_LE(params.field_id, std::numeric_limits::max() >> 3); + RTC_DCHECK_EQ(params.field_type, FieldType::kString); + uint64_t field_tag = params.field_id << 3; + field_tag += static_cast(params.field_type); + encoded_fields_.push_back(EncodeVarInt(field_tag)); + + if (values.size() > 1) { + // If multiple values in the batch, write the encoding + // parameters. (Values >0 reserved for future use.) + uint64_t encoding_params = 0; + encoded_fields_.push_back(EncodeVarInt(encoding_params)); + } + + // Write the strings as (length, data) pairs. + for (absl::string_view s : values) { + encoded_fields_.push_back(EncodeVarInt(s.size())); + encoded_fields_.push_back(std::string(s)); + } +} + +std::string EventEncoder::AsString() { + std::string encoded_event; + + if (batch_size_ == 0) { + RTC_DCHECK_EQ(encoded_fields_.size(), 0); + return encoded_event; + } + + // Compute size of encoded fields. + size_t total_fields_size = 0; + for (const std::string& s : encoded_fields_) { + total_fields_size += s.size(); + } + + constexpr size_t kExpectedMaxEventTagBytes = 4; + constexpr size_t kExpectedMaxSizeEncodingBytes = 4; + encoded_event.reserve(kExpectedMaxEventTagBytes + + kExpectedMaxSizeEncodingBytes + total_fields_size); + + // Encode event tag (event id and whether batch or single event). + encoded_event.append(EncodeVarInt(event_tag_)); + + // Encode size of the remaining fields. + encoded_event.append(EncodeVarInt(total_fields_size)); + + // Append encoded fields. + for (const std::string& s : encoded_fields_) { + encoded_event.append(s); + } + + return encoded_event; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.h b/logging/rtc_event_log/events/rtc_event_field_encoding.h new file mode 100644 index 0000000000..33b77b80f5 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_encoding.h @@ -0,0 +1,179 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +// To maintain backwards compatibility with past (or future) logs, +// the constants in this enum must not be changed. +// New field types with numerical IDs 5-7 can be added, but old +// parsers will fail to parse events containing the new fields. +enum class FieldType : uint8_t { + kFixed8 = 0, + kFixed32 = 1, + kFixed64 = 2, + kVarInt = 3, + kString = 4, +}; + +// EventParameters map an event name to a numerical ID. +struct EventParameters { + // The name is primarily used for debugging purposes. + const char* const name; + // + const RtcEvent::Type id; +}; + +// FieldParameters define the encoding for a field. +struct FieldParameters { + // The name is primarily used for debugging purposes. + const char* const name; + // Numerical ID for the field. Must be strictly greater than 0, + // and unique within each event type. + const uint64_t field_id; + // Encoding type for the base (i.e. non-delta) field in a batch. + const FieldType field_type; + // Number of bits after which wrap-around occurs. In most cases, + // this should be the number of bits in the field data type, i.e. + // 8 for an uint8_t, 32 for a int32_t and so on. However, `value_width` + // can be used to achieve a more efficient encoding if it is known + // that the field uses a smaller number of bits. For example, a + // 15-bit counter could set `value_width` to 15 even if the data is + // actually stored in a uint32_t. + const uint64_t value_width; + // Field ID 0 is reserved for timestamps. + static constexpr uint64_t kTimestampField = 0; +}; + +// The EventEncoder is used to encode a batch of events. +class EventEncoder { + public: + EventEncoder(EventParameters params, rtc::ArrayView batch); + + void EncodeField(const FieldParameters& params, + const std::vector& values, + const std::vector* positions = nullptr); + + void EncodeField(const FieldParameters& params, + const ValuesWithPositions& values); + + void EncodeField(const FieldParameters& params, + const std::vector& values); + + std::string AsString(); + + private: + size_t batch_size_; + uint32_t event_tag_; + std::vector encoded_fields_; +}; + +std::string EncodeSingleValue(uint64_t value, FieldType field_type); +std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, + uint64_t base, + rtc::ArrayView values); + +// Given a batch of RtcEvents and a member pointer, extract that +// member from each event in the batch. Signed integer members are +// encoded as unsigned, and the bitsize increased so the result can +// represented as a std::vector. +// This is intended to be used in conjuction with +// EventEncoder::EncodeField to encode a batch of events as follows: +// auto values = ExtractRtcEventMember(batch, RtcEventFoo::timestamp_ms); +// encoder.EncodeField(timestamp_params, values) +template ::value, bool> = true> +std::vector ExtractRtcEventMember( + rtc::ArrayView batch, + const T E::*member) { + std::vector values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + T value = static_cast(event)->*member; + values.push_back(EncodeAsUnsigned(value)); + } + return values; +} + +// Extract an optional field from a batch of RtcEvents. +// The function returns a vector of positions in addition to the vector of +// values. The vector `positions` has the same length as the batch where +// `positions[i] == true` iff the batch[i]->member has a value. +// The values vector only contains the values that exists, so it +// may be shorter than the batch. +template ::value, bool> = true> +ValuesWithPositions ExtractRtcEventMember(rtc::ArrayView batch, + const absl::optional E::*member) { + ValuesWithPositions result; + result.position_mask.reserve(batch.size()); + result.values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + absl::optional field = static_cast(event)->*member; + result.position_mask.push_back(field.has_value()); + if (field.has_value()) { + result.values.push_back(EncodeAsUnsigned(field.value())); + } + } + return result; +} + +// Extract an enum field from a batch of RtcEvents. +// Requires specializing RtcEventLogEnum for the enum type T. +template ::value, bool> = true> +std::vector ExtractRtcEventMember( + rtc::ArrayView batch, + const T E::*member) { + std::vector values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + T value = static_cast(event)->*member; + values.push_back(RtcEventLogEnum::Encode(value)); + } + return values; +} + +// Extract a string field from a batch of RtcEvents. +template +std::vector ExtractRtcEventMember( + rtc::ArrayView batch, + const std::string E::*member) { + std::vector values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + absl::string_view str = static_cast(event)->*member; + values.push_back(str); + } + return values; +} + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc new file mode 100644 index 0000000000..a9b0c08307 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc @@ -0,0 +1,398 @@ + +/* + * 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 "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" + +namespace { +absl::optional ConvertFieldType(uint64_t value) { + switch (value) { + case static_cast(webrtc::FieldType::kFixed8): + return webrtc::FieldType::kFixed8; + case static_cast(webrtc::FieldType::kFixed32): + return webrtc::FieldType::kFixed32; + case static_cast(webrtc::FieldType::kFixed64): + return webrtc::FieldType::kFixed64; + case static_cast(webrtc::FieldType::kVarInt): + return webrtc::FieldType::kVarInt; + case static_cast(webrtc::FieldType::kString): + return webrtc::FieldType::kString; + default: + return absl::nullopt; + } +} +} // namespace + +namespace webrtc { + +uint64_t EventParser::ReadLittleEndian(uint8_t bytes) { + RTC_DCHECK_LE(bytes, sizeof(uint64_t)); + RTC_DCHECK_GE(bytes, 1); + + uint64_t value = 0; + + if (bytes > pending_data_.length()) { + SetError(); + return value; + } + + const uint8_t* p = reinterpret_cast(pending_data_.data()); + unsigned int shift = 0; + uint8_t remaining = bytes; + while (remaining > 0) { + value += (static_cast(*p) << shift); + shift += 8; + ++p; + --remaining; + } + + pending_data_ = pending_data_.substr(bytes); + return value; +} + +uint64_t EventParser::ReadVarInt() { + uint64_t output = 0; + bool success; + std::tie(success, pending_data_) = DecodeVarInt(pending_data_, &output); + if (!success) { + SetError(); + } + return output; +} + +uint64_t EventParser::ReadOptionalValuePositions() { + RTC_DCHECK(positions_.empty()); + size_t bits_to_read = NumEventsInBatch(); + positions_.reserve(bits_to_read); + if (pending_data_.size() * 8 < bits_to_read) { + SetError(); + return 0; + } + + BitstreamReader reader(pending_data_); + for (size_t i = 0; i < bits_to_read; i++) { + positions_.push_back(reader.ReadBit()); + } + if (!reader.Ok()) { + SetError(); + return 0; + } + + size_t num_existing_values = + std::count(positions_.begin(), positions_.end(), 1); + pending_data_ = pending_data_.substr((bits_to_read + 7) / 8); + return num_existing_values; +} + +uint64_t EventParser::ReadSingleValue(FieldType field_type) { + switch (field_type) { + case FieldType::kFixed8: + return ReadLittleEndian(/*bytes=*/1); + case FieldType::kFixed32: + return ReadLittleEndian(/*bytes=*/4); + case FieldType::kFixed64: + return ReadLittleEndian(/*bytes=*/8); + case FieldType::kVarInt: + return ReadVarInt(); + case FieldType::kString: + RTC_DCHECK_NOTREACHED(); + SetError(); + return 0; + } + RTC_DCHECK_NOTREACHED(); + SetError(); + return 0; +} + +void EventParser::ReadDeltasAndPopulateValues( + FixedLengthEncodingParametersV3 params, + uint64_t num_deltas, + uint64_t base) { + RTC_DCHECK(values_.empty()); + values_.reserve(num_deltas + 1); + values_.push_back(base); + + if (pending_data_.size() * 8 < num_deltas * params.delta_bit_width()) { + SetError(); + return; + } + + BitstreamReader reader(pending_data_); + const uint64_t top_bit = static_cast(1) + << (params.delta_bit_width() - 1); + + uint64_t value = base; + for (uint64_t i = 0; i < num_deltas; ++i) { + uint64_t delta = reader.ReadBits(params.delta_bit_width()); + RTC_DCHECK_LE(value, webrtc_event_logging::MaxUnsignedValueOfBitWidth( + params.value_bit_width())); + RTC_DCHECK_LE(delta, webrtc_event_logging::MaxUnsignedValueOfBitWidth( + params.delta_bit_width())); + bool negative_delta = params.signed_deltas() && ((delta & top_bit) != 0); + if (negative_delta) { + uint64_t delta_abs = (~delta & params.delta_mask()) + 1; + value = (value - delta_abs) & params.value_mask(); + } else { + value = (value + delta) & params.value_mask(); + } + values_.push_back(value); + } + + if (!reader.Ok()) { + SetError(); + return; + } + + pending_data_ = + pending_data_.substr((num_deltas * params.delta_bit_width() + 7) / 8); +} + +RtcEventLogParseStatus EventParser::Initialize(absl::string_view s, + bool batched) { + pending_data_ = s; + num_events_ = 1; + + if (batched) { + num_events_ = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error( + "Failed to read number of events in batch.", __FILE__, __LINE__); + } + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseNumericFieldInternal( + uint64_t value_bit_width, + FieldType field_type) { + RTC_DCHECK(values_.empty()); + RTC_DCHECK(positions_.empty()); + + if (num_events_ == 1) { + // Just a single value in the batch. + uint64_t base = ReadSingleValue(field_type); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read value", __FILE__, + __LINE__); + } + positions_.push_back(true); + values_.push_back(base); + } else { + // Delta compressed batch. + // Read delta header. + uint64_t header_value = ReadVarInt(); + if (!Ok()) + return RtcEventLogParseStatus::Error("Failed to read delta header", + __FILE__, __LINE__); + // NB: value_bit_width may be incorrect for the field, if this isn't the + // field we are looking for. + absl::optional delta_header = + FixedLengthEncodingParametersV3::ParseDeltaHeader(header_value, + value_bit_width); + if (!delta_header.has_value()) { + return RtcEventLogParseStatus::Error("Failed to parse delta header", + __FILE__, __LINE__); + } + + uint64_t num_existing_deltas = NumEventsInBatch() - 1; + if (delta_header->values_optional()) { + size_t num_nonempty_values = ReadOptionalValuePositions(); + if (!Ok()) { + return RtcEventLogParseStatus::Error( + "Failed to read positions of optional values", __FILE__, __LINE__); + } + if (num_nonempty_values < 1 || NumEventsInBatch() < num_nonempty_values) { + return RtcEventLogParseStatus::Error( + "Expected at least one non_empty value", __FILE__, __LINE__); + } + num_existing_deltas = num_nonempty_values - 1; + } else { + // All elements in the batch have values. + positions_.assign(NumEventsInBatch(), 1u); + } + + // Read base. + uint64_t base = ReadSingleValue(field_type); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read value", __FILE__, + __LINE__); + } + + if (delta_header->values_equal()) { + // Duplicate the base value num_existing_deltas times. + values_.assign(num_existing_deltas + 1, base); + } else { + // Read deltas; ceil(num_existing_deltas*delta_width/8) bits + ReadDeltasAndPopulateValues(delta_header.value(), num_existing_deltas, + base); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to decode deltas", + __FILE__, __LINE__); + } + } + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseStringFieldInternal() { + RTC_DCHECK(strings_.empty()); + if (num_events_ > 1) { + // String encoding params reserved for future use. + uint64_t encoding_params = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read string encoding", + __FILE__, __LINE__); + } + if (encoding_params != 0) { + return RtcEventLogParseStatus::Error( + "Unrecognized string encoding parameters", __FILE__, __LINE__); + } + } + strings_.reserve(num_events_); + for (uint64_t i = 0; i < num_events_; ++i) { + // Just a single value in the batch. + uint64_t size = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read string size", + __FILE__, __LINE__); + } + if (size > pending_data_.size()) { + return RtcEventLogParseStatus::Error("String size exceeds remaining data", + __FILE__, __LINE__); + } + strings_.push_back(pending_data_.substr(0, size)); + pending_data_ = pending_data_.substr(size); + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseField(const FieldParameters& params) { + // Verify that the event parses fields in increasing order. + if (params.field_id == FieldParameters::kTimestampField) { + RTC_DCHECK_EQ(last_field_id_, FieldParameters::kTimestampField); + } else { + RTC_DCHECK_GT(params.field_id, last_field_id_); + } + last_field_id_ = params.field_id; + + // Initialization for positional fields that don't encode field ID and type. + uint64_t field_id = params.field_id; + FieldType field_type = params.field_type; + + // Fields are encoded in increasing field_id order. + // Skip unknown fields with field_id < params.field_id until we either + // find params.field_id or a field with higher id, in which case we know that + // params.field_id doesn't exist. + while (!pending_data_.empty()) { + absl::string_view field_start = pending_data_; + ClearTemporaries(); + + // Read tag for non-positional fields. + if (params.field_id != FieldParameters::kTimestampField) { + uint64_t field_tag = ReadVarInt(); + if (!Ok()) + return RtcEventLogParseStatus::Error("Failed to read field tag", + __FILE__, __LINE__); + // Split tag into field ID and field type. + field_id = field_tag >> 3; + absl::optional conversion = ConvertFieldType(field_tag & 7u); + if (!conversion.has_value()) + return RtcEventLogParseStatus::Error("Failed to parse field type", + __FILE__, __LINE__); + field_type = conversion.value(); + } + + if (field_id > params.field_id) { + // We've passed all fields with ids less than or equal to what we are + // looking for. Reset pending_data_ to first field with id higher than + // params.field_id, since we didn't find the field we were looking for. + pending_data_ = field_start; + return RtcEventLogParseStatus::Success(); + } + + if (field_type == FieldType::kString) { + auto status = ParseStringFieldInternal(); + if (!status.ok()) { + return status; + } + } else { + auto status = ParseNumericFieldInternal(params.value_width, field_type); + if (!status.ok()) { + return status; + } + } + + if (field_id == params.field_id) { + // The field we're looking for has been found and values populated. + return RtcEventLogParseStatus::Success(); + } + } + + // Field not found because the event ended. + ClearTemporaries(); + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatusOr> +EventParser::ParseStringField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr>; + RTC_DCHECK_EQ(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + rtc::ArrayView strings = GetStrings(); + if (required_field && strings.size() != NumEventsInBatch()) { + return StatusOr::Error("Required string field not found", __FILE__, + __LINE__); + } + return StatusOr(strings); +} + +RtcEventLogParseStatusOr> +EventParser::ParseNumericField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr>; + RTC_DCHECK_NE(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + rtc::ArrayView values = GetValues(); + if (required_field && values.size() != NumEventsInBatch()) { + return StatusOr::Error("Required numerical field not found", __FILE__, + __LINE__); + } + return StatusOr(values); +} + +RtcEventLogParseStatusOr +EventParser::ParseOptionalNumericField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr; + RTC_DCHECK_NE(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + ValueAndPostionView view{GetValues(), GetPositions()}; + if (required_field && view.positions.size() != NumEventsInBatch()) { + return StatusOr::Error("Required numerical field not found", __FILE__, + __LINE__); + } + return StatusOr(view); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h new file mode 100644 index 0000000000..c33d4bee3a --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h @@ -0,0 +1,285 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ + +#include +#include + +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" + +// TODO(terelius): Compared to a generic 'Status' class, this +// class allows us additional information about the context +// in which the error occurred. This is currently limited to +// the source location (file and line), but we plan on adding +// information about the event and field name being parsed. +// If/when we start using absl::Status in WebRTC, consider +// whether payloads would be an appropriate alternative. +class RtcEventLogParseStatus { + template + friend class RtcEventLogParseStatusOr; + + public: + static RtcEventLogParseStatus Success() { return RtcEventLogParseStatus(); } + static RtcEventLogParseStatus Error(std::string error, + std::string file, + int line) { + return RtcEventLogParseStatus(error, file, line); + } + + bool ok() const { return error_.empty(); } + ABSL_DEPRECATED("Use ok() instead") explicit operator bool() const { + return ok(); + } + + std::string message() const { return error_; } + + private: + RtcEventLogParseStatus() : error_() {} + RtcEventLogParseStatus(std::string error, std::string file, int line) + : error_(error + " (" + file + ": " + std::to_string(line) + ")") {} + + std::string error_; +}; + +template +class RtcEventLogParseStatusOr { + public: + RtcEventLogParseStatusOr(RtcEventLogParseStatus status) // NOLINT + : status_(status), value_() {} + RtcEventLogParseStatusOr(const T& value) // NOLINT + : status_(), value_(value) {} + + bool ok() const { return status_.ok(); } + + std::string message() const { return status_.message(); } + + RtcEventLogParseStatus status() const { return status_; } + + const T& value() const { + RTC_DCHECK(ok()); + return value_; + } + + T& value() { + RTC_DCHECK(ok()); + return value_; + } + + static RtcEventLogParseStatusOr Error(std::string error, + std::string file, + int line) { + return RtcEventLogParseStatusOr(error, file, line); + } + + private: + RtcEventLogParseStatusOr() : status_() {} + RtcEventLogParseStatusOr(std::string error, std::string file, int line) + : status_(error, file, line), value_() {} + + RtcEventLogParseStatus status_; + T value_; +}; + +namespace webrtc { + +class EventParser { + public: + struct ValueAndPostionView { + rtc::ArrayView values; + rtc::ArrayView positions; + }; + + EventParser() = default; + + // N.B: This method stores a abls::string_view into the string to be + // parsed. The caller is responsible for ensuring that the actual string + // remains unmodified and outlives the EventParser. + RtcEventLogParseStatus Initialize(absl::string_view s, bool batched); + + // Attempts to parse the field specified by `params`, skipping past + // other fields that may occur before it. If 'required_field == true', + // then failing to find the field is an error, otherwise the functions + // return success, but with an empty view of values. + RtcEventLogParseStatusOr> ParseStringField( + const FieldParameters& params, + bool required_field = true); + RtcEventLogParseStatusOr> ParseNumericField( + const FieldParameters& params, + bool required_field = true); + RtcEventLogParseStatusOr ParseOptionalNumericField( + const FieldParameters& params, + bool required_field = true); + + // Number of events in a batch. + uint64_t NumEventsInBatch() const { return num_events_; } + + // Bytes remaining in `pending_data_`. Assuming there are no unknown + // fields, BytesRemaining() should return 0 when all known fields + // in the event have been parsed. + size_t RemainingBytes() const { return pending_data_.size(); } + + private: + uint64_t ReadLittleEndian(uint8_t bytes); + uint64_t ReadVarInt(); + uint64_t ReadSingleValue(FieldType field_type); + uint64_t ReadOptionalValuePositions(); + void ReadDeltasAndPopulateValues(FixedLengthEncodingParametersV3 params, + uint64_t num_deltas, + uint64_t base); + RtcEventLogParseStatus ParseNumericFieldInternal(uint64_t value_bit_width, + FieldType field_type); + RtcEventLogParseStatus ParseStringFieldInternal(); + + // Attempts to parse the field specified by `params`, skipping past + // other fields that may occur before it. Returns + // RtcEventLogParseStatus::Success() and populates `values_` (and + // `positions_`) if the field is found. Returns + // RtcEventLogParseStatus::Success() and clears `values_` (and `positions_`) + // if the field doesn't exist. Returns a RtcEventLogParseStatus::Error() if + // the log is incomplete, malformed or otherwise can't be parsed. + RtcEventLogParseStatus ParseField(const FieldParameters& params); + + void SetError() { error_ = true; } + bool Ok() const { return !error_; } + + rtc::ArrayView GetValues() { return values_; } + rtc::ArrayView GetPositions() { return positions_; } + rtc::ArrayView GetStrings() { return strings_; } + + void ClearTemporaries() { + positions_.clear(); + values_.clear(); + strings_.clear(); + } + + // Tracks whether an error has occurred in one of the helper + // functions above. + bool error_ = false; + + // Temporary storage for result. + std::vector positions_; + std::vector values_; + std::vector strings_; + + // String to be consumed. + absl::string_view pending_data_; + uint64_t num_events_ = 1; + uint64_t last_field_id_ = FieldParameters::kTimestampField; +}; + +// Inverse of the ExtractRtcEventMember function used when parsing +// a log. Uses a vector of values to populate a specific field in a +// vector of structs. +template ::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView values, + T E::*member, + rtc::ArrayView output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*member = DecodeFromUnsignedToType(values[i]); + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for optional fields. +template ::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView positions, + const rtc::ArrayView values, + absl::optional E::*member, + rtc::ArrayView output) { + size_t batch_size = positions.size(); + RTC_CHECK_EQ(output.size(), batch_size); + RTC_CHECK_LE(values.size(), batch_size); + auto value_it = values.begin(); + for (size_t i = 0; i < batch_size; ++i) { + if (positions[i]) { + RTC_CHECK(value_it != values.end()); + output[i].*member = DecodeFromUnsignedToType(value_it); + ++value_it; + } else { + output[i].*member = absl::nullopt; + } + } + RTC_CHECK(value_it == values.end()); + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for enum fields. +template ::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView values, + T E::*member, + rtc::ArrayView output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + auto result = RtcEventLogEnum::Decode(values[i]); + if (!result.ok()) { + return result.status(); + } + output[i].*member = result.value(); + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for string fields. +template +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView values, + std::string E::*member, + rtc::ArrayView output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*member = values[i]; + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for Timestamp fields. +// N.B. Assumes that the encoded value uses millisecond precision. +template +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventTimestamp(const rtc::ArrayView& values, + Timestamp E::*timestamp, + rtc::ArrayView output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(batch_size, output.size()); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*timestamp = + Timestamp::Millis(DecodeFromUnsignedToType(values[i])); + } + return RtcEventLogParseStatus::Success(); +} + +template +rtc::ArrayView ExtendLoggedBatch(std::vector& output, + size_t new_elements) { + size_t old_size = output.size(); + output.insert(output.end(), old_size + new_elements, E()); + rtc::ArrayView output_batch = output; + output_batch.subview(old_size); + RTC_DCHECK_EQ(output_batch.size(), new_elements); + return output_batch; +} + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc new file mode 100644 index 0000000000..b1554694ad --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc @@ -0,0 +1,885 @@ +/* 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 "logging/rtc_event_log/events/rtc_event_field_encoding.h" + +#include +#include +#include + +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +constexpr int32_t kInt32Max = std::numeric_limits::max(); +constexpr int32_t kInt32Min = std::numeric_limits::min(); +constexpr uint32_t kUint32Max = std::numeric_limits::max(); +constexpr int64_t kInt64Max = std::numeric_limits::max(); +constexpr int64_t kInt64Min = std::numeric_limits::min(); +constexpr uint64_t kUint64Max = std::numeric_limits::max(); + +template ::value, bool> = true> +size_t ExpectedVarIntSize(T value) { + size_t bytes = 0; + uint64_t x = EncodeAsUnsigned(value); + do { + ++bytes; + x = x >> 7; + } while (x > 0); + return bytes; +} + +template ::value, bool> = true> +size_t ExpectedBaseValueSize(const FieldParameters& params, T value) { + switch (params.field_type) { + case FieldType::kFixed8: + return 1; + case FieldType::kFixed32: + return 4; + case FieldType::kFixed64: + return 8; + case FieldType::kVarInt: + return ExpectedVarIntSize(value); + default: + break; + } + RTC_DCHECK_NOTREACHED(); + return 0; +} + +template ::value, bool> = true> +size_t ExpectedEncodingSize(const FieldParameters& params, + const std::vector& v, + size_t expected_bits_per_delta) { + if (v.size() == 0) + return 0; + + uint64_t numeric_field_type = static_cast(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + T base = v[0]; + size_t base_size = ExpectedBaseValueSize(params, base); + if (v.size() == 1) + return tag_size + base_size; + + size_t delta_header_size = 1; + // Check if there is an element *not* equal to base. + if (std::all_of(v.begin(), v.end(), [base](T x) { return x == base; })) { + return tag_size + base_size + delta_header_size; + } + + size_t delta_size = ((v.size() - 1) * expected_bits_per_delta + 7) / 8; + return tag_size + base_size + delta_header_size + delta_size; +} + +template ::value, bool> = true> +size_t ExpectedEncodingSize(const FieldParameters& params, + const std::vector>& v, + size_t expected_bits_per_delta) { + size_t num_existing_values = + v.size() - std::count(v.begin(), v.end(), absl::nullopt); + auto first_existing_value = std::find_if( + v.begin(), v.end(), [](absl::optional x) { return x.has_value(); }); + if (num_existing_values == 0) + return 0; + + uint64_t numeric_field_type = static_cast(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + T base = first_existing_value->value(); + size_t base_size = ExpectedBaseValueSize(params, base); + if (num_existing_values == 1 && v.size() == 1) + return tag_size + base_size; + + size_t delta_header_size = (num_existing_values == v.size() ? 1 : 2); + size_t positions_size = + (num_existing_values == v.size() ? 0 : (v.size() + 7) / 8); + // Check if there is an element *not* equal to base. + if (std::all_of(v.begin(), v.end(), + [base](absl::optional x) { return x == base; })) { + return tag_size + base_size + delta_header_size + positions_size; + } + + size_t delta_size = + ((num_existing_values - 1) * expected_bits_per_delta + 7) / 8; + return tag_size + base_size + delta_header_size + positions_size + delta_size; +} + +size_t ExpectedStringEncodingSize(const FieldParameters& params, + const std::vector& values) { + EXPECT_EQ(params.field_type, FieldType::kString); + uint64_t numeric_field_type = static_cast(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + + size_t expected_size = tag_size; + if (values.size() > 1) { + // VarInt encoding header reserved for future use. Currently always 0. + expected_size += 1; + } + for (const auto& s : values) { + expected_size += ExpectedVarIntSize(s.size()); + expected_size += s.size(); + } + return expected_size; +} + +} // namespace + +class RtcTestEvent final : public RtcEvent { + public: + RtcTestEvent(bool b, + int32_t signed32, + uint32_t unsigned32, + int64_t signed64, + uint64_t unsigned64) + : b_(b), + signed32_(signed32), + unsigned32_(unsigned32), + signed64_(signed64), + unsigned64_(unsigned64) {} + RtcTestEvent(bool b, + int32_t signed32, + uint32_t unsigned32, + int64_t signed64, + uint64_t unsigned64, + absl::optional optional_signed32, + absl::optional optional_signed64, + uint32_t wrapping21, + std::string string) + : b_(b), + signed32_(signed32), + unsigned32_(unsigned32), + signed64_(signed64), + unsigned64_(unsigned64), + optional_signed32_(optional_signed32), + optional_signed64_(optional_signed64), + wrapping21_(wrapping21), + string_(string) {} + ~RtcTestEvent() override = default; + + Type GetType() const override { return static_cast(4711); } + bool IsConfigEvent() const override { return false; } + + static constexpr EventParameters event_params{ + "TestEvent", static_cast(4711)}; + static constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + static constexpr FieldParameters bool_params{"b", 2, FieldType::kFixed8, 1}; + static constexpr FieldParameters signed32_params{"signed32", 3, + FieldType::kVarInt, 32}; + static constexpr FieldParameters unsigned32_params{"unsigned32", 4, + FieldType::kFixed32, 32}; + static constexpr FieldParameters signed64_params{"signed64", 5, + FieldType::kFixed64, 64}; + static constexpr FieldParameters unsigned64_params{"unsigned64", 6, + FieldType::kVarInt, 64}; + static constexpr FieldParameters optional32_params{"optional_signed32", 7, + FieldType::kFixed32, 32}; + static constexpr FieldParameters optional64_params{"optional_signed64", 8, + FieldType::kVarInt, 64}; + static constexpr FieldParameters wrapping21_params{"wrapping21", 9, + FieldType::kFixed32, 21}; + static constexpr FieldParameters string_params{ + "string", 10, FieldType::kString, /*value_width = */ 0}; + + static constexpr Type kType = static_cast(4711); + + const bool b_; + const int32_t signed32_; + const uint32_t unsigned32_; + const int64_t signed64_; + const uint64_t unsigned64_; + const absl::optional optional_signed32_ = absl::nullopt; + const absl::optional optional_signed64_ = absl::nullopt; + const uint32_t wrapping21_ = 0; + const std::string string_; +}; + +constexpr EventParameters RtcTestEvent::event_params; +constexpr FieldParameters RtcTestEvent::timestamp_params; +constexpr FieldParameters RtcTestEvent::bool_params; +constexpr FieldParameters RtcTestEvent::signed32_params; +constexpr FieldParameters RtcTestEvent::unsigned32_params; +constexpr FieldParameters RtcTestEvent::signed64_params; +constexpr FieldParameters RtcTestEvent::unsigned64_params; + +constexpr FieldParameters RtcTestEvent::optional32_params; +constexpr FieldParameters RtcTestEvent::optional64_params; +constexpr FieldParameters RtcTestEvent::wrapping21_params; +constexpr FieldParameters RtcTestEvent::string_params; + +constexpr RtcEvent::Type RtcTestEvent::kType; + +class RtcEventFieldTest : public ::testing::Test { + protected: + void SetUp() override {} + + void CreateFullEvents( + const std::vector& bool_values, + const std::vector& signed32_values, + const std::vector& unsigned32_values, + const std::vector& signed64_values, + const std::vector& unsigned64_values, + const std::vector>& optional32_values, + const std::vector>& optional64_values, + const std::vector& wrapping21_values, + const std::vector& string_values) { + size_t size = bool_values.size(); + RTC_CHECK_EQ(signed32_values.size(), size); + RTC_CHECK_EQ(unsigned32_values.size(), size); + RTC_CHECK_EQ(signed64_values.size(), size); + RTC_CHECK_EQ(unsigned64_values.size(), size); + RTC_CHECK_EQ(optional32_values.size(), size); + RTC_CHECK_EQ(optional64_values.size(), size); + RTC_CHECK_EQ(wrapping21_values.size(), size); + RTC_CHECK_EQ(string_values.size(), size); + + for (size_t i = 0; i < size; i++) { + batch_.push_back(new RtcTestEvent( + bool_values[i], signed32_values[i], unsigned32_values[i], + signed64_values[i], unsigned64_values[i], optional32_values[i], + optional64_values[i], wrapping21_values[i], string_values[i])); + } + } + + void PrintBytes(const std::string& s) { + for (auto c : s) { + fprintf(stderr, "%d ", static_cast(c)); + } + fprintf(stderr, "\n"); + } + + void ParseEventHeader(absl::string_view encoded_event) { + uint64_t event_tag; + bool success; + std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &event_tag); + ASSERT_TRUE(success); + uint64_t event_id = event_tag >> 1; + ASSERT_EQ(event_id, static_cast(RtcTestEvent::event_params.id)); + bool batched = event_tag & 1u; + ASSERT_EQ(batched, batch_.size() > 1u); + + uint64_t size; + std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &size); + ASSERT_EQ(encoded_event.size(), size); + + ASSERT_TRUE(parser_.Initialize(encoded_event, batched).ok()); + } + + void ParseAndVerifyTimestamps() { + auto result = parser_.ParseNumericField(RtcTestEvent::timestamp_params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), batch_.size()); + for (size_t i = 0; i < batch_.size(); i++) { + EXPECT_EQ(result.value()[i], + static_cast(batch_[i]->timestamp_ms())); + } + } + + void ParseAndVerifyStringField( + const FieldParameters& params, + const std::vector& expected_values, + size_t expected_skipped_bytes = 0) { + size_t expected_size = ExpectedStringEncodingSize(params, expected_values) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseStringField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), expected_values.size()); + for (size_t i = 0; i < expected_values.size(); i++) { + EXPECT_EQ(result.value()[i], expected_values[i]); + } + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size) + << " for field " << params.name; + } + + template + void ParseAndVerifyField(const FieldParameters& params, + const std::vector& expected_values, + size_t expected_bits_per_delta, + size_t expected_skipped_bytes = 0) { + size_t expected_size = + ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseNumericField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), expected_values.size()); + for (size_t i = 0; i < expected_values.size(); i++) { + EXPECT_EQ(DecodeFromUnsignedToType(result.value()[i]), + expected_values[i]); + } + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size) + << " for field " << params.name; + } + + template + void ParseAndVerifyOptionalField( + const FieldParameters& params, + const std::vector>& expected_values, + size_t expected_bits_per_delta, + size_t expected_skipped_bytes = 0) { + size_t expected_size = + ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseOptionalNumericField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + rtc::ArrayView values = result.value().values; + rtc::ArrayView positions = result.value().positions; + ASSERT_EQ(positions.size(), expected_values.size()); + auto value_it = values.begin(); + for (size_t i = 0; i < expected_values.size(); i++) { + if (positions[i]) { + ASSERT_NE(value_it, values.end()); + ASSERT_TRUE(expected_values[i].has_value()); + EXPECT_EQ(DecodeFromUnsignedToType(*value_it), + expected_values[i].value()); + ++value_it; + } else { + EXPECT_EQ(absl::nullopt, expected_values[i]); + } + } + EXPECT_EQ(value_it, values.end()); + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size); + } + + void ParseAndVerifyMissingField(const FieldParameters& params) { + auto result = parser_.ParseNumericField(params, /*required_field=*/false); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + EXPECT_EQ(result.value().size(), 0u); + } + + void ParseAndVerifyMissingOptionalField(const FieldParameters& params) { + auto result = + parser_.ParseOptionalNumericField(params, /*required_field=*/false); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + rtc::ArrayView values = result.value().values; + rtc::ArrayView positions = result.value().positions; + EXPECT_EQ(positions.size(), 0u); + EXPECT_EQ(values.size(), 0u); + } + + void TearDown() override { + for (const RtcEvent* event : batch_) { + delete event; + } + } + + std::vector batch_; + EventParser parser_; +}; + +TEST_F(RtcEventFieldTest, EmptyList) { + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + std::string s = encoder.AsString(); + EXPECT_TRUE(s.empty()); +} + +TEST_F(RtcEventFieldTest, Singleton) { + std::vector bool_values = {true}; + std::vector signed32_values = {-2}; + std::vector unsigned32_values = {123456789}; + std::vector signed64_values = {-9876543210}; + std::vector unsigned64_values = {9876543210}; + std::vector> optional32_values = {kInt32Min}; + std::vector> optional64_values = {kInt64Max}; + std::vector wrapping21_values = {(1 << 21) - 1}; + std::vector string_values = {"foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*no deltas*/ 0); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, EqualElements) { + std::vector bool_values = {true, true, true, true}; + std::vector signed32_values = {-2, -2, -2, -2}; + std::vector unsigned32_values = {123456789, 123456789, 123456789, + 123456789}; + std::vector signed64_values = {-9876543210, -9876543210, -9876543210, + -9876543210}; + std::vector unsigned64_values = {9876543210, 9876543210, 9876543210, + 9876543210}; + std::vector> optional32_values = { + kInt32Min, kInt32Min, kInt32Min, kInt32Min}; + std::vector> optional64_values = { + kInt64Max, kInt64Max, kInt64Max, kInt64Max}; + std::vector wrapping21_values = {(1 << 21) - 1, (1 << 21) - 1, + (1 << 21) - 1, (1 << 21) - 1}; + std::vector string_values = {"foo", "foo", "foo", "foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*no deltas*/ 0); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, Increasing) { + std::vector bool_values = {false, true, false, true}; + std::vector signed32_values = {-2, -1, 0, 1}; + std::vector unsigned32_values = {kUint32Max - 1, kUint32Max, 0, 1}; + std::vector signed64_values = {kInt64Max - 1, kInt64Max, kInt64Min, + kInt64Min + 1}; + std::vector unsigned64_values = {kUint64Max - 1, kUint64Max, 0, 1}; + std::vector> optional32_values = { + kInt32Max - 1, kInt32Max, kInt32Min, kInt32Min + 1}; + std::vector> optional64_values = { + kInt64Max - 1, kInt64Max, kInt64Min, kInt64Min + 1}; + std::vector wrapping21_values = {(1 << 21) - 2, (1 << 21) - 1, 0, + 1}; + std::vector string_values = { + "", "a", "bc", "def"}; // No special compression of strings. + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 1); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, Decreasing) { + std::vector bool_values = {true, false, true, false}; + std::vector signed32_values = {2, 1, 0, -1}; + std::vector unsigned32_values = {1, 0, kUint32Max, kUint32Max - 1}; + std::vector signed64_values = {kInt64Min + 1, kInt64Min, kInt64Max, + kInt64Max - 1}; + std::vector unsigned64_values = {1, 0, kUint64Max, kUint64Max - 1}; + std::vector> optional32_values = { + kInt32Min + 1, kInt32Min, kInt32Max, kInt32Max - 1}; + std::vector> optional64_values = { + kInt64Min + 1, kInt64Min, kInt64Max, kInt64Max - 1}; + std::vector wrapping21_values = {1, 0, (1 << 21) - 1, + (1 << 21) - 2}; + std::vector string_values = { + "def", "bc", "a", ""}; // No special compression of strings. + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 1); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, SkipsDeprecatedFields) { + // Expect parser to skip fields it doesn't recognize, but find subsequent + // fields. + std::vector bool_values = {true, false}; + std::vector signed32_values = {kInt32Min / 2, kInt32Max / 2}; + std::vector unsigned32_values = {0, kUint32Max / 2}; + std::vector signed64_values = {kInt64Min / 2, kInt64Max / 2}; + std::vector unsigned64_values = {0, kUint64Max / 2}; + std::vector> optional32_values = {kInt32Max / 2, + kInt32Min / 2}; + std::vector> optional64_values = {kInt64Min / 2, + kInt64Max / 2}; + std::vector wrapping21_values = {0, 1 << 20}; + std::vector string_values = {"foo", "bar"}; + + size_t signed32_encoding_size = + /*tag*/ 1 + /* varint base*/ 5 + /* delta_header*/ 1 + /*deltas*/ 4; + size_t signed64_encoding_size = + /*tag*/ 1 + /* fixed64 base*/ 8 + /* delta_header*/ 1 + /*deltas*/ 8; + size_t optional32_encoding_size = + /*tag*/ 1 + /* fixed32 base*/ 4 + /* delta_header*/ 1 + /*deltas*/ 4; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta_bits=*/1); + // Skips parsing the `signed32_values`. The following unsigned fields should + // still be found. + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta_bits=*/31, + /*expected_skipped_bytes=*/signed32_encoding_size); + // Skips parsing the `signed64_values`. The following unsigned fields should + // still be found. + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta_bits=*/63, signed64_encoding_size); + // Skips parsing the `optional32_values`. The following unsigned fields should + // still be found. + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, + /*delta_bits=*/63, optional32_encoding_size); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta_bits=*/20); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, SkipsMissingFields) { + // Expect parsing of missing field to succeed but return an empty list. + + std::vector bool_values = {true, false}; + std::vector signed32_values = {kInt32Min / 2, kInt32Max / 2}; + std::vector unsigned32_values = {0, kUint32Max / 2}; + std::vector signed64_values = {kInt64Min / 2, kInt64Max / 2}; + std::vector unsigned64_values = {0, kUint64Max / 2}; + std::vector> optional32_values = {kInt32Max / 2, + kInt32Min / 2}; + std::vector> optional64_values = {kInt64Min / 2, + kInt64Max / 2}; + std::vector wrapping21_values = {0, 1 << 20}; + std::vector string_values = {"foo", "foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + // Skip encoding the `bool_values`. + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + // Skip encoding the `unsigned32_values`. + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + // Skip encoding the `unsigned64_values`. + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + // Skip encoding the `optional64_values`. + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyMissingField(RtcTestEvent::bool_params); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta_bits=*/31); + ParseAndVerifyMissingField(RtcTestEvent::unsigned32_params); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta_bits=*/63); + ParseAndVerifyMissingField(RtcTestEvent::unsigned64_params); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta_bits=*/31); + ParseAndVerifyMissingOptionalField(RtcTestEvent::optional64_params); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta_bits=*/20); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, OptionalFields) { + std::vector> optional32_values = { + 2, absl::nullopt, 4, absl::nullopt, 6, absl::nullopt}; + std::vector> optional64_values = { + absl::nullopt, 1024, absl::nullopt, 1025, absl::nullopt, 1026}; + std::vector wrapping21_values = {(1 << 21) - 3, 0, 2, 5, 5, 6}; + + for (size_t i = 0; i < optional32_values.size(); i++) { + batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], + optional64_values[i], + wrapping21_values[i], "")); + } + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + std::string s = encoder.AsString(); + + // Optional debug output + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 2); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 2); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, AllNulloptTreatedAsMissing) { + std::vector> optional32_values = { + absl::nullopt, absl::nullopt, absl::nullopt, + absl::nullopt, absl::nullopt, absl::nullopt}; + std::vector> optional64_values = { + absl::nullopt, 1024, absl::nullopt, 1025, absl::nullopt, 1026}; + + for (size_t i = 0; i < optional32_values.size(); i++) { + batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], + optional64_values[i], 0, "")); + } + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + std::string s = encoder.AsString(); + + // Optional debug output + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyMissingOptionalField(RtcTestEvent::optional32_params); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta_bits=*/1); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction.cc b/logging/rtc_event_log/events/rtc_event_field_extraction.cc new file mode 100644 index 0000000000..99f0b3697c --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_extraction.cc @@ -0,0 +1,60 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc_event_logging { + +// The bitwidth required to encode values in the range +// [0, `max_pos_magnitude`] using an unsigned representation. +uint8_t UnsignedBitWidth(uint64_t max_magnitude) { + uint8_t required_bits = 1; + while (max_magnitude >>= 1) { + ++required_bits; + } + return required_bits; +} + +// The bitwidth required to encode signed values in the range +// [-`max_neg_magnitude`, `max_pos_magnitude`] using a signed +// 2-complement representation. +uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude) { + const uint8_t bitwidth_positive = + max_pos_magnitude > 0 ? UnsignedBitWidth(max_pos_magnitude) : 0; + const uint8_t bitwidth_negative = + (max_neg_magnitude > 1) ? UnsignedBitWidth(max_neg_magnitude - 1) : 0; + return 1 + std::max(bitwidth_positive, bitwidth_negative); +} + +// Return the maximum integer of a given bit width. +uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width) { + RTC_DCHECK_GE(bit_width, 1); + RTC_DCHECK_LE(bit_width, 64); + return (bit_width == 64) ? std::numeric_limits::max() + : ((static_cast(1) << bit_width) - 1); +} + +// Computes the delta between `previous` and `current`, under the assumption +// that `bit_mask` is the largest value before wrap-around occurs. The bitmask +// must be of the form 2^x-1. (We use the wrap-around to more efficiently +// compress counters that wrap around at different bit widths than the +// backing C++ data type.) +uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask) { + RTC_DCHECK_LE(previous, bit_mask); + RTC_DCHECK_LE(current, bit_mask); + return (current - previous) & bit_mask; +} + +} // namespace webrtc_event_logging diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction.h b/logging/rtc_event_log/events/rtc_event_field_extraction.h new file mode 100644 index 0000000000..eb9d67f1c2 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_extraction.h @@ -0,0 +1,84 @@ +/* + * 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 LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "rtc_base/logging.h" + +namespace webrtc_event_logging { +uint8_t UnsignedBitWidth(uint64_t max_magnitude); +uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude); +uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width); +uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask); +} // namespace webrtc_event_logging + +namespace webrtc { +template ::value, bool> = true> +uint64_t EncodeAsUnsigned(T value) { + return webrtc_event_logging::ToUnsigned(value); +} + +template ::value, bool> = true> +uint64_t EncodeAsUnsigned(T value) { + return static_cast(value); +} + +template ::value, bool> = true> +T DecodeFromUnsignedToType(uint64_t value) { + T signed_value = 0; + bool success = webrtc_event_logging::ToSigned(value, &signed_value); + if (!success) { + RTC_LOG(LS_ERROR) << "Failed to convert " << value << "to signed type."; + // TODO(terelius): Propagate error? + } + return signed_value; +} + +template ::value, bool> = true> +T DecodeFromUnsignedToType(uint64_t value) { + // TODO(terelius): Check range? + return static_cast(value); +} + +// RtcEventLogEnum defines a mapping between an enum T +// and the event log encodings. To log a new enum type T, +// specialize RtcEventLogEnum and add static methods +// static uint64_t Encode(T x) {} +// static RtcEventLogParseStatusOr Decode(uint64_t x) {} +template +class RtcEventLogEnum { + static_assert(sizeof(T) != sizeof(T), + "Missing specialisation of RtcEventLogEnum for type"); +}; + +// Represents a vector> optional_values +// as a bit-vector `position_mask` which identifies the positions +// of existing values, and a (potentially shorter) +// `vector values` containing the actual values. +// The bit vector is constructed such that position_mask[i] +// is true iff optional_values[i] has a value, and `values.size()` +// is equal to the number of set bits in `position_mask`. +struct ValuesWithPositions { + std::vector position_mask; + std::vector values; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc b/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc new file mode 100644 index 0000000000..f9fb993af0 --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc @@ -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. + */ + +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +#include "rtc_base/random.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(UnsignedBitWidthTest, SmallValues) { + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(1), 1u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(2), 2u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(3), 2u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(4), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(5), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(6), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(7), 3u); +} + +TEST(UnsignedBitWidthTest, PowersOfTwo) { + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u); + + for (unsigned i = 0; i < 64; i++) { + uint64_t x = 1; + x = x << i; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1); + } +} + +TEST(UnsignedBitWidthTest, PowersOfTwoMinusOne) { + for (unsigned i = 1; i < 64; i++) { + uint64_t x = 1; + x = (x << i) - 1; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i); + } + + uint64_t x = ~static_cast(0); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), 64u); +} + +TEST(UnsignedBitWidthTest, RandomInputs) { + Random rand(12345); + + for (unsigned i = 0; i < 64; i++) { + uint64_t x = 1; + x = x << i; + uint64_t high = rand.Rand(); + uint64_t low = rand.Rand(); + x += ((high << 32) + low) % x; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1); + } +} + +TEST(SignedBitWidthTest, SignedBitWidth) { + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(0, 1), 1u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 0), 2u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 2), 2u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 128), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 1), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 128), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 129), 9u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(128, 1), 9u); +} + +TEST(MaxUnsignedValueOfBitWidthTest, MaxUnsignedValueOfBitWidth) { + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(1), 0x01u); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(6), 0x3Fu); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(8), 0xFFu); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(32), 0xFFFFFFFFu); +} + +TEST(EncodeAsUnsignedTest, NegativeValues) { + // Negative values are converted as if cast to unsigned type of + // the same bitsize using 2-complement representation. + int16_t x = -1; + EXPECT_EQ(EncodeAsUnsigned(x), static_cast(0xFFFF)); + int64_t y = -1; + EXPECT_EQ(EncodeAsUnsigned(y), static_cast(0xFFFFFFFFFFFFFFFFull)); +} + +TEST(EncodeAsUnsignedTest, PositiveValues) { + // Postive values are unchanged. + int16_t x = 42; + EXPECT_EQ(EncodeAsUnsigned(x), static_cast(42)); + int64_t y = 42; + EXPECT_EQ(EncodeAsUnsigned(y), static_cast(42)); +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_frame_decoded.h b/logging/rtc_event_log/events/rtc_event_frame_decoded.h index 4a6bb90d02..91190faea9 100644 --- a/logging/rtc_event_log/events/rtc_event_frame_decoded.h +++ b/logging/rtc_event_log/events/rtc_event_frame_decoded.h @@ -13,14 +13,33 @@ #include +#include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "api/video/video_codec_type.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedFrameDecoded { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t render_time_ms; + uint32_t ssrc; + int width; + int height; + VideoCodecType codec; + uint8_t qp; +}; + class RtcEventFrameDecoded final : public RtcEvent { public: static constexpr Type kType = Type::FrameDecoded; @@ -45,6 +64,19 @@ class RtcEventFrameDecoded final : public RtcEvent { VideoCodecType codec() const { return codec_; } uint8_t qp() const { return qp_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventFrameDecoded(const RtcEventFrameDecoded& other); @@ -56,19 +88,6 @@ class RtcEventFrameDecoded final : public RtcEvent { const uint8_t qp_; }; -struct LoggedFrameDecoded { - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int64_t render_time_ms; - uint32_t ssrc; - int width; - int height; - VideoCodecType codec; - uint8_t qp; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FRAME_DECODED_H_ diff --git a/logging/rtc_event_log/events/rtc_event_generic_ack_received.h b/logging/rtc_event_log/events/rtc_event_generic_ack_received.h index 3cd8f5ccee..57fd7cd9a6 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_ack_received.h +++ b/logging/rtc_event_log/events/rtc_event_generic_ack_received.h @@ -12,14 +12,38 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_ACK_RECEIVED_H_ #include +#include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedGenericAckReceived { + LoggedGenericAckReceived() = default; + LoggedGenericAckReceived(Timestamp timestamp, + int64_t packet_number, + int64_t acked_packet_number, + absl::optional receive_acked_packet_time_ms) + : timestamp(timestamp), + packet_number(packet_number), + acked_packet_number(acked_packet_number), + receive_acked_packet_time_ms(receive_acked_packet_time_ms) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + int64_t acked_packet_number; + absl::optional receive_acked_packet_time_ms; +}; + struct AckedPacket { // The packet number that was acked. int64_t packet_number; @@ -57,6 +81,19 @@ class RtcEventGenericAckReceived final : public RtcEvent { return receive_acked_packet_time_ms_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventGenericAckReceived(const RtcEventGenericAckReceived& packet); @@ -76,26 +113,6 @@ class RtcEventGenericAckReceived final : public RtcEvent { const absl::optional receive_acked_packet_time_ms_; }; -struct LoggedGenericAckReceived { - LoggedGenericAckReceived() = default; - LoggedGenericAckReceived(Timestamp timestamp, - int64_t packet_number, - int64_t acked_packet_number, - absl::optional receive_acked_packet_time_ms) - : timestamp(timestamp), - packet_number(packet_number), - acked_packet_number(acked_packet_number), - receive_acked_packet_time_ms(receive_acked_packet_time_ms) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int64_t packet_number; - int64_t acked_packet_number; - absl::optional receive_acked_packet_time_ms; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_ACK_RECEIVED_H_ diff --git a/logging/rtc_event_log/events/rtc_event_generic_packet_received.h b/logging/rtc_event_log/events/rtc_event_generic_packet_received.h index 428e7b3806..a6006ca4d4 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_packet_received.h +++ b/logging/rtc_event_log/events/rtc_event_generic_packet_received.h @@ -12,12 +12,34 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_RECEIVED_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedGenericPacketReceived { + LoggedGenericPacketReceived() = default; + LoggedGenericPacketReceived(Timestamp timestamp, + int64_t packet_number, + int packet_length) + : timestamp(timestamp), + packet_number(packet_number), + packet_length(packet_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + int packet_length; +}; + class RtcEventGenericPacketReceived final : public RtcEvent { public: static constexpr Type kType = Type::GenericPacketReceived; @@ -37,6 +59,19 @@ class RtcEventGenericPacketReceived final : public RtcEvent { // including ICE/TURN/IP overheads. size_t packet_length() const { return packet_length_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventGenericPacketReceived(const RtcEventGenericPacketReceived& packet); @@ -44,23 +79,6 @@ class RtcEventGenericPacketReceived final : public RtcEvent { const size_t packet_length_; }; -struct LoggedGenericPacketReceived { - LoggedGenericPacketReceived() = default; - LoggedGenericPacketReceived(Timestamp timestamp, - int64_t packet_number, - int packet_length) - : timestamp(timestamp), - packet_number(packet_number), - packet_length(packet_length) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int64_t packet_number; - int packet_length; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_RECEIVED_H_ diff --git a/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h b/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h index 6e626e63a1..903950a398 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h +++ b/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h @@ -12,12 +12,43 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_SENT_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedGenericPacketSent { + LoggedGenericPacketSent() = default; + LoggedGenericPacketSent(Timestamp timestamp, + int64_t packet_number, + size_t overhead_length, + size_t payload_length, + size_t padding_length) + : timestamp(timestamp), + packet_number(packet_number), + overhead_length(overhead_length), + payload_length(payload_length), + padding_length(padding_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + size_t packet_length() const { + return payload_length + padding_length + overhead_length; + } + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + size_t overhead_length; + size_t payload_length; + size_t padding_length; +}; + class RtcEventGenericPacketSent final : public RtcEvent { public: static constexpr Type kType = Type::GenericPacketSent; @@ -52,6 +83,19 @@ class RtcEventGenericPacketSent final : public RtcEvent { size_t padding_length() const { return padding_length_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventGenericPacketSent(const RtcEventGenericPacketSent& packet); @@ -61,31 +105,6 @@ class RtcEventGenericPacketSent final : public RtcEvent { const size_t padding_length_; }; -struct LoggedGenericPacketSent { - LoggedGenericPacketSent() = default; - LoggedGenericPacketSent(Timestamp timestamp, - int64_t packet_number, - size_t overhead_length, - size_t payload_length, - size_t padding_length) - : timestamp(timestamp), - packet_number(packet_number), - overhead_length(overhead_length), - payload_length(payload_length), - padding_length(padding_length) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - size_t packet_length() const { - return payload_length + padding_length + overhead_length; - } - Timestamp timestamp = Timestamp::MinusInfinity(); - int64_t packet_number; - size_t overhead_length; - size_t payload_length; - size_t padding_length; -}; } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_SENT_H_ diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h index 1f4d825a99..bdacf15a59 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h @@ -14,9 +14,13 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { @@ -28,6 +32,27 @@ enum class IceCandidatePairEventType { kNumValues, }; +struct LoggedIceCandidatePairEvent { + LoggedIceCandidatePairEvent() = default; + LoggedIceCandidatePairEvent(Timestamp timestamp, + IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id) + : timestamp(timestamp), + type(type), + candidate_pair_id(candidate_pair_id), + transaction_id(transaction_id) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + IceCandidatePairEventType type; + uint32_t candidate_pair_id; + uint32_t transaction_id; +}; + class RtcEventIceCandidatePair final : public RtcEvent { public: static constexpr Type kType = Type::IceCandidatePairEvent; @@ -47,6 +72,19 @@ class RtcEventIceCandidatePair final : public RtcEvent { uint32_t candidate_pair_id() const { return candidate_pair_id_; } uint32_t transaction_id() const { return transaction_id_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventIceCandidatePair(const RtcEventIceCandidatePair& other); @@ -55,26 +93,6 @@ class RtcEventIceCandidatePair final : public RtcEvent { const uint32_t transaction_id_; }; -struct LoggedIceCandidatePairEvent { - LoggedIceCandidatePairEvent() = default; - LoggedIceCandidatePairEvent(Timestamp timestamp, - IceCandidatePairEventType type, - uint32_t candidate_pair_id, - uint32_t transaction_id) - : timestamp(timestamp), - type(type), - candidate_pair_id(candidate_pair_id), - transaction_id(transaction_id) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - IceCandidatePairEventType type; - uint32_t candidate_pair_id; - uint32_t transaction_id; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_H_ diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h index 465a799780..e72d999cff 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h @@ -14,9 +14,13 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { @@ -65,6 +69,23 @@ enum class IceCandidateNetworkType { kNumValues, }; +struct LoggedIceCandidatePairConfig { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + IceCandidatePairConfigType type; + uint32_t candidate_pair_id; + IceCandidateType local_candidate_type; + IceCandidatePairProtocol local_relay_protocol; + IceCandidateNetworkType local_network_type; + IceCandidatePairAddressFamily local_address_family; + IceCandidateType remote_candidate_type; + IceCandidatePairAddressFamily remote_address_family; + IceCandidatePairProtocol candidate_pair_protocol; +}; + class IceCandidatePairDescription { public: IceCandidatePairDescription(); @@ -105,6 +126,19 @@ class RtcEventIceCandidatePairConfig final : public RtcEvent { return candidate_pair_desc_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventIceCandidatePairConfig(const RtcEventIceCandidatePairConfig& other); @@ -113,22 +147,6 @@ class RtcEventIceCandidatePairConfig final : public RtcEvent { const IceCandidatePairDescription candidate_pair_desc_; }; -struct LoggedIceCandidatePairConfig { - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - IceCandidatePairConfigType type; - uint32_t candidate_pair_id; - IceCandidateType local_candidate_type; - IceCandidatePairProtocol local_relay_protocol; - IceCandidateNetworkType local_network_type; - IceCandidatePairAddressFamily local_address_family; - IceCandidateType remote_candidate_type; - IceCandidatePairAddressFamily remote_address_family; - IceCandidatePairProtocol candidate_pair_protocol; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_CONFIG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h index 974a0c9a5c..ae6810c39d 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h +++ b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h @@ -14,12 +14,40 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedBweProbeClusterCreatedEvent { + LoggedBweProbeClusterCreatedEvent() = default; + LoggedBweProbeClusterCreatedEvent(Timestamp timestamp, + int32_t id, + int32_t bitrate_bps, + uint32_t min_packets, + uint32_t min_bytes) + : timestamp(timestamp), + id(id), + bitrate_bps(bitrate_bps), + min_packets(min_packets), + min_bytes(min_bytes) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + int32_t bitrate_bps; + uint32_t min_packets; + uint32_t min_bytes; +}; + class RtcEventProbeClusterCreated final : public RtcEvent { public: static constexpr Type kType = Type::ProbeClusterCreated; @@ -40,6 +68,19 @@ class RtcEventProbeClusterCreated final : public RtcEvent { uint32_t min_probes() const { return min_probes_; } uint32_t min_bytes() const { return min_bytes_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventProbeClusterCreated(const RtcEventProbeClusterCreated& other); @@ -49,29 +90,6 @@ class RtcEventProbeClusterCreated final : public RtcEvent { const uint32_t min_bytes_; }; -struct LoggedBweProbeClusterCreatedEvent { - LoggedBweProbeClusterCreatedEvent() = default; - LoggedBweProbeClusterCreatedEvent(Timestamp timestamp, - int32_t id, - int32_t bitrate_bps, - uint32_t min_packets, - uint32_t min_bytes) - : timestamp(timestamp), - id(id), - bitrate_bps(bitrate_bps), - min_packets(min_packets), - min_bytes(min_bytes) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int32_t id; - int32_t bitrate_bps; - uint32_t min_packets; - uint32_t min_bytes; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_CLUSTER_CREATED_H_ diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h index fa61b314b4..1aa6e75cb7 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h @@ -14,9 +14,13 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { @@ -27,6 +31,22 @@ enum class ProbeFailureReason { kLast }; +struct LoggedBweProbeFailureEvent { + LoggedBweProbeFailureEvent() = default; + LoggedBweProbeFailureEvent(Timestamp timestamp, + int32_t id, + ProbeFailureReason failure_reason) + : timestamp(timestamp), id(id), failure_reason(failure_reason) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + ProbeFailureReason failure_reason; +}; + class RtcEventProbeResultFailure final : public RtcEvent { public: static constexpr Type kType = Type::ProbeResultFailure; @@ -42,6 +62,19 @@ class RtcEventProbeResultFailure final : public RtcEvent { int32_t id() const { return id_; } ProbeFailureReason failure_reason() const { return failure_reason_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventProbeResultFailure(const RtcEventProbeResultFailure& other); @@ -49,21 +82,6 @@ class RtcEventProbeResultFailure final : public RtcEvent { const ProbeFailureReason failure_reason_; }; -struct LoggedBweProbeFailureEvent { - LoggedBweProbeFailureEvent() = default; - LoggedBweProbeFailureEvent(Timestamp timestamp, - int32_t id, - ProbeFailureReason failure_reason) - : timestamp(timestamp), id(id), failure_reason(failure_reason) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int32_t id; - ProbeFailureReason failure_reason; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_FAILURE_H_ diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_success.h b/logging/rtc_event_log/events/rtc_event_probe_result_success.h index d00cfa81d6..49d1abec5a 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_success.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_success.h @@ -14,12 +14,32 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedBweProbeSuccessEvent { + LoggedBweProbeSuccessEvent() = default; + LoggedBweProbeSuccessEvent(Timestamp timestamp, + int32_t id, + int32_t bitrate_bps) + : timestamp(timestamp), id(id), bitrate_bps(bitrate_bps) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + int32_t bitrate_bps; +}; + class RtcEventProbeResultSuccess final : public RtcEvent { public: static constexpr Type kType = Type::ProbeResultSuccess; @@ -35,6 +55,19 @@ class RtcEventProbeResultSuccess final : public RtcEvent { int32_t id() const { return id_; } int32_t bitrate_bps() const { return bitrate_bps_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventProbeResultSuccess(const RtcEventProbeResultSuccess& other); @@ -42,21 +75,6 @@ class RtcEventProbeResultSuccess final : public RtcEvent { const int32_t bitrate_bps_; }; -struct LoggedBweProbeSuccessEvent { - LoggedBweProbeSuccessEvent() = default; - LoggedBweProbeSuccessEvent(Timestamp timestamp, - int32_t id, - int32_t bitrate_bps) - : timestamp(timestamp), id(id), bitrate_bps(bitrate_bps) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - int32_t id; - int32_t bitrate_bps; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_SUCCESS_H_ diff --git a/logging/rtc_event_log/events/rtc_event_remote_estimate.h b/logging/rtc_event_log/events/rtc_event_remote_estimate.h index 956e05f682..4a39ecc597 100644 --- a/logging/rtc_event_log/events/rtc_event_remote_estimate.h +++ b/logging/rtc_event_log/events/rtc_event_remote_estimate.h @@ -11,14 +11,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_REMOTE_ESTIMATE_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/data_rate.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedRemoteEstimateEvent { + LoggedRemoteEstimateEvent() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + absl::optional link_capacity_lower; + absl::optional link_capacity_upper; +}; + class RtcEventRemoteEstimate final : public RtcEvent { public: static constexpr Type kType = Type::RemoteEstimateEvent; @@ -31,19 +47,22 @@ class RtcEventRemoteEstimate final : public RtcEvent { Type GetType() const override { return kType; } bool IsConfigEvent() const override { return false; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + const DataRate link_capacity_lower_; const DataRate link_capacity_upper_; }; -struct LoggedRemoteEstimateEvent { - LoggedRemoteEstimateEvent() = default; - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - absl::optional link_capacity_lower; - absl::optional link_capacity_upper; -}; } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_REMOTE_ESTIMATE_H_ diff --git a/logging/rtc_event_log/events/rtc_event_route_change.h b/logging/rtc_event_log/events/rtc_event_route_change.h index 4a4e9aef80..bc1461d7bb 100644 --- a/logging/rtc_event_log/events/rtc_event_route_change.h +++ b/logging/rtc_event_log/events/rtc_event_route_change.h @@ -12,12 +12,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ROUTE_CHANGE_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" namespace webrtc { +struct LoggedRouteChangeEvent { + LoggedRouteChangeEvent() = default; + LoggedRouteChangeEvent(Timestamp timestamp, bool connected, uint32_t overhead) + : timestamp(timestamp), connected(connected), overhead(overhead) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool connected; + uint32_t overhead; +}; + class RtcEventRouteChange final : public RtcEvent { public: static constexpr Type kType = Type::RouteChangeEvent; @@ -33,6 +51,19 @@ class RtcEventRouteChange final : public RtcEvent { bool connected() const { return connected_; } uint32_t overhead() const { return overhead_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventRouteChange(const RtcEventRouteChange& other); @@ -40,18 +71,5 @@ class RtcEventRouteChange final : public RtcEvent { const uint32_t overhead_; }; -struct LoggedRouteChangeEvent { - LoggedRouteChangeEvent() = default; - LoggedRouteChangeEvent(Timestamp timestamp, bool connected, uint32_t overhead) - : timestamp(timestamp), connected(connected), overhead(overhead) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - bool connected; - uint32_t overhead; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ROUTE_CHANGE_H_ diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h index 1cbac7712f..84fe398e08 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h @@ -14,9 +14,14 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "rtc_base/buffer.h" namespace webrtc { @@ -35,6 +40,19 @@ class RtcEventRtcpPacketIncoming final : public RtcEvent { const rtc::Buffer& packet() const { return packet_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventRtcpPacketIncoming(const RtcEventRtcpPacketIncoming& other); diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h index 0ecccbeaae..687bd319b4 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h @@ -14,9 +14,14 @@ #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "rtc_base/buffer.h" namespace webrtc { @@ -35,6 +40,19 @@ class RtcEventRtcpPacketOutgoing final : public RtcEvent { const rtc::Buffer& packet() const { return packet_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventRtcpPacketOutgoing(const RtcEventRtcpPacketOutgoing& other); diff --git a/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h b/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h index ee48fa360b..926ddddff5 100644 --- a/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h +++ b/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h @@ -13,11 +13,17 @@ #include #include +#include #include +#include #include +#include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "modules/rtp_rtcp/source/rtp_packet.h" namespace webrtc { @@ -59,6 +65,19 @@ class RtcEventRtpPacketIncoming final : public RtcEvent { size_t header_length() const { return packet_.headers_size(); } size_t padding_length() const { return packet_.padding_size(); } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventRtpPacketIncoming(const RtcEventRtpPacketIncoming& other); diff --git a/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h b/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h index 626c094ca9..c7b7a09718 100644 --- a/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h +++ b/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h @@ -13,11 +13,17 @@ #include #include +#include #include +#include #include +#include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "modules/rtp_rtcp/source/rtp_packet.h" namespace webrtc { @@ -61,6 +67,19 @@ class RtcEventRtpPacketOutgoing final : public RtcEvent { size_t padding_length() const { return packet_.padding_size(); } int probe_cluster_id() const { return probe_cluster_id_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventRtpPacketOutgoing(const RtcEventRtpPacketOutgoing& other); diff --git a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h index e7b9061872..0be56c2065 100644 --- a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h @@ -12,13 +12,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_RECEIVE_STREAM_CONFIG_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "logging/rtc_event_log/rtc_stream_config.h" namespace webrtc { +struct LoggedVideoRecvConfig { + LoggedVideoRecvConfig() = default; + LoggedVideoRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + class RtcEventVideoReceiveStreamConfig final : public RtcEvent { public: static constexpr Type kType = Type::VideoReceiveStreamConfig; @@ -34,6 +51,19 @@ class RtcEventVideoReceiveStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventVideoReceiveStreamConfig( const RtcEventVideoReceiveStreamConfig& other); @@ -41,18 +71,6 @@ class RtcEventVideoReceiveStreamConfig final : public RtcEvent { const std::unique_ptr config_; }; -struct LoggedVideoRecvConfig { - LoggedVideoRecvConfig() = default; - LoggedVideoRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) - : timestamp(timestamp), config(config) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtclog::StreamConfig config; -}; - } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_RECEIVE_STREAM_CONFIG_H_ diff --git a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h index e72e75e49d..f1717b19ea 100644 --- a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h @@ -12,13 +12,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_SEND_STREAM_CONFIG_H_ #include +#include +#include +#include "absl/strings/string_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" #include "logging/rtc_event_log/rtc_stream_config.h" namespace webrtc { +struct LoggedVideoSendConfig { + LoggedVideoSendConfig() = default; + LoggedVideoSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + class RtcEventVideoSendStreamConfig final : public RtcEvent { public: static constexpr Type kType = Type::VideoSendStreamConfig; @@ -34,23 +51,25 @@ class RtcEventVideoSendStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } + static std::string Encode(rtc::ArrayView batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + private: RtcEventVideoSendStreamConfig(const RtcEventVideoSendStreamConfig& other); const std::unique_ptr config_; }; -struct LoggedVideoSendConfig { - LoggedVideoSendConfig() = default; - LoggedVideoSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) - : timestamp(timestamp), config(config) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtclog::StreamConfig config; -}; } // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_SEND_STREAM_CONFIG_H_ diff --git a/logging/rtc_event_log/logged_events.cc b/logging/rtc_event_log/logged_events.cc deleted file mode 100644 index 5ef3de11c0..0000000000 --- a/logging/rtc_event_log/logged_events.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "logging/rtc_event_log/logged_events.h" - -namespace webrtc { - -LoggedPacketInfo::LoggedPacketInfo(const LoggedRtpPacket& rtp, - LoggedMediaType media_type, - bool rtx, - Timestamp capture_time) - : ssrc(rtp.header.ssrc), - stream_seq_no(rtp.header.sequenceNumber), - size(static_cast(rtp.total_length)), - payload_size(static_cast(rtp.total_length - - rtp.header.paddingLength - - rtp.header.headerLength)), - padding_size(static_cast(rtp.header.paddingLength)), - payload_type(rtp.header.payloadType), - media_type(media_type), - rtx(rtx), - marker_bit(rtp.header.markerBit), - has_transport_seq_no(rtp.header.extension.hasTransportSequenceNumber), - transport_seq_no(static_cast( - has_transport_seq_no ? rtp.header.extension.transportSequenceNumber - : 0)), - capture_time(capture_time), - log_packet_time(Timestamp::Micros(rtp.log_time_us())), - reported_send_time(rtp.header.extension.hasAbsoluteSendTime - ? rtp.header.extension.GetAbsoluteSendTimestamp() - : Timestamp::MinusInfinity()) {} - -LoggedPacketInfo::LoggedPacketInfo(const LoggedPacketInfo&) = default; - -LoggedPacketInfo::~LoggedPacketInfo() {} - -LoggedRtcpPacket::LoggedRtcpPacket(Timestamp timestamp, - const std::vector& packet) - : timestamp(timestamp), raw_data(packet) {} - -LoggedRtcpPacket::LoggedRtcpPacket(Timestamp timestamp, - const std::string& packet) - : timestamp(timestamp), raw_data(packet.size()) { - memcpy(raw_data.data(), packet.data(), packet.size()); -} - -LoggedRtcpPacket::LoggedRtcpPacket(const LoggedRtcpPacket& rhs) = default; - -LoggedRtcpPacket::~LoggedRtcpPacket() = default; - -} // namespace webrtc diff --git a/logging/rtc_event_log/logged_events.h b/logging/rtc_event_log/logged_events.h index 5bce658c30..d6b3cc607e 100644 --- a/logging/rtc_event_log/logged_events.h +++ b/logging/rtc_event_log/logged_events.h @@ -7,337 +7,12 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ + #ifndef LOGGING_RTC_EVENT_LOG_LOGGED_EVENTS_H_ #define LOGGING_RTC_EVENT_LOG_LOGGED_EVENTS_H_ -#include -#include +// TODO(terelius): Delete this forwarding header when downstream +// projects have been updated. +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" -#include "absl/types/optional.h" -#include "api/rtp_headers.h" -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" -#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" -#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" -#include "modules/rtp_rtcp/source/rtcp_packet/fir.h" -#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h" -#include "modules/rtp_rtcp/source/rtcp_packet/nack.h" -#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" -#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" -#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" -#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" -#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" - -namespace webrtc { - -// The different event types are deliberately POD. Analysis of large logs is -// already resource intensive. The code simplifications that would be possible -// possible by having a base class (containing e.g. the log time) are not -// considered to outweigh the added memory and runtime overhead incurred by -// adding a vptr. - -struct LoggedRtpPacket { - LoggedRtpPacket(Timestamp timestamp, - RTPHeader header, - size_t header_length, - size_t total_length) - : timestamp(timestamp), - header(header), - header_length(header_length), - total_length(total_length) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp; - // TODO(terelius): This allocates space for 15 CSRCs even if none are used. - RTPHeader header; - size_t header_length; - size_t total_length; -}; - -struct LoggedRtpPacketIncoming { - LoggedRtpPacketIncoming(Timestamp timestamp, - RTPHeader header, - size_t header_length, - size_t total_length) - : rtp(timestamp, header, header_length, total_length) {} - int64_t log_time_us() const { return rtp.timestamp.us(); } - int64_t log_time_ms() const { return rtp.timestamp.ms(); } - - LoggedRtpPacket rtp; -}; - -struct LoggedRtpPacketOutgoing { - LoggedRtpPacketOutgoing(Timestamp timestamp, - RTPHeader header, - size_t header_length, - size_t total_length) - : rtp(timestamp, header, header_length, total_length) {} - int64_t log_time_us() const { return rtp.timestamp.us(); } - int64_t log_time_ms() const { return rtp.timestamp.ms(); } - - LoggedRtpPacket rtp; -}; - -struct LoggedRtcpPacket { - LoggedRtcpPacket(Timestamp timestamp, const std::vector& packet); - LoggedRtcpPacket(Timestamp timestamp, const std::string& packet); - LoggedRtcpPacket(const LoggedRtcpPacket&); - ~LoggedRtcpPacket(); - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp; - std::vector raw_data; -}; - -struct LoggedRtcpPacketIncoming { - LoggedRtcpPacketIncoming(Timestamp timestamp, - const std::vector& packet) - : rtcp(timestamp, packet) {} - LoggedRtcpPacketIncoming(Timestamp timestamp, const std::string& packet) - : rtcp(timestamp, packet) {} - - int64_t log_time_us() const { return rtcp.timestamp.us(); } - int64_t log_time_ms() const { return rtcp.timestamp.ms(); } - - LoggedRtcpPacket rtcp; -}; - -struct LoggedRtcpPacketOutgoing { - LoggedRtcpPacketOutgoing(Timestamp timestamp, - const std::vector& packet) - : rtcp(timestamp, packet) {} - LoggedRtcpPacketOutgoing(Timestamp timestamp, const std::string& packet) - : rtcp(timestamp, packet) {} - - int64_t log_time_us() const { return rtcp.timestamp.us(); } - int64_t log_time_ms() const { return rtcp.timestamp.ms(); } - - LoggedRtcpPacket rtcp; -}; - -struct LoggedRtcpPacketReceiverReport { - LoggedRtcpPacketReceiverReport() = default; - LoggedRtcpPacketReceiverReport(Timestamp timestamp, - const rtcp::ReceiverReport& rr) - : timestamp(timestamp), rr(rr) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::ReceiverReport rr; -}; - -struct LoggedRtcpPacketSenderReport { - LoggedRtcpPacketSenderReport() = default; - LoggedRtcpPacketSenderReport(Timestamp timestamp, - const rtcp::SenderReport& sr) - : timestamp(timestamp), sr(sr) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::SenderReport sr; -}; - -struct LoggedRtcpPacketExtendedReports { - LoggedRtcpPacketExtendedReports() = default; - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::ExtendedReports xr; -}; - -struct LoggedRtcpPacketRemb { - LoggedRtcpPacketRemb() = default; - LoggedRtcpPacketRemb(Timestamp timestamp, const rtcp::Remb& remb) - : timestamp(timestamp), remb(remb) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::Remb remb; -}; - -struct LoggedRtcpPacketNack { - LoggedRtcpPacketNack() = default; - LoggedRtcpPacketNack(Timestamp timestamp, const rtcp::Nack& nack) - : timestamp(timestamp), nack(nack) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::Nack nack; -}; - -struct LoggedRtcpPacketFir { - LoggedRtcpPacketFir() = default; - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::Fir fir; -}; - -struct LoggedRtcpPacketPli { - LoggedRtcpPacketPli() = default; - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::Pli pli; -}; - -struct LoggedRtcpPacketTransportFeedback { - LoggedRtcpPacketTransportFeedback() - : transport_feedback(/*include_timestamps=*/true, /*include_lost*/ true) { - } - LoggedRtcpPacketTransportFeedback( - Timestamp timestamp, - const rtcp::TransportFeedback& transport_feedback) - : timestamp(timestamp), transport_feedback(transport_feedback) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::TransportFeedback transport_feedback; -}; - -struct LoggedRtcpPacketLossNotification { - LoggedRtcpPacketLossNotification() = default; - LoggedRtcpPacketLossNotification( - Timestamp timestamp, - const rtcp::LossNotification& loss_notification) - : timestamp(timestamp), loss_notification(loss_notification) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::LossNotification loss_notification; -}; - -struct LoggedRtcpPacketBye { - LoggedRtcpPacketBye() = default; - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - rtcp::Bye bye; -}; - -struct LoggedStartEvent { - explicit LoggedStartEvent(Timestamp timestamp) - : LoggedStartEvent(timestamp, timestamp) {} - - LoggedStartEvent(Timestamp timestamp, Timestamp utc_start_time) - : timestamp(timestamp), utc_start_time(utc_start_time) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp utc_time() const { return utc_start_time; } - - Timestamp timestamp; - Timestamp utc_start_time; -}; - -struct LoggedStopEvent { - explicit LoggedStopEvent(Timestamp timestamp) : timestamp(timestamp) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp; -}; - -struct InferredRouteChangeEvent { - int64_t log_time_ms() const { return log_time.ms(); } - int64_t log_time_us() const { return log_time.us(); } - uint32_t route_id; - Timestamp log_time = Timestamp::MinusInfinity(); - uint16_t send_overhead; - uint16_t return_overhead; -}; - -enum class LoggedMediaType : uint8_t { kUnknown, kAudio, kVideo }; - -struct LoggedPacketInfo { - LoggedPacketInfo(const LoggedRtpPacket& rtp, - LoggedMediaType media_type, - bool rtx, - Timestamp capture_time); - LoggedPacketInfo(const LoggedPacketInfo&); - ~LoggedPacketInfo(); - int64_t log_time_ms() const { return log_packet_time.ms(); } - int64_t log_time_us() const { return log_packet_time.us(); } - uint32_t ssrc; - uint16_t stream_seq_no; - uint16_t size; - uint16_t payload_size; - uint16_t padding_size; - uint16_t overhead = 0; - uint8_t payload_type; - LoggedMediaType media_type = LoggedMediaType::kUnknown; - bool rtx = false; - bool marker_bit = false; - bool has_transport_seq_no = false; - bool last_in_feedback = false; - uint16_t transport_seq_no = 0; - // The RTP header timestamp unwrapped and converted from tick count to seconds - // based timestamp. - Timestamp capture_time; - // The time the packet was logged. This is the receive time for incoming - // packets and send time for outgoing. - Timestamp log_packet_time; - // Send time as reported by abs-send-time extension, For outgoing packets this - // corresponds to log_packet_time, but might be measured using another clock. - Timestamp reported_send_time; - // The receive time that was reported in feedback. For incoming packets this - // corresponds to log_packet_time, but might be measured using another clock. - // PlusInfinity indicates that the packet was lost. - Timestamp reported_recv_time = Timestamp::MinusInfinity(); - // The time feedback message was logged. This is the feedback send time for - // incoming packets and feedback receive time for outgoing. - // PlusInfinity indicates that feedback was expected but not received. - Timestamp log_feedback_time = Timestamp::MinusInfinity(); - // The delay betweeen receiving an RTP packet and sending feedback for - // incoming packets. For outgoing packets we don't know the feedback send - // time, and this is instead calculated as the difference in reported receive - // time between this packet and the last packet in the same feedback message. - TimeDelta feedback_hold_duration = TimeDelta::MinusInfinity(); -}; - -enum class LoggedIceEventType { - kAdded, - kUpdated, - kDestroyed, - kSelected, - kCheckSent, - kCheckReceived, - kCheckResponseSent, - kCheckResponseReceived, -}; - -struct LoggedIceEvent { - uint32_t candidate_pair_id; - Timestamp log_time; - LoggedIceEventType event_type; -}; - - -} // namespace webrtc #endif // LOGGING_RTC_EVENT_LOG_LOGGED_EVENTS_H_ diff --git a/logging/rtc_event_log/rtc_event_log_impl.cc b/logging/rtc_event_log/rtc_event_log_impl.cc index 38cf5f7941..0766b4a884 100644 --- a/logging/rtc_event_log/rtc_event_log_impl.cc +++ b/logging/rtc_event_log/rtc_event_log_impl.cc @@ -22,7 +22,6 @@ #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/event.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -40,15 +39,15 @@ std::unique_ptr CreateEncoder( RtcEventLog::EncodingType type) { switch (type) { case RtcEventLog::EncodingType::Legacy: - RTC_LOG(LS_INFO) << "Creating legacy encoder for RTC event log."; + RTC_DLOG(LS_INFO) << "Creating legacy encoder for RTC event log."; return std::make_unique(); case RtcEventLog::EncodingType::NewFormat: - RTC_LOG(LS_INFO) << "Creating new format encoder for RTC event log."; + RTC_DLOG(LS_INFO) << "Creating new format encoder for RTC event log."; return std::make_unique(); default: RTC_LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type) << ")"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return std::unique_ptr(nullptr); } } @@ -92,8 +91,7 @@ bool RtcEventLogImpl::StartLogging(std::unique_ptr output, const int64_t timestamp_us = rtc::TimeMillis() * 1000; const int64_t utc_time_us = rtc::TimeUTCMillis() * 1000; - RTC_LOG(LS_INFO) << "Starting WebRTC event log. (Timestamp, UTC) = " - "(" + RTC_LOG(LS_INFO) << "Starting WebRTC event log. (Timestamp, UTC) = (" << timestamp_us << ", " << utc_time_us << ")."; RTC_DCHECK_RUN_ON(&logging_state_checker_); @@ -114,7 +112,7 @@ bool RtcEventLogImpl::StartLogging(std::unique_ptr output, } void RtcEventLogImpl::StopLogging() { - RTC_LOG(LS_INFO) << "Stopping WebRTC event log."; + RTC_DLOG(LS_INFO) << "Stopping WebRTC event log."; // TODO(danilchap): Do not block current thread waiting on the task queue. // It might work for now, for current callers, but disallows caller to share // threads with the `task_queue_`. @@ -122,7 +120,7 @@ void RtcEventLogImpl::StopLogging() { StopLogging([&output_stopped]() { output_stopped.Set(); }); output_stopped.Wait(rtc::Event::kForever); - RTC_LOG(LS_INFO) << "WebRTC event log successfully stopped."; + RTC_DLOG(LS_INFO) << "WebRTC event log successfully stopped."; } void RtcEventLogImpl::StopLogging(std::function callback) { diff --git a/logging/rtc_event_log/rtc_event_log_parser.cc b/logging/rtc_event_log/rtc_event_log_parser.cc index 6ae4bdee4a..b4392e8088 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.cc +++ b/logging/rtc_event_log/rtc_event_log_parser.cc @@ -28,6 +28,7 @@ #include "logging/rtc_event_log/encoder/delta_encoding.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" #include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/rtc_event_processor.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "modules/include/module_common_types_public.h" @@ -53,6 +54,12 @@ return ParsedRtcEventLog::ParseStatus::Error(#X, __FILE__, __LINE__); \ } while (0) +#define RTC_PARSE_CHECK_OR_RETURN_MESSAGE(X, M) \ + do { \ + if (!(X)) \ + return ParsedRtcEventLog::ParseStatus::Error((M), __FILE__, __LINE__); \ + } while (0) + #define RTC_PARSE_CHECK_OR_RETURN_OP(OP, X, Y) \ do { \ if (!((X)OP(Y))) \ @@ -174,7 +181,7 @@ RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) { case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE: return RtcpMode::kReducedSize; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return RtcpMode::kOff; } @@ -188,7 +195,7 @@ BandwidthUsage GetRuntimeDetectorState( case rtclog::DelayBasedBweUpdate::BWE_OVERUSING: return BandwidthUsage::kBwOverusing; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return BandwidthUsage::kBwNormal; } @@ -204,7 +211,7 @@ IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( case rtclog::IceCandidatePairConfig::SELECTED: return IceCandidatePairConfigType::kSelected; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairConfigType::kAdded; } @@ -222,7 +229,7 @@ IceCandidateType GetRuntimeIceCandidateType( case rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: return IceCandidateType::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidateType::kUnknown; } @@ -240,7 +247,7 @@ IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( case rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL: return IceCandidatePairProtocol::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairProtocol::kUnknown; } @@ -254,7 +261,7 @@ IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( case rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: return IceCandidatePairAddressFamily::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairAddressFamily::kUnknown; } @@ -274,7 +281,7 @@ IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( case rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: return IceCandidateNetworkType::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidateNetworkType::kUnknown; } @@ -290,7 +297,7 @@ IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( case rtclog::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED: return IceCandidatePairEventType::kCheckResponseReceived; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairEventType::kCheckSent; } @@ -311,7 +318,7 @@ VideoCodecType GetRuntimeCodecType(rtclog2::FrameDecodedEvents::Codec codec) { "VideoCodecType::kVideoCodecMultiplex"; return VideoCodecType::kVideoCodecMultiplex; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return VideoCodecType::kVideoCodecMultiplex; } @@ -768,7 +775,7 @@ BandwidthUsage GetRuntimeDetectorState( case rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return BandwidthUsage::kBwNormal; } @@ -784,7 +791,7 @@ ProbeFailureReason GetRuntimeProbeFailureReason( case rtclog2::BweProbeResultFailure::UNKNOWN: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ProbeFailureReason::kTimeout; } @@ -802,10 +809,10 @@ DtlsTransportState GetRuntimeDtlsTransportState( case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED: return DtlsTransportState::kFailed; case rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return DtlsTransportState::kNumValues; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return DtlsTransportState::kNumValues; } @@ -823,7 +830,7 @@ IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( case rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairConfigType::kAdded; } @@ -841,7 +848,7 @@ IceCandidateType GetRuntimeIceCandidateType( case rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: return IceCandidateType::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidateType::kUnknown; } @@ -859,7 +866,7 @@ IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( case rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL: return IceCandidatePairProtocol::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairProtocol::kUnknown; } @@ -873,7 +880,7 @@ IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( case rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: return IceCandidatePairAddressFamily::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairAddressFamily::kUnknown; } @@ -893,7 +900,7 @@ IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( case rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: return IceCandidateNetworkType::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidateNetworkType::kUnknown; } @@ -911,7 +918,7 @@ IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( case rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return IceCandidatePairEventType::kCheckSent; } @@ -945,6 +952,35 @@ std::vector GetRuntimeRtpHeaderExtensionConfig( } // End of conversion functions. +LoggedPacketInfo::LoggedPacketInfo(const LoggedRtpPacket& rtp, + LoggedMediaType media_type, + bool rtx, + Timestamp capture_time) + : ssrc(rtp.header.ssrc), + stream_seq_no(rtp.header.sequenceNumber), + size(static_cast(rtp.total_length)), + payload_size(static_cast(rtp.total_length - + rtp.header.paddingLength - + rtp.header.headerLength)), + padding_size(static_cast(rtp.header.paddingLength)), + payload_type(rtp.header.payloadType), + media_type(media_type), + rtx(rtx), + marker_bit(rtp.header.markerBit), + has_transport_seq_no(rtp.header.extension.hasTransportSequenceNumber), + transport_seq_no(static_cast( + has_transport_seq_no ? rtp.header.extension.transportSequenceNumber + : 0)), + capture_time(capture_time), + log_packet_time(Timestamp::Micros(rtp.log_time_us())), + reported_send_time(rtp.header.extension.hasAbsoluteSendTime + ? rtp.header.extension.GetAbsoluteSendTimestamp() + : Timestamp::MinusInfinity()) {} + +LoggedPacketInfo::LoggedPacketInfo(const LoggedPacketInfo&) = default; + +LoggedPacketInfo::~LoggedPacketInfo() {} + ParsedRtcEventLog::~ParsedRtcEventLog() = default; ParsedRtcEventLog::LoggedRtpStreamIncoming::LoggedRtpStreamIncoming() = default; @@ -1074,8 +1110,8 @@ void ParsedRtcEventLog::Clear() { last_incoming_rtcp_packet_.clear(); - first_timestamp_ = std::numeric_limits::max(); - last_timestamp_ = std::numeric_limits::min(); + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); first_log_segment_ = LogSegment(0, std::numeric_limits::max()); incoming_rtp_extensions_maps_.clear(); @@ -1171,24 +1207,24 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( const int64_t timestamp_us = incoming.rtcp.timestamp.us(); const uint8_t* packet_begin = incoming.rtcp.raw_data.data(); const uint8_t* packet_end = packet_begin + incoming.rtcp.raw_data.size(); - auto status = StoreRtcpBlocks( + auto store_rtcp_status = StoreRtcpBlocks( timestamp_us, packet_begin, packet_end, &incoming_sr_, &incoming_rr_, &incoming_xr_, &incoming_remb_, &incoming_nack_, &incoming_fir_, &incoming_pli_, &incoming_bye_, &incoming_transport_feedback_, &incoming_loss_notification_); - RTC_RETURN_IF_ERROR(status); + RTC_RETURN_IF_ERROR(store_rtcp_status); } for (const auto& outgoing : outgoing_rtcp_packets_) { const int64_t timestamp_us = outgoing.rtcp.timestamp.us(); const uint8_t* packet_begin = outgoing.rtcp.raw_data.data(); const uint8_t* packet_end = packet_begin + outgoing.rtcp.raw_data.size(); - auto status = StoreRtcpBlocks( + auto store_rtcp_status = StoreRtcpBlocks( timestamp_us, packet_begin, packet_end, &outgoing_sr_, &outgoing_rr_, &outgoing_xr_, &outgoing_remb_, &outgoing_nack_, &outgoing_fir_, &outgoing_pli_, &outgoing_bye_, &outgoing_transport_feedback_, &outgoing_loss_notification_); - RTC_RETURN_IF_ERROR(status); + RTC_RETURN_IF_ERROR(store_rtcp_status); } // Store first and last timestamp events that might happen before the call is @@ -1196,8 +1232,8 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( // stream configurations and starting/stopping the log. // TODO(terelius): Figure out if we actually need to find the first and last // timestamp in the parser. It seems like this could be done by the caller. - first_timestamp_ = std::numeric_limits::max(); - last_timestamp_ = std::numeric_limits::min(); + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); StoreFirstAndLastTimestamp(alr_state_events()); StoreFirstAndLastTimestamp(route_change_events()); for (const auto& audio_stream : audio_playout_events()) { @@ -1236,7 +1272,8 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( // event, we could use the timestamp of the the last previous regular event. auto start_iter = start_log_events().begin(); auto stop_iter = stop_log_events().begin(); - int64_t start_us = first_timestamp(); + int64_t start_us = + first_timestamp().us_or(std::numeric_limits::max()); int64_t next_start_us = std::numeric_limits::max(); int64_t stop_us = std::numeric_limits::max(); if (start_iter != start_log_events().end()) { @@ -1250,15 +1287,14 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( } stop_us = std::min(stop_us, next_start_us); if (stop_us == std::numeric_limits::max() && - last_timestamp() != std::numeric_limits::min()) { - stop_us = last_timestamp(); + !last_timestamp().IsMinusInfinity()) { + stop_us = last_timestamp().us(); } RTC_PARSE_CHECK_OR_RETURN_LE(start_us, stop_us); first_log_segment_ = LogSegment(start_us, stop_us); - if (first_timestamp_ == std::numeric_limits::max() && - last_timestamp_ == std::numeric_limits::min()) { - first_timestamp_ = last_timestamp_ = 0; + if (first_timestamp_ > last_timestamp_) { + first_timestamp_ = last_timestamp_ = Timestamp::Zero(); } return status; @@ -1267,18 +1303,34 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( absl::string_view s) { constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + // Protobuf defines the message tag as + // (field_number << 3) | wire_type. In the legacy encoding, the field number + // is supposed to be 1 and the wire type for a length-delimited field is 2. + // In the new encoding we still expect the wire type to be 2, but the field + // number will be greater than 1. + constexpr uint64_t kExpectedV1Tag = (1 << 3) | 2; + bool success = false; + + // "Peek" at the first varint. + absl::string_view event_start = s; + uint64_t tag = 0; + std::tie(success, std::ignore) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) << "Failed to read varint from beginning of event log."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + s = event_start; + + if (tag >> 1 == static_cast(RtcEvent::Type::BeginV3Log)) { + return ParseStreamInternalV3(s); + } while (!s.empty()) { - absl::string_view event_start = s; - bool success = false; - - // Read the next message tag. Protobuf defines the message tag as - // (field_number << 3) | wire_type. In the legacy encoding, the field number - // is supposed to be 1 and the wire type for a length-delimited field is 2. - // In the new encoding we still expect the wire type to be 2, but the field - // number will be greater than 1. - constexpr uint64_t kExpectedV1Tag = (1 << 3) | 2; - uint64_t tag = 0; + // If not, "reset" event_start and read the field tag for the next event. + event_start = s; std::tie(success, s) = DecodeVarInt(s, &tag); if (!success) { RTC_LOG(LS_WARNING) @@ -1288,6 +1340,7 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( return ParseStatus::Error("Failed to read field tag varint", __FILE__, __LINE__); } + constexpr uint64_t kWireTypeMask = 0x07; const uint64_t wire_type = tag & kWireTypeMask; if (wire_type != 2) { @@ -1357,12 +1410,161 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( return ParseStatus::Success(); } +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternalV3( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + bool expect_begin_log_event = true; + bool success = false; + + while (!s.empty()) { + // Read event type. + uint64_t event_tag = 0; + std::tie(success, s) = DecodeVarInt(s, &event_tag); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event type."); + bool batched = event_tag & 1; + uint64_t event_type = event_tag >> 1; + + // Read event size + uint64_t event_size_bytes = 0; + std::tie(success, s) = DecodeVarInt(s, &event_size_bytes); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event size."); + if (event_size_bytes > kMaxEventSize || event_size_bytes > s.size()) { + RTC_LOG(LS_WARNING) << "Event size is too large."; + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, kMaxEventSize); + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, s.size()); + } + + // Read remaining event fields into a buffer. + absl::string_view event_fields = s.substr(0, event_size_bytes); + s = s.substr(event_size_bytes); + + if (expect_begin_log_event) { + RTC_PARSE_CHECK_OR_RETURN_EQ( + event_type, static_cast(RtcEvent::Type::BeginV3Log)); + expect_begin_log_event = false; + } + + switch (event_type) { + case static_cast(RtcEvent::Type::BeginV3Log): + RtcEventBeginLog::Parse(event_fields, batched, start_log_events_); + break; + case static_cast(RtcEvent::Type::EndV3Log): + RtcEventEndLog::Parse(event_fields, batched, stop_log_events_); + expect_begin_log_event = true; + break; + case static_cast(RtcEvent::Type::AlrStateEvent): + RtcEventAlrState::Parse(event_fields, batched, alr_state_events_); + break; + case static_cast(RtcEvent::Type::AudioPlayout): + RtcEventAudioPlayout::Parse(event_fields, batched, + audio_playout_events_); + break; + case static_cast(RtcEvent::Type::BweUpdateDelayBased): + RtcEventBweUpdateDelayBased::Parse(event_fields, batched, + bwe_delay_updates_); + break; + case static_cast(RtcEvent::Type::AudioNetworkAdaptation): + RtcEventAudioNetworkAdaptation::Parse(event_fields, batched, + audio_network_adaptation_events_); + break; + case static_cast(RtcEvent::Type::AudioReceiveStreamConfig): + RtcEventAudioReceiveStreamConfig::Parse(event_fields, batched, + audio_recv_configs_); + break; + case static_cast(RtcEvent::Type::AudioSendStreamConfig): + RtcEventAudioSendStreamConfig::Parse(event_fields, batched, + audio_send_configs_); + break; + case static_cast(RtcEvent::Type::BweUpdateLossBased): + RtcEventBweUpdateLossBased::Parse(event_fields, batched, + bwe_loss_updates_); + break; + case static_cast(RtcEvent::Type::DtlsTransportState): + RtcEventDtlsTransportState::Parse(event_fields, batched, + dtls_transport_states_); + break; + case static_cast(RtcEvent::Type::DtlsWritableState): + RtcEventDtlsWritableState::Parse(event_fields, batched, + dtls_writable_states_); + break; + case static_cast(RtcEvent::Type::FrameDecoded): + RtcEventFrameDecoded::Parse(event_fields, batched, decoded_frames_); + break; + case static_cast(RtcEvent::Type::GenericAckReceived): + RtcEventGenericAckReceived::Parse(event_fields, batched, + generic_acks_received_); + break; + case static_cast(RtcEvent::Type::GenericPacketReceived): + RtcEventGenericPacketReceived::Parse(event_fields, batched, + generic_packets_received_); + break; + case static_cast(RtcEvent::Type::GenericPacketSent): + RtcEventGenericPacketSent::Parse(event_fields, batched, + generic_packets_sent_); + break; + case static_cast(RtcEvent::Type::IceCandidatePairConfig): + RtcEventIceCandidatePairConfig::Parse(event_fields, batched, + ice_candidate_pair_configs_); + break; + case static_cast(RtcEvent::Type::IceCandidatePairEvent): + RtcEventIceCandidatePair::Parse(event_fields, batched, + ice_candidate_pair_events_); + break; + case static_cast(RtcEvent::Type::ProbeClusterCreated): + RtcEventProbeClusterCreated::Parse(event_fields, batched, + bwe_probe_cluster_created_events_); + break; + case static_cast(RtcEvent::Type::ProbeResultFailure): + RtcEventProbeResultFailure::Parse(event_fields, batched, + bwe_probe_failure_events_); + break; + case static_cast(RtcEvent::Type::ProbeResultSuccess): + RtcEventProbeResultSuccess::Parse(event_fields, batched, + bwe_probe_success_events_); + break; + case static_cast(RtcEvent::Type::RemoteEstimateEvent): + RtcEventRemoteEstimate::Parse(event_fields, batched, + remote_estimate_events_); + break; + case static_cast(RtcEvent::Type::RouteChangeEvent): + RtcEventRouteChange::Parse(event_fields, batched, route_change_events_); + break; + case static_cast(RtcEvent::Type::RtcpPacketIncoming): + RtcEventRtcpPacketIncoming::Parse(event_fields, batched, + incoming_rtcp_packets_); + break; + case static_cast(RtcEvent::Type::RtcpPacketOutgoing): + RtcEventRtcpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtcp_packets_); + break; + case static_cast(RtcEvent::Type::RtpPacketIncoming): + RtcEventRtpPacketIncoming::Parse(event_fields, batched, + incoming_rtp_packets_map_); + break; + case static_cast(RtcEvent::Type::RtpPacketOutgoing): + RtcEventRtpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtp_packets_map_); + break; + case static_cast(RtcEvent::Type::VideoReceiveStreamConfig): + RtcEventVideoReceiveStreamConfig::Parse(event_fields, batched, + video_recv_configs_); + break; + case static_cast(RtcEvent::Type::VideoSendStreamConfig): + RtcEventVideoSendStreamConfig::Parse(event_fields, batched, + video_send_configs_); + break; + } + } + + return ParseStatus::Success(); +} + template void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector& v) { if (v.empty()) return; - first_timestamp_ = std::min(first_timestamp_, v.front().log_time_us()); - last_timestamp_ = std::max(last_timestamp_, v.back().log_time_us()); + first_timestamp_ = std::min(first_timestamp_, v.front().log_time()); + last_timestamp_ = std::max(last_timestamp_, v.back().log_time()); } ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedLegacyEvent( @@ -1589,13 +1791,13 @@ const RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeaderExtensionMap( } if (parse_unconfigured_header_extensions_ == UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig) { - RTC_LOG(LS_WARNING) << "Using default header extension map for SSRC " - << ssrc; + RTC_DLOG(LS_WARNING) << "Using default header extension map for SSRC " + << ssrc; extensions_maps.insert(std::make_pair(ssrc, default_extension_map_)); return &default_extension_map_; } - RTC_LOG(LS_WARNING) << "Not parsing header extensions for SSRC " << ssrc - << ". No header extension map found."; + RTC_DLOG(LS_WARNING) << "Not parsing header extensions for SSRC " << ssrc + << ". No header extension map found."; return nullptr; } @@ -1892,7 +2094,7 @@ ParsedRtcEventLog::GetBweProbeFailure(const rtclog::Event& event) const { } else if (pr_event.result() == rtclog::BweProbeResult::TIMEOUT) { res.failure_reason = ProbeFailureReason::kTimeout; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } RTC_PARSE_CHECK_OR_RETURN(!pr_event.has_bitrate_bps()); @@ -2360,7 +2562,7 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedNewFormatEvent( } else if (stream.frame_decoded_events_size() == 1) { return StoreFrameDecodedEvents(stream.frame_decoded_events(0)); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ParseStatus::Success(); } } @@ -3165,12 +3367,12 @@ ParsedRtcEventLog::StoreAudioNetworkAdaptationEvent( runtime_config.frame_length_ms = signed_frame_length_ms; } if (uplink_packet_loss_fraction_values[i].has_value()) { - float uplink_packet_loss_fraction; + float uplink_packet_loss_fraction2; RTC_PARSE_CHECK_OR_RETURN(ParsePacketLossFractionFromProtoFormat( rtc::checked_cast( uplink_packet_loss_fraction_values[i].value()), - &uplink_packet_loss_fraction)); - runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction; + &uplink_packet_loss_fraction2)); + runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction2; } if (enable_fec_values[i].has_value()) { runtime_config.enable_fec = diff --git a/logging/rtc_event_log/rtc_event_log_parser.h b/logging/rtc_event_log/rtc_event_log_parser.h index d4c8409b61..9ef4e347de 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.h +++ b/logging/rtc_event_log/rtc_event_log_parser.h @@ -15,22 +15,24 @@ #include #include #include -#include // pair #include #include "absl/base/attributes.h" #include "api/rtc_event_log/rtc_event_log.h" #include "call/video_receive_stream.h" #include "call/video_send_stream.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_alr_state.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" #include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_begin_log.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" #include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" #include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_end_log.h" #include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" #include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" #include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" @@ -42,9 +44,12 @@ #include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" #include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" #include "logging/rtc_event_log/events/rtc_event_route_change.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" #include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" -#include "logging/rtc_event_log/logged_events.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" #include "rtc_base/ignore_wundef.h" @@ -64,6 +69,80 @@ namespace webrtc { enum PacketDirection { kIncomingPacket = 0, kOutgoingPacket }; +enum class LoggedMediaType : uint8_t { kUnknown, kAudio, kVideo }; + +struct LoggedPacketInfo { + LoggedPacketInfo(const LoggedRtpPacket& rtp, + LoggedMediaType media_type, + bool rtx, + Timestamp capture_time); + LoggedPacketInfo(const LoggedPacketInfo&); + ~LoggedPacketInfo(); + int64_t log_time_ms() const { return log_packet_time.ms(); } + int64_t log_time_us() const { return log_packet_time.us(); } + uint32_t ssrc; + uint16_t stream_seq_no; + uint16_t size; + uint16_t payload_size; + uint16_t padding_size; + uint16_t overhead = 0; + uint8_t payload_type; + LoggedMediaType media_type = LoggedMediaType::kUnknown; + bool rtx = false; + bool marker_bit = false; + bool has_transport_seq_no = false; + bool last_in_feedback = false; + uint16_t transport_seq_no = 0; + // The RTP header timestamp unwrapped and converted from tick count to seconds + // based timestamp. + Timestamp capture_time; + // The time the packet was logged. This is the receive time for incoming + // packets and send time for outgoing. + Timestamp log_packet_time; + // Send time as reported by abs-send-time extension, For outgoing packets this + // corresponds to log_packet_time, but might be measured using another clock. + Timestamp reported_send_time; + // The receive time that was reported in feedback. For incoming packets this + // corresponds to log_packet_time, but might be measured using another clock. + // PlusInfinity indicates that the packet was lost. + Timestamp reported_recv_time = Timestamp::MinusInfinity(); + // The time feedback message was logged. This is the feedback send time for + // incoming packets and feedback receive time for outgoing. + // PlusInfinity indicates that feedback was expected but not received. + Timestamp log_feedback_time = Timestamp::MinusInfinity(); + // The delay betweeen receiving an RTP packet and sending feedback for + // incoming packets. For outgoing packets we don't know the feedback send + // time, and this is instead calculated as the difference in reported receive + // time between this packet and the last packet in the same feedback message. + TimeDelta feedback_hold_duration = TimeDelta::MinusInfinity(); +}; + +struct InferredRouteChangeEvent { + int64_t log_time_ms() const { return log_time.ms(); } + int64_t log_time_us() const { return log_time.us(); } + uint32_t route_id; + Timestamp log_time = Timestamp::MinusInfinity(); + uint16_t send_overhead; + uint16_t return_overhead; +}; + +enum class LoggedIceEventType { + kAdded, + kUpdated, + kDestroyed, + kSelected, + kCheckSent, + kCheckReceived, + kCheckResponseSent, + kCheckResponseReceived, +}; + +struct LoggedIceEvent { + uint32_t candidate_pair_id; + Timestamp log_time; + LoggedIceEventType event_type; +}; + // This class is used to process lists of LoggedRtpPacketIncoming // and LoggedRtpPacketOutgoing without duplicating the code. // TODO(terelius): Remove this class. Instead use e.g. a vector of pointers @@ -240,48 +319,11 @@ class ParsedRtcEventLog { kDontParse, kAttemptWebrtcDefaultConfig }; - class ParseStatus { - public: - static ParseStatus Success() { return ParseStatus(); } - static ParseStatus Error(std::string error, std::string file, int line) { - return ParseStatus(error, file, line); - } - bool ok() const { return error_.empty() && file_.empty() && line_ == 0; } - std::string message() const { - return error_ + " failed at " + file_ + " line " + std::to_string(line_); - } - - ABSL_DEPRECATED("Use ok() instead") operator bool() const { return ok(); } - - private: - ParseStatus() : error_(), file_(), line_(0) {} - ParseStatus(std::string error, std::string file, int line) - : error_(error), file_(file), line_(line) {} - std::string error_; - std::string file_; - int line_; - }; + using ParseStatus = RtcEventLogParseStatus; template - class ParseStatusOr { - public: - ParseStatusOr(const ParseStatus& error) // NOLINT - : status_(error), value_() {} - ParseStatusOr(const T& value) // NOLINT - : status_(ParseStatus::Success()), value_(value) {} - bool ok() const { return status_.ok(); } - const T& value() const& { - RTC_DCHECK(status_.ok()); - return value_; - } - std::string message() const { return status_.message(); } - const ParseStatus& status() const { return status_; } - - private: - ParseStatus status_; - T value_; - }; + using ParseStatusOr = RtcEventLogParseStatusOr; struct LoggedRtpStreamIncoming { LoggedRtpStreamIncoming(); @@ -601,8 +643,8 @@ class ParsedRtcEventLog { return decoded_frames_; } - int64_t first_timestamp() const { return first_timestamp_; } - int64_t last_timestamp() const { return last_timestamp_; } + Timestamp first_timestamp() const { return first_timestamp_; } + Timestamp last_timestamp() const { return last_timestamp_; } const LogSegment& first_log_segment() const { return first_log_segment_; } @@ -620,6 +662,7 @@ class ParsedRtcEventLog { private: ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternal(absl::string_view s); + ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternalV3(absl::string_view s); ABSL_MUST_USE_RESULT ParseStatus StoreParsedLegacyEvent(const rtclog::Event& event); @@ -846,8 +889,8 @@ class ParsedRtcEventLog { std::vector last_incoming_rtcp_packet_; - int64_t first_timestamp_; - int64_t last_timestamp_; + Timestamp first_timestamp_ = Timestamp::PlusInfinity(); + Timestamp last_timestamp_ = Timestamp::MinusInfinity(); LogSegment first_log_segment_ = LogSegment(0, std::numeric_limits::max()); diff --git a/logging/rtc_event_log/rtc_event_log_unittest.cc b/logging/rtc_event_log/rtc_event_log_unittest.cc index a494d9e22b..34904a5e02 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "api/rtc_event_log/rtc_event_log.h" + #include #include #include @@ -17,9 +19,7 @@ #include #include -#include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log/rtc_event_log_factory.h" -#include "api/rtc_event_log_output_file.h" #include "api/task_queue/default_task_queue_factory.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" @@ -50,6 +50,7 @@ #include "rtc_base/fake_clock.h" #include "rtc_base/random.h" #include "test/gtest.h" +#include "test/logging/memory_log_writer.h" #include "test/testsupport/file_utils.h" namespace webrtc { @@ -113,11 +114,12 @@ class RtcEventLogSession output_period_ms_(std::get<1>(GetParam())), encoding_type_(std::get<2>(GetParam())), gen_(seed_ * 880001UL), - verifier_(encoding_type_) { + verifier_(encoding_type_), + log_storage_(), + log_output_factory_(log_storage_.CreateFactory()) { clock_.SetTime(Timestamp::Micros(prng_.Rand())); // Find the name of the current test, in order to use it as a temporary // filename. - // TODO(terelius): Use a general utility function to generate a temp file. auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::string test_name = std::string(test_info->test_case_name()) + "_" + test_info->name(); @@ -203,6 +205,8 @@ class RtcEventLogSession test::EventVerifier verifier_; rtc::ScopedFakeClock clock_; std::string temp_filename_; + MemoryLogStorage log_storage_; + std::unique_ptr log_output_factory_; }; bool SsrcUsed( @@ -272,9 +276,9 @@ void RtcEventLogSession::WriteVideoRecvConfigs(size_t video_recv_streams, } while (SsrcUsed(ssrc, incoming_extensions_)); RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); incoming_extensions_.emplace_back(ssrc, extensions); - auto event = gen_.NewVideoReceiveStreamConfig(ssrc, extensions); - event_log->Log(event->Copy()); - video_recv_config_list_.push_back(std::move(event)); + auto new_event = gen_.NewVideoReceiveStreamConfig(ssrc, extensions); + event_log->Log(new_event->Copy()); + video_recv_config_list_.push_back(std::move(new_event)); } } @@ -314,7 +318,7 @@ void RtcEventLogSession::WriteLog(EventCounts count, auto task_queue_factory = CreateDefaultTaskQueueFactory(); RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); - // The log file will be flushed to disk when the event_log goes out of scope. + // The log will be flushed to output when the event_log goes out of scope. std::unique_ptr event_log = rtc_event_log_factory.CreateRtcEventLog(encoding_type_); @@ -333,9 +337,8 @@ void RtcEventLogSession::WriteLog(EventCounts count, for (; remaining_events > 0; remaining_events--) { if (remaining_events == remaining_events_at_start) { clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); - event_log->StartLogging( - std::make_unique(temp_filename_, 10000000), - output_period_ms_); + event_log->StartLogging(log_output_factory_->Create(temp_filename_), + output_period_ms_); start_time_us_ = rtc::TimeMicros(); utc_start_time_us_ = rtc::TimeUTCMicros(); } @@ -546,7 +549,7 @@ void RtcEventLogSession::WriteLog(EventCounts count, } selection -= count.generic_acks_received; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } event_log->StopLogging(); @@ -555,12 +558,14 @@ void RtcEventLogSession::WriteLog(EventCounts count, ASSERT_EQ(count.total_nonconfig_events(), static_cast(0)); } -// Read the file and verify that what we read back from the event log is the +// Read the log and verify that what we read back from the event log is the // same as what we wrote down. void RtcEventLogSession::ReadAndVerifyLog() { - // Read the generated file from disk. + // Read the generated log from memory. ParsedRtcEventLog parsed_log; - ASSERT_TRUE(parsed_log.ParseFile(temp_filename_).ok()); + auto it = log_storage_.logs().find(temp_filename_); + ASSERT_TRUE(it != log_storage_.logs().end()); + ASSERT_TRUE(parsed_log.ParseString(it->second).ok()); // Start and stop events. auto& parsed_start_log_events = parsed_log.start_log_events(); @@ -780,16 +785,13 @@ void RtcEventLogSession::ReadAndVerifyLog() { parsed_generic_acks_received[i]); } - EXPECT_EQ(first_timestamp_ms_, parsed_log.first_timestamp() / 1000); - EXPECT_EQ(last_timestamp_ms_, parsed_log.last_timestamp() / 1000); + EXPECT_EQ(first_timestamp_ms_, parsed_log.first_timestamp().ms()); + EXPECT_EQ(last_timestamp_ms_, parsed_log.last_timestamp().ms()); EXPECT_EQ(parsed_log.first_log_segment().start_time_ms(), std::min(start_time_us_ / 1000, first_timestamp_ms_)); EXPECT_EQ(parsed_log.first_log_segment().stop_time_ms(), stop_time_us_ / 1000); - - // Clean up temporary file - can be pretty slow. - remove(temp_filename_.c_str()); } } // namespace @@ -875,9 +877,14 @@ class RtcEventLogCircularBufferTest : public ::testing::TestWithParam { public: RtcEventLogCircularBufferTest() - : encoding_type_(GetParam()), verifier_(encoding_type_) {} + : encoding_type_(GetParam()), + verifier_(encoding_type_), + log_storage_(), + log_output_factory_(log_storage_.CreateFactory()) {} const RtcEventLog::EncodingType encoding_type_; const test::EventVerifier verifier_; + MemoryLogStorage log_storage_; + std::unique_ptr log_output_factory_; }; TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { @@ -899,8 +906,8 @@ TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { auto task_queue_factory = CreateDefaultTaskQueueFactory(); RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); - // When `log` goes out of scope, it causes the log file to be flushed - // to disk. + // When `log` goes out of scope, the contents are flushed + // to the output. std::unique_ptr log = rtc_event_log_factory.CreateRtcEventLog(encoding_type_); @@ -917,16 +924,17 @@ TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { } int64_t start_time_us = rtc::TimeMicros(); int64_t utc_start_time_us = rtc::TimeUTCMicros(); - log->StartLogging( - std::make_unique(temp_filename, 10000000), - RtcEventLog::kImmediateOutput); + log->StartLogging(log_output_factory_->Create(temp_filename), + RtcEventLog::kImmediateOutput); fake_clock->AdvanceTime(TimeDelta::Millis(10)); int64_t stop_time_us = rtc::TimeMicros(); log->StopLogging(); - // Read the generated file from disk. + // Read the generated log from memory. ParsedRtcEventLog parsed_log; - ASSERT_TRUE(parsed_log.ParseFile(temp_filename).ok()); + auto it = log_storage_.logs().find(temp_filename); + ASSERT_TRUE(it != log_storage_.logs().end()); + ASSERT_TRUE(parsed_log.ParseString(it->second).ok()); const auto& start_log_events = parsed_log.start_log_events(); ASSERT_EQ(start_log_events.size(), 1u); @@ -960,9 +968,6 @@ TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { RtcEventProbeResultSuccess(first_id + i, first_bitrate_bps + i * 1000), probe_success_events[i]); } - - // Clean up temporary file - can be pretty slow. - remove(temp_filename.c_str()); } INSTANTIATE_TEST_SUITE_P( diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc index 0960c98502..e77a67182b 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc @@ -462,7 +462,7 @@ EventGenerator::NewRtcpPacketIncoming() { return std::make_unique(buffer); } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); rtc::Buffer buffer; return std::make_unique(buffer); } @@ -531,7 +531,7 @@ EventGenerator::NewRtcpPacketOutgoing() { return std::make_unique(buffer); } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); rtc::Buffer buffer; return std::make_unique(buffer); } diff --git a/media/BUILD.gn b/media/BUILD.gn index acfa09f039..b9d92089af 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -57,6 +57,7 @@ rtc_library("rtc_media_base") { "../api:rtp_parameters", "../api:scoped_refptr", "../api:sequence_checker", + "../api:webrtc_key_value_config", "../api/audio:audio_frame_processor", "../api/audio_codecs:audio_codecs_api", "../api/crypto:frame_decryptor_interface", @@ -64,7 +65,6 @@ rtc_library("rtc_media_base") { "../api/crypto:options", "../api/transport:datagram_transport_interface", "../api/transport:stun_types", - "../api/transport:webrtc_key_value_config", "../api/transport/rtp:rtp_source", "../api/units:time_delta", "../api/video:video_bitrate_allocation", @@ -211,13 +211,20 @@ rtc_library("rtc_internal_video_codecs") { "../modules/video_coding:webrtc_vp8", "../modules/video_coding:webrtc_vp9", "../modules/video_coding/codecs/av1:libaom_av1_decoder", - "../modules/video_coding/codecs/av1:libaom_av1_encoder", + "../modules/video_coding/codecs/av1:libaom_av1_encoder_if_supported", "../rtc_base:checks", "../rtc_base:rtc_base_approved", "../rtc_base/system:rtc_export", + "../system_wrappers:field_trial", "../test:fake_video_codecs", ] - absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] + if (rtc_include_dav1d_in_internal_decoder_factory) { + deps += [ "../modules/video_coding/codecs/av1:dav1d_decoder" ] + } + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] sources = [ "engine/fake_video_codec_factory.cc", "engine/fake_video_codec_factory.h", @@ -385,6 +392,7 @@ if (rtc_build_dcsctp) { deps = [ ":rtc_data_sctp_transport_internal", "../api:array_view", + "../api/task_queue:task_queue", "../media:rtc_media_base", "../net/dcsctp/public:factory", "../net/dcsctp/public:socket", @@ -445,6 +453,7 @@ rtc_library("rtc_data_sctp_transport_factory") { ] deps = [ ":rtc_data_sctp_transport_internal", + "../api:webrtc_key_value_config", "../api/transport:sctp_transport_factory_interface", "../rtc_base:threading", "../rtc_base/experiments:field_trial_parser", @@ -516,6 +525,7 @@ if (rtc_include_tests) { "../rtc_base:threading", "../rtc_base/synchronization:mutex", "../rtc_base/third_party/sigslot", + "../test:scoped_key_value_config", "../test:test_support", "//testing/gtest", ] @@ -592,6 +602,7 @@ if (rtc_include_tests) { "../api/test/video:function_video_factory", "../api/transport:field_trial_based_config", "../api/units:time_delta", + "../api/units:timestamp", "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:video_bitrate_allocation", "../api/video:video_codec_constants", @@ -614,6 +625,7 @@ if (rtc_include_tests) { "../modules/video_coding:webrtc_h264", "../modules/video_coding:webrtc_vp8", "../modules/video_coding/codecs/av1:libaom_av1_decoder", + "../modules/video_coding/codecs/av1:libaom_av1_encoder_if_supported", "../p2p:p2p_test_utils", "../rtc_base", "../rtc_base:checks", @@ -631,9 +643,11 @@ if (rtc_include_tests) { "../test:fake_video_codecs", "../test:field_trial", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_main", "../test:test_support", "../test:video_test_common", + "../test/time_controller", ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", @@ -653,6 +667,7 @@ if (rtc_include_tests) { "base/video_common_unittest.cc", "engine/encoder_simulcast_proxy_unittest.cc", "engine/internal_decoder_factory_unittest.cc", + "engine/internal_encoder_factory_unittest.cc", "engine/multiplex_codec_factory_unittest.cc", "engine/null_webrtc_video_engine_unittest.cc", "engine/payload_type_mapper_unittest.cc", diff --git a/media/base/adapted_video_track_source.cc b/media/base/adapted_video_track_source.cc index 2fce973f68..f8f8f2dad1 100644 --- a/media/base/adapted_video_track_source.cc +++ b/media/base/adapted_video_track_source.cc @@ -114,4 +114,9 @@ bool AdaptedVideoTrackSource::AdaptFrame(int width, return true; } +void AdaptedVideoTrackSource::ProcessConstraints( + const webrtc::VideoTrackSourceConstraints& constraints) { + broadcaster_.ProcessConstraints(constraints); +} + } // namespace rtc diff --git a/media/base/adapted_video_track_source.h b/media/base/adapted_video_track_source.h index d40baeff6d..1386fbd9db 100644 --- a/media/base/adapted_video_track_source.h +++ b/media/base/adapted_video_track_source.h @@ -86,6 +86,8 @@ class RTC_EXPORT AdaptedVideoTrackSource rtc::VideoSinkInterface* sink) override {} void RemoveEncodedSink( rtc::VideoSinkInterface* sink) override {} + void ProcessConstraints( + const webrtc::VideoTrackSourceConstraints& constraints) override; cricket::VideoAdapter video_adapter_; diff --git a/media/base/codec.cc b/media/base/codec.cc index 9b09f5e73b..a01914dff5 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -18,7 +18,6 @@ #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" -#include "system_wrappers/include/field_trial.h" namespace cricket { namespace { @@ -129,13 +128,14 @@ bool Codec::operator==(const Codec& c) const { feedback_params == c.feedback_params; } -bool Codec::Matches(const Codec& codec) const { +bool Codec::Matches(const Codec& codec, + const webrtc::WebRtcKeyValueConfig* field_trials) const { // Match the codec id/name based on the typical static/dynamic name rules. // Matching is case-insensitive. // Legacy behaviour with killswitch. - if (webrtc::field_trial::IsDisabled( - "WebRTC-PayloadTypes-Lower-Dynamic-Range")) { + if (field_trials && + field_trials->IsDisabled("WebRTC-PayloadTypes-Lower-Dynamic-Range")) { const int kMaxStaticPayloadId = 95; return (id <= kMaxStaticPayloadId || codec.id <= kMaxStaticPayloadId) ? (id == codec.id) @@ -238,7 +238,9 @@ bool AudioCodec::operator==(const AudioCodec& c) const { return bitrate == c.bitrate && channels == c.channels && Codec::operator==(c); } -bool AudioCodec::Matches(const AudioCodec& codec) const { +bool AudioCodec::Matches( + const AudioCodec& codec, + const webrtc::WebRtcKeyValueConfig* field_trials) const { // If a nonzero clockrate is specified, it must match the actual clockrate. // If a nonzero bitrate is specified, it must match the actual bitrate, // unless the codec is VBR (0), where we just force the supplied value. @@ -248,7 +250,7 @@ bool AudioCodec::Matches(const AudioCodec& codec) const { // omitted if the number of channels is one." // Preference is ignored. // TODO(juberti): Treat a zero clockrate as 8000Hz, the RTP default clockrate. - return Codec::Matches(codec) && + return Codec::Matches(codec, field_trials) && ((codec.clockrate == 0 /*&& clockrate == 8000*/) || clockrate == codec.clockrate) && (codec.bitrate == 0 || bitrate <= 0 || bitrate == codec.bitrate) && @@ -324,8 +326,10 @@ bool VideoCodec::operator==(const VideoCodec& c) const { return Codec::operator==(c) && packetization == c.packetization; } -bool VideoCodec::Matches(const VideoCodec& other) const { - return Codec::Matches(other) && +bool VideoCodec::Matches( + const VideoCodec& other, + const webrtc::WebRtcKeyValueConfig* field_trials) const { + return Codec::Matches(other, field_trials) && IsSameCodecSpecific(name, params, other.name, other.params); } diff --git a/media/base/codec.h b/media/base/codec.h index cfc31aed1f..70ecfd326d 100644 --- a/media/base/codec.h +++ b/media/base/codec.h @@ -19,6 +19,7 @@ #include "absl/types/optional.h" #include "api/rtp_parameters.h" #include "api/video_codecs/sdp_video_format.h" +#include "api/webrtc_key_value_config.h" #include "media/base/media_constants.h" #include "rtc_base/system/rtc_export.h" @@ -75,7 +76,9 @@ struct RTC_EXPORT Codec { virtual ~Codec(); // Indicates if this codec is compatible with the specified codec. - bool Matches(const Codec& codec) const; + bool Matches( + const Codec& codec, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) const; bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const; // Find the parameter for `name` and write the value to `out`. @@ -132,7 +135,9 @@ struct AudioCodec : public Codec { ~AudioCodec() override = default; // Indicates if this codec is compatible with the specified codec. - bool Matches(const AudioCodec& codec) const; + bool Matches( + const AudioCodec& codec, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) const; std::string ToString() const; @@ -163,7 +168,9 @@ struct RTC_EXPORT VideoCodec : public Codec { // Indicates if this video codec is the same as the other video codec, e.g. if // they are both VP8 or VP9, or if they are both H264 with the same H264 // profile. H264 levels however are not compared. - bool Matches(const VideoCodec& codec) const; + bool Matches( + const VideoCodec& codec, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) const; std::string ToString() const; diff --git a/media/base/fake_frame_source.cc b/media/base/fake_frame_source.cc index 8a05536c83..61bc5857d9 100644 --- a/media/base/fake_frame_source.cc +++ b/media/base/fake_frame_source.cc @@ -52,7 +52,8 @@ webrtc::VideoFrame FakeFrameSource::GetFrameRotationApplied() { case webrtc::kVideoRotation_270: return GetFrame(height_, width_, webrtc::kVideoRotation_0, interval_us_); } - RTC_NOTREACHED() << "Invalid rotation value: " << static_cast(rotation_); + RTC_DCHECK_NOTREACHED() << "Invalid rotation value: " + << static_cast(rotation_); // Without this return, the Windows Visual Studio compiler complains // "not all control paths return a value". return GetFrame(); diff --git a/media/base/fake_media_engine.h b/media/base/fake_media_engine.h index e4f7b6659f..bf8a0592a5 100644 --- a/media/base/fake_media_engine.h +++ b/media/base/fake_media_engine.h @@ -275,7 +275,7 @@ class RtpHelper : public Base { } void OnPacketSent(const rtc::SentPacket& sent_packet) override {} void OnReadyToSend(bool ready) override { ready_to_send_ = ready; } - void OnNetworkRouteChanged(const std::string& transport_name, + void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override { last_network_route_ = network_route; ++num_network_route_changes_; diff --git a/media/base/fake_rtp.cc b/media/base/fake_rtp.cc index 4f42821762..21322419e1 100644 --- a/media/base/fake_rtp.cc +++ b/media/base/fake_rtp.cc @@ -21,7 +21,7 @@ void CompareHeaderExtensions(const char* packet1, size_t packet1_size, const char* packet2, size_t packet2_size, - const std::vector encrypted_headers, + const std::vector& encrypted_headers, bool expect_equal) { // Sanity check: packets must be large enough to contain the RTP header and // extensions header. diff --git a/media/base/fake_rtp.h b/media/base/fake_rtp.h index f2578151ed..8a176038cb 100644 --- a/media/base/fake_rtp.h +++ b/media/base/fake_rtp.h @@ -295,7 +295,7 @@ void CompareHeaderExtensions(const char* packet1, size_t packet1_size, const char* packet2, size_t packet2_size, - const std::vector encrypted_headers, + const std::vector& encrypted_headers, bool expect_equal); #endif // MEDIA_BASE_FAKE_RTP_H_ diff --git a/media/base/media_channel.cc b/media/base/media_channel.cc index 11953c2c5b..1b11fcc4e8 100644 --- a/media/base/media_channel.cc +++ b/media/base/media_channel.cc @@ -26,14 +26,8 @@ VideoOptions::VideoOptions() : content_hint(VideoTrackInterface::ContentHint::kNone) {} VideoOptions::~VideoOptions() = default; -MediaChannel::MediaChannel(const MediaConfig& config, - TaskQueueBase* network_thread) - : enable_dscp_(config.enable_dscp), - network_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()), - network_thread_(network_thread) {} - -MediaChannel::MediaChannel(TaskQueueBase* network_thread) - : enable_dscp_(false), +MediaChannel::MediaChannel(TaskQueueBase* network_thread, bool enable_dscp) + : enable_dscp_(enable_dscp), network_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()), network_thread_(network_thread) {} @@ -95,6 +89,11 @@ bool MediaChannel::ExtmapAllowMixed() const { return extmap_allow_mixed_; } +bool MediaChannel::HasNetworkInterface() const { + RTC_DCHECK_RUN_ON(network_thread_); + return network_interface_ != nullptr; +} + void MediaChannel::SetEncoderToPacketizerFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) {} diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 20e0bc4e58..e16c312cac 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -38,7 +38,6 @@ #include "common_video/include/quality_limitation_reason.h" #include "media/base/codec.h" #include "media/base/delayable.h" -#include "media/base/media_config.h" #include "media/base/media_constants.h" #include "media/base/stream_params.h" #include "modules/audio_processing/include/audio_processing_statistics.h" @@ -180,9 +179,8 @@ class MediaChannel { virtual ~NetworkInterface() {} }; - MediaChannel(const MediaConfig& config, - webrtc::TaskQueueBase* network_thread); - explicit MediaChannel(webrtc::TaskQueueBase* network_thread); + explicit MediaChannel(webrtc::TaskQueueBase* network_thread, + bool enable_dscp = false); virtual ~MediaChannel(); virtual cricket::MediaType media_type() const = 0; @@ -199,7 +197,7 @@ class MediaChannel { virtual void OnReadyToSend(bool ready) = 0; // Called when the network route used for sending packets changed. virtual void OnNetworkRouteChanged( - const std::string& transport_name, + absl::string_view transport_name, const rtc::NetworkRoute& network_route) = 0; // Creates a new outgoing media stream with SSRCs and CNAME as described // by sp. @@ -220,11 +218,14 @@ class MediaChannel { // Resets any cached StreamParams for an unsignaled RecvStream, and removes // any existing unsignaled streams. virtual void ResetUnsignaledRecvStream() = 0; - // Informs the media channel when the transport's demuxer criteria is updated. + // This is currently a workaround because of the demuxer state being managed + // across two separate threads. Once the state is consistently managed on + // the same thread (network), this workaround can be removed. + // These two notifications inform the media channel when the transport's + // demuxer criteria is being updated. // * OnDemuxerCriteriaUpdatePending() happens on the same thread that the // channel's streams are added and removed (worker thread). - // * OnDemuxerCriteriaUpdateComplete() happens on the thread where the demuxer - // lives (network thread). + // * OnDemuxerCriteriaUpdateComplete() happens on the same thread. // Because the demuxer is updated asynchronously, there is a window of time // where packets are arriving to the channel for streams that have already // been removed on the worker thread. It is important NOT to treat these as @@ -269,6 +270,10 @@ class MediaChannel { void SetExtmapAllowMixed(bool extmap_allow_mixed); bool ExtmapAllowMixed() const; + // Returns `true` if a non-null NetworkInterface pointer is held. + // Must be called on the network thread. + bool HasNetworkInterface() const; + virtual webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const = 0; virtual webrtc::RTCError SetRtpSendParameters( uint32_t ssrc, @@ -385,6 +390,8 @@ struct MediaSenderInfo { uint64_t retransmitted_packets_sent = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-nackcount uint32_t nacks_rcvd = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-targetbitrate + double target_bitrate = 0.0; int packets_lost = 0; float fraction_lost = 0.0f; int64_t rtt_ms = 0; @@ -557,7 +564,7 @@ struct VideoSenderInfo : public MediaSenderInfo { int send_frame_width = 0; int send_frame_height = 0; int frames = 0; - int framerate_input = 0; + double framerate_input = 0; int framerate_sent = 0; int aggregated_framerate_sent = 0; int nominal_bitrate = 0; @@ -782,11 +789,9 @@ struct AudioRecvParameters : RtpParameters {}; class VoiceMediaChannel : public MediaChannel, public Delayable { public: - explicit VoiceMediaChannel(webrtc::TaskQueueBase* network_thread) - : MediaChannel(network_thread) {} - VoiceMediaChannel(const MediaConfig& config, - webrtc::TaskQueueBase* network_thread) - : MediaChannel(config, network_thread) {} + VoiceMediaChannel(webrtc::TaskQueueBase* network_thread, + bool enable_dscp = false) + : MediaChannel(network_thread, enable_dscp) {} ~VoiceMediaChannel() override {} cricket::MediaType media_type() const override; @@ -870,11 +875,9 @@ struct VideoRecvParameters : RtpParameters {}; class VideoMediaChannel : public MediaChannel, public Delayable { public: - explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread) - : MediaChannel(network_thread) {} - VideoMediaChannel(const MediaConfig& config, - webrtc::TaskQueueBase* network_thread) - : MediaChannel(config, network_thread) {} + explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread, + bool enable_dscp = false) + : MediaChannel(network_thread, enable_dscp) {} ~VideoMediaChannel() override {} cricket::MediaType media_type() const override; diff --git a/media/base/media_constants.cc b/media/base/media_constants.cc index 17a8a83bd0..da5e7a8adf 100644 --- a/media/base/media_constants.cc +++ b/media/base/media_constants.cc @@ -39,6 +39,9 @@ const char kCodecParamRtxTime[] = "rtx-time"; const char kCodecParamAssociatedPayloadType[] = "apt"; const char kCodecParamAssociatedCodecName[] = "acn"; +// Parameters that do not follow the key-value convention +// are treated as having the empty string as key. +const char kCodecParamNotInNameValueFormat[] = ""; const char kOpusCodecName[] = "opus"; const char kIsacCodecName[] = "ISAC"; @@ -99,7 +102,7 @@ const char kComfortNoiseCodecName[] = "CN"; const char kVp8CodecName[] = "VP8"; const char kVp9CodecName[] = "VP9"; -const char kAv1CodecName[] = "AV1X"; +const char kAv1CodecName[] = "AV1"; const char kH264CodecName[] = "H264"; // RFC 6184 RTP Payload Format for H.264 video diff --git a/media/base/media_constants.h b/media/base/media_constants.h index 1f471e7dd7..16c5db92b9 100644 --- a/media/base/media_constants.h +++ b/media/base/media_constants.h @@ -42,6 +42,7 @@ extern const char kCodecParamRtxTime[]; extern const char kCodecParamAssociatedPayloadType[]; extern const char kCodecParamAssociatedCodecName[]; +extern const char kCodecParamNotInNameValueFormat[]; extern const char kOpusCodecName[]; extern const char kIsacCodecName[]; diff --git a/media/base/media_engine.h b/media/base/media_engine.h index 6f47127f30..7b4dbe4364 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -19,11 +19,12 @@ #include "api/audio_codecs/audio_encoder_factory.h" #include "api/crypto/crypto_options.h" #include "api/rtp_parameters.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/video/video_bitrate_allocator_factory.h" +#include "api/webrtc_key_value_config.h" #include "call/audio_state.h" #include "media/base/codec.h" #include "media/base/media_channel.h" +#include "media/base/media_config.h" #include "media/base/video_common.h" #include "rtc_base/system/file_wrapper.h" @@ -63,7 +64,9 @@ class VoiceEngineInterface : public RtpHeaderExtensionQueryInterface { public: VoiceEngineInterface() = default; virtual ~VoiceEngineInterface() = default; - RTC_DISALLOW_COPY_AND_ASSIGN(VoiceEngineInterface); + + VoiceEngineInterface(const VoiceEngineInterface&) = delete; + VoiceEngineInterface& operator=(const VoiceEngineInterface&) = delete; // Initialization // Starts the engine. @@ -97,7 +100,9 @@ class VideoEngineInterface : public RtpHeaderExtensionQueryInterface { public: VideoEngineInterface() = default; virtual ~VideoEngineInterface() = default; - RTC_DISALLOW_COPY_AND_ASSIGN(VideoEngineInterface); + + VideoEngineInterface(const VideoEngineInterface&) = delete; + VideoEngineInterface& operator=(const VideoEngineInterface&) = delete; // Creates a video media channel, paired with the specified voice channel. // Returns NULL on failure. diff --git a/media/base/rtp_utils.cc b/media/base/rtp_utils.cc index 97a3c4c7f1..c630cbc7e4 100644 --- a/media/base/rtp_utils.cc +++ b/media/base/rtp_utils.cc @@ -58,7 +58,7 @@ void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data, // | ID | len=2 | absolute send time | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ if (length != kAbsSendTimeExtensionLen) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } @@ -84,7 +84,7 @@ void UpdateRtpAuthTag(uint8_t* rtp, // ROC (rollover counter) is at the beginning of the auth tag. const size_t kRocLength = 4; if (tag_length < kRocLength || tag_length > length) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } @@ -105,7 +105,7 @@ void UpdateRtpAuthTag(uint8_t* rtp, auth_required_length, output, sizeof(output)); if (result < tag_length) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } @@ -372,7 +372,7 @@ bool ApplyPacketOptions(uint8_t* data, size_t rtp_start_pos; size_t rtp_length; if (!UnwrapTurnPacket(data, length, &rtp_start_pos, &rtp_length)) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -380,7 +380,7 @@ bool ApplyPacketOptions(uint8_t* data, auto packet = rtc::MakeArrayView(data + rtp_start_pos, rtp_length); if (!webrtc::IsRtpPacket(packet) || !ValidateRtpHeader(data + rtp_start_pos, rtp_length, nullptr)) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/media/base/stream_params.cc b/media/base/stream_params.cc index db781acfc7..0fe1be6ac7 100644 --- a/media/base/stream_params.cc +++ b/media/base/stream_params.cc @@ -111,7 +111,7 @@ StreamParams& StreamParams::operator=(const StreamParams&) = default; StreamParams& StreamParams::operator=(StreamParams&&) = default; bool StreamParams::operator==(const StreamParams& other) const { - return (groupid == other.groupid && id == other.id && ssrcs == other.ssrcs && + return (id == other.id && ssrcs == other.ssrcs && ssrc_groups == other.ssrc_groups && cname == other.cname && stream_ids_ == other.stream_ids_ && // RIDs are not required to be in the same order for equality. @@ -122,9 +122,6 @@ std::string StreamParams::ToString() const { char buf[2 * 1024]; rtc::SimpleStringBuilder sb(buf); sb << "{"; - if (!groupid.empty()) { - sb << "groupid:" << groupid << ";"; - } if (!id.empty()) { sb << "id:" << id << ";"; } diff --git a/media/base/stream_params.h b/media/base/stream_params.h index b8c37706df..c9c8a09592 100644 --- a/media/base/stream_params.h +++ b/media/base/stream_params.h @@ -54,7 +54,6 @@ #include "absl/algorithm/container.h" #include "media/base/rid_description.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/unique_id_generator.h" namespace cricket { @@ -183,11 +182,6 @@ struct StreamParams { std::string ToString() const; - // Resource of the MUC jid of the participant of with this stream. - // For 1:1 calls, should be left empty (which means remote streams - // and local streams should not be mixed together). This is not used - // internally and should be deprecated. - std::string groupid; // A unique identifier of the StreamParams object. When the SDP is created, // this comes from the track ID of the sender that the StreamParams object // is associated with. @@ -224,26 +218,22 @@ struct StreamParams { std::vector rids_; }; -// A Stream can be selected by either groupid+id or ssrc. +// A Stream can be selected by either id or ssrc. struct StreamSelector { explicit StreamSelector(uint32_t ssrc) : ssrc(ssrc) {} - StreamSelector(const std::string& groupid, const std::string& streamid) - : ssrc(0), groupid(groupid), streamid(streamid) {} - explicit StreamSelector(const std::string& streamid) : ssrc(0), streamid(streamid) {} bool Matches(const StreamParams& stream) const { if (ssrc == 0) { - return stream.groupid == groupid && stream.id == streamid; + return stream.id == streamid; } else { return stream.has_ssrc(ssrc); } } uint32_t ssrc; - std::string groupid; std::string streamid; }; @@ -274,19 +264,15 @@ inline const StreamParams* GetStreamBySsrc(const StreamParamsVec& streams, } inline const StreamParams* GetStreamByIds(const StreamParamsVec& streams, - const std::string& groupid, const std::string& id) { - return GetStream(streams, [&groupid, &id](const StreamParams& sp) { - return sp.groupid == groupid && sp.id == id; - }); + return GetStream(streams, + [&id](const StreamParams& sp) { return sp.id == id; }); } inline StreamParams* GetStreamByIds(StreamParamsVec& streams, - const std::string& groupid, const std::string& id) { - return GetStream(streams, [&groupid, &id](const StreamParams& sp) { - return sp.groupid == groupid && sp.id == id; - }); + return GetStream(streams, + [&id](const StreamParams& sp) { return sp.id == id; }); } inline const StreamParams* GetStream(const StreamParamsVec& streams, @@ -318,11 +304,9 @@ inline bool RemoveStreamBySsrc(StreamParamsVec* streams, uint32_t ssrc) { streams, [&ssrc](const StreamParams& sp) { return sp.has_ssrc(ssrc); }); } inline bool RemoveStreamByIds(StreamParamsVec* streams, - const std::string& groupid, const std::string& id) { - return RemoveStream(streams, [&groupid, &id](const StreamParams& sp) { - return sp.groupid == groupid && sp.id == id; - }); + return RemoveStream(streams, + [&id](const StreamParams& sp) { return sp.id == id; }); } } // namespace cricket diff --git a/media/base/test_utils.h b/media/base/test_utils.h index 22bda4f12a..fbd9f35184 100644 --- a/media/base/test_utils.h +++ b/media/base/test_utils.h @@ -37,10 +37,12 @@ inline std::vector MakeVector(const T a[], size_t s) { // Checks whether `codecs` contains `codec`; checks using Codec::Matches(). template -bool ContainsMatchingCodec(const std::vector& codecs, const C& codec) { +bool ContainsMatchingCodec(const std::vector& codecs, + const C& codec, + const webrtc::WebRtcKeyValueConfig* field_trials) { typename std::vector::const_iterator it; for (it = codecs.begin(); it != codecs.end(); ++it) { - if (it->Matches(codec)) { + if (it->Matches(codec, field_trials)) { return true; } } diff --git a/media/base/video_adapter.h b/media/base/video_adapter.h index 1bae10d419..071330f330 100644 --- a/media/base/video_adapter.h +++ b/media/base/video_adapter.h @@ -19,7 +19,6 @@ #include "api/video/video_source_interface.h" #include "common_video/framerate_controller.h" #include "media/base/video_common.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/thread_annotations.h" @@ -38,6 +37,9 @@ class RTC_EXPORT VideoAdapter { explicit VideoAdapter(int source_resolution_alignment); virtual ~VideoAdapter(); + VideoAdapter(const VideoAdapter&) = delete; + VideoAdapter& operator=(const VideoAdapter&) = delete; + // Return the adapted resolution and cropping parameters given the // input resolution. The input frame should first be cropped, then // scaled to the final output resolution. Returns true if the frame @@ -146,8 +148,6 @@ class RTC_EXPORT VideoAdapter { // The critical section to protect the above variables. mutable webrtc::Mutex mutex_; - - RTC_DISALLOW_COPY_AND_ASSIGN(VideoAdapter); }; } // namespace cricket diff --git a/media/base/video_broadcaster.cc b/media/base/video_broadcaster.cc index 1b55786338..1167d7fb32 100644 --- a/media/base/video_broadcaster.cc +++ b/media/base/video_broadcaster.cc @@ -32,6 +32,13 @@ void VideoBroadcaster::AddOrUpdateSink( if (!FindSinkPair(sink)) { // `Sink` is a new sink, which didn't receive previous frame. previous_frame_sent_to_all_sinks_ = false; + + if (last_constraints_.has_value()) { + RTC_LOG(LS_INFO) << __func__ << " forwarding stored constraints min_fps " + << last_constraints_->min_fps.value_or(-1) << " max_fps " + << last_constraints_->max_fps.value_or(-1); + sink->OnConstraintsChanged(*last_constraints_); + } } VideoSourceBase::AddOrUpdateSink(sink, wants); UpdateWants(); @@ -100,6 +107,18 @@ void VideoBroadcaster::OnDiscardedFrame() { } } +void VideoBroadcaster::ProcessConstraints( + const webrtc::VideoTrackSourceConstraints& constraints) { + webrtc::MutexLock lock(&sinks_and_wants_lock_); + RTC_LOG(LS_INFO) << __func__ << " min_fps " + << constraints.min_fps.value_or(-1) << " max_fps " + << constraints.max_fps.value_or(-1) << " broadcasting to " + << sink_pairs().size() << " sinks."; + last_constraints_ = constraints; + for (auto& sink_pair : sink_pairs()) + sink_pair.sink->OnConstraintsChanged(constraints); +} + void VideoBroadcaster::UpdateWants() { VideoSinkWants wants; wants.rotation_applied = false; diff --git a/media/base/video_broadcaster.h b/media/base/video_broadcaster.h index 2f4e578224..c253d44b09 100644 --- a/media/base/video_broadcaster.h +++ b/media/base/video_broadcaster.h @@ -11,6 +11,7 @@ #ifndef MEDIA_BASE_VIDEO_BROADCASTER_H_ #define MEDIA_BASE_VIDEO_BROADCASTER_H_ +#include "api/media_stream_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/video/video_frame_buffer.h" @@ -31,6 +32,11 @@ class VideoBroadcaster : public VideoSourceBase, public: VideoBroadcaster(); ~VideoBroadcaster() override; + + // Adds a new, or updates an already existing sink. If the sink is new and + // ProcessConstraints has been called previously, the new sink's + // OnConstraintsCalled method will be invoked with the most recent + // constraints. void AddOrUpdateSink(VideoSinkInterface* sink, const VideoSinkWants& wants) override; void RemoveSink(VideoSinkInterface* sink) override; @@ -50,6 +56,11 @@ class VideoBroadcaster : public VideoSourceBase, void OnDiscardedFrame() override; + // Called on the network thread when constraints change. Forwards the + // constraints to sinks added with AddOrUpdateSink via OnConstraintsChanged. + void ProcessConstraints( + const webrtc::VideoTrackSourceConstraints& constraints); + protected: void UpdateWants() RTC_EXCLUSIVE_LOCKS_REQUIRED(sinks_and_wants_lock_); const rtc::scoped_refptr& GetBlackFrameBuffer( @@ -62,6 +73,8 @@ class VideoBroadcaster : public VideoSourceBase, rtc::scoped_refptr black_frame_buffer_; bool previous_frame_sent_to_all_sinks_ RTC_GUARDED_BY(sinks_and_wants_lock_) = true; + absl::optional last_constraints_ + RTC_GUARDED_BY(sinks_and_wants_lock_); }; } // namespace rtc diff --git a/media/base/video_broadcaster_unittest.cc b/media/base/video_broadcaster_unittest.cc index b007278547..b9672375a7 100644 --- a/media/base/video_broadcaster_unittest.cc +++ b/media/base/video_broadcaster_unittest.cc @@ -16,13 +16,31 @@ #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "api/video/video_rotation.h" +#include "api/video/video_source_interface.h" #include "media/base/fake_video_renderer.h" +#include "test/gmock.h" #include "test/gtest.h" using cricket::FakeVideoRenderer; using rtc::VideoBroadcaster; using rtc::VideoSinkWants; +using ::testing::AllOf; +using ::testing::Eq; +using ::testing::Field; +using ::testing::Mock; +using ::testing::Optional; + +class MockSink : public rtc::VideoSinkInterface { + public: + void OnFrame(const webrtc::VideoFrame&) override {} + + MOCK_METHOD(void, + OnConstraintsChanged, + (const webrtc::VideoTrackSourceConstraints& constraints), + (override)); +}; + TEST(VideoBroadcasterTest, frame_wanted) { VideoBroadcaster broadcaster; EXPECT_FALSE(broadcaster.frame_wanted()); @@ -232,3 +250,83 @@ TEST(VideoBroadcasterTest, SinkWantsBlackFrames) { EXPECT_TRUE(sink2.black_frame()); EXPECT_EQ(30, sink2.timestamp_us()); } + +TEST(VideoBroadcasterTest, ConstraintsChangedNotCalledOnSinkAddition) { + MockSink sink; + VideoBroadcaster broadcaster; + EXPECT_CALL(sink, OnConstraintsChanged).Times(0); + broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); +} + +TEST(VideoBroadcasterTest, ForwardsLastConstraintsOnAdd) { + MockSink sink; + VideoBroadcaster broadcaster; + broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3}); + broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4}); + EXPECT_CALL( + sink, + OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4))))); + broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); +} + +TEST(VideoBroadcasterTest, UpdatesOnlyNewSinksWithConstraints) { + MockSink sink1; + VideoBroadcaster broadcaster; + broadcaster.AddOrUpdateSink(&sink1, VideoSinkWants()); + broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4}); + Mock::VerifyAndClearExpectations(&sink1); + EXPECT_CALL(sink1, OnConstraintsChanged).Times(0); + MockSink sink2; + EXPECT_CALL( + sink2, + OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4))))); + broadcaster.AddOrUpdateSink(&sink2, VideoSinkWants()); +} + +TEST(VideoBroadcasterTest, ForwardsConstraintsToSink) { + MockSink sink; + VideoBroadcaster broadcaster; + EXPECT_CALL(sink, OnConstraintsChanged).Times(0); + broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); + Mock::VerifyAndClearExpectations(&sink); + + EXPECT_CALL(sink, OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, + Eq(absl::nullopt)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, + Eq(absl::nullopt))))); + broadcaster.ProcessConstraints( + webrtc::VideoTrackSourceConstraints{absl::nullopt, absl::nullopt}); + Mock::VerifyAndClearExpectations(&sink); + + EXPECT_CALL( + sink, + OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, + Eq(absl::nullopt)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3))))); + broadcaster.ProcessConstraints( + webrtc::VideoTrackSourceConstraints{absl::nullopt, 3}); + Mock::VerifyAndClearExpectations(&sink); + + EXPECT_CALL( + sink, + OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, + Eq(absl::nullopt))))); + broadcaster.ProcessConstraints( + webrtc::VideoTrackSourceConstraints{2, absl::nullopt}); + Mock::VerifyAndClearExpectations(&sink); + + EXPECT_CALL( + sink, + OnConstraintsChanged(AllOf( + Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)), + Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3))))); + broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3}); +} diff --git a/media/engine/encoder_simulcast_proxy_unittest.cc b/media/engine/encoder_simulcast_proxy_unittest.cc index e5eb7a3703..fbd9d554bd 100644 --- a/media/engine/encoder_simulcast_proxy_unittest.cc +++ b/media/engine/encoder_simulcast_proxy_unittest.cc @@ -184,35 +184,5 @@ TEST(EncoderSimulcastProxy, ForwardsHardwareAccelerated) { EXPECT_TRUE(simulcast_enabled_proxy.GetEncoderInfo().is_hardware_accelerated); } -TEST(EncoderSimulcastProxy, ForwardsInternalSource) { - auto mock_encoder_owned = std::make_unique>(); - NiceMock* mock_encoder = mock_encoder_owned.get(); - NiceMock simulcast_factory; - - EXPECT_CALL(*mock_encoder, InitEncode(_, _)) - .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - - EXPECT_CALL(simulcast_factory, CreateVideoEncoder) - .Times(1) - .WillOnce(Return(ByMove(std::move(mock_encoder_owned)))); - - EncoderSimulcastProxy simulcast_enabled_proxy(&simulcast_factory, - SdpVideoFormat("VP8")); - VideoCodec codec_settings; - webrtc::test::CodecSettings(kVideoCodecVP8, &codec_settings); - EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, - simulcast_enabled_proxy.InitEncode(&codec_settings, kSettings)); - - VideoEncoder::EncoderInfo info; - - info.has_internal_source = false; - EXPECT_CALL(*mock_encoder, GetEncoderInfo()).WillOnce(Return(info)); - EXPECT_FALSE(simulcast_enabled_proxy.GetEncoderInfo().has_internal_source); - - info.has_internal_source = true; - EXPECT_CALL(*mock_encoder, GetEncoderInfo()).WillOnce(Return(info)); - EXPECT_TRUE(simulcast_enabled_proxy.GetEncoderInfo().has_internal_source); -} - } // namespace testing } // namespace webrtc diff --git a/media/engine/fake_webrtc_call.cc b/media/engine/fake_webrtc_call.cc index 62bcab9bfe..2f6efd6276 100644 --- a/media/engine/fake_webrtc_call.cc +++ b/media/engine/fake_webrtc_call.cc @@ -413,20 +413,22 @@ webrtc::FlexfecReceiveStream::Stats FakeFlexfecReceiveStream::GetStats() const { } void FakeFlexfecReceiveStream::OnRtpPacket(const webrtc::RtpPacketReceived&) { - RTC_NOTREACHED() << "Not implemented."; + RTC_DCHECK_NOTREACHED() << "Not implemented."; } -FakeCall::FakeCall() - : FakeCall(rtc::Thread::Current(), rtc::Thread::Current()) {} +FakeCall::FakeCall(webrtc::test::ScopedKeyValueConfig* field_trials) + : FakeCall(rtc::Thread::Current(), rtc::Thread::Current(), field_trials) {} FakeCall::FakeCall(webrtc::TaskQueueBase* worker_thread, - webrtc::TaskQueueBase* network_thread) + webrtc::TaskQueueBase* network_thread, + webrtc::test::ScopedKeyValueConfig* field_trials) : network_thread_(network_thread), worker_thread_(worker_thread), audio_network_state_(webrtc::kNetworkUp), video_network_state_(webrtc::kNetworkUp), num_created_send_streams_(0), - num_created_receive_streams_(0) {} + num_created_receive_streams_(0), + trials_(field_trials ? field_trials : &fallback_trials_) {} FakeCall::~FakeCall() { EXPECT_EQ(0u, video_send_streams_.size()); diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h index e732379cbd..96d881bb08 100644 --- a/media/engine/fake_webrtc_call.h +++ b/media/engine/fake_webrtc_call.h @@ -36,6 +36,7 @@ #include "call/video_send_stream.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/buffer.h" +#include "test/scoped_key_value_config.h" namespace cricket { class FakeAudioSendStream final : public webrtc::AudioSendStream { @@ -193,8 +194,7 @@ class FakeVideoSendStream final void OnFrame(const webrtc::VideoFrame& frame) override; // webrtc::VideoSendStream implementation. - void UpdateActiveSimulcastLayers( - const std::vector active_layers) override; + void UpdateActiveSimulcastLayers(std::vector active_layers) override; void Start() override; void Stop() override; bool started() override { return IsSending(); } @@ -314,9 +314,10 @@ class FakeFlexfecReceiveStream final : public webrtc::FlexfecReceiveStream { class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { public: - FakeCall(); + explicit FakeCall(webrtc::test::ScopedKeyValueConfig* field_trials = nullptr); FakeCall(webrtc::TaskQueueBase* worker_thread, - webrtc::TaskQueueBase* network_thread); + webrtc::TaskQueueBase* network_thread, + webrtc::test::ScopedKeyValueConfig* field_trials = nullptr); ~FakeCall() override; webrtc::MockRtpTransportControllerSend* GetMockTransportControllerSend() { @@ -354,6 +355,11 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { void SetClientBitratePreferences( const webrtc::BitrateSettings& preferences) override {} + void SetFieldTrial(const std::string& field_trial_string) { + trials_overrides_ = std::make_unique( + *trials_, field_trial_string); + } + private: webrtc::AudioSendStream* CreateAudioSendStream( const webrtc::AudioSendStream::Config& config) override; @@ -396,7 +402,7 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { webrtc::Call::Stats GetStats() const override; const webrtc::WebRtcKeyValueConfig& trials() const override { - return trials_; + return *trials_; } webrtc::TaskQueueBase* network_thread() const override; @@ -433,7 +439,16 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { int num_created_send_streams_; int num_created_receive_streams_; - webrtc::FieldTrialBasedConfig trials_; + + // The field trials that are in use, either supplied by caller + // or pointer to &fallback_trials_. + webrtc::test::ScopedKeyValueConfig* trials_; + + // fallback_trials_ is used if caller does not provide any field trials. + webrtc::test::ScopedKeyValueConfig fallback_trials_; + + // An extra field trial that can be set using SetFieldTrial. + std::unique_ptr trials_overrides_; }; } // namespace cricket diff --git a/media/engine/fake_webrtc_video_engine.cc b/media/engine/fake_webrtc_video_engine.cc index 6b042f7981..7383c26399 100644 --- a/media/engine/fake_webrtc_video_engine.cc +++ b/media/engine/fake_webrtc_video_engine.cc @@ -167,7 +167,6 @@ webrtc::VideoEncoder::EncoderInfo FakeWebRtcVideoEncoder::GetEncoderInfo() const { EncoderInfo info; info.is_hardware_accelerated = true; - info.has_internal_source = false; return info; } @@ -188,7 +187,6 @@ int FakeWebRtcVideoEncoder::GetNumEncodedFrames() { // Video encoder factory. FakeWebRtcVideoEncoderFactory::FakeWebRtcVideoEncoderFactory() : num_created_encoders_(0), - encoders_have_internal_sources_(false), vp8_factory_mode_(false) {} std::vector @@ -227,14 +225,6 @@ FakeWebRtcVideoEncoderFactory::CreateVideoEncoder( return encoder; } -webrtc::VideoEncoderFactory::CodecInfo -FakeWebRtcVideoEncoderFactory::QueryVideoEncoder( - const webrtc::SdpVideoFormat& format) const { - webrtc::VideoEncoderFactory::CodecInfo info; - info.has_internal_source = encoders_have_internal_sources_; - return info; -} - bool FakeWebRtcVideoEncoderFactory::WaitForCreatedVideoEncoders( int num_encoders) { int64_t start_offset_ms = rtc::TimeMillis(); @@ -254,11 +244,6 @@ void FakeWebRtcVideoEncoderFactory::EncoderDestroyed( encoders_.end()); } -void FakeWebRtcVideoEncoderFactory::set_encoders_have_internal_sources( - bool internal_source) { - encoders_have_internal_sources_ = internal_source; -} - void FakeWebRtcVideoEncoderFactory::AddSupportedVideoCodec( const webrtc::SdpVideoFormat& format) { formats_.push_back(format); diff --git a/media/engine/fake_webrtc_video_engine.h b/media/engine/fake_webrtc_video_engine.h index 831e6169c8..37ef53add7 100644 --- a/media/engine/fake_webrtc_video_engine.h +++ b/media/engine/fake_webrtc_video_engine.h @@ -116,8 +116,6 @@ class FakeWebRtcVideoEncoderFactory : public webrtc::VideoEncoderFactory { std::vector GetSupportedFormats() const override; std::unique_ptr CreateVideoEncoder( const webrtc::SdpVideoFormat& format) override; - CodecInfo QueryVideoEncoder( - const webrtc::SdpVideoFormat& format) const override; bool WaitForCreatedVideoEncoders(int num_encoders); void EncoderDestroyed(FakeWebRtcVideoEncoder* encoder); @@ -133,7 +131,6 @@ class FakeWebRtcVideoEncoderFactory : public webrtc::VideoEncoderFactory { std::vector formats_; std::vector encoders_ RTC_GUARDED_BY(mutex_); int num_created_encoders_ RTC_GUARDED_BY(mutex_); - bool encoders_have_internal_sources_; bool vp8_factory_mode_; }; diff --git a/media/engine/internal_decoder_factory.cc b/media/engine/internal_decoder_factory.cc index a8d1f00009..c24c488546 100644 --- a/media/engine/internal_decoder_factory.cc +++ b/media/engine/internal_decoder_factory.cc @@ -12,6 +12,7 @@ #include "absl/strings/match.h" #include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_codec.h" #include "media/base/codec.h" #include "media/base/media_constants.h" #include "modules/video_coding/codecs/av1/libaom_av1_decoder.h" @@ -20,8 +21,25 @@ #include "modules/video_coding/codecs/vp9/include/vp9.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) +#include "modules/video_coding/codecs/av1/dav1d_decoder.h" // nogncheck +#endif namespace webrtc { +namespace { +constexpr char kDav1dFieldTrial[] = "WebRTC-Dav1dDecoder"; +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) +constexpr bool kDav1dIsIncluded = true; +#else +constexpr bool kDav1dIsIncluded = false; +std::unique_ptr CreateDav1dDecoder() { + return nullptr; +} +#endif + +} // namespace std::vector InternalDecoderFactory::GetSupportedFormats() const { @@ -29,13 +47,35 @@ std::vector InternalDecoderFactory::GetSupportedFormats() formats.push_back(SdpVideoFormat(cricket::kVp8CodecName)); for (const SdpVideoFormat& format : SupportedVP9DecoderCodecs()) formats.push_back(format); - for (const SdpVideoFormat& h264_format : SupportedH264Codecs()) + for (const SdpVideoFormat& h264_format : SupportedH264DecoderCodecs()) formats.push_back(h264_format); - if (kIsLibaomAv1DecoderSupported) + + if (kIsLibaomAv1DecoderSupported || + (kDav1dIsIncluded && field_trial::IsEnabled(kDav1dFieldTrial))) { formats.push_back(SdpVideoFormat(cricket::kAv1CodecName)); + } + return formats; } +VideoDecoderFactory::CodecSupport InternalDecoderFactory::QueryCodecSupport( + const SdpVideoFormat& format, + bool reference_scaling) const { + // Query for supported formats and check if the specified format is supported. + // Return unsupported if an invalid combination of format and + // reference_scaling is specified. + if (reference_scaling) { + VideoCodecType codec = PayloadStringToCodecType(format.name); + if (codec != kVideoCodecVP9 && codec != kVideoCodecAV1) { + return {/*is_supported=*/false, /*is_power_efficient=*/false}; + } + } + + CodecSupport codec_support; + codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); + return codec_support; +} + std::unique_ptr InternalDecoderFactory::CreateVideoDecoder( const SdpVideoFormat& format) { if (!format.IsCodecInList(GetSupportedFormats())) { @@ -50,11 +90,18 @@ std::unique_ptr InternalDecoderFactory::CreateVideoDecoder( return VP9Decoder::Create(); if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName)) return H264Decoder::Create(); - if (kIsLibaomAv1DecoderSupported && - absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName)) - return CreateLibaomAv1Decoder(); - RTC_NOTREACHED(); + if (absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName) && + kDav1dIsIncluded && field_trial::IsEnabled(kDav1dFieldTrial)) { + return CreateDav1dDecoder(); + } + + if (absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName) && + kIsLibaomAv1DecoderSupported) { + return CreateLibaomAv1Decoder(); + } + + RTC_DCHECK_NOTREACHED(); return nullptr; } diff --git a/media/engine/internal_decoder_factory.h b/media/engine/internal_decoder_factory.h index 2a580dea0b..0129fb2173 100644 --- a/media/engine/internal_decoder_factory.h +++ b/media/engine/internal_decoder_factory.h @@ -24,6 +24,8 @@ namespace webrtc { class RTC_EXPORT InternalDecoderFactory : public VideoDecoderFactory { public: std::vector GetSupportedFormats() const override; + CodecSupport QueryCodecSupport(const SdpVideoFormat& format, + bool reference_scaling) const override; std::unique_ptr CreateVideoDecoder( const SdpVideoFormat& format) override; }; diff --git a/media/engine/internal_decoder_factory_unittest.cc b/media/engine/internal_decoder_factory_unittest.cc index a2a69211b9..d70390f934 100644 --- a/media/engine/internal_decoder_factory_unittest.cc +++ b/media/engine/internal_decoder_factory_unittest.cc @@ -19,48 +19,116 @@ #include "test/gtest.h" namespace webrtc { - +namespace { using ::testing::Contains; using ::testing::Field; using ::testing::Not; -TEST(InternalDecoderFactory, TestVP8) { +#ifdef RTC_ENABLE_VP9 +constexpr bool kVp9Enabled = true; +#else +constexpr bool kVp9Enabled = false; +#endif +#ifdef WEBRTC_USE_H264 +constexpr bool kH264Enabled = true; +#else +constexpr bool kH264Enabled = false; +#endif +constexpr VideoDecoderFactory::CodecSupport kSupported = { + /*is_supported=*/true, /*is_power_efficient=*/false}; +constexpr VideoDecoderFactory::CodecSupport kUnsupported = { + /*is_supported=*/false, /*is_power_efficient=*/false}; + +MATCHER_P(Support, expected, "") { + return arg.is_supported == expected.is_supported && + arg.is_power_efficient == expected.is_power_efficient; +} + +TEST(InternalDecoderFactoryTest, Vp8) { InternalDecoderFactory factory; std::unique_ptr decoder = factory.CreateVideoDecoder(SdpVideoFormat(cricket::kVp8CodecName)); EXPECT_TRUE(decoder); } -#ifdef RTC_ENABLE_VP9 -TEST(InternalDecoderFactory, TestVP9Profile0) { +TEST(InternalDecoderFactoryTest, Vp9Profile0) { InternalDecoderFactory factory; std::unique_ptr decoder = factory.CreateVideoDecoder(SdpVideoFormat( cricket::kVp9CodecName, {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}})); - EXPECT_TRUE(decoder); + EXPECT_EQ(static_cast(decoder), kVp9Enabled); } -TEST(InternalDecoderFactory, TestVP9Profile1) { +TEST(InternalDecoderFactoryTest, Vp9Profile1) { InternalDecoderFactory factory; std::unique_ptr decoder = factory.CreateVideoDecoder(SdpVideoFormat( cricket::kVp9CodecName, {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile1)}})); - EXPECT_TRUE(decoder); + EXPECT_EQ(static_cast(decoder), kVp9Enabled); } -#endif // RTC_ENABLE_VP9 -TEST(InternalDecoderFactory, Av1) { +TEST(InternalDecoderFactoryTest, H264) { + InternalDecoderFactory factory; + std::unique_ptr decoder = + factory.CreateVideoDecoder(SdpVideoFormat(cricket::kH264CodecName)); + EXPECT_EQ(static_cast(decoder), kH264Enabled); +} + +TEST(InternalDecoderFactoryTest, Av1) { InternalDecoderFactory factory; if (kIsLibaomAv1DecoderSupported) { EXPECT_THAT(factory.GetSupportedFormats(), - Contains(Field(&SdpVideoFormat::name, "AV1X"))); - EXPECT_TRUE(factory.CreateVideoDecoder(SdpVideoFormat("AV1X"))); + Contains(Field(&SdpVideoFormat::name, cricket::kAv1CodecName))); + EXPECT_TRUE( + factory.CreateVideoDecoder(SdpVideoFormat(cricket::kAv1CodecName))); } else { - EXPECT_THAT(factory.GetSupportedFormats(), - Not(Contains(Field(&SdpVideoFormat::name, "AV1X")))); + EXPECT_THAT( + factory.GetSupportedFormats(), + Not(Contains(Field(&SdpVideoFormat::name, cricket::kAv1CodecName)))); } } +TEST(InternalDecoderFactoryTest, QueryCodecSupportNoReferenceScaling) { + InternalDecoderFactory factory; + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp8CodecName), + /*reference_scaling=*/false), + Support(kSupported)); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp9CodecName), + /*reference_scaling=*/false), + Support(kVp9Enabled ? kSupported : kUnsupported)); + EXPECT_THAT(factory.QueryCodecSupport( + SdpVideoFormat(cricket::kVp9CodecName, + {{kVP9FmtpProfileId, + VP9ProfileToString(VP9Profile::kProfile1)}}), + /*reference_scaling=*/false), + Support(kVp9Enabled ? kSupported : kUnsupported)); + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kAv1CodecName), + /*reference_scaling=*/false), + Support(kIsLibaomAv1DecoderSupported ? kSupported : kUnsupported)); +} + +TEST(InternalDecoderFactoryTest, QueryCodecSupportReferenceScaling) { + InternalDecoderFactory factory; + // VP9 and AV1 support for spatial layers. + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp9CodecName), + /*reference_scaling=*/true), + Support(kVp9Enabled ? kSupported : kUnsupported)); + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kAv1CodecName), + /*reference_scaling=*/true), + Support(kIsLibaomAv1DecoderSupported ? kSupported : kUnsupported)); + + // Invalid config even though VP8 and H264 are supported. + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kH264CodecName), + /*reference_scaling=*/true), + Support(kUnsupported)); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp8CodecName), + /*reference_scaling=*/true), + Support(kUnsupported)); +} + +} // namespace } // namespace webrtc diff --git a/media/engine/internal_encoder_factory.cc b/media/engine/internal_encoder_factory.cc index 738516eafc..5ccb93d0c1 100644 --- a/media/engine/internal_encoder_factory.cc +++ b/media/engine/internal_encoder_factory.cc @@ -16,7 +16,7 @@ #include "api/video_codecs/sdp_video_format.h" #include "media/base/codec.h" #include "media/base/media_constants.h" -#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" +#include "modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h" #include "modules/video_coding/codecs/h264/include/h264.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" #include "modules/video_coding/codecs/vp9/include/vp9.h" @@ -51,10 +51,44 @@ std::unique_ptr InternalEncoderFactory::CreateVideoEncoder( return H264Encoder::Create(cricket::VideoCodec(format)); if (kIsLibaomAv1EncoderSupported && absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName)) - return CreateLibaomAv1Encoder(); + return CreateLibaomAv1EncoderIfSupported(); RTC_LOG(LS_ERROR) << "Trying to created encoder of unsupported format " << format.name; return nullptr; } +VideoEncoderFactory::CodecSupport InternalEncoderFactory::QueryCodecSupport( + const SdpVideoFormat& format, + absl::optional scalability_mode) const { + // Query for supported formats and check if the specified format is supported. + // Begin with filtering out unsupported scalability modes. + if (scalability_mode) { + bool scalability_mode_supported = false; + if (absl::EqualsIgnoreCase(format.name, cricket::kVp8CodecName)) { + scalability_mode_supported = + VP8Encoder::SupportsScalabilityMode(*scalability_mode); + } else if (absl::EqualsIgnoreCase(format.name, cricket::kVp9CodecName)) { + scalability_mode_supported = + VP9Encoder::SupportsScalabilityMode(*scalability_mode); + } else if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName)) { + scalability_mode_supported = + H264Encoder::SupportsScalabilityMode(*scalability_mode); + } else if (kIsLibaomAv1EncoderSupported && + absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName)) { + scalability_mode_supported = + LibaomAv1EncoderSupportsScalabilityMode(*scalability_mode); + } + + static constexpr VideoEncoderFactory::CodecSupport kUnsupported = { + /*is_supported=*/false, /*is_power_efficient=*/false}; + if (!scalability_mode_supported) { + return kUnsupported; + } + } + + CodecSupport codec_support; + codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); + return codec_support; +} + } // namespace webrtc diff --git a/media/engine/internal_encoder_factory.h b/media/engine/internal_encoder_factory.h index 3f43e461a7..e12810cd30 100644 --- a/media/engine/internal_encoder_factory.h +++ b/media/engine/internal_encoder_factory.h @@ -12,8 +12,10 @@ #define MEDIA_ENGINE_INTERNAL_ENCODER_FACTORY_H_ #include +#include #include +#include "absl/types/optional.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" @@ -25,7 +27,9 @@ class RTC_EXPORT InternalEncoderFactory : public VideoEncoderFactory { public: static std::vector SupportedFormats(); std::vector GetSupportedFormats() const override; - + CodecSupport QueryCodecSupport( + const SdpVideoFormat& format, + absl::optional scalability_mode) const override; std::unique_ptr CreateVideoEncoder( const SdpVideoFormat& format) override; }; diff --git a/media/engine/internal_encoder_factory_unittest.cc b/media/engine/internal_encoder_factory_unittest.cc new file mode 100644 index 0000000000..c3a667e733 --- /dev/null +++ b/media/engine/internal_encoder_factory_unittest.cc @@ -0,0 +1,139 @@ +/* + * 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 "media/engine/internal_encoder_factory.h" + +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/vp9_profile.h" +#include "media/base/media_constants.h" +#include "modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { +using ::testing::Contains; +using ::testing::Field; +using ::testing::Not; + +#ifdef RTC_ENABLE_VP9 +constexpr bool kVp9Enabled = true; +#else +constexpr bool kVp9Enabled = false; +#endif +#ifdef WEBRTC_USE_H264 +constexpr bool kH264Enabled = true; +#else +constexpr bool kH264Enabled = false; +#endif +constexpr VideoEncoderFactory::CodecSupport kSupported = { + /*is_supported=*/true, /*is_power_efficient=*/false}; +constexpr VideoEncoderFactory::CodecSupport kUnsupported = { + /*is_supported=*/false, /*is_power_efficient=*/false}; + +MATCHER_P(Support, expected, "") { + return arg.is_supported == expected.is_supported && + arg.is_power_efficient == expected.is_power_efficient; +} + +TEST(InternalEncoderFactoryTest, Vp8) { + InternalEncoderFactory factory; + std::unique_ptr encoder = + factory.CreateVideoEncoder(SdpVideoFormat(cricket::kVp8CodecName)); + EXPECT_TRUE(encoder); +} + +TEST(InternalEncoderFactoryTest, Vp9Profile0) { + InternalEncoderFactory factory; + if (kVp9Enabled) { + std::unique_ptr encoder = + factory.CreateVideoEncoder(SdpVideoFormat( + cricket::kVp9CodecName, + {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}})); + EXPECT_TRUE(encoder); + } else { + EXPECT_THAT( + factory.GetSupportedFormats(), + Not(Contains(Field(&SdpVideoFormat::name, cricket::kVp9CodecName)))); + } +} + +TEST(InternalEncoderFactoryTest, H264) { + InternalEncoderFactory factory; + if (kH264Enabled) { + std::unique_ptr encoder = + factory.CreateVideoEncoder(SdpVideoFormat(cricket::kH264CodecName)); + EXPECT_TRUE(encoder); + } else { + EXPECT_THAT( + factory.GetSupportedFormats(), + Not(Contains(Field(&SdpVideoFormat::name, cricket::kH264CodecName)))); + } +} + +TEST(InternalEncoderFactoryTest, Av1) { + InternalEncoderFactory factory; + if (kIsLibaomAv1EncoderSupported) { + EXPECT_THAT(factory.GetSupportedFormats(), + Contains(Field(&SdpVideoFormat::name, cricket::kAv1CodecName))); + EXPECT_TRUE( + factory.CreateVideoEncoder(SdpVideoFormat(cricket::kAv1CodecName))); + } else { + EXPECT_THAT( + factory.GetSupportedFormats(), + Not(Contains(Field(&SdpVideoFormat::name, cricket::kAv1CodecName)))); + } +} + +TEST(InternalEncoderFactoryTest, QueryCodecSupportNoScalabilityMode) { + InternalEncoderFactory factory; + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp8CodecName), + /*scalability_mode=*/absl::nullopt), + Support(kSupported)); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp9CodecName), + /*scalability_mode=*/absl::nullopt), + Support(kVp9Enabled ? kSupported : kUnsupported)); + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kAv1CodecName), + /*scalability_mode=*/absl::nullopt), + Support(kIsLibaomAv1EncoderSupported ? kSupported : kUnsupported)); +} + +TEST(InternalEncoderFactoryTest, QueryCodecSupportWithScalabilityMode) { + InternalEncoderFactory factory; + // VP8 and VP9 supported for singles spatial layers. + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp8CodecName), "L1T2"), + Support(kSupported)); + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp9CodecName), "L1T3"), + Support(kVp9Enabled ? kSupported : kUnsupported)); + + // VP9 support for spatial layers. + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp9CodecName), "L3T3"), + Support(kVp9Enabled ? kSupported : kUnsupported)); + + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kAv1CodecName), "L2T1"), + Support(kIsLibaomAv1EncoderSupported ? kSupported : kUnsupported)); + + // Invalid scalability modes even though VP8 and H264 are supported. + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat(cricket::kH264CodecName), + "L2T2"), + Support(kUnsupported)); + EXPECT_THAT( + factory.QueryCodecSupport(SdpVideoFormat(cricket::kVp8CodecName), "L3T3"), + Support(kUnsupported)); +} + +} // namespace +} // namespace webrtc diff --git a/media/engine/payload_type_mapper.cc b/media/engine/payload_type_mapper.cc index cbc0a5340d..c63d1d7221 100644 --- a/media/engine/payload_type_mapper.cc +++ b/media/engine/payload_type_mapper.cc @@ -73,7 +73,12 @@ PayloadTypeMapper::PayloadTypeMapper() {kCodecParamUseInbandFec, kParamValueTrue}}}, 111}, // RED for opus is assigned in the lower range, starting at the top. - {{kRedCodecName, 48000, 2}, 63}, + // Note that the FMTP refers to the opus payload type. + {{kRedCodecName, + 48000, + 2, + {{kCodecParamNotInNameValueFormat, "111/111"}}}, + 63}, // TODO(solenberg): Remove the hard coded 16k,32k,48k DTMF once we // assign payload types dynamically for send side as well. {{kDtmfCodecName, 48000, 1}, 110}, diff --git a/media/engine/payload_type_mapper_unittest.cc b/media/engine/payload_type_mapper_unittest.cc index 9c29827fa9..2d9372454b 100644 --- a/media/engine/payload_type_mapper_unittest.cc +++ b/media/engine/payload_type_mapper_unittest.cc @@ -58,7 +58,8 @@ TEST_F(PayloadTypeMapperTest, WebRTCPayloadTypes) { 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}})); - EXPECT_EQ(63, mapper_.FindMappingFor({kRedCodecName, 48000, 2})); + EXPECT_EQ( + 63, mapper_.FindMappingFor({kRedCodecName, 48000, 2, {{"", "111/111"}}})); // TODO(solenberg): Remove 16k, 32k, 48k DTMF checks once these payload types // are dynamically assigned. EXPECT_EQ(110, mapper_.FindMappingFor({kDtmfCodecName, 48000, 1})); diff --git a/media/engine/simulcast.cc b/media/engine/simulcast.cc index 18857b973b..51141e903c 100644 --- a/media/engine/simulcast.cc +++ b/media/engine/simulcast.cc @@ -178,7 +178,7 @@ int FindSimulcastFormatIndex(int width, return i; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc index 721ec88a24..704c2d6a0b 100644 --- a/media/engine/simulcast_encoder_adapter.cc +++ b/media/engine/simulcast_encoder_adapter.cc @@ -159,8 +159,8 @@ SimulcastEncoderAdapter::EncoderContext::EncoderContext( void SimulcastEncoderAdapter::EncoderContext::Release() { if (encoder_) { - encoder_->RegisterEncodeCompleteCallback(nullptr); encoder_->Release(); + encoder_->RegisterEncodeCompleteCallback(nullptr); } } @@ -709,17 +709,40 @@ SimulcastEncoderAdapter::FetchOrCreateEncoderContext( encoder_context = std::move(*encoder_context_iter); cached_encoder_contexts_.erase(encoder_context_iter); } else { - std::unique_ptr encoder = + std::unique_ptr primary_encoder = primary_encoder_factory_->CreateVideoEncoder(video_format_); - VideoEncoder::EncoderInfo primary_info = encoder->GetEncoderInfo(); - VideoEncoder::EncoderInfo fallback_info = primary_info; + + std::unique_ptr fallback_encoder; if (fallback_encoder_factory_ != nullptr) { - std::unique_ptr fallback_encoder = + fallback_encoder = fallback_encoder_factory_->CreateVideoEncoder(video_format_); + } + + std::unique_ptr encoder; + VideoEncoder::EncoderInfo primary_info; + VideoEncoder::EncoderInfo fallback_info; + + if (primary_encoder != nullptr) { + primary_info = primary_encoder->GetEncoderInfo(); + fallback_info = primary_info; + + if (fallback_encoder == nullptr) { + encoder = std::move(primary_encoder); + } else { + encoder = CreateVideoEncoderSoftwareFallbackWrapper( + std::move(fallback_encoder), std::move(primary_encoder), + prefer_temporal_support); + } + } else if (fallback_encoder != nullptr) { + RTC_LOG(LS_WARNING) << "Failed to create primary " << video_format_.name + << " encoder. Use fallback encoder."; fallback_info = fallback_encoder->GetEncoderInfo(); - encoder = CreateVideoEncoderSoftwareFallbackWrapper( - std::move(fallback_encoder), std::move(encoder), - prefer_temporal_support); + primary_info = fallback_info; + encoder = std::move(fallback_encoder); + } else { + RTC_LOG(LS_ERROR) << "Failed to create primary and fallback " + << video_format_.name << " encoders."; + return nullptr; } encoder_context = std::make_unique( @@ -768,8 +791,8 @@ webrtc::VideoCodec SimulcastEncoderAdapter::MakeStreamCodec( // kComplexityHigher, which maps to cpu_used = -4. int pixels_per_frame = codec_params.width * codec_params.height; if (pixels_per_frame < 352 * 288) { - codec_params.VP8()->complexity = - webrtc::VideoCodecComplexity::kComplexityHigher; + codec_params.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); } // Turn off denoising for all streams but the highest resolution. codec_params.VP8()->denoisingOn = false; @@ -829,7 +852,10 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { // Create one encoder and query it. std::unique_ptr encoder_context = - FetchOrCreateEncoderContext(true); + FetchOrCreateEncoderContext(/*is_lowest_quality_stream=*/true); + if (encoder_context == nullptr) { + return encoder_info; + } const VideoEncoder::EncoderInfo& primary_info = encoder_context->PrimaryInfo(); @@ -870,7 +896,6 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { encoder_impl_info.has_trusted_rate_controller; encoder_info.is_hardware_accelerated = encoder_impl_info.is_hardware_accelerated; - encoder_info.has_internal_source = encoder_impl_info.has_internal_source; encoder_info.is_qp_trusted = encoder_impl_info.is_qp_trusted; } else { encoder_info.implementation_name += ", "; @@ -891,13 +916,10 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { encoder_info.is_hardware_accelerated |= encoder_impl_info.is_hardware_accelerated; - // Has internal source only if all encoders have it. - encoder_info.has_internal_source &= encoder_impl_info.has_internal_source; - // Treat QP from frame/slice/tile header as average QP only if all // encoders report it as average QP. encoder_info.is_qp_trusted = - encoder_info.is_qp_trusted.value_or(true) & + encoder_info.is_qp_trusted.value_or(true) && encoder_impl_info.is_qp_trusted.value_or(true); } encoder_info.fps_allocation[i] = encoder_impl_info.fps_allocation[0]; diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc index 5f3e54f3aa..5025656ae0 100644 --- a/media/engine/simulcast_encoder_adapter_unittest.cc +++ b/media/engine/simulcast_encoder_adapter_unittest.cc @@ -171,6 +171,9 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { const std::vector& encoders() const; void SetEncoderNames(const std::vector& encoder_names); + void set_create_video_encode_return_nullptr(bool return_nullptr) { + create_video_encoder_return_nullptr_ = return_nullptr; + } void set_init_encode_return_value(int32_t value); void set_requested_resolution_alignments( std::vector requested_resolution_alignments) { @@ -183,6 +186,7 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { void DestroyVideoEncoder(VideoEncoder* encoder); private: + bool create_video_encoder_return_nullptr_ = false; int32_t init_encode_return_value_ = 0; std::vector encoders_; std::vector encoder_names_; @@ -238,7 +242,6 @@ class MockVideoEncoder : public VideoEncoder { apply_alignment_to_all_simulcast_layers_; info.has_trusted_rate_controller = has_trusted_rate_controller_; info.is_hardware_accelerated = is_hardware_accelerated_; - info.has_internal_source = has_internal_source_; info.fps_allocation[0] = fps_allocation_; info.supports_simulcast = supports_simulcast_; info.is_qp_trusted = is_qp_trusted_; @@ -291,10 +294,6 @@ class MockVideoEncoder : public VideoEncoder { is_hardware_accelerated_ = is_hardware_accelerated; } - void set_has_internal_source(bool has_internal_source) { - has_internal_source_ = has_internal_source; - } - void set_fps_allocation(const FramerateFractions& fps_allocation) { fps_allocation_ = fps_allocation; } @@ -326,7 +325,6 @@ class MockVideoEncoder : public VideoEncoder { bool apply_alignment_to_all_simulcast_layers_ = false; bool has_trusted_rate_controller_ = false; bool is_hardware_accelerated_ = false; - bool has_internal_source_ = false; int32_t init_encode_return_value_ = 0; VideoEncoder::RateControlParameters last_set_rates_; FramerateFractions fps_allocation_; @@ -346,6 +344,10 @@ std::vector MockVideoEncoderFactory::GetSupportedFormats() std::unique_ptr MockVideoEncoderFactory::CreateVideoEncoder( const SdpVideoFormat& format) { + if (create_video_encoder_return_nullptr_) { + return nullptr; + } + auto encoder = std::make_unique<::testing::NiceMock>(this); encoder->set_init_encode_return_value(init_encode_return_value_); const char* encoder_name = encoder_names_.empty() @@ -500,7 +502,8 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, EXPECT_EQ(ref.maxBitrate, target.maxBitrate); EXPECT_EQ(ref.minBitrate, target.minBitrate); EXPECT_EQ(ref.maxFramerate, target.maxFramerate); - EXPECT_EQ(ref.VP8().complexity, target.VP8().complexity); + EXPECT_EQ(ref.GetVideoEncoderComplexity(), + target.GetVideoEncoderComplexity()); EXPECT_EQ(ref.VP8().numberOfTemporalLayers, target.VP8().numberOfTemporalLayers); EXPECT_EQ(ref.VP8().denoisingOn, target.VP8().denoisingOn); @@ -536,8 +539,8 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, // stream 0, the lowest resolution stream. InitRefCodec(0, &ref_codec); ref_codec.qpMax = 45; - ref_codec.VP8()->complexity = - webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); @@ -984,7 +987,7 @@ class FakeNativeBufferI420 : public VideoFrameBuffer { if (allow_to_i420_) { return I420Buffer::Create(width_, height_); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return nullptr; } @@ -1132,7 +1135,8 @@ TEST_F(TestSimulcastEncoderAdapterFake, DoesNotAlterMaxQpForScreenshare) { VideoCodec ref_codec; InitRefCodec(0, &ref_codec); ref_codec.qpMax = kHighMaxQp; - ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); @@ -1165,7 +1169,8 @@ TEST_F(TestSimulcastEncoderAdapterFake, VideoCodec ref_codec; InitRefCodec(2, &ref_codec, true /* reverse_layer_order */); ref_codec.qpMax = kHighMaxQp; - ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 2); @@ -1371,28 +1376,6 @@ TEST_F(TestSimulcastEncoderAdapterFake, VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000})); } -TEST_F(TestSimulcastEncoderAdapterFake, ReportsInternalSource) { - SimulcastTestFixtureImpl::DefaultSettings( - &codec_, static_cast(kTestTemporalLayerProfile), - kVideoCodecVP8); - codec_.numberOfSimulcastStreams = 3; - adapter_->RegisterEncodeCompleteCallback(this); - EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); - ASSERT_EQ(3u, helper_->factory()->encoders().size()); - - // All encoders have internal source, simulcast adapter reports true. - for (MockVideoEncoder* encoder : helper_->factory()->encoders()) { - encoder->set_has_internal_source(true); - } - EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); - EXPECT_TRUE(adapter_->GetEncoderInfo().has_internal_source); - - // One encoder does not have internal source, simulcast adapter reports false. - helper_->factory()->encoders()[2]->set_has_internal_source(false); - EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); - EXPECT_FALSE(adapter_->GetEncoderInfo().has_internal_source); -} - TEST_F(TestSimulcastEncoderAdapterFake, ReportsIsQpTrusted) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), @@ -1711,5 +1694,46 @@ TEST_F(TestSimulcastEncoderAdapterFake, EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder); } +TEST_F(TestSimulcastEncoderAdapterFake, + UseFallbackEncoderIfCreatePrimaryEncoderFailed) { + // Enable support for fallback encoder factory and re-setup. + use_fallback_factory_ = true; + SetUp(); + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 1; + helper_->factory()->SetEncoderNames({"primary"}); + helper_->fallback_factory()->SetEncoderNames({"fallback"}); + + // Emulate failure at creating of primary encoder and verify that SEA switches + // to fallback encoder. + helper_->factory()->set_create_video_encode_return_nullptr(true); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + ASSERT_EQ(0u, helper_->factory()->encoders().size()); + ASSERT_EQ(1u, helper_->fallback_factory()->encoders().size()); + EXPECT_EQ("fallback", adapter_->GetEncoderInfo().implementation_name); +} + +TEST_F(TestSimulcastEncoderAdapterFake, + InitEncodeReturnsErrorIfEncoderCannotBeCreated) { + // Enable support for fallback encoder factory and re-setup. + use_fallback_factory_ = true; + SetUp(); + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 1; + helper_->factory()->SetEncoderNames({"primary"}); + helper_->fallback_factory()->SetEncoderNames({"fallback"}); + + // Emulate failure at creating of primary and fallback encoders and verify + // that `InitEncode` returns an error. + helper_->factory()->set_create_video_encode_return_nullptr(true); + helper_->fallback_factory()->set_create_video_encode_return_nullptr(true); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_MEMORY, + adapter_->InitEncode(&codec_, kSettings)); +} + } // namespace test } // namespace webrtc diff --git a/media/engine/webrtc_media_engine.cc b/media/engine/webrtc_media_engine.cc index 6ce52e4c8e..f083b9c9ca 100644 --- a/media/engine/webrtc_media_engine.cc +++ b/media/engine/webrtc_media_engine.cc @@ -10,6 +10,7 @@ #include "media/engine/webrtc_media_engine.h" +#include #include #include @@ -74,7 +75,8 @@ void DiscardRedundantExtensions( } // namespace bool ValidateRtpExtensions( - const std::vector& extensions) { + rtc::ArrayView extensions, + rtc::ArrayView old_extensions) { bool id_used[1 + webrtc::RtpExtension::kMaxId] = {false}; for (const auto& extension : extensions) { if (extension.id < webrtc::RtpExtension::kMinId || @@ -89,6 +91,45 @@ bool ValidateRtpExtensions( } id_used[extension.id] = true; } + // Validate the extension list against the already negotiated extensions. + // Re-registering is OK, re-mapping (either same URL at new ID or same + // ID used with new URL) is an illegal remap. + + // This is required in order to avoid a crash when registering an + // extension. A better structure would use the registered extensions + // in the RTPSender. This requires spinning through: + // + // WebRtcVoiceMediaChannel::::WebRtcAudioSendStream::stream_ (pointer) + // AudioSendStream::rtp_rtcp_module_ (pointer) + // ModuleRtpRtcpImpl2::rtp_sender_ (pointer) + // RtpSenderContext::packet_generator (struct member) + // RTPSender::rtp_header_extension_map_ (class member) + // + // Getting at this seems like a hard slog. + if (!old_extensions.empty()) { + absl::string_view urimap[1 + webrtc::RtpExtension::kMaxId]; + std::map idmap; + for (const auto& old_extension : old_extensions) { + urimap[old_extension.id] = old_extension.uri; + idmap[old_extension.uri] = old_extension.id; + } + for (const auto& extension : extensions) { + if (!urimap[extension.id].empty() && + urimap[extension.id] != extension.uri) { + RTC_LOG(LS_ERROR) << "Extension negotiation failure: " << extension.id + << " was mapped to " << urimap[extension.id] + << " but is proposed changed to " << extension.uri; + return false; + } + const auto& it = idmap.find(extension.uri); + if (it != idmap.end() && it->second != extension.id) { + RTC_LOG(LS_ERROR) << "Extension negotation failure: " << extension.uri + << " was identified by " << it->second + << " but is proposed changed to " << extension.id; + return false; + } + } + } return true; } @@ -97,7 +138,8 @@ std::vector FilterRtpExtensions( bool (*supported)(absl::string_view), bool filter_redundant_extensions, const webrtc::WebRtcKeyValueConfig& trials) { - RTC_DCHECK(ValidateRtpExtensions(extensions)); + // Don't check against old parameters; this should have been done earlier. + RTC_DCHECK(ValidateRtpExtensions(extensions, {})); RTC_DCHECK(supported); std::vector result; diff --git a/media/engine/webrtc_media_engine.h b/media/engine/webrtc_media_engine.h index 34ec4cdc9c..ff977609b2 100644 --- a/media/engine/webrtc_media_engine.h +++ b/media/engine/webrtc_media_engine.h @@ -63,8 +63,11 @@ RTC_EXPORT std::unique_ptr CreateMediaEngine( MediaEngineDependencies dependencies); // Verify that extension IDs are within 1-byte extension range and are not -// overlapping. -bool ValidateRtpExtensions(const std::vector& extensions); +// overlapping, and that they form a legal change from previously registerd +// extensions (if any). +bool ValidateRtpExtensions( + rtc::ArrayView extennsions, + rtc::ArrayView old_extensions); // Discard any extensions not validated by the 'supported' predicate. Duplicate // extensions are removed if 'filter_redundant_extensions' is set, and also any diff --git a/media/engine/webrtc_media_engine_unittest.cc b/media/engine/webrtc_media_engine_unittest.cc index 78d13fc297..79efea4e9c 100644 --- a/media/engine/webrtc_media_engine_unittest.cc +++ b/media/engine/webrtc_media_engine_unittest.cc @@ -13,10 +13,9 @@ #include #include -#include "api/transport/field_trial_based_config.h" #include "media/engine/webrtc_media_engine_defaults.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using webrtc::RtpExtension; @@ -66,51 +65,78 @@ bool IsSorted(const std::vector& extensions) { } } // namespace -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_EmptyList) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsEmptyList) { std::vector extensions; - EXPECT_TRUE(ValidateRtpExtensions(extensions)); + EXPECT_TRUE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_AllGood) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsAllGood) { std::vector extensions = MakeUniqueExtensions(); - EXPECT_TRUE(ValidateRtpExtensions(extensions)); + EXPECT_TRUE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OutOfRangeId_Low) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOutOfRangeId_Low) { std::vector extensions = MakeUniqueExtensions(); extensions.push_back(RtpExtension("foo", 0)); - EXPECT_FALSE(ValidateRtpExtensions(extensions)); + EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OutOfRangeId_High) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOutOfRangeIdHigh) { std::vector extensions = MakeUniqueExtensions(); extensions.push_back(RtpExtension("foo", 256)); - EXPECT_FALSE(ValidateRtpExtensions(extensions)); + EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OverlappingIds_StartOfSet) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOverlappingIdsStartOfSet) { std::vector extensions = MakeUniqueExtensions(); extensions.push_back(RtpExtension("foo", 1)); - EXPECT_FALSE(ValidateRtpExtensions(extensions)); + EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OverlappingIds_EndOfSet) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOverlappingIdsEndOfSet) { std::vector extensions = MakeUniqueExtensions(); extensions.push_back(RtpExtension("foo", 255)); - EXPECT_FALSE(ValidateRtpExtensions(extensions)); + EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_EmptyList) { +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsEmptyToEmpty) { std::vector extensions; - webrtc::FieldTrialBasedConfig trials; + EXPECT_TRUE(ValidateRtpExtensions(extensions, extensions)); +} + +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsNoChange) { + std::vector extensions = MakeUniqueExtensions(); + EXPECT_TRUE(ValidateRtpExtensions(extensions, extensions)); +} + +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsChangeIdNotUrl) { + std::vector old_extensions = MakeUniqueExtensions(); + std::vector new_extensions = old_extensions; + std::swap(new_extensions[0].id, new_extensions[1].id); + + EXPECT_FALSE(ValidateRtpExtensions(new_extensions, old_extensions)); +} + +TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsChangeIdForUrl) { + std::vector old_extensions = MakeUniqueExtensions(); + std::vector new_extensions = old_extensions; + // Change first extension to something not generated by MakeUniqueExtensions + new_extensions[0].id = 123; + + EXPECT_FALSE(ValidateRtpExtensions(new_extensions, old_extensions)); +} + +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsEmptyList) { + std::vector extensions; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions1, true, trials); EXPECT_EQ(0u, filtered.size()); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_IncludeOnlySupported) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsIncludeOnlySupported) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions1, false, trials); EXPECT_EQ(2u, filtered.size()); @@ -118,27 +144,27 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_IncludeOnlySupported) { EXPECT_EQ("i", filtered[1].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_SortedByName_1) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName1) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, false, trials); EXPECT_EQ(12u, filtered.size()); EXPECT_TRUE(IsSorted(filtered)); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_SortedByName_2) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName2) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(12u, filtered.size()); EXPECT_TRUE(IsSorted(filtered)); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_DontRemoveRedundant) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsDontRemoveRedundant) { std::vector extensions = MakeRedundantExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, false, trials); EXPECT_EQ(12u, filtered.size()); @@ -146,9 +172,9 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_DontRemoveRedundant) { EXPECT_EQ(filtered[0].uri, filtered[1].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundant) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundant) { std::vector extensions = MakeRedundantExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(6u, filtered.size()); @@ -156,13 +182,13 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundant) { EXPECT_NE(filtered[0].uri, filtered[1].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_1) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantEncrypted1) { std::vector extensions; extensions.push_back(webrtc::RtpExtension("b", 1)); extensions.push_back(webrtc::RtpExtension("b", 2, true)); extensions.push_back(webrtc::RtpExtension("c", 3)); extensions.push_back(webrtc::RtpExtension("b", 4)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -173,13 +199,13 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_1) { EXPECT_NE(filtered[1].uri, filtered[2].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_2) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantEncrypted2) { std::vector extensions; extensions.push_back(webrtc::RtpExtension("b", 1, true)); extensions.push_back(webrtc::RtpExtension("b", 2)); extensions.push_back(webrtc::RtpExtension("c", 3)); extensions.push_back(webrtc::RtpExtension("b", 4)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -190,10 +216,9 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_2) { EXPECT_NE(filtered[1].uri, filtered[2].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_1) { - webrtc::test::ScopedFieldTrials override_field_trials_( +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe1) { + webrtc::test::ScopedKeyValueConfig trials( "WebRTC-FilterAbsSendTimeExtension/Enabled/"); - webrtc::FieldTrialBasedConfig trials; std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -209,7 +234,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_1) { } TEST(WebRtcMediaEngineTest, - FilterRtpExtensions_RemoveRedundantBwe_1_KeepAbsSendTime) { + FilterRtpExtensionsRemoveRedundantBwe1KeepAbsSendTime) { std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -218,7 +243,7 @@ TEST(WebRtcMediaEngineTest, extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(2u, filtered.size()); @@ -226,10 +251,9 @@ TEST(WebRtcMediaEngineTest, EXPECT_EQ(RtpExtension::kAbsSendTimeUri, filtered[1].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBweEncrypted_1) { - webrtc::test::ScopedFieldTrials override_field_trials_( +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBweEncrypted1) { + webrtc::test::ScopedKeyValueConfig trials( "WebRTC-FilterAbsSendTimeExtension/Enabled/"); - webrtc::FieldTrialBasedConfig trials; std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -251,7 +275,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBweEncrypted_1) { } TEST(WebRtcMediaEngineTest, - FilterRtpExtensions_RemoveRedundantBweEncrypted_1_KeepAbsSendTime) { + FilterRtpExtensionsRemoveRedundantBweEncrypted1KeepAbsSendTime) { std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -264,7 +288,7 @@ TEST(WebRtcMediaEngineTest, extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 2, true)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -274,23 +298,23 @@ TEST(WebRtcMediaEngineTest, EXPECT_NE(filtered[0].encrypt, filtered[1].encrypt); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_2) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe2) { std::vector extensions; extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 1)); extensions.push_back(RtpExtension(RtpExtension::kAbsSendTimeUri, 14)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 7)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(1u, filtered.size()); EXPECT_EQ(RtpExtension::kAbsSendTimeUri, filtered[0].uri); } -TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_3) { +TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe3) { std::vector extensions; extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 2)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(1u, filtered.size()); @@ -300,7 +324,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_3) { TEST(WebRtcMediaEngineTest, Create) { MediaEngineDependencies deps; webrtc::SetMediaEngineDefaults(&deps); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; deps.trials = &trials; std::unique_ptr engine = diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 9bb330cc87..3e58e48c20 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -53,6 +53,22 @@ using ::webrtc::ParseRtpSsrc; const int kMinLayerSize = 16; constexpr int64_t kUnsignaledSsrcCooldownMs = rtc::kNumMillisecsPerSec / 2; +// TODO(bugs.webrtc.org/13166): Remove AV1X when backwards compatibility is not +// needed. +constexpr char kAv1xCodecName[] = "AV1X"; + +int ScaleDownResolution(int resolution, + double scale_down_by, + int min_resolution) { + // Resolution is never scalied down to smaller than min_resolution. + // If the input resolution is already smaller than min_resolution, + // no scaling should be done at all. + if (resolution <= min_resolution) + return resolution; + return std::max(static_cast(resolution / scale_down_by + 0.5), + min_resolution); +} + const char* StreamTypeToString( webrtc::VideoSendStream::StreamStats::StreamType type) { switch (type) { @@ -110,6 +126,30 @@ void AddDefaultFeedbackParams(VideoCodec* codec, } } +// Helper function to determine whether a codec should use the [35, 63] range. +// Should be used when adding new codecs (or variants). +bool IsCodecValidForLowerRange(const VideoCodec& codec) { + if (absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName) || + absl::EqualsIgnoreCase(codec.name, kAv1CodecName) || + absl::EqualsIgnoreCase(codec.name, kAv1xCodecName)) { + return true; + } else if (absl::EqualsIgnoreCase(codec.name, kH264CodecName)) { + std::string profileLevelId; + std::string packetizationMode; + + if (codec.GetParam(kH264FmtpProfileLevelId, &profileLevelId)) { + if (absl::StartsWithIgnoreCase(profileLevelId, "4d00")) { + if (codec.GetParam(kH264FmtpPacketizationMode, &packetizationMode)) { + return packetizationMode == "0"; + } + } + // H264 with YUV444. + return absl::StartsWithIgnoreCase(profileLevelId, "f400"); + } + } + return false; +} + // This function will assign dynamic payload types (in the range [96, 127] // and then [35, 63]) to the input codecs, and also add ULPFEC, RED, FlexFEC, // and associated RTX codecs for recognized codecs (VP8, VP9, H264, and RED). @@ -170,9 +210,6 @@ std::vector GetPayloadTypesAndDefaultCodecs( std::vector output_codecs; for (const webrtc::SdpVideoFormat& format : supported_formats) { VideoCodec codec(format); - bool isCodecValidForLowerRange = - absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName) || - absl::EqualsIgnoreCase(codec.name, kAv1CodecName); bool isFecCodec = absl::EqualsIgnoreCase(codec.name, kUlpfecCodecName) || absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName); @@ -188,7 +225,7 @@ std::vector GetPayloadTypesAndDefaultCodecs( // Lower range gets used for "new" codecs or when running out of payload // types in the upper range. - if (isCodecValidForLowerRange || + if (IsCodecValidForLowerRange(codec) || payload_type_upper >= kLastDynamicPayloadTypeUpperRange) { codec.id = payload_type_lower++; } else { @@ -208,7 +245,7 @@ std::vector GetPayloadTypesAndDefaultCodecs( RTC_DCHECK_EQ(payload_type_upper, kLastDynamicPayloadTypeUpperRange); break; } - if (isCodecValidForLowerRange || + if (IsCodecValidForLowerRange(codec) || payload_type_upper >= kLastDynamicPayloadTypeUpperRange) { output_codecs.push_back( VideoCodec::CreateRtxCodec(payload_type_lower++, codec.id)); @@ -295,10 +332,16 @@ static bool ValidateStreamParams(const StreamParams& sp) { // Returns true if the given codec is disallowed from doing simulcast. bool IsCodecDisabledForSimulcast(const std::string& codec_name, const webrtc::WebRtcKeyValueConfig& trials) { - return !absl::StartsWith(trials.Lookup("WebRTC-H264Simulcast"), "Disabled") - ? absl::EqualsIgnoreCase(codec_name, kVp9CodecName) - : absl::EqualsIgnoreCase(codec_name, kH264CodecName) || - absl::EqualsIgnoreCase(codec_name, kVp9CodecName); + if (absl::EqualsIgnoreCase(codec_name, kVp9CodecName) || + absl::EqualsIgnoreCase(codec_name, kAv1CodecName)) { + return true; + } + + if (absl::EqualsIgnoreCase(codec_name, kH264CodecName)) { + return absl::StartsWith(trials.Lookup("WebRTC-H264Simulcast"), "Disabled"); + } + + return false; } // The selected thresholds for QVGA and VGA corresponded to a QP around 10. @@ -321,49 +364,6 @@ static int GetMaxDefaultVideoBitrateKbps(int width, return max_bitrate; } -bool GetVp9LayersFromFieldTrialGroup( - size_t* num_spatial_layers, - size_t* num_temporal_layers, - const webrtc::WebRtcKeyValueConfig& trials) { - std::string group = trials.Lookup("WebRTC-SupportVP9SVC"); - if (group.empty()) - return false; - - if (sscanf(group.c_str(), "EnabledByFlag_%zuSL%zuTL", num_spatial_layers, - num_temporal_layers) != 2) { - return false; - } - if (*num_spatial_layers > webrtc::kMaxSpatialLayers || - *num_spatial_layers < 1) - return false; - - const size_t kMaxTemporalLayers = 3; - if (*num_temporal_layers > kMaxTemporalLayers || *num_temporal_layers < 1) - return false; - - return true; -} - -absl::optional GetVp9SpatialLayersFromFieldTrial( - const webrtc::WebRtcKeyValueConfig& trials) { - size_t num_sl; - size_t num_tl; - if (GetVp9LayersFromFieldTrialGroup(&num_sl, &num_tl, trials)) { - return num_sl; - } - return absl::nullopt; -} - -absl::optional GetVp9TemporalLayersFromFieldTrial( - const webrtc::WebRtcKeyValueConfig& trials) { - size_t num_sl; - size_t num_tl; - if (GetVp9LayersFromFieldTrialGroup(&num_sl, &num_tl, trials)) { - return num_tl; - } - return absl::nullopt; -} - // Returns its smallest positive argument. If neither argument is positive, // returns an arbitrary nonpositive value. int MinPositive(int a, int b) { @@ -486,7 +486,6 @@ WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings( (parameters_.config.rtp.ssrcs.size() == 1 || NumActiveStreams(rtp_parameters_) == 1); - bool frame_dropping = !is_screencast; bool denoising; bool codec_default_denoising = false; if (is_screencast) { @@ -500,7 +499,6 @@ WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings( if (absl::EqualsIgnoreCase(codec.name, kH264CodecName)) { webrtc::VideoCodecH264 h264_settings = webrtc::VideoEncoder::GetDefaultH264Settings(); - h264_settings.frameDroppingOn = frame_dropping; return rtc::make_ref_counted< webrtc::VideoEncoderConfig::H264EncoderSpecificSettings>(h264_settings); } @@ -510,29 +508,20 @@ WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings( vp8_settings.automaticResizeOn = automatic_resize; // VP8 denoising is enabled by default. vp8_settings.denoisingOn = codec_default_denoising ? true : denoising; - vp8_settings.frameDroppingOn = frame_dropping; return rtc::make_ref_counted< webrtc::VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings); } if (absl::EqualsIgnoreCase(codec.name, kVp9CodecName)) { webrtc::VideoCodecVP9 vp9_settings = webrtc::VideoEncoder::GetDefaultVp9Settings(); - const size_t default_num_spatial_layers = - parameters_.config.rtp.ssrcs.size(); - const size_t num_spatial_layers = - GetVp9SpatialLayersFromFieldTrial(call_->trials()) - .value_or(default_num_spatial_layers); - - const size_t default_num_temporal_layers = - num_spatial_layers > 1 ? kConferenceDefaultNumTemporalLayers : 1; - const size_t num_temporal_layers = - GetVp9TemporalLayersFromFieldTrial(call_->trials()) - .value_or(default_num_temporal_layers); vp9_settings.numberOfSpatialLayers = std::min( - num_spatial_layers, kConferenceMaxNumSpatialLayers); - vp9_settings.numberOfTemporalLayers = std::min( - num_temporal_layers, kConferenceMaxNumTemporalLayers); + parameters_.config.rtp.ssrcs.size(), kConferenceMaxNumSpatialLayers); + vp9_settings.numberOfTemporalLayers = + std::min(parameters_.config.rtp.ssrcs.size() > 1 + ? kConferenceDefaultNumTemporalLayers + : 1, + kConferenceMaxNumTemporalLayers); // VP9 denoising is disabled by default. vp9_settings.denoisingOn = codec_default_denoising ? true : denoising; @@ -540,15 +529,16 @@ WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings( // Ensure frame dropping is always enabled. RTC_DCHECK(vp9_settings.frameDroppingOn); if (!is_screencast) { - webrtc::FieldTrialFlag interlayer_pred_experiment_enabled = - webrtc::FieldTrialFlag("Enabled"); + webrtc::FieldTrialFlag interlayer_pred_experiment_enabled("Enabled"); webrtc::FieldTrialEnum inter_layer_pred_mode( "inter_layer_pred_mode", webrtc::InterLayerPredMode::kOnKeyPic, {{"off", webrtc::InterLayerPredMode::kOff}, {"on", webrtc::InterLayerPredMode::kOn}, {"onkeypic", webrtc::InterLayerPredMode::kOnKeyPic}}); + webrtc::FieldTrialFlag force_flexible_mode("FlexibleMode"); webrtc::ParseFieldTrial( - {&interlayer_pred_experiment_enabled, &inter_layer_pred_mode}, + {&interlayer_pred_experiment_enabled, &inter_layer_pred_mode, + &force_flexible_mode}, call_->trials().Lookup("WebRTC-Vp9InterLayerPred")); if (interlayer_pred_experiment_enabled) { vp9_settings.interLayerPred = inter_layer_pred_mode; @@ -556,6 +546,7 @@ WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings( // Limit inter-layer prediction to key pictures by default. vp9_settings.interLayerPred = webrtc::InterLayerPredMode::kOnKeyPic; } + vp9_settings.flexibleMode = force_flexible_mode.Get(); } else { // Multiple spatial layers vp9 screenshare needs flexible mode. vp9_settings.flexibleMode = vp9_settings.numberOfSpatialLayers > 1; @@ -705,7 +696,7 @@ WebRtcVideoChannel::WebRtcVideoChannel( webrtc::VideoEncoderFactory* encoder_factory, webrtc::VideoDecoderFactory* decoder_factory, webrtc::VideoBitrateAllocatorFactory* bitrate_allocator_factory) - : VideoMediaChannel(config, call->network_thread()), + : VideoMediaChannel(call->network_thread(), config.enable_dscp), worker_thread_(call->worker_thread()), call_(call), // RingRTC change to not process unsignaled SSRCs @@ -811,7 +802,7 @@ bool WebRtcVideoChannel::GetChangedSendParameters( const VideoSendParameters& params, ChangedSendParameters* changed_params) const { if (!ValidateCodecFormats(params.codecs) || - !ValidateRtpExtensions(params.extensions)) { + !ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) { return false; } @@ -846,7 +837,7 @@ bool WebRtcVideoChannel::GetChangedSendParameters( std::vector filtered_extensions = FilterRtpExtensions( params.extensions, webrtc::RtpExtension::IsSupportedForVideo, true, call_->trials()); - if (!send_rtp_extensions_ || (*send_rtp_extensions_ != filtered_extensions)) { + if (send_rtp_extensions_ != filtered_extensions) { changed_params->rtp_header_extensions = absl::optional>(filtered_extensions); } @@ -899,6 +890,12 @@ bool WebRtcVideoChannel::SetSendParameters(const VideoSendParameters& params) { } void WebRtcVideoChannel::RequestEncoderFallback() { + if (!worker_thread_->IsCurrent()) { + worker_thread_->PostTask( + ToQueuedTask(task_safety_, [this] { RequestEncoderFallback(); })); + return; + } + RTC_DCHECK_RUN_ON(&thread_checker_); if (negotiated_codecs_.size() <= 1) { RTC_LOG(LS_WARNING) << "Encoder failed but no fallback codec is available"; @@ -913,47 +910,16 @@ void WebRtcVideoChannel::RequestEncoderFallback() { } void WebRtcVideoChannel::RequestEncoderSwitch( - const EncoderSwitchRequestCallback::Config& conf) { - RTC_DCHECK_RUN_ON(&thread_checker_); - - if (!allow_codec_switching_) { - RTC_LOG(LS_INFO) << "Encoder switch requested but codec switching has" - " not been enabled yet."; - requested_encoder_switch_ = conf; + const webrtc::SdpVideoFormat& format, + bool allow_default_fallback) { + if (!worker_thread_->IsCurrent()) { + worker_thread_->PostTask( + ToQueuedTask(task_safety_, [this, format, allow_default_fallback] { + RequestEncoderSwitch(format, allow_default_fallback); + })); return; } - for (const VideoCodecSettings& codec_setting : negotiated_codecs_) { - if (codec_setting.codec.name == conf.codec_name) { - if (conf.param) { - auto it = codec_setting.codec.params.find(*conf.param); - if (it == codec_setting.codec.params.end()) - continue; - - if (conf.value && it->second != *conf.value) - continue; - } - - if (send_codec_ == codec_setting) { - // Already using this codec, no switch required. - return; - } - - ChangedSendParameters params; - params.send_codec = codec_setting; - ApplyChangedParams(params); - return; - } - } - - RTC_LOG(LS_WARNING) << "Requested encoder with codec_name:" << conf.codec_name - << ", param:" << conf.param.value_or("none") - << " and value:" << conf.value.value_or("none") - << "not found. No switch performed."; -} - -void WebRtcVideoChannel::RequestEncoderSwitch( - const webrtc::SdpVideoFormat& format) { RTC_DCHECK_RUN_ON(&thread_checker_); for (const VideoCodecSettings& codec_setting : negotiated_codecs_) { @@ -976,8 +942,13 @@ void WebRtcVideoChannel::RequestEncoderSwitch( } } - RTC_LOG(LS_WARNING) << "Encoder switch failed: SdpVideoFormat " - << format.ToString() << " not negotiated."; + RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " << format.ToString() + << ". Is default fallback allowed: " + << allow_default_fallback; + + if (allow_default_fallback) { + RequestEncoderFallback(); + } } bool WebRtcVideoChannel::ApplyChangedParams( @@ -993,7 +964,7 @@ bool WebRtcVideoChannel::ApplyChangedParams( SetExtmapAllowMixed(*changed_params.extmap_allow_mixed); } if (changed_params.rtp_header_extensions) { - send_rtp_extensions_ = changed_params.rtp_header_extensions; + send_rtp_extensions_ = *changed_params.rtp_header_extensions; } if (changed_params.send_codec || changed_params.max_bandwidth_bps) { @@ -1071,8 +1042,16 @@ webrtc::RtpParameters WebRtcVideoChannel::GetRtpSendParameters( // Need to add the common list of codecs to the send stream-specific // RTP parameters. for (const VideoCodec& codec : send_params_.codecs) { - rtp_params.codecs.push_back(codec.ToCodecParameters()); + if (send_codec_ && send_codec_->codec.id == codec.id) { + // Put the current send codec to the front of the codecs list. + RTC_DCHECK_EQ(codec.name, send_codec_->codec.name); + rtp_params.codecs.insert(rtp_params.codecs.begin(), + codec.ToCodecParameters()); + } else { + rtp_params.codecs.push_back(codec.ToCodecParameters()); + } } + return rtp_params; } @@ -1170,7 +1149,7 @@ bool WebRtcVideoChannel::GetChangedRecvParameters( const VideoRecvParameters& params, ChangedRecvParameters* changed_params) const { if (!ValidateCodecFormats(params.codecs) || - !ValidateRtpExtensions(params.extensions)) { + !ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) { return false; } @@ -1456,18 +1435,18 @@ bool WebRtcVideoChannel::AddRecvStream(const StreamParams& sp, if (!ValidateStreamParams(sp)) return false; - uint32_t ssrc = sp.first_ssrc(); - - // Remove running stream if this was a default stream. - const auto& prev_stream = receive_streams_.find(ssrc); - if (prev_stream != receive_streams_.end()) { - if (default_stream || !prev_stream->second->IsDefaultStream()) { - RTC_LOG(LS_ERROR) << "Receive stream for SSRC '" << ssrc - << "' already exists."; - return false; + for (uint32_t ssrc : sp.ssrcs) { + // Remove running stream if this was a default stream. + const auto& prev_stream = receive_streams_.find(ssrc); + if (prev_stream != receive_streams_.end()) { + if (default_stream || !prev_stream->second->IsDefaultStream()) { + RTC_LOG(LS_ERROR) << "Receive stream for SSRC '" << ssrc + << "' already exists."; + return false; + } + DeleteReceiveStream(prev_stream->second); + receive_streams_.erase(prev_stream); } - DeleteReceiveStream(prev_stream->second); - receive_streams_.erase(prev_stream); } if (!ValidateReceiveSsrcAvailability(sp)) @@ -1490,7 +1469,7 @@ bool WebRtcVideoChannel::AddRecvStream(const StreamParams& sp, if (unsignaled_frame_transformer_ && !config.frame_transformer) config.frame_transformer = unsignaled_frame_transformer_; - receive_streams_[ssrc] = new WebRtcVideoReceiveStream( + receive_streams_[sp.first_ssrc()] = new WebRtcVideoReceiveStream( this, call_, sp, std::move(config), default_stream, recv_codecs_, flexfec_config); @@ -1597,11 +1576,8 @@ void WebRtcVideoChannel::OnDemuxerCriteriaUpdatePending() { } void WebRtcVideoChannel::OnDemuxerCriteriaUpdateComplete() { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - worker_thread_->PostTask(ToQueuedTask(task_safety_, [this] { - RTC_DCHECK_RUN_ON(&thread_checker_); - ++demuxer_criteria_completed_id_; - })); + RTC_DCHECK_RUN_ON(&thread_checker_); + ++demuxer_criteria_completed_id_; } bool WebRtcVideoChannel::SetSink( @@ -1874,11 +1850,12 @@ void WebRtcVideoChannel::OnReadyToSend(bool ready) { } void WebRtcVideoChannel::OnNetworkRouteChanged( - const std::string& transport_name, + absl::string_view transport_name, const rtc::NetworkRoute& network_route) { RTC_DCHECK_RUN_ON(&network_thread_checker_); worker_thread_->PostTask(ToQueuedTask( - task_safety_, [this, name = transport_name, route = network_route] { + task_safety_, + [this, name = std::string(transport_name), route = network_route] { RTC_DCHECK_RUN_ON(&thread_checker_); webrtc::RtpTransportControllerSendInterface* transport = call_->GetTransportControllerSend(); @@ -1955,11 +1932,6 @@ void WebRtcVideoChannel::SetVideoCodecSwitchingEnabled(bool enabled) { allow_codec_switching_ = enabled; if (allow_codec_switching_) { RTC_LOG(LS_INFO) << "Encoder switching enabled."; - if (requested_encoder_switch_) { - RTC_LOG(LS_INFO) << "Executing cached video encoder switch request."; - RequestEncoderSwitch(*requested_encoder_switch_); - requested_encoder_switch_.reset(); - } } } @@ -2347,19 +2319,15 @@ webrtc::RTCError WebRtcVideoChannel::WebRtcVideoSendStream::SetRtpParameters( new_degradation_preference = true; } - // TODO(bugs.webrtc.org/8807): The bitrate priority really doesn't require an - // entire encoder reconfiguration, it just needs to update the bitrate - // allocator. + // Some fields (e.g. bitrate priority) only need to update the bitrate + // allocator which is updated via ReconfigureEncoder (however, note that the + // actual encoder should only be reconfigured if needed). bool reconfigure_encoder = new_param || (new_parameters.encodings[0].bitrate_priority != rtp_parameters_.encodings[0].bitrate_priority) || new_parameters.encodings[0].scalability_mode != rtp_parameters_.encodings[0].scalability_mode; - // TODO(bugs.webrtc.org/8807): The active field as well should not require - // a full encoder reconfiguration, but it needs to update both the bitrate - // allocator and the video bitrate allocator. - // // Note that the simulcast encoder adapter relies on the fact that layers // de/activation triggers encoder reinitialization. bool new_send_state = false; @@ -3604,13 +3572,13 @@ EncoderStreamFactory::CreateDefaultVideoStreams( layer.max_framerate = max_framerate; if (encoder_config.simulcast_layers[0].scale_resolution_down_by > 1.) { - layer.width = std::max( - layer.width / - encoder_config.simulcast_layers[0].scale_resolution_down_by, + layer.width = ScaleDownResolution( + layer.width, + encoder_config.simulcast_layers[0].scale_resolution_down_by, kMinLayerSize); - layer.height = std::max( - layer.height / - encoder_config.simulcast_layers[0].scale_resolution_down_by, + layer.height = ScaleDownResolution( + layer.height, + encoder_config.simulcast_layers[0].scale_resolution_down_by, kMinLayerSize); } @@ -3686,16 +3654,19 @@ EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams( const bool norm_size_configured = webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent().has_value(); const int normalized_width = - (default_scale_factors_used || norm_size_configured) + (default_scale_factors_used || norm_size_configured) && + (width >= kMinLayerSize) ? NormalizeSimulcastSize(width, encoder_config.number_of_streams) : width; const int normalized_height = - (default_scale_factors_used || norm_size_configured) + (default_scale_factors_used || norm_size_configured) && + (height >= kMinLayerSize) ? NormalizeSimulcastSize(height, encoder_config.number_of_streams) : height; - for (size_t i = 0; i < layers.size(); ++i) { layers[i].active = encoder_config.simulcast_layers[i].active; + layers[i].scalability_mode = + encoder_config.simulcast_layers[i].scalability_mode; // Update with configured num temporal layers if supported by codec. if (encoder_config.simulcast_layers[i].num_temporal_layers && IsTemporalLayersSupported(codec_name_)) { @@ -3709,12 +3680,10 @@ EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams( if (has_scale_resolution_down_by) { const double scale_resolution_down_by = std::max( encoder_config.simulcast_layers[i].scale_resolution_down_by, 1.0); - layers[i].width = std::max( - static_cast(normalized_width / scale_resolution_down_by), - kMinLayerSize); - layers[i].height = std::max( - static_cast(normalized_height / scale_resolution_down_by), - kMinLayerSize); + layers[i].width = ScaleDownResolution( + normalized_width, scale_resolution_down_by, kMinLayerSize); + layers[i].height = ScaleDownResolution( + normalized_height, scale_resolution_down_by, kMinLayerSize); } // Update simulcast bitrates with configured min and max bitrate. if (encoder_config.simulcast_layers[i].min_bitrate_bps > 0) { diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h index 8b3a7f42c6..940985d9f8 100644 --- a/media/engine/webrtc_video_engine.h +++ b/media/engine/webrtc_video_engine.h @@ -167,7 +167,7 @@ class WebRtcVideoChannel : public VideoMediaChannel, int64_t packet_time_us) override; void OnPacketSent(const rtc::SentPacket& sent_packet) override; void OnReadyToSend(bool ready) override; - void OnNetworkRouteChanged(const std::string& transport_name, + void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override; void SetInterface(NetworkInterface* iface) override; @@ -224,11 +224,8 @@ class WebRtcVideoChannel : public VideoMediaChannel, // Implements webrtc::EncoderSwitchRequestCallback. void RequestEncoderFallback() override; - - // TODO(bugs.webrtc.org/11341) : Remove this version of RequestEncoderSwitch. - void RequestEncoderSwitch( - const EncoderSwitchRequestCallback::Config& conf) override; - void RequestEncoderSwitch(const webrtc::SdpVideoFormat& format) override; + void RequestEncoderSwitch(const webrtc::SdpVideoFormat& format, + bool allow_default_fallback) override; void SetRecordableEncodedFrameCallback( uint32_t ssrc, @@ -598,7 +595,7 @@ class WebRtcVideoChannel : public VideoMediaChannel, std::vector negotiated_codecs_ RTC_GUARDED_BY(thread_checker_); - absl::optional> send_rtp_extensions_ + std::vector send_rtp_extensions_ RTC_GUARDED_BY(thread_checker_); webrtc::VideoEncoderFactory* const encoder_factory_ @@ -638,9 +635,10 @@ class WebRtcVideoChannel : public VideoMediaChannel, std::unique_ptr unknown_ssrc_packet_buffer_ RTC_GUARDED_BY(thread_checker_); + // TODO(bugs.webrtc.org/11341): Remove this and relevant PC API. Presence + // of multiple negotiated codecs allows generic encoder fallback on failures. + // Presence of EncoderSelector allows switching to specific encoders. bool allow_codec_switching_ = false; - absl::optional - requested_encoder_switch_; }; class EncoderStreamFactory diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index be431cb8d2..0c6c1695c8 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -30,6 +30,7 @@ #include "api/test/video/function_video_decoder_factory.h" #include "api/transport/field_trial_based_config.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocation.h" @@ -59,11 +60,11 @@ #include "rtc_base/gunit.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/time_utils.h" -#include "system_wrappers/include/field_trial.h" #include "test/fake_decoder.h" -#include "test/field_trial.h" #include "test/frame_forwarder.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" +#include "test/time_controller/simulated_time_controller.h" using ::testing::_; using ::testing::Contains; @@ -242,12 +243,9 @@ class WebRtcVideoEngineTest : public ::testing::Test { public: WebRtcVideoEngineTest() : WebRtcVideoEngineTest("") {} explicit WebRtcVideoEngineTest(const std::string& field_trials) - : override_field_trials_( - field_trials.empty() - ? nullptr - : std::make_unique( - field_trials)), - task_queue_factory_(webrtc::CreateDefaultTaskQueueFactory()), + : field_trials_(field_trials), + time_controller_(webrtc::Timestamp::Millis(4711)), + task_queue_factory_(time_controller_.CreateTaskQueueFactory()), call_(webrtc::Call::Create([&] { webrtc::Call::Config call_config(&event_log_); call_config.task_queue_factory = task_queue_factory_.get(); @@ -262,11 +260,7 @@ class WebRtcVideoEngineTest : public ::testing::Test { encoder_factory_), std::unique_ptr( decoder_factory_), - field_trials_) { - // Ensure fake clock doesn't return 0, which will cause some initializations - // fail inside RTP senders. - fake_clock_.AdvanceTime(webrtc::TimeDelta::Micros(1)); - } + field_trials_) {} protected: void AssignDefaultAptRtxTypes(); @@ -287,11 +281,8 @@ class WebRtcVideoEngineTest : public ::testing::Test { void ExpectRtpCapabilitySupport(const char* uri, bool supported) const; - // Has to be the first one, so it is initialized before the call or there is a - // race condition in the clock access. - rtc::ScopedFakeClock fake_clock_; - std::unique_ptr override_field_trials_; - webrtc::FieldTrialBasedConfig field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; + webrtc::GlobalSimulatedTimeController time_controller_; webrtc::RtcEventLogNull event_log_; std::unique_ptr task_queue_factory_; // Used in WebRtcVideoEngineVoiceTest, but defined here so it's properly @@ -560,6 +551,7 @@ TEST_F(WebRtcVideoEngineTest, UseFactoryForVp8WhenSupported) { rtc::kNumMicrosecsPerSec / 30); EXPECT_TRUE(channel->SetVideoSend(kSsrc, nullptr, &frame_forwarder)); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); // Sending one frame will have allocate the encoder. ASSERT_TRUE(encoder_factory_->WaitForCreatedVideoEncoders(1)); EXPECT_TRUE_WAIT(encoder_factory_->encoders()[0]->GetNumEncodedFrames() > 0, @@ -810,7 +802,7 @@ TEST_F(WebRtcVideoEngineTest, UsesSimulcastAdapterForVp8Factories) { rtc::kNumMicrosecsPerSec / 60); EXPECT_TRUE(channel->SetVideoSend(ssrcs.front(), nullptr, &frame_forwarder)); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); - + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); ASSERT_TRUE(encoder_factory_->WaitForCreatedVideoEncoders(2)); // Verify that encoders are configured for simulcast through adapter @@ -852,6 +844,7 @@ TEST_F(WebRtcVideoEngineTest, ChannelWithH264CanChangeToVp8) { EXPECT_TRUE(channel->SetVideoSend(kSsrc, nullptr, &frame_forwarder)); // Sending one frame will have allocate the encoder. frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); ASSERT_EQ_WAIT(1u, encoder_factory_->encoders().size(), kTimeout); @@ -861,6 +854,7 @@ TEST_F(WebRtcVideoEngineTest, ChannelWithH264CanChangeToVp8) { // Sending one frame will switch encoder. frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); EXPECT_EQ_WAIT(1u, encoder_factory_->encoders().size(), kTimeout); } @@ -890,6 +884,7 @@ TEST_F(WebRtcVideoEngineTest, rtc::kNumMicrosecsPerSec / 30); EXPECT_TRUE(channel->SetVideoSend(ssrcs.front(), nullptr, &frame_forwarder)); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); ASSERT_TRUE(encoder_factory_->WaitForCreatedVideoEncoders(2)); ASSERT_TRUE(encoder_factory_->encoders()[0]->WaitForInitEncode()); @@ -922,6 +917,7 @@ TEST_F(WebRtcVideoEngineTest, rtc::kNumMicrosecsPerSec / 30); EXPECT_TRUE(channel->SetVideoSend(kSsrc, nullptr, &frame_forwarder)); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); ASSERT_TRUE(encoder_factory_->WaitForCreatedVideoEncoders(1)); ASSERT_EQ(1u, encoder_factory_->encoders().size()); ASSERT_TRUE(encoder_factory_->encoders()[0]->WaitForInitEncode()); @@ -934,9 +930,8 @@ TEST_F(WebRtcVideoEngineTest, } TEST_F(WebRtcVideoEngineTest, SimulcastEnabledForH264BehindFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-H264Simulcast/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-H264Simulcast/Enabled/"); AddSupportedVideoCodecType("H264"); std::unique_ptr channel(engine_.CreateMediaChannel( @@ -956,6 +951,7 @@ TEST_F(WebRtcVideoEngineTest, SimulcastEnabledForH264BehindFieldTrial) { rtc::kNumMicrosecsPerSec / 30); EXPECT_TRUE(channel->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder)); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); + time_controller_.AdvanceTime(webrtc::TimeDelta::Zero()); ASSERT_TRUE(encoder_factory_->WaitForCreatedVideoEncoders(1)); ASSERT_EQ(1u, encoder_factory_->encoders().size()); @@ -975,9 +971,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03SendCodecEnablesWithFieldTrial) { EXPECT_THAT(engine_.send_codecs(), Not(Contains(flexfec))); - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Enabled/"); EXPECT_THAT(engine_.send_codecs(), Contains(flexfec)); } @@ -990,9 +985,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03ReceiveCodecDisablesWithFieldTrial) { EXPECT_THAT(engine_.recv_codecs(), Contains(flexfec)); - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Disabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Disabled/"); EXPECT_THAT(engine_.recv_codecs(), Not(Contains(flexfec))); } @@ -1003,9 +997,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03LowerPayloadTypeRange) { auto flexfec = Field("name", &VideoCodec::name, "flexfec-03"); // FlexFEC is active with field trial. - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Enabled/"); auto send_codecs = engine_.send_codecs(); auto it = std::find_if(send_codecs.begin(), send_codecs.end(), [](const cricket::VideoCodec& codec) { @@ -1208,14 +1201,8 @@ TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, Vp8) { /*lntf_expected=*/false); // Mock encoder creation. `engine` take ownership of the encoder. - webrtc::VideoEncoderFactory::CodecInfo codec_info; - codec_info.has_internal_source = false; const webrtc::SdpVideoFormat format("VP8"); - EXPECT_CALL(*encoder_factory, QueryVideoEncoder(format)) - .WillRepeatedly(Return(codec_info)); - rtc::Event encoder_created; EXPECT_CALL(*encoder_factory, CreateVideoEncoder(format)).WillOnce([&] { - encoder_created.Set(); return std::make_unique(nullptr); }); @@ -1226,7 +1213,9 @@ TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, Vp8) { // Create a call. webrtc::RtcEventLogNull event_log; - auto task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); + webrtc::GlobalSimulatedTimeController time_controller( + webrtc::Timestamp::Millis(4711)); + auto task_queue_factory = time_controller.CreateTaskQueueFactory(); webrtc::Call::Config call_config(&event_log); webrtc::FieldTrialBasedConfig field_trials; call_config.trials = &field_trials; @@ -1253,7 +1242,7 @@ TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, Vp8) { EXPECT_TRUE(send_channel->SetVideoSend(send_ssrc, nullptr, &frame_forwarder)); // Sending one frame will allocate the encoder. frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); - encoder_created.Wait(kTimeout); + time_controller.AdvanceTime(webrtc::TimeDelta::Zero()); // Create recv channel. const int recv_ssrc = 321; @@ -1453,7 +1442,7 @@ class WebRtcVideoChannelEncodedFrameCallbackTest : public ::testing::Test { } static const std::vector kSdpVideoFormats; - webrtc::FieldTrialBasedConfig field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; webrtc::RtcEventLogNull event_log_; std::unique_ptr task_queue_factory_; std::unique_ptr call_; @@ -1741,8 +1730,8 @@ class WebRtcVideoChannelBaseTest : public ::testing::Test { } webrtc::RtcEventLogNull event_log_; - webrtc::FieldTrialBasedConfig field_trials_; - std::unique_ptr override_field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; + std::unique_ptr override_field_trials_; std::unique_ptr task_queue_factory_; std::unique_ptr call_; std::unique_ptr @@ -1797,9 +1786,8 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSize) { // Set field trial to override the default recv buffer size, and then re-run // setup where the interface is created and configured. const int kCustomRecvBufferSize = 123456; - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-IncreasedReceivebuffers/123456/"); + override_field_trials_ = std::make_unique( + field_trials_, "WebRTC-IncreasedReceivebuffers/123456/"); ResetTest(); @@ -1815,9 +1803,8 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSizeWithSuffix) { // Set field trial to override the default recv buffer size, and then re-run // setup where the interface is created and configured. const int kCustomRecvBufferSize = 123456; - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-IncreasedReceivebuffers/123456_Dogfood/"); + override_field_trials_ = std::make_unique( + field_trials_, "WebRTC-IncreasedReceivebuffers/123456_Dogfood/"); ResetTest(); EXPECT_TRUE(SetOneCodec(DefaultCodec())); @@ -1826,55 +1813,32 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSizeWithSuffix) { EXPECT_EQ(kCustomRecvBufferSize, network_interface_.recvbuf_size()); } +class InvalidRecvBufferSizeFieldTrial + : public WebRtcVideoChannelBaseTest, + public ::testing::WithParamInterface {}; + // Test that we properly set the send and recv buffer sizes when overriding // via field trials that don't make any sense. -TEST_F(WebRtcVideoChannelBaseTest, InvalidRecvBufferSize) { +TEST_P(InvalidRecvBufferSizeFieldTrial, InvalidRecvBufferSize) { // Set bogus field trial values to override the default recv buffer size, and // then re-run setup where the interface is created and configured. The // default value should still be used. + override_field_trials_ = std::make_unique( + field_trials_, + std::string("WebRTC-IncreasedReceivebuffers/") + GetParam() + "/"); - const char* prev_field_trials = webrtc::field_trial::GetFieldTrialString(); + ResetTest(); - std::string field_trial_string; - for (std::string group : {" ", "NotANumber", "-1", "0"}) { - std::string trial_string = "WebRTC-IncreasedReceivebuffers/"; - trial_string += group; - trial_string += "/"; - - // Dear reader. Sorry for this... it's a bit of a mess. - // TODO(bugs.webrtc.org/12854): This test needs to be rewritten to not use - // ResetTest and changing global field trials in a loop. - TearDown(); - // This is a hack to appease tsan. Because of the way the test is written - // active state within Call, including running task queues may race with - // the test changing the global field trial variable. - // This particular hack, pauses the transport controller TQ while we - // change the field trial. - rtc::TaskQueue* tq = call_->GetTransportControllerSend()->GetWorkerQueue(); - rtc::Event waiting, resume; - tq->PostTask([&waiting, &resume]() { - waiting.Set(); - resume.Wait(rtc::Event::kForever); - }); - - waiting.Wait(rtc::Event::kForever); - field_trial_string = std::move(trial_string); - webrtc::field_trial::InitFieldTrialsFromString(field_trial_string.c_str()); - - SetUp(); - resume.Set(); - - // OK, now the test can carry on. - - EXPECT_TRUE(SetOneCodec(DefaultCodec())); - EXPECT_TRUE(SetSend(true)); - EXPECT_EQ(64 * 1024, network_interface_.sendbuf_size()); - EXPECT_EQ(256 * 1024, network_interface_.recvbuf_size()); - } - - webrtc::field_trial::InitFieldTrialsFromString(prev_field_trials); + EXPECT_TRUE(SetOneCodec(DefaultCodec())); + EXPECT_TRUE(SetSend(true)); + EXPECT_EQ(64 * 1024, network_interface_.sendbuf_size()); + EXPECT_EQ(256 * 1024, network_interface_.recvbuf_size()); } +INSTANTIATE_TEST_SUITE_P(All, + InvalidRecvBufferSizeFieldTrial, + Values("NotANumber", "-1", " ", "0")); + // Test that stats work properly for a 1-1 call. TEST_F(WebRtcVideoChannelBaseTest, GetStats) { const int kDurationSec = 3; @@ -2403,116 +2367,80 @@ TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderFallback) { EXPECT_EQ("VP8", codec.name); } -TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchWithConfig) { - const std::string kParam = "the-param"; - const std::string kPing = "ping"; - const std::string kPong = "pong"; - +TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchDefaultFallback) { cricket::VideoSendParameters parameters; - VideoCodec vp9 = GetEngineCodec("VP9"); - vp9.params[kParam] = kPong; - parameters.codecs.push_back(vp9); - - VideoCodec vp8 = GetEngineCodec("VP8"); - vp8.params[kParam] = kPing; - parameters.codecs.push_back(vp8); - + parameters.codecs.push_back(GetEngineCodec("VP9")); + parameters.codecs.push_back(GetEngineCodec("VP8")); EXPECT_TRUE(channel_->SetSendParameters(parameters)); - channel_->SetVideoCodecSwitchingEnabled(true); VideoCodec codec; ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP9")); + EXPECT_EQ("VP9", codec.name); // RequestEncoderSwitch will post a task to the worker thread (which is also // the current thread), hence the ProcessMessages call. - webrtc::EncoderSwitchRequestCallback::Config conf1{"VP8", kParam, kPing}; - channel_->RequestEncoderSwitch(conf1); + channel_->RequestEncoderSwitch(webrtc::SdpVideoFormat("UnavailableCodec"), + /*allow_default_fallback=*/true); rtc::Thread::Current()->ProcessMessages(30); - ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP8")); - EXPECT_THAT(codec.params, Contains(Pair(kParam, kPing))); - webrtc::EncoderSwitchRequestCallback::Config conf2{"VP9", kParam, kPong}; - channel_->RequestEncoderSwitch(conf2); - rtc::Thread::Current()->ProcessMessages(30); + // Requested encoder is not available. Default fallback is allowed. Switch to + // the next negotiated codec, VP8. ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP9")); - EXPECT_THAT(codec.params, Contains(Pair(kParam, kPong))); + EXPECT_EQ("VP8", codec.name); } -TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchIncorrectParam) { - const std::string kParam = "the-param"; - const std::string kPing = "ping"; - const std::string kPong = "pong"; +TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchStrictPreference) { + VideoCodec vp9 = GetEngineCodec("VP9"); + vp9.params["profile-id"] = "0"; cricket::VideoSendParameters parameters; - VideoCodec vp9 = GetEngineCodec("VP9"); - vp9.params[kParam] = kPong; + parameters.codecs.push_back(GetEngineCodec("VP8")); parameters.codecs.push_back(vp9); - - VideoCodec vp8 = GetEngineCodec("VP8"); - vp8.params[kParam] = kPing; - parameters.codecs.push_back(vp8); - EXPECT_TRUE(channel_->SetSendParameters(parameters)); - channel_->SetVideoCodecSwitchingEnabled(true); VideoCodec codec; ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP9")); + EXPECT_EQ("VP8", codec.name); - // RequestEncoderSwitch will post a task to the worker thread (which is also - // the current thread), hence the ProcessMessages call. - webrtc::EncoderSwitchRequestCallback::Config conf1{"VP8", kParam, kPing}; - channel_->RequestEncoderSwitch(conf1); + channel_->RequestEncoderSwitch( + webrtc::SdpVideoFormat("VP9", {{"profile-id", "1"}}), + /*allow_default_fallback=*/false); rtc::Thread::Current()->ProcessMessages(30); - ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP8")); - EXPECT_THAT(codec.params, Contains(Pair(kParam, kPing))); - // Incorrect conf2.value, expect no codec switch. - webrtc::EncoderSwitchRequestCallback::Config conf2{"VP9", kParam, kPing}; - channel_->RequestEncoderSwitch(conf2); - rtc::Thread::Current()->ProcessMessages(30); + // VP9 profile_id=1 is not available. Default fallback is not allowed. Switch + // is not performed. ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP8")); - EXPECT_THAT(codec.params, Contains(Pair(kParam, kPing))); + EXPECT_EQ("VP8", codec.name); + + channel_->RequestEncoderSwitch( + webrtc::SdpVideoFormat("VP9", {{"profile-id", "0"}}), + /*allow_default_fallback=*/false); + rtc::Thread::Current()->ProcessMessages(30); + + // VP9 profile_id=0 is available. Switch encoder. + ASSERT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_EQ("VP9", codec.name); } -TEST_F(WebRtcVideoChannelBaseTest, - RequestEncoderSwitchWithConfigBeforeEnabling) { - const std::string kParam = "the-param"; - const std::string kPing = "ping"; - const std::string kPong = "pong"; - +TEST_F(WebRtcVideoChannelBaseTest, SendCodecIsMovedToFrontInRtpParameters) { cricket::VideoSendParameters parameters; - VideoCodec vp9 = GetEngineCodec("VP9"); - vp9.params[kParam] = kPong; - parameters.codecs.push_back(vp9); - - VideoCodec vp8 = GetEngineCodec("VP8"); - vp8.params[kParam] = kPing; - parameters.codecs.push_back(vp8); - + parameters.codecs.push_back(GetEngineCodec("VP9")); + parameters.codecs.push_back(GetEngineCodec("VP8")); EXPECT_TRUE(channel_->SetSendParameters(parameters)); - - VideoCodec codec; - ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP9")); - - webrtc::EncoderSwitchRequestCallback::Config conf{"VP8", kParam, kPing}; - channel_->RequestEncoderSwitch(conf); - - // Enable codec switching after it has been requested. channel_->SetVideoCodecSwitchingEnabled(true); - // RequestEncoderSwitch will post a task to the worker thread (which is also + auto send_codecs = channel_->GetRtpSendParameters(kSsrc).codecs; + ASSERT_EQ(send_codecs.size(), 2u); + EXPECT_THAT("VP9", send_codecs[0].name); + + // RequestEncoderFallback will post a task to the worker thread (which is also // the current thread), hence the ProcessMessages call. + channel_->RequestEncoderFallback(); rtc::Thread::Current()->ProcessMessages(30); - ASSERT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_THAT(codec.name, Eq("VP8")); - EXPECT_THAT(codec.params, Contains(Pair(kParam, kPing))); + + send_codecs = channel_->GetRtpSendParameters(kSsrc).codecs; + ASSERT_EQ(send_codecs.size(), 2u); + EXPECT_THAT("VP8", send_codecs[0].name); } #endif // defined(RTC_ENABLE_VP9) @@ -2531,7 +2459,7 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { AddSupportedVideoCodecType("H264"); #endif - fake_call_.reset(new FakeCall()); + fake_call_.reset(new FakeCall(&field_trials_)); channel_.reset(engine_.CreateMediaChannel( fake_call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions(), video_bitrate_allocator_factory_.get())); @@ -2545,6 +2473,7 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { void TearDown() override { channel_->SetInterface(nullptr); channel_ = nullptr; + fake_call_ = nullptr; } void ResetTest() { @@ -2570,7 +2499,7 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { int64_t packet_time_us) { channel_->OnPacketReceived(packet, packet_time_us); rtc::Thread::Current()->ProcessMessages(0); - fake_clock_.AdvanceTime( + time_controller_.AdvanceTime( webrtc::TimeDelta::Millis(kUnsignalledReceiveStreamCooldownMs)); } @@ -2983,8 +2912,8 @@ TEST_F(WebRtcVideoChannelTest, RecvAbsoluteSendTimeHeaderExtensions) { } TEST_F(WebRtcVideoChannelTest, FiltersExtensionsPicksTransportSeqNum) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FilterAbsSendTimeExtension/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FilterAbsSendTimeExtension/Enabled/"); // Enable three redundant extensions. std::vector extensions; extensions.push_back(RtpExtension::kAbsSendTimeUri); @@ -3211,17 +3140,15 @@ TEST_F(WebRtcVideoChannelTest, LossNotificationIsDisabledByDefault) { } TEST_F(WebRtcVideoChannelTest, LossNotificationIsEnabledByFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-RtcpLossNotification/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-RtcpLossNotification/Enabled/"); ResetTest(); TestLossNotificationState(true); } TEST_F(WebRtcVideoChannelTest, LossNotificationCanBeEnabledAndDisabled) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-RtcpLossNotification/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-RtcpLossNotification/Enabled/"); ResetTest(); AssignDefaultCodec(); @@ -3537,16 +3464,16 @@ TEST_F(WebRtcVideoChannelTest, VerifyVp8SpecificSettings) { EXPECT_EQ(3u, stream->GetVideoStreams().size()); ASSERT_TRUE(stream->GetVp8Settings(&vp8_settings)) << "No VP8 config set."; EXPECT_FALSE(vp8_settings.denoisingOn); - // Resizing and frame dropping always off for screen sharing. + // Resizing always off for screen sharing. EXPECT_FALSE(vp8_settings.automaticResizeOn); - EXPECT_FALSE(vp8_settings.frameDroppingOn); + EXPECT_TRUE(vp8_settings.frameDroppingOn); stream = SetDenoisingOption(last_ssrc_, &frame_forwarder, true); ASSERT_TRUE(stream->GetVp8Settings(&vp8_settings)) << "No VP8 config set."; EXPECT_FALSE(vp8_settings.denoisingOn); EXPECT_FALSE(vp8_settings.automaticResizeOn); - EXPECT_FALSE(vp8_settings.frameDroppingOn); + EXPECT_TRUE(vp8_settings.frameDroppingOn); EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr)); } @@ -3791,14 +3718,6 @@ INSTANTIATE_TEST_SUITE_P( Vp9SettingsTestWithFieldTrial, Values( std::make_tuple("", 1, 1, webrtc::InterLayerPredMode::kOnKeyPic), - std::make_tuple("WebRTC-SupportVP9SVC/Default/", - 1, - 1, - webrtc::InterLayerPredMode::kOnKeyPic), - std::make_tuple("WebRTC-SupportVP9SVC/EnabledByFlag_2SL3TL/", - 2, - 3, - webrtc::InterLayerPredMode::kOnKeyPic), std::make_tuple("WebRTC-Vp9InterLayerPred/Default/", 1, 1, @@ -3830,8 +3749,8 @@ TEST_F(WebRtcVideoChannelTest, VerifyMinBitrate) { } TEST_F(WebRtcVideoChannelTest, VerifyMinBitrateWithForcedFallbackFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,2,34567/"); std::vector streams = AddSendStream()->GetVideoStreams(); ASSERT_EQ(1u, streams.size()); @@ -3840,9 +3759,8 @@ TEST_F(WebRtcVideoChannelTest, VerifyMinBitrateWithForcedFallbackFieldTrial) { TEST_F(WebRtcVideoChannelTest, BalancedDegradationPreferenceNotSupportedWithoutFieldtrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-Video-BalancedDegradation/Disabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Video-BalancedDegradation/Disabled/"); const bool kResolutionScalingEnabled = true; const bool kFpsScalingEnabled = false; TestDegradationPreference(kResolutionScalingEnabled, kFpsScalingEnabled); @@ -3850,9 +3768,8 @@ TEST_F(WebRtcVideoChannelTest, TEST_F(WebRtcVideoChannelTest, BalancedDegradationPreferenceSupportedBehindFieldtrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-Video-BalancedDegradation/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Video-BalancedDegradation/Enabled/"); const bool kResolutionScalingEnabled = true; const bool kFpsScalingEnabled = true; TestDegradationPreference(kResolutionScalingEnabled, kFpsScalingEnabled); @@ -4022,7 +3939,7 @@ TEST_F(WebRtcVideoChannelTest, EstimatesNtpStartTimeCorrectly) { // This timestamp is kInitialTimestamp (-1) + kFrameOffsetMs * 90, which // triggers a constant-overflow warning, hence we're calculating it explicitly // here. - fake_clock_.AdvanceTime(webrtc::TimeDelta::Millis(kFrameOffsetMs)); + time_controller_.AdvanceTime(webrtc::TimeDelta::Millis(kFrameOffsetMs)); video_frame.set_timestamp(kFrameOffsetMs * 90 - 1); video_frame.set_ntp_time_ms(kInitialNtpTimeMs + kFrameOffsetMs); stream->InjectFrame(video_frame); @@ -4042,7 +3959,7 @@ TEST_F(WebRtcVideoChannelTest, SetDefaultSendCodecs) { VideoCodec codec; EXPECT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_TRUE(codec.Matches(engine_.send_codecs()[0])); + EXPECT_TRUE(codec.Matches(engine_.send_codecs()[0], &field_trials_)); // Using a RTX setup to verify that the default RTX payload type is good. const std::vector ssrcs = MAKE_VECTOR(kSsrcs1); @@ -5484,7 +5401,7 @@ TEST_F(WebRtcVideoChannelTest, GetStatsReportsPerLayerQpSum) { webrtc::VideoSendStream::Stats GetInitialisedStats() { webrtc::VideoSendStream::Stats stats; stats.encoder_implementation_name = "vp"; - stats.input_frame_rate = 1; + stats.input_frame_rate = 1.0; stats.encode_frame_rate = 2; stats.avg_encode_time_ms = 3; stats.encode_usage_percent = 4; @@ -6752,7 +6669,7 @@ TEST_F(WebRtcVideoChannelTest, UnsignalledSsrcHasACooldown) { channel_->OnPacketReceived(packet.Buffer(), /* packet_time_us */ -1); } rtc::Thread::Current()->ProcessMessages(0); - fake_clock_.AdvanceTime( + time_controller_.AdvanceTime( webrtc::TimeDelta::Millis(kUnsignalledReceiveStreamCooldownMs - 1)); // We now have an unsignalled receive stream for kSsrc1. @@ -6776,7 +6693,7 @@ TEST_F(WebRtcVideoChannelTest, UnsignalledSsrcHasACooldown) { // After 500 ms, kSsrc2 should trigger a new unsignalled receive stream that // replaces the old one. - fake_clock_.AdvanceTime(webrtc::TimeDelta::Millis(1)); + time_controller_.AdvanceTime(webrtc::TimeDelta::Millis(1)); { // Receive a packet for kSsrc2. RtpPacket packet; @@ -7463,8 +7380,8 @@ TEST_F(WebRtcVideoChannelTest, // so that the bottom layer has width and height divisible by 2. // TODO(bugs.webrtc.org/8785): Remove this field trial when we fully trust // the number of simulcast layers set by the app. - webrtc::test::ScopedFieldTrials field_trial( - "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trial( + field_trials_, "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); // Set up WebRtcVideoChannel for 3-layer VP8 simulcast. VideoSendParameters parameters; @@ -7618,8 +7535,8 @@ TEST_F(WebRtcVideoChannelTest, // so that the bottom layer has width and height divisible by 2. // TODO(bugs.webrtc.org/8785): Remove this field trial when we fully trust // the number of simulcast layers set by the app. - webrtc::test::ScopedFieldTrials field_trial( - "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trial( + field_trials_, "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); // Set up WebRtcVideoChannel for 3-layer H264 simulcast. encoder_factory_->AddSupportedVideoCodecType(kH264CodecName); @@ -8623,6 +8540,24 @@ TEST_F(WebRtcVideoChannelTest, EXPECT_FALSE(rtp_parameters.encodings[0].ssrc); } +// Test that if a default stream is created for a non-primary stream (for +// example, RTX before we know it's RTX), we are still able to explicitly add +// the stream later. +TEST_F(WebRtcVideoChannelTest, + AddReceiveStreamAfterReceivingNonPrimaryUnsignaledSsrc) { + // Receive VP8 RTX packet. + RtpPacket rtp_packet; + const cricket::VideoCodec vp8 = GetEngineCodec("VP8"); + rtp_packet.SetPayloadType(default_apt_rtx_types_[vp8.id]); + rtp_packet.SetSsrc(2); + ReceivePacketAndAdvanceTime(rtp_packet.Buffer(), /* packet_time_us */ -1); + EXPECT_EQ(1u, fake_call_->GetVideoReceiveStreams().size()); + + cricket::StreamParams params = cricket::StreamParams::CreateLegacy(1); + params.AddFidSsrc(1, 2); + EXPECT_TRUE(channel_->AddRecvStream(params)); +} + void WebRtcVideoChannelTest::TestReceiverLocalSsrcConfiguration( bool receiver_first) { EXPECT_TRUE(channel_->SetSendParameters(send_parameters_)); @@ -8810,7 +8745,6 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { ASSERT_EQ(expected_streams.size(), video_streams.size()); size_t num_streams = video_streams.size(); - int total_max_bitrate_bps = 0; for (size_t i = 0; i < num_streams; ++i) { EXPECT_EQ(expected_streams[i].width, video_streams[i].width); EXPECT_EQ(expected_streams[i].height, video_streams[i].height); @@ -8841,12 +8775,6 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { EXPECT_EQ(expected_streams[i].num_temporal_layers, video_streams[i].num_temporal_layers); } - - if (i == num_streams - 1) { - total_max_bitrate_bps += video_streams[i].max_bitrate_bps; - } else { - total_max_bitrate_bps += video_streams[i].target_bitrate_bps; - } } EXPECT_TRUE(channel_->SetVideoSend(ssrcs.front(), nullptr, nullptr)); @@ -8882,13 +8810,13 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { return streams[streams.size() - 1]; } + webrtc::test::ScopedKeyValueConfig field_trials_; webrtc::RtcEventLogNull event_log_; FakeCall fake_call_; cricket::FakeWebRtcVideoEncoderFactory* encoder_factory_; cricket::FakeWebRtcVideoDecoderFactory* decoder_factory_; std::unique_ptr mock_rate_allocator_factory_; - webrtc::FieldTrialBasedConfig field_trials_; WebRtcVideoEngine engine_; std::unique_ptr channel_; uint32_t last_ssrc_; diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 076484b786..28cdff3468 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -125,9 +125,10 @@ bool IsCodec(const AudioCodec& codec, const char* ref_name) { bool FindCodec(const std::vector& codecs, const AudioCodec& codec, - AudioCodec* found_codec) { + AudioCodec* found_codec, + const webrtc::WebRtcKeyValueConfig* field_trials) { for (const AudioCodec& c : codecs) { - if (c.Matches(codec)) { + if (c.Matches(codec, field_trials)) { if (found_codec != NULL) { *found_codec = c; } @@ -306,8 +307,6 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( audio_mixer_(audio_mixer), apm_(audio_processing), audio_frame_processor_(audio_frame_processor), - audio_red_for_opus_enabled_( - IsEnabled(trials, "WebRTC-Audio-Red-For-Opus")), minimized_remsampling_on_mobile_trial_enabled_( IsEnabled(trials, "WebRTC-Audio-MinimizeResamplingOnMobile")) { // This may be called from any thread, so detach thread checkers. @@ -338,6 +337,7 @@ void WebRtcVoiceEngine::Init() { RTC_LOG(LS_INFO) << "WebRtcVoiceEngine::Init"; // TaskQueue expects to be created/destroyed on the same thread. + RTC_DCHECK(!low_priority_worker_queue_); low_priority_worker_queue_.reset( new rtc::TaskQueue(task_queue_factory_->CreateTaskQueue( "rtc-low-prio", webrtc::TaskQueueFactory::Priority::LOW))); @@ -398,15 +398,12 @@ void WebRtcVoiceEngine::Init() { options.noise_suppression = true; options.typing_detection = true; #endif - options.experimental_ns = false; options.highpass_filter = true; options.stereo_swapping = false; options.audio_jitter_buffer_max_packets = 200; options.audio_jitter_buffer_fast_accelerate = false; options.audio_jitter_buffer_min_delay_ms = 0; options.audio_jitter_buffer_enable_rtx_handling = false; - options.experimental_agc = false; - options.residual_echo_detector = true; bool error = ApplyOptions(options); RTC_DCHECK(error); } @@ -453,17 +450,14 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { // Override noise suppression options for Android. #if defined(WEBRTC_ANDROID) options.typing_detection = false; - options.experimental_ns = false; #endif // Set and adjust gain control options. #if defined(WEBRTC_IOS) // On iOS, VPIO provides built-in AGC. options.auto_gain_control = false; - options.experimental_agc = false; RTC_LOG(LS_INFO) << "Always disable AGC on iOS. Use built-in instead."; #elif defined(WEBRTC_ANDROID) - options.experimental_agc = false; #endif #if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) @@ -571,17 +565,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { return true; } - webrtc::Config config; - - if (options.experimental_ns) { - experimental_ns_ = options.experimental_ns; - } - if (experimental_ns_) { - RTC_LOG(LS_INFO) << "Experimental ns is enabled? " << *experimental_ns_; - config.Set( - new webrtc::ExperimentalNs(*experimental_ns_)); - } - webrtc::AudioProcessing::Config apm_config = ap->GetConfig(); if (options.echo_cancellation) { @@ -600,30 +583,12 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { apm_config.gain_controller1.mode = apm_config.gain_controller1.kAdaptiveAnalog; #endif - constexpr int kMinVolumeLevel = 0; - constexpr int kMaxVolumeLevel = 255; - apm_config.gain_controller1.analog_level_minimum = kMinVolumeLevel; - apm_config.gain_controller1.analog_level_maximum = kMaxVolumeLevel; - } - if (options.tx_agc_target_dbov) { - apm_config.gain_controller1.target_level_dbfs = *options.tx_agc_target_dbov; - } - if (options.tx_agc_digital_compression_gain) { - apm_config.gain_controller1.compression_gain_db = - *options.tx_agc_digital_compression_gain; - } - if (options.tx_agc_limiter) { - apm_config.gain_controller1.enable_limiter = *options.tx_agc_limiter; } if (options.highpass_filter) { apm_config.high_pass_filter.enabled = *options.highpass_filter; } - if (options.residual_echo_detector) { - apm_config.residual_echo_detector.enabled = *options.residual_echo_detector; - } - if (options.noise_suppression) { const bool enabled = *options.noise_suppression; apm_config.noise_suppression.enabled = enabled; @@ -633,9 +598,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { } if (options.typing_detection) { - RTC_LOG(LS_INFO) << "Typing detection is enabled? " - << *options.typing_detection; - apm_config.voice_detection.enabled = *options.typing_detection; + RTC_LOG(LS_WARNING) << "Typing detection is requested, but unsupported."; } ap->ApplyConfig(apm_config); @@ -663,8 +626,6 @@ WebRtcVoiceEngine::GetRtpHeaderExtensions() const { webrtc::RtpExtension::kAbsSendTimeUri, webrtc::RtpExtension::kTransportSequenceNumberUri, webrtc::RtpExtension::kMidUri, - // webrtc::RtpExtension::kRidUri, - // webrtc::RtpExtension::kRepairedRidUri }) { result.emplace_back(uri, id++, webrtc::RtpTransceiverDirection::kSendRecv); } @@ -769,8 +730,10 @@ std::vector WebRtcVoiceEngine::CollectCodecs( out.push_back(codec); - if (codec.name == kOpusCodecName && audio_red_for_opus_enabled_) { - map_format({kRedCodecName, 48000, 2}, &out); + if (codec.name == kOpusCodecName) { + std::string redFmtp = + rtc::ToString(codec.id) + "/" + rtc::ToString(codec.id); + map_format({kRedCodecName, 48000, 2, {{"", redFmtp}}}, &out); } } } @@ -1349,14 +1312,12 @@ WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel( const AudioOptions& options, const webrtc::CryptoOptions& crypto_options, webrtc::Call* call) - : VoiceMediaChannel(config, call->network_thread()), + : VoiceMediaChannel(call->network_thread(), config.enable_dscp), worker_thread_(call->worker_thread()), engine_(engine), call_(call), audio_config_(config.audio), - crypto_options_(crypto_options), - audio_red_for_opus_enabled_( - IsEnabled(call->trials(), "WebRTC-Audio-Red-For-Opus")) { + crypto_options_(crypto_options) { network_thread_checker_.Detach(); RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel"; RTC_DCHECK(call); @@ -1390,7 +1351,7 @@ bool WebRtcVoiceMediaChannel::SetSendParameters( return false; } - if (!ValidateRtpExtensions(params.extensions)) { + if (!ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) { return false; } @@ -1436,7 +1397,7 @@ bool WebRtcVoiceMediaChannel::SetRecvParameters( return false; } - if (!ValidateRtpExtensions(params.extensions)) { + if (!ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) { return false; } std::vector filtered_extensions = FilterRtpExtensions( @@ -1609,7 +1570,7 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( // Log a warning if a codec's payload type is changing. This used to be // treated as an error. It's abnormal, but not really illegal. AudioCodec old_codec; - if (FindCodec(recv_codecs_, codec, &old_codec) && + if (FindCodec(recv_codecs_, codec, &old_codec, &call_->trials()) && old_codec.id != codec.id) { RTC_LOG(LS_WARNING) << codec.name << " mapped to a second payload type (" << codec.id << ", was already mapped to " @@ -1617,7 +1578,7 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( } auto format = AudioCodecToSdpAudioFormat(codec); if (!IsCodec(codec, kCnCodecName) && !IsCodec(codec, kDtmfCodecName) && - (!audio_red_for_opus_enabled_ || !IsCodec(codec, kRedCodecName)) && + !IsCodec(codec, kRedCodecName) && !engine()->decoder_factory_->IsSupportedDecoder(format)) { RTC_LOG(LS_ERROR) << "Unsupported codec: " << rtc::ToString(format); return false; @@ -1668,6 +1629,37 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( return true; } +// Utility function to check if RED codec and its parameters match a codec spec. +bool CheckRedParameters( + const AudioCodec& red_codec, + const webrtc::AudioSendStream::Config::SendCodecSpec& send_codec_spec) { + if (red_codec.clockrate != send_codec_spec.format.clockrate_hz || + red_codec.channels != send_codec_spec.format.num_channels) { + return false; + } + + // Check the FMTP line for the empty parameter which should match + // /[/...] + auto red_parameters = red_codec.params.find(""); + if (red_parameters == red_codec.params.end()) { + RTC_LOG(LS_WARNING) << "audio/RED missing fmtp parameters."; + return false; + } + std::vector redundant_payloads = + rtc::split(red_parameters->second, '/'); + // 32 is chosen as a maximum upper bound for consistency with the + // red payload splitter. + if (redundant_payloads.size() < 2 || redundant_payloads.size() > 32) { + return false; + } + for (auto pt : redundant_payloads) { + if (pt != rtc::ToString(send_codec_spec.payload_type)) { + return false; + } + } + return true; +} + // Utility function called from SetSendParameters() to extract current send // codec settings from the given list of codecs (originally from SDP). Both send // and receive streams may be reconfigured based on the new settings. @@ -1772,20 +1764,17 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } } - if (audio_red_for_opus_enabled_) { - // Loop through the codecs to find the RED codec that matches opus - // with respect to clockrate and number of channels. - size_t red_codec_position = 0; - for (const AudioCodec& red_codec : codecs) { - if (red_codec_position < send_codec_position && - IsCodec(red_codec, kRedCodecName) && - red_codec.clockrate == send_codec_spec->format.clockrate_hz && - red_codec.channels == send_codec_spec->format.num_channels) { - send_codec_spec->red_payload_type = red_codec.id; - break; - } - red_codec_position++; + // Loop through the codecs to find the RED codec that matches opus + // with respect to clockrate and number of channels. + size_t red_codec_position = 0; + for (const AudioCodec& red_codec : codecs) { + if (red_codec_position < send_codec_position && + IsCodec(red_codec, kRedCodecName) && + CheckRedParameters(red_codec, *send_codec_spec)) { + send_codec_spec->red_payload_type = red_codec.id; + break; } + red_codec_position++; } if (send_codec_spec_ != send_codec_spec) { @@ -1849,8 +1838,7 @@ void WebRtcVoiceMediaChannel::SetSend(bool send) { return; } - // Apply channel specific options, and initialize the ADM for recording (this - // may take time on some platforms, e.g. Android). + // Apply channel specific options. if (send) { engine()->ApplyOptions(options_); @@ -1860,7 +1848,8 @@ void WebRtcVoiceMediaChannel::SetSend(bool send) { // is never answered. #if false // InitRecording() may return an error if the ADM is already recording. - if (!engine()->adm()->RecordingIsInitialized() && + if (options_.init_recording_on_send.value_or(true) && + !engine()->adm()->RecordingIsInitialized() && !engine()->adm()->Recording()) { if (engine()->adm()->InitRecording() != 0) { RTC_LOG(LS_WARNING) << "Failed to initialize recording"; @@ -2277,14 +2266,15 @@ void WebRtcVoiceMediaChannel::OnPacketSent(const rtc::SentPacket& sent_packet) { } void WebRtcVoiceMediaChannel::OnNetworkRouteChanged( - const std::string& transport_name, + absl::string_view transport_name, const rtc::NetworkRoute& network_route) { RTC_DCHECK_RUN_ON(&network_thread_checker_); call_->OnAudioTransportOverheadChanged(network_route.packet_overhead); worker_thread_->PostTask(ToQueuedTask( - task_safety_, [this, name = transport_name, route = network_route] { + task_safety_, + [this, name = std::string(transport_name), route = network_route] { RTC_DCHECK_RUN_ON(worker_thread_); call_->GetTransportControllerSend()->OnNetworkRouteChanged(name, route); })); @@ -2359,6 +2349,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info, sinfo.packets_lost = stats.packets_lost; sinfo.fraction_lost = stats.fraction_lost; sinfo.nacks_rcvd = stats.nacks_rcvd; + sinfo.target_bitrate = stats.target_bitrate_bps; sinfo.codec_name = stats.codec_name; sinfo.codec_payload_type = stats.codec_payload_type; sinfo.jitter_ms = stats.jitter_ms; diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h index 40d113d7ae..812205693f 100644 --- a/media/engine/webrtc_voice_engine.h +++ b/media/engine/webrtc_voice_engine.h @@ -124,18 +124,12 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { bool is_dumping_aec_ = false; bool initialized_ = false; - // Cache experimental_ns and apply in case they are missing in the audio - // options. - absl::optional experimental_ns_; // Jitter buffer settings for new streams. size_t audio_jitter_buffer_max_packets_ = 200; bool audio_jitter_buffer_fast_accelerate_ = false; int audio_jitter_buffer_min_delay_ms_ = 0; bool audio_jitter_buffer_enable_rtx_handling_ = false; - // If this field is enabled, we will negotiate and use RFC 2198 - // redundancy for opus audio. - const bool audio_red_for_opus_enabled_; const bool minimized_remsampling_on_mobile_trial_enabled_; }; @@ -209,7 +203,7 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, void OnPacketReceived(rtc::CopyOnWriteBuffer packet, int64_t packet_time_us) override; void OnPacketSent(const rtc::SentPacket& sent_packet) override; - void OnNetworkRouteChanged(const std::string& transport_name, + void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override; void OnReadyToSend(bool ready) override; bool GetStats(VoiceMediaInfo* info, bool get_and_clear_legacy_stats) override; @@ -338,8 +332,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, // Unsignaled streams have an option to have a frame decryptor set on them. rtc::scoped_refptr unsignaled_frame_decryptor_; - - const bool audio_red_for_opus_enabled_; }; } // namespace cricket diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc index 5983745c13..194ab0e05a 100644 --- a/media/engine/webrtc_voice_engine_unittest.cc +++ b/media/engine/webrtc_voice_engine_unittest.cc @@ -33,10 +33,10 @@ #include "rtc_base/arraysize.h" #include "rtc_base/byte_order.h" #include "rtc_base/numerics/safe_conversions.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_decoder_factory.h" #include "test/mock_audio_encoder_factory.h" +#include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::ContainerEq; @@ -180,9 +180,7 @@ class FakeAudioSource : public cricket::AudioSource { class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { public: - WebRtcVoiceEngineTestFake() : WebRtcVoiceEngineTestFake("") {} - - explicit WebRtcVoiceEngineTestFake(const char* field_trials) + WebRtcVoiceEngineTestFake() : use_null_apm_(GetParam()), task_queue_factory_(webrtc::CreateDefaultTaskQueueFactory()), adm_(webrtc::test::MockAudioDeviceModule::CreateStrict()), @@ -190,8 +188,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { ? nullptr : rtc::make_ref_counted< StrictMock>()), - call_(), - override_field_trials_(field_trials) { + call_(&field_trials_) { // AudioDeviceModule. AdmSetupExpectations(adm_); @@ -212,7 +209,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { auto decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); engine_.reset(new cricket::WebRtcVoiceEngine( task_queue_factory_.get(), adm_, encoder_factory, decoder_factory, - nullptr, apm_, nullptr, trials_config_)); + nullptr, apm_, nullptr, field_trials_)); engine_->Init(); send_parameters_.codecs.push_back(kPcmuCodec); recv_parameters_.codecs.push_back(kPcmuCodec); @@ -221,7 +218,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { // Default Options. VerifyEchoCancellationSettings(/*enabled=*/true); EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_TRUE(IsTypingDetectionEnabled()); EXPECT_TRUE(apm_config_.noise_suppression.enabled); EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel); VerifyGainControlEnabledCorrectly(); @@ -307,9 +303,15 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { void SetSend(bool enable) { ASSERT_TRUE(channel_); if (enable) { - EXPECT_CALL(*adm_, RecordingIsInitialized()).WillOnce(Return(false)); - EXPECT_CALL(*adm_, Recording()).WillOnce(Return(false)); - EXPECT_CALL(*adm_, InitRecording()).WillOnce(Return(0)); + EXPECT_CALL(*adm_, RecordingIsInitialized()) + .Times(::testing::AtMost(1)) + .WillOnce(Return(false)); + EXPECT_CALL(*adm_, Recording()) + .Times(::testing::AtMost(1)) + .WillOnce(Return(false)); + EXPECT_CALL(*adm_, InitRecording()) + .Times(::testing::AtMost(1)) + .WillOnce(Return(0)); } channel_->SetSend(enable); } @@ -764,8 +766,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { void VerifyGainControlEnabledCorrectly() { EXPECT_TRUE(apm_config_.gain_controller1.enabled); EXPECT_EQ(kDefaultAgcMode, apm_config_.gain_controller1.mode); - EXPECT_EQ(0, apm_config_.gain_controller1.analog_level_minimum); - EXPECT_EQ(255, apm_config_.gain_controller1.analog_level_maximum); } void VerifyGainControlDefaultSettings() { @@ -789,12 +789,9 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { return apm_config_.high_pass_filter.enabled; } - bool IsTypingDetectionEnabled() { - return apm_config_.voice_detection.enabled; - } - protected: const bool use_null_apm_; + webrtc::test::ScopedKeyValueConfig field_trials_; std::unique_ptr task_queue_factory_; rtc::scoped_refptr adm_; rtc::scoped_refptr> apm_; @@ -805,10 +802,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { cricket::AudioRecvParameters recv_parameters_; FakeAudioSource fake_source_; webrtc::AudioProcessing::Config apm_config_; - - private: - webrtc::test::ScopedFieldTrials override_field_trials_; - webrtc::FieldTrialBasedConfig trials_config_; }; INSTANTIATE_TEST_SUITE_P(TestBothWithAndWithoutNullApm, @@ -1034,19 +1027,12 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvRed) { cricket::AudioRecvParameters parameters; parameters.codecs.push_back(kOpusCodec); parameters.codecs.push_back(kRed48000Codec); + parameters.codecs[1].params[""] = "111/111"; EXPECT_TRUE(channel_->SetRecvParameters(parameters)); EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map, (ContainerEq>( - {{111, {"opus", 48000, 2}}, {112, {"red", 48000, 2}}}))); -} - -// Test that we do not support Opus/Red by default. -TEST_P(WebRtcVoiceEngineTestFake, RecvRedDefault) { - EXPECT_TRUE(SetupRecvStream()); - cricket::AudioRecvParameters parameters; - parameters.codecs.push_back(kOpusCodec); - parameters.codecs.push_back(kRed48000Codec); - EXPECT_FALSE(channel_->SetRecvParameters(parameters)); + {{111, {"opus", 48000, 2}}, + {112, {"red", 48000, 2, {{"", "111/111"}}}}}))); } TEST_P(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) { @@ -1246,8 +1232,8 @@ TEST_P(WebRtcVoiceEngineTestFake, } TEST_P(WebRtcVoiceEngineTestFake, AdaptivePtimeFieldTrial) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Audio-AdaptivePtime/enabled:true/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Audio-AdaptivePtime/enabled:true/"); EXPECT_TRUE(SetupSendStream()); EXPECT_TRUE(GetAudioNetworkAdaptorConfig(kSsrcX)); } @@ -1498,15 +1484,13 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecs) { EXPECT_FALSE(channel_->CanInsertDtmf()); } -// Test that we use Opus/Red under the field trial when it is -// listed as the first codec. +// Test that we use Opus/Red by default when it is +// listed as the first codec and there is an fmtp line. TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRed) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled/"); - EXPECT_TRUE(SetupSendStream()); cricket::AudioSendParameters parameters; parameters.codecs.push_back(kRed48000Codec); + parameters.codecs[0].params[""] = "111/111"; parameters.codecs.push_back(kOpusCodec); SetSendParameters(parameters); const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec; @@ -1515,15 +1499,13 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRed) { EXPECT_EQ(112, send_codec_spec.red_payload_type); } -// Test that we do not use Opus/Red under the field trial by default. -TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedDefault) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled/"); - +// Test that we do not use Opus/Red by default when it is +// listed as the first codec but there is no fmtp line. +TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedNoFmtp) { EXPECT_TRUE(SetupSendStream()); cricket::AudioSendParameters parameters; - parameters.codecs.push_back(kOpusCodec); parameters.codecs.push_back(kRed48000Codec); + parameters.codecs.push_back(kOpusCodec); SetSendParameters(parameters); const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec; EXPECT_EQ(111, send_codec_spec.payload_type); @@ -1531,6 +1513,62 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedDefault) { EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type); } +// Test that we do not use Opus/Red by default. +TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedDefault) { + EXPECT_TRUE(SetupSendStream()); + cricket::AudioSendParameters parameters; + parameters.codecs.push_back(kOpusCodec); + parameters.codecs.push_back(kRed48000Codec); + parameters.codecs[1].params[""] = "111/111"; + SetSendParameters(parameters); + const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec; + EXPECT_EQ(111, send_codec_spec.payload_type); + EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str()); + EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type); +} + +// Test that the RED fmtp line must match the payload type. +TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedFmtpMismatch) { + EXPECT_TRUE(SetupSendStream()); + cricket::AudioSendParameters parameters; + parameters.codecs.push_back(kRed48000Codec); + parameters.codecs[0].params[""] = "8/8"; + parameters.codecs.push_back(kOpusCodec); + SetSendParameters(parameters); + const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec; + EXPECT_EQ(111, send_codec_spec.payload_type); + EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str()); + EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type); +} + +// Test that the RED fmtp line must show 2..32 payloads. +TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedFmtpAmountOfRedundancy) { + EXPECT_TRUE(SetupSendStream()); + cricket::AudioSendParameters parameters; + parameters.codecs.push_back(kRed48000Codec); + parameters.codecs[0].params[""] = "111"; + parameters.codecs.push_back(kOpusCodec); + SetSendParameters(parameters); + const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec; + EXPECT_EQ(111, send_codec_spec.payload_type); + EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str()); + EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type); + for (int i = 1; i < 32; i++) { + parameters.codecs[0].params[""] += "/111"; + SetSendParameters(parameters); + const auto& send_codec_spec2 = *GetSendStreamConfig(kSsrcX).send_codec_spec; + EXPECT_EQ(111, send_codec_spec2.payload_type); + EXPECT_STRCASEEQ("opus", send_codec_spec2.format.name.c_str()); + EXPECT_EQ(112, send_codec_spec2.red_payload_type); + } + parameters.codecs[0].params[""] += "/111"; + SetSendParameters(parameters); + const auto& send_codec_spec3 = *GetSendStreamConfig(kSsrcX).send_codec_spec; + EXPECT_EQ(111, send_codec_spec3.payload_type); + EXPECT_STRCASEEQ("opus", send_codec_spec3.format.name.c_str()); + EXPECT_EQ(absl::nullopt, send_codec_spec3.red_payload_type); +} + // Test that WebRtcVoiceEngine reconfigures, rather than recreates its // AudioSendStream. TEST_P(WebRtcVoiceEngineTestFake, DontRecreateSendStream) { @@ -2424,58 +2462,6 @@ TEST_P(WebRtcVoiceEngineTestFake, PlayoutWithMultipleStreams) { EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY)); } -TEST_P(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) { - EXPECT_TRUE(SetupSendStream()); - EXPECT_CALL(*adm_, BuiltInAGCIsAvailable()) - .Times(::testing::AtLeast(1)) - .WillRepeatedly(Return(false)); - - if (!use_null_apm_) { - // Ensure default options. - VerifyGainControlEnabledCorrectly(); - VerifyGainControlDefaultSettings(); - } - - const auto& agc_config = apm_config_.gain_controller1; - - send_parameters_.options.auto_gain_control = false; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_FALSE(agc_config.enabled); - } - send_parameters_.options.auto_gain_control = absl::nullopt; - - send_parameters_.options.tx_agc_target_dbov = 5; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_EQ(5, agc_config.target_level_dbfs); - } - send_parameters_.options.tx_agc_target_dbov = absl::nullopt; - - send_parameters_.options.tx_agc_digital_compression_gain = 10; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_EQ(10, agc_config.compression_gain_db); - } - send_parameters_.options.tx_agc_digital_compression_gain = absl::nullopt; - - send_parameters_.options.tx_agc_limiter = false; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_FALSE(agc_config.enable_limiter); - } - send_parameters_.options.tx_agc_limiter = absl::nullopt; - - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - // Expect all options to have been preserved. - EXPECT_FALSE(agc_config.enabled); - EXPECT_EQ(5, agc_config.target_level_dbfs); - EXPECT_EQ(10, agc_config.compression_gain_db); - EXPECT_FALSE(agc_config.enable_limiter); - } -} - TEST_P(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) { EXPECT_TRUE(SetupSendStream()); send_parameters_.options.audio_network_adaptor = true; @@ -2517,14 +2503,6 @@ TEST_P(WebRtcVoiceEngineTestFake, AudioNetworkAdaptorNotGetOverridden) { GetAudioNetworkAdaptorConfig(kSsrcX)); } -class WebRtcVoiceEngineWithSendSideBweWithOverheadTest - : public WebRtcVoiceEngineTestFake { - public: - WebRtcVoiceEngineWithSendSideBweWithOverheadTest() - : WebRtcVoiceEngineTestFake( - "WebRTC-Audio-Allocation/min:6000bps,max:32000bps/") {} -}; - // Test that we can set the outgoing SSRC properly. // SSRC is set in SetupSendStream() by calling AddSendStream. TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrc) { @@ -2982,32 +2960,10 @@ TEST_P(WebRtcVoiceEngineTestFake, SetAudioOptions) { if (!use_null_apm_) { VerifyEchoCancellationSettings(/*enabled=*/true); EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_TRUE(IsTypingDetectionEnabled()); } EXPECT_EQ(200u, GetRecvStreamConfig(kSsrcY).jitter_buffer_max_packets); EXPECT_FALSE(GetRecvStreamConfig(kSsrcY).jitter_buffer_fast_accelerate); - // Turn typing detection off. - send_parameters_.options.typing_detection = false; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_FALSE(IsTypingDetectionEnabled()); - } - - // Leave typing detection unchanged, but non-default. - send_parameters_.options.typing_detection = absl::nullopt; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_FALSE(IsTypingDetectionEnabled()); - } - - // Turn typing detection on. - send_parameters_.options.typing_detection = true; - SetSendParameters(send_parameters_); - if (!use_null_apm_) { - EXPECT_TRUE(IsTypingDetectionEnabled()); - } - // Turn echo cancellation off send_parameters_.options.echo_cancellation = false; SetSendParameters(send_parameters_); @@ -3073,6 +3029,34 @@ TEST_P(WebRtcVoiceEngineTestFake, SetAudioOptions) { } } +TEST_P(WebRtcVoiceEngineTestFake, InitRecordingOnSend) { + EXPECT_CALL(*adm_, RecordingIsInitialized()).WillOnce(Return(false)); + EXPECT_CALL(*adm_, Recording()).WillOnce(Return(false)); + EXPECT_CALL(*adm_, InitRecording()).Times(1); + + std::unique_ptr channel( + engine_->CreateMediaChannel(&call_, cricket::MediaConfig(), + cricket::AudioOptions(), + webrtc::CryptoOptions())); + + channel->SetSend(true); +} + +TEST_P(WebRtcVoiceEngineTestFake, SkipInitRecordingOnSend) { + EXPECT_CALL(*adm_, RecordingIsInitialized()).Times(0); + EXPECT_CALL(*adm_, Recording()).Times(0); + EXPECT_CALL(*adm_, InitRecording()).Times(0); + + cricket::AudioOptions options; + options.init_recording_on_send = false; + + std::unique_ptr channel( + engine_->CreateMediaChannel(&call_, cricket::MediaConfig(), options, + webrtc::CryptoOptions())); + + channel->SetSend(true); +} + TEST_P(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { EXPECT_TRUE(SetupSendStream()); EXPECT_CALL(*adm_, BuiltInAECIsAvailable()) diff --git a/media/sctp/dcsctp_transport.cc b/media/sctp/dcsctp_transport.cc index 49cb4ef042..0a671ced87 100644 --- a/media/sctp/dcsctp_transport.cc +++ b/media/sctp/dcsctp_transport.cc @@ -10,6 +10,7 @@ #include "media/sctp/dcsctp_transport.h" +#include #include #include #include @@ -37,6 +38,13 @@ namespace webrtc { namespace { using ::dcsctp::SendPacketStatus; +// When there is packet loss for a long time, the SCTP retry timers will use +// exponential backoff, which can grow to very long durations and when the +// connection recovers, it may take a long time to reach the new backoff +// duration. By limiting it to a reasonable limit, the time to recover reduces. +constexpr dcsctp::DurationMs kMaxTimerBackoffDuration = + dcsctp::DurationMs(3000); + enum class WebrtcPPID : dcsctp::PPID::UnderlyingType { // https://www.rfc-editor.org/rfc/rfc8832.html#section-8.1 kDCEP = 50, @@ -119,7 +127,7 @@ DcSctpTransport::DcSctpTransport(rtc::Thread* network_thread, socket_->HandleTimeout(timeout_id); }) { RTC_DCHECK_RUN_ON(network_thread_); - static int instance_count = 0; + static std::atomic instance_count = 0; rtc::StringBuilder sb; sb << debug_name_ << instance_count++; debug_name_ = sb.Release(); @@ -156,6 +164,10 @@ bool DcSctpTransport::Start(int local_sctp_port, options.local_port = local_sctp_port; options.remote_port = remote_sctp_port; options.max_message_size = max_message_size; + options.max_timer_backoff_duration = kMaxTimerBackoffDuration; + // Don't close the connection automatically on too many retransmissions. + options.max_retransmissions = absl::nullopt; + options.max_init_retransmits = absl::nullopt; std::unique_ptr packet_observer; if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) { @@ -348,8 +360,9 @@ SendPacketStatus DcSctpTransport::SendPacketWithStatus( return SendPacketStatus::kSuccess; } -std::unique_ptr DcSctpTransport::CreateTimeout() { - return task_queue_timeout_factory_.CreateTimeout(); +std::unique_ptr DcSctpTransport::CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) { + return task_queue_timeout_factory_.CreateTimeout(precision); } dcsctp::TimeMs DcSctpTransport::TimeMillis() { @@ -395,9 +408,18 @@ void DcSctpTransport::OnMessageReceived(dcsctp::DcSctpMessage message) { void DcSctpTransport::OnError(dcsctp::ErrorKind error, absl::string_view message) { - RTC_LOG(LS_ERROR) << debug_name_ - << "->OnError(error=" << dcsctp::ToString(error) - << ", message=" << message << ")."; + if (error == dcsctp::ErrorKind::kResourceExhaustion) { + // Indicates that a message failed to be enqueued, because the send buffer + // is full, which is a very common (and wanted) state for high throughput + // sending/benchmarks. + RTC_LOG(LS_VERBOSE) << debug_name_ + << "->OnError(error=" << dcsctp::ToString(error) + << ", message=" << message << ")."; + } else { + RTC_LOG(LS_ERROR) << debug_name_ + << "->OnError(error=" << dcsctp::ToString(error) + << ", message=" << message << ")."; + } } void DcSctpTransport::OnAborted(dcsctp::ErrorKind error, @@ -505,6 +527,7 @@ void DcSctpTransport::OnTransportReadPacket( size_t length, const int64_t& /* packet_time_us */, int flags) { + RTC_DCHECK_RUN_ON(network_thread_); if (flags) { // We are only interested in SCTP packets. return; diff --git a/media/sctp/dcsctp_transport.h b/media/sctp/dcsctp_transport.h index c8c5199396..11c2f829c5 100644 --- a/media/sctp/dcsctp_transport.h +++ b/media/sctp/dcsctp_transport.h @@ -17,6 +17,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" #include "media/sctp/sctp_transport_internal.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/public/dcsctp_socket.h" @@ -61,7 +62,8 @@ class DcSctpTransport : public cricket::SctpTransportInternal, // dcsctp::DcSctpSocketCallbacks dcsctp::SendPacketStatus SendPacketWithStatus( rtc::ArrayView data) override; - std::unique_ptr CreateTimeout() override; + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) override; dcsctp::TimeMs TimeMillis() override; uint32_t GetRandomInt(uint32_t low, uint32_t high) override; void OnTotalBufferedAmountLow() override; diff --git a/media/sctp/sctp_transport_factory.cc b/media/sctp/sctp_transport_factory.cc index 5097d423d9..21b5b026eb 100644 --- a/media/sctp/sctp_transport_factory.cc +++ b/media/sctp/sctp_transport_factory.cc @@ -10,12 +10,12 @@ #include "media/sctp/sctp_transport_factory.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/system/unused.h" #ifdef WEBRTC_HAVE_DCSCTP #include "media/sctp/dcsctp_transport.h" // nogncheck #include "system_wrappers/include/clock.h" // nogncheck -#include "system_wrappers/include/field_trial.h" // nogncheck #endif #ifdef WEBRTC_HAVE_USRSCTP @@ -24,12 +24,14 @@ namespace cricket { -SctpTransportFactory::SctpTransportFactory(rtc::Thread* network_thread) - : network_thread_(network_thread), use_dcsctp_("Enabled", false) { +SctpTransportFactory::SctpTransportFactory( + rtc::Thread* network_thread, + const webrtc::WebRtcKeyValueConfig& field_trials) + : network_thread_(network_thread), use_usrsctp_("Disabled", false) { RTC_UNUSED(network_thread_); #ifdef WEBRTC_HAVE_DCSCTP - webrtc::ParseFieldTrial({&use_dcsctp_}, webrtc::field_trial::FindFullName( - "WebRTC-DataChannel-Dcsctp")); + webrtc::ParseFieldTrial({&use_usrsctp_}, + field_trials.Lookup("WebRTC-DataChannel-Dcsctp")); #endif } @@ -38,7 +40,7 @@ SctpTransportFactory::CreateSctpTransport( rtc::PacketTransportInternal* transport) { std::unique_ptr result; #ifdef WEBRTC_HAVE_DCSCTP - if (use_dcsctp_.Get()) { + if (!use_usrsctp_.Get()) { result = std::unique_ptr(new webrtc::DcSctpTransport( network_thread_, transport, webrtc::Clock::GetRealTimeClock())); } diff --git a/media/sctp/sctp_transport_factory.h b/media/sctp/sctp_transport_factory.h index ed7c2163d7..4fcec8e0e0 100644 --- a/media/sctp/sctp_transport_factory.h +++ b/media/sctp/sctp_transport_factory.h @@ -14,6 +14,7 @@ #include #include "api/transport/sctp_transport_factory_interface.h" +#include "api/webrtc_key_value_config.h" #include "media/sctp/sctp_transport_internal.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/thread.h" @@ -22,14 +23,16 @@ namespace cricket { class SctpTransportFactory : public webrtc::SctpTransportFactoryInterface { public: - explicit SctpTransportFactory(rtc::Thread* network_thread); + explicit SctpTransportFactory( + rtc::Thread* network_thread, + const webrtc::WebRtcKeyValueConfig& field_trials); std::unique_ptr CreateSctpTransport( rtc::PacketTransportInternal* transport) override; private: rtc::Thread* network_thread_; - webrtc::FieldTrialFlag use_dcsctp_; + webrtc::FieldTrialFlag use_usrsctp_; }; } // namespace cricket diff --git a/media/sctp/usrsctp_transport.cc b/media/sctp/usrsctp_transport.cc index c7e3016a49..4babf110a2 100644 --- a/media/sctp/usrsctp_transport.cc +++ b/media/sctp/usrsctp_transport.cc @@ -788,8 +788,8 @@ SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) { if (send_res < 0) { if (errno == SCTP_EWOULDBLOCK) { ready_to_send_data_ = false; - RTC_LOG(LS_INFO) << debug_name_ - << "->SendMessageInternal(...): EWOULDBLOCK returned"; + RTC_LOG(LS_VERBOSE) << debug_name_ + << "->SendMessageInternal(...): EWOULDBLOCK returned"; return SDR_BLOCK; } @@ -1172,8 +1172,8 @@ void UsrsctpTransport::OnPacketRead(rtc::PacketTransportInternal* transport, // packet will have called connect, and a connection will be established. if (sock_) { // Pass received packet to SCTP stack. Once processed by usrsctp, the data - // will be will be given to the global OnSctpInboundData, and then, - // marshalled by the AsyncInvoker. + // will be will be given to the global OnSctpInboundPacket callback and + // posted to the transport thread. VerboseLogPacket(data, len, SCTP_DUMP_INBOUND); usrsctp_conninput(reinterpret_cast(id_), data, len, 0); } else { diff --git a/media/sctp/usrsctp_transport.h b/media/sctp/usrsctp_transport.h index 06988fd156..7c7ce8c4a8 100644 --- a/media/sctp/usrsctp_transport.h +++ b/media/sctp/usrsctp_transport.h @@ -22,7 +22,6 @@ #include "absl/types/optional.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -64,7 +63,7 @@ struct SctpInboundPacket; // 11. SctpTransport::OnDataFromSctpToTransport(data) // 12. SctpTransport::SignalDataReceived(data) // [from the same thread, methods registered/connected to -// SctpTransport are called with the recieved data] +// SctpTransport are called with the received data] class UsrsctpTransport : public SctpTransportInternal, public sigslot::has_slots<> { public: @@ -76,6 +75,9 @@ class UsrsctpTransport : public SctpTransportInternal, rtc::PacketTransportInternal* transport); ~UsrsctpTransport() override; + UsrsctpTransport(const UsrsctpTransport&) = delete; + UsrsctpTransport& operator=(const UsrsctpTransport&) = delete; + // SctpTransportInternal overrides (see sctptransportinternal.h for comments). void SetDtlsTransport(rtc::PacketTransportInternal* transport) override; bool Start(int local_port, int remote_port, int max_message_size) override; @@ -285,8 +287,6 @@ class UsrsctpTransport : public SctpTransportInternal, uintptr_t id_ = 0; friend class UsrsctpTransportMap; - - RTC_DISALLOW_COPY_AND_ASSIGN(UsrsctpTransport); }; class UsrsctpTransportMap; diff --git a/media/sctp/usrsctp_transport_reliability_unittest.cc b/media/sctp/usrsctp_transport_reliability_unittest.cc index 104e320398..987dd04358 100644 --- a/media/sctp/usrsctp_transport_reliability_unittest.cc +++ b/media/sctp/usrsctp_transport_reliability_unittest.cc @@ -58,6 +58,9 @@ class SimulatedPacketTransport final : public rtc::PacketTransportInternal { SignalWritableState(this); } + SimulatedPacketTransport(const SimulatedPacketTransport&) = delete; + SimulatedPacketTransport& operator=(const SimulatedPacketTransport&) = delete; + const std::string& transport_name() const override { return transport_name_; } bool writable() const override { return destination_ != nullptr; } @@ -129,7 +132,6 @@ class SimulatedPacketTransport final : public rtc::PacketTransportInternal { std::atomic destination_ ATOMIC_VAR_INIT(nullptr); webrtc::Random random_; webrtc::ScopedTaskSafety task_safety_; - RTC_DISALLOW_COPY_AND_ASSIGN(SimulatedPacketTransport); }; /** @@ -156,6 +158,9 @@ class SctpDataSender final { RTC_DCHECK(transport_); } + SctpDataSender(const SctpDataSender&) = delete; + SctpDataSender& operator=(const SctpDataSender&) = delete; + void Start() { thread_->PostTask(ToQueuedTask(task_safety_.flag(), [this] { if (started_) { @@ -236,7 +241,6 @@ class SctpDataSender final { std::atomic num_bytes_sent_ ATOMIC_VAR_INIT(0); absl::optional last_error_; webrtc::ScopedTaskSafetyDetached task_safety_; - RTC_DISALLOW_COPY_AND_ASSIGN(SctpDataSender); }; /** @@ -251,6 +255,9 @@ class SctpDataReceiver final : public sigslot::has_slots<> { : receiver_id_(receiver_id), target_messages_count_(target_messages_count) {} + SctpDataReceiver(const SctpDataReceiver&) = delete; + SctpDataReceiver& operator=(const SctpDataReceiver&) = delete; + void OnDataReceived(const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& data) { num_bytes_received_ += data.size(); @@ -259,8 +266,8 @@ class SctpDataReceiver final : public sigslot::has_slots<> { } if (num_messages_received_ % kLogPerMessagesCount == 0) { - RTC_LOG(INFO) << receiver_id_ << " receiver got " - << num_messages_received_ << " messages"; + RTC_LOG(LS_INFO) << receiver_id_ << " receiver got " + << num_messages_received_ << " messages"; } } @@ -278,7 +285,6 @@ class SctpDataReceiver final : public sigslot::has_slots<> { rtc::Event received_target_messages_count_{true, false}; const uint32_t receiver_id_; const uint64_t target_messages_count_; - RTC_DISALLOW_COPY_AND_ASSIGN(SctpDataReceiver); }; /** @@ -297,6 +303,9 @@ class ThreadPool final { } } + ThreadPool(const ThreadPool&) = delete; + ThreadPool& operator=(const ThreadPool&) = delete; + rtc::Thread* GetRandomThread() { return threads_[random_.Rand(0U, threads_.size() - 1)].get(); } @@ -304,7 +313,6 @@ class ThreadPool final { private: webrtc::Random random_; std::vector> threads_; - RTC_DISALLOW_COPY_AND_ASSIGN(ThreadPool); }; /** @@ -360,6 +368,9 @@ class SctpPingPong final { }); } + SctpPingPong(const SctpPingPong&) = delete; + SctpPingPong& operator=(const SctpPingPong&) = delete; + bool Start() { CreateTwoConnectedSctpTransportsWithAllStreams(); @@ -577,7 +588,6 @@ class SctpPingPong final { const uint8_t packet_loss_percents_; const uint16_t avg_send_delay_millis_; const webrtc::SendDataParams send_params_; - RTC_DISALLOW_COPY_AND_ASSIGN(SctpPingPong); }; /** diff --git a/media/sctp/usrsctp_transport_unittest.cc b/media/sctp/usrsctp_transport_unittest.cc index 59e9c59b3d..8fdbabc14a 100644 --- a/media/sctp/usrsctp_transport_unittest.cc +++ b/media/sctp/usrsctp_transport_unittest.cc @@ -36,7 +36,7 @@ static const int kTransport2Port = 5002; namespace cricket { -// This is essentially a buffer to hold recieved data. It stores only the last +// This is essentially a buffer to hold received data. It stores only the last // received data. Calling OnDataReceived twice overwrites old data with the // newer one. // TODO(ldixon): Implement constraints, and allow new data to be added to old diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index e67a05ddae..af76a11eae 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -118,12 +118,12 @@ rtc_library("red") { deps = [ "../../api:array_view", + "../../api:webrtc_key_value_config", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", - "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -993,7 +993,6 @@ rtc_library("neteq") { deps = [ ":audio_coding_module_typedefs", ":webrtc_cng", - "..:module_api", "..:module_api_public", "../../api:array_view", "../../api:rtp_headers", @@ -1319,7 +1318,6 @@ if (rtc_include_tests) { ":g722_test", ":ilbc_test", ":isac_api_test", - ":isac_fix_test", ":isac_switch_samprate_test", ":isac_test", ":neteq_ilbc_quality_test", @@ -1398,6 +1396,7 @@ if (rtc_include_tests) { "../../rtc_base:rtc_base_approved", "../../rtc_base/synchronization:mutex", "../../test:fileutils", + "../../test:scoped_key_value_config", "../../test:test_support", ] absl_deps = [ @@ -1439,7 +1438,6 @@ if (rtc_include_tests) { deps = audio_coding_deps + [ "../../api:scoped_refptr", - "..:module_api", ":audio_coding", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:builtin_audio_decoder_factory", @@ -1838,20 +1836,6 @@ if (rtc_include_tests) { "//third_party/abseil-cpp/absl/flags:flag", ] } - - rtc_test("isac_fix_test") { - testonly = true - - sources = [ "codecs/isac/fix/test/kenny.cc" ] - - deps = [ - ":isac_fix", - "../../test:perf_test", - "../../test:test_support", - ] - - data = [ "../../resources/speech_and_misc_wb.pcm" ] - } } rtc_library("isac_test_util") { @@ -2095,6 +2079,7 @@ if (rtc_include_tests) { "../../test:fileutils", "../../test:rtc_expect_death", "../../test:rtp_test_utils", + "../../test:scoped_key_value_config", "../../test:test_common", "../../test:test_support", "codecs/opus/test", diff --git a/modules/audio_coding/acm2/acm_receive_test.h b/modules/audio_coding/acm2/acm_receive_test.h index 6349c6392a..2095ef9025 100644 --- a/modules/audio_coding/acm2/acm_receive_test.h +++ b/modules/audio_coding/acm2/acm_receive_test.h @@ -18,7 +18,6 @@ #include "api/audio_codecs/audio_decoder_factory.h" #include "api/scoped_refptr.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/clock.h" namespace webrtc { @@ -45,6 +44,9 @@ class AcmReceiveTestOldApi { rtc::scoped_refptr decoder_factory); virtual ~AcmReceiveTestOldApi(); + AcmReceiveTestOldApi(const AcmReceiveTestOldApi&) = delete; + AcmReceiveTestOldApi& operator=(const AcmReceiveTestOldApi&) = delete; + // Registers the codecs with default parameters from ACM. void RegisterDefaultCodecs(); @@ -67,8 +69,6 @@ class AcmReceiveTestOldApi { AudioSink* audio_sink_; int output_freq_hz_; NumOutputChannels exptected_output_channels_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AcmReceiveTestOldApi); }; // This test toggles the output frequency every `toggle_period_ms`. The test diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc index 6d9211cd25..aa9816956e 100644 --- a/modules/audio_coding/acm2/acm_receiver.cc +++ b/modules/audio_coding/acm2/acm_receiver.cc @@ -66,14 +66,14 @@ AcmReceiver::~AcmReceiver() = default; int AcmReceiver::SetMinimumDelay(int delay_ms) { if (neteq_->SetMinimumDelay(delay_ms)) return 0; - RTC_LOG(LERROR) << "AcmReceiver::SetExtraDelay " << delay_ms; + RTC_LOG(LS_ERROR) << "AcmReceiver::SetExtraDelay " << delay_ms; return -1; } int AcmReceiver::SetMaximumDelay(int delay_ms) { if (neteq_->SetMaximumDelay(delay_ms)) return 0; - RTC_LOG(LERROR) << "AcmReceiver::SetExtraDelay " << delay_ms; + RTC_LOG(LS_ERROR) << "AcmReceiver::SetExtraDelay " << delay_ms; return -1; } @@ -134,9 +134,9 @@ int AcmReceiver::InsertPacket(const RTPHeader& rtp_header, } // `mutex_` is released. if (neteq_->InsertPacket(rtp_header, incoming_payload) < 0) { - RTC_LOG(LERROR) << "AcmReceiver::InsertPacket " - << static_cast(rtp_header.payloadType) - << " Failed to insert packet"; + RTC_LOG(LS_ERROR) << "AcmReceiver::InsertPacket " + << static_cast(rtp_header.payloadType) + << " Failed to insert packet"; return -1; } return 0; @@ -150,7 +150,7 @@ int AcmReceiver::GetAudio(int desired_freq_hz, int current_sample_rate_hz = 0; if (neteq_->GetAudio(audio_frame, muted, ¤t_sample_rate_hz) != NetEq::kOK) { - RTC_LOG(LERROR) << "AcmReceiver::GetAudio - NetEq Failed."; + RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - NetEq Failed."; return -1; } @@ -170,8 +170,8 @@ int AcmReceiver::GetAudio(int desired_freq_hz, audio_frame->num_channels_, AudioFrame::kMaxDataSizeSamples, temp_output); if (samples_per_channel_int < 0) { - RTC_LOG(LERROR) << "AcmReceiver::GetAudio - " - "Resampling last_audio_buffer_ failed."; + RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - " + "Resampling last_audio_buffer_ failed."; return -1; } } @@ -185,7 +185,7 @@ int AcmReceiver::GetAudio(int desired_freq_hz, audio_frame->num_channels_, AudioFrame::kMaxDataSizeSamples, audio_frame->mutable_data()); if (samples_per_channel_int < 0) { - RTC_LOG(LERROR) + RTC_LOG(LS_ERROR) << "AcmReceiver::GetAudio - Resampling audio_buffer_ failed."; return -1; } diff --git a/modules/audio_coding/acm2/acm_receiver_unittest.cc b/modules/audio_coding/acm2/acm_receiver_unittest.cc index 2338a53235..e73acc2338 100644 --- a/modules/audio_coding/acm2/acm_receiver_unittest.cc +++ b/modules/audio_coding/acm2/acm_receiver_unittest.cc @@ -119,7 +119,7 @@ class AcmReceiverTestOldApi : public AudioPacketizationCallback, rtp_header_, rtc::ArrayView(payload_data, payload_len_bytes)); if (ret_val < 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } rtp_header_.sequenceNumber++; diff --git a/modules/audio_coding/acm2/acm_resampler.cc b/modules/audio_coding/acm2/acm_resampler.cc index 7c7393d72f..e307c6ca57 100644 --- a/modules/audio_coding/acm2/acm_resampler.cc +++ b/modules/audio_coding/acm2/acm_resampler.cc @@ -30,7 +30,7 @@ int ACMResampler::Resample10Msec(const int16_t* in_audio, size_t in_length = in_freq_hz * num_audio_channels / 100; if (in_freq_hz == out_freq_hz) { if (out_capacity_samples < in_length) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } memcpy(out_audio, in_audio, in_length * sizeof(int16_t)); diff --git a/modules/audio_coding/acm2/acm_send_test.h b/modules/audio_coding/acm2/acm_send_test.h index 0c82415d11..b14cb80c6a 100644 --- a/modules/audio_coding/acm2/acm_send_test.h +++ b/modules/audio_coding/acm2/acm_send_test.h @@ -17,7 +17,6 @@ #include "api/audio/audio_frame.h" #include "modules/audio_coding/include/audio_coding_module.h" #include "modules/audio_coding/neteq/tools/packet_source.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/clock.h" namespace webrtc { @@ -35,6 +34,9 @@ class AcmSendTestOldApi : public AudioPacketizationCallback, int test_duration_ms); ~AcmSendTestOldApi() override; + AcmSendTestOldApi(const AcmSendTestOldApi&) = delete; + AcmSendTestOldApi& operator=(const AcmSendTestOldApi&) = delete; + // Registers the send codec. Returns true on success, false otherwise. bool RegisterCodec(const char* payload_name, int sampling_freq_hz, @@ -81,8 +83,6 @@ class AcmSendTestOldApi : public AudioPacketizationCallback, uint16_t sequence_number_; std::vector last_payload_vec_; bool data_to_send_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AcmSendTestOldApi); }; } // namespace test diff --git a/modules/audio_coding/acm2/audio_coding_module.cc b/modules/audio_coding/acm2/audio_coding_module.cc index 8ba1b9f3de..e2081e20dc 100644 --- a/modules/audio_coding/acm2/audio_coding_module.cc +++ b/modules/audio_coding/acm2/audio_coding_module.cc @@ -92,6 +92,8 @@ class AudioCodingModuleImpl final : public AudioCodingModule { ANAStats GetANAStats() const override; + int GetTargetBitrate() const override; + private: struct InputData { InputData() : buffer(kInitialInputDataBufferSize) {} @@ -342,13 +344,13 @@ int AudioCodingModuleImpl::Add10MsData(const AudioFrame& audio_frame) { int AudioCodingModuleImpl::Add10MsDataInternal(const AudioFrame& audio_frame, InputData* input_data) { if (audio_frame.samples_per_channel_ == 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); RTC_LOG(LS_ERROR) << "Cannot Add 10 ms audio, payload length is zero"; return -1; } if (audio_frame.sample_rate_hz_ > kMaxInputSampleRateHz) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); RTC_LOG(LS_ERROR) << "Cannot Add 10 ms audio, input frequency not valid"; return -1; } @@ -603,6 +605,14 @@ ANAStats AudioCodingModuleImpl::GetANAStats() const { return ANAStats(); } +int AudioCodingModuleImpl::GetTargetBitrate() const { + MutexLock lock(&acm_mutex_); + if (!encoder_stack_) { + return -1; + } + return encoder_stack_->GetTargetBitrate(); +} + } // namespace AudioCodingModule::Config::Config( diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc index c3f257d0e1..c429cc4723 100644 --- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc +++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc @@ -835,32 +835,6 @@ TEST_F(AcmReRegisterIsacMtTestOldApi, MAYBE_DoTest) { #if !defined(WEBRTC_IOS) class AcmReceiverBitExactnessOldApi : public ::testing::Test { - public: - static std::string PlatformChecksum(std::string others, - std::string win64, - std::string android_arm32, - std::string android_arm64, - std::string android_arm64_clang, - std::string mac_arm64) { -#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) - return win64; -#elif defined(WEBRTC_MAC) && defined(WEBRTC_ARCH_ARM64) - return mac_arm64; -#elif defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM) - return android_arm32; -#elif defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM64) -#if defined(__clang__) - // Android ARM64 with Clang compiler - return android_arm64_clang; -#else - // Android ARM64 with non-Clang compiler - return android_arm64; -#endif // __clang__ -#else - return others; -#endif - } - protected: struct ExternalDecoder { int rtp_payload_type; @@ -917,90 +891,34 @@ class AcmReceiverBitExactnessOldApi : public ::testing::Test { }; #if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) && \ - defined(WEBRTC_CODEC_ILBC) + defined(WEBRTC_CODEC_ILBC) && defined(WEBRTC_LINUX) && \ + defined(WEBRTC_ARCH_X86_64) TEST_F(AcmReceiverBitExactnessOldApi, 8kHzOutput) { - std::string others_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "e0c966d7b8c36ff60167988fa35d33e0" -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) - : "5af28619e3a3c606b2242c9a12f4f64e"; -#else - : "7d8f6b84abd1e57ec010a53bc2130652"; -#endif - std::string win64_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "405a50f0bcb8827e20aa944299fc59f6" - : "0ed5830930f5527a01bbec0ba11f8541"; - Run(8000, PlatformChecksum( - others_checksum_reference, win64_checksum_reference, - /*android_arm32=*/"b892ed69c38b21b16c132ec2ce03aa7b", - /*android_arm64=*/"4598140b5e4f7ee66c5adad609e65a3e", - /*android_arm64_clang=*/"5fec8d770778ef7969ec98c56d9eb10f", - /*mac_arm64=*/"636efe6d0a148f22c5383f356da3deac")); + std::string checksum_reference = GetCPUInfo(kAVX2) != 0 + ? "d8671dd38dab43fc9ca64a45c048c218" + : "4710c99559aec2f9f02a983ba2146f2d"; + Run(/*output_freq_hz=*/8000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 16kHzOutput) { - std::string others_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "a63c578e1195c8420f453962c6d8519c" - -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) - : "f788cc9200ac4a7d498d9081987808a3"; -#else - : "6bac83762c1306b932cd25a560155681"; -#endif - std::string win64_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "58fd62a5c49ee513f9fa6fe7dbf62c97" - : "0509cf0672f543efb4b050e8cffefb1d"; - Run(16000, PlatformChecksum( - others_checksum_reference, win64_checksum_reference, - /*android_arm32=*/"3cea9abbeabbdea9a79719941b241af5", - /*android_arm64=*/"f2aad418af974a3b1694d5ae5cc2c3c7", - /*android_arm64_clang=*/"9d4b92c31c00e321a4cff29ad002d6a2", - /*mac_arm64=*/"1e2d1b482fdc924f79a838503ee7ead5")); + std::string checksum_reference = GetCPUInfo(kAVX2) != 0 + ? "abcb31509af46545edb4f6700728a4de" + : "70b3217df49834b7093c631531068bd0"; + Run(/*output_freq_hz=*/16000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 32kHzOutput) { - std::string others_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "8775ce387f44dc5ff4a26da295d5ee7c" -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) - : "5b84b2a179cb8533a8f9bcd19612e7f0"; -#else - : "e319222ca47733709f90fdf33c8574db"; -#endif - std::string win64_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "04ce6a1dac5ffdd8438d804623d0132f" - : "39a4a7a1c455b35baeffb9fd193d7858"; - Run(32000, PlatformChecksum( - others_checksum_reference, win64_checksum_reference, - /*android_arm32=*/"4df55b3b62bcbf4328786d474ae87f61", - /*android_arm64=*/"100869c8dcde51346c2073e52a272d98", - /*android_arm64_clang=*/"ff58d3153d2780a3df6bc2068844cb2d", - /*mac_arm64=*/"51788e9784a10ae14a030f075a039205")); + std::string checksum_reference = GetCPUInfo(kAVX2) != 0 + ? "8489b7743d6cd1903807ac81e5ee493d" + : "2679e4e596e33259228c62df545eb635"; + Run(/*output_freq_hz=*/32000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutput) { - std::string others_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "7a55700b7ca9aa60237db58b33e55606" -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) - : "a2459749062f96297283cce4a8c7e6db"; -#else - : "57d1d316c88279f4f3da3511665069a9"; -#endif - std::string win64_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "f59833d9b0924f4b0704707dd3589f80" - : "74cbe7345e2b6b45c1e455a5d1e921ca"; - Run(48000, PlatformChecksum( - others_checksum_reference, win64_checksum_reference, - /*android_arm32=*/"f52bc7bf0f499c9da25932fdf176c4ec", - /*android_arm64=*/"bd44bf97e7899186532f91235cef444d", - /*android_arm64_clang=*/"364d403dae55d73cd69e6dbd6b723a4d", - /*mac_arm64=*/"71bc5c15a151400517c2119d1602ee9f")); + std::string checksum_reference = GetCPUInfo(kAVX2) != 0 + ? "454996a7adb3f62b259a53a09ff624cf" + : "f0148c5ef84e74e019ac7057af839102"; + Run(/*output_freq_hz=*/48000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutputExternalDecoder) { @@ -1076,28 +994,12 @@ TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutputExternalDecoder) { rtc::scoped_refptr fact_; // Fallback factory. }; - rtc::scoped_refptr> factory( - new rtc::RefCountedObject); - std::string others_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "7a55700b7ca9aa60237db58b33e55606" -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) - : "a2459749062f96297283cce4a8c7e6db"; -#else - : "57d1d316c88279f4f3da3511665069a9"; -#endif - std::string win64_checksum_reference = - GetCPUInfo(kAVX2) != 0 ? "f59833d9b0924f4b0704707dd3589f80" - : "74cbe7345e2b6b45c1e455a5d1e921ca"; - Run(48000, - PlatformChecksum( - others_checksum_reference, win64_checksum_reference, - /*android_arm32=*/"f52bc7bf0f499c9da25932fdf176c4ec", - /*android_arm64=*/"bd44bf97e7899186532f91235cef444d", - /*android_arm64_clang=*/"364d403dae55d73cd69e6dbd6b723a4d", - /*mac_arm64=*/"71bc5c15a151400517c2119d1602ee9f"), - factory, [](AudioCodingModule* acm) { + auto factory = rtc::make_ref_counted(); + std::string checksum_reference = GetCPUInfo(kAVX2) != 0 + ? "454996a7adb3f62b259a53a09ff624cf" + : "f0148c5ef84e74e019ac7057af839102"; + Run(48000, checksum_reference, factory, + [](AudioCodingModule* acm) { acm->SetReceiveCodecs({{0, {"MockPCMu", 8000, 1}}, {103, {"ISAC", 16000, 1}}, {104, {"ISAC", 32000, 1}}, @@ -1311,274 +1213,180 @@ class AcmSenderBitExactnessNewApi : public AcmSenderBitExactnessOldApi {}; // Run bit exactness tests only for release builds. #if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) && \ - defined(NDEBUG) + defined(NDEBUG) && defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) TEST_F(AcmSenderBitExactnessOldApi, IsacWb30ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 480, 480)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( -#if defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"2c9cb15d4ed55b5a0cadd04883bc73b0", -#else - /*others=*/"6f7f227f4e2ace7027257eecb7b17e08", -#endif - /*win64=*/"9336a9b993cbd8a751f0e8958e66c89c", - /*android_arm32=*/"5c2eb46199994506236f68b2c8e51b0d", - /*android_arm64=*/"343f1f42be0607c61e6516aece424609", - /*android_arm64_clang=*/"2c9cb15d4ed55b5a0cadd04883bc73b0", - /*mac_arm64=*/"6f7f227f4e2ace7027257eecb7b17e08"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( -#if defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"3c79f16f34218271f3dca4e2b1dfe1bb", -#else - /*others=*/"3fbb620556a08bcb88d9134e846bbb8e", -#endif - /*win64=*/"d42cb5195463da26c8129bbfe73a22e6", - /*android_arm32=*/"83de248aea9c3c2bd680b6952401b4ca", - /*android_arm64=*/"3c79f16f34218271f3dca4e2b1dfe1bb", - /*android_arm64_clang=*/"3c79f16f34218271f3dca4e2b1dfe1bb", - /*mac_arm64=*/"3fbb620556a08bcb88d9134e846bbb8e"), - 33, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"a3077ac01b0137e8bbc237fb1f9816a5", + /*payload_checksum_ref=*/"3c79f16f34218271f3dca4e2b1dfe1bb", + /*expected_packets=*/33, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, IsacWb60ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 960, 960)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( -#if defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"1ad29139a04782a33daad8c2b9b35875", -#else - /*others=*/"8b4377f3048d946d69b771c1e5fa8839", -#endif - /*win64=*/"14d63c5f08127d280e722e3191b73bdd", - /*android_arm32=*/"9a81e467eb1485f84aca796f8ea65011", - /*android_arm64=*/"ef75e900e6f375e3061163c53fd09a63", - /*android_arm64_clang=*/"1ad29139a04782a33daad8c2b9b35875", - /*mac_arm64=*/"8b4377f3048d946d69b771c1e5fa8839"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( -#if defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"9e0a0ab743ad987b55b8e14802769c56", -#else - /*others=*/"080f341c0d498e7a60522084bf8264ae", -#endif - /*win64=*/"ebe04a819d3a9d83a83a17f271e1139a", - /*android_arm32=*/"97aeef98553b5a4b5a68f8b716e8eaf0", - /*android_arm64=*/"9e0a0ab743ad987b55b8e14802769c56", - /*android_arm64_clang=*/"9e0a0ab743ad987b55b8e14802769c56", - /*mac_arm64=*/"080f341c0d498e7a60522084bf8264ae"), - 16, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"76da9b7514f986fc2bb32b1c3170e8d4", + /*payload_checksum_ref=*/"9e0a0ab743ad987b55b8e14802769c56", + /*expected_packets=*/16, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } #endif // Run bit exactness test only for release build. -#if defined(WEBRTC_ANDROID) || !defined(NDEBUG) -#define MAYBE_IsacSwb30ms DISABLED_IsacSwb30ms -#else -#define MAYBE_IsacSwb30ms IsacSwb30ms -#endif -#if defined(WEBRTC_CODEC_ISAC) -TEST_F(AcmSenderBitExactnessOldApi, MAYBE_IsacSwb30ms) { +#if defined(WEBRTC_CODEC_ISAC) && defined(NDEBUG) && defined(WEBRTC_LINUX) && \ + defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessOldApi, IsacSwb30ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 32000, 1, 104, 960, 960)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86) - /*others=*/"521a04159bb991fa2f32f550d5184f60", -#elif defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"5683b58da0fbf2063c7adc2e6bfb3fb8", -#else - /*others=*/"c1858ba5d734df6fe52e715eb1b25f31", -#endif - /*win64=*/"2b3c387d06f00b7b7aad4c9be56fb83d", "android_arm32_audio", - "android_arm64_audio", "android_arm64_clang_audio", - /*mac_arm64=*/"c1858ba5d734df6fe52e715eb1b25f31"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( -#if defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) && \ - defined(WEBRTC_ARCH_X86) - /*others=*/"ce86106a93419aefb063097108ec94ab", -#else - /*others=*/"127e24a1005ac80394b1f88d0cbc72a8", -#endif - /*win64=*/"bcc2041e7744c7ebd9f701866856849c", "android_arm32_payload", - "android_arm64_payload", "android_arm64_clang_payload", - /*mac_arm64=*/"127e24a1005ac80394b1f88d0cbc72a8"), - 33, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"f4cf577f28a0dcbac33358b757518e0c", + /*payload_checksum_ref=*/"ce86106a93419aefb063097108ec94ab", + /*expected_packets=*/33, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } #endif TEST_F(AcmSenderBitExactnessOldApi, Pcm16_8000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80)); - Run("15396f66b5b0ab6842e151c807395e4c", "c1edd36339ce0326cc4550041ad719a0", - 100, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"69118ed438ac76252d023e0463819471", + /*payload_checksum_ref=*/"c1edd36339ce0326cc4550041ad719a0", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcm16_16000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 1, 108, 160, 160)); - Run("54ae004529874c2b362c7f0ccd19cb99", "ad786526383178b08d80d6eee06e9bad", - 100, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"bc6ab94d12a464921763d7544fdbd07e", + /*payload_checksum_ref=*/"ad786526383178b08d80d6eee06e9bad", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcm16_32000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 1, 109, 320, 320)); - Run("d6a4a68b8c838dcc1e7ae7136467cdf0", "5ef82ea885e922263606c6fdbc49f651", - 100, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"c50244419c5c3a2f04cc69a022c266a2", + /*payload_checksum_ref=*/"5ef82ea885e922263606c6fdbc49f651", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_8000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 2, 111, 80, 80)); - Run("6b011dab43e3a8a46ccff7e4412ed8a2", "62ce5adb0d4965d0a52ec98ae7f98974", - 100, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"4fccf4cc96f1e8e8de4b9fadf62ded9e", + /*payload_checksum_ref=*/"62ce5adb0d4965d0a52ec98ae7f98974", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_16000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 2, 112, 160, 160)); - Run("17fc9854358bfe0419408290664bd78e", "41ca8edac4b8c71cd54fd9f25ec14870", - 100, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"e15e388d9d4af8c02a59fe1552fedee3", + /*payload_checksum_ref=*/"41ca8edac4b8c71cd54fd9f25ec14870", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_32000khz_10ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 2, 113, 320, 320)); - Run("9ac9a1f64d55da2fc9f3167181cc511d", "50e58502fb04421bf5b857dda4c96879", - 100, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"b240520c0d05003fde7a174ae5957286", + /*payload_checksum_ref=*/"50e58502fb04421bf5b857dda4c96879", + /*expected_packets=*/100, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcmu_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMU", 8000, 1, 0, 160, 160)); - Run("81a9d4c0bb72e9becc43aef124c981e9", "8f9b8750bd80fe26b6cbf6659b89f0f9", - 50, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"c8d1fc677f33c2022ec5f83c7f302280", + /*payload_checksum_ref=*/"8f9b8750bd80fe26b6cbf6659b89f0f9", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcma_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 1, 8, 160, 160)); - Run("39611f798969053925a49dc06d08de29", "6ad745e55aa48981bfc790d0eeef2dd1", - 50, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"47eb60e855eb12d1b0e6da9c975754a4", + /*payload_checksum_ref=*/"6ad745e55aa48981bfc790d0eeef2dd1", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcmu_stereo_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMU", 8000, 2, 110, 160, 160)); - Run("437bec032fdc5cbaa0d5175430af7b18", "60b6f25e8d1e74cb679cfe756dd9bca5", - 50, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"6ef2f57d4934714787fd0a834e3ea18e", + /*payload_checksum_ref=*/"60b6f25e8d1e74cb679cfe756dd9bca5", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } TEST_F(AcmSenderBitExactnessOldApi, Pcma_stereo_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 2, 118, 160, 160)); - Run("a5c6d83c5b7cedbeff734238220a4b0c", "92b282c83efd20e7eeef52ba40842cf7", - 50, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"a84d75e098d87ab6b260687eb4b612a2", + /*payload_checksum_ref=*/"92b282c83efd20e7eeef52ba40842cf7", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } -#if defined(WEBRTC_ANDROID) -#define MAYBE_Ilbc_30ms DISABLED_Ilbc_30ms -#else -#define MAYBE_Ilbc_30ms Ilbc_30ms -#endif -#if defined(WEBRTC_CODEC_ILBC) -TEST_F(AcmSenderBitExactnessOldApi, MAYBE_Ilbc_30ms) { +#if defined(WEBRTC_CODEC_ILBC) && defined(WEBRTC_LINUX) && \ + defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessOldApi, Ilbc_30ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("ILBC", 8000, 1, 102, 240, 240)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"7b6ec10910debd9af08011d3ed5249f7", - /*win64=*/"7b6ec10910debd9af08011d3ed5249f7", "android_arm32_audio", - "android_arm64_audio", "android_arm64_clang_audio", - /*mac_arm64=*/"7b6ec10910debd9af08011d3ed5249f7"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"cfae2e9f6aba96e145f2bcdd5050ce78", - /*win64=*/"cfae2e9f6aba96e145f2bcdd5050ce78", "android_arm32_payload", - "android_arm64_payload", "android_arm64_clang_payload", - /*mac_arm64=*/"cfae2e9f6aba96e145f2bcdd5050ce78"), - 33, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"b14dba0de36efa5ec88a32c0b320b70f", + /*payload_checksum_ref=*/"cfae2e9f6aba96e145f2bcdd5050ce78", + /*expected_packets=*/33, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } #endif -#if defined(WEBRTC_ANDROID) -#define MAYBE_G722_20ms DISABLED_G722_20ms -#else -#define MAYBE_G722_20ms G722_20ms -#endif -TEST_F(AcmSenderBitExactnessOldApi, MAYBE_G722_20ms) { +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessOldApi, G722_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 1, 9, 320, 160)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"e99c89be49a46325d03c0d990c292d68", - /*win64=*/"e99c89be49a46325d03c0d990c292d68", "android_arm32_audio", - "android_arm64_audio", "android_arm64_clang_audio", - /*mac_arm64=*/"e99c89be49a46325d03c0d990c292d68"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"fc68a87e1380614e658087cb35d5ca10", - /*win64=*/"fc68a87e1380614e658087cb35d5ca10", "android_arm32_payload", - "android_arm64_payload", "android_arm64_clang_payload", - /*mac_arm64=*/"fc68a87e1380614e658087cb35d5ca10"), - 50, test::AcmReceiveTestOldApi::kMonoOutput); + Run(/*audio_checksum_ref=*/"a87a91ec0124510a64967f5d768554ff", + /*payload_checksum_ref=*/"fc68a87e1380614e658087cb35d5ca10", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput); } - -#if defined(WEBRTC_ANDROID) -#define MAYBE_G722_stereo_20ms DISABLED_G722_stereo_20ms -#else -#define MAYBE_G722_stereo_20ms G722_stereo_20ms #endif -TEST_F(AcmSenderBitExactnessOldApi, MAYBE_G722_stereo_20ms) { + +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessOldApi, G722_stereo_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 2, 119, 320, 160)); - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"e280aed283e499d37091b481ca094807", - /*win64=*/"e280aed283e499d37091b481ca094807", "android_arm32_audio", - "android_arm64_audio", "android_arm64_clang_audio", - /*mac_arm64=*/"e280aed283e499d37091b481ca094807"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/"66516152eeaa1e650ad94ff85f668dac", - /*win64=*/"66516152eeaa1e650ad94ff85f668dac", "android_arm32_payload", - "android_arm64_payload", "android_arm64_clang_payload", - /*mac_arm64=*/"66516152eeaa1e650ad94ff85f668dac"), - 50, test::AcmReceiveTestOldApi::kStereoOutput); + Run(/*audio_checksum_ref=*/"be0b8528ff9db3a2219f55ddd36faf7f", + /*payload_checksum_ref=*/"66516152eeaa1e650ad94ff85f668dac", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } +#endif namespace { // Checksum depends on libopus being compiled with or without SSE. -const std::string audio_maybe_sse = - "e0ddf36854059151cdb7a0c4af3d282a" - "|32574e78db4eab0c467d3c0785e3b484"; -const std::string payload_maybe_sse = +const std::string audio_checksum = + "6a76fe2ffba057c06eb63239b3c47abe" + "|0c4f9d33b4a7379a34ee0c0d5718afe6"; +const std::string payload_checksum = "b43bdf7638b2bc2a5a6f30bdc640b9ed" "|c30d463e7ed10bdd1da9045f80561f27"; -// Common checksums. -const std::string audio_checksum = - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/audio_maybe_sse, - /*win64=*/audio_maybe_sse, - /*android_arm32=*/"6fcceb83acf427730570bc13eeac920c", - /*android_arm64=*/"fd96f15d547c4e155daeeef4253b174e", - /*android_arm64_clang=*/"fd96f15d547c4e155daeeef4253b174e", - "Mac_arm64_checksum_placeholder"); -const std::string payload_checksum = - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/payload_maybe_sse, - /*win64=*/payload_maybe_sse, - /*android_arm32=*/"4bd846d0aa5656ecd5dfd85701a1b78c", - /*android_arm64=*/"7efbfc9f8e3b4b2933ae2d01ab919028", - /*android_arm64_clang=*/"7efbfc9f8e3b4b2933ae2d01ab919028", - "Mac_arm64_checksum_placeholder"); } // namespace -// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been -// updated. -TEST_F(AcmSenderBitExactnessOldApi, DISABLED_Opus_stereo_20ms) { +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms) { ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 2, 120, 960, 960)); - Run(audio_checksum, payload_checksum, 50, - test::AcmReceiveTestOldApi::kStereoOutput); + Run(audio_checksum, payload_checksum, /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } +#endif -// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been -// updated. -TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusFromFormat_stereo_20ms) { +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessNewApi, OpusFromFormat_stereo_20ms) { const auto config = AudioEncoderOpus::SdpToConfig( SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}})); ASSERT_TRUE(SetUpSender(kTestFileFakeStereo32kHz, 32000)); ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder( AudioEncoderOpus::MakeAudioEncoder(*config, 120), 120)); - Run(audio_checksum, payload_checksum, 50, - test::AcmReceiveTestOldApi::kStereoOutput); + Run(audio_checksum, payload_checksum, /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } +#endif // TODO(webrtc:8649): Disabled until the Encoder counterpart of // https://webrtc-review.googlesource.com/c/src/+/129768 lands. +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusManyChannels) { constexpr int kNumChannels = 4; constexpr int kOpusPayloadType = 120; @@ -1606,33 +1414,19 @@ TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusManyChannels) { AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config); rtc::scoped_refptr decoder_factory = - new rtc::RefCountedObject( - opus_decoder.get()); + rtc::make_ref_counted(opus_decoder.get()); // Set up an EXTERNAL DECODER to parse 4 channels. - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( // audio checksum - /*others=*/"audio checksum check " - "downstream|8051617907766bec5f4e4a4f7c6d5291", - /*win64=*/"8051617907766bec5f4e4a4f7c6d5291", - /*android_arm32=*/"6183752a62dc1368f959eb3a8c93b846", - "android arm64 audio checksum", - /*android_arm64_clang=*/"48bf1f3ca0b72f3c9cdfbe79956122b1", - "Mac_arm64_checksum_placeholder"), - // payload_checksum, - AcmReceiverBitExactnessOldApi::PlatformChecksum( // payload checksum - /*others=*/"payload checksum check " - "downstream|b09c52e44b2bdd9a0809e3a5b1623a76", - /*win64=*/"b09c52e44b2bdd9a0809e3a5b1623a76", - /*android_arm32=*/"2ea535ef60f7d0c9d89e3002d4c2124f", - "android arm64 payload checksum", - /*android_arm64_clang=*/"e87995a80f50a0a735a230ca8b04a67d", - "Mac_arm64_checksum_placeholder"), - 50, test::AcmReceiveTestOldApi::kQuadOutput, decoder_factory); + Run("audio checksum check downstream|8051617907766bec5f4e4a4f7c6d5291", + "payload checksum check downstream|b09c52e44b2bdd9a0809e3a5b1623a76", + /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kQuadOutput, + decoder_factory); } +#endif -// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been -// updated. -TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusFromFormat_stereo_20ms_voip) { +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) +TEST_F(AcmSenderBitExactnessNewApi, OpusFromFormat_stereo_20ms_voip) { auto config = AudioEncoderOpus::SdpToConfig( SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}})); // If not set, default will be kAudio in case of stereo. @@ -1641,27 +1435,15 @@ TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusFromFormat_stereo_20ms_voip) { ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder( AudioEncoderOpus::MakeAudioEncoder(*config, 120), 120)); const std::string audio_maybe_sse = - "2d7e5797444f75e5bfeaffbd8c25176b" - "|408d4bdc05a8c23e46c6ac06c5b917ee"; + "058c03ca2c9bb5c0066d4c15ce50d772" + "|ca54661b220cc35239c6864ab858d29a"; const std::string payload_maybe_sse = - "b38b5584cfa7b6999b2e8e996c950c88" + "f270ec7be7a5ed60c203c2317c4e1011" "|eb0752ce1b6f2436fefc2e19bd084fb5"; - Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/audio_maybe_sse, - /*win64=*/audio_maybe_sse, - /*android_arm32=*/"f1cefe107ffdced7694d7f735342adf3", - /*android_arm64=*/"3b1bfe5dd8ed16ee5b04b93a5b5e7e48", - /*android_arm64_clang=*/"3b1bfe5dd8ed16ee5b04b93a5b5e7e48", - "Mac_arm64_checksum_placeholder"), - AcmReceiverBitExactnessOldApi::PlatformChecksum( - /*others=*/payload_maybe_sse, - /*win64=*/payload_maybe_sse, - /*android_arm32=*/"5e79a2f51c633fe145b6c10ae198d1aa", - /*android_arm64=*/"e730050cb304d54d853fd285ab0424fa", - /*android_arm64_clang=*/"e730050cb304d54d853fd285ab0424fa", - "Mac_arm64_checksum_placeholder"), - 50, test::AcmReceiveTestOldApi::kStereoOutput); + Run(audio_maybe_sse, payload_maybe_sse, /*expected_packets=*/50, + /*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput); } +#endif // This test is for verifying the SetBitRate function. The bitrate is changed at // the beginning, and the number of generated bytes are checked. @@ -1896,7 +1678,7 @@ TEST_F(AcmSenderBitExactnessOldApi, External_Pcmu_20ms) { ASSERT_TRUE(SetUpSender(kTestFileMono32kHz, 32000)); ASSERT_NO_FATAL_FAILURE( SetUpTestExternalEncoder(std::move(mock_encoder), config.payload_type)); - Run("81a9d4c0bb72e9becc43aef124c981e9", "8f9b8750bd80fe26b6cbf6659b89f0f9", + Run("c8d1fc677f33c2022ec5f83c7f302280", "8f9b8750bd80fe26b6cbf6659b89f0f9", 50, test::AcmReceiveTestOldApi::kMonoOutput); } diff --git a/modules/audio_coding/acm2/call_statistics.cc b/modules/audio_coding/acm2/call_statistics.cc index 0aad594f97..9f3bdadc88 100644 --- a/modules/audio_coding/acm2/call_statistics.cc +++ b/modules/audio_coding/acm2/call_statistics.cc @@ -45,7 +45,7 @@ void CallStatistics::DecodedByNetEq(AudioFrame::SpeechType speech_type, } case AudioFrame::kUndefined: { // If the audio is decoded by NetEq, `kUndefined` is not an option. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } } diff --git a/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h b/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h index 1c91fa19a8..664e76bda5 100644 --- a/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h +++ b/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h @@ -21,7 +21,6 @@ #include "modules/audio_coding/audio_network_adaptor/debug_dump_writer.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -44,6 +43,9 @@ class AudioNetworkAdaptorImpl final : public AudioNetworkAdaptor { ~AudioNetworkAdaptorImpl() override; + AudioNetworkAdaptorImpl(const AudioNetworkAdaptorImpl&) = delete; + AudioNetworkAdaptorImpl& operator=(const AudioNetworkAdaptorImpl&) = delete; + void SetUplinkBandwidth(int uplink_bandwidth_bps) override; void SetUplinkPacketLossFraction(float uplink_packet_loss_fraction) override; @@ -80,8 +82,6 @@ class AudioNetworkAdaptorImpl final : public AudioNetworkAdaptor { absl::optional prev_config_; ANAStats stats_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioNetworkAdaptorImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/bitrate_controller.h b/modules/audio_coding/audio_network_adaptor/bitrate_controller.h index 41bfbd1c32..c1032146cc 100644 --- a/modules/audio_coding/audio_network_adaptor/bitrate_controller.h +++ b/modules/audio_coding/audio_network_adaptor/bitrate_controller.h @@ -16,7 +16,6 @@ #include "absl/types/optional.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace audio_network_adaptor { @@ -39,6 +38,9 @@ class BitrateController final : public Controller { ~BitrateController() override; + BitrateController(const BitrateController&) = delete; + BitrateController& operator=(const BitrateController&) = delete; + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; void MakeDecision(AudioEncoderRuntimeConfig* config) override; @@ -49,7 +51,6 @@ class BitrateController final : public Controller { int frame_length_ms_; absl::optional target_audio_bitrate_bps_; absl::optional overhead_bytes_per_packet_; - RTC_DISALLOW_COPY_AND_ASSIGN(BitrateController); }; } // namespace audio_network_adaptor diff --git a/modules/audio_coding/audio_network_adaptor/channel_controller.h b/modules/audio_coding/audio_network_adaptor/channel_controller.h index f211f40f17..3cd4bb7dec 100644 --- a/modules/audio_coding/audio_network_adaptor/channel_controller.h +++ b/modules/audio_coding/audio_network_adaptor/channel_controller.h @@ -16,7 +16,6 @@ #include "absl/types/optional.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -41,6 +40,9 @@ class ChannelController final : public Controller { ~ChannelController() override; + ChannelController(const ChannelController&) = delete; + ChannelController& operator=(const ChannelController&) = delete; + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; void MakeDecision(AudioEncoderRuntimeConfig* config) override; @@ -49,7 +51,6 @@ class ChannelController final : public Controller { const Config config_; size_t channels_to_encode_; absl::optional uplink_bandwidth_bps_; - RTC_DISALLOW_COPY_AND_ASSIGN(ChannelController); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/config.proto b/modules/audio_coding/audio_network_adaptor/config.proto index 63b220ddf0..a815451993 100644 --- a/modules/audio_coding/audio_network_adaptor/config.proto +++ b/modules/audio_coding/audio_network_adaptor/config.proto @@ -5,6 +5,7 @@ package webrtc.audio_network_adaptor.config; option optimize_for = LITE_RUNTIME; option java_package = "org.webrtc.AudioNetworkAdaptor"; option java_outer_classname = "Config"; +option objc_class_prefix = "WANA"; message FecController { message Threshold { diff --git a/modules/audio_coding/audio_network_adaptor/controller_manager.cc b/modules/audio_coding/audio_network_adaptor/controller_manager.cc index 6708bc0c18..87759c37ea 100644 --- a/modules/audio_coding/audio_network_adaptor/controller_manager.cc +++ b/modules/audio_coding/audio_network_adaptor/controller_manager.cc @@ -265,7 +265,7 @@ std::unique_ptr ControllerManagerImpl::Create( break; case audio_network_adaptor::config::Controller::kFecControllerRplrBased: // FecControllerRplrBased has been removed and can't be used anymore. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); continue; case audio_network_adaptor::config::Controller::kFrameLengthController: controller = CreateFrameLengthController( @@ -293,7 +293,7 @@ std::unique_ptr ControllerManagerImpl::Create( encoder_frame_lengths_ms); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (controller_config.has_scoring_point()) { auto& scoring_point = controller_config.scoring_point(); @@ -321,7 +321,7 @@ std::unique_ptr ControllerManagerImpl::Create( } #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif // WEBRTC_ENABLE_PROTOBUF } diff --git a/modules/audio_coding/audio_network_adaptor/controller_manager.h b/modules/audio_coding/audio_network_adaptor/controller_manager.h index c168ebc6ce..f7d7b34fb1 100644 --- a/modules/audio_coding/audio_network_adaptor/controller_manager.h +++ b/modules/audio_coding/audio_network_adaptor/controller_manager.h @@ -17,7 +17,6 @@ #include #include "modules/audio_coding/audio_network_adaptor/controller.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -80,6 +79,9 @@ class ControllerManagerImpl final : public ControllerManager { ~ControllerManagerImpl() override; + ControllerManagerImpl(const ControllerManagerImpl&) = delete; + ControllerManagerImpl& operator=(const ControllerManagerImpl&) = delete; + // Sort controllers based on their significance. std::vector GetSortedControllers( const Controller::NetworkMetrics& metrics) override; @@ -114,8 +116,6 @@ class ControllerManagerImpl final : public ControllerManager { // `scoring_points_` saves the scoring points of various // controllers. std::map controller_scoring_points_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ControllerManagerImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc index 669cf5ee53..2616706ee5 100644 --- a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc +++ b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc @@ -76,7 +76,7 @@ DebugDumpWriterImpl::DebugDumpWriterImpl(FILE* file_handle) { dump_file_ = FileWrapper(file_handle); RTC_CHECK(dump_file_.is_open()); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); #endif } diff --git a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h index 367f659542..8fdf2f7728 100644 --- a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h +++ b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h @@ -15,7 +15,6 @@ #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ignore_wundef.h" #include "rtc_base/system/file_wrapper.h" #if WEBRTC_ENABLE_PROTOBUF diff --git a/modules/audio_coding/audio_network_adaptor/dtx_controller.h b/modules/audio_coding/audio_network_adaptor/dtx_controller.h index 83fdf3ddd7..b8a8e476e4 100644 --- a/modules/audio_coding/audio_network_adaptor/dtx_controller.h +++ b/modules/audio_coding/audio_network_adaptor/dtx_controller.h @@ -14,7 +14,6 @@ #include "absl/types/optional.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -35,6 +34,9 @@ class DtxController final : public Controller { ~DtxController() override; + DtxController(const DtxController&) = delete; + DtxController& operator=(const DtxController&) = delete; + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; void MakeDecision(AudioEncoderRuntimeConfig* config) override; @@ -43,7 +45,6 @@ class DtxController final : public Controller { const Config config_; bool dtx_enabled_; absl::optional uplink_bandwidth_bps_; - RTC_DISALLOW_COPY_AND_ASSIGN(DtxController); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/event_log_writer.h b/modules/audio_coding/audio_network_adaptor/event_log_writer.h index c5e57e63e6..a147311fc7 100644 --- a/modules/audio_coding/audio_network_adaptor/event_log_writer.h +++ b/modules/audio_coding/audio_network_adaptor/event_log_writer.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_ #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { class RtcEventLog; @@ -24,6 +23,10 @@ class EventLogWriter final { float min_bitrate_change_fraction, float min_packet_loss_change_fraction); ~EventLogWriter(); + + EventLogWriter(const EventLogWriter&) = delete; + EventLogWriter& operator=(const EventLogWriter&) = delete; + void MaybeLogEncoderConfig(const AudioEncoderRuntimeConfig& config); private: @@ -34,7 +37,6 @@ class EventLogWriter final { const float min_bitrate_change_fraction_; const float min_packet_loss_change_fraction_; AudioEncoderRuntimeConfig last_logged_config_; - RTC_DISALLOW_COPY_AND_ASSIGN(EventLogWriter); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc b/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc index 936e22429a..c5e5fa76e3 100644 --- a/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc +++ b/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.cc @@ -26,7 +26,7 @@ class NullSmoothingFilter final : public SmoothingFilter { absl::optional GetAverage() override { return last_sample_; } bool SetTimeConstantMs(int time_constant_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h b/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h index 85d235ed26..0c57ad1d1e 100644 --- a/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h +++ b/modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h @@ -18,7 +18,6 @@ #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" #include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -53,6 +52,9 @@ class FecControllerPlrBased final : public Controller { ~FecControllerPlrBased() override; + FecControllerPlrBased(const FecControllerPlrBased&) = delete; + FecControllerPlrBased& operator=(const FecControllerPlrBased&) = delete; + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; void MakeDecision(AudioEncoderRuntimeConfig* config) override; @@ -65,8 +67,6 @@ class FecControllerPlrBased final : public Controller { bool fec_enabled_; absl::optional uplink_bandwidth_bps_; const std::unique_ptr packet_loss_smoother_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerPlrBased); }; } // namespace webrtc diff --git a/modules/audio_coding/audio_network_adaptor/frame_length_controller.h b/modules/audio_coding/audio_network_adaptor/frame_length_controller.h index 74a787e1c1..04693f8db7 100644 --- a/modules/audio_coding/audio_network_adaptor/frame_length_controller.h +++ b/modules/audio_coding/audio_network_adaptor/frame_length_controller.h @@ -19,7 +19,6 @@ #include "absl/types/optional.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -62,6 +61,9 @@ class FrameLengthController final : public Controller { ~FrameLengthController() override; + FrameLengthController(const FrameLengthController&) = delete; + FrameLengthController& operator=(const FrameLengthController&) = delete; + void UpdateNetworkMetrics(const NetworkMetrics& network_metrics) override; void MakeDecision(AudioEncoderRuntimeConfig* config) override; @@ -84,8 +86,6 @@ class FrameLengthController final : public Controller { // True if the previous frame length decision was an increase, otherwise // false. bool prev_decision_increase_ = false; - - RTC_DISALLOW_COPY_AND_ASSIGN(FrameLengthController); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc b/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc index 968c118c8e..82ed46aa01 100644 --- a/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc +++ b/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc @@ -104,9 +104,9 @@ TEST(AudioDecoderFactoryTest, CreateL16) { rtc::scoped_refptr adf = CreateBuiltinAudioDecoderFactory(); ASSERT_TRUE(adf); - // L16 supports any clock rate, any number of channels. + // L16 supports any clock rate and any number of channels up to 24. const int clockrates[] = {8000, 16000, 32000, 48000}; - const int num_channels[] = {1, 2, 3, 4711}; + const int num_channels[] = {1, 2, 3, 24}; for (int clockrate : clockrates) { EXPECT_FALSE(adf->MakeAudioDecoder(SdpAudioFormat("l16", clockrate, 0), absl::nullopt)); @@ -117,6 +117,34 @@ TEST(AudioDecoderFactoryTest, CreateL16) { } } +// Tests that using more channels than the maximum does not work +TEST(AudioDecoderFactoryTest, MaxNrOfChannels) { + rtc::scoped_refptr adf = + CreateBuiltinAudioDecoderFactory(); + std::vector codecs = { +#ifdef WEBRTC_CODEC_OPUS + "opus", +#endif +#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX) + "isac", +#endif +#ifdef WEBRTC_CODEC_ILBC + "ilbc", +#endif + "pcmu", + "pcma", + "l16", + "G722", + "G711", + }; + + for (auto codec : codecs) { + EXPECT_FALSE(adf->MakeAudioDecoder( + SdpAudioFormat(codec, 32000, AudioDecoder::kMaxNumberOfChannels + 1), + absl::nullopt)); + } +} + TEST(AudioDecoderFactoryTest, CreateG722) { rtc::scoped_refptr adf = CreateBuiltinAudioDecoderFactory(); diff --git a/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc b/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc index 108b1c17bf..26ae1eda8a 100644 --- a/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc +++ b/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc @@ -144,4 +144,35 @@ TEST(BuiltinAudioEncoderFactoryTest, SupportsTheExpectedFormats) { ASSERT_THAT(supported_formats, ElementsAreArray(expected_formats)); } + +// Tests that using more channels than the maximum does not work. +TEST(BuiltinAudioEncoderFactoryTest, MaxNrOfChannels) { + rtc::scoped_refptr aef = + CreateBuiltinAudioEncoderFactory(); + std::vector codecs = { +#ifdef WEBRTC_CODEC_OPUS + "opus", +#endif +#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX) + "isac", +#endif +#ifdef WEBRTC_CODEC_ILBC + "ilbc", +#endif + "pcmu", + "pcma", + "l16", + "G722", + "G711", + }; + + for (auto codec : codecs) { + EXPECT_FALSE(aef->MakeAudioEncoder( + /*payload_type=*/111, + /*format=*/ + SdpAudioFormat(codec, 32000, AudioEncoder::kMaxNumberOfChannels + 1), + /*codec_pair_id=*/absl::nullopt)); + } +} + } // namespace webrtc diff --git a/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc b/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc index a34c563d33..c688004363 100644 --- a/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc +++ b/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc @@ -14,7 +14,6 @@ #include #include "common_audio/vad/mock/mock_vad.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/numerics/safe_conversions.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" @@ -50,6 +49,9 @@ class AudioEncoderCngTest : public ::testing::Test { EXPECT_CALL(*mock_encoder_, NumChannels()).WillRepeatedly(Return(1)); } + AudioEncoderCngTest(const AudioEncoderCngTest&) = delete; + AudioEncoderCngTest& operator=(const AudioEncoderCngTest&) = delete; + void TearDown() override { EXPECT_CALL(*mock_vad_, Die()).Times(1); cng_.reset(); @@ -208,8 +210,6 @@ class AudioEncoderCngTest : public ::testing::Test { rtc::Buffer encoded_; AudioEncoder::EncodedInfo encoded_info_; int sample_rate_hz_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderCngTest); }; TEST_F(AudioEncoderCngTest, CreateAndDestroy) { diff --git a/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc b/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc index d580a0509b..46ac671b30 100644 --- a/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc +++ b/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc @@ -40,8 +40,14 @@ int AudioDecoderPcmU::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = + PacketDuration(encoded, encoded_len) * + Channels(); // 1 byte per sample per channel int16_t temp_type = 1; // Default is speech. - size_t ret = WebRtcG711_DecodeU(encoded, encoded_len, decoded, &temp_type); + size_t ret = + WebRtcG711_DecodeU(encoded, encoded_len_adjusted, decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return static_cast(ret); } @@ -75,8 +81,14 @@ int AudioDecoderPcmA::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = + PacketDuration(encoded, encoded_len) * + Channels(); // 1 byte per sample per channel int16_t temp_type = 1; // Default is speech. - size_t ret = WebRtcG711_DecodeA(encoded, encoded_len, decoded, &temp_type); + size_t ret = + WebRtcG711_DecodeA(encoded, encoded_len_adjusted, decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return static_cast(ret); } diff --git a/modules/audio_coding/codecs/g711/audio_decoder_pcm.h b/modules/audio_coding/codecs/g711/audio_decoder_pcm.h index 618591876d..3fa42cba30 100644 --- a/modules/audio_coding/codecs/g711/audio_decoder_pcm.h +++ b/modules/audio_coding/codecs/g711/audio_decoder_pcm.h @@ -19,7 +19,6 @@ #include "api/audio_codecs/audio_decoder.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -28,6 +27,10 @@ class AudioDecoderPcmU final : public AudioDecoder { explicit AudioDecoderPcmU(size_t num_channels) : num_channels_(num_channels) { RTC_DCHECK_GE(num_channels, 1); } + + AudioDecoderPcmU(const AudioDecoderPcmU&) = delete; + AudioDecoderPcmU& operator=(const AudioDecoderPcmU&) = delete; + void Reset() override; std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; @@ -44,7 +47,6 @@ class AudioDecoderPcmU final : public AudioDecoder { private: const size_t num_channels_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU); }; class AudioDecoderPcmA final : public AudioDecoder { @@ -52,6 +54,10 @@ class AudioDecoderPcmA final : public AudioDecoder { explicit AudioDecoderPcmA(size_t num_channels) : num_channels_(num_channels) { RTC_DCHECK_GE(num_channels, 1); } + + AudioDecoderPcmA(const AudioDecoderPcmA&) = delete; + AudioDecoderPcmA& operator=(const AudioDecoderPcmA&) = delete; + void Reset() override; std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; @@ -68,7 +74,6 @@ class AudioDecoderPcmA final : public AudioDecoder { private: const size_t num_channels_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/g711/audio_encoder_pcm.h b/modules/audio_coding/codecs/g711/audio_encoder_pcm.h index c4413f50a4..d50be4b457 100644 --- a/modules/audio_coding/codecs/g711/audio_encoder_pcm.h +++ b/modules/audio_coding/codecs/g711/audio_encoder_pcm.h @@ -17,7 +17,6 @@ #include "absl/types/optional.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -83,6 +82,9 @@ class AudioEncoderPcmA final : public AudioEncoderPcm { explicit AudioEncoderPcmA(const Config& config) : AudioEncoderPcm(config, kSampleRateHz) {} + AudioEncoderPcmA(const AudioEncoderPcmA&) = delete; + AudioEncoderPcmA& operator=(const AudioEncoderPcmA&) = delete; + protected: size_t EncodeCall(const int16_t* audio, size_t input_len, @@ -94,7 +96,6 @@ class AudioEncoderPcmA final : public AudioEncoderPcm { private: static const int kSampleRateHz = 8000; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderPcmA); }; class AudioEncoderPcmU final : public AudioEncoderPcm { @@ -106,6 +107,9 @@ class AudioEncoderPcmU final : public AudioEncoderPcm { explicit AudioEncoderPcmU(const Config& config) : AudioEncoderPcm(config, kSampleRateHz) {} + AudioEncoderPcmU(const AudioEncoderPcmU&) = delete; + AudioEncoderPcmU& operator=(const AudioEncoderPcmU&) = delete; + protected: size_t EncodeCall(const int16_t* audio, size_t input_len, @@ -117,7 +121,6 @@ class AudioEncoderPcmU final : public AudioEncoderPcm { private: static const int kSampleRateHz = 8000; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderPcmU); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/g722/audio_decoder_g722.cc b/modules/audio_coding/codecs/g722/audio_decoder_g722.cc index f02ca7f896..c21ab9341f 100644 --- a/modules/audio_coding/codecs/g722/audio_decoder_g722.cc +++ b/modules/audio_coding/codecs/g722/audio_decoder_g722.cc @@ -114,6 +114,12 @@ int AudioDecoderG722StereoImpl::DecodeInternal(const uint8_t* encoded, return static_cast(ret); } +int AudioDecoderG722StereoImpl::PacketDuration(const uint8_t* encoded, + size_t encoded_len) const { + // 1/2 encoded byte per sample per channel. + return static_cast(2 * encoded_len / Channels()); +} + int AudioDecoderG722StereoImpl::SampleRateHz() const { return 16000; } diff --git a/modules/audio_coding/codecs/g722/audio_decoder_g722.h b/modules/audio_coding/codecs/g722/audio_decoder_g722.h index eeca13975f..5872fad5de 100644 --- a/modules/audio_coding/codecs/g722/audio_decoder_g722.h +++ b/modules/audio_coding/codecs/g722/audio_decoder_g722.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_CODECS_G722_AUDIO_DECODER_G722_H_ #include "api/audio_codecs/audio_decoder.h" -#include "rtc_base/constructor_magic.h" typedef struct WebRtcG722DecInst G722DecInst; @@ -22,6 +21,10 @@ class AudioDecoderG722Impl final : public AudioDecoder { public: AudioDecoderG722Impl(); ~AudioDecoderG722Impl() override; + + AudioDecoderG722Impl(const AudioDecoderG722Impl&) = delete; + AudioDecoderG722Impl& operator=(const AudioDecoderG722Impl&) = delete; + bool HasDecodePlc() const override; void Reset() override; std::vector ParsePayload(rtc::Buffer&& payload, @@ -39,17 +42,22 @@ class AudioDecoderG722Impl final : public AudioDecoder { private: G722DecInst* dec_state_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722Impl); }; class AudioDecoderG722StereoImpl final : public AudioDecoder { public: AudioDecoderG722StereoImpl(); ~AudioDecoderG722StereoImpl() override; + + AudioDecoderG722StereoImpl(const AudioDecoderG722StereoImpl&) = delete; + AudioDecoderG722StereoImpl& operator=(const AudioDecoderG722StereoImpl&) = + delete; + void Reset() override; std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; int SampleRateHz() const override; + int PacketDuration(const uint8_t* encoded, size_t encoded_len) const override; size_t Channels() const override; protected: @@ -71,7 +79,6 @@ class AudioDecoderG722StereoImpl final : public AudioDecoder { G722DecInst* dec_state_left_; G722DecInst* dec_state_right_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderG722StereoImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/g722/audio_encoder_g722.h b/modules/audio_coding/codecs/g722/audio_encoder_g722.h index c836503f2b..a932aa8b7d 100644 --- a/modules/audio_coding/codecs/g722/audio_encoder_g722.h +++ b/modules/audio_coding/codecs/g722/audio_encoder_g722.h @@ -20,7 +20,6 @@ #include "api/units/time_delta.h" #include "modules/audio_coding/codecs/g722/g722_interface.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,9 @@ class AudioEncoderG722Impl final : public AudioEncoder { AudioEncoderG722Impl(const AudioEncoderG722Config& config, int payload_type); ~AudioEncoderG722Impl() override; + AudioEncoderG722Impl(const AudioEncoderG722Impl&) = delete; + AudioEncoderG722Impl& operator=(const AudioEncoderG722Impl&) = delete; + int SampleRateHz() const override; size_t NumChannels() const override; int RtpTimestampRateHz() const override; @@ -63,7 +65,6 @@ class AudioEncoderG722Impl final : public AudioEncoder { uint32_t first_timestamp_in_buffer_; const std::unique_ptr encoders_; rtc::Buffer interleave_buffer_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderG722Impl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h b/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h index c2d62ed2d1..46ba755148 100644 --- a/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h +++ b/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h @@ -18,7 +18,6 @@ #include "api/audio_codecs/audio_decoder.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" typedef struct iLBC_decinst_t_ IlbcDecoderInstance; @@ -28,6 +27,10 @@ class AudioDecoderIlbcImpl final : public AudioDecoder { public: AudioDecoderIlbcImpl(); ~AudioDecoderIlbcImpl() override; + + AudioDecoderIlbcImpl(const AudioDecoderIlbcImpl&) = delete; + AudioDecoderIlbcImpl& operator=(const AudioDecoderIlbcImpl&) = delete; + bool HasDecodePlc() const override; size_t DecodePlc(size_t num_frames, int16_t* decoded) override; void Reset() override; @@ -45,7 +48,6 @@ class AudioDecoderIlbcImpl final : public AudioDecoder { private: IlbcDecoderInstance* dec_state_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbcImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h b/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h index 05a900e3c4..c8dfa2ca6d 100644 --- a/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h +++ b/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h @@ -21,7 +21,6 @@ #include "api/audio_codecs/ilbc/audio_encoder_ilbc_config.h" #include "api/units/time_delta.h" #include "modules/audio_coding/codecs/ilbc/ilbc.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -30,6 +29,9 @@ class AudioEncoderIlbcImpl final : public AudioEncoder { AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config, int payload_type); ~AudioEncoderIlbcImpl() override; + AudioEncoderIlbcImpl(const AudioEncoderIlbcImpl&) = delete; + AudioEncoderIlbcImpl& operator=(const AudioEncoderIlbcImpl&) = delete; + int SampleRateHz() const override; size_t NumChannels() const override; size_t Num10MsFramesInNextPacket() const override; @@ -53,7 +55,6 @@ class AudioEncoderIlbcImpl final : public AudioEncoder { uint32_t first_timestamp_in_buffer_; int16_t input_buffer_[kMaxSamplesPerPacket]; IlbcEncoderInstance* encoder_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbcImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h b/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h index 23a302018f..aae708f295 100644 --- a/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h +++ b/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h @@ -16,7 +16,6 @@ #include "absl/types/optional.h" #include "api/audio_codecs/audio_decoder.h" #include "api/scoped_refptr.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -30,6 +29,9 @@ class AudioDecoderIsacT final : public AudioDecoder { explicit AudioDecoderIsacT(const Config& config); virtual ~AudioDecoderIsacT() override; + AudioDecoderIsacT(const AudioDecoderIsacT&) = delete; + AudioDecoderIsacT& operator=(const AudioDecoderIsacT&) = delete; + bool HasDecodePlc() const override; size_t DecodePlc(size_t num_frames, int16_t* decoded) override; void Reset() override; @@ -45,8 +47,6 @@ class AudioDecoderIsacT final : public AudioDecoder { private: typename T::instance_type* isac_state_; int sample_rate_hz_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacT); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h b/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h index 8bde0e34ad..c382ea076e 100644 --- a/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h +++ b/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h @@ -18,7 +18,6 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/scoped_refptr.h" #include "api/units/time_delta.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/field_trial.h" namespace webrtc { @@ -44,6 +43,9 @@ class AudioEncoderIsacT final : public AudioEncoder { explicit AudioEncoderIsacT(const Config& config); ~AudioEncoderIsacT() override; + AudioEncoderIsacT(const AudioEncoderIsacT&) = delete; + AudioEncoderIsacT& operator=(const AudioEncoderIsacT&) = delete; + int SampleRateHz() const override; size_t NumChannels() const override; size_t Num10MsFramesInNextPacket() const override; @@ -99,8 +101,6 @@ class AudioEncoderIsacT final : public AudioEncoder { // Start out with a reasonable default that we can use until we receive a real // value. DataSize overhead_per_packet_ = DataSize::Bytes(28); - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIsacT); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/isac/fix/include/isacfix.h b/modules/audio_coding/codecs/isac/fix/include/isacfix.h index 87956a6997..dcc7b0991d 100644 --- a/modules/audio_coding/codecs/isac/fix/include/isacfix.h +++ b/modules/audio_coding/codecs/isac/fix/include/isacfix.h @@ -394,7 +394,7 @@ int16_t WebRtcIsacfix_FreeInternal(ISACFIX_MainStruct* ISAC_main_inst); /**************************************************************************** * WebRtcIsacfix_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. It should always return a complete packet, i.e. only called once * even for 60 msec frames * diff --git a/modules/audio_coding/codecs/isac/fix/source/arith_routins.h b/modules/audio_coding/codecs/isac/fix/source/arith_routins.h index cc4ed555cf..d112bfe7f2 100644 --- a/modules/audio_coding/codecs/isac/fix/source/arith_routins.h +++ b/modules/audio_coding/codecs/isac/fix/source/arith_routins.h @@ -38,7 +38,7 @@ int WebRtcIsacfix_EncLogisticMulti2(Bitstr_enc* streamData, int16_t* dataQ7, const uint16_t* env, - const int16_t lenData); + int16_t lenData); /**************************************************************************** * WebRtcIsacfix_EncTerminate(...) @@ -73,7 +73,7 @@ int16_t WebRtcIsacfix_EncTerminate(Bitstr_enc* streamData); int WebRtcIsacfix_DecLogisticMulti2(int16_t* data, Bitstr_dec* streamData, const int32_t* env, - const int16_t lenData); + int16_t lenData); /**************************************************************************** * WebRtcIsacfix_EncHistMulti(...) @@ -92,7 +92,7 @@ int WebRtcIsacfix_DecLogisticMulti2(int16_t* data, int WebRtcIsacfix_EncHistMulti(Bitstr_enc* streamData, const int16_t* data, const uint16_t* const* cdf, - const int16_t lenData); + int16_t lenData); /**************************************************************************** * WebRtcIsacfix_DecHistBisectMulti(...) @@ -118,7 +118,7 @@ int16_t WebRtcIsacfix_DecHistBisectMulti(int16_t* data, Bitstr_dec* streamData, const uint16_t* const* cdf, const uint16_t* cdfSize, - const int16_t lenData); + int16_t lenData); /**************************************************************************** * WebRtcIsacfix_DecHistOneStepMulti(...) @@ -144,6 +144,6 @@ int16_t WebRtcIsacfix_DecHistOneStepMulti(int16_t* data, Bitstr_dec* streamData, const uint16_t* const* cdf, const uint16_t* initIndex, - const int16_t lenData); + int16_t lenData); #endif /* MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ARITH_ROUTINS_H_ */ diff --git a/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h b/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h index ebb74d6c49..f106746f14 100644 --- a/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h +++ b/modules/audio_coding/codecs/isac/fix/source/bandwidth_estimator.h @@ -56,17 +56,17 @@ int32_t WebRtcIsacfix_InitBandwidthEstimator(BwEstimatorstr* bwest_str); */ int32_t WebRtcIsacfix_UpdateUplinkBwImpl(BwEstimatorstr* bwest_str, - const uint16_t rtp_number, - const int16_t frameSize, - const uint32_t send_ts, - const uint32_t arr_ts, - const size_t pksize, - const uint16_t Index); + uint16_t rtp_number, + int16_t frameSize, + uint32_t send_ts, + uint32_t arr_ts, + size_t pksize, + uint16_t Index); /* Update receiving estimates. Used when we only receive BWE index, no iSAC data * packet. */ int16_t WebRtcIsacfix_UpdateUplinkBwRec(BwEstimatorstr* bwest_str, - const int16_t Index); + int16_t Index); /**************************************************************************** * WebRtcIsacfix_GetDownlinkBwIndexImpl(...) @@ -100,19 +100,19 @@ int16_t WebRtcIsacfix_GetUplinkMaxDelay(const BwEstimatorstr* bwest_str); */ uint16_t WebRtcIsacfix_GetMinBytes( RateModel* State, - int16_t StreamSize, /* bytes in bitstream */ - const int16_t FrameLen, /* ms per frame */ - const int16_t BottleNeck, /* bottle neck rate; excl headers (bps) */ - const int16_t DelayBuildUp); /* max delay from bottle neck buffering (ms) */ + int16_t StreamSize, /* bytes in bitstream */ + int16_t FrameLen, /* ms per frame */ + int16_t BottleNeck, /* bottle neck rate; excl headers (bps) */ + int16_t DelayBuildUp); /* max delay from bottle neck buffering (ms) */ /* * update long-term average bitrate and amount of data in buffer */ void WebRtcIsacfix_UpdateRateModel( RateModel* State, - int16_t StreamSize, /* bytes in bitstream */ - const int16_t FrameSamples, /* samples per frame */ - const int16_t BottleNeck); /* bottle neck rate; excl headers (bps) */ + int16_t StreamSize, /* bytes in bitstream */ + int16_t FrameSamples, /* samples per frame */ + int16_t BottleNeck); /* bottle neck rate; excl headers (bps) */ void WebRtcIsacfix_InitRateModel(RateModel* State); diff --git a/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h b/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h index b4251cee1e..ae11394f7c 100644 --- a/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h +++ b/modules/audio_coding/codecs/isac/fix/source/entropy_coding.h @@ -101,19 +101,19 @@ void WebRtcIsacfix_TranscodeLpcCoef(int32_t* tmpcoeffs_gQ6, int16_t* index_gQQ); typedef void (*MatrixProduct1)(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix1_index_factor1, - const int matrix0_index_factor1, - const int matrix1_index_init_case, - const int matrix1_index_step, - const int matrix0_index_step, - const int inner_loop_count, - const int mid_loop_count, - const int shift); + int matrix1_index_factor1, + int matrix0_index_factor1, + int matrix1_index_init_case, + int matrix1_index_step, + int matrix0_index_step, + int inner_loop_count, + int mid_loop_count, + int shift); typedef void (*MatrixProduct2)(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix0_index_factor, - const int matrix0_index_step); + int matrix0_index_factor, + int matrix0_index_step); extern MatrixProduct1 WebRtcIsacfix_MatrixProduct1; extern MatrixProduct2 WebRtcIsacfix_MatrixProduct2; @@ -121,57 +121,57 @@ extern MatrixProduct2 WebRtcIsacfix_MatrixProduct2; void WebRtcIsacfix_MatrixProduct1C(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix1_index_factor1, - const int matrix0_index_factor1, - const int matrix1_index_init_case, - const int matrix1_index_step, - const int matrix0_index_step, - const int inner_loop_count, - const int mid_loop_count, - const int shift); + int matrix1_index_factor1, + int matrix0_index_factor1, + int matrix1_index_init_case, + int matrix1_index_step, + int matrix0_index_step, + int inner_loop_count, + int mid_loop_count, + int shift); void WebRtcIsacfix_MatrixProduct2C(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix0_index_factor, - const int matrix0_index_step); + int matrix0_index_factor, + int matrix0_index_step); #if defined(WEBRTC_HAS_NEON) void WebRtcIsacfix_MatrixProduct1Neon(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix1_index_factor1, - const int matrix0_index_factor1, - const int matrix1_index_init_case, - const int matrix1_index_step, - const int matrix0_index_step, - const int inner_loop_count, - const int mid_loop_count, - const int shift); + int matrix1_index_factor1, + int matrix0_index_factor1, + int matrix1_index_init_case, + int matrix1_index_step, + int matrix0_index_step, + int inner_loop_count, + int mid_loop_count, + int shift); void WebRtcIsacfix_MatrixProduct2Neon(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix0_index_factor, - const int matrix0_index_step); + int matrix0_index_factor, + int matrix0_index_step); #endif #if defined(MIPS32_LE) void WebRtcIsacfix_MatrixProduct1MIPS(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix1_index_factor1, - const int matrix0_index_factor1, - const int matrix1_index_init_case, - const int matrix1_index_step, - const int matrix0_index_step, - const int inner_loop_count, - const int mid_loop_count, - const int shift); + int matrix1_index_factor1, + int matrix0_index_factor1, + int matrix1_index_init_case, + int matrix1_index_step, + int matrix0_index_step, + int inner_loop_count, + int mid_loop_count, + int shift); void WebRtcIsacfix_MatrixProduct2MIPS(const int16_t matrix0[], const int32_t matrix1[], int32_t matrix_product[], - const int matrix0_index_factor, - const int matrix0_index_step); + int matrix0_index_factor, + int matrix0_index_step); #endif #endif // MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ENTROPY_CODING_H_ diff --git a/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h b/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h index 6b99914b64..f741e6f677 100644 --- a/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h +++ b/modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h @@ -46,7 +46,7 @@ typedef void (*AllpassFilter2FixDec16)( int16_t* data_ch2, // Input and output in channel 2, in Q0 const int16_t* factor_ch1, // Scaling factor for channel 1, in Q15 const int16_t* factor_ch2, // Scaling factor for channel 2, in Q15 - const int length, // Length of the data buffers + int length, // Length of the data buffers int32_t* filter_state_ch1, // Filter state for channel 1, in Q16 int32_t* filter_state_ch2); // Filter state for channel 2, in Q16 extern AllpassFilter2FixDec16 WebRtcIsacfix_AllpassFilter2FixDec16; @@ -55,7 +55,7 @@ void WebRtcIsacfix_AllpassFilter2FixDec16C(int16_t* data_ch1, int16_t* data_ch2, const int16_t* factor_ch1, const int16_t* factor_ch2, - const int length, + int length, int32_t* filter_state_ch1, int32_t* filter_state_ch2); @@ -64,7 +64,7 @@ void WebRtcIsacfix_AllpassFilter2FixDec16Neon(int16_t* data_ch1, int16_t* data_ch2, const int16_t* factor_ch1, const int16_t* factor_ch2, - const int length, + int length, int32_t* filter_state_ch1, int32_t* filter_state_ch2); #endif @@ -74,7 +74,7 @@ void WebRtcIsacfix_AllpassFilter2FixDec16MIPS(int16_t* data_ch1, int16_t* data_ch2, const int16_t* factor_ch1, const int16_t* factor_ch2, - const int length, + int length, int32_t* filter_state_ch1, int32_t* filter_state_ch2); #endif diff --git a/modules/audio_coding/codecs/isac/fix/source/isacfix.c b/modules/audio_coding/codecs/isac/fix/source/isacfix.c index 9a66591de1..a7d44e883d 100644 --- a/modules/audio_coding/codecs/isac/fix/source/isacfix.c +++ b/modules/audio_coding/codecs/isac/fix/source/isacfix.c @@ -381,7 +381,7 @@ int WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, /**************************************************************************** * WebRtcIsacfix_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. It should always return a complete packet, i.e. only called once * even for 60 msec frames * diff --git a/modules/audio_coding/codecs/isac/fix/test/kenny.cc b/modules/audio_coding/codecs/isac/fix/test/kenny.cc deleted file mode 100644 index 4b431cfdf9..0000000000 --- a/modules/audio_coding/codecs/isac/fix/test/kenny.cc +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include -#include - -#include "modules/audio_coding/codecs/isac/fix/include/isacfix.h" -#include "test/gtest.h" -#include "test/testsupport/perf_test.h" - -// TODO(kma): Clean up the code and change benchmarking the whole codec to -// separate encoder and decoder. - -/* Defines */ -#define SEED_FILE \ - "randseed.txt" /* Used when running decoder on garbage data \ - */ -#define MAX_FRAMESAMPLES \ - 960 /* max number of samples per frame (= 60 ms frame) */ -#define FRAMESAMPLES_10ms 160 /* number of samples per 10ms frame */ -#define FS 16000 /* sampling frequency (Hz) */ - -/* Function for reading audio data from PCM file */ -int readframe(int16_t* data, FILE* inp, int length) { - short k, rlen, status = 0; - - rlen = fread(data, sizeof(int16_t), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - return status; -} - -// Globals needed because gtest does not provide access to argv. -// This should be reworked to use flags. -static int global_argc; -static char** global_argv; - -/* Struct for bottleneck model */ -typedef struct { - uint32_t send_time; /* samples */ - uint32_t arrival_time; /* samples */ - uint32_t sample_count; /* samples */ - uint16_t rtp_number; -} BottleNeckModel; - -void get_arrival_time(int current_framesamples, /* samples */ - size_t packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel* BN_data) { - const int HeaderSize = 35; - int HeaderRate; - - HeaderRate = HeaderSize * 8 * FS / current_framesamples; /* bits/s */ - - /* everything in samples */ - BN_data->sample_count = BN_data->sample_count + current_framesamples; - - BN_data->arrival_time += static_cast( - ((packet_size + HeaderSize) * 8 * FS) / (bottleneck + HeaderRate)); - BN_data->send_time += current_framesamples; - - if (BN_data->arrival_time < BN_data->sample_count) - BN_data->arrival_time = BN_data->sample_count; - - BN_data->rtp_number++; -} - -void get_arrival_time2(int current_framesamples, - int current_delay, - BottleNeckModel* BN_data) { - if (current_delay == -1) - // dropped packet - { - BN_data->arrival_time += current_framesamples; - } else if (current_delay != -2) { - // - BN_data->arrival_time += - (current_framesamples + ((FS / 1000) * current_delay)); - } - // else - // current packet has same timestamp as previous packet - - BN_data->rtp_number++; -} - -TEST(IsacFixTest, Kenny) { - int argc = global_argc; - char** argv = global_argv; - - char inname[100], outname[100], outbitsname[100], bottleneck_file[100]; - FILE *inp, *outp, *f_bn, *outbits; - int endfile; - - const char* perf_result_file = NULL; - - int i; - int errtype, h = 0, k, packetLossPercent = 0; - int16_t CodingMode; - int16_t bottleneck; - int framesize = 30; /* ms */ - int cur_framesmpls, err = 0, lostPackets = 0; - - /* Runtime statistics */ - double starttime, runtime, length_file; - - int stream_len_int = 0; - size_t stream_len = 0; - int16_t framecnt; - int declen = 0; - int16_t shortdata[FRAMESAMPLES_10ms]; - int16_t decoded[MAX_FRAMESAMPLES]; - uint16_t streamdata[500]; - int16_t speechType[1]; - size_t prevFrameSize = 1; - int16_t rateBPS = 0; - int16_t fixedFL = 0; - int16_t payloadSize = 0; - int32_t payloadRate = 0; - int setControlBWE = 0; - int readLoss; - FILE* plFile = NULL; - - char version_number[20]; - char tmpBit[5] = ".bit"; - - int totalbits = 0; - int totalsmpls = 0; - int16_t testNum, testCE; - - FILE* fp_gns = NULL; - int gns = 0; - int cur_delay = 0; - char gns_file[100]; - - int nbTest = 0; - int16_t lostFrame; - float scale = (float)0.7; - /* only one structure used for ISAC encoder */ - ISACFIX_MainStruct* ISAC_main_inst = NULL; - - /* For fault test 10, garbage data */ - FILE* seedfile; - unsigned int random_seed = (unsigned int)time(NULL); // 1196764538 - - BottleNeckModel BN_data; - f_bn = NULL; - - readLoss = 0; - packetLossPercent = 0; - - /* Handling wrong input arguments in the command line */ - if ((argc < 3) || (argc > 22)) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - WebRtcIsacfix_version(version_number); - printf("iSAC version %s \n\n", version_number); - - printf("Usage:\n\n"); - printf("%s [-I] bottleneck_value infile outfile [-F num]\n\n", argv[0]); - printf("with:\n"); - printf("[-I] :if -I option is specified, the coder will use\n"); - printf(" an instantaneous Bottleneck value. If not, it\n"); - printf(" will be an adaptive Bottleneck value.\n\n"); - printf("bottleneck_value :the value of the bottleneck provided either\n"); - printf(" as a fixed value (e.g. 25000) or\n"); - printf(" read from a file (e.g. bottleneck.txt)\n\n"); - printf("infile :Normal speech input file\n\n"); - printf("outfile :Speech output file\n\n"); - printf( - "[-INITRATE num] :Set a new value for initial rate. Note! Only used" - " in adaptive mode.\n\n"); - printf( - "[-FL num] :Set (initial) frame length in msec. Valid length" - " are 30 and 60 msec.\n\n"); - printf("[-FIXED_FL] :Frame length to be fixed to initial value.\n\n"); - printf( - "[-MAX num] :Set the limit for the payload size of iSAC" - " in bytes. \n"); - printf(" Minimum 100, maximum 400.\n\n"); - printf("[-MAXRATE num] :Set the maxrate for iSAC in bits per second. \n"); - printf(" Minimum 32000, maximum 53400.\n\n"); - printf("[-F num] :if -F option is specified, the test function\n"); - printf( - " will run the iSAC API fault scenario specified" - " by the\n"); - printf(" supplied number.\n"); - printf(" F 1 - Call encoder prior to init encoder call\n"); - printf(" F 2 - Call decoder prior to init decoder call\n"); - printf(" F 3 - Call decoder prior to encoder call\n"); - printf( - " F 4 - Call decoder with a too short coded" - " sequence\n"); - printf( - " F 5 - Call decoder with a too long coded" - " sequence\n"); - printf(" F 6 - Call decoder with random bit stream\n"); - printf( - " F 7 - Call init encoder/decoder at random" - " during a call\n"); - printf( - " F 8 - Call encoder/decoder without having" - " allocated memory for \n"); - printf(" encoder/decoder instance\n"); - printf(" F 9 - Call decodeB without calling decodeA\n"); - printf(" F 10 - Call decodeB with garbage data\n"); - printf( - "[-PL num] :if -PL option is specified 0 encode with narrowband encoder" - " (infile is narrowband)\n"); - printf( - " num=2 => decode with narrowband decoder" - " (outfile is narrowband)\n\n"); - printf("[-CE num] :Test of APIs used by Conference Engine.\n"); - printf( - " CE 1 - createInternal, freeInternal," - " getNewBitstream \n"); - printf(" CE 2 - transcode, getBWE \n"); - printf(" CE 3 - getSendBWE, setSendBWE. \n\n"); - printf( - "[-RTP_INIT num] :if -RTP_INIT option is specified num will be" - " the initial\n"); - printf(" value of the rtp sequence number.\n\n"); - printf("[--isolated_script_test_perf_output=file]\n"); - printf( - " :If this option is specified, perf values will be" - " written to this file in a JSON format.\n\n"); - printf("Example usage :\n\n"); - printf("%s -I bottleneck.txt speechIn.pcm speechOut.pcm\n\n", argv[0]); - exit(1); - } - - /* Print version number */ - WebRtcIsacfix_version(version_number); - printf("iSAC version %s \n\n", version_number); - - /* Loop over all command line arguments */ - CodingMode = 0; - testNum = 0; - testCE = 0; - i = 1; - - /* Instantaneous mode */ - if (!strcmp("-I", argv[i])) { - printf("\nInstantaneous BottleNeck\n"); - CodingMode = 1; - i++; - } - - /* Bottleneck value is processed after the for */ - i++; - - /* Get Input and Output files */ - sscanf(argv[i++], "%s", inname); - sscanf(argv[i++], "%s", outname); - - for (; i < argc; i++) { - /* Set (initial) bottleneck value */ - if (!strcmp("-INITRATE", argv[i])) { - if (i + 1 >= argc) { - printf("-INITRATE requires a parameter.\n"); - exit(1); - } - rateBPS = atoi(argv[i + 1]); - setControlBWE = 1; - if ((rateBPS < 10000) || (rateBPS > 32000)) { - printf( - "\n%d is not a initial rate. " - "Valid values are in the range 10000 to 32000.\n", - rateBPS); - exit(1); - } - printf("\nNew initial rate: %d\n", rateBPS); - i++; - } - - /* Set (initial) framelength */ - if (!strcmp("-FL", argv[i])) { - if (i + 1 >= argc) { - printf("-FL requires a parameter.\n"); - exit(1); - } - framesize = atoi(argv[i + 1]); - if ((framesize != 30) && (framesize != 60)) { - printf( - "\n%d is not a valid frame length. " - "Valid length are 30 and 60 msec.\n", - framesize); - exit(1); - } - printf("\nFrame Length: %d\n", framesize); - i++; - } - - /* Fixed frame length */ - if (!strcmp("-FIXED_FL", argv[i])) { - fixedFL = 1; - setControlBWE = 1; - } - - /* Set maximum allowed payload size in bytes */ - if (!strcmp("-MAX", argv[i])) { - if (i + 1 >= argc) { - printf("-MAX requires a parameter.\n"); - exit(1); - } - payloadSize = atoi(argv[i + 1]); - printf("Maximum Payload Size: %d\n", payloadSize); - i++; - } - - /* Set maximum rate in bytes */ - if (!strcmp("-MAXRATE", argv[i])) { - if (i + 1 >= argc) { - printf("-MAXRATE requires a parameter.\n"); - exit(1); - } - payloadRate = atoi(argv[i + 1]); - printf("Maximum Rate in kbps: %d\n", payloadRate); - i++; - } - - /* Test of fault scenarious */ - if (!strcmp("-F", argv[i])) { - if (i + 1 >= argc) { - printf("-F requires a parameter."); - exit(1); - } - testNum = atoi(argv[i + 1]); - printf("\nFault test: %d\n", testNum); - if (testNum < 1 || testNum > 10) { - printf( - "\n%d is not a valid Fault Scenario number." - " Valid Fault Scenarios are numbered 1-10.\n", - testNum); - exit(1); - } - i++; - } - - /* Packet loss test */ - if (!strcmp("-PL", argv[i])) { - if (i + 1 >= argc) { - printf("-PL requires a parameter.\n"); - exit(1); - } - if (isdigit(*argv[i + 1])) { - packetLossPercent = atoi(argv[i + 1]); - if ((packetLossPercent < 0) | (packetLossPercent > 100)) { - printf("\nInvalid packet loss perentage \n"); - exit(1); - } - if (packetLossPercent > 0) { - printf("\nSimulating %d %% of independent packet loss\n", - packetLossPercent); - } else { - printf("\nNo Packet Loss Is Simulated \n"); - } - readLoss = 0; - } else { - readLoss = 1; - plFile = fopen(argv[i + 1], "rb"); - if (plFile == NULL) { - FAIL() << "Couldn't open the frameloss file: " << argv[i + 1]; - } - printf( - "\nSimulating packet loss through the given " - "channel file: %s\n", - argv[i + 1]); - } - i++; - } - - /* Random packetlosses */ - if (!strcmp("-rnd", argv[i])) { - srand(time(NULL)); - printf("\n Random pattern in lossed packets \n"); - } - - /* Use gns file */ - if (!strcmp("-G", argv[i])) { - if (i + 1 >= argc) { - printf("-G requires a parameter.\n"); - exit(1); - } - sscanf(argv[i + 1], "%s", gns_file); - fp_gns = fopen(gns_file, "rb"); - if (fp_gns == NULL) { - FAIL() << "Cannot read file " << gns_file << "."; - } - gns = 1; - i++; - } - - /* Run Narrowband interfaces (either encoder or decoder) */ - if (!strcmp("-NB", argv[i])) { - if (i + 1 >= argc) { - printf("-NB requires a parameter.\n"); - exit(1); - } - nbTest = atoi(argv[i + 1]); - i++; - } - - /* Run Conference Engine APIs */ - if (!strcmp("-CE", argv[i])) { - if (i + 1 >= argc) { - printf("-CE requires a parameter.\n"); - exit(1); - } - testCE = atoi(argv[i + 1]); - if (testCE == 1 || testCE == 2) { - i++; - scale = (float)atof(argv[i + 1]); - } else if (testCE < 1 || testCE > 3) { - printf( - "\n%d is not a valid CE-test number, valid Fault " - "Scenarios are numbered 1-3\n", - testCE); - exit(1); - } - i++; - } - - /* Set initial RTP number */ - if (!strcmp("-RTP_INIT", argv[i])) { - if (i + 1 >= argc) { - printf("-RTP_INIT requires a parameter.\n"); - exit(1); - } - i++; - } - - if (strstr(argv[i], "--isolated_script_test_perf_output") == argv[i]) { - const char* filename_start = strstr(argv[i], "="); - if (!filename_start || strlen(filename_start) < 2) { - printf("Expected --isolated_script_test_perf_output=/some/filename\n"); - exit(1); - } - perf_result_file = filename_start + 1; - } - } - - /* Get Bottleneck value */ - /* Gns files and bottleneck should not and can not be used simultaneously */ - bottleneck = atoi(argv[CodingMode + 1]); - if (bottleneck == 0 && gns == 0) { - sscanf(argv[CodingMode + 1], "%s", bottleneck_file); - f_bn = fopen(bottleneck_file, "rb"); - if (f_bn == NULL) { - printf("No value provided for BottleNeck\n"); - FAIL() << "Cannot read file " << bottleneck_file; - } else { - int aux_var; - printf("reading bottleneck rates from file %s\n\n", bottleneck_file); - if (fscanf(f_bn, "%d", &aux_var) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - if (fscanf(f_bn, "%d", &aux_var) == EOF) { - FAIL(); - } - } - bottleneck = (int16_t)aux_var; - /* Bottleneck is a cosine function - * Matlab code for writing the bottleneck file: - * BottleNeck_10ms = 20e3 + 10e3 * cos((0:5999)/5999*2*pi); - * fid = fopen('bottleneck.txt', 'wb'); - * fprintf(fid, '%d\n', BottleNeck_10ms); fclose(fid); - */ - } - } else { - f_bn = NULL; - printf("\nfixed bottleneck rate of %d bits/s\n\n", bottleneck); - } - - if (CodingMode == 0) { - printf("\nAdaptive BottleNeck\n"); - } - - /* Add '.bit' to output bitstream file */ - while ((int)outname[h] != 0) { - outbitsname[h] = outname[h]; - h++; - } - for (k = 0; k < 5; k++) { - outbitsname[h] = tmpBit[k]; - h++; - } - if ((inp = fopen(inname, "rb")) == NULL) { - FAIL() << " iSAC: Cannot read file " << inname; - } - if ((outp = fopen(outname, "wb")) == NULL) { - FAIL() << " iSAC: Cannot write file " << outname; - } - - if ((outbits = fopen(outbitsname, "wb")) == NULL) { - FAIL() << " iSAC: Cannot write file " << outbitsname; - } - printf("\nInput:%s\nOutput:%s\n\n", inname, outname); - - /* Error test number 10, garbage data */ - if (testNum == 10) { - /* Test to run decoder with garbage data */ - srand(random_seed); - - if ((seedfile = fopen(SEED_FILE, "a+t")) == NULL) { - printf("Error: Could not open file %s\n", SEED_FILE); - } else { - fprintf(seedfile, "%u\n", random_seed); - fclose(seedfile); - } - } - - /* Runtime statistics */ - starttime = clock() / (double)CLOCKS_PER_SEC; - - /* Initialize the ISAC and BN structs */ - if (testNum != 8) { - err = WebRtcIsacfix_Create(&ISAC_main_inst); - - /* Error check */ - if (err < 0) { - printf("\n\n Error in create.\n\n"); - } - if (testCE == 1) { - err = WebRtcIsacfix_CreateInternal(ISAC_main_inst); - /* Error check */ - if (err < 0) { - printf("\n\n Error in createInternal.\n\n"); - } - } - } - - /* Init of bandwidth data */ - BN_data.send_time = 0; - BN_data.arrival_time = 0; - BN_data.sample_count = 0; - BN_data.rtp_number = 0; - - /* Initialize encoder and decoder */ - framecnt = 0; - endfile = 0; - if (testNum != 1) { - WebRtcIsacfix_EncoderInit(ISAC_main_inst, CodingMode); - } - if (testNum != 2) { - WebRtcIsacfix_DecoderInit(ISAC_main_inst); - } - - if (CodingMode == 1) { - err = WebRtcIsacfix_Control(ISAC_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in control: %d.\n\n", errtype); - } - } else if (setControlBWE == 1) { - err = WebRtcIsacfix_ControlBwe(ISAC_main_inst, rateBPS, framesize, fixedFL); - } - - if (payloadSize != 0) { - err = WebRtcIsacfix_SetMaxPayloadSize(ISAC_main_inst, payloadSize); - if (err < 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - FAIL() << "Error in SetMaxPayloadSize: " << errtype; - } - } - if (payloadRate != 0) { - err = WebRtcIsacfix_SetMaxRate(ISAC_main_inst, payloadRate); - if (err < 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - FAIL() << "Error in SetMaxRateInBytes: " << errtype; - } - } - - *speechType = 1; - - while (endfile == 0) { - if (testNum == 7 && (rand() % 2 == 0)) { - err = WebRtcIsacfix_EncoderInit(ISAC_main_inst, CodingMode); - /* Error check */ - if (err < 0) { - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in encoderinit: %d.\n\n", errtype); - } - - WebRtcIsacfix_DecoderInit(ISAC_main_inst); - } - - cur_framesmpls = 0; - while (1) { - /* Read 10 ms speech block */ - if (nbTest != 1) { - endfile = readframe(shortdata, inp, FRAMESAMPLES_10ms); - } else { - endfile = readframe(shortdata, inp, (FRAMESAMPLES_10ms / 2)); - } - - if (testNum == 7) { - srand(time(NULL)); - } - - /* iSAC encoding */ - if (!(testNum == 3 && framecnt == 0)) { - if (nbTest != 1) { - short bwe; - - /* Encode */ - stream_len_int = WebRtcIsacfix_Encode(ISAC_main_inst, shortdata, - (uint8_t*)streamdata); - - /* If packet is ready, and CE testing, call the different API - functions from the internal API. */ - if (stream_len_int > 0) { - if (testCE == 1) { - err = WebRtcIsacfix_ReadBwIndex( - reinterpret_cast(streamdata), - static_cast(stream_len_int), &bwe); - stream_len_int = WebRtcIsacfix_GetNewBitStream( - ISAC_main_inst, bwe, scale, - reinterpret_cast(streamdata)); - } else if (testCE == 2) { - /* transcode function not supported */ - } else if (testCE == 3) { - /* Only for Function testing. The functions should normally - not be used in this way */ - - err = WebRtcIsacfix_GetDownLinkBwIndex(ISAC_main_inst, &bwe); - /* Error Check */ - if (err < 0) { - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in getSendBWE: %d.\n", errtype); - } - - err = WebRtcIsacfix_UpdateUplinkBw(ISAC_main_inst, bwe); - /* Error Check */ - if (err < 0) { - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in setBWE: %d.\n", errtype); - } - } - } - } else { - stream_len_int = -1; - } - } else { - break; - } - - if (stream_len_int < 0 || err < 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in encoder: %d.\n", errtype); - } else { - stream_len = static_cast(stream_len_int); - if (fwrite(streamdata, sizeof(char), stream_len, outbits) != - stream_len) { - FAIL(); - } - } - - cur_framesmpls += FRAMESAMPLES_10ms; - - /* read next bottleneck rate */ - if (f_bn != NULL) { - int aux_var; - if (fscanf(f_bn, "%d", &aux_var) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - if (fscanf(f_bn, "%d", &aux_var) == EOF) { - FAIL(); - } - } - bottleneck = (int16_t)aux_var; - if (CodingMode == 1) { - WebRtcIsacfix_Control(ISAC_main_inst, bottleneck, framesize); - } - } - - /* exit encoder loop if the encoder returned a bitstream */ - if (stream_len != 0) - break; - } - - /* make coded sequence to short be inreasing */ - /* the length the decoder expects */ - if (testNum == 4) { - stream_len += 10; - } - - /* make coded sequence to long be decreasing */ - /* the length the decoder expects */ - if (testNum == 5) { - stream_len -= 10; - } - - if (testNum == 6) { - srand(time(NULL)); - for (i = 0; i < static_cast(stream_len); i++) { - streamdata[i] = rand(); - } - } - - /* set pointer to beginning of file */ - if (fp_gns != NULL) { - if (fscanf(fp_gns, "%d", &cur_delay) == EOF) { - fseek(fp_gns, 0L, SEEK_SET); - if (fscanf(fp_gns, "%d", &cur_delay) == EOF) { - FAIL(); - } - } - } - - /* simulate packet handling through NetEq and the modem */ - if (!(testNum == 3 && framecnt == 0)) { - if (gns == 0) { - get_arrival_time(cur_framesmpls, stream_len, bottleneck, &BN_data); - } else { - get_arrival_time2(cur_framesmpls, cur_delay, &BN_data); - } - } - - /* packet not dropped */ - if (cur_delay != -1) { - /* Error test number 10, garbage data */ - if (testNum == 10) { - for (i = 0; i < static_cast(stream_len); i++) { - streamdata[i] = (short)(streamdata[i] + (short)rand()); - } - } - - if (testNum != 9) { - err = WebRtcIsacfix_UpdateBwEstimate( - ISAC_main_inst, reinterpret_cast(streamdata), - stream_len, BN_data.rtp_number, BN_data.send_time, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in decoder: %d.\n", errtype); - } - } - - if (readLoss == 1) { - if (fread(&lostFrame, sizeof(int16_t), 1, plFile) != 1) { - rewind(plFile); - } - lostFrame = !lostFrame; - } else { - lostFrame = (rand() % 100 < packetLossPercent); - } - - /* iSAC decoding */ - if (lostFrame && framecnt > 0) { - if (nbTest != 2) { - declen = static_cast( - WebRtcIsacfix_DecodePlc(ISAC_main_inst, decoded, prevFrameSize)); - } else { - declen = -1; - } - lostPackets++; - } else { - if (nbTest != 2) { - size_t FL; - /* Call getFramelen, only used here for function test */ - err = WebRtcIsacfix_ReadFrameLen( - reinterpret_cast(streamdata), stream_len, &FL); - declen = WebRtcIsacfix_Decode( - ISAC_main_inst, reinterpret_cast(streamdata), - stream_len, decoded, speechType); - /* Error check */ - if (err < 0 || declen < 0 || FL != static_cast(declen)) { - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf( - "\nError %d in ReadFrameLen (%s), Decode (%s), with FL %zu and " - "declen %d.\n", - errtype, err < 0 ? "yes" : "no", declen < 0 ? "yes" : "no", FL, - declen); - } - prevFrameSize = static_cast(declen / 480); - - } else { - declen = -1; - prevFrameSize = static_cast(declen / 240); - } - } - - if (declen <= 0) { - /* exit if returned with error */ - errtype = WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in decoder: %d.\n", errtype); - } - - /* Write decoded speech frame to file */ - if (fwrite(decoded, sizeof(int16_t), declen, outp) != (size_t)declen) { - FAIL(); - } - // fprintf( ratefile, "%f \n", stream_len / ( ((double)declen)/ - // ((double)FS) ) * 8 ); - } else { - lostPackets++; - } - framecnt++; - - totalsmpls += declen; - totalbits += static_cast(8 * stream_len); - - /* Error test number 10, garbage data */ - if (testNum == 10) { - if ((seedfile = fopen(SEED_FILE, "a+t")) == NULL) { - printf("Error: Could not open file %s\n", SEED_FILE); - } else { - fprintf(seedfile, "ok\n\n"); - fclose(seedfile); - } - } - } - printf("\nLost Frames %d ~ %4.1f%%\n", lostPackets, - (double)lostPackets / (double)framecnt * 100.0); - printf("\n\ntotal bits = %d bits", totalbits); - printf("\nmeasured average bitrate = %0.3f kbits/s", - (double)totalbits * (FS / 1000) / totalsmpls); - printf("\n"); - - /* Runtime statistics */ - - runtime = (double)(((double)clock() / (double)CLOCKS_PER_SEC) - starttime); - length_file = ((double)framecnt * (double)declen / FS); - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run iSAC: %.2f s (%.2f %% of realtime)\n\n", runtime, - (100 * runtime / length_file)); - printf("\n\n_______________________________________________\n"); - - // Record the results with Perf test tools. - webrtc::test::PrintResult("isac", "", "time_per_10ms_frame", - (runtime * 10) / length_file, "ms", false); - - if (perf_result_file) { - EXPECT_TRUE(webrtc::test::WritePerfResults(perf_result_file)); - } - - fclose(inp); - fclose(outp); - fclose(outbits); - - if (testCE == 1) { - WebRtcIsacfix_FreeInternal(ISAC_main_inst); - } - WebRtcIsacfix_Free(ISAC_main_inst); -} - -int main(int argc, char* argv[]) { - ::testing::InitGoogleTest(&argc, argv); - global_argc = argc; - global_argv = argv; - - return RUN_ALL_TESTS(); -} diff --git a/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc index be8d0c6466..cafca75e46 100644 --- a/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc +++ b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc @@ -51,8 +51,8 @@ std::unique_ptr GetPcmTestFileReader(int sample_rate_hz) { filename = test::ResourcePath("audio_coding/testfile32kHz", "pcm"); break; default: - RTC_NOTREACHED() << "No test file available for " << sample_rate_hz - << " Hz."; + RTC_DCHECK_NOTREACHED() + << "No test file available for " << sample_rate_hz << " Hz."; } auto pcm_file = std::make_unique(); pcm_file->ReadStereo(false); diff --git a/modules/audio_coding/codecs/isac/main/include/isac.h b/modules/audio_coding/codecs/isac/main/include/isac.h index f45bbb3897..3b05a8bcda 100644 --- a/modules/audio_coding/codecs/isac/main/include/isac.h +++ b/modules/audio_coding/codecs/isac/main/include/isac.h @@ -453,7 +453,7 @@ int16_t WebRtcIsac_SetEncSampRate(ISACStruct* ISAC_main_inst, /****************************************************************************** * WebRtcIsac_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. If the rate is set to a value less than bottleneck of codec * the new bistream will be re-encoded with the given target rate. * It should always return a complete packet, i.e. only called once diff --git a/modules/audio_coding/codecs/isac/main/source/arith_routines.h b/modules/audio_coding/codecs/isac/main/source/arith_routines.h index 6e7ea1da5e..3f9f6de7bb 100644 --- a/modules/audio_coding/codecs/isac/main/source/arith_routines.h +++ b/modules/audio_coding/codecs/isac/main/source/arith_routines.h @@ -24,9 +24,9 @@ int WebRtcIsac_EncLogisticMulti2( Bitstr* streamdata, /* in-/output struct containing bitstream */ int16_t* dataQ7, /* input: data vector */ const uint16_t* - env, /* input: side info vector defining the width of the pdf */ - const int N, /* input: data vector length */ - const int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ + env, /* input: side info vector defining the width of the pdf */ + int N, /* input: data vector length */ + int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ /* returns the number of bytes in the stream */ int WebRtcIsac_EncTerminate( @@ -38,15 +38,15 @@ int WebRtcIsac_DecLogisticMulti2( Bitstr* streamdata, /* in-/output struct containing bitstream */ const uint16_t* env, /* input: side info vector defining the width of the pdf */ - const int16_t* dither, /* input: dither vector */ - const int N, /* input: data vector length */ - const int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ + const int16_t* dither, /* input: dither vector */ + int N, /* input: data vector length */ + int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ void WebRtcIsac_EncHistMulti( Bitstr* streamdata, /* in-/output struct containing bitstream */ const int* data, /* input: data vector */ const uint16_t* const* cdf, /* input: array of cdf arrays */ - const int N); /* input: data vector length */ + int N); /* input: data vector length */ int WebRtcIsac_DecHistBisectMulti( int* data, /* output: data vector */ @@ -54,7 +54,7 @@ int WebRtcIsac_DecHistBisectMulti( const uint16_t* const* cdf, /* input: array of cdf arrays */ const uint16_t* cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ - const int N); /* input: data vector length */ + int N); /* input: data vector length */ int WebRtcIsac_DecHistOneStepMulti( int* data, /* output: data vector */ @@ -62,6 +62,6 @@ int WebRtcIsac_DecHistOneStepMulti( const uint16_t* const* cdf, /* input: array of cdf arrays */ const uint16_t* init_index, /* input: vector of initial cdf table search entries */ - const int N); /* input: data vector length */ + int N); /* input: data vector length */ #endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ */ diff --git a/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h b/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h index 221e65ff3f..5f4550a3a5 100644 --- a/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h +++ b/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h @@ -92,11 +92,11 @@ int32_t WebRtcIsac_InitBandwidthEstimator( * estimated by other side */ /* returns 0 if everything went fine, -1 otherwise */ int16_t WebRtcIsac_UpdateBandwidthEstimator(BwEstimatorstr* bwest_str, - const uint16_t rtp_number, - const int32_t frame_length, - const uint32_t send_ts, - const uint32_t arr_ts, - const size_t pksize); + uint16_t rtp_number, + int32_t frame_length, + uint32_t send_ts, + uint32_t arr_ts, + size_t pksize); /* Update receiving estimates. Used when we only receive BWE index, no iSAC data * packet. */ @@ -131,10 +131,10 @@ int32_t WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr* bwest_str); */ int WebRtcIsac_GetMinBytes( RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameLen, /* ms per frame */ - const double BottleNeck, /* bottle neck rate; excl headers (bps) */ - const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ + int StreamSize, /* bytes in bitstream */ + int FrameLen, /* ms per frame */ + double BottleNeck, /* bottle neck rate; excl headers (bps) */ + double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ enum ISACBandwidth bandwidth /*,int16_t frequentLargePackets*/); @@ -143,9 +143,9 @@ int WebRtcIsac_GetMinBytes( */ void WebRtcIsac_UpdateRateModel( RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameSamples, /* samples per frame */ - const double BottleNeck); /* bottle neck rate; excl headers (bps) */ + int StreamSize, /* bytes in bitstream */ + int FrameSamples, /* samples per frame */ + double BottleNeck); /* bottle neck rate; excl headers (bps) */ void WebRtcIsac_InitRateModel(RateModel* State); diff --git a/modules/audio_coding/codecs/isac/main/source/isac.c b/modules/audio_coding/codecs/isac/main/source/isac.c index 73f132c228..456f447d9a 100644 --- a/modules/audio_coding/codecs/isac/main/source/isac.c +++ b/modules/audio_coding/codecs/isac/main/source/isac.c @@ -678,7 +678,7 @@ int WebRtcIsac_Encode(ISACStruct* ISAC_main_inst, /****************************************************************************** * WebRtcIsac_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. If the rate is set to a value less than bottleneck of codec * the new bistream will be re-encoded with the given target rate. * It should always return a complete packet, i.e. only called once diff --git a/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc b/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc index f081a5380f..f81aeeea80 100644 --- a/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc +++ b/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc @@ -88,7 +88,7 @@ class SplitBySamplesTest : public ::testing::TestWithParam { samples_per_ms_ = 8; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } @@ -138,7 +138,6 @@ TEST_P(SplitBySamplesTest, PayloadSizes) { EXPECT_EQ(expected_split.num_frames, results.size()); uint32_t expected_timestamp = kBaseTimestamp; - uint32_t expected_byte_offset = 0; uint8_t value = 0; for (size_t i = 0; i != expected_split.num_frames; ++i) { const auto& result = results[i]; @@ -155,7 +154,6 @@ TEST_P(SplitBySamplesTest, PayloadSizes) { expected_timestamp += rtc::checked_cast( expected_split.frame_sizes[i] * samples_per_ms_); - expected_byte_offset += rtc::checked_cast(length_bytes); } } } diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc index c8fd176fbb..285ea89959 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc @@ -26,6 +26,7 @@ std::unique_ptr AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder( AudioDecoderMultiChannelOpusConfig config) { if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); return nullptr; } // Fill the pointer with a working decoder through the C interface. This @@ -78,6 +79,9 @@ AudioDecoderMultiChannelOpusImpl::SdpToConfig(const SdpAudioFormat& format) { return absl::nullopt; } config.channel_mapping = *channel_mapping; + if (!config.IsOk()) { + return absl::nullopt; + } return config; } diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h index efc3f0dda8..2ff47a8a53 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h +++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h @@ -21,7 +21,6 @@ #include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h" #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -32,6 +31,11 @@ class AudioDecoderMultiChannelOpusImpl final : public AudioDecoder { ~AudioDecoderMultiChannelOpusImpl() override; + AudioDecoderMultiChannelOpusImpl(const AudioDecoderMultiChannelOpusImpl&) = + delete; + AudioDecoderMultiChannelOpusImpl& operator=( + const AudioDecoderMultiChannelOpusImpl&) = delete; + std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; void Reset() override; @@ -63,7 +67,6 @@ class AudioDecoderMultiChannelOpusImpl final : public AudioDecoder { OpusDecInst* dec_state_; const AudioDecoderMultiChannelOpusConfig config_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderMultiChannelOpusImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc index 66eecb758e..57e2107f3c 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc @@ -45,13 +45,7 @@ TEST(AudioDecoderMultiOpusTest, InvalidChannelMappings) { {"num_streams", "2"}}); const absl::optional decoder_config = AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(decoder_config.has_value()); - EXPECT_FALSE(decoder_config->IsOk()); - - const std::unique_ptr opus_decoder = - AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config); - - EXPECT_FALSE(opus_decoder); + EXPECT_FALSE(decoder_config.has_value()); } { // The mapping is too long. There are only 5 channels, but 6 elements in the @@ -62,13 +56,7 @@ TEST(AudioDecoderMultiOpusTest, InvalidChannelMappings) { {"num_streams", "2"}}); const absl::optional decoder_config = AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(decoder_config.has_value()); - EXPECT_FALSE(decoder_config->IsOk()); - - const std::unique_ptr opus_decoder = - AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config); - - EXPECT_FALSE(opus_decoder); + EXPECT_FALSE(decoder_config.has_value()); } { // The mapping doesn't parse correctly. diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.h b/modules/audio_coding/codecs/opus/audio_decoder_opus.h index c79272284d..e8fd0440bc 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.h @@ -19,7 +19,6 @@ #include "api/audio_codecs/audio_decoder.h" #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,9 @@ class AudioDecoderOpusImpl final : public AudioDecoder { int sample_rate_hz = 48000); ~AudioDecoderOpusImpl() override; + AudioDecoderOpusImpl(const AudioDecoderOpusImpl&) = delete; + AudioDecoderOpusImpl& operator=(const AudioDecoderOpusImpl&) = delete; + std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; void Reset() override; @@ -55,7 +57,6 @@ class AudioDecoderOpusImpl final : public AudioDecoder { OpusDecInst* dec_state_; const size_t channels_; const int sample_rate_hz_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderOpusImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc index 1feef3d359..38a11c123d 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc @@ -131,6 +131,7 @@ AudioEncoderMultiChannelOpusImpl::MakeAudioEncoder( const AudioEncoderMultiChannelOpusConfig& config, int payload_type) { if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); return nullptr; } return std::make_unique(config, @@ -280,6 +281,9 @@ AudioEncoderMultiChannelOpusImpl::SdpToConfig(const SdpAudioFormat& format) { } config.channel_mapping = *channel_mapping; + if (!config.IsOk()) { + return absl::nullopt; + } return config; } diff --git a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h index eadb4a6eb9..8a7210515c 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h @@ -21,7 +21,6 @@ #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" #include "api/units/time_delta.h" #include "modules/audio_coding/codecs/opus/opus_interface.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,11 @@ class AudioEncoderMultiChannelOpusImpl final : public AudioEncoder { int payload_type); ~AudioEncoderMultiChannelOpusImpl() override; + AudioEncoderMultiChannelOpusImpl(const AudioEncoderMultiChannelOpusImpl&) = + delete; + AudioEncoderMultiChannelOpusImpl& operator=( + const AudioEncoderMultiChannelOpusImpl&) = delete; + // Static interface for use by BuiltinAudioEncoderFactory. static constexpr const char* GetPayloadName() { return "multiopus"; } static absl::optional QueryAudioEncoder( @@ -81,7 +85,6 @@ class AudioEncoderMultiChannelOpusImpl final : public AudioEncoder { int next_frame_length_ms_; friend struct AudioEncoderMultiChannelOpus; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderMultiChannelOpusImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_unittest.cc index 44da7d7ffd..92f6f2c169 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_unittest.cc @@ -28,10 +28,9 @@ TEST(AudioEncoderMultiOpusTest, CheckConfigValidity) { {"num_streams", "2"}}); const absl::optional encoder_config = AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(encoder_config.has_value()); // Maps input channel 0 to coded channel 3, which doesn't exist. - EXPECT_FALSE(encoder_config->IsOk()); + EXPECT_FALSE(encoder_config.has_value()); } { @@ -41,10 +40,9 @@ TEST(AudioEncoderMultiOpusTest, CheckConfigValidity) { {"num_streams", "2"}}); const absl::optional encoder_config = AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(encoder_config.has_value()); // The mapping is too short. - EXPECT_FALSE(encoder_config->IsOk()); + EXPECT_FALSE(encoder_config.has_value()); } { const SdpAudioFormat sdp_format("multiopus", 48000, 3, @@ -53,10 +51,9 @@ TEST(AudioEncoderMultiOpusTest, CheckConfigValidity) { {"num_streams", "1"}}); const absl::optional encoder_config = AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(encoder_config.has_value()); // Coded channel 0 comes from both input channels 0, 1 and 2. - EXPECT_FALSE(encoder_config->IsOk()); + EXPECT_FALSE(encoder_config.has_value()); } { const SdpAudioFormat sdp_format("multiopus", 48000, 3, @@ -77,11 +74,10 @@ TEST(AudioEncoderMultiOpusTest, CheckConfigValidity) { {"num_streams", "2"}}); const absl::optional encoder_config = AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(encoder_config.has_value()); // This is NOT fine, because channels nothing says how coded channel 1 // should be coded. - EXPECT_FALSE(encoder_config->IsOk()); + EXPECT_FALSE(encoder_config.has_value()); } } @@ -105,7 +101,7 @@ TEST(AudioEncoderMultiOpusTest, ConfigValuesAreParsedCorrectly) { testing::ContainerEq(std::vector({0, 4, 1, 2, 3, 5}))); } -TEST(AudioEncoderMultiOpusTest, CreateFromValidOrInvalidConfig) { +TEST(AudioEncoderMultiOpusTest, CreateFromValidConfig) { { const SdpAudioFormat sdp_format("multiopus", 48000, 3, {{"channel_mapping", "0,255,255"}, @@ -113,19 +109,7 @@ TEST(AudioEncoderMultiOpusTest, CreateFromValidOrInvalidConfig) { {"num_streams", "2"}}); const absl::optional encoder_config = AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format); - ASSERT_TRUE(encoder_config.has_value()); - - // Invalid config from the ConfigValidity test. It's not allowed by our - // checks, but Opus is more forgiving. - EXPECT_FALSE(encoder_config->IsOk()); - - const std::unique_ptr opus_encoder = - AudioEncoderMultiChannelOpus::MakeAudioEncoder(*encoder_config, - kOpusPayloadType); - - // Shouldn't be possible (but shouldn't result in a crash) to create an - // Encoder from an invalid config. - EXPECT_FALSE(opus_encoder); + ASSERT_FALSE(encoder_config.has_value()); } { const SdpAudioFormat sdp_format("multiopus", 48000, 3, diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index 9f7e4e35c0..441e1a9607 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -229,7 +229,10 @@ AudioCodecInfo AudioEncoderOpusImpl::QueryAudioEncoder( std::unique_ptr AudioEncoderOpusImpl::MakeAudioEncoder( const AudioEncoderOpusConfig& config, int payload_type) { - RTC_DCHECK(config.IsOk()); + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } return std::make_unique(config, payload_type); } @@ -268,7 +271,10 @@ absl::optional AudioEncoderOpusImpl::SdpToConfig( FindSupportedFrameLengths(min_frame_length_ms, max_frame_length_ms, &config.supported_frame_lengths_ms); - RTC_DCHECK(config.IsOk()); + if (!config.IsOk()) { + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } return config; } diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.h b/modules/audio_coding/codecs/opus/audio_encoder_opus.h index 658112b93e..59eb887f04 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.h @@ -23,7 +23,6 @@ #include "common_audio/smoothing_filter.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "modules/audio_coding/codecs/opus/opus_interface.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -61,6 +60,9 @@ class AudioEncoderOpusImpl final : public AudioEncoder { AudioEncoderOpusImpl(int payload_type, const SdpAudioFormat& format); ~AudioEncoderOpusImpl() override; + AudioEncoderOpusImpl(const AudioEncoderOpusImpl&) = delete; + AudioEncoderOpusImpl& operator=(const AudioEncoderOpusImpl&) = delete; + int SampleRateHz() const override; size_t NumChannels() const override; int RtpTimestampRateHz() const override; @@ -178,7 +180,6 @@ class AudioEncoderOpusImpl final : public AudioEncoder { int consecutive_dtx_frames_; friend struct AudioEncoderOpus; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderOpusImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.cc b/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.cc index 1dd2ff289e..7761efe8b3 100644 --- a/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.cc +++ b/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.cc @@ -42,7 +42,12 @@ int AudioDecoderPcm16B::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(sample_rate_hz_, sample_rate_hz); - size_t ret = WebRtcPcm16b_Decode(encoded, encoded_len, decoded); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = + PacketDuration(encoded, encoded_len) * 2 * + Channels(); // 2 bytes per sample per channel + size_t ret = WebRtcPcm16b_Decode(encoded, encoded_len_adjusted, decoded); *speech_type = ConvertSpeechType(1); return static_cast(ret); } diff --git a/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h b/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h index f08c4a6298..6f50161d3f 100644 --- a/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h +++ b/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h @@ -18,13 +18,16 @@ #include "api/audio_codecs/audio_decoder.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { class AudioDecoderPcm16B final : public AudioDecoder { public: AudioDecoderPcm16B(int sample_rate_hz, size_t num_channels); + + AudioDecoderPcm16B(const AudioDecoderPcm16B&) = delete; + AudioDecoderPcm16B& operator=(const AudioDecoderPcm16B&) = delete; + void Reset() override; std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; @@ -42,7 +45,6 @@ class AudioDecoderPcm16B final : public AudioDecoder { private: const int sample_rate_hz_; const size_t num_channels_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h b/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h index 71c757250a..c363b40b3f 100644 --- a/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h +++ b/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_CODECS_PCM16B_AUDIO_ENCODER_PCM16B_H_ #include "modules/audio_coding/codecs/g711/audio_encoder_pcm.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,9 @@ class AudioEncoderPcm16B final : public AudioEncoderPcm { explicit AudioEncoderPcm16B(const Config& config) : AudioEncoderPcm(config, config.sample_rate_hz) {} + AudioEncoderPcm16B(const AudioEncoderPcm16B&) = delete; + AudioEncoderPcm16B& operator=(const AudioEncoderPcm16B&) = delete; + protected: size_t EncodeCall(const int16_t* audio, size_t input_len, @@ -37,9 +39,6 @@ class AudioEncoderPcm16B final : public AudioEncoderPcm { size_t BytesPerSample() const override; AudioEncoder::CodecType GetCodecType() const override; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderPcm16B); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc index 06679625df..c8a26e8c61 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc @@ -18,12 +18,13 @@ #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { static constexpr const int kRedMaxPacketSize = 1 << 10; // RED packets must be less than 1024 bytes to fit the 10 bit // block length. +static constexpr const size_t kRedMaxTimestampDelta = + 1 << 14; // RED packets can encode a timestamp delta of 14 bits. static constexpr const size_t kAudioMaxRtpPacketLen = 1200; // The typical MTU is 1200 bytes. @@ -38,25 +39,29 @@ AudioEncoderCopyRed::Config::Config() = default; AudioEncoderCopyRed::Config::Config(Config&&) = default; AudioEncoderCopyRed::Config::~Config() = default; -size_t GetMaxRedundancyFromFieldTrial() { +size_t GetMaxRedundancyFromFieldTrial( + const WebRtcKeyValueConfig& field_trials) { const std::string red_trial = - webrtc::field_trial::FindFullName("WebRTC-Audio-Red-For-Opus"); + field_trials.Lookup("WebRTC-Audio-Red-For-Opus"); size_t redundancy = 0; if (sscanf(red_trial.c_str(), "Enabled-%zu", &redundancy) != 1 || - redundancy < 1 || redundancy > 9) { + redundancy > 9) { return kRedNumberOfRedundantEncodings; } return redundancy; } -AudioEncoderCopyRed::AudioEncoderCopyRed(Config&& config) +AudioEncoderCopyRed::AudioEncoderCopyRed( + Config&& config, + const WebRtcKeyValueConfig& field_trials) : speech_encoder_(std::move(config.speech_encoder)), primary_encoded_(0, kAudioMaxRtpPacketLen), max_packet_length_(kAudioMaxRtpPacketLen), red_payload_type_(config.payload_type) { RTC_CHECK(speech_encoder_) << "Speech encoder not provided."; - auto number_of_redundant_encodings = GetMaxRedundancyFromFieldTrial(); + auto number_of_redundant_encodings = + GetMaxRedundancyFromFieldTrial(field_trials); for (size_t i = 0; i < number_of_redundant_encodings; i++) { std::pair redundant; redundant.second.EnsureCapacity(kAudioMaxRtpPacketLen); @@ -100,7 +105,7 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( RTC_CHECK(info.redundant.empty()) << "Cannot use nested redundant encoders."; RTC_DCHECK_EQ(primary_encoded_.size(), info.encoded_bytes); - if (info.encoded_bytes == 0 || info.encoded_bytes > kRedMaxPacketSize) { + if (info.encoded_bytes == 0 || info.encoded_bytes >= kRedMaxPacketSize) { return info; } RTC_DCHECK_GT(max_packet_length_, info.encoded_bytes); @@ -110,7 +115,9 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( auto it = redundant_encodings_.begin(); // Determine how much redundancy we can fit into our packet by - // iterating forward. + // iterating forward. This is determined both by the length as well + // as the timestamp difference. The latter can occur with opus DTX which + // has timestamp gaps of 400ms which exceeds REDs timestamp delta field size. for (; it != redundant_encodings_.end(); it++) { if (bytes_available < kRedHeaderLength + it->first.encoded_bytes) { break; @@ -118,6 +125,9 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( if (it->first.encoded_bytes == 0) { break; } + if (rtp_timestamp - it->first.encoded_timestamp >= kRedMaxTimestampDelta) { + break; + } bytes_available -= kRedHeaderLength + it->first.encoded_bytes; header_length_bytes += kRedHeaderLength; } @@ -161,8 +171,10 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( rit->second.SetData(next->second); } it = redundant_encodings_.begin(); - it->first = info; - it->second.SetData(primary_encoded_); + if (it != redundant_encodings_.end()) { + it->first = info; + it->second.SetData(primary_encoded_); + } // Update main EncodedInfo. info.payload_type = red_payload_type_; diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h index d5b1bf6868..e7471b3e12 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h @@ -22,8 +22,8 @@ #include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -43,10 +43,14 @@ class AudioEncoderCopyRed final : public AudioEncoder { std::unique_ptr speech_encoder; }; - explicit AudioEncoderCopyRed(Config&& config); + AudioEncoderCopyRed(Config&& config, + const WebRtcKeyValueConfig& field_trials); ~AudioEncoderCopyRed() override; + AudioEncoderCopyRed(const AudioEncoderCopyRed&) = delete; + AudioEncoderCopyRed& operator=(const AudioEncoderCopyRed&) = delete; + int SampleRateHz() const override; size_t NumChannels() const override; int RtpTimestampRateHz() const override; @@ -92,8 +96,6 @@ class AudioEncoderCopyRed final : public AudioEncoder { size_t max_packet_length_; int red_payload_type_; std::list> redundant_encodings_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderCopyRed); }; } // namespace webrtc diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc index f8e3437d13..795a996624 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc @@ -18,6 +18,7 @@ #include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" +#include "test/scoped_key_value_config.h" #include "test/testsupport/rtc_expect_death.h" using ::testing::_; @@ -49,7 +50,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::unique_ptr(mock_encoder_); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials_)); memset(audio_, 0, sizeof(audio_)); EXPECT_CALL(*mock_encoder_, NumChannels()).WillRepeatedly(Return(1U)); EXPECT_CALL(*mock_encoder_, SampleRateHz()) @@ -68,6 +69,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { timestamp_ += rtc::checked_cast(num_audio_samples_10ms); } + test::ScopedKeyValueConfig field_trials_; MockAudioEncoder* mock_encoder_; std::unique_ptr red_; uint32_t timestamp_; @@ -196,15 +198,41 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes1) { } // Checks that the correct payload sizes are populated into the redundancy -// information for a redundancy level of 2. -TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-2/"); +// information for a redundancy level of 0. +TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes0) { + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-0/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); + + // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence + // of calls. + static const int kNumPackets = 10; + InSequence s; + for (int encode_size = 1; encode_size <= kNumPackets; ++encode_size) { + EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _)) + .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(encode_size))); + } + + for (size_t i = 1; i <= kNumPackets; ++i) { + Encode(); + ASSERT_EQ(0u, encoded_info_.redundant.size()); + EXPECT_EQ(1 + i, encoded_info_.encoded_bytes); + } +} +// Checks that the correct payload sizes are populated into the redundancy +// information for a redundancy level of 2. +TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) { + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-2/"); + // Recreate the RED encoder to take the new field trial setting into account. + AudioEncoderCopyRed::Config config; + config.payload_type = red_payload_type_; + config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence // of calls. @@ -240,13 +268,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) { // Checks that the correct payload sizes are populated into the redundancy // information for a redundancy level of 3. TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes3) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-3/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials_)); // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence // of calls. @@ -435,15 +463,43 @@ TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header) { encoded_info_.redundant[1].encoded_timestamp; } -// Variant with a redundancy of 2. -TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header2) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-2/"); +// Variant with a redundancy of 0. +TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header0) { + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-0/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); + + const int primary_payload_type = red_payload_type_ + 1; + AudioEncoder::EncodedInfo info; + info.encoded_bytes = 10; + info.encoded_timestamp = timestamp_; + info.payload_type = primary_payload_type; + + EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _)) + .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info))); + Encode(); + info.encoded_timestamp = timestamp_; // update timestamp. + EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _)) + .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info))); + Encode(); // Second call will not produce a redundant encoding. + + EXPECT_EQ(encoded_.size(), + 1u + 1 * 10u); // header size + one encoded payloads. + EXPECT_EQ(encoded_[0], primary_payload_type); +} +// Variant with a redundancy of 2. +TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header2) { + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-2/"); + // Recreate the RED encoder to take the new field trial setting into account. + AudioEncoderCopyRed::Config config; + config.payload_type = red_payload_type_; + config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); const int primary_payload_type = red_payload_type_ + 1; AudioEncoder::EncodedInfo info; @@ -529,6 +585,29 @@ TEST_F(AudioEncoderCopyRedTest, RespectsPayloadMTU) { EXPECT_EQ(encoded_.size(), 5u + 500u + 400u); } +TEST_F(AudioEncoderCopyRedTest, LargeTimestampGap) { + const int primary_payload_type = red_payload_type_ + 1; + AudioEncoder::EncodedInfo info; + info.encoded_bytes = 100; + info.encoded_timestamp = timestamp_; + info.payload_type = primary_payload_type; + + EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _)) + .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info))); + Encode(); + // Update timestamp to simulate a 400ms gap like the one + // opus DTX causes. + timestamp_ += 19200; + info.encoded_timestamp = timestamp_; // update timestamp. + info.encoded_bytes = 200; + EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _)) + .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info))); + Encode(); + + // The old packet will be dropped. + EXPECT_EQ(encoded_.size(), 1u + 200u); +} + #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) // This test fixture tests various error conditions that makes the @@ -546,11 +625,13 @@ TEST_F(AudioEncoderCopyRedDeathTest, WrongFrameSize) { } TEST_F(AudioEncoderCopyRedDeathTest, NullSpeechEncoder) { + test::ScopedKeyValueConfig field_trials; AudioEncoderCopyRed* red = NULL; AudioEncoderCopyRed::Config config; config.speech_encoder = NULL; - RTC_EXPECT_DEATH(red = new AudioEncoderCopyRed(std::move(config)), - "Speech encoder not provided."); + RTC_EXPECT_DEATH( + red = new AudioEncoderCopyRed(std::move(config), field_trials), + "Speech encoder not provided."); // The delete operation is needed to avoid leak reports from memcheck. delete red; } diff --git a/modules/audio_coding/include/audio_coding_module.h b/modules/audio_coding/include/audio_coding_module.h index 7551814023..8b518fb979 100644 --- a/modules/audio_coding/include/audio_coding_module.h +++ b/modules/audio_coding/include/audio_coding_module.h @@ -54,7 +54,7 @@ class AudioPacketizationCallback { uint32_t timestamp, const uint8_t* payload_data, size_t payload_len_bytes) { - RTC_NOTREACHED() << "This method must be overridden, or not used."; + RTC_DCHECK_NOTREACHED() << "This method must be overridden, or not used."; return -1; } }; @@ -190,7 +190,7 @@ class AudioCodingModule { // 0 if payload is successfully pushed in. // virtual int32_t IncomingPacket(const uint8_t* incoming_payload, - const size_t payload_len_bytes, + size_t payload_len_bytes, const RTPHeader& rtp_header) = 0; /////////////////////////////////////////////////////////////////////////// @@ -237,6 +237,8 @@ class AudioCodingModule { NetworkStatistics* network_statistics) = 0; virtual ANAStats GetANAStats() const = 0; + + virtual int GetTargetBitrate() const = 0; }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/accelerate.h b/modules/audio_coding/neteq/accelerate.h index e03f609ffb..01fe874d54 100644 --- a/modules/audio_coding/neteq/accelerate.h +++ b/modules/audio_coding/neteq/accelerate.h @@ -15,7 +15,6 @@ #include #include "modules/audio_coding/neteq/time_stretch.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -33,6 +32,9 @@ class Accelerate : public TimeStretch { const BackgroundNoise& background_noise) : TimeStretch(sample_rate_hz, num_channels, background_noise) {} + Accelerate(const Accelerate&) = delete; + Accelerate& operator=(const Accelerate&) = delete; + // This method performs the actual Accelerate operation. The samples are // read from `input`, of length `input_length` elements, and are written to // `output`. The number of samples removed through time-stretching is @@ -62,9 +64,6 @@ class Accelerate : public TimeStretch { bool active_speech, bool fast_mode, AudioMultiVector* output) const override; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(Accelerate); }; struct AccelerateFactory { diff --git a/modules/audio_coding/neteq/audio_decoder_unittest.cc b/modules/audio_coding/neteq/audio_decoder_unittest.cc index ac0e48efa5..bb5c6d167b 100644 --- a/modules/audio_coding/neteq/audio_decoder_unittest.cc +++ b/modules/audio_coding/neteq/audio_decoder_unittest.cc @@ -590,7 +590,7 @@ TEST_F(AudioDecoderIsacFixTest, EncodeDecode) { #if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM) static const int kEncodedBytes = 685; #elif defined(WEBRTC_MAC) && defined(WEBRTC_ARCH_ARM64) // M1 Mac - static const int kEncodedBytes = 684; + static const int kEncodedBytes = 673; #elif defined(WEBRTC_ARCH_ARM64) static const int kEncodedBytes = 673; #elif defined(WEBRTC_WIN) && defined(_MSC_VER) && !defined(__clang__) @@ -598,7 +598,7 @@ TEST_F(AudioDecoderIsacFixTest, EncodeDecode) { #elif defined(WEBRTC_IOS) && defined(WEBRTC_ARCH_X86_64) static const int kEncodedBytes = 671; #else - static const int kEncodedBytes = 687; + static const int kEncodedBytes = 671; #endif EncodeDecodeTest(kEncodedBytes, tolerance, mse, delay); ReInitTest(); diff --git a/modules/audio_coding/neteq/audio_multi_vector.h b/modules/audio_coding/neteq/audio_multi_vector.h index 10179d7f07..715ec6dfc7 100644 --- a/modules/audio_coding/neteq/audio_multi_vector.h +++ b/modules/audio_coding/neteq/audio_multi_vector.h @@ -18,7 +18,6 @@ #include "api/array_view.h" #include "modules/audio_coding/neteq/audio_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,9 @@ class AudioMultiVector { virtual ~AudioMultiVector(); + AudioMultiVector(const AudioMultiVector&) = delete; + AudioMultiVector& operator=(const AudioMultiVector&) = delete; + // Deletes all values and make the vector empty. virtual void Clear(); @@ -130,9 +132,6 @@ class AudioMultiVector { protected: std::vector channels_; size_t num_channels_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioMultiVector); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/audio_vector.h b/modules/audio_coding/neteq/audio_vector.h index c722b56965..d68f3ec6be 100644 --- a/modules/audio_coding/neteq/audio_vector.h +++ b/modules/audio_coding/neteq/audio_vector.h @@ -17,7 +17,6 @@ #include #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -31,6 +30,9 @@ class AudioVector { virtual ~AudioVector(); + AudioVector(const AudioVector&) = delete; + AudioVector& operator=(const AudioVector&) = delete; + // Deletes all values and make the vector empty. virtual void Clear(); @@ -164,8 +166,6 @@ class AudioVector { // The index of the sample after the last sample in `array_`. size_t end_index_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioVector); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/background_noise.h b/modules/audio_coding/neteq/background_noise.h index 005b3766fc..8e6d5890a0 100644 --- a/modules/audio_coding/neteq/background_noise.h +++ b/modules/audio_coding/neteq/background_noise.h @@ -16,7 +16,6 @@ #include #include "api/array_view.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,9 @@ class BackgroundNoise { explicit BackgroundNoise(size_t num_channels); virtual ~BackgroundNoise(); + BackgroundNoise(const BackgroundNoise&) = delete; + BackgroundNoise& operator=(const BackgroundNoise&) = delete; + void Reset(); // Updates the parameter estimates based on the signal currently in the @@ -130,8 +132,6 @@ class BackgroundNoise { size_t num_channels_; std::unique_ptr channel_parameters_; bool initialized_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BackgroundNoise); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/buffer_level_filter.h b/modules/audio_coding/neteq/buffer_level_filter.h index 94a37150e4..ced36da9c2 100644 --- a/modules/audio_coding/neteq/buffer_level_filter.h +++ b/modules/audio_coding/neteq/buffer_level_filter.h @@ -14,14 +14,16 @@ #include #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { class BufferLevelFilter { public: BufferLevelFilter(); virtual ~BufferLevelFilter() {} + + BufferLevelFilter(const BufferLevelFilter&) = delete; + BufferLevelFilter& operator=(const BufferLevelFilter&) = delete; + virtual void Reset(); // Updates the filter. Current buffer size is `buffer_size_samples`. @@ -46,8 +48,6 @@ class BufferLevelFilter { private: int level_factor_; // Filter factor for the buffer level filter in Q8. int filtered_current_level_; // Filtered current buffer level in Q8. - - RTC_DISALLOW_COPY_AND_ASSIGN(BufferLevelFilter); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/comfort_noise.h b/modules/audio_coding/neteq/comfort_noise.h index 6419d397d3..31fcee31d0 100644 --- a/modules/audio_coding/neteq/comfort_noise.h +++ b/modules/audio_coding/neteq/comfort_noise.h @@ -13,8 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { // Forward declarations. @@ -42,6 +40,9 @@ class ComfortNoise { decoder_database_(decoder_database), sync_buffer_(sync_buffer) {} + ComfortNoise(const ComfortNoise&) = delete; + ComfortNoise& operator=(const ComfortNoise&) = delete; + // Resets the state. Should be called before each new comfort noise period. void Reset(); @@ -65,7 +66,6 @@ class ComfortNoise { DecoderDatabase* decoder_database_; SyncBuffer* sync_buffer_; int internal_error_code_; - RTC_DISALLOW_COPY_AND_ASSIGN(ComfortNoise); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h index 693f6169e4..a8571ade96 100644 --- a/modules/audio_coding/neteq/decision_logic.h +++ b/modules/audio_coding/neteq/decision_logic.h @@ -18,7 +18,6 @@ #include "api/neteq/tick_timer.h" #include "modules/audio_coding/neteq/buffer_level_filter.h" #include "modules/audio_coding/neteq/delay_manager.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -37,6 +36,9 @@ class DecisionLogic : public NetEqController { ~DecisionLogic() override; + DecisionLogic(const DecisionLogic&) = delete; + DecisionLogic& operator=(const DecisionLogic&) = delete; + // Resets object to a clean state. void Reset() override; @@ -192,8 +194,6 @@ class DecisionLogic : public NetEqController { FieldTrialParameter estimate_dtx_delay_; FieldTrialParameter time_stretch_cn_; FieldTrialConstrained target_level_window_ms_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DecisionLogic); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/decoder_database.h b/modules/audio_coding/neteq/decoder_database.h index a63a9cff18..6c2ce54039 100644 --- a/modules/audio_coding/neteq/decoder_database.h +++ b/modules/audio_coding/neteq/decoder_database.h @@ -20,7 +20,6 @@ #include "api/scoped_refptr.h" #include "modules/audio_coding/codecs/cng/webrtc_cng.h" #include "modules/audio_coding/neteq/packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -122,6 +121,9 @@ class DecoderDatabase { virtual ~DecoderDatabase(); + DecoderDatabase(const DecoderDatabase&) = delete; + DecoderDatabase& operator=(const DecoderDatabase&) = delete; + // Returns true if the database is empty. virtual bool Empty() const; @@ -208,8 +210,6 @@ class DecoderDatabase { mutable std::unique_ptr active_cng_decoder_; rtc::scoped_refptr decoder_factory_; const absl::optional codec_pair_id_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DecoderDatabase); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index 97d1d2eaad..9f6b269b03 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -20,6 +20,7 @@ #include "modules/include/module_common_types_public.h" #include "rtc_base/checks.h" +#include "rtc_base/experiments/struct_parameters_parser.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/numerics/safe_minmax.h" @@ -45,9 +46,17 @@ std::unique_ptr MaybeCreateReorderOptimizer( } // namespace DelayManager::Config::Config() { - Parser()->Parse(webrtc::field_trial::FindFullName( - "WebRTC-Audio-NetEqDelayManagerConfig")); - MaybeUpdateFromLegacyFieldTrial(); + StructParametersParser::Create( // + "quantile", &quantile, // + "forget_factor", &forget_factor, // + "start_forget_weight", &start_forget_weight, // + "resample_interval_ms", &resample_interval_ms, // + "max_history_ms", &max_history_ms, // + "use_reorder_optimizer", &use_reorder_optimizer, // + "reorder_forget_factor", &reorder_forget_factor, // + "ms_per_loss_percent", &ms_per_loss_percent) + ->Parse(webrtc::field_trial::FindFullName( + "WebRTC-Audio-NetEqDelayManagerConfig")); } void DelayManager::Config::Log() { @@ -63,42 +72,6 @@ void DelayManager::Config::Log() { << " ms_per_loss_percent=" << ms_per_loss_percent; } -std::unique_ptr DelayManager::Config::Parser() { - return StructParametersParser::Create( // - "quantile", &quantile, // - "forget_factor", &forget_factor, // - "start_forget_weight", &start_forget_weight, // - "resample_interval_ms", &resample_interval_ms, // - "max_history_ms", &max_history_ms, // - "use_reorder_optimizer", &use_reorder_optimizer, // - "reorder_forget_factor", &reorder_forget_factor, // - "ms_per_loss_percent", &ms_per_loss_percent); -} - -// TODO(jakobi): remove legacy field trial. -void DelayManager::Config::MaybeUpdateFromLegacyFieldTrial() { - constexpr char kDelayHistogramFieldTrial[] = - "WebRTC-Audio-NetEqDelayHistogram"; - if (!webrtc::field_trial::IsEnabled(kDelayHistogramFieldTrial)) { - return; - } - const auto field_trial_string = - webrtc::field_trial::FindFullName(kDelayHistogramFieldTrial); - double percentile = -1.0; - double forget_factor = -1.0; - double start_forget_weight = -1.0; - if (sscanf(field_trial_string.c_str(), "Enabled-%lf-%lf-%lf", &percentile, - &forget_factor, &start_forget_weight) >= 2 && - percentile >= 0.0 && percentile <= 100.0 && forget_factor >= 0.0 && - forget_factor <= 1.0) { - this->quantile = percentile / 100; - this->forget_factor = forget_factor; - this->start_forget_weight = start_forget_weight >= 1 - ? absl::make_optional(start_forget_weight) - : absl::nullopt; - } -} - DelayManager::DelayManager(const Config& config, const TickTimer* tick_timer) : max_packets_in_buffer_(config.max_packets_in_buffer), underrun_optimizer_(tick_timer, diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 277b80de74..56d108ad11 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -22,8 +22,6 @@ #include "modules/audio_coding/neteq/relative_arrival_delay_tracker.h" #include "modules/audio_coding/neteq/reorder_optimizer.h" #include "modules/audio_coding/neteq/underrun_optimizer.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/experiments/struct_parameters_parser.h" namespace webrtc { @@ -34,10 +32,10 @@ class DelayManager { void Log(); // Options that can be configured via field trial. - double quantile = 0.97; - double forget_factor = 0.9993; + double quantile = 0.95; + double forget_factor = 0.983; absl::optional start_forget_weight = 2; - absl::optional resample_interval_ms; + absl::optional resample_interval_ms = 500; int max_history_ms = 2000; bool use_reorder_optimizer = true; @@ -47,18 +45,15 @@ class DelayManager { // Options that are externally populated. int max_packets_in_buffer = 200; int base_minimum_delay_ms = 0; - - private: - std::unique_ptr Parser(); - - // TODO(jakobi): remove legacy field trial. - void MaybeUpdateFromLegacyFieldTrial(); }; DelayManager(const Config& config, const TickTimer* tick_timer); virtual ~DelayManager(); + DelayManager(const DelayManager&) = delete; + DelayManager& operator=(const DelayManager&) = delete; + // Updates the delay manager with a new incoming packet, with `timestamp` from // the RTP header. This updates the statistics and a new target buffer level // is calculated. Returns the relative delay if it can be calculated. If @@ -118,9 +113,7 @@ class DelayManager { int maximum_delay_ms_; // Externally set maximum allowed delay. int packet_len_ms_ = 0; - int target_level_ms_; // Currently preferred buffer level. - - RTC_DISALLOW_COPY_AND_ASSIGN(DelayManager); + int target_level_ms_; // Currently preferred buffer level. }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index fc4e0cb4cd..ee353065ea 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -44,8 +44,8 @@ class DelayManagerTest : public ::testing::Test { absl::optional InsertNextPacket(); void IncreaseTime(int inc_ms); - DelayManager dm_; TickTimer tick_timer_; + DelayManager dm_; uint32_t ts_; }; @@ -74,39 +74,18 @@ TEST_F(DelayManagerTest, CreateAndDestroy) { } TEST_F(DelayManagerTest, UpdateNormal) { - // First packet arrival. - InsertNextPacket(); - // Advance time by one frame size. - IncreaseTime(kFrameSizeMs); - // Second packet arrival. - InsertNextPacket(); + for (int i = 0; i < 50; ++i) { + InsertNextPacket(); + IncreaseTime(kFrameSizeMs); + } EXPECT_EQ(20, dm_.TargetDelayMs()); } -TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) { - // First packet arrival. - InsertNextPacket(); - // Advance time by two frame size. - IncreaseTime(2 * kFrameSizeMs); - // Second packet arrival. - InsertNextPacket(); - EXPECT_EQ(40, dm_.TargetDelayMs()); -} - TEST_F(DelayManagerTest, MaxDelay) { - const int kExpectedTarget = 5 * kFrameSizeMs; - // First packet arrival. InsertNextPacket(); - // Second packet arrival. - IncreaseTime(kExpectedTarget); - InsertNextPacket(); - - // No limit is set. - EXPECT_EQ(kExpectedTarget, dm_.TargetDelayMs()); - - const int kMaxDelayMs = 3 * kFrameSizeMs; + const int kMaxDelayMs = 60; + EXPECT_GT(dm_.TargetDelayMs(), kMaxDelayMs); EXPECT_TRUE(dm_.SetMaximumDelay(kMaxDelayMs)); - IncreaseTime(kFrameSizeMs); InsertNextPacket(); EXPECT_EQ(kMaxDelayMs, dm_.TargetDelayMs()); @@ -115,17 +94,9 @@ TEST_F(DelayManagerTest, MaxDelay) { } TEST_F(DelayManagerTest, MinDelay) { - const int kExpectedTarget = 5 * kFrameSizeMs; - // First packet arrival. InsertNextPacket(); - // Second packet arrival. - IncreaseTime(kExpectedTarget); - InsertNextPacket(); - - // No limit is applied. - EXPECT_EQ(kExpectedTarget, dm_.TargetDelayMs()); - int kMinDelayMs = 7 * kFrameSizeMs; + EXPECT_LT(dm_.TargetDelayMs(), kMinDelayMs); dm_.SetMinimumDelay(kMinDelayMs); IncreaseTime(kFrameSizeMs); InsertNextPacket(); @@ -251,48 +222,11 @@ TEST_F(DelayManagerTest, MinimumDelayMemorization) { } TEST_F(DelayManagerTest, BaseMinimumDelay) { - const int kExpectedTarget = 5 * kFrameSizeMs; // First packet arrival. InsertNextPacket(); - // Second packet arrival. - IncreaseTime(kExpectedTarget); - InsertNextPacket(); - - // No limit is applied. - EXPECT_EQ(kExpectedTarget, dm_.TargetDelayMs()); constexpr int kBaseMinimumDelayMs = 7 * kFrameSizeMs; - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs); - - IncreaseTime(kFrameSizeMs); - InsertNextPacket(); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs); - EXPECT_EQ(kBaseMinimumDelayMs, dm_.TargetDelayMs()); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayAffectsTargetDelay) { - const int kExpectedTarget = 5; - const int kTimeIncrement = kExpectedTarget * kFrameSizeMs; - // First packet arrival. - InsertNextPacket(); - // Second packet arrival. - IncreaseTime(kTimeIncrement); - InsertNextPacket(); - - // No limit is applied. - EXPECT_EQ(kTimeIncrement, dm_.TargetDelayMs()); - - // Minimum delay is lower than base minimum delay, that is why base minimum - // delay is used to calculate target level. - constexpr int kMinimumDelayPackets = kExpectedTarget + 1; - constexpr int kBaseMinimumDelayPackets = kExpectedTarget + 2; - - constexpr int kMinimumDelayMs = kMinimumDelayPackets * kFrameSizeMs; - constexpr int kBaseMinimumDelayMs = kBaseMinimumDelayPackets * kFrameSizeMs; - - EXPECT_TRUE(kMinimumDelayMs < kBaseMinimumDelayMs); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_LT(dm_.TargetDelayMs(), kBaseMinimumDelayMs); EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs); diff --git a/modules/audio_coding/neteq/dsp_helper.cc b/modules/audio_coding/neteq/dsp_helper.cc index 54ec556831..a979f94214 100644 --- a/modules/audio_coding/neteq/dsp_helper.cc +++ b/modules/audio_coding/neteq/dsp_helper.cc @@ -354,7 +354,7 @@ int DspHelper::DownsampleTo4kHz(const int16_t* input, break; } default: { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } } diff --git a/modules/audio_coding/neteq/dsp_helper.h b/modules/audio_coding/neteq/dsp_helper.h index 7bdeba6ec0..4aead7df18 100644 --- a/modules/audio_coding/neteq/dsp_helper.h +++ b/modules/audio_coding/neteq/dsp_helper.h @@ -16,7 +16,6 @@ #include "modules/audio_coding/neteq/audio_multi_vector.h" #include "modules/audio_coding/neteq/audio_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -150,11 +149,12 @@ class DspHelper { bool compensate_delay, int16_t* output); + DspHelper(const DspHelper&) = delete; + DspHelper& operator=(const DspHelper&) = delete; + private: // Table of constants used in method DspHelper::ParabolicFit(). static const int16_t kParabolaCoefficients[17][3]; - - RTC_DISALLOW_COPY_AND_ASSIGN(DspHelper); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/dtmf_buffer.h b/modules/audio_coding/neteq/dtmf_buffer.h index 9209cae864..62b751525c 100644 --- a/modules/audio_coding/neteq/dtmf_buffer.h +++ b/modules/audio_coding/neteq/dtmf_buffer.h @@ -16,8 +16,6 @@ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { struct DtmfEvent { @@ -50,6 +48,9 @@ class DtmfBuffer { virtual ~DtmfBuffer(); + DtmfBuffer(const DtmfBuffer&) = delete; + DtmfBuffer& operator=(const DtmfBuffer&) = delete; + // Flushes the buffer. virtual void Flush(); @@ -97,8 +98,6 @@ class DtmfBuffer { static bool CompareEvents(const DtmfEvent& a, const DtmfEvent& b); DtmfList buffer_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DtmfBuffer); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/dtmf_tone_generator.cc b/modules/audio_coding/neteq/dtmf_tone_generator.cc index 49cbf8fcbc..9061e27c67 100644 --- a/modules/audio_coding/neteq/dtmf_tone_generator.cc +++ b/modules/audio_coding/neteq/dtmf_tone_generator.cc @@ -119,7 +119,7 @@ int DtmfToneGenerator::Init(int fs, int event, int attenuation) { } else if (fs == 48000) { fs_index = 3; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); fs_index = 1; // Default to 8000 Hz. } diff --git a/modules/audio_coding/neteq/dtmf_tone_generator.h b/modules/audio_coding/neteq/dtmf_tone_generator.h index 968bc7f8c7..35114f4f49 100644 --- a/modules/audio_coding/neteq/dtmf_tone_generator.h +++ b/modules/audio_coding/neteq/dtmf_tone_generator.h @@ -15,7 +15,6 @@ #include #include "modules/audio_coding/neteq/audio_multi_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,10 @@ class DtmfToneGenerator { DtmfToneGenerator(); virtual ~DtmfToneGenerator() {} + + DtmfToneGenerator(const DtmfToneGenerator&) = delete; + DtmfToneGenerator& operator=(const DtmfToneGenerator&) = delete; + virtual int Init(int fs, int event, int attenuation); virtual void Reset(); virtual int Generate(size_t num_samples, AudioMultiVector* output); @@ -48,8 +51,6 @@ class DtmfToneGenerator { int amplitude_; // Amplitude for this event. int16_t sample_history1_[2]; // Last 2 samples for the 1st oscillator. int16_t sample_history2_[2]; // Last 2 samples for the 2nd oscillator. - - RTC_DISALLOW_COPY_AND_ASSIGN(DtmfToneGenerator); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/expand.h b/modules/audio_coding/neteq/expand.h index 2d22b11289..2e64583ec2 100644 --- a/modules/audio_coding/neteq/expand.h +++ b/modules/audio_coding/neteq/expand.h @@ -15,7 +15,6 @@ #include #include "modules/audio_coding/neteq/audio_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -41,6 +40,9 @@ class Expand { virtual ~Expand(); + Expand(const Expand&) = delete; + Expand& operator=(const Expand&) = delete; + // Resets the object. virtual void Reset(); @@ -134,8 +136,6 @@ class Expand { bool stop_muting_; size_t expand_duration_samples_; std::unique_ptr channel_parameters_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Expand); }; struct ExpandFactory { diff --git a/modules/audio_coding/neteq/expand_uma_logger.h b/modules/audio_coding/neteq/expand_uma_logger.h index 246aaffd4f..a29d3532f3 100644 --- a/modules/audio_coding/neteq/expand_uma_logger.h +++ b/modules/audio_coding/neteq/expand_uma_logger.h @@ -17,7 +17,6 @@ #include "absl/types/optional.h" #include "api/neteq/tick_timer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -36,6 +35,9 @@ class ExpandUmaLogger { ~ExpandUmaLogger(); + ExpandUmaLogger(const ExpandUmaLogger&) = delete; + ExpandUmaLogger& operator=(const ExpandUmaLogger&) = delete; + // In this call, value should be an incremental sample counter. The sample // rate must be strictly positive. void UpdateSampleCounter(uint64_t value, int sample_rate_hz); @@ -48,8 +50,6 @@ class ExpandUmaLogger { absl::optional last_logged_value_; uint64_t last_value_ = 0; int sample_rate_hz_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(ExpandUmaLogger); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/expand_unittest.cc b/modules/audio_coding/neteq/expand_unittest.cc index 9c3264fad0..9355fce5e1 100644 --- a/modules/audio_coding/neteq/expand_unittest.cc +++ b/modules/audio_coding/neteq/expand_unittest.cc @@ -135,11 +135,9 @@ TEST_F(ExpandTest, DelayedPacketOutage) { // arrived before it). TEST_F(ExpandTest, LostPacketOutage) { AudioMultiVector output(num_channels_); - size_t sum_output_len_samples = 0; for (int i = 0; i < 10; ++i) { EXPECT_EQ(0, expand_.Process(&output)); EXPECT_GT(output.Size(), 0u); - sum_output_len_samples += output.Size(); EXPECT_EQ(0, statistics_.last_outage_duration_samples()); } expand_.SetParametersForMergeAfterExpand(); diff --git a/modules/audio_coding/neteq/merge.cc b/modules/audio_coding/neteq/merge.cc index ca5ec220a3..22cf6a7754 100644 --- a/modules/audio_coding/neteq/merge.cc +++ b/modules/audio_coding/neteq/merge.cc @@ -372,7 +372,7 @@ size_t Merge::CorrelateAndPeakSearch(size_t start_position, while (((best_correlation_index + input_length) < (timestamps_per_call_ + expand_->overlap_length())) || ((best_correlation_index + input_length) < start_position)) { - RTC_NOTREACHED(); // Should never happen. + RTC_DCHECK_NOTREACHED(); // Should never happen. best_correlation_index += expand_period; // Jump one lag ahead. } return best_correlation_index; diff --git a/modules/audio_coding/neteq/merge.h b/modules/audio_coding/neteq/merge.h index 13aa31df8e..2f27106bfe 100644 --- a/modules/audio_coding/neteq/merge.h +++ b/modules/audio_coding/neteq/merge.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_NETEQ_MERGE_H_ #include "modules/audio_coding/neteq/audio_multi_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -36,6 +35,9 @@ class Merge { SyncBuffer* sync_buffer); virtual ~Merge(); + Merge(const Merge&) = delete; + Merge& operator=(const Merge&) = delete; + // The main method to produce the audio data. The decoded data is supplied in // `input`, having `input_length` samples in total for all channels // (interleaved). The result is written to `output`. The number of channels @@ -93,8 +95,6 @@ class Merge { int16_t input_downsampled_[kInputDownsampLength]; AudioMultiVector expanded_; std::vector temp_data_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Merge); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/nack_tracker.cc b/modules/audio_coding/neteq/nack_tracker.cc index 9f04534af1..35afb736c8 100644 --- a/modules/audio_coding/neteq/nack_tracker.cc +++ b/modules/audio_coding/neteq/nack_tracker.cc @@ -10,38 +10,53 @@ #include "modules/audio_coding/neteq/nack_tracker.h" - #include #include #include "rtc_base/checks.h" +#include "rtc_base/experiments/struct_parameters_parser.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { const int kDefaultSampleRateKhz = 48; -const int kDefaultPacketSizeMs = 20; +const int kMaxPacketSizeMs = 120; +constexpr char kNackTrackerConfigFieldTrial[] = + "WebRTC-Audio-NetEqNackTrackerConfig"; } // namespace -NackTracker::NackTracker(int nack_threshold_packets) - : nack_threshold_packets_(nack_threshold_packets), - sequence_num_last_received_rtp_(0), +NackTracker::Config::Config() { + auto parser = StructParametersParser::Create( + "packet_loss_forget_factor", &packet_loss_forget_factor, + "ms_per_loss_percent", &ms_per_loss_percent, "never_nack_multiple_times", + &never_nack_multiple_times, "require_valid_rtt", &require_valid_rtt, + "max_loss_rate", &max_loss_rate); + parser->Parse( + webrtc::field_trial::FindFullName(kNackTrackerConfigFieldTrial)); + RTC_LOG(LS_INFO) << "Nack tracker config:" + " packet_loss_forget_factor=" + << packet_loss_forget_factor + << " ms_per_loss_percent=" << ms_per_loss_percent + << " never_nack_multiple_times=" << never_nack_multiple_times + << " require_valid_rtt=" << require_valid_rtt + << " max_loss_rate=" << max_loss_rate; +} + +NackTracker::NackTracker() + : sequence_num_last_received_rtp_(0), timestamp_last_received_rtp_(0), any_rtp_received_(false), sequence_num_last_decoded_rtp_(0), timestamp_last_decoded_rtp_(0), any_rtp_decoded_(false), sample_rate_khz_(kDefaultSampleRateKhz), - samples_per_packet_(sample_rate_khz_ * kDefaultPacketSizeMs), max_nack_list_size_(kNackListSizeLimit) {} NackTracker::~NackTracker() = default; -NackTracker* NackTracker::Create(int nack_threshold_packets) { - return new NackTracker(nack_threshold_packets); -} - void NackTracker::UpdateSampleRate(int sample_rate_hz) { RTC_DCHECK_GT(sample_rate_hz, 0); sample_rate_khz_ = sample_rate_hz / 1000; @@ -74,69 +89,62 @@ void NackTracker::UpdateLastReceivedPacket(uint16_t sequence_number, if (IsNewerSequenceNumber(sequence_num_last_received_rtp_, sequence_number)) return; - UpdateSamplesPerPacket(sequence_number, timestamp); + UpdatePacketLossRate(sequence_number - sequence_num_last_received_rtp_ - 1); - UpdateList(sequence_number); + UpdateList(sequence_number, timestamp); sequence_num_last_received_rtp_ = sequence_number; timestamp_last_received_rtp_ = timestamp; LimitNackListSize(); } -void NackTracker::UpdateSamplesPerPacket( +absl::optional NackTracker::GetSamplesPerPacket( uint16_t sequence_number_current_received_rtp, - uint32_t timestamp_current_received_rtp) { + uint32_t timestamp_current_received_rtp) const { uint32_t timestamp_increase = timestamp_current_received_rtp - timestamp_last_received_rtp_; uint16_t sequence_num_increase = sequence_number_current_received_rtp - sequence_num_last_received_rtp_; - samples_per_packet_ = timestamp_increase / sequence_num_increase; + int samples_per_packet = timestamp_increase / sequence_num_increase; + if (samples_per_packet == 0 || + samples_per_packet > kMaxPacketSizeMs * sample_rate_khz_) { + // Not a valid samples per packet. + return absl::nullopt; + } + return samples_per_packet; } -void NackTracker::UpdateList(uint16_t sequence_number_current_received_rtp) { - // Some of the packets which were considered late, now are considered missing. - ChangeFromLateToMissing(sequence_number_current_received_rtp); - - if (IsNewerSequenceNumber(sequence_number_current_received_rtp, - sequence_num_last_received_rtp_ + 1)) - AddToList(sequence_number_current_received_rtp); -} - -void NackTracker::ChangeFromLateToMissing( - uint16_t sequence_number_current_received_rtp) { - NackList::const_iterator lower_bound = - nack_list_.lower_bound(static_cast( - sequence_number_current_received_rtp - nack_threshold_packets_)); - - for (NackList::iterator it = nack_list_.begin(); it != lower_bound; ++it) - it->second.is_missing = true; -} - -uint32_t NackTracker::EstimateTimestamp(uint16_t sequence_num) { - uint16_t sequence_num_diff = sequence_num - sequence_num_last_received_rtp_; - return sequence_num_diff * samples_per_packet_ + timestamp_last_received_rtp_; -} - -void NackTracker::AddToList(uint16_t sequence_number_current_received_rtp) { +void NackTracker::UpdateList(uint16_t sequence_number_current_received_rtp, + uint32_t timestamp_current_received_rtp) { + if (!IsNewerSequenceNumber(sequence_number_current_received_rtp, + sequence_num_last_received_rtp_ + 1)) { + return; + } RTC_DCHECK(!any_rtp_decoded_ || IsNewerSequenceNumber(sequence_number_current_received_rtp, sequence_num_last_decoded_rtp_)); - // Packets with sequence numbers older than `upper_bound_missing` are - // considered missing, and the rest are considered late. - uint16_t upper_bound_missing = - sequence_number_current_received_rtp - nack_threshold_packets_; + absl::optional samples_per_packet = GetSamplesPerPacket( + sequence_number_current_received_rtp, timestamp_current_received_rtp); + if (!samples_per_packet) { + return; + } for (uint16_t n = sequence_num_last_received_rtp_ + 1; IsNewerSequenceNumber(sequence_number_current_received_rtp, n); ++n) { - bool is_missing = IsNewerSequenceNumber(upper_bound_missing, n); - uint32_t timestamp = EstimateTimestamp(n); - NackElement nack_element(TimeToPlay(timestamp), timestamp, is_missing); + uint32_t timestamp = EstimateTimestamp(n, *samples_per_packet); + NackElement nack_element(TimeToPlay(timestamp), timestamp); nack_list_.insert(nack_list_.end(), std::make_pair(n, nack_element)); } } +uint32_t NackTracker::EstimateTimestamp(uint16_t sequence_num, + int samples_per_packet) { + uint16_t sequence_num_diff = sequence_num - sequence_num_last_received_rtp_; + return sequence_num_diff * samples_per_packet + timestamp_last_received_rtp_; +} + void NackTracker::UpdateEstimatedPlayoutTimeBy10ms() { while (!nack_list_.empty() && nack_list_.begin()->second.time_to_play_ms <= 10) @@ -190,7 +198,6 @@ void NackTracker::Reset() { timestamp_last_decoded_rtp_ = 0; any_rtp_decoded_ = false; sample_rate_khz_ = kDefaultSampleRateKhz; - samples_per_packet_ = sample_rate_khz_ * kDefaultPacketSizeMs; } void NackTracker::SetMaxNackListSize(size_t max_nack_list_size) { @@ -215,17 +222,42 @@ int64_t NackTracker::TimeToPlay(uint32_t timestamp) const { } // We don't erase elements with time-to-play shorter than round-trip-time. -std::vector NackTracker::GetNackList( - int64_t round_trip_time_ms) const { +std::vector NackTracker::GetNackList(int64_t round_trip_time_ms) { RTC_DCHECK_GE(round_trip_time_ms, 0); std::vector sequence_numbers; + if (config_.require_valid_rtt && round_trip_time_ms == 0) { + return sequence_numbers; + } + if (packet_loss_rate_ > + static_cast(config_.max_loss_rate * (1 << 30))) { + return sequence_numbers; + } + // The estimated packet loss is between 0 and 1, so we need to multiply by 100 + // here. + int max_wait_ms = + 100.0 * config_.ms_per_loss_percent * packet_loss_rate_ / (1 << 30); for (NackList::const_iterator it = nack_list_.begin(); it != nack_list_.end(); ++it) { - if (it->second.is_missing && - it->second.time_to_play_ms > round_trip_time_ms) + int64_t time_since_packet_ms = + (timestamp_last_received_rtp_ - it->second.estimated_timestamp) / + sample_rate_khz_; + if (it->second.time_to_play_ms > round_trip_time_ms || + time_since_packet_ms + round_trip_time_ms < max_wait_ms) sequence_numbers.push_back(it->first); } + if (config_.never_nack_multiple_times) { + nack_list_.clear(); + } return sequence_numbers; } +void NackTracker::UpdatePacketLossRate(int packets_lost) { + const uint64_t alpha_q30 = (1 << 30) * config_.packet_loss_forget_factor; + // Exponential filter. + packet_loss_rate_ = (alpha_q30 * packet_loss_rate_) >> 30; + for (int i = 0; i < packets_lost; ++i) { + packet_loss_rate_ = + ((alpha_q30 * packet_loss_rate_) >> 30) + ((1 << 30) - alpha_q30); + } +} } // namespace webrtc diff --git a/modules/audio_coding/neteq/nack_tracker.h b/modules/audio_coding/neteq/nack_tracker.h index ac0a77f98f..0cc95b0882 100644 --- a/modules/audio_coding/neteq/nack_tracker.h +++ b/modules/audio_coding/neteq/nack_tracker.h @@ -17,6 +17,7 @@ #include #include +#include "absl/types/optional.h" #include "modules/include/module_common_types_public.h" #include "rtc_base/gtest_prod_util.h" @@ -30,12 +31,9 @@ // Every time 10ms audio is pulled from NetEq LastDecodedPacket() should be // called, and time-to-play is updated at that moment. // -// If packet N is received, any packet prior to |N - NackThreshold| which is not -// arrived is considered lost, and should be labeled as "missing" (the size of -// the list might be limited and older packet eliminated from the list). Packets -// |N - NackThreshold|, |N - NackThreshold + 1|, ..., |N - 1| are considered -// "late." A "late" packet with sequence number K is changed to "missing" any -// time a packet with sequence number newer than |K + NackList| is arrived. +// If packet N is received, any packet prior to N which has not arrived is +// considered lost, and should be labeled as "missing" (the size of +// the list might be limited and older packet eliminated from the list). // // The NackTracker class has to know about the sample rate of the packets to // compute time-to-play. So sample rate should be set as soon as the first @@ -56,9 +54,7 @@ class NackTracker { // A limit for the size of the NACK list. static const size_t kNackListSizeLimit = 500; // 10 seconds for 20 ms frame // packets. - // Factory method. - static NackTracker* Create(int nack_threshold_packets); - + NackTracker(); ~NackTracker(); // Set a maximum for the size of the NACK list. If the last received packet @@ -87,23 +83,42 @@ class NackTracker { // Get a list of "missing" packets which have expected time-to-play larger // than the given round-trip-time (in milliseconds). // Note: Late packets are not included. - std::vector GetNackList(int64_t round_trip_time_ms) const; + // Calling this method multiple times may give different results, since the + // internal nack list may get flushed if never_nack_multiple_times_ is true. + std::vector GetNackList(int64_t round_trip_time_ms); // Reset to default values. The NACK list is cleared. - // `nack_threshold_packets_` & `max_nack_list_size_` preserve their values. + // `max_nack_list_size_` preserves its value. void Reset(); + // Returns the estimated packet loss rate in Q30, for testing only. + uint32_t GetPacketLossRateForTest() { return packet_loss_rate_; } + private: // This test need to access the private method GetNackList(). FRIEND_TEST_ALL_PREFIXES(NackTrackerTest, EstimateTimestampAndTimeToPlay); + // Options that can be configured via field trial. + struct Config { + Config(); + + // The exponential decay factor used to estimate the packet loss rate. + double packet_loss_forget_factor = 0.996; + // How many additional ms we are willing to wait (at most) for nacked + // packets for each additional percentage of packet loss. + int ms_per_loss_percent = 20; + // If true, never nack packets more than once. + bool never_nack_multiple_times = false; + // Only nack if the RTT is valid. + bool require_valid_rtt = false; + // Do not nack if the loss rate is above this value. + double max_loss_rate = 1.0; + }; + struct NackElement { - NackElement(int64_t initial_time_to_play_ms, - uint32_t initial_timestamp, - bool missing) + NackElement(int64_t initial_time_to_play_ms, uint32_t initial_timestamp) : time_to_play_ms(initial_time_to_play_ms), - estimated_timestamp(initial_timestamp), - is_missing(missing) {} + estimated_timestamp(initial_timestamp) {} // Estimated time (ms) left for this packet to be decoded. This estimate is // updated every time jitter buffer decodes a packet. @@ -116,10 +131,6 @@ class NackTracker { // errors, there will be a minor misestimation in time-to-play of missing // packets. This will have a very minor effect on NACK performance. uint32_t estimated_timestamp; - - // True if the packet is considered missing. Otherwise indicates packet is - // late. - bool is_missing; }; class NackListCompare { @@ -132,35 +143,25 @@ class NackTracker { typedef std::map NackList; - // Constructor. - explicit NackTracker(int nack_threshold_packets); - // This API is used only for testing to assess whether time-to-play is // computed correctly. NackList GetNackList() const; - // Given the `sequence_number_current_received_rtp` of currently received RTP, - // recognize packets which are not arrive and add to the list. - void AddToList(uint16_t sequence_number_current_received_rtp); - // This function subtracts 10 ms of time-to-play for all packets in NACK list. // This is called when 10 ms elapsed with no new RTP packet decoded. void UpdateEstimatedPlayoutTimeBy10ms(); - // Given the `sequence_number_current_received_rtp` and - // `timestamp_current_received_rtp` of currently received RTP update number - // of samples per packet. - void UpdateSamplesPerPacket(uint16_t sequence_number_current_received_rtp, - uint32_t timestamp_current_received_rtp); + // Returns a valid number of samples per packet given the current received + // sequence number and timestamp or nullopt of none could be computed. + absl::optional GetSamplesPerPacket( + uint16_t sequence_number_current_received_rtp, + uint32_t timestamp_current_received_rtp) const; // Given the `sequence_number_current_received_rtp` of currently received RTP - // update the list. That is; some packets will change from late to missing, - // some packets are inserted as missing and some inserted as late. - void UpdateList(uint16_t sequence_number_current_received_rtp); - - // Packets which are considered late for too long (according to - // `nack_threshold_packets_`) are flagged as missing. - void ChangeFromLateToMissing(uint16_t sequence_number_current_received_rtp); + // update the list. Packets that are older than the received packet are added + // to the nack list. + void UpdateList(uint16_t sequence_number_current_received_rtp, + uint32_t timestamp_current_received_rtp); // Packets which have sequence number older that // `sequence_num_last_received_rtp_` - `max_nack_list_size_` are removed @@ -168,17 +169,15 @@ class NackTracker { void LimitNackListSize(); // Estimate timestamp of a missing packet given its sequence number. - uint32_t EstimateTimestamp(uint16_t sequence_number); + uint32_t EstimateTimestamp(uint16_t sequence_number, int samples_per_packet); // Compute time-to-play given a timestamp. int64_t TimeToPlay(uint32_t timestamp) const; - // If packet N is arrived, any packet prior to N - `nack_threshold_packets_` - // which is not arrived is considered missing, and should be in NACK list. - // Also any packet in the range of N-1 and N - `nack_threshold_packets_`, - // exclusive, which is not arrived is considered late, and should should be - // in the list of late packets. - const int nack_threshold_packets_; + // Updates the estimated packet lost rate. + void UpdatePacketLossRate(int packets_lost); + + const Config config_; // Valid if a packet is received. uint16_t sequence_num_last_received_rtp_; @@ -192,10 +191,6 @@ class NackTracker { int sample_rate_khz_; // Sample rate in kHz. - // Number of samples per packet. We update this every time we receive a - // packet, not only for consecutive packets. - int samples_per_packet_; - // A list of missing packets to be retransmitted. Components of the list // contain the sequence number of missing packets and the estimated time that // each pack is going to be played out. @@ -204,6 +199,9 @@ class NackTracker { // NACK list will not keep track of missing packets prior to // `sequence_num_last_received_rtp_` - `max_nack_list_size_`. size_t max_nack_list_size_; + + // Current estimate of the packet loss rate in Q30. + uint32_t packet_loss_rate_ = 0; }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/nack_tracker_unittest.cc b/modules/audio_coding/neteq/nack_tracker_unittest.cc index 3f5a05b1fc..bcc5120ff3 100644 --- a/modules/audio_coding/neteq/nack_tracker_unittest.cc +++ b/modules/audio_coding/neteq/nack_tracker_unittest.cc @@ -16,12 +16,12 @@ #include #include "modules/audio_coding/include/audio_coding_module_typedefs.h" +#include "test/field_trial.h" #include "test/gtest.h" namespace webrtc { namespace { -const int kNackThreshold = 3; const int kSampleRateHz = 16000; const int kPacketSizeMs = 30; const uint32_t kTimestampIncrement = 480; // 30 ms. @@ -54,55 +54,31 @@ bool IsNackListCorrect(const std::vector& nack_list, } // namespace TEST(NackTrackerTest, EmptyListWhenNoPacketLoss) { - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); int seq_num = 1; uint32_t timestamp = 0; std::vector nack_list; for (int n = 0; n < 100; n++) { - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); seq_num++; timestamp += kTimestampIncrement; - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); } } -TEST(NackTrackerTest, NoNackIfReorderWithinNackThreshold) { - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); - - int seq_num = 1; - uint32_t timestamp = 0; - std::vector nack_list; - - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); - EXPECT_TRUE(nack_list.empty()); - int num_late_packets = kNackThreshold + 1; - - // Push in reverse order - while (num_late_packets > 0) { - nack->UpdateLastReceivedPacket( - seq_num + num_late_packets, - timestamp + num_late_packets * kTimestampIncrement); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); - EXPECT_TRUE(nack_list.empty()); - num_late_packets--; - } -} - TEST(NackTrackerTest, LatePacketsMovedToNackThenNackListDoesNotChange) { const uint16_t kSequenceNumberLostPackets[] = {2, 3, 4, 5, 6, 7, 8, 9}; static const int kNumAllLostPackets = sizeof(kSequenceNumberLostPackets) / sizeof(kSequenceNumberLostPackets[0]); for (int k = 0; k < 2; k++) { // Two iteration with/without wrap around. - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); uint16_t sequence_num_lost_packets[kNumAllLostPackets]; for (int n = 0; n < kNumAllLostPackets; n++) { @@ -115,27 +91,25 @@ TEST(NackTrackerTest, LatePacketsMovedToNackThenNackListDoesNotChange) { uint32_t timestamp = 0; std::vector nack_list; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); seq_num = sequence_num_lost_packets[kNumAllLostPackets - 1] + 1; timestamp += kTimestampIncrement * (kNumAllLostPackets + 1); - int num_lost_packets = std::max(0, kNumAllLostPackets - kNackThreshold); + int num_lost_packets = std::max(0, kNumAllLostPackets); - for (int n = 0; n < kNackThreshold + 1; ++n) { - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); - EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, - num_lost_packets)); - seq_num++; - timestamp += kTimestampIncrement; - num_lost_packets++; - } + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); + EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, + num_lost_packets)); + seq_num++; + timestamp += kTimestampIncrement; + num_lost_packets++; for (int n = 0; n < 100; ++n) { - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, kNumAllLostPackets)); seq_num++; @@ -150,8 +124,8 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { sizeof(kSequenceNumberLostPackets[0]); for (int k = 0; k < 2; ++k) { // Two iteration with/without wrap around. - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); uint16_t sequence_num_lost_packets[kNumAllLostPackets]; for (int n = 0; n < kNumAllLostPackets; ++n) { @@ -162,8 +136,8 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { uint16_t seq_num = sequence_num_lost_packets[0] - 1; uint32_t timestamp = 0; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); size_t index_retransmitted_rtp = 0; @@ -171,16 +145,16 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { seq_num = sequence_num_lost_packets[kNumAllLostPackets - 1] + 1; timestamp += kTimestampIncrement * (kNumAllLostPackets + 1); - size_t num_lost_packets = std::max(0, kNumAllLostPackets - kNackThreshold); + size_t num_lost_packets = kNumAllLostPackets; for (int n = 0; n < kNumAllLostPackets; ++n) { // Number of lost packets does not change for the first // |kNackThreshold + 1| packets, one is added to the list and one is // removed. Thereafter, the list shrinks every iteration. - if (n >= kNackThreshold + 1) + if (n >= 1) num_lost_packets--; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(IsNackListCorrect( nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], num_lost_packets)); @@ -188,13 +162,13 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { timestamp += kTimestampIncrement; // Retransmission of a lost RTP. - nack->UpdateLastReceivedPacket( + nack.UpdateLastReceivedPacket( sequence_num_lost_packets[index_retransmitted_rtp], timestamp_retransmitted_rtp); index_retransmitted_rtp++; timestamp_retransmitted_rtp += kTimestampIncrement; - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(IsNackListCorrect( nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], num_lost_packets - 1)); // One less lost packet in the list. @@ -212,8 +186,8 @@ TEST(NackTrackerTest, EstimateTimestampAndTimeToPlay) { sizeof(kLostPackets) / sizeof(kLostPackets[0]); for (int k = 0; k < 4; ++k) { - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); // Sequence number wrap around if `k` is 2 or 3; int seq_num_offset = (k < 2) ? 0 : 65531; @@ -238,23 +212,23 @@ TEST(NackTrackerTest, EstimateTimestampAndTimeToPlay) { const uint32_t first_timestamp = timestamp; // Two consecutive packets to have a correct estimate of timestamp increase. - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); seq_num++; timestamp += kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); // A packet after the last one which is supposed to be lost. seq_num = seq_num_lost_packets[kNumAllLostPackets - 1] + 1; timestamp = timestamp_lost_packets[kNumAllLostPackets - 1] + kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); - NackTracker::NackList nack_list = nack->GetNackList(); + NackTracker::NackList nack_list = nack.GetNackList(); EXPECT_EQ(static_cast(kNumAllLostPackets), nack_list.size()); // Pretend the first packet is decoded. - nack->UpdateLastDecodedPacket(first_seq_num, first_timestamp); - nack_list = nack->GetNackList(); + nack.UpdateLastDecodedPacket(first_seq_num, first_timestamp); + nack_list = nack.GetNackList(); NackTracker::NackList::iterator it = nack_list.begin(); while (it != nack_list.end()) { @@ -268,8 +242,8 @@ TEST(NackTrackerTest, EstimateTimestampAndTimeToPlay) { // Pretend 10 ms is passed, and we had pulled audio from NetEq, it still // reports the same sequence number as decoded, time-to-play should be // updated by 10 ms. - nack->UpdateLastDecodedPacket(first_seq_num, first_timestamp); - nack_list = nack->GetNackList(); + nack.UpdateLastDecodedPacket(first_seq_num, first_timestamp); + nack_list = nack.GetNackList(); it = nack_list.begin(); while (it != nack_list.end()) { seq_num = it->first - seq_num_offset; @@ -284,77 +258,77 @@ TEST(NackTrackerTest, MissingPacketsPriorToLastDecodedRtpShouldNotBeInNackList) { for (int m = 0; m < 2; ++m) { uint16_t seq_num_offset = (m == 0) ? 0 : 65531; // Wrap around if `m` is 1. - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); // Two consecutive packets to have a correct estimate of timestamp increase. uint16_t seq_num = 0; - nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, - seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); seq_num++; - nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, - seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); // Skip 10 packets (larger than NACK threshold). const int kNumLostPackets = 10; seq_num += kNumLostPackets + 1; - nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, - seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); - const size_t kExpectedListSize = kNumLostPackets - kNackThreshold; - std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + const size_t kExpectedListSize = kNumLostPackets; + std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_EQ(kExpectedListSize, nack_list.size()); for (int k = 0; k < 2; ++k) { // Decoding of the first and the second arrived packets. for (int n = 0; n < kPacketSizeMs / 10; ++n) { - nack->UpdateLastDecodedPacket(seq_num_offset + k, - k * kTimestampIncrement); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastDecodedPacket(seq_num_offset + k, + k * kTimestampIncrement); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_EQ(kExpectedListSize, nack_list.size()); } } // Decoding of the last received packet. - nack->UpdateLastDecodedPacket(seq_num + seq_num_offset, - seq_num * kTimestampIncrement); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastDecodedPacket(seq_num + seq_num_offset, + seq_num * kTimestampIncrement); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); // Make sure list of late packets is also empty. To check that, push few // packets, if the late list is not empty its content will pop up in NACK // list. - for (int n = 0; n < kNackThreshold + 10; ++n) { + for (int n = 0; n < 10; ++n) { seq_num++; - nack->UpdateLastReceivedPacket(seq_num_offset + seq_num, - seq_num * kTimestampIncrement); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num_offset + seq_num, + seq_num * kTimestampIncrement); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); } } } TEST(NackTrackerTest, Reset) { - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); // Two consecutive packets to have a correct estimate of timestamp increase. uint16_t seq_num = 0; - nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); seq_num++; - nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); // Skip 10 packets (larger than NACK threshold). const int kNumLostPackets = 10; seq_num += kNumLostPackets + 1; - nack->UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); + nack.UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); - const size_t kExpectedListSize = kNumLostPackets - kNackThreshold; - std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + const size_t kExpectedListSize = kNumLostPackets; + std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_EQ(kExpectedListSize, nack_list.size()); - nack->Reset(); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.Reset(); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); } @@ -362,23 +336,23 @@ TEST(NackTrackerTest, ListSizeAppliedFromBeginning) { const size_t kNackListSize = 10; for (int m = 0; m < 2; ++m) { uint16_t seq_num_offset = (m == 0) ? 0 : 65525; // Wrap around if `m` is 1. - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); - nack->SetMaxNackListSize(kNackListSize); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); uint16_t seq_num = seq_num_offset; uint32_t timestamp = 0x12345678; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); // Packet lost more than NACK-list size limit. - uint16_t num_lost_packets = kNackThreshold + kNackListSize + 5; + uint16_t num_lost_packets = kNackListSize + 5; seq_num += num_lost_packets + 1; timestamp += (num_lost_packets + 1) * kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); - std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); - EXPECT_EQ(kNackListSize - kNackThreshold, nack_list.size()); + std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + EXPECT_EQ(kNackListSize, nack_list.size()); } } @@ -386,15 +360,15 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { const size_t kNackListSize = 10; for (int m = 0; m < 2; ++m) { uint16_t seq_num_offset = (m == 0) ? 0 : 65525; // Wrap around if `m` is 1. - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); uint16_t seq_num = seq_num_offset; uint32_t timestamp = 0x87654321; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); // Packet lost more than NACK-list size limit. - uint16_t num_lost_packets = kNackThreshold + kNackListSize + 5; + uint16_t num_lost_packets = kNackListSize + 5; std::unique_ptr seq_num_lost(new uint16_t[num_lost_packets]); for (int n = 0; n < num_lost_packets; ++n) { @@ -403,39 +377,26 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { ++seq_num; timestamp += (num_lost_packets + 1) * kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - size_t expected_size = num_lost_packets - kNackThreshold; + nack.UpdateLastReceivedPacket(seq_num, timestamp); + size_t expected_size = num_lost_packets; - std::vector nack_list = nack->GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_EQ(expected_size, nack_list.size()); - nack->SetMaxNackListSize(kNackListSize); - expected_size = kNackListSize - kNackThreshold; - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.SetMaxNackListSize(kNackListSize); + expected_size = kNackListSize; + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(IsNackListCorrect( nack_list, &seq_num_lost[num_lost_packets - kNackListSize], expected_size)); - // NACK list does not change size but the content is changing. The oldest - // element is removed and one from late list is inserted. - size_t n; - for (n = 1; n <= static_cast(kNackThreshold); ++n) { - ++seq_num; - timestamp += kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); - EXPECT_TRUE(IsNackListCorrect( - nack_list, &seq_num_lost[num_lost_packets - kNackListSize + n], - expected_size)); - } - // NACK list should shrink. - for (; n < kNackListSize; ++n) { + for (size_t n = 1; n < kNackListSize; ++n) { ++seq_num; timestamp += kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); --expected_size; - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(IsNackListCorrect( nack_list, &seq_num_lost[num_lost_packets - kNackListSize + n], expected_size)); @@ -444,28 +405,28 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { // After this packet, NACK list should be empty. ++seq_num; timestamp += kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack->GetNackList(kShortRoundTripTimeMs); + nack.UpdateLastReceivedPacket(seq_num, timestamp); + nack_list = nack.GetNackList(kShortRoundTripTimeMs); EXPECT_TRUE(nack_list.empty()); } } TEST(NackTrackerTest, RoudTripTimeIsApplied) { const int kNackListSize = 200; - std::unique_ptr nack(NackTracker::Create(kNackThreshold)); - nack->UpdateSampleRate(kSampleRateHz); - nack->SetMaxNackListSize(kNackListSize); + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); uint16_t seq_num = 0; uint32_t timestamp = 0x87654321; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); // Packet lost more than NACK-list size limit. - uint16_t kNumLostPackets = kNackThreshold + 5; + uint16_t kNumLostPackets = 5; seq_num += (1 + kNumLostPackets); timestamp += (1 + kNumLostPackets) * kTimestampIncrement; - nack->UpdateLastReceivedPacket(seq_num, timestamp); + nack.UpdateLastReceivedPacket(seq_num, timestamp); // Expected time-to-play are: // kPacketSizeMs - 10, 2*kPacketSizeMs - 10, 3*kPacketSizeMs - 10, ... @@ -473,10 +434,132 @@ TEST(NackTrackerTest, RoudTripTimeIsApplied) { // sequence number: 1, 2, 3, 4, 5 // time-to-play: 20, 50, 80, 110, 140 // - std::vector nack_list = nack->GetNackList(100); + std::vector nack_list = nack.GetNackList(100); ASSERT_EQ(2u, nack_list.size()); EXPECT_EQ(4, nack_list[0]); EXPECT_EQ(5, nack_list[1]); } +// Set never_nack_multiple_times to true with a field trial and verify that +// packets are not nacked multiple times. +TEST(NackTrackerTest, DoNotNackMultipleTimes) { + test::ScopedFieldTrials field_trials( + "WebRTC-Audio-NetEqNackTrackerConfig/" + "packet_loss_forget_factor:0.996,ms_per_loss_percent:20," + "never_nack_multiple_times:true/"); + const int kNackListSize = 200; + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); + + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + nack.UpdateLastReceivedPacket(seq_num, timestamp); + + uint16_t kNumLostPackets = 3; + + seq_num += (1 + kNumLostPackets); + timestamp += (1 + kNumLostPackets) * kTimestampIncrement; + nack.UpdateLastReceivedPacket(seq_num, timestamp); + + std::vector nack_list = nack.GetNackList(10); + ASSERT_EQ(3u, nack_list.size()); + EXPECT_EQ(1, nack_list[0]); + EXPECT_EQ(2, nack_list[1]); + EXPECT_EQ(3, nack_list[2]); + // When we get the nack list again, it should be empty. + std::vector nack_list2 = nack.GetNackList(10); + EXPECT_TRUE(nack_list2.empty()); +} + +// Test if estimated packet loss rate is correct. +TEST(NackTrackerTest, PacketLossRateCorrect) { + const int kNackListSize = 200; + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + auto add_packet = [&nack, &seq_num, ×tamp](bool received) { + if (received) { + nack.UpdateLastReceivedPacket(seq_num, timestamp); + } + seq_num++; + timestamp += kTimestampIncrement; + }; + // Add some packets, but every fourth packet is lost. + for (int i = 0; i < 300; i++) { + add_packet(true); + add_packet(true); + add_packet(true); + add_packet(false); + } + // 1 << 28 is 0.25 in Q30. We expect the packet loss estimate to be within + // 0.01 of that. + EXPECT_NEAR(nack.GetPacketLossRateForTest(), 1 << 28, (1 << 30) / 100); +} + +TEST(NackTrackerTest, DoNotNackAfterDtx) { + const int kNackListSize = 200; + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + nack.UpdateLastReceivedPacket(seq_num, timestamp); + EXPECT_TRUE(nack.GetNackList(0).empty()); + constexpr int kDtxPeriod = 400; + nack.UpdateLastReceivedPacket(seq_num + 2, + timestamp + kDtxPeriod * kSampleRateHz / 1000); + EXPECT_TRUE(nack.GetNackList(0).empty()); +} + +TEST(NackTrackerTest, DoNotNackIfLossRateIsTooHigh) { + test::ScopedFieldTrials field_trials( + "WebRTC-Audio-NetEqNackTrackerConfig/max_loss_rate:0.4/"); + const int kNackListSize = 200; + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + auto add_packet = [&nack, &seq_num, ×tamp](bool received) { + if (received) { + nack.UpdateLastReceivedPacket(seq_num, timestamp); + } + seq_num++; + timestamp += kTimestampIncrement; + }; + for (int i = 0; i < 500; i++) { + add_packet(true); + add_packet(false); + } + // Expect 50% loss rate which is higher that the configured maximum 40%. + EXPECT_NEAR(nack.GetPacketLossRateForTest(), 1 << 29, (1 << 30) / 100); + EXPECT_TRUE(nack.GetNackList(0).empty()); +} + +TEST(NackTrackerTest, OnlyNackIfRttIsValid) { + test::ScopedFieldTrials field_trials( + "WebRTC-Audio-NetEqNackTrackerConfig/require_valid_rtt:true/"); + const int kNackListSize = 200; + NackTracker nack; + nack.UpdateSampleRate(kSampleRateHz); + nack.SetMaxNackListSize(kNackListSize); + uint16_t seq_num = 0; + uint32_t timestamp = 0x87654321; + auto add_packet = [&nack, &seq_num, ×tamp](bool received) { + if (received) { + nack.UpdateLastReceivedPacket(seq_num, timestamp); + } + seq_num++; + timestamp += kTimestampIncrement; + }; + add_packet(true); + add_packet(false); + add_packet(true); + EXPECT_TRUE(nack.GetNackList(0).empty()); + EXPECT_FALSE(nack.GetNackList(10).empty()); +} + } // namespace webrtc diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index c7b98a5450..30886c3ace 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -10,12 +10,12 @@ #include "modules/audio_coding/neteq/neteq_impl.h" - #include #include #include #include #include +#include #include #include @@ -50,7 +50,6 @@ #include "rtc_base/strings/audio_format_to_string.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -71,24 +70,6 @@ std::unique_ptr CreateNetEqController( return controller_factory.CreateNetEqController(config); } -int GetDelayChainLengthMs(int config_extra_delay_ms) { - constexpr char kExtraDelayFieldTrial[] = "WebRTC-Audio-NetEqExtraDelay"; - if (webrtc::field_trial::IsEnabled(kExtraDelayFieldTrial)) { - const auto field_trial_string = - webrtc::field_trial::FindFullName(kExtraDelayFieldTrial); - int extra_delay_ms = -1; - if (sscanf(field_trial_string.c_str(), "Enabled-%d", &extra_delay_ms) == - 1 && - extra_delay_ms >= 0 && extra_delay_ms <= 2000) { - RTC_LOG(LS_INFO) << "Delay chain length set to " << extra_delay_ms - << " ms in field trial"; - return (extra_delay_ms / 10) * 10; // Rounding down to multiple of 10. - } - } - // Field trial not set, or invalid value read. Use value from config. - return config_extra_delay_ms; -} - } // namespace NetEqImpl::Dependencies::Dependencies( @@ -154,10 +135,7 @@ NetEqImpl::NetEqImpl(const NetEq::Config& config, speech_expand_uma_logger_("WebRTC.Audio.SpeechExpandRatePercent", 10, // Report once every 10 s. tick_timer_.get()), - no_time_stretching_(config.for_test_no_time_stretching), - output_delay_chain_ms_( - GetDelayChainLengthMs(config.extra_output_delay_ms)), - output_delay_chain_(rtc::CheckedDivExact(output_delay_chain_ms_, 10)) { + no_time_stretching_(config.for_test_no_time_stretching) { RTC_LOG(LS_INFO) << "NetEq config: " << config.ToString(); int fs = config.sample_rate_hz; if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) { @@ -243,7 +221,7 @@ void SetAudioFrameActivityAndType(bool vad_enabled, break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (!vad_enabled) { // Always set kVadUnknown when receive VAD is inactive. @@ -275,27 +253,8 @@ int NetEqImpl::GetAudio(AudioFrame* audio_frame, last_output_sample_rate_hz_ == 48000) << "Unexpected sample rate " << last_output_sample_rate_hz_; - if (!output_delay_chain_.empty()) { - if (output_delay_chain_empty_) { - for (auto& f : output_delay_chain_) { - f.CopyFrom(*audio_frame); - } - output_delay_chain_empty_ = false; - delayed_last_output_sample_rate_hz_ = last_output_sample_rate_hz_; - } else { - RTC_DCHECK_GE(output_delay_chain_ix_, 0); - RTC_DCHECK_LT(output_delay_chain_ix_, output_delay_chain_.size()); - swap(output_delay_chain_[output_delay_chain_ix_], *audio_frame); - *muted = audio_frame->muted(); - output_delay_chain_ix_ = - (output_delay_chain_ix_ + 1) % output_delay_chain_.size(); - delayed_last_output_sample_rate_hz_ = audio_frame->sample_rate_hz(); - } - } - if (current_sample_rate_hz) { - *current_sample_rate_hz = delayed_last_output_sample_rate_hz_.value_or( - last_output_sample_rate_hz_); + *current_sample_rate_hz = last_output_sample_rate_hz_; } return kOK; @@ -340,8 +299,7 @@ bool NetEqImpl::SetMinimumDelay(int delay_ms) { MutexLock lock(&mutex_); if (delay_ms >= 0 && delay_ms <= 10000) { RTC_DCHECK(controller_.get()); - return controller_->SetMinimumDelay( - std::max(delay_ms - output_delay_chain_ms_, 0)); + return controller_->SetMinimumDelay(delay_ms); } return false; } @@ -350,8 +308,7 @@ bool NetEqImpl::SetMaximumDelay(int delay_ms) { MutexLock lock(&mutex_); if (delay_ms >= 0 && delay_ms <= 10000) { RTC_DCHECK(controller_.get()); - return controller_->SetMaximumDelay( - std::max(delay_ms - output_delay_chain_ms_, 0)); + return controller_->SetMaximumDelay(delay_ms); } return false; } @@ -372,7 +329,7 @@ int NetEqImpl::GetBaseMinimumDelayMs() const { int NetEqImpl::TargetDelayMs() const { MutexLock lock(&mutex_); RTC_DCHECK(controller_.get()); - return controller_->TargetLevelMs() + output_delay_chain_ms_; + return controller_->TargetLevelMs(); } int NetEqImpl::FilteredCurrentDelayMs() const { @@ -382,8 +339,7 @@ int NetEqImpl::FilteredCurrentDelayMs() const { const int delay_samples = controller_->GetFilteredBufferLevel() + sync_buffer_->FutureLength(); // The division below will truncate. The return value is in ms. - return delay_samples / rtc::CheckedDivExact(fs_hz_, 1000) + - output_delay_chain_ms_; + return delay_samples / rtc::CheckedDivExact(fs_hz_, 1000); } int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) { @@ -391,11 +347,6 @@ int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) { RTC_DCHECK(decoder_database_.get()); *stats = CurrentNetworkStatisticsInternal(); stats_->GetNetworkStatistics(decoder_frame_length_, stats); - // Compensate for output delay chain. - stats->mean_waiting_time_ms += output_delay_chain_ms_; - stats->median_waiting_time_ms += output_delay_chain_ms_; - stats->min_waiting_time_ms += output_delay_chain_ms_; - stats->max_waiting_time_ms += output_delay_chain_ms_; return 0; } @@ -417,10 +368,6 @@ NetEqNetworkStatistics NetEqImpl::CurrentNetworkStatisticsInternal() const { RTC_DCHECK_GT(fs_hz_, 0); stats.current_buffer_size_ms = static_cast(total_samples_in_buffers * 1000 / fs_hz_); - - // Compensate for output delay chain. - stats.current_buffer_size_ms += output_delay_chain_ms_; - stats.preferred_buffer_size_ms += output_delay_chain_ms_; return stats; } @@ -464,19 +411,12 @@ absl::optional NetEqImpl::GetPlayoutTimestamp() const { // which is indicated by returning an empty value. return absl::nullopt; } - size_t sum_samples_in_output_delay_chain = 0; - for (const auto& audio_frame : output_delay_chain_) { - sum_samples_in_output_delay_chain += audio_frame.samples_per_channel(); - } - return timestamp_scaler_->ToExternal( - playout_timestamp_ - - static_cast(sum_samples_in_output_delay_chain)); + return timestamp_scaler_->ToExternal(playout_timestamp_); } int NetEqImpl::last_output_sample_rate_hz() const { MutexLock lock(&mutex_); - return delayed_last_output_sample_rate_hz_.value_or( - last_output_sample_rate_hz_); + return last_output_sample_rate_hz_; } absl::optional NetEqImpl::GetDecoderFormat( @@ -514,8 +454,7 @@ void NetEqImpl::FlushBuffers() { void NetEqImpl::EnableNack(size_t max_nack_list_size) { MutexLock lock(&mutex_); if (!nack_enabled_) { - const int kNackThresholdPackets = 2; - nack_.reset(NackTracker::Create(kNackThresholdPackets)); + nack_ = std::make_unique(); nack_enabled_ = true; nack_->UpdateSampleRate(fs_hz_); } @@ -859,6 +798,11 @@ int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame, RTC_DCHECK(audio_frame->muted()); // Reset() should mute the frame. playout_timestamp_ += static_cast(output_size_samples_); audio_frame->sample_rate_hz_ = fs_hz_; + // Make sure the total number of samples fits in the AudioFrame. + if (output_size_samples_ * sync_buffer_->Channels() > + AudioFrame::kMaxDataSizeSamples) { + return kSampleUnderrun; + } audio_frame->samples_per_channel_ = output_size_samples_; audio_frame->timestamp_ = first_packet_ @@ -955,7 +899,7 @@ int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame, } case Operation::kUndefined: { RTC_LOG(LS_ERROR) << "Invalid operation kUndefined."; - RTC_NOTREACHED(); // This should not happen. + RTC_DCHECK_NOTREACHED(); // This should not happen. last_mode_ = Mode::kError; return kInvalidOperation; } @@ -1118,7 +1062,7 @@ int NetEqImpl::GetDecision(Operation* operation, // Don't use this packet, discard it. if (packet_buffer_->DiscardNextPacket(stats_.get()) != PacketBuffer::kOK) { - RTC_NOTREACHED(); // Must be ok by design. + RTC_DCHECK_NOTREACHED(); // Must be ok by design. } // Check buffer again. if (!new_codec_) { @@ -1371,7 +1315,7 @@ int NetEqImpl::GetDecision(Operation* operation, } } - timestamp_ = end_timestamp; + timestamp_ = sync_buffer_->end_timestamp(); return 0; } @@ -1938,7 +1882,7 @@ int NetEqImpl::DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf) { // // it must be copied to the speech buffer. // // TODO(hlundin): This code seems incorrect. (Legacy.) Write test and // // verify correct operation. - // RTC_NOTREACHED(); + // RTC_DCHECK_NOTREACHED(); // // Must generate enough data to replace all of the `sync_buffer_` // // "future". // int required_length = sync_buffer_->FutureLength(); @@ -2028,7 +1972,8 @@ int NetEqImpl::ExtractPackets(size_t required_samples, next_packet = nullptr; if (!packet) { RTC_LOG(LS_ERROR) << "Should always be able to extract a packet here"; - RTC_NOTREACHED(); // Should always be able to extract a packet here. + RTC_DCHECK_NOTREACHED(); // Should always be able to extract a packet + // here. return -1; } const uint64_t waiting_time_ms = packet->waiting_time->ElapsedMs(); @@ -2062,7 +2007,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples, } else if (!has_cng_packet) { RTC_LOG(LS_WARNING) << "Unknown payload type " << static_cast(packet->payload_type); - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (packet_duration == 0) { @@ -2073,9 +2018,8 @@ int NetEqImpl::ExtractPackets(size_t required_samples, extracted_samples = packet->timestamp - first_timestamp + packet_duration; RTC_DCHECK(controller_); - stats_->JitterBufferDelay( - packet_duration, waiting_time_ms + output_delay_chain_ms_, - controller_->TargetLevelMs() + output_delay_chain_ms_); + stats_->JitterBufferDelay(packet_duration, waiting_time_ms, + controller_->TargetLevelMs()); packet_list->push_back(std::move(*packet)); // Store packet in list. packet = absl::nullopt; // Ensure it's never used after the move. diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h index 98bf0e23be..e2cd6c6054 100644 --- a/modules/audio_coding/neteq/neteq_impl.h +++ b/modules/audio_coding/neteq/neteq_impl.h @@ -29,7 +29,6 @@ #include "modules/audio_coding/neteq/packet.h" #include "modules/audio_coding/neteq/random_vector.h" #include "modules/audio_coding/neteq/statistics_calculator.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -124,6 +123,9 @@ class NetEqImpl : public webrtc::NetEq { ~NetEqImpl() override; + NetEqImpl(const NetEqImpl&) = delete; + NetEqImpl& operator=(const NetEqImpl&) = delete; + // Inserts a new packet into NetEq. Returns 0 on success, -1 on failure. int InsertPacket(const RTPHeader& rtp_header, rtc::ArrayView payload) override; @@ -399,25 +401,6 @@ class NetEqImpl : public webrtc::NetEq { ExpandUmaLogger speech_expand_uma_logger_ RTC_GUARDED_BY(mutex_); bool no_time_stretching_ RTC_GUARDED_BY(mutex_); // Only used for test. rtc::BufferT concealment_audio_ RTC_GUARDED_BY(mutex_); - // Data members used for adding extra delay to the output of NetEq. - // The delay in ms (which is 10 times the number of elements in - // output_delay_chain_). - const int output_delay_chain_ms_ RTC_GUARDED_BY(mutex_); - // Vector of AudioFrames which contains the delayed audio. Accessed as a - // circular buffer. - std::vector output_delay_chain_ RTC_GUARDED_BY(mutex_); - // Index into output_delay_chain_. - size_t output_delay_chain_ix_ RTC_GUARDED_BY(mutex_) = 0; - // Did output_delay_chain_ get populated yet? - bool output_delay_chain_empty_ RTC_GUARDED_BY(mutex_) = true; - // Contains the sample rate of the AudioFrame last emitted from the delay - // chain. If the extra output delay chain is not used, or if no audio has been - // emitted yet, the variable is empty. - absl::optional delayed_last_output_sample_rate_hz_ - RTC_GUARDED_BY(mutex_); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(NetEqImpl); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc index 7010adfa40..b39a880292 100644 --- a/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -18,6 +18,7 @@ #include "api/neteq/default_neteq_controller_factory.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_controller.h" +#include "modules/audio_coding/codecs/g711/audio_decoder_pcm.h" #include "modules/audio_coding/neteq/accelerate.h" #include "modules/audio_coding/neteq/decision_logic.h" #include "modules/audio_coding/neteq/default_neteq_factory.h" @@ -75,6 +76,7 @@ class NetEqImplTest : public ::testing::Test { void CreateInstance( const rtc::scoped_refptr& decoder_factory) { ASSERT_TRUE(decoder_factory); + config_.enable_muted_state = enable_muted_state_; NetEqImpl::Dependencies deps(config_, &clock_, decoder_factory, DefaultNetEqControllerFactory()); @@ -245,6 +247,7 @@ class NetEqImplTest : public ::testing::Test { MockRedPayloadSplitter* mock_payload_splitter_ = nullptr; RedPayloadSplitter* red_payload_splitter_ = nullptr; bool use_mock_payload_splitter_ = true; + bool enable_muted_state_ = false; }; // This tests the interface class NetEq. @@ -1026,22 +1029,37 @@ TEST_F(NetEqImplTest, CodecInternalCng) { EXPECT_CALL(mock_decoder, PacketDuration(nullptr, 0)) .WillRepeatedly(Return(rtc::checked_cast(kPayloadLengthSamples))); - // Pointee(x) verifies that first byte of the payload equals x, this makes it - // possible to verify that the correct payload is fed to Decode(). - EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(0), kPayloadLengthBytes, - kSampleRateKhz * 1000, _, _)) - .WillOnce(DoAll(SetArrayArgument<3>(dummy_output, - dummy_output + kPayloadLengthSamples), - SetArgPointee<4>(AudioDecoder::kSpeech), - Return(rtc::checked_cast(kPayloadLengthSamples)))); + EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType, + SdpAudioFormat("opus", 48000, 2))); - EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(1), kPayloadLengthBytes, - kSampleRateKhz * 1000, _, _)) - .WillOnce(DoAll(SetArrayArgument<3>(dummy_output, - dummy_output + kPayloadLengthSamples), - SetArgPointee<4>(AudioDecoder::kComfortNoise), - Return(rtc::checked_cast(kPayloadLengthSamples)))); + struct Packet { + int sequence_number_delta; + int timestamp_delta; + AudioDecoder::SpeechType decoder_output_type; + }; + std::vector packets = { + {0, 0, AudioDecoder::kSpeech}, + {1, kPayloadLengthSamples, AudioDecoder::kComfortNoise}, + {2, 2 * kPayloadLengthSamples, AudioDecoder::kSpeech}, + {1, kPayloadLengthSamples, AudioDecoder::kSpeech}}; + for (size_t i = 0; i < packets.size(); ++i) { + rtp_header.sequenceNumber += packets[i].sequence_number_delta; + rtp_header.timestamp += packets[i].timestamp_delta; + payload[0] = i; + EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload)); + + // Pointee(x) verifies that first byte of the payload equals x, this makes + // it possible to verify that the correct payload is fed to Decode(). + EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(i), kPayloadLengthBytes, + kSampleRateKhz * 1000, _, _)) + .WillOnce(DoAll(SetArrayArgument<3>( + dummy_output, dummy_output + kPayloadLengthSamples), + SetArgPointee<4>(packets[i].decoder_output_type), + Return(rtc::checked_cast(kPayloadLengthSamples)))); + } + + // Expect comfort noise to be returned by the decoder. EXPECT_CALL(mock_decoder, DecodeInternal(IsNull(), 0, kSampleRateKhz * 1000, _, _)) .WillOnce(DoAll(SetArrayArgument<3>(dummy_output, @@ -1049,87 +1067,24 @@ TEST_F(NetEqImplTest, CodecInternalCng) { SetArgPointee<4>(AudioDecoder::kComfortNoise), Return(rtc::checked_cast(kPayloadLengthSamples)))); - EXPECT_CALL(mock_decoder, DecodeInternal(Pointee(2), kPayloadLengthBytes, - kSampleRateKhz * 1000, _, _)) - .WillOnce(DoAll(SetArrayArgument<3>(dummy_output, - dummy_output + kPayloadLengthSamples), - SetArgPointee<4>(AudioDecoder::kSpeech), - Return(rtc::checked_cast(kPayloadLengthSamples)))); + std::vector expected_output = { + AudioFrame::kNormalSpeech, AudioFrame::kCNG, AudioFrame::kNormalSpeech}; + size_t output_index = 0; - EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType, - SdpAudioFormat("opus", 48000, 2))); - - const size_t kMaxOutputSize = static_cast(10 * kSampleRateKhz); - AudioFrame output; - AudioFrame::SpeechType expected_type[8] = { - AudioFrame::kNormalSpeech, AudioFrame::kNormalSpeech, AudioFrame::kCNG, - AudioFrame::kCNG, AudioFrame::kCNG, AudioFrame::kCNG, - AudioFrame::kNormalSpeech, AudioFrame::kNormalSpeech}; - int expected_timestamp_increment[8] = { - -1, // will not be used. - 10 * kSampleRateKhz, - -1, - -1, // timestamp will be empty during CNG mode; indicated by -1 here. - -1, - -1, - 50 * kSampleRateKhz, - 10 * kSampleRateKhz}; - - // Insert one packet (decoder will return speech). - EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload)); - - bool muted; - EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); - absl::optional last_timestamp = neteq_->GetPlayoutTimestamp(); - ASSERT_TRUE(last_timestamp); - - // Insert second packet (decoder will return CNG). - payload[0] = 1; - rtp_header.sequenceNumber++; - rtp_header.timestamp += kPayloadLengthSamples; - EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload)); - - // Lambda for verifying the timestamps. - auto verify_timestamp = [&last_timestamp, &expected_timestamp_increment]( - absl::optional ts, size_t i) { - if (expected_timestamp_increment[i] == -1) { - // Expect to get an empty timestamp value during CNG and PLC. - EXPECT_FALSE(ts) << "i = " << i; + int timeout_counter = 0; + while (!packet_buffer_->Empty()) { + ASSERT_LT(timeout_counter++, 20) << "Test timed out"; + AudioFrame output; + bool muted; + EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); + if (output_index + 1 < expected_output.size() && + output.speech_type_ == expected_output[output_index + 1]) { + ++output_index; } else { - ASSERT_TRUE(ts) << "i = " << i; - EXPECT_EQ(*ts, *last_timestamp + expected_timestamp_increment[i]) - << "i = " << i; - last_timestamp = ts; + EXPECT_EQ(output.speech_type_, expected_output[output_index]); } - }; - - for (size_t i = 1; i < 6; ++i) { - ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_); - EXPECT_EQ(1u, output.num_channels_); - EXPECT_EQ(expected_type[i - 1], output.speech_type_); - EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); - SCOPED_TRACE(""); - verify_timestamp(neteq_->GetPlayoutTimestamp(), i); } - // Insert third packet, which leaves a gap from last packet. - payload[0] = 2; - rtp_header.sequenceNumber += 2; - rtp_header.timestamp += 2 * kPayloadLengthSamples; - EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload)); - - for (size_t i = 6; i < 8; ++i) { - ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_); - EXPECT_EQ(1u, output.num_channels_); - EXPECT_EQ(expected_type[i - 1], output.speech_type_); - EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); - SCOPED_TRACE(""); - verify_timestamp(neteq_->GetPlayoutTimestamp(), i); - } - - // Now check the packet buffer, and make sure it is empty. - EXPECT_TRUE(packet_buffer_->Empty()); - EXPECT_CALL(mock_decoder, Die()); } @@ -1649,6 +1604,69 @@ TEST_F(NetEqImplTest, NotifyControllerOfReorderedPacket) { EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload)); } +// When using a codec with 1000 channels, there should be no crashes. +TEST_F(NetEqImplTest, NoCrashWith1000Channels) { + using ::testing::AllOf; + using ::testing::Field; + UseNoMocks(); + use_mock_decoder_database_ = true; + enable_muted_state_ = true; + CreateInstance(); + const size_t kPayloadLength = 100; + const uint8_t kPayloadType = 0; + const uint16_t kFirstSequenceNumber = 0x1234; + const uint32_t kFirstTimestamp = 0x12345678; + const uint32_t kSsrc = 0x87654321; + uint8_t payload[kPayloadLength] = {0}; + RTPHeader rtp_header; + rtp_header.payloadType = kPayloadType; + rtp_header.sequenceNumber = kFirstSequenceNumber; + rtp_header.timestamp = kFirstTimestamp; + rtp_header.ssrc = kSsrc; + Packet fake_packet; + fake_packet.payload_type = kPayloadType; + fake_packet.sequence_number = kFirstSequenceNumber; + fake_packet.timestamp = kFirstTimestamp; + + AudioDecoder* decoder = nullptr; + + auto mock_decoder_factory = rtc::make_ref_counted(); + EXPECT_CALL(*mock_decoder_factory, MakeAudioDecoderMock(_, _, _)) + .WillOnce(Invoke([&](const SdpAudioFormat& format, + absl::optional codec_pair_id, + std::unique_ptr* dec) { + EXPECT_EQ("pcmu", format.name); + *dec = std::make_unique(1000); + decoder = dec->get(); + })); + DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), + absl::nullopt, mock_decoder_factory); + // Expectations for decoder database. + EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType)) + .WillRepeatedly(Return(&info)); + EXPECT_CALL(*mock_decoder_database_, GetActiveCngDecoder()) + .WillRepeatedly(ReturnNull()); + EXPECT_CALL(*mock_decoder_database_, GetActiveDecoder()) + .WillRepeatedly(Return(decoder)); + EXPECT_CALL(*mock_decoder_database_, SetActiveDecoder(_, _)) + .WillOnce(Invoke([](uint8_t rtp_payload_type, bool* new_decoder) { + *new_decoder = true; + return 0; + })); + + // Insert first packet. + neteq_->InsertPacket(rtp_header, payload); + + AudioFrame audio_frame; + bool muted; + + // Repeat 40 times to ensure we enter muted state. + for (int i = 0; i < 40; i++) { + // GetAudio should return an error, and not crash, even in muted state. + EXPECT_NE(0, neteq_->GetAudio(&audio_frame, &muted)); + } +} + class Decoder120ms : public AudioDecoder { public: Decoder120ms(int sample_rate_hz, SpeechType speech_type) diff --git a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc index 862edaf606..2c68501a98 100644 --- a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc +++ b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc @@ -274,7 +274,7 @@ class NetEqNetworkStatsTest { // Next we introduce packet losses. SetPacketLossRate(0.1); - expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 1065; + expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 898; RunTest(50, expects); // Next we enable FEC. diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc index 5ce6b890d3..4ff1a431e1 100644 --- a/modules/audio_coding/neteq/neteq_unittest.cc +++ b/modules/audio_coding/neteq/neteq_unittest.cc @@ -44,36 +44,10 @@ ABSL_FLAG(bool, gen_ref, false, "Generate reference files."); namespace webrtc { -namespace { - -const std::string& PlatformChecksum(const std::string& checksum_general, - const std::string& checksum_android_32, - const std::string& checksum_android_64, - const std::string& checksum_win_32, - const std::string& checksum_win_64) { -#if defined(WEBRTC_ANDROID) -#ifdef WEBRTC_ARCH_64_BITS - return checksum_android_64; -#else - return checksum_android_32; -#endif // WEBRTC_ARCH_64_BITS -#elif defined(WEBRTC_WIN) -#ifdef WEBRTC_ARCH_64_BITS - return checksum_win_64; -#else - return checksum_win_32; -#endif // WEBRTC_ARCH_64_BITS -#else - return checksum_general; -#endif // WEBRTC_WIN -} - -} // namespace - - -#if !defined(WEBRTC_IOS) && defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && \ - (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) && \ - defined(WEBRTC_CODEC_ILBC) && !defined(WEBRTC_ARCH_ARM64) +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) && \ + defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && \ + (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) && \ + defined(WEBRTC_CODEC_ILBC) #define MAYBE_TestBitExactness TestBitExactness #else #define MAYBE_TestBitExactness DISABLED_TestBitExactness @@ -82,79 +56,57 @@ TEST_F(NetEqDecodingTest, MAYBE_TestBitExactness) { const std::string input_rtp_file = webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"); - const std::string output_checksum = PlatformChecksum( -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) && defined(WEBRTC_ARCH_X86) - "8d9c177b7f2f9398c0944a851edffae214de2c56", -#else - "6c35140ce4d75874bdd60aa1872400b05fd05ca2", -#endif - "ab451bb8301d9a92fbf4de91556b56f1ea38b4ce", "not used", - "6c35140ce4d75874bdd60aa1872400b05fd05ca2", - "64b46bb3c1165537a880ae8404afce2efba456c0"); + const std::string output_checksum = + "ba4fae83a52f5e9d95b0910f05d540114285697b"; - const std::string network_stats_checksum = PlatformChecksum( -// TODO(bugs.webrtc.org/12941): Linux x86 optimized builds have a different -// checksum. -#if defined(WEBRTC_LINUX) && defined(NDEBUG) && defined(WEBRTC_ARCH_X86) - "8cc08e3cd6801dcba4fcc15eb4036c19296a140d", -#else - "90594d85fa31d3d9584d79293bf7aa4ee55ed751", -#endif - "77b9c3640b81aff6a38d69d07dd782d39c15321d", "not used", - "90594d85fa31d3d9584d79293bf7aa4ee55ed751", - "90594d85fa31d3d9584d79293bf7aa4ee55ed751"); + const std::string network_stats_checksum = + "fa878a8464ef1cb3d01503b7f927c3e2ce6f02c4"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); } -#if !defined(WEBRTC_IOS) && defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && \ - defined(WEBRTC_CODEC_OPUS) +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) && \ + defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && defined(WEBRTC_CODEC_OPUS) #define MAYBE_TestOpusBitExactness TestOpusBitExactness #else #define MAYBE_TestOpusBitExactness DISABLED_TestOpusBitExactness #endif -// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been -// updated. -TEST_F(NetEqDecodingTest, DISABLED_TestOpusBitExactness) { +TEST_F(NetEqDecodingTest, MAYBE_TestOpusBitExactness) { const std::string input_rtp_file = webrtc::test::ResourcePath("audio_coding/neteq_opus", "rtp"); - const std::string maybe_sse = - "c7887ff60eecf460332c6c7a28c81561f9e8a40f" - "|673dd422cfc174152536d3b13af64f9722520ab5"; - const std::string output_checksum = PlatformChecksum( - maybe_sse, "e39283dd61a89cead3786ef8642d2637cc447296", - "53d8073eb848b70974cba9e26424f4946508fd19", maybe_sse, maybe_sse); + // The checksum depends on SSE being enabled, the second part is the non-SSE + // checksum. + const std::string output_checksum = + "6e23d8827ae54ca352e1448ae363bdfd2878c78e|" + "47cddbf3494b0233f48cb350676e704807237545"; const std::string network_stats_checksum = - PlatformChecksum("c438bfa3b018f77691279eb9c63730569f54585c", - "8a474ed0992591e0c84f593824bb05979c3de157", - "9a05378dbf7e6edd56cdeb8ec45bcd6d8589623c", - "c438bfa3b018f77691279eb9c63730569f54585c", - "c438bfa3b018f77691279eb9c63730569f54585c"); + "f89a9533dbb35a4c449b44c3ed120f7f1c7f90b6"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); } -// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been -// updated. -TEST_F(NetEqDecodingTest, DISABLED_TestOpusDtxBitExactness) { +#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64) && \ + defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && defined(WEBRTC_CODEC_OPUS) +#define MAYBE_TestOpusDtxBitExactness TestOpusDtxBitExactness +#else +#define MAYBE_TestOpusDtxBitExactness DISABLED_TestOpusDtxBitExactness +#endif +TEST_F(NetEqDecodingTest, MAYBE_TestOpusDtxBitExactness) { const std::string input_rtp_file = webrtc::test::ResourcePath("audio_coding/neteq_opus_dtx", "rtp"); - const std::string maybe_sse = - "0fb0a3d6b3758ca6e108368bb777cd38d0a865af" - "|79cfb99a21338ba977eb0e15eb8464e2db9436f8"; - const std::string output_checksum = PlatformChecksum( - maybe_sse, "b6632690f8d7c2340c838df2821fc014f1cc8360", - "f890b9eb9bc5ab8313489230726b297f6a0825af", maybe_sse, maybe_sse); + // The checksum depends on SSE being enabled, the second part is the non-SSE + // checksum. + const std::string output_checksum = + "5cea4a8e750842ac67b79e8e2ce6a0a1c01f8130|" + "e97e32a77355e7ce46a2dc2f43bf1c2805530fcb"; const std::string network_stats_checksum = - "18983bb67a57628c604dbdefa99574c6e0c5bb48"; + "dc8447b9fee1a21fd5d1f4045d62b982a3fb0215"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); @@ -384,7 +336,6 @@ class NetEqBgnTest : public NetEqDecodingTest { PopulateRtpInfo(0, 0, &rtp_info); rtp_info.payloadType = payload_type; - uint32_t receive_timestamp = 0; bool muted; for (int n = 0; n < 10; ++n) { // Insert few packets and get audio. auto block = input.GetNextBlock(); @@ -405,8 +356,6 @@ class NetEqBgnTest : public NetEqDecodingTest { rtp_info.timestamp += rtc::checked_cast(expected_samples_per_channel); rtp_info.sequenceNumber++; - receive_timestamp += - rtc::checked_cast(expected_samples_per_channel); } output.Reset(); @@ -546,11 +495,16 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { out_frame_.timestamp_ + out_frame_.samples_per_channel_); } - // Insert speech again. ++seq_no; timestamp += kCngPeriodSamples; - PopulateRtpInfo(seq_no, timestamp, &rtp_info); - ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload)); + uint32_t first_speech_timestamp = timestamp; + // Insert speech again. + for (int i = 0; i < 3; ++i) { + PopulateRtpInfo(seq_no, timestamp, &rtp_info); + ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload)); + ++seq_no; + timestamp += kSamples; + } // Pull audio once and verify that the output is speech again. ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted)); @@ -558,7 +512,7 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { EXPECT_EQ(AudioFrame::kNormalSpeech, out_frame_.speech_type_); absl::optional playout_timestamp = neteq_->GetPlayoutTimestamp(); ASSERT_TRUE(playout_timestamp); - EXPECT_EQ(timestamp + kSamples - algorithmic_delay_samples, + EXPECT_EQ(first_speech_timestamp + kSamples - algorithmic_delay_samples, *playout_timestamp); } @@ -1115,186 +1069,5 @@ TEST(NetEqNoTimeStretchingMode, RunTest) { EXPECT_EQ(0, stats.preemptive_rate); } -namespace { -// Helper classes and data types and functions for NetEqOutputDelayTest. - -class VectorAudioSink : public AudioSink { - public: - // Does not take ownership of the vector. - VectorAudioSink(std::vector* output_vector) : v_(output_vector) {} - - virtual ~VectorAudioSink() = default; - - bool WriteArray(const int16_t* audio, size_t num_samples) override { - v_->reserve(v_->size() + num_samples); - for (size_t i = 0; i < num_samples; ++i) { - v_->push_back(audio[i]); - } - return true; - } - - private: - std::vector* const v_; -}; - -struct TestResult { - NetEqLifetimeStatistics lifetime_stats; - NetEqNetworkStatistics network_stats; - absl::optional playout_timestamp; - int target_delay_ms; - int filtered_current_delay_ms; - int sample_rate_hz; -}; - -// This class is used as callback object to NetEqTest to collect some stats -// at the end of the simulation. -class SimEndStatsCollector : public NetEqSimulationEndedCallback { - public: - SimEndStatsCollector(TestResult& result) : result_(result) {} - - void SimulationEnded(int64_t /*simulation_time_ms*/, NetEq* neteq) override { - result_.playout_timestamp = neteq->GetPlayoutTimestamp(); - result_.target_delay_ms = neteq->TargetDelayMs(); - result_.filtered_current_delay_ms = neteq->FilteredCurrentDelayMs(); - result_.sample_rate_hz = neteq->last_output_sample_rate_hz(); - } - - private: - TestResult& result_; -}; - -TestResult DelayLineNetEqTest(int delay_ms, - std::vector* output_vector) { - NetEq::Config config; - config.for_test_no_time_stretching = true; - config.extra_output_delay_ms = delay_ms; - auto codecs = NetEqTest::StandardDecoderMap(); - NetEqPacketSourceInput::RtpHeaderExtensionMap rtp_ext_map = { - {1, kRtpExtensionAudioLevel}, - {3, kRtpExtensionAbsoluteSendTime}, - {5, kRtpExtensionTransportSequenceNumber}, - {7, kRtpExtensionVideoContentType}, - {8, kRtpExtensionVideoTiming}}; - std::unique_ptr input = std::make_unique( - webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"), - rtp_ext_map, absl::nullopt /*No SSRC filter*/); - std::unique_ptr input_time_limit( - new TimeLimitedNetEqInput(std::move(input), 10000)); - std::unique_ptr output = - std::make_unique(output_vector); - - TestResult result; - SimEndStatsCollector stats_collector(result); - NetEqTest::Callbacks callbacks; - callbacks.simulation_ended_callback = &stats_collector; - - NetEqTest test(config, CreateBuiltinAudioDecoderFactory(), codecs, - /*text_log=*/nullptr, /*neteq_factory=*/nullptr, - /*input=*/std::move(input_time_limit), std::move(output), - callbacks); - test.Run(); - result.lifetime_stats = test.LifetimeStats(); - result.network_stats = test.SimulationStats(); - return result; -} -} // namespace - -// Tests the extra output delay functionality of NetEq. -TEST(NetEqOutputDelayTest, RunTest) { - std::vector output; - const auto result_no_delay = DelayLineNetEqTest(0, &output); - std::vector output_delayed; - constexpr int kDelayMs = 100; - const auto result_delay = DelayLineNetEqTest(kDelayMs, &output_delayed); - - // Verify that the loss concealment remains unchanged. The point of the delay - // is to not affect the jitter buffering behavior. - // First verify that there are concealments in the test. - EXPECT_GT(result_no_delay.lifetime_stats.concealed_samples, 0u); - // And that not all of the output is concealment. - EXPECT_GT(result_no_delay.lifetime_stats.total_samples_received, - result_no_delay.lifetime_stats.concealed_samples); - // Now verify that they remain unchanged by the delay. - EXPECT_EQ(result_no_delay.lifetime_stats.concealed_samples, - result_delay.lifetime_stats.concealed_samples); - // Accelerate and pre-emptive expand should also be unchanged. - EXPECT_EQ(result_no_delay.lifetime_stats.inserted_samples_for_deceleration, - result_delay.lifetime_stats.inserted_samples_for_deceleration); - EXPECT_EQ(result_no_delay.lifetime_stats.removed_samples_for_acceleration, - result_delay.lifetime_stats.removed_samples_for_acceleration); - // Verify that delay stats are increased with the delay chain. - EXPECT_EQ( - result_no_delay.lifetime_stats.jitter_buffer_delay_ms + - kDelayMs * result_no_delay.lifetime_stats.jitter_buffer_emitted_count, - result_delay.lifetime_stats.jitter_buffer_delay_ms); - EXPECT_EQ( - result_no_delay.lifetime_stats.jitter_buffer_target_delay_ms + - kDelayMs * result_no_delay.lifetime_stats.jitter_buffer_emitted_count, - result_delay.lifetime_stats.jitter_buffer_target_delay_ms); - EXPECT_EQ(result_no_delay.network_stats.current_buffer_size_ms + kDelayMs, - result_delay.network_stats.current_buffer_size_ms); - EXPECT_EQ(result_no_delay.network_stats.preferred_buffer_size_ms + kDelayMs, - result_delay.network_stats.preferred_buffer_size_ms); - EXPECT_EQ(result_no_delay.network_stats.mean_waiting_time_ms + kDelayMs, - result_delay.network_stats.mean_waiting_time_ms); - EXPECT_EQ(result_no_delay.network_stats.median_waiting_time_ms + kDelayMs, - result_delay.network_stats.median_waiting_time_ms); - EXPECT_EQ(result_no_delay.network_stats.min_waiting_time_ms + kDelayMs, - result_delay.network_stats.min_waiting_time_ms); - EXPECT_EQ(result_no_delay.network_stats.max_waiting_time_ms + kDelayMs, - result_delay.network_stats.max_waiting_time_ms); - - ASSERT_TRUE(result_no_delay.playout_timestamp); - ASSERT_TRUE(result_delay.playout_timestamp); - EXPECT_EQ(*result_no_delay.playout_timestamp - - static_cast( - kDelayMs * - rtc::CheckedDivExact(result_no_delay.sample_rate_hz, 1000)), - *result_delay.playout_timestamp); - EXPECT_EQ(result_no_delay.target_delay_ms + kDelayMs, - result_delay.target_delay_ms); - EXPECT_EQ(result_no_delay.filtered_current_delay_ms + kDelayMs, - result_delay.filtered_current_delay_ms); - - // Verify expected delay in decoded signal. The test vector uses 8 kHz sample - // rate, so the delay will be 8 times the delay in ms. - constexpr size_t kExpectedDelaySamples = kDelayMs * 8; - for (size_t i = 0; - i < output.size() && i + kExpectedDelaySamples < output_delayed.size(); - ++i) { - EXPECT_EQ(output[i], output_delayed[i + kExpectedDelaySamples]); - } -} - -// Tests the extra output delay functionality of NetEq when configured via -// field trial. -TEST(NetEqOutputDelayTest, RunTestWithFieldTrial) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-NetEqExtraDelay/Enabled-50/"); - constexpr int kExpectedDelayMs = 50; - std::vector output; - const auto result = DelayLineNetEqTest(0, &output); - - // The base delay values are taken from the resuts of the non-delayed case in - // NetEqOutputDelayTest.RunTest above. - EXPECT_EQ(20 + kExpectedDelayMs, result.target_delay_ms); - EXPECT_EQ(24 + kExpectedDelayMs, result.filtered_current_delay_ms); -} - -// Set a non-multiple-of-10 value in the field trial, and verify that we don't -// crash, and that the result is rounded down. -TEST(NetEqOutputDelayTest, RunTestWithFieldTrialOddValue) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-NetEqExtraDelay/Enabled-103/"); - constexpr int kRoundedDelayMs = 100; - std::vector output; - const auto result = DelayLineNetEqTest(0, &output); - - // The base delay values are taken from the resuts of the non-delayed case in - // NetEqOutputDelayTest.RunTest above. - EXPECT_EQ(20 + kRoundedDelayMs, result.target_delay_ms); - EXPECT_EQ(24 + kRoundedDelayMs, result.filtered_current_delay_ms); -} - } // namespace test } // namespace webrtc diff --git a/modules/audio_coding/neteq/normal.cc b/modules/audio_coding/neteq/normal.cc index 6ffae0975f..461ee7fa4a 100644 --- a/modules/audio_coding/neteq/normal.cc +++ b/modules/audio_coding/neteq/normal.cc @@ -159,7 +159,7 @@ int Normal::Process(const int16_t* input, if (cng_decoder) { // Generate long enough for 48kHz. - if (!cng_decoder->Generate(cng_output, 0)) { + if (!cng_decoder->Generate(cng_output, false)) { // Error returned; set return vector to all zeros. memset(cng_output, 0, sizeof(cng_output)); } diff --git a/modules/audio_coding/neteq/normal.h b/modules/audio_coding/neteq/normal.h index 3607208f11..772293b605 100644 --- a/modules/audio_coding/neteq/normal.h +++ b/modules/audio_coding/neteq/normal.h @@ -17,7 +17,6 @@ #include "api/neteq/neteq.h" #include "modules/audio_coding/neteq/statistics_calculator.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -49,6 +48,9 @@ class Normal { virtual ~Normal() {} + Normal(const Normal&) = delete; + Normal& operator=(const Normal&) = delete; + // Performs the "Normal" operation. The decoder data is supplied in `input`, // having `length` samples in total for all channels (interleaved). The // result is written to `output`. The number of channels allocated in @@ -68,8 +70,6 @@ class Normal { const size_t samples_per_ms_; const int16_t default_win_slope_Q14_; StatisticsCalculator* const statistics_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Normal); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/packet_buffer.h b/modules/audio_coding/neteq/packet_buffer.h index 20a053323a..c6fb47ffbf 100644 --- a/modules/audio_coding/neteq/packet_buffer.h +++ b/modules/audio_coding/neteq/packet_buffer.h @@ -15,7 +15,6 @@ #include "modules/audio_coding/neteq/decoder_database.h" #include "modules/audio_coding/neteq/packet.h" #include "modules/include/module_common_types_public.h" // IsNewerTimestamp -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -51,6 +50,9 @@ class PacketBuffer { // Deletes all packets in the buffer before destroying the buffer. virtual ~PacketBuffer(); + PacketBuffer(const PacketBuffer&) = delete; + PacketBuffer& operator=(const PacketBuffer&) = delete; + // Flushes the buffer and deletes all packets in it. virtual void Flush(StatisticsCalculator* stats); @@ -173,7 +175,6 @@ class PacketBuffer { size_t max_number_of_packets_; PacketList buffer_; const TickTimer* tick_timer_; - RTC_DISALLOW_COPY_AND_ASSIGN(PacketBuffer); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/post_decode_vad.h b/modules/audio_coding/neteq/post_decode_vad.h index 3134d5f3a9..3bd91b9edb 100644 --- a/modules/audio_coding/neteq/post_decode_vad.h +++ b/modules/audio_coding/neteq/post_decode_vad.h @@ -16,7 +16,6 @@ #include "api/audio_codecs/audio_decoder.h" #include "common_audio/vad/include/webrtc_vad.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -31,6 +30,9 @@ class PostDecodeVad { virtual ~PostDecodeVad(); + PostDecodeVad(const PostDecodeVad&) = delete; + PostDecodeVad& operator=(const PostDecodeVad&) = delete; + // Enables post-decode VAD. void Enable(); @@ -63,8 +65,6 @@ class PostDecodeVad { bool active_speech_; int sid_interval_counter_; ::VadInst* vad_instance_; - - RTC_DISALLOW_COPY_AND_ASSIGN(PostDecodeVad); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/preemptive_expand.h b/modules/audio_coding/neteq/preemptive_expand.h index 708ebfd1bd..6338b993fd 100644 --- a/modules/audio_coding/neteq/preemptive_expand.h +++ b/modules/audio_coding/neteq/preemptive_expand.h @@ -15,7 +15,6 @@ #include #include "modules/audio_coding/neteq/time_stretch.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -36,6 +35,9 @@ class PreemptiveExpand : public TimeStretch { old_data_length_per_channel_(0), overlap_samples_(overlap_samples) {} + PreemptiveExpand(const PreemptiveExpand&) = delete; + PreemptiveExpand& operator=(const PreemptiveExpand&) = delete; + // This method performs the actual PreemptiveExpand operation. The samples are // read from `input`, of length `input_length` elements, and are written to // `output`. The number of samples added through time-stretching is @@ -67,8 +69,6 @@ class PreemptiveExpand : public TimeStretch { private: size_t old_data_length_per_channel_; size_t overlap_samples_; - - RTC_DISALLOW_COPY_AND_ASSIGN(PreemptiveExpand); }; struct PreemptiveExpandFactory { diff --git a/modules/audio_coding/neteq/random_vector.h b/modules/audio_coding/neteq/random_vector.h index 1d3760055b..4a782f1116 100644 --- a/modules/audio_coding/neteq/random_vector.h +++ b/modules/audio_coding/neteq/random_vector.h @@ -14,8 +14,6 @@ #include #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { // This class generates pseudo-random samples. @@ -26,6 +24,9 @@ class RandomVector { RandomVector() : seed_(777), seed_increment_(1) {} + RandomVector(const RandomVector&) = delete; + RandomVector& operator=(const RandomVector&) = delete; + void Reset(); void Generate(size_t length, int16_t* output); @@ -39,8 +40,6 @@ class RandomVector { private: uint32_t seed_; int16_t seed_increment_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RandomVector); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/red_payload_splitter.h b/modules/audio_coding/neteq/red_payload_splitter.h index 55660913d5..2f48e4b7d4 100644 --- a/modules/audio_coding/neteq/red_payload_splitter.h +++ b/modules/audio_coding/neteq/red_payload_splitter.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_NETEQ_RED_PAYLOAD_SPLITTER_H_ #include "modules/audio_coding/neteq/packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -30,6 +29,9 @@ class RedPayloadSplitter { virtual ~RedPayloadSplitter() {} + RedPayloadSplitter(const RedPayloadSplitter&) = delete; + RedPayloadSplitter& operator=(const RedPayloadSplitter&) = delete; + // Splits each packet in `packet_list` into its separate RED payloads. Each // RED payload is packetized into a Packet. The original elements in // `packet_list` are properly deleted, and replaced by the new packets. @@ -43,9 +45,6 @@ class RedPayloadSplitter { // is accepted. Any packet with another payload type is discarded. virtual void CheckRedPayloads(PacketList* packet_list, const DecoderDatabase& decoder_database); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(RedPayloadSplitter); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/relative_arrival_delay_tracker.cc b/modules/audio_coding/neteq/relative_arrival_delay_tracker.cc index 02c5a4321e..b50ac80bab 100644 --- a/modules/audio_coding/neteq/relative_arrival_delay_tracker.cc +++ b/modules/audio_coding/neteq/relative_arrival_delay_tracker.cc @@ -49,7 +49,7 @@ absl::optional RelativeArrivalDelayTracker::Update(uint32_t timestamp, void RelativeArrivalDelayTracker::Reset() { delay_history_.clear(); - packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch(); + packet_iat_stopwatch_.reset(); newest_timestamp_ = absl::nullopt; last_timestamp_ = absl::nullopt; } diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h index 5c3fb75d1b..269e6a09b2 100644 --- a/modules/audio_coding/neteq/statistics_calculator.h +++ b/modules/audio_coding/neteq/statistics_calculator.h @@ -15,7 +15,6 @@ #include #include "api/neteq/neteq.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -28,6 +27,9 @@ class StatisticsCalculator { virtual ~StatisticsCalculator(); + StatisticsCalculator(const StatisticsCalculator&) = delete; + StatisticsCalculator& operator=(const StatisticsCalculator&) = delete; + // Resets most of the counters. void Reset(); @@ -197,8 +199,6 @@ class StatisticsCalculator { PeriodicUmaAverage excess_buffer_delay_; PeriodicUmaCount buffer_full_counter_; bool decoded_output_played_ = false; - - RTC_DISALLOW_COPY_AND_ASSIGN(StatisticsCalculator); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/sync_buffer.cc b/modules/audio_coding/neteq/sync_buffer.cc index 80e1691bfa..7d7cac7157 100644 --- a/modules/audio_coding/neteq/sync_buffer.cc +++ b/modules/audio_coding/neteq/sync_buffer.cc @@ -28,7 +28,7 @@ void SyncBuffer::PushBack(const AudioMultiVector& append_this) { next_index_ -= samples_added; } else { // This means that we are pushing out future data that was never used. - // RTC_NOTREACHED(); + // RTC_DCHECK_NOTREACHED(); // TODO(hlundin): This assert must be disabled to support 60 ms frames. // This should not happen even for 60 ms frames, but it does. Investigate // why. diff --git a/modules/audio_coding/neteq/sync_buffer.h b/modules/audio_coding/neteq/sync_buffer.h index 7d24730cb3..cf56c432e3 100644 --- a/modules/audio_coding/neteq/sync_buffer.h +++ b/modules/audio_coding/neteq/sync_buffer.h @@ -20,7 +20,6 @@ #include "modules/audio_coding/neteq/audio_multi_vector.h" #include "modules/audio_coding/neteq/audio_vector.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -32,6 +31,9 @@ class SyncBuffer : public AudioMultiVector { end_timestamp_(0), dtmf_index_(0) {} + SyncBuffer(const SyncBuffer&) = delete; + SyncBuffer& operator=(const SyncBuffer&) = delete; + // Returns the number of samples yet to play out from the buffer. size_t FutureLength() const; @@ -102,8 +104,6 @@ class SyncBuffer : public AudioMultiVector { size_t next_index_; uint32_t end_timestamp_; // The timestamp of the last sample in the buffer. size_t dtmf_index_; // Index to the first non-DTMF sample in the buffer. - - RTC_DISALLOW_COPY_AND_ASSIGN(SyncBuffer); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/test/neteq_decoding_test.cc b/modules/audio_coding/neteq/test/neteq_decoding_test.cc index 1c70f1421b..6f27cdad4f 100644 --- a/modules/audio_coding/neteq/test/neteq_decoding_test.cc +++ b/modules/audio_coding/neteq/test/neteq_decoding_test.cc @@ -193,7 +193,7 @@ void NetEqDecodingTest::PopulateRtpInfo(int frame_index, rtp_info->timestamp = timestamp; rtp_info->ssrc = 0x1234; // Just an arbitrary SSRC. rtp_info->payloadType = 94; // PCM16b WB codec. - rtp_info->markerBit = 0; + rtp_info->markerBit = false; } void NetEqDecodingTest::PopulateCng(int frame_index, @@ -205,7 +205,7 @@ void NetEqDecodingTest::PopulateCng(int frame_index, rtp_info->timestamp = timestamp; rtp_info->ssrc = 0x1234; // Just an arbitrary SSRC. rtp_info->payloadType = 98; // WB CNG. - rtp_info->markerBit = 0; + rtp_info->markerBit = false; payload[0] = 64; // Noise level -64 dBov, quite arbitrarily chosen. *payload_len = 1; // Only noise level, no spectral parameters. } @@ -245,15 +245,9 @@ void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, NetEqNetworkStatistics network_stats; ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); - // Due to internal NetEq logic, preferred buffer-size is about 4 times the - // packet size for first few packets. Therefore we refrain from checking - // the criteria. - if (packets_inserted > 4) { - // Expect preferred and actual buffer size to be no more than 2 frames. - EXPECT_LE(network_stats.preferred_buffer_size_ms, kFrameSizeMs * 2); - EXPECT_LE(network_stats.current_buffer_size_ms, - kFrameSizeMs * 2 + algorithmic_delay_ms_); - } + EXPECT_LE(network_stats.preferred_buffer_size_ms, 80); + EXPECT_LE(network_stats.current_buffer_size_ms, + 80 + algorithmic_delay_ms_); last_seq_no = seq_no; last_timestamp = timestamp; diff --git a/modules/audio_coding/neteq/time_stretch.h b/modules/audio_coding/neteq/time_stretch.h index 998d080714..f0ddaebeca 100644 --- a/modules/audio_coding/neteq/time_stretch.h +++ b/modules/audio_coding/neteq/time_stretch.h @@ -14,7 +14,6 @@ #include // memset, size_t #include "modules/audio_coding/neteq/audio_multi_vector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -49,6 +48,9 @@ class TimeStretch { virtual ~TimeStretch() {} + TimeStretch(const TimeStretch&) = delete; + TimeStretch& operator=(const TimeStretch&) = delete; + // This method performs the processing common to both Accelerate and // PreemptiveExpand. ReturnCodes Process(const int16_t* input, @@ -105,8 +107,6 @@ class TimeStretch { int32_t vec2_energy, size_t peak_index, int scaling) const; - - RTC_DISALLOW_COPY_AND_ASSIGN(TimeStretch); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/timestamp_scaler.h b/modules/audio_coding/neteq/timestamp_scaler.h index 4d578fc433..f42ce7207a 100644 --- a/modules/audio_coding/neteq/timestamp_scaler.h +++ b/modules/audio_coding/neteq/timestamp_scaler.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_NETEQ_TIMESTAMP_SCALER_H_ #include "modules/audio_coding/neteq/packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,9 @@ class TimestampScaler { virtual ~TimestampScaler() {} + TimestampScaler(const TimestampScaler&) = delete; + TimestampScaler& operator=(const TimestampScaler&) = delete; + // Start over. virtual void Reset(); @@ -59,8 +61,6 @@ class TimestampScaler { uint32_t external_ref_; uint32_t internal_ref_; const DecoderDatabase& decoder_database_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TimestampScaler); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/tools/audio_checksum.h b/modules/audio_coding/neteq/tools/audio_checksum.h index e4306fa036..9d6f3432c0 100644 --- a/modules/audio_coding/neteq/tools/audio_checksum.h +++ b/modules/audio_coding/neteq/tools/audio_checksum.h @@ -16,7 +16,6 @@ #include "modules/audio_coding/neteq/tools/audio_sink.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/message_digest.h" #include "rtc_base/string_encode.h" #include "rtc_base/system/arch.h" @@ -31,6 +30,9 @@ class AudioChecksum : public AudioSink { checksum_result_(checksum_->Size()), finished_(false) {} + AudioChecksum(const AudioChecksum&) = delete; + AudioChecksum& operator=(const AudioChecksum&) = delete; + bool WriteArray(const int16_t* audio, size_t num_samples) override { if (finished_) return false; @@ -56,8 +58,6 @@ class AudioChecksum : public AudioSink { std::unique_ptr checksum_; rtc::Buffer checksum_result_; bool finished_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioChecksum); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/audio_loop.h b/modules/audio_coding/neteq/tools/audio_loop.h index 25da463921..076960a21c 100644 --- a/modules/audio_coding/neteq/tools/audio_loop.h +++ b/modules/audio_coding/neteq/tools/audio_loop.h @@ -15,7 +15,6 @@ #include #include "api/array_view.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -29,12 +28,15 @@ class AudioLoop { virtual ~AudioLoop() {} + AudioLoop(const AudioLoop&) = delete; + AudioLoop& operator=(const AudioLoop&) = delete; + // Initializes the AudioLoop by reading from `file_name`. The loop will be no // longer than `max_loop_length_samples`, if the length of the file is // greater. Otherwise, the loop length is the same as the file length. // The audio will be delivered in blocks of `block_length_samples`. // Returns false if the initialization failed, otherwise true. - bool Init(const std::string file_name, + bool Init(std::string file_name, size_t max_loop_length_samples, size_t block_length_samples); @@ -47,8 +49,6 @@ class AudioLoop { size_t loop_length_samples_; size_t block_length_samples_; std::unique_ptr audio_array_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioLoop); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/audio_sink.h b/modules/audio_coding/neteq/tools/audio_sink.h index cd6733b1d8..53729fa920 100644 --- a/modules/audio_coding/neteq/tools/audio_sink.h +++ b/modules/audio_coding/neteq/tools/audio_sink.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_ #include "api/audio/audio_frame.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -24,6 +23,9 @@ class AudioSink { AudioSink() {} virtual ~AudioSink() {} + AudioSink(const AudioSink&) = delete; + AudioSink& operator=(const AudioSink&) = delete; + // Writes `num_samples` from `audio` to the AudioSink. Returns true if // successful, otherwise false. virtual bool WriteArray(const int16_t* audio, size_t num_samples) = 0; @@ -34,9 +36,6 @@ class AudioSink { return WriteArray(audio_frame.data(), audio_frame.samples_per_channel_ * audio_frame.num_channels_); } - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioSink); }; // Forks the output audio to two AudioSink objects. @@ -45,23 +44,25 @@ class AudioSinkFork : public AudioSink { AudioSinkFork(AudioSink* left, AudioSink* right) : left_sink_(left), right_sink_(right) {} + AudioSinkFork(const AudioSinkFork&) = delete; + AudioSinkFork& operator=(const AudioSinkFork&) = delete; + bool WriteArray(const int16_t* audio, size_t num_samples) override; private: AudioSink* left_sink_; AudioSink* right_sink_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioSinkFork); }; // An AudioSink implementation that does nothing. class VoidAudioSink : public AudioSink { public: VoidAudioSink() = default; - bool WriteArray(const int16_t* audio, size_t num_samples) override; - private: - RTC_DISALLOW_COPY_AND_ASSIGN(VoidAudioSink); + VoidAudioSink(const VoidAudioSink&) = delete; + VoidAudioSink& operator=(const VoidAudioSink&) = delete; + + bool WriteArray(const int16_t* audio, size_t num_samples) override; }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h b/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h index 6a79ce4d1f..ab4f5c2281 100644 --- a/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h +++ b/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h @@ -16,7 +16,6 @@ #include #include "modules/audio_coding/neteq/tools/packet_source.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -31,6 +30,9 @@ class ConstantPcmPacketSource : public PacketSource { int sample_rate_hz, int payload_type); + ConstantPcmPacketSource(const ConstantPcmPacketSource&) = delete; + ConstantPcmPacketSource& operator=(const ConstantPcmPacketSource&) = delete; + std::unique_ptr NextPacket() override; private: @@ -46,8 +48,6 @@ class ConstantPcmPacketSource : public PacketSource { uint16_t seq_number_; uint32_t timestamp_; const uint32_t payload_ssrc_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ConstantPcmPacketSource); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/input_audio_file.h b/modules/audio_coding/neteq/tools/input_audio_file.h index 010d8cccbd..056dbf5c20 100644 --- a/modules/audio_coding/neteq/tools/input_audio_file.h +++ b/modules/audio_coding/neteq/tools/input_audio_file.h @@ -15,18 +15,19 @@ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { namespace test { // Class for handling a looping input audio file. class InputAudioFile { public: - explicit InputAudioFile(const std::string file_name, bool loop_at_end = true); + explicit InputAudioFile(std::string file_name, bool loop_at_end = true); virtual ~InputAudioFile(); + InputAudioFile(const InputAudioFile&) = delete; + InputAudioFile& operator=(const InputAudioFile&) = delete; + // Reads `samples` elements from source file to `destination`. Returns true // if the read was successful, otherwise false. If the file end is reached, // the file is rewound and reading continues from the beginning. @@ -52,7 +53,6 @@ class InputAudioFile { private: FILE* fp_; const bool loop_at_end_; - RTC_DISALLOW_COPY_AND_ASSIGN(InputAudioFile); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc index 337f54ed6e..3f06b1cfc4 100644 --- a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc +++ b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc @@ -33,8 +33,7 @@ NetEqStatsPlotter::NetEqStatsPlotter(bool make_matlab_plot, stats_getter_.reset(new NetEqStatsGetter(std::move(delay_analyzer))); } -void NetEqStatsPlotter::SimulationEnded(int64_t simulation_time_ms, - NetEq* /*neteq*/) { +void NetEqStatsPlotter::SimulationEnded(int64_t simulation_time_ms) { if (make_matlab_plot_) { auto matlab_script_name = base_file_name_; std::replace(matlab_script_name.begin(), matlab_script_name.end(), '.', diff --git a/modules/audio_coding/neteq/tools/neteq_stats_plotter.h b/modules/audio_coding/neteq/tools/neteq_stats_plotter.h index d6918670fd..c4df24e073 100644 --- a/modules/audio_coding/neteq/tools/neteq_stats_plotter.h +++ b/modules/audio_coding/neteq/tools/neteq_stats_plotter.h @@ -28,7 +28,7 @@ class NetEqStatsPlotter : public NetEqSimulationEndedCallback { bool show_concealment_events, std::string base_file_name); - void SimulationEnded(int64_t simulation_time_ms, NetEq* neteq) override; + void SimulationEnded(int64_t simulation_time_ms) override; NetEqStatsGetter* stats_getter() { return stats_getter_.get(); } diff --git a/modules/audio_coding/neteq/tools/neteq_test.cc b/modules/audio_coding/neteq/tools/neteq_test.cc index 22f5ad6931..7902438304 100644 --- a/modules/audio_coding/neteq/tools/neteq_test.cc +++ b/modules/audio_coding/neteq/tools/neteq_test.cc @@ -91,8 +91,7 @@ int64_t NetEqTest::Run() { simulation_time += step_result.simulation_step_ms; } while (!step_result.is_simulation_finished); if (callbacks_.simulation_ended_callback) { - callbacks_.simulation_ended_callback->SimulationEnded(simulation_time, - neteq_.get()); + callbacks_.simulation_ended_callback->SimulationEnded(simulation_time); } return simulation_time; } diff --git a/modules/audio_coding/neteq/tools/neteq_test.h b/modules/audio_coding/neteq/tools/neteq_test.h index 3b787a6cfb..0a6c24f3d6 100644 --- a/modules/audio_coding/neteq/tools/neteq_test.h +++ b/modules/audio_coding/neteq/tools/neteq_test.h @@ -61,7 +61,7 @@ class NetEqGetAudioCallback { class NetEqSimulationEndedCallback { public: virtual ~NetEqSimulationEndedCallback() = default; - virtual void SimulationEnded(int64_t simulation_time_ms, NetEq* neteq) = 0; + virtual void SimulationEnded(int64_t simulation_time_ms) = 0; }; // Class that provides an input--output test for NetEq. The input (both packets diff --git a/modules/audio_coding/neteq/tools/output_audio_file.h b/modules/audio_coding/neteq/tools/output_audio_file.h index ad97722cbc..491cbd0420 100644 --- a/modules/audio_coding/neteq/tools/output_audio_file.h +++ b/modules/audio_coding/neteq/tools/output_audio_file.h @@ -16,7 +16,6 @@ #include #include "modules/audio_coding/neteq/tools/audio_sink.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -34,6 +33,9 @@ class OutputAudioFile : public AudioSink { fclose(out_file_); } + OutputAudioFile(const OutputAudioFile&) = delete; + OutputAudioFile& operator=(const OutputAudioFile&) = delete; + bool WriteArray(const int16_t* audio, size_t num_samples) override { RTC_DCHECK(out_file_); return fwrite(audio, sizeof(*audio), num_samples, out_file_) == num_samples; @@ -41,8 +43,6 @@ class OutputAudioFile : public AudioSink { private: FILE* out_file_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OutputAudioFile); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/output_wav_file.h b/modules/audio_coding/neteq/tools/output_wav_file.h index ae2e9700fe..1485f4e911 100644 --- a/modules/audio_coding/neteq/tools/output_wav_file.h +++ b/modules/audio_coding/neteq/tools/output_wav_file.h @@ -15,7 +15,6 @@ #include "common_audio/wav_file.h" #include "modules/audio_coding/neteq/tools/audio_sink.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -29,6 +28,9 @@ class OutputWavFile : public AudioSink { int num_channels = 1) : wav_writer_(file_name, sample_rate_hz, num_channels) {} + OutputWavFile(const OutputWavFile&) = delete; + OutputWavFile& operator=(const OutputWavFile&) = delete; + bool WriteArray(const int16_t* audio, size_t num_samples) override { wav_writer_.WriteSamples(audio, num_samples); return true; @@ -36,8 +38,6 @@ class OutputWavFile : public AudioSink { private: WavWriter wav_writer_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OutputWavFile); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/packet.h b/modules/audio_coding/neteq/tools/packet.h index 92e5ee9c3d..96710907df 100644 --- a/modules/audio_coding/neteq/tools/packet.h +++ b/modules/audio_coding/neteq/tools/packet.h @@ -16,7 +16,6 @@ #include "api/array_view.h" #include "api/rtp_headers.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/copy_on_write_buffer.h" namespace webrtc { @@ -54,6 +53,9 @@ class Packet { virtual ~Packet(); + Packet(const Packet&) = delete; + Packet& operator=(const Packet&) = delete; + // Parses the first bytes of the RTP payload, interpreting them as RED headers // according to RFC 2198. The headers will be inserted into `headers`. The // caller of the method assumes ownership of the objects in the list, and @@ -95,8 +97,6 @@ class Packet { size_t virtual_payload_length_bytes_ = 0; const double time_ms_; // Used to denote a packet's arrival time. const bool valid_header_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Packet); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/packet_source.h b/modules/audio_coding/neteq/tools/packet_source.h index 975680f5a9..be1705cae1 100644 --- a/modules/audio_coding/neteq/tools/packet_source.h +++ b/modules/audio_coding/neteq/tools/packet_source.h @@ -15,7 +15,6 @@ #include #include "modules/audio_coding/neteq/tools/packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -26,6 +25,9 @@ class PacketSource { PacketSource(); virtual ~PacketSource(); + PacketSource(const PacketSource&) = delete; + PacketSource& operator=(const PacketSource&) = delete; + // Returns next packet. Returns nullptr if the source is depleted, or if an // error occurred. virtual std::unique_ptr NextPacket() = 0; @@ -34,9 +36,6 @@ class PacketSource { protected: std::bitset<128> filter_; // Payload type is 7 bits in the RFC. - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(PacketSource); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/resample_input_audio_file.h b/modules/audio_coding/neteq/tools/resample_input_audio_file.h index 9106d5b769..497a4109df 100644 --- a/modules/audio_coding/neteq/tools/resample_input_audio_file.h +++ b/modules/audio_coding/neteq/tools/resample_input_audio_file.h @@ -15,7 +15,6 @@ #include "common_audio/resampler/include/resampler.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -37,6 +36,9 @@ class ResampleInputAudioFile : public InputAudioFile { file_rate_hz_(file_rate_hz), output_rate_hz_(output_rate_hz) {} + ResampleInputAudioFile(const ResampleInputAudioFile&) = delete; + ResampleInputAudioFile& operator=(const ResampleInputAudioFile&) = delete; + bool Read(size_t samples, int output_rate_hz, int16_t* destination); bool Read(size_t samples, int16_t* destination) override; void set_output_rate_hz(int rate_hz); @@ -45,7 +47,6 @@ class ResampleInputAudioFile : public InputAudioFile { const int file_rate_hz_; int output_rate_hz_; Resampler resampler_; - RTC_DISALLOW_COPY_AND_ASSIGN(ResampleInputAudioFile); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/rtc_event_log_source.h b/modules/audio_coding/neteq/tools/rtc_event_log_source.h index d4be2a7939..e2d0f61666 100644 --- a/modules/audio_coding/neteq/tools/rtc_event_log_source.h +++ b/modules/audio_coding/neteq/tools/rtc_event_log_source.h @@ -19,7 +19,6 @@ #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "modules/audio_coding/neteq/tools/packet_source.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -43,6 +42,9 @@ class RtcEventLogSource : public PacketSource { virtual ~RtcEventLogSource(); + RtcEventLogSource(const RtcEventLogSource&) = delete; + RtcEventLogSource& operator=(const RtcEventLogSource&) = delete; + std::unique_ptr NextPacket() override; // Returns the timestamp of the next audio output event, in milliseconds. The @@ -60,8 +62,6 @@ class RtcEventLogSource : public PacketSource { size_t rtp_packet_index_ = 0; std::vector audio_outputs_; size_t audio_output_index_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogSource); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/rtp_encode.cc b/modules/audio_coding/neteq/tools/rtp_encode.cc index 204f169a6d..ee392f26a3 100644 --- a/modules/audio_coding/neteq/tools/rtp_encode.cc +++ b/modules/audio_coding/neteq/tools/rtp_encode.cc @@ -191,7 +191,7 @@ AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) { config.sample_rate_hz = 48000; return config; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return config; } } @@ -242,7 +242,7 @@ std::unique_ptr CreateEncoder(CodecType codec_type, GetCodecConfig(), payload_type); } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -259,7 +259,7 @@ AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) { case 48000: return 100; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return 0; }; diff --git a/modules/audio_coding/neteq/tools/rtp_file_source.h b/modules/audio_coding/neteq/tools/rtp_file_source.h index d6aab24abc..7e284aca45 100644 --- a/modules/audio_coding/neteq/tools/rtp_file_source.h +++ b/modules/audio_coding/neteq/tools/rtp_file_source.h @@ -19,7 +19,6 @@ #include "absl/types/optional.h" #include "modules/audio_coding/neteq/tools/packet_source.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -41,6 +40,9 @@ class RtpFileSource : public PacketSource { ~RtpFileSource() override; + RtpFileSource(const RtpFileSource&) = delete; + RtpFileSource& operator=(const RtpFileSource&) = delete; + // Registers an RTP header extension and binds it to `id`. virtual bool RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id); @@ -58,8 +60,6 @@ class RtpFileSource : public PacketSource { std::unique_ptr rtp_reader_; const absl::optional ssrc_filter_; RtpHeaderExtensionMap rtp_header_extension_map_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpFileSource); }; } // namespace test diff --git a/modules/audio_coding/neteq/tools/rtp_generator.h b/modules/audio_coding/neteq/tools/rtp_generator.h index 6ca6e1bac7..2e615adec5 100644 --- a/modules/audio_coding/neteq/tools/rtp_generator.h +++ b/modules/audio_coding/neteq/tools/rtp_generator.h @@ -12,7 +12,6 @@ #define MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_ #include "api/rtp_headers.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -34,6 +33,9 @@ class RtpGenerator { virtual ~RtpGenerator() {} + RtpGenerator(const RtpGenerator&) = delete; + RtpGenerator& operator=(const RtpGenerator&) = delete; + // Writes the next RTP header to `rtp_header`, which will be of type // `payload_type`. Returns the send time for this packet (in ms). The value of // `payload_length_samples` determines the send time for the next packet. @@ -50,9 +52,6 @@ class RtpGenerator { const uint32_t ssrc_; const int samples_per_ms_; double drift_factor_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(RtpGenerator); }; class TimestampJumpRtpGenerator : public RtpGenerator { @@ -66,6 +65,10 @@ class TimestampJumpRtpGenerator : public RtpGenerator { jump_from_timestamp_(jump_from_timestamp), jump_to_timestamp_(jump_to_timestamp) {} + TimestampJumpRtpGenerator(const TimestampJumpRtpGenerator&) = delete; + TimestampJumpRtpGenerator& operator=(const TimestampJumpRtpGenerator&) = + delete; + uint32_t GetRtpHeader(uint8_t payload_type, size_t payload_length_samples, RTPHeader* rtp_header) override; @@ -73,7 +76,6 @@ class TimestampJumpRtpGenerator : public RtpGenerator { private: uint32_t jump_from_timestamp_; uint32_t jump_to_timestamp_; - RTC_DISALLOW_COPY_AND_ASSIGN(TimestampJumpRtpGenerator); }; } // namespace test diff --git a/modules/audio_coding/neteq/underrun_optimizer.cc b/modules/audio_coding/neteq/underrun_optimizer.cc index dad0424c48..baed812327 100644 --- a/modules/audio_coding/neteq/underrun_optimizer.cc +++ b/modules/audio_coding/neteq/underrun_optimizer.cc @@ -63,7 +63,7 @@ void UnderrunOptimizer::Update(int relative_delay_ms) { void UnderrunOptimizer::Reset() { histogram_.Reset(); - resample_stopwatch_ = tick_timer_->GetNewStopwatch(); + resample_stopwatch_.reset(); max_delay_in_interval_ms_ = 0; optimal_delay_ms_.reset(); } diff --git a/modules/audio_coding/test/EncodeDecodeTest.h b/modules/audio_coding/test/EncodeDecodeTest.h index c96a4d69f4..fbc42fbe0e 100644 --- a/modules/audio_coding/test/EncodeDecodeTest.h +++ b/modules/audio_coding/test/EncodeDecodeTest.h @@ -28,11 +28,11 @@ class TestPacketization : public AudioPacketizationCallback { public: TestPacketization(RTPStream* rtpStream, uint16_t frequency); ~TestPacketization(); - int32_t SendData(const AudioFrameType frameType, - const uint8_t payloadType, - const uint32_t timeStamp, + int32_t SendData(AudioFrameType frameType, + uint8_t payloadType, + uint32_t timeStamp, const uint8_t* payloadData, - const size_t payloadSize, + size_t payloadSize, int64_t absolute_capture_timestamp_ms) override; private: diff --git a/modules/audio_coding/test/RTPFile.h b/modules/audio_coding/test/RTPFile.h index a3d1520922..a9f574bab0 100644 --- a/modules/audio_coding/test/RTPFile.h +++ b/modules/audio_coding/test/RTPFile.h @@ -25,11 +25,11 @@ class RTPStream { public: virtual ~RTPStream() {} - virtual void Write(const uint8_t payloadType, - const uint32_t timeStamp, - const int16_t seqNo, + virtual void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, const uint8_t* payloadData, - const size_t payloadSize, + size_t payloadSize, uint32_t frequency) = 0; // Returns the packet's payload size. Zero should be treated as an @@ -75,11 +75,11 @@ class RTPBuffer : public RTPStream { ~RTPBuffer() = default; - void Write(const uint8_t payloadType, - const uint32_t timeStamp, - const int16_t seqNo, + void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, const uint8_t* payloadData, - const size_t payloadSize, + size_t payloadSize, uint32_t frequency) override; size_t Read(RTPHeader* rtp_header, @@ -108,11 +108,11 @@ class RTPFile : public RTPStream { void ReadHeader(); - void Write(const uint8_t payloadType, - const uint32_t timeStamp, - const int16_t seqNo, + void Write(uint8_t payloadType, + uint32_t timeStamp, + int16_t seqNo, const uint8_t* payloadData, - const size_t payloadSize, + size_t payloadSize, uint32_t frequency) override; size_t Read(RTPHeader* rtp_header, diff --git a/modules/audio_coding/test/TestRedFec.cc b/modules/audio_coding/test/TestRedFec.cc index d2c8d8a132..892fbc83d6 100644 --- a/modules/audio_coding/test/TestRedFec.cc +++ b/modules/audio_coding/test/TestRedFec.cc @@ -190,7 +190,8 @@ void TestRedFec::RegisterSendCodec( AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type; config.speech_encoder = std::move(encoder); - encoder = std::make_unique(std::move(config)); + encoder = std::make_unique(std::move(config), + field_trials_); receive_codecs.emplace( std::make_pair(red_payload_type, SdpAudioFormat("red", codec_format.clockrate_hz, 1))); diff --git a/modules/audio_coding/test/TestRedFec.h b/modules/audio_coding/test/TestRedFec.h index 0e92d27330..dbadd88487 100644 --- a/modules/audio_coding/test/TestRedFec.h +++ b/modules/audio_coding/test/TestRedFec.h @@ -19,6 +19,7 @@ #include "common_audio/vad/include/vad.h" #include "modules/audio_coding/test/Channel.h" #include "modules/audio_coding/test/PCMFile.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -37,6 +38,7 @@ class TestRedFec { void Run(); void OpenOutFile(int16_t testNumber); + test::ScopedKeyValueConfig field_trials_; const rtc::scoped_refptr encoder_factory_; const rtc::scoped_refptr decoder_factory_; std::unique_ptr _acmA; diff --git a/modules/audio_coding/test/TestStereo.cc b/modules/audio_coding/test/TestStereo.cc index 1b1222c077..599fafb602 100644 --- a/modules/audio_coding/test/TestStereo.cc +++ b/modules/audio_coding/test/TestStereo.cc @@ -508,7 +508,7 @@ void TestStereo::Run(TestPackStereo* channel, in_file_stereo_->FastForward(100); in_file_mono_->FastForward(100); - while (1) { + while (true) { // Simulate packet loss by setting `packet_loss_` to "true" in // `percent_loss` percent of the loops. if (percent_loss > 0) { diff --git a/modules/audio_coding/test/TestStereo.h b/modules/audio_coding/test/TestStereo.h index 3ee4dbf594..4c50a4b555 100644 --- a/modules/audio_coding/test/TestStereo.h +++ b/modules/audio_coding/test/TestStereo.h @@ -31,11 +31,11 @@ class TestPackStereo : public AudioPacketizationCallback { void RegisterReceiverACM(AudioCodingModule* acm); - int32_t SendData(const AudioFrameType frame_type, - const uint8_t payload_type, - const uint32_t timestamp, + int32_t SendData(AudioFrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, const uint8_t* payload_data, - const size_t payload_size, + size_t payload_size, int64_t absolute_capture_timestamp_ms) override; uint16_t payload_size(); diff --git a/modules/audio_coding/test/TwoWayCommunication.h b/modules/audio_coding/test/TwoWayCommunication.h index 7d0cdb9566..b7eb9e5583 100644 --- a/modules/audio_coding/test/TwoWayCommunication.h +++ b/modules/audio_coding/test/TwoWayCommunication.h @@ -31,9 +31,9 @@ class TwoWayCommunication { private: void SetUpAutotest(AudioEncoderFactory* const encoder_factory, const SdpAudioFormat& format1, - const int payload_type1, + int payload_type1, const SdpAudioFormat& format2, - const int payload_type2); + int payload_type2); std::unique_ptr _acmA; std::unique_ptr _acmB; diff --git a/modules/audio_coding/test/iSACTest.cc b/modules/audio_coding/test/iSACTest.cc index 94a6889ee7..52f5a6ad4f 100644 --- a/modules/audio_coding/test/iSACTest.cc +++ b/modules/audio_coding/test/iSACTest.cc @@ -257,11 +257,6 @@ void ISACTest::EncodeDecode(int testNr, wbISACConfig), kISAC16kPayloadType)); - bool adaptiveMode = false; - if ((swbISACConfig.currentRateBitPerSec == -1) || - (wbISACConfig.currentRateBitPerSec == -1)) { - adaptiveMode = true; - } _myTimer.Reset(); _channel_A2B->ResetStats(); _channel_B2A->ResetStats(); diff --git a/modules/audio_device/android/aaudio_player.cc b/modules/audio_device/android/aaudio_player.cc index 7f635126bd..5257b2ba1b 100644 --- a/modules/audio_device/android/aaudio_player.cc +++ b/modules/audio_device/android/aaudio_player.cc @@ -27,19 +27,19 @@ enum AudioDeviceMessageType : uint32_t { AAudioPlayer::AAudioPlayer(AudioManager* audio_manager) : main_thread_(rtc::Thread::Current()), aaudio_(audio_manager, AAUDIO_DIRECTION_OUTPUT, this) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; thread_checker_aaudio_.Detach(); } AAudioPlayer::~AAudioPlayer() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK_RUN_ON(&main_thread_checker_); Terminate(); - RTC_LOG(INFO) << "#detected underruns: " << underrun_count_; + RTC_LOG(LS_INFO) << "#detected underruns: " << underrun_count_; } int AAudioPlayer::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK_RUN_ON(&main_thread_checker_); if (aaudio_.audio_parameters().channels() == 2) { RTC_DLOG(LS_WARNING) << "Stereo mode is enabled"; @@ -48,14 +48,14 @@ int AAudioPlayer::Init() { } int AAudioPlayer::Terminate() { - RTC_LOG(INFO) << "Terminate"; + RTC_LOG(LS_INFO) << "Terminate"; RTC_DCHECK_RUN_ON(&main_thread_checker_); StopPlayout(); return 0; } int AAudioPlayer::InitPlayout() { - RTC_LOG(INFO) << "InitPlayout"; + RTC_LOG(LS_INFO) << "InitPlayout"; RTC_DCHECK_RUN_ON(&main_thread_checker_); RTC_DCHECK(!initialized_); RTC_DCHECK(!playing_); @@ -72,7 +72,7 @@ bool AAudioPlayer::PlayoutIsInitialized() const { } int AAudioPlayer::StartPlayout() { - RTC_LOG(INFO) << "StartPlayout"; + RTC_LOG(LS_INFO) << "StartPlayout"; RTC_DCHECK_RUN_ON(&main_thread_checker_); RTC_DCHECK(!playing_); if (!initialized_) { @@ -93,7 +93,7 @@ int AAudioPlayer::StartPlayout() { } int AAudioPlayer::StopPlayout() { - RTC_LOG(INFO) << "StopPlayout"; + RTC_LOG(LS_INFO) << "StopPlayout"; RTC_DCHECK_RUN_ON(&main_thread_checker_); if (!initialized_ || !playing_) { return 0; @@ -114,7 +114,7 @@ bool AAudioPlayer::Playing() const { } void AAudioPlayer::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { - RTC_DLOG(INFO) << "AttachAudioBuffer"; + RTC_DLOG(LS_INFO) << "AttachAudioBuffer"; RTC_DCHECK_RUN_ON(&main_thread_checker_); audio_device_buffer_ = audioBuffer; const AudioParameters audio_parameters = aaudio_.audio_parameters(); @@ -142,7 +142,7 @@ void AAudioPlayer::OnErrorCallback(aaudio_result_t error) { if (aaudio_.stream_state() == AAUDIO_STREAM_STATE_DISCONNECTED) { // The stream is disconnected and any attempt to use it will return // AAUDIO_ERROR_DISCONNECTED. - RTC_LOG(WARNING) << "Output stream disconnected"; + RTC_LOG(LS_WARNING) << "Output stream disconnected"; // AAudio documentation states: "You should not close or reopen the stream // from the callback, use another thread instead". A message is therefore // sent to the main thread to do the restart operation. @@ -157,9 +157,9 @@ aaudio_data_callback_result_t AAudioPlayer::OnDataCallback(void* audio_data, // Log device id in first data callback to ensure that a valid device is // utilized. if (first_data_callback_) { - RTC_LOG(INFO) << "--- First output data callback: " - "device id=" - << aaudio_.device_id(); + RTC_LOG(LS_INFO) << "--- First output data callback: " + "device id=" + << aaudio_.device_id(); first_data_callback_ = false; } @@ -179,8 +179,8 @@ aaudio_data_callback_result_t AAudioPlayer::OnDataCallback(void* audio_data, latency_millis_ = aaudio_.EstimateLatencyMillis(); // TODO(henrika): use for development only. if (aaudio_.frames_written() % (1000 * aaudio_.frames_per_burst()) == 0) { - RTC_DLOG(INFO) << "output latency: " << latency_millis_ - << ", num_frames: " << num_frames; + RTC_DLOG(LS_INFO) << "output latency: " << latency_millis_ + << ", num_frames: " << num_frames; } // Read audio data from the WebRTC source using the FineAudioBuffer object @@ -215,7 +215,7 @@ void AAudioPlayer::OnMessage(rtc::Message* msg) { void AAudioPlayer::HandleStreamDisconnected() { RTC_DCHECK_RUN_ON(&main_thread_checker_); - RTC_DLOG(INFO) << "HandleStreamDisconnected"; + RTC_DLOG(LS_INFO) << "HandleStreamDisconnected"; if (!initialized_ || !playing_) { return; } diff --git a/modules/audio_device/android/aaudio_recorder.cc b/modules/audio_device/android/aaudio_recorder.cc index 68c9cee858..4757cf8cf0 100644 --- a/modules/audio_device/android/aaudio_recorder.cc +++ b/modules/audio_device/android/aaudio_recorder.cc @@ -28,19 +28,19 @@ enum AudioDeviceMessageType : uint32_t { AAudioRecorder::AAudioRecorder(AudioManager* audio_manager) : main_thread_(rtc::Thread::Current()), aaudio_(audio_manager, AAUDIO_DIRECTION_INPUT, this) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; thread_checker_aaudio_.Detach(); } AAudioRecorder::~AAudioRecorder() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); Terminate(); - RTC_LOG(INFO) << "detected owerflows: " << overflow_count_; + RTC_LOG(LS_INFO) << "detected owerflows: " << overflow_count_; } int AAudioRecorder::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); if (aaudio_.audio_parameters().channels() == 2) { RTC_DLOG(LS_WARNING) << "Stereo mode is enabled"; @@ -49,14 +49,14 @@ int AAudioRecorder::Init() { } int AAudioRecorder::Terminate() { - RTC_LOG(INFO) << "Terminate"; + RTC_LOG(LS_INFO) << "Terminate"; RTC_DCHECK(thread_checker_.IsCurrent()); StopRecording(); return 0; } int AAudioRecorder::InitRecording() { - RTC_LOG(INFO) << "InitRecording"; + RTC_LOG(LS_INFO) << "InitRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); RTC_DCHECK(!recording_); @@ -68,7 +68,7 @@ int AAudioRecorder::InitRecording() { } int AAudioRecorder::StartRecording() { - RTC_LOG(INFO) << "StartRecording"; + RTC_LOG(LS_INFO) << "StartRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(initialized_); RTC_DCHECK(!recording_); @@ -85,7 +85,7 @@ int AAudioRecorder::StartRecording() { } int AAudioRecorder::StopRecording() { - RTC_LOG(INFO) << "StopRecording"; + RTC_LOG(LS_INFO) << "StopRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); if (!initialized_ || !recording_) { return 0; @@ -100,7 +100,7 @@ int AAudioRecorder::StopRecording() { } void AAudioRecorder::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { - RTC_LOG(INFO) << "AttachAudioBuffer"; + RTC_LOG(LS_INFO) << "AttachAudioBuffer"; RTC_DCHECK(thread_checker_.IsCurrent()); audio_device_buffer_ = audioBuffer; const AudioParameters audio_parameters = aaudio_.audio_parameters(); @@ -114,19 +114,19 @@ void AAudioRecorder::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { } int AAudioRecorder::EnableBuiltInAEC(bool enable) { - RTC_LOG(INFO) << "EnableBuiltInAEC: " << enable; + RTC_LOG(LS_INFO) << "EnableBuiltInAEC: " << enable; RTC_LOG(LS_ERROR) << "Not implemented"; return -1; } int AAudioRecorder::EnableBuiltInAGC(bool enable) { - RTC_LOG(INFO) << "EnableBuiltInAGC: " << enable; + RTC_LOG(LS_INFO) << "EnableBuiltInAGC: " << enable; RTC_LOG(LS_ERROR) << "Not implemented"; return -1; } int AAudioRecorder::EnableBuiltInNS(bool enable) { - RTC_LOG(INFO) << "EnableBuiltInNS: " << enable; + RTC_LOG(LS_INFO) << "EnableBuiltInNS: " << enable; RTC_LOG(LS_ERROR) << "Not implemented"; return -1; } @@ -137,7 +137,7 @@ void AAudioRecorder::OnErrorCallback(aaudio_result_t error) { if (aaudio_.stream_state() == AAUDIO_STREAM_STATE_DISCONNECTED) { // The stream is disconnected and any attempt to use it will return // AAUDIO_ERROR_DISCONNECTED.. - RTC_LOG(WARNING) << "Input stream disconnected => restart is required"; + RTC_LOG(LS_WARNING) << "Input stream disconnected => restart is required"; // AAudio documentation states: "You should not close or reopen the stream // from the callback, use another thread instead". A message is therefore // sent to the main thread to do the restart operation. @@ -154,14 +154,14 @@ aaudio_data_callback_result_t AAudioRecorder::OnDataCallback( int32_t num_frames) { // TODO(henrika): figure out why we sometimes hit this one. // RTC_DCHECK(thread_checker_aaudio_.IsCurrent()); - // RTC_LOG(INFO) << "OnDataCallback: " << num_frames; + // RTC_LOG(LS_INFO) << "OnDataCallback: " << num_frames; // Drain the input buffer at first callback to ensure that it does not // contain any old data. Will also ensure that the lowest possible latency // is obtained. if (first_data_callback_) { - RTC_LOG(INFO) << "--- First input data callback: " - "device id=" - << aaudio_.device_id(); + RTC_LOG(LS_INFO) << "--- First input data callback: " + "device id=" + << aaudio_.device_id(); aaudio_.ClearInputStream(audio_data, num_frames); first_data_callback_ = false; } @@ -177,8 +177,8 @@ aaudio_data_callback_result_t AAudioRecorder::OnDataCallback( latency_millis_ = aaudio_.EstimateLatencyMillis(); // TODO(henrika): use for development only. if (aaudio_.frames_read() % (1000 * aaudio_.frames_per_burst()) == 0) { - RTC_DLOG(INFO) << "input latency: " << latency_millis_ - << ", num_frames: " << num_frames; + RTC_DLOG(LS_INFO) << "input latency: " << latency_millis_ + << ", num_frames: " << num_frames; } // Copy recorded audio in `audio_data` to the WebRTC sink using the // FineAudioBuffer object. @@ -204,7 +204,7 @@ void AAudioRecorder::OnMessage(rtc::Message* msg) { void AAudioRecorder::HandleStreamDisconnected() { RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_LOG(INFO) << "HandleStreamDisconnected"; + RTC_LOG(LS_INFO) << "HandleStreamDisconnected"; if (!initialized_ || !recording_) { return; } diff --git a/modules/audio_device/android/aaudio_wrapper.cc b/modules/audio_device/android/aaudio_wrapper.cc index ab1278436e..3d824b5c57 100644 --- a/modules/audio_device/android/aaudio_wrapper.cc +++ b/modules/audio_device/android/aaudio_wrapper.cc @@ -91,8 +91,8 @@ void ErrorCallback(AAudioStream* stream, aaudio_result_t error) { RTC_DCHECK(user_data); AAudioWrapper* aaudio_wrapper = reinterpret_cast(user_data); - RTC_LOG(WARNING) << "ErrorCallback: " - << DirectionToString(aaudio_wrapper->direction()); + RTC_LOG(LS_WARNING) << "ErrorCallback: " + << DirectionToString(aaudio_wrapper->direction()); RTC_DCHECK(aaudio_wrapper->observer()); aaudio_wrapper->observer()->OnErrorCallback(error); } @@ -134,23 +134,23 @@ AAudioWrapper::AAudioWrapper(AudioManager* audio_manager, aaudio_direction_t direction, AAudioObserverInterface* observer) : direction_(direction), observer_(observer) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; RTC_DCHECK(observer_); direction_ == AAUDIO_DIRECTION_OUTPUT ? audio_parameters_ = audio_manager->GetPlayoutAudioParameters() : audio_parameters_ = audio_manager->GetRecordAudioParameters(); aaudio_thread_checker_.Detach(); - RTC_LOG(INFO) << audio_parameters_.ToString(); + RTC_LOG(LS_INFO) << audio_parameters_.ToString(); } AAudioWrapper::~AAudioWrapper() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!stream_); } bool AAudioWrapper::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); // Creates a stream builder which can be used to open an audio stream. ScopedStreamBuilder builder; @@ -174,7 +174,7 @@ bool AAudioWrapper::Init() { } bool AAudioWrapper::Start() { - RTC_LOG(INFO) << "Start"; + RTC_LOG(LS_INFO) << "Start"; RTC_DCHECK(thread_checker_.IsCurrent()); // TODO(henrika): this state check might not be needed. aaudio_stream_state_t current_state = AAudioStream_getState(stream_); @@ -190,7 +190,7 @@ bool AAudioWrapper::Start() { } bool AAudioWrapper::Stop() { - RTC_LOG(INFO) << "Stop: " << DirectionToString(direction()); + RTC_LOG(LS_INFO) << "Stop: " << DirectionToString(direction()); RTC_DCHECK(thread_checker_.IsCurrent()); // Asynchronous request for the stream to stop. RETURN_ON_ERROR(AAudioStream_requestStop(stream_), false); @@ -240,7 +240,7 @@ double AAudioWrapper::EstimateLatencyMillis() const { // Returns new buffer size or a negative error value if buffer size could not // be increased. bool AAudioWrapper::IncreaseOutputBufferSize() { - RTC_LOG(INFO) << "IncreaseBufferSize"; + RTC_LOG(LS_INFO) << "IncreaseBufferSize"; RTC_DCHECK(stream_); RTC_DCHECK(aaudio_thread_checker_.IsCurrent()); RTC_DCHECK_EQ(direction(), AAUDIO_DIRECTION_OUTPUT); @@ -255,20 +255,20 @@ bool AAudioWrapper::IncreaseOutputBufferSize() { << ") is higher than max: " << max_buffer_size; return false; } - RTC_LOG(INFO) << "Updating buffer size to: " << buffer_size - << " (max=" << max_buffer_size << ")"; + RTC_LOG(LS_INFO) << "Updating buffer size to: " << buffer_size + << " (max=" << max_buffer_size << ")"; buffer_size = AAudioStream_setBufferSizeInFrames(stream_, buffer_size); if (buffer_size < 0) { RTC_LOG(LS_ERROR) << "Failed to change buffer size: " << AAudio_convertResultToText(buffer_size); return false; } - RTC_LOG(INFO) << "Buffer size changed to: " << buffer_size; + RTC_LOG(LS_INFO) << "Buffer size changed to: " << buffer_size; return true; } void AAudioWrapper::ClearInputStream(void* audio_data, int32_t num_frames) { - RTC_LOG(INFO) << "ClearInputStream"; + RTC_LOG(LS_INFO) << "ClearInputStream"; RTC_DCHECK(stream_); RTC_DCHECK(aaudio_thread_checker_.IsCurrent()); RTC_DCHECK_EQ(direction(), AAUDIO_DIRECTION_INPUT); @@ -357,7 +357,7 @@ int64_t AAudioWrapper::frames_read() const { } void AAudioWrapper::SetStreamConfiguration(AAudioStreamBuilder* builder) { - RTC_LOG(INFO) << "SetStreamConfiguration"; + RTC_LOG(LS_INFO) << "SetStreamConfiguration"; RTC_DCHECK(builder); RTC_DCHECK(thread_checker_.IsCurrent()); // Request usage of default primary output/input device. @@ -390,7 +390,7 @@ void AAudioWrapper::SetStreamConfiguration(AAudioStreamBuilder* builder) { } bool AAudioWrapper::OpenStream(AAudioStreamBuilder* builder) { - RTC_LOG(INFO) << "OpenStream"; + RTC_LOG(LS_INFO) << "OpenStream"; RTC_DCHECK(builder); AAudioStream* stream = nullptr; RETURN_ON_ERROR(AAudioStreamBuilder_openStream(builder, &stream), false); @@ -400,7 +400,7 @@ bool AAudioWrapper::OpenStream(AAudioStreamBuilder* builder) { } void AAudioWrapper::CloseStream() { - RTC_LOG(INFO) << "CloseStream"; + RTC_LOG(LS_INFO) << "CloseStream"; RTC_DCHECK(stream_); LOG_ON_ERROR(AAudioStream_close(stream_)); stream_ = nullptr; @@ -419,16 +419,16 @@ void AAudioWrapper::LogStreamConfiguration() { ss << ", direction=" << DirectionToString(direction()); ss << ", device id=" << AAudioStream_getDeviceId(stream_); ss << ", frames per callback=" << frames_per_callback(); - RTC_LOG(INFO) << ss.str(); + RTC_LOG(LS_INFO) << ss.str(); } void AAudioWrapper::LogStreamState() { - RTC_LOG(INFO) << "AAudio stream state: " - << AAudio_convertStreamStateToText(stream_state()); + RTC_LOG(LS_INFO) << "AAudio stream state: " + << AAudio_convertStreamStateToText(stream_state()); } bool AAudioWrapper::VerifyStreamConfiguration() { - RTC_LOG(INFO) << "VerifyStreamConfiguration"; + RTC_LOG(LS_INFO) << "VerifyStreamConfiguration"; RTC_DCHECK(stream_); // TODO(henrika): should we verify device ID as well? if (AAudioStream_getSampleRate(stream_) != audio_parameters().sample_rate()) { @@ -466,16 +466,16 @@ bool AAudioWrapper::VerifyStreamConfiguration() { } bool AAudioWrapper::OptimizeBuffers() { - RTC_LOG(INFO) << "OptimizeBuffers"; + RTC_LOG(LS_INFO) << "OptimizeBuffers"; RTC_DCHECK(stream_); // Maximum number of frames that can be filled without blocking. - RTC_LOG(INFO) << "max buffer capacity in frames: " - << buffer_capacity_in_frames(); + RTC_LOG(LS_INFO) << "max buffer capacity in frames: " + << buffer_capacity_in_frames(); // Query the number of frames that the application should read or write at // one time for optimal performance. int32_t frames_per_burst = AAudioStream_getFramesPerBurst(stream_); - RTC_LOG(INFO) << "frames per burst for optimal performance: " - << frames_per_burst; + RTC_LOG(LS_INFO) << "frames per burst for optimal performance: " + << frames_per_burst; frames_per_burst_ = frames_per_burst; if (direction() == AAUDIO_DIRECTION_INPUT) { // There is no point in calling setBufferSizeInFrames() for input streams @@ -492,7 +492,7 @@ bool AAudioWrapper::OptimizeBuffers() { return false; } // Maximum number of frames that can be filled without blocking. - RTC_LOG(INFO) << "buffer burst size in frames: " << buffer_size; + RTC_LOG(LS_INFO) << "buffer burst size in frames: " << buffer_size; return true; } diff --git a/modules/audio_device/android/audio_device_template.h b/modules/audio_device/android/audio_device_template.h index 3ea248f79e..999c5878c6 100644 --- a/modules/audio_device/android/audio_device_template.h +++ b/modules/audio_device/android/audio_device_template.h @@ -39,22 +39,22 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { output_(audio_manager_), input_(audio_manager_), initialized_(false) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_CHECK(audio_manager); audio_manager_->SetActiveAudioLayer(audio_layer); } - virtual ~AudioDeviceTemplate() { RTC_LOG(INFO) << __FUNCTION__; } + virtual ~AudioDeviceTemplate() { RTC_LOG(LS_INFO) << __FUNCTION__; } int32_t ActiveAudioLayer( AudioDeviceModule::AudioLayer& audioLayer) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; audioLayer = audio_layer_; return 0; } InitStatus Init() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); if (!audio_manager_->Init()) { @@ -74,7 +74,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } int32_t Terminate() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(thread_checker_.IsCurrent()); int32_t err = input_.Terminate(); err |= output_.Terminate(); @@ -85,18 +85,18 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } bool Initialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(thread_checker_.IsCurrent()); return initialized_; } int16_t PlayoutDevices() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 1; } int16_t RecordingDevices() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 1; } @@ -115,7 +115,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { int32_t SetPlayoutDevice(uint16_t index) override { // OK to use but it has no effect currently since device selection is // done using Andoid APIs instead. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 0; } @@ -127,7 +127,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { int32_t SetRecordingDevice(uint16_t index) override { // OK to use but it has no effect currently since device selection is // done using Andoid APIs instead. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 0; } @@ -137,41 +137,41 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } int32_t PlayoutIsAvailable(bool& available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; available = true; return 0; } int32_t InitPlayout() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.InitPlayout(); } bool PlayoutIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.PlayoutIsInitialized(); } int32_t RecordingIsAvailable(bool& available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; available = true; return 0; } int32_t InitRecording() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return input_.InitRecording(); } bool RecordingIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return input_.RecordingIsInitialized(); } int32_t StartPlayout() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (!audio_manager_->IsCommunicationModeEnabled()) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "The application should use MODE_IN_COMMUNICATION audio mode!"; } return output_.StartPlayout(); @@ -181,20 +181,20 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { // Avoid using audio manger (JNI/Java cost) if playout was inactive. if (!Playing()) return 0; - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; int32_t err = output_.StopPlayout(); return err; } bool Playing() const override { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; return output_.Playing(); } int32_t StartRecording() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (!audio_manager_->IsCommunicationModeEnabled()) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "The application should use MODE_IN_COMMUNICATION audio mode!"; } return input_.StartRecording(); @@ -202,7 +202,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { int32_t StopRecording() override { // Avoid using audio manger (JNI/Java cost) if recording was inactive. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (!Recording()) return 0; int32_t err = input_.StopRecording(); @@ -212,47 +212,47 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { bool Recording() const override { return input_.Recording(); } int32_t InitSpeaker() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 0; } bool SpeakerIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return true; } int32_t InitMicrophone() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return 0; } bool MicrophoneIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return true; } int32_t SpeakerVolumeIsAvailable(bool& available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.SpeakerVolumeIsAvailable(available); } int32_t SetSpeakerVolume(uint32_t volume) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.SetSpeakerVolume(volume); } int32_t SpeakerVolume(uint32_t& volume) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.SpeakerVolume(volume); } int32_t MaxSpeakerVolume(uint32_t& maxVolume) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.MaxSpeakerVolume(maxVolume); } int32_t MinSpeakerVolume(uint32_t& minVolume) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return output_.MinSpeakerVolume(minVolume); } @@ -299,13 +299,13 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { // Returns true if the audio manager has been configured to support stereo // and false otherwised. Default is mono. int32_t StereoPlayoutIsAvailable(bool& available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; available = audio_manager_->IsStereoPlayoutSupported(); return 0; } int32_t SetStereoPlayout(bool enable) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; bool available = audio_manager_->IsStereoPlayoutSupported(); // Android does not support changes between mono and stero on the fly. // Instead, the native audio layer is configured via the audio manager @@ -320,13 +320,13 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } int32_t StereoRecordingIsAvailable(bool& available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; available = audio_manager_->IsStereoRecordSupported(); return 0; } int32_t SetStereoRecording(bool enable) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; bool available = audio_manager_->IsStereoRecordSupported(); // Android does not support changes between mono and stero on the fly. // Instead, the native audio layer is configured via the audio manager @@ -336,7 +336,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } int32_t StereoRecording(bool& enabled) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; enabled = audio_manager_->IsStereoRecordSupported(); return 0; } @@ -349,7 +349,7 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { } void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; output_.AttachAudioBuffer(audioBuffer); input_.AttachAudioBuffer(audioBuffer); } @@ -367,13 +367,13 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { // a "Not Implemented" log will be filed. This non-perfect state will remain // until I have added full support for audio effects based on OpenSL ES APIs. bool BuiltInAECIsAvailable() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return audio_manager_->IsAcousticEchoCancelerSupported(); } // TODO(henrika): add implementation for OpenSL ES based audio as well. int32_t EnableBuiltInAEC(bool enable) override { - RTC_DLOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; RTC_CHECK(BuiltInAECIsAvailable()) << "HW AEC is not available"; return input_.EnableBuiltInAEC(enable); } @@ -383,13 +383,13 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { // TODO(henrika): add implementation for OpenSL ES based audio as well. // In addition, see comments for BuiltInAECIsAvailable(). bool BuiltInAGCIsAvailable() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return audio_manager_->IsAutomaticGainControlSupported(); } // TODO(henrika): add implementation for OpenSL ES based audio as well. int32_t EnableBuiltInAGC(bool enable) override { - RTC_DLOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; RTC_CHECK(BuiltInAGCIsAvailable()) << "HW AGC is not available"; return input_.EnableBuiltInAGC(enable); } @@ -399,13 +399,13 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { // TODO(henrika): add implementation for OpenSL ES based audio as well. // In addition, see comments for BuiltInAECIsAvailable(). bool BuiltInNSIsAvailable() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return audio_manager_->IsNoiseSuppressorSupported(); } // TODO(henrika): add implementation for OpenSL ES based audio as well. int32_t EnableBuiltInNS(bool enable) override { - RTC_DLOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; RTC_CHECK(BuiltInNSIsAvailable()) << "HW NS is not available"; return input_.EnableBuiltInNS(enable); } diff --git a/modules/audio_device/android/audio_device_unittest.cc b/modules/audio_device/android/audio_device_unittest.cc index 11f747e1d8..79cd69f2f1 100644 --- a/modules/audio_device/android/audio_device_unittest.cc +++ b/modules/audio_device/android/audio_device_unittest.cc @@ -892,7 +892,7 @@ TEST_F(AudioDeviceTest, StartRecordingVerifyCallbacks) { EXPECT_CALL( mock, RecordedDataIsAvailable(NotNull(), record_frames_per_10ms_buffer(), kBytesPerSample, record_channels(), - record_sample_rate(), _, 0, 0, false, _)) + record_sample_rate(), _, 0, 0, false, _, _)) .Times(AtLeast(kNumCallbacks)); EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock)); @@ -913,7 +913,7 @@ TEST_F(AudioDeviceTest, StartPlayoutAndRecordingVerifyCallbacks) { EXPECT_CALL( mock, RecordedDataIsAvailable(NotNull(), record_frames_per_10ms_buffer(), kBytesPerSample, record_channels(), - record_sample_rate(), _, 0, 0, false, _)) + record_sample_rate(), _, 0, 0, false, _, _)) .Times(AtLeast(kNumCallbacks)); EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock)); StartPlayout(); diff --git a/modules/audio_device/android/audio_manager.cc b/modules/audio_device/android/audio_manager.cc index 7de20656fd..0b55496619 100644 --- a/modules/audio_device/android/audio_manager.cc +++ b/modules/audio_device/android/audio_manager.cc @@ -33,11 +33,11 @@ AudioManager::JavaAudioManager::JavaAudioManager( is_device_blacklisted_for_open_sles_usage_( native_reg->GetMethodId("isDeviceBlacklistedForOpenSLESUsage", "()Z")) { - RTC_LOG(INFO) << "JavaAudioManager::ctor"; + RTC_LOG(LS_INFO) << "JavaAudioManager::ctor"; } AudioManager::JavaAudioManager::~JavaAudioManager() { - RTC_LOG(INFO) << "JavaAudioManager::~dtor"; + RTC_LOG(LS_INFO) << "JavaAudioManager::~dtor"; } bool AudioManager::JavaAudioManager::Init() { @@ -68,7 +68,7 @@ AudioManager::AudioManager() low_latency_playout_(false), low_latency_record_(false), delay_estimate_in_milliseconds_(0) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; RTC_CHECK(j_environment_); JNINativeMethod native_methods[] = { {"nativeCacheAudioParameters", "(IIIZZZZZZZIIJ)V", @@ -83,14 +83,14 @@ AudioManager::AudioManager() } AudioManager::~AudioManager() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); Close(); } void AudioManager::SetActiveAudioLayer( AudioDeviceModule::AudioLayer audio_layer) { - RTC_LOG(INFO) << "SetActiveAudioLayer: " << audio_layer; + RTC_LOG(LS_INFO) << "SetActiveAudioLayer: " << audio_layer; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); // Store the currently utilized audio layer. @@ -103,18 +103,18 @@ void AudioManager::SetActiveAudioLayer( (audio_layer == AudioDeviceModule::kAndroidJavaAudio) ? kHighLatencyModeDelayEstimateInMilliseconds : kLowLatencyModeDelayEstimateInMilliseconds; - RTC_LOG(INFO) << "delay_estimate_in_milliseconds: " - << delay_estimate_in_milliseconds_; + RTC_LOG(LS_INFO) << "delay_estimate_in_milliseconds: " + << delay_estimate_in_milliseconds_; } SLObjectItf AudioManager::GetOpenSLEngine() { - RTC_LOG(INFO) << "GetOpenSLEngine"; + RTC_LOG(LS_INFO) << "GetOpenSLEngine"; RTC_DCHECK(thread_checker_.IsCurrent()); // Only allow usage of OpenSL ES if such an audio layer has been specified. if (audio_layer_ != AudioDeviceModule::kAndroidOpenSLESAudio && audio_layer_ != AudioDeviceModule::kAndroidJavaInputAndOpenSLESOutputAudio) { - RTC_LOG(INFO) + RTC_LOG(LS_INFO) << "Unable to create OpenSL engine for the current audio layer: " << audio_layer_; return nullptr; @@ -123,7 +123,8 @@ SLObjectItf AudioManager::GetOpenSLEngine() { // If one already has been created, return existing object instead of // creating a new. if (engine_object_.Get() != nullptr) { - RTC_LOG(WARNING) << "The OpenSL ES engine object has already been created"; + RTC_LOG(LS_WARNING) + << "The OpenSL ES engine object has already been created"; return engine_object_.Get(); } // Create the engine object in thread safe mode. @@ -149,7 +150,7 @@ SLObjectItf AudioManager::GetOpenSLEngine() { } bool AudioManager::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); RTC_DCHECK_NE(audio_layer_, AudioDeviceModule::kPlatformDefaultAudio); @@ -162,7 +163,7 @@ bool AudioManager::Init() { } bool AudioManager::Close() { - RTC_LOG(INFO) << "Close"; + RTC_LOG(LS_INFO) << "Close"; RTC_DCHECK(thread_checker_.IsCurrent()); if (!initialized_) return true; @@ -273,7 +274,7 @@ void AudioManager::OnCacheAudioParameters(JNIEnv* env, jboolean a_audio, jint output_buffer_size, jint input_buffer_size) { - RTC_LOG(INFO) + RTC_LOG(LS_INFO) << "OnCacheAudioParameters: " "hardware_aec: " << static_cast(hardware_aec) diff --git a/modules/audio_device/android/audio_record_jni.cc b/modules/audio_device/android/audio_record_jni.cc index 2c28ab242d..9d7bf73097 100644 --- a/modules/audio_device/android/audio_record_jni.cc +++ b/modules/audio_device/android/audio_record_jni.cc @@ -34,7 +34,7 @@ class ScopedHistogramTimer { ~ScopedHistogramTimer() { const int64_t life_time_ms = rtc::TimeSince(start_time_ms_); RTC_HISTOGRAM_COUNTS_1000(histogram_name_, life_time_ms); - RTC_LOG(INFO) << histogram_name_ << ": " << life_time_ms; + RTC_LOG(LS_INFO) << histogram_name_ << ": " << life_time_ms; } private: @@ -93,7 +93,7 @@ AudioRecordJni::AudioRecordJni(AudioManager* audio_manager) initialized_(false), recording_(false), audio_device_buffer_(nullptr) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; RTC_DCHECK(audio_parameters_.is_valid()); RTC_CHECK(j_environment_); JNINativeMethod native_methods[] = { @@ -115,26 +115,26 @@ AudioRecordJni::AudioRecordJni(AudioManager* audio_manager) } AudioRecordJni::~AudioRecordJni() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); Terminate(); } int32_t AudioRecordJni::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); return 0; } int32_t AudioRecordJni::Terminate() { - RTC_LOG(INFO) << "Terminate"; + RTC_LOG(LS_INFO) << "Terminate"; RTC_DCHECK(thread_checker_.IsCurrent()); StopRecording(); return 0; } int32_t AudioRecordJni::InitRecording() { - RTC_LOG(INFO) << "InitRecording"; + RTC_LOG(LS_INFO) << "InitRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); RTC_DCHECK(!recording_); @@ -147,7 +147,7 @@ int32_t AudioRecordJni::InitRecording() { return -1; } frames_per_buffer_ = static_cast(frames_per_buffer); - RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_; + RTC_LOG(LS_INFO) << "frames_per_buffer: " << frames_per_buffer_; const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t); RTC_CHECK_EQ(direct_buffer_capacity_in_bytes_, frames_per_buffer_ * bytes_per_frame); @@ -157,7 +157,7 @@ int32_t AudioRecordJni::InitRecording() { } int32_t AudioRecordJni::StartRecording() { - RTC_LOG(INFO) << "StartRecording"; + RTC_LOG(LS_INFO) << "StartRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!recording_); if (!initialized_) { @@ -175,7 +175,7 @@ int32_t AudioRecordJni::StartRecording() { } int32_t AudioRecordJni::StopRecording() { - RTC_LOG(INFO) << "StopRecording"; + RTC_LOG(LS_INFO) << "StopRecording"; RTC_DCHECK(thread_checker_.IsCurrent()); if (!initialized_ || !recording_) { return 0; @@ -195,24 +195,24 @@ int32_t AudioRecordJni::StopRecording() { } void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { - RTC_LOG(INFO) << "AttachAudioBuffer"; + RTC_LOG(LS_INFO) << "AttachAudioBuffer"; RTC_DCHECK(thread_checker_.IsCurrent()); audio_device_buffer_ = audioBuffer; const int sample_rate_hz = audio_parameters_.sample_rate(); - RTC_LOG(INFO) << "SetRecordingSampleRate(" << sample_rate_hz << ")"; + RTC_LOG(LS_INFO) << "SetRecordingSampleRate(" << sample_rate_hz << ")"; audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz); const size_t channels = audio_parameters_.channels(); - RTC_LOG(INFO) << "SetRecordingChannels(" << channels << ")"; + RTC_LOG(LS_INFO) << "SetRecordingChannels(" << channels << ")"; audio_device_buffer_->SetRecordingChannels(channels); total_delay_in_milliseconds_ = audio_manager_->GetDelayEstimateInMilliseconds(); RTC_DCHECK_GT(total_delay_in_milliseconds_, 0); - RTC_LOG(INFO) << "total_delay_in_milliseconds: " - << total_delay_in_milliseconds_; + RTC_LOG(LS_INFO) << "total_delay_in_milliseconds: " + << total_delay_in_milliseconds_; } int32_t AudioRecordJni::EnableBuiltInAEC(bool enable) { - RTC_LOG(INFO) << "EnableBuiltInAEC(" << enable << ")"; + RTC_LOG(LS_INFO) << "EnableBuiltInAEC(" << enable << ")"; RTC_DCHECK(thread_checker_.IsCurrent()); return j_audio_record_->EnableBuiltInAEC(enable) ? 0 : -1; } @@ -223,7 +223,7 @@ int32_t AudioRecordJni::EnableBuiltInAGC(bool enable) { } int32_t AudioRecordJni::EnableBuiltInNS(bool enable) { - RTC_LOG(INFO) << "EnableBuiltInNS(" << enable << ")"; + RTC_LOG(LS_INFO) << "EnableBuiltInNS(" << enable << ")"; RTC_DCHECK(thread_checker_.IsCurrent()); return j_audio_record_->EnableBuiltInNS(enable) ? 0 : -1; } @@ -240,12 +240,12 @@ void JNICALL AudioRecordJni::CacheDirectBufferAddress(JNIEnv* env, void AudioRecordJni::OnCacheDirectBufferAddress(JNIEnv* env, jobject byte_buffer) { - RTC_LOG(INFO) << "OnCacheDirectBufferAddress"; + RTC_LOG(LS_INFO) << "OnCacheDirectBufferAddress"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!direct_buffer_address_); direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer); jlong capacity = env->GetDirectBufferCapacity(byte_buffer); - RTC_LOG(INFO) << "direct buffer capacity: " << capacity; + RTC_LOG(LS_INFO) << "direct buffer capacity: " << capacity; direct_buffer_capacity_in_bytes_ = static_cast(capacity); } @@ -274,7 +274,7 @@ void AudioRecordJni::OnDataIsRecorded(int length) { // of `playDelayMs` and `recDelayMs`, hence the distributions does not matter. audio_device_buffer_->SetVQEData(total_delay_in_milliseconds_, 0); if (audio_device_buffer_->DeliverRecordedData() == -1) { - RTC_LOG(INFO) << "AudioDeviceBuffer::DeliverRecordedData failed"; + RTC_LOG(LS_INFO) << "AudioDeviceBuffer::DeliverRecordedData failed"; } } diff --git a/modules/audio_device/android/audio_track_jni.cc b/modules/audio_device/android/audio_track_jni.cc index daaeeca1ea..178ccadfdb 100644 --- a/modules/audio_device/android/audio_track_jni.cc +++ b/modules/audio_device/android/audio_track_jni.cc @@ -103,7 +103,7 @@ AudioTrackJni::AudioTrackJni(AudioManager* audio_manager) initialized_(false), playing_(false), audio_device_buffer_(nullptr) { - RTC_LOG(INFO) << "ctor"; + RTC_LOG(LS_INFO) << "ctor"; RTC_DCHECK(audio_parameters_.is_valid()); RTC_CHECK(j_environment_); JNINativeMethod native_methods[] = { @@ -125,26 +125,26 @@ AudioTrackJni::AudioTrackJni(AudioManager* audio_manager) } AudioTrackJni::~AudioTrackJni() { - RTC_LOG(INFO) << "dtor"; + RTC_LOG(LS_INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); Terminate(); } int32_t AudioTrackJni::Init() { - RTC_LOG(INFO) << "Init"; + RTC_LOG(LS_INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); return 0; } int32_t AudioTrackJni::Terminate() { - RTC_LOG(INFO) << "Terminate"; + RTC_LOG(LS_INFO) << "Terminate"; RTC_DCHECK(thread_checker_.IsCurrent()); StopPlayout(); return 0; } int32_t AudioTrackJni::InitPlayout() { - RTC_LOG(INFO) << "InitPlayout"; + RTC_LOG(LS_INFO) << "InitPlayout"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); RTC_DCHECK(!playing_); @@ -158,7 +158,7 @@ int32_t AudioTrackJni::InitPlayout() { } int32_t AudioTrackJni::StartPlayout() { - RTC_LOG(INFO) << "StartPlayout"; + RTC_LOG(LS_INFO) << "StartPlayout"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!playing_); if (!initialized_) { @@ -175,7 +175,7 @@ int32_t AudioTrackJni::StartPlayout() { } int32_t AudioTrackJni::StopPlayout() { - RTC_LOG(INFO) << "StopPlayout"; + RTC_LOG(LS_INFO) << "StopPlayout"; RTC_DCHECK(thread_checker_.IsCurrent()); if (!initialized_ || !playing_) { return 0; @@ -200,7 +200,7 @@ int AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) { } int AudioTrackJni::SetSpeakerVolume(uint32_t volume) { - RTC_LOG(INFO) << "SetSpeakerVolume(" << volume << ")"; + RTC_LOG(LS_INFO) << "SetSpeakerVolume(" << volume << ")"; RTC_DCHECK(thread_checker_.IsCurrent()); return j_audio_track_->SetStreamVolume(volume) ? 0 : -1; } @@ -220,20 +220,20 @@ int AudioTrackJni::MinSpeakerVolume(uint32_t& min_volume) const { int AudioTrackJni::SpeakerVolume(uint32_t& volume) const { RTC_DCHECK(thread_checker_.IsCurrent()); volume = j_audio_track_->GetStreamVolume(); - RTC_LOG(INFO) << "SpeakerVolume: " << volume; + RTC_LOG(LS_INFO) << "SpeakerVolume: " << volume; return 0; } // TODO(henrika): possibly add stereo support. void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { - RTC_LOG(INFO) << "AttachAudioBuffer"; + RTC_LOG(LS_INFO) << "AttachAudioBuffer"; RTC_DCHECK(thread_checker_.IsCurrent()); audio_device_buffer_ = audioBuffer; const int sample_rate_hz = audio_parameters_.sample_rate(); - RTC_LOG(INFO) << "SetPlayoutSampleRate(" << sample_rate_hz << ")"; + RTC_LOG(LS_INFO) << "SetPlayoutSampleRate(" << sample_rate_hz << ")"; audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz); const size_t channels = audio_parameters_.channels(); - RTC_LOG(INFO) << "SetPlayoutChannels(" << channels << ")"; + RTC_LOG(LS_INFO) << "SetPlayoutChannels(" << channels << ")"; audio_device_buffer_->SetPlayoutChannels(channels); } @@ -249,16 +249,16 @@ void JNICALL AudioTrackJni::CacheDirectBufferAddress(JNIEnv* env, void AudioTrackJni::OnCacheDirectBufferAddress(JNIEnv* env, jobject byte_buffer) { - RTC_LOG(INFO) << "OnCacheDirectBufferAddress"; + RTC_LOG(LS_INFO) << "OnCacheDirectBufferAddress"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!direct_buffer_address_); direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer); jlong capacity = env->GetDirectBufferCapacity(byte_buffer); - RTC_LOG(INFO) << "direct buffer capacity: " << capacity; + RTC_LOG(LS_INFO) << "direct buffer capacity: " << capacity; direct_buffer_capacity_in_bytes_ = static_cast(capacity); const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t); frames_per_buffer_ = direct_buffer_capacity_in_bytes_ / bytes_per_frame; - RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_; + RTC_LOG(LS_INFO) << "frames_per_buffer: " << frames_per_buffer_; } JNI_FUNCTION_ALIGN diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java index 11ed669c3e..92f1c93524 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java @@ -95,8 +95,6 @@ public class WebRtcAudioEffects { // Returns true if the platform AEC should be excluded based on its UUID. // AudioEffect.queryEffects() can throw IllegalStateException. private static boolean isAcousticEchoCancelerExcludedByUUID() { - if (Build.VERSION.SDK_INT < 18) - return false; for (Descriptor d : getAvailableEffects()) { if (d.type.equals(AudioEffect.EFFECT_TYPE_AEC) && d.uuid.equals(AOSP_ACOUSTIC_ECHO_CANCELER)) { @@ -109,8 +107,6 @@ public class WebRtcAudioEffects { // Returns true if the platform NS should be excluded based on its UUID. // AudioEffect.queryEffects() can throw IllegalStateException. private static boolean isNoiseSuppressorExcludedByUUID() { - if (Build.VERSION.SDK_INT < 18) - return false; for (Descriptor d : getAvailableEffects()) { if (d.type.equals(AudioEffect.EFFECT_TYPE_NS) && d.uuid.equals(AOSP_NOISE_SUPPRESSOR)) { return true; @@ -121,15 +117,11 @@ public class WebRtcAudioEffects { // Returns true if the device supports Acoustic Echo Cancellation (AEC). private static boolean isAcousticEchoCancelerEffectAvailable() { - if (Build.VERSION.SDK_INT < 18) - return false; return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AEC); } // Returns true if the device supports Noise Suppression (NS). private static boolean isNoiseSuppressorEffectAvailable() { - if (Build.VERSION.SDK_INT < 18) - return false; return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_NS); } @@ -277,9 +269,6 @@ public class WebRtcAudioEffects { // As an example: Samsung Galaxy S6 includes an AGC in the descriptor but // AutomaticGainControl.isAvailable() returns false. private boolean effectTypeIsVoIP(UUID type) { - if (Build.VERSION.SDK_INT < 18) - return false; - return (AudioEffect.EFFECT_TYPE_AEC.equals(type) && isAcousticEchoCancelerSupported()) || (AudioEffect.EFFECT_TYPE_NS.equals(type) && isNoiseSuppressorSupported()); } diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java index b057c3a454..4457c6926e 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java @@ -258,6 +258,7 @@ public class WebRtcAudioManager { // as well. The NDK doc states that: "As of API level 21, lower latency // audio input is supported on select devices. To take advantage of this // feature, first confirm that lower latency output is available". + // RingRTC change (or ... retention?) to keep support for SDK >= 19. return Build.VERSION.SDK_INT >= 21 && isLowLatencyOutputSupported(); } @@ -301,9 +302,6 @@ public class WebRtcAudioManager { } private int getSampleRateForApiLevel() { - if (Build.VERSION.SDK_INT < 17) { - return WebRtcAudioUtils.getDefaultSampleRateHz(); - } String sampleRateString = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); return (sampleRateString == null) ? WebRtcAudioUtils.getDefaultSampleRateHz() : Integer.parseInt(sampleRateString); @@ -312,9 +310,6 @@ public class WebRtcAudioManager { // Returns the native output buffer size for low-latency output streams. private int getLowLatencyOutputFramesPerBuffer() { assertTrue(isLowLatencyOutputSupported()); - if (Build.VERSION.SDK_INT < 17) { - return DEFAULT_FRAME_PER_BUFFER; - } String framesPerBuffer = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); return framesPerBuffer == null ? DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer); diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java index e3988e1a36..5cfa982894 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java @@ -10,6 +10,7 @@ package org.webrtc.voiceengine; +// RingRTC change (or ... retention?) to keep support for SDK >= 19. import android.annotation.TargetApi; import android.content.Context; import android.media.AudioAttributes; @@ -46,6 +47,7 @@ public class WebRtcAudioTrack { // By default, WebRTC creates audio tracks with a usage attribute // corresponding to voice communications, such as telephony or VoIP. + // RingRTC change (or ... retention?) to keep support for SDK >= 19. private static final int DEFAULT_USAGE = getDefaultUsageAttribute(); private static int usageAttribute = DEFAULT_USAGE; @@ -60,6 +62,7 @@ public class WebRtcAudioTrack { usageAttribute = usage; } + // RingRTC change (or ... retention?) to keep support for SDK >= 19. private static int getDefaultUsageAttribute() { if (Build.VERSION.SDK_INT >= 21) { return AudioAttributes.USAGE_VOICE_COMMUNICATION; @@ -154,7 +157,7 @@ public class WebRtcAudioTrack { byteBuffer.put(emptyBytes); byteBuffer.position(0); } - int bytesWritten = writeBytes(audioTrack, byteBuffer, sizeInBytes); + int bytesWritten = audioTrack.write(byteBuffer, sizeInBytes, AudioTrack.WRITE_BLOCKING); if (bytesWritten != sizeInBytes) { Logging.e(TAG, "AudioTrack.write played invalid number of bytes: " + bytesWritten); // If a write() returns a negative value, an error has occurred. @@ -188,14 +191,6 @@ public class WebRtcAudioTrack { } } - private int writeBytes(AudioTrack audioTrack, ByteBuffer byteBuffer, int sizeInBytes) { - if (Build.VERSION.SDK_INT >= 21) { - return audioTrack.write(byteBuffer, sizeInBytes, AudioTrack.WRITE_BLOCKING); - } else { - return audioTrack.write(byteBuffer.array(), byteBuffer.arrayOffset(), sizeInBytes); - } - } - // Stops the inner thread loop which results in calling AudioTrack.stop(). // Does not block the calling thread. public void stopThread() { @@ -257,6 +252,7 @@ public class WebRtcAudioTrack { // Create an AudioTrack object and initialize its associated audio buffer. // The size of this buffer determines how long an AudioTrack can play // before running out of data. + // RingRTC change (or ... retention?) to keep support for SDK >= 19. if (Build.VERSION.SDK_INT >= 21) { // If we are on API level 21 or higher, it is possible to use a special AudioTrack // constructor that uses AudioAttributes and AudioFormat as input. It allows us to @@ -353,6 +349,7 @@ public class WebRtcAudioTrack { threadChecker.checkIsOnValidThread(); Logging.d(TAG, "setStreamVolume(" + volume + ")"); assertTrue(audioManager != null); + // RingRTC change (or ... retention?) to keep support for SDK >= 19. if (isVolumeFixed()) { Logging.e(TAG, "The device implements a fixed volume policy."); return false; @@ -361,6 +358,7 @@ public class WebRtcAudioTrack { return true; } + // RingRTC change (or ... retention?) to keep support for SDK >= 19. private boolean isVolumeFixed() { if (Build.VERSION.SDK_INT < 21) return false; @@ -384,6 +382,7 @@ public class WebRtcAudioTrack { + "max gain: " + AudioTrack.getMaxVolume()); } + // RingRTC change (or ... retention?) to keep support for SDK >= 19. // Creates and AudioTrack instance using AudioAttributes and AudioFormat as input. // It allows certain platforms or routing policies to use this information for more // refined volume or routing decisions. @@ -418,6 +417,7 @@ public class WebRtcAudioTrack { AudioManager.AUDIO_SESSION_ID_GENERATE); } + // RingRTC change (or ... retention?) to keep support for SDK >= 19. @SuppressWarnings("deprecation") // Deprecated in API level 25. private static AudioTrack createAudioTrackOnLowerThanLollipop( int sampleRateInHz, int channelConfig, int bufferSizeInBytes) { diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java index 61cab58f07..1a9b17a030 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java @@ -214,6 +214,7 @@ public final class WebRtcAudioUtils { + "BT SCO: " + audioManager.isBluetoothScoOn()); } + // RingRTC change (or ... retention?) to keep support for SDK >= 19. private static boolean isVolumeFixed(AudioManager audioManager) { if (Build.VERSION.SDK_INT < 21) { return false; @@ -233,6 +234,7 @@ public final class WebRtcAudioUtils { }; Logging.d(tag, "Audio State: "); // Some devices may not have volume controls and might use a fixed volume. + // RingRTC change (or ... retention?) to keep support for SDK >= 19. boolean fixedVolume = isVolumeFixed(audioManager); Logging.d(tag, " fixed volume=" + fixedVolume); if (!fixedVolume) { diff --git a/modules/audio_device/audio_device_buffer.cc b/modules/audio_device/audio_device_buffer.cc index 572982ef4c..873e5d6088 100644 --- a/modules/audio_device/audio_device_buffer.cc +++ b/modules/audio_device/audio_device_buffer.cc @@ -54,6 +54,7 @@ AudioDeviceBuffer::AudioDeviceBuffer(TaskQueueFactory* task_queue_factory) typing_status_(false), play_delay_ms_(0), rec_delay_ms_(0), + capture_timestamp_ns_(0), num_stat_reports_(0), last_timer_task_time_(0), rec_stat_count_(0), @@ -61,10 +62,10 @@ AudioDeviceBuffer::AudioDeviceBuffer(TaskQueueFactory* task_queue_factory) play_start_time_(0), only_silence_recorded_(true), log_stats_(false) { - RTC_LOG(INFO) << "AudioDeviceBuffer::ctor"; + RTC_LOG(LS_INFO) << "AudioDeviceBuffer::ctor"; #ifdef AUDIO_DEVICE_PLAYS_SINUS_TONE phase_ = 0.0; - RTC_LOG(WARNING) << "AUDIO_DEVICE_PLAYS_SINUS_TONE is defined!"; + RTC_LOG(LS_WARNING) << "AUDIO_DEVICE_PLAYS_SINUS_TONE is defined!"; #endif } @@ -72,13 +73,13 @@ AudioDeviceBuffer::~AudioDeviceBuffer() { RTC_DCHECK_RUN_ON(&main_thread_checker_); RTC_DCHECK(!playing_); RTC_DCHECK(!recording_); - RTC_LOG(INFO) << "AudioDeviceBuffer::~dtor"; + RTC_LOG(LS_INFO) << "AudioDeviceBuffer::~dtor"; } int32_t AudioDeviceBuffer::RegisterAudioCallback( AudioTransport* audio_callback) { RTC_DCHECK_RUN_ON(&main_thread_checker_); - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (playing_ || recording_) { RTC_LOG(LS_ERROR) << "Failed to set audio transport since media was active"; return -1; @@ -95,7 +96,7 @@ void AudioDeviceBuffer::StartPlayout() { if (playing_) { return; } - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // Clear members tracking playout stats and do it on the task queue. task_queue_.PostTask([this] { ResetPlayStats(); }); // Start a periodic timer based on task queue if not already done by the @@ -114,7 +115,7 @@ void AudioDeviceBuffer::StartRecording() { if (recording_) { return; } - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // Clear members tracking recording stats and do it on the task queue. task_queue_.PostTask([this] { ResetRecStats(); }); // Start a periodic timer based on task queue if not already done by the @@ -136,13 +137,14 @@ void AudioDeviceBuffer::StopPlayout() { if (!playing_) { return; } - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; playing_ = false; // Stop periodic logging if no more media is active. if (!recording_) { StopPeriodicLogging(); } - RTC_LOG(INFO) << "total playout time: " << rtc::TimeSince(play_start_time_); + RTC_LOG(LS_INFO) << "total playout time: " + << rtc::TimeSince(play_start_time_); } void AudioDeviceBuffer::StopRecording() { @@ -150,7 +152,7 @@ void AudioDeviceBuffer::StopRecording() { if (!recording_) { return; } - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; recording_ = false; // Stop periodic logging if no more media is active. if (!playing_) { @@ -170,20 +172,20 @@ void AudioDeviceBuffer::StopRecording() { if (time_since_start > kMinValidCallTimeTimeInMilliseconds) { const int only_zeros = static_cast(only_silence_recorded_); RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.RecordedOnlyZeros", only_zeros); - RTC_LOG(INFO) << "HISTOGRAM(WebRTC.Audio.RecordedOnlyZeros): " - << only_zeros; + RTC_LOG(LS_INFO) << "HISTOGRAM(WebRTC.Audio.RecordedOnlyZeros): " + << only_zeros; } - RTC_LOG(INFO) << "total recording time: " << time_since_start; + RTC_LOG(LS_INFO) << "total recording time: " << time_since_start; } int32_t AudioDeviceBuffer::SetRecordingSampleRate(uint32_t fsHz) { - RTC_LOG(INFO) << "SetRecordingSampleRate(" << fsHz << ")"; + RTC_LOG(LS_INFO) << "SetRecordingSampleRate(" << fsHz << ")"; rec_sample_rate_ = fsHz; return 0; } int32_t AudioDeviceBuffer::SetPlayoutSampleRate(uint32_t fsHz) { - RTC_LOG(INFO) << "SetPlayoutSampleRate(" << fsHz << ")"; + RTC_LOG(LS_INFO) << "SetPlayoutSampleRate(" << fsHz << ")"; play_sample_rate_ = fsHz; return 0; } @@ -197,13 +199,13 @@ uint32_t AudioDeviceBuffer::PlayoutSampleRate() const { } int32_t AudioDeviceBuffer::SetRecordingChannels(size_t channels) { - RTC_LOG(INFO) << "SetRecordingChannels(" << channels << ")"; + RTC_LOG(LS_INFO) << "SetRecordingChannels(" << channels << ")"; rec_channels_ = channels; return 0; } int32_t AudioDeviceBuffer::SetPlayoutChannels(size_t channels) { - RTC_LOG(INFO) << "SetPlayoutChannels(" << channels << ")"; + RTC_LOG(LS_INFO) << "SetPlayoutChannels(" << channels << ")"; play_channels_ = channels; return 0; } @@ -228,6 +230,12 @@ void AudioDeviceBuffer::SetVQEData(int play_delay_ms, int rec_delay_ms) { int32_t AudioDeviceBuffer::SetRecordedBuffer(const void* audio_buffer, size_t samples_per_channel) { + return SetRecordedBuffer(audio_buffer, samples_per_channel, 0); +} + +int32_t AudioDeviceBuffer::SetRecordedBuffer(const void* audio_buffer, + size_t samples_per_channel, + int64_t capture_timestamp_ns) { // Copy the complete input buffer to the local buffer. const size_t old_size = rec_buffer_.size(); rec_buffer_.SetData(static_cast(audio_buffer), @@ -238,6 +246,17 @@ int32_t AudioDeviceBuffer::SetRecordedBuffer(const void* audio_buffer, RTC_LOG(LS_INFO) << "Size of recording buffer: " << rec_buffer_.size(); } + // If the timestamp is less then or equal to zero, it's not valid and are + // ignored. If we do antimestamp alignment on them they might accidentally + // become greater then zero, and will be handled as if they were a correct + // timestamp. + capture_timestamp_ns_ = + (capture_timestamp_ns > 0) + ? rtc::kNumNanosecsPerMicrosec * + timestamp_aligner_.TranslateTimestamp( + capture_timestamp_ns_ / rtc::kNumNanosecsPerMicrosec, + rtc::TimeMicros()) + : capture_timestamp_ns; // Derive a new level value twice per second and check if it is non-zero. int16_t max_abs = 0; RTC_DCHECK_LT(rec_stat_count_, 50); @@ -270,7 +289,7 @@ int32_t AudioDeviceBuffer::DeliverRecordedData() { int32_t res = audio_transport_cb_->RecordedDataIsAvailable( rec_buffer_.data(), frames, bytes_per_frame, rec_channels_, rec_sample_rate_, total_delay_ms, 0, 0, typing_status_, - new_mic_level_dummy); + new_mic_level_dummy, capture_timestamp_ns_); if (res == -1) { RTC_LOG(LS_ERROR) << "RecordedDataIsAvailable() failed"; } @@ -408,21 +427,21 @@ void AudioDeviceBuffer::LogStats(LogState state) { ((100.0f * std::abs(rate - rec_sample_rate)) / rec_sample_rate)); RTC_HISTOGRAM_PERCENTAGE("WebRTC.Audio.RecordSampleRateOffsetInPercent", abs_diff_rate_in_percent); - RTC_LOG(INFO) << "[REC : " << time_since_last << "msec, " - << rec_sample_rate / 1000 << "kHz] callbacks: " - << stats.rec_callbacks - last_stats_.rec_callbacks - << ", " - "samples: " - << diff_samples - << ", " - "rate: " - << static_cast(rate + 0.5) - << ", " - "rate diff: " - << abs_diff_rate_in_percent - << "%, " - "level: " - << stats.max_rec_level; + RTC_LOG(LS_INFO) << "[REC : " << time_since_last << "msec, " + << rec_sample_rate / 1000 << "kHz] callbacks: " + << stats.rec_callbacks - last_stats_.rec_callbacks + << ", " + "samples: " + << diff_samples + << ", " + "rate: " + << static_cast(rate + 0.5) + << ", " + "rate diff: " + << abs_diff_rate_in_percent + << "%, " + "level: " + << stats.max_rec_level; } diff_samples = stats.play_samples - last_stats_.play_samples; @@ -434,21 +453,21 @@ void AudioDeviceBuffer::LogStats(LogState state) { ((100.0f * std::abs(rate - play_sample_rate)) / play_sample_rate)); RTC_HISTOGRAM_PERCENTAGE("WebRTC.Audio.PlayoutSampleRateOffsetInPercent", abs_diff_rate_in_percent); - RTC_LOG(INFO) << "[PLAY: " << time_since_last << "msec, " - << play_sample_rate / 1000 << "kHz] callbacks: " - << stats.play_callbacks - last_stats_.play_callbacks - << ", " - "samples: " - << diff_samples - << ", " - "rate: " - << static_cast(rate + 0.5) - << ", " - "rate diff: " - << abs_diff_rate_in_percent - << "%, " - "level: " - << stats.max_play_level; + RTC_LOG(LS_INFO) << "[PLAY: " << time_since_last << "msec, " + << play_sample_rate / 1000 << "kHz] callbacks: " + << stats.play_callbacks - last_stats_.play_callbacks + << ", " + "samples: " + << diff_samples + << ", " + "rate: " + << static_cast(rate + 0.5) + << ", " + "rate diff: " + << abs_diff_rate_in_percent + << "%, " + "level: " + << stats.max_play_level; } } last_stats_ = stats; diff --git a/modules/audio_device/audio_device_buffer.h b/modules/audio_device/audio_device_buffer.h index a0b7953194..9a6a88a1be 100644 --- a/modules/audio_device/audio_device_buffer.h +++ b/modules/audio_device/audio_device_buffer.h @@ -23,6 +23,7 @@ #include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_queue.h" #include "rtc_base/thread_annotations.h" +#include "rtc_base/timestamp_aligner.h" namespace webrtc { @@ -97,8 +98,13 @@ class AudioDeviceBuffer { size_t RecordingChannels() const; size_t PlayoutChannels() const; + // TODO(bugs.webrtc.org/13621) Deprecate this function virtual int32_t SetRecordedBuffer(const void* audio_buffer, size_t samples_per_channel); + + virtual int32_t SetRecordedBuffer(const void* audio_buffer, + size_t samples_per_channel, + int64_t capture_timestamp_ns); virtual void SetVQEData(int play_delay_ms, int rec_delay_ms); virtual int32_t DeliverRecordedData(); uint32_t NewMicLevel() const; @@ -187,6 +193,9 @@ class AudioDeviceBuffer { int play_delay_ms_; int rec_delay_ms_; + // Capture timestamp. + int64_t capture_timestamp_ns_; + // Counts number of times LogStats() has been called. size_t num_stat_reports_ RTC_GUARDED_BY(task_queue_); @@ -219,6 +228,10 @@ class AudioDeviceBuffer { // being printed in the LogStats() task. bool log_stats_ RTC_GUARDED_BY(task_queue_); + // Used for converting capture timestaps (received from AudioRecordThread + // via AudioRecordJni::DataIsRecorded) to RTC clock. + rtc::TimestampAligner timestamp_aligner_; + // Should *never* be defined in production builds. Only used for testing. // When defined, the output signal will be replaced by a sinus tone at 440Hz. #ifdef AUDIO_DEVICE_PLAYS_SINUS_TONE diff --git a/modules/audio_device/audio_device_data_observer.cc b/modules/audio_device/audio_device_data_observer.cc index be78fd16d7..e54494c285 100644 --- a/modules/audio_device/audio_device_data_observer.cc +++ b/modules/audio_device/audio_device_data_observer.cc @@ -45,17 +45,34 @@ class ADMWrapper : public AudioDeviceModule, public AudioTransport { // Make sure we have a valid ADM before returning it to user. bool IsValid() { return is_valid_; } + int32_t RecordedDataIsAvailable(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samples_per_sec, + uint32_t total_delay_ms, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel) override { + return RecordedDataIsAvailable(audioSamples, nSamples, nBytesPerSample, + nChannels, samples_per_sec, total_delay_ms, + clockDrift, currentMicLevel, keyPressed, + newMicLevel, /*capture_timestamp_ns*/ 0); + } + // AudioTransport methods overrides. int32_t RecordedDataIsAvailable(const void* audioSamples, - const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samples_per_sec, - const uint32_t total_delay_ms, - const int32_t clockDrift, - const uint32_t currentMicLevel, - const bool keyPressed, - uint32_t& newMicLevel) override { + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samples_per_sec, + uint32_t total_delay_ms, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + int64_t capture_timestamp_ns) override { int32_t res = 0; // Capture PCM data of locally captured audio. if (observer_) { @@ -67,7 +84,8 @@ class ADMWrapper : public AudioDeviceModule, public AudioTransport { if (audio_transport_) { res = audio_transport_->RecordedDataIsAvailable( audioSamples, nSamples, nBytesPerSample, nChannels, samples_per_sec, - total_delay_ms, clockDrift, currentMicLevel, keyPressed, newMicLevel); + total_delay_ms, clockDrift, currentMicLevel, keyPressed, newMicLevel, + capture_timestamp_ns); } return res; @@ -110,7 +128,7 @@ class ADMWrapper : public AudioDeviceModule, public AudioTransport { void* audio_data, int64_t* elapsed_time_ms, int64_t* ntp_time_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // Override AudioDeviceModule's RegisterAudioCallback method to remember the diff --git a/modules/audio_device/audio_device_impl.cc b/modules/audio_device/audio_device_impl.cc index 604289b9e3..35a1414620 100644 --- a/modules/audio_device/audio_device_impl.cc +++ b/modules/audio_device/audio_device_impl.cc @@ -73,7 +73,7 @@ namespace webrtc { rtc::scoped_refptr AudioDeviceModule::Create( AudioLayer audio_layer, TaskQueueFactory* task_queue_factory) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return AudioDeviceModule::CreateForTest(audio_layer, task_queue_factory); } @@ -81,7 +81,7 @@ rtc::scoped_refptr AudioDeviceModule::Create( rtc::scoped_refptr AudioDeviceModule::CreateForTest( AudioLayer audio_layer, TaskQueueFactory* task_queue_factory) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // The "AudioDeviceModule::kWindowsCoreAudio2" audio layer has its own // dedicated factory method which should be used instead. @@ -118,31 +118,31 @@ AudioDeviceModuleImpl::AudioDeviceModuleImpl( AudioLayer audio_layer, TaskQueueFactory* task_queue_factory) : audio_layer_(audio_layer), audio_device_buffer_(task_queue_factory) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; } int32_t AudioDeviceModuleImpl::CheckPlatform() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // Ensure that the current platform is supported PlatformType platform(kPlatformNotSupported); #if defined(_WIN32) platform = kPlatformWin32; - RTC_LOG(INFO) << "current platform is Win32"; + RTC_LOG(LS_INFO) << "current platform is Win32"; #elif defined(WEBRTC_ANDROID) platform = kPlatformAndroid; - RTC_LOG(INFO) << "current platform is Android"; + RTC_LOG(LS_INFO) << "current platform is Android"; #elif defined(WEBRTC_LINUX) platform = kPlatformLinux; - RTC_LOG(INFO) << "current platform is Linux"; + RTC_LOG(LS_INFO) << "current platform is Linux"; #elif defined(WEBRTC_IOS) platform = kPlatformIOS; - RTC_LOG(INFO) << "current platform is IOS"; + RTC_LOG(LS_INFO) << "current platform is IOS"; #elif defined(WEBRTC_MAC) platform = kPlatformMac; - RTC_LOG(INFO) << "current platform is Mac"; + RTC_LOG(LS_INFO) << "current platform is Mac"; #endif if (platform == kPlatformNotSupported) { - RTC_LOG(LERROR) + RTC_LOG(LS_ERROR) << "current platform is not supported => this module will self " "destruct!"; return -1; @@ -152,19 +152,19 @@ int32_t AudioDeviceModuleImpl::CheckPlatform() { } int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; // Dummy ADM implementations if build flags are set. #if defined(WEBRTC_DUMMY_AUDIO_BUILD) audio_device_.reset(new AudioDeviceDummy()); - RTC_LOG(INFO) << "Dummy Audio APIs will be utilized"; + RTC_LOG(LS_INFO) << "Dummy Audio APIs will be utilized"; #elif defined(WEBRTC_DUMMY_FILE_DEVICES) audio_device_.reset(FileAudioDeviceFactory::CreateFileAudioDevice()); if (audio_device_) { - RTC_LOG(INFO) << "Will use file-playing dummy device."; + RTC_LOG(LS_INFO) << "Will use file-playing dummy device."; } else { // Create a dummy device instead. audio_device_.reset(new AudioDeviceDummy()); - RTC_LOG(INFO) << "Dummy Audio APIs will be utilized"; + RTC_LOG(LS_INFO) << "Dummy Audio APIs will be utilized"; } // Real (non-dummy) ADM implementations. @@ -174,10 +174,10 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { #if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) if ((audio_layer == kWindowsCoreAudio) || (audio_layer == kPlatformDefaultAudio)) { - RTC_LOG(INFO) << "Attempting to use the Windows Core Audio APIs..."; + RTC_LOG(LS_INFO) << "Attempting to use the Windows Core Audio APIs..."; if (AudioDeviceWindowsCore::CoreAudioIsSupported()) { audio_device_.reset(new AudioDeviceWindowsCore()); - RTC_LOG(INFO) << "Windows Core Audio APIs will be utilized"; + RTC_LOG(LS_INFO) << "Windows Core Audio APIs will be utilized"; } } #endif // defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) @@ -251,11 +251,11 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { // - kPlatformDefaultAudio => ALSA, and // - kLinuxAlsaAudio => ALSA, and // - kLinuxPulseAudio => Invalid selection. - RTC_LOG(WARNING) << "PulseAudio is disabled using build flag."; + RTC_LOG(LS_WARNING) << "PulseAudio is disabled using build flag."; if ((audio_layer == kLinuxAlsaAudio) || (audio_layer == kPlatformDefaultAudio)) { audio_device_.reset(new AudioDeviceLinuxALSA()); - RTC_LOG(INFO) << "Linux ALSA APIs will be utilized."; + RTC_LOG(LS_INFO) << "Linux ALSA APIs will be utilized."; } #else // Build flag 'rtc_include_pulse_audio' is set to true (default). In this @@ -263,15 +263,15 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { // - kPlatformDefaultAudio => PulseAudio, and // - kLinuxPulseAudio => PulseAudio, and // - kLinuxAlsaAudio => ALSA (supported but not default). - RTC_LOG(INFO) << "PulseAudio support is enabled."; + RTC_LOG(LS_INFO) << "PulseAudio support is enabled."; if ((audio_layer == kLinuxPulseAudio) || (audio_layer == kPlatformDefaultAudio)) { // Linux PulseAudio implementation is default. audio_device_.reset(new AudioDeviceLinuxPulse()); - RTC_LOG(INFO) << "Linux PulseAudio APIs will be utilized"; + RTC_LOG(LS_INFO) << "Linux PulseAudio APIs will be utilized"; } else if (audio_layer == kLinuxAlsaAudio) { audio_device_.reset(new AudioDeviceLinuxALSA()); - RTC_LOG(WARNING) << "Linux ALSA APIs will be utilized."; + RTC_LOG(LS_WARNING) << "Linux ALSA APIs will be utilized."; } #endif // #if !defined(WEBRTC_ENABLE_LINUX_PULSE) #endif // #if defined(WEBRTC_LINUX) @@ -281,7 +281,7 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { if (audio_layer == kPlatformDefaultAudio) { audio_device_.reset( new ios_adm::AudioDeviceIOS(/*bypass_voice_processing=*/false)); - RTC_LOG(INFO) << "iPhone Audio APIs will be utilized."; + RTC_LOG(LS_INFO) << "iPhone Audio APIs will be utilized."; } // END #if defined(WEBRTC_IOS) @@ -289,14 +289,14 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { #elif defined(WEBRTC_MAC) if (audio_layer == kPlatformDefaultAudio) { audio_device_.reset(new AudioDeviceMac()); - RTC_LOG(INFO) << "Mac OS X Audio APIs will be utilized."; + RTC_LOG(LS_INFO) << "Mac OS X Audio APIs will be utilized."; } #endif // WEBRTC_MAC // Dummy ADM implementation. if (audio_layer == kDummyAudio) { audio_device_.reset(new AudioDeviceDummy()); - RTC_LOG(INFO) << "Dummy Audio APIs will be utilized."; + RTC_LOG(LS_INFO) << "Dummy Audio APIs will be utilized."; } #endif // if defined(WEBRTC_DUMMY_AUDIO_BUILD) @@ -309,17 +309,17 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() { } int32_t AudioDeviceModuleImpl::AttachAudioBuffer() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; audio_device_->AttachAudioBuffer(&audio_device_buffer_); return 0; } AudioDeviceModuleImpl::~AudioDeviceModuleImpl() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; } int32_t AudioDeviceModuleImpl::ActiveAudioLayer(AudioLayer* audioLayer) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; AudioLayer activeAudio; if (audio_device_->ActiveAudioLayer(activeAudio) == -1) { return -1; @@ -329,7 +329,7 @@ int32_t AudioDeviceModuleImpl::ActiveAudioLayer(AudioLayer* audioLayer) const { } int32_t AudioDeviceModuleImpl::Init() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; if (initialized_) return 0; RTC_CHECK(audio_device_); @@ -346,7 +346,7 @@ int32_t AudioDeviceModuleImpl::Init() { } int32_t AudioDeviceModuleImpl::Terminate() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; if (!initialized_) return 0; if (audio_device_->Terminate() == -1) { @@ -357,65 +357,65 @@ int32_t AudioDeviceModuleImpl::Terminate() { } bool AudioDeviceModuleImpl::Initialized() const { - RTC_LOG(INFO) << __FUNCTION__ << ": " << initialized_; + RTC_LOG(LS_INFO) << __FUNCTION__ << ": " << initialized_; return initialized_; } int32_t AudioDeviceModuleImpl::InitSpeaker() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); return audio_device_->InitSpeaker(); } int32_t AudioDeviceModuleImpl::InitMicrophone() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); return audio_device_->InitMicrophone(); } int32_t AudioDeviceModuleImpl::SpeakerVolumeIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->SpeakerVolumeIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetSpeakerVolume(uint32_t volume) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << volume << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << volume << ")"; CHECKinitialized_(); return audio_device_->SetSpeakerVolume(volume); } int32_t AudioDeviceModuleImpl::SpeakerVolume(uint32_t* volume) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); uint32_t level = 0; if (audio_device_->SpeakerVolume(level) == -1) { return -1; } *volume = level; - RTC_LOG(INFO) << "output: " << *volume; + RTC_LOG(LS_INFO) << "output: " << *volume; return 0; } bool AudioDeviceModuleImpl::SpeakerIsInitialized() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); bool isInitialized = audio_device_->SpeakerIsInitialized(); - RTC_LOG(INFO) << "output: " << isInitialized; + RTC_LOG(LS_INFO) << "output: " << isInitialized; return isInitialized; } bool AudioDeviceModuleImpl::MicrophoneIsInitialized() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); bool isInitialized = audio_device_->MicrophoneIsInitialized(); - RTC_LOG(INFO) << "output: " << isInitialized; + RTC_LOG(LS_INFO) << "output: " << isInitialized; return isInitialized; } @@ -440,119 +440,119 @@ int32_t AudioDeviceModuleImpl::MinSpeakerVolume(uint32_t* minVolume) const { } int32_t AudioDeviceModuleImpl::SpeakerMuteIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->SpeakerMuteIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetSpeakerMute(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); return audio_device_->SetSpeakerMute(enable); } int32_t AudioDeviceModuleImpl::SpeakerMute(bool* enabled) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool muted = false; if (audio_device_->SpeakerMute(muted) == -1) { return -1; } *enabled = muted; - RTC_LOG(INFO) << "output: " << muted; + RTC_LOG(LS_INFO) << "output: " << muted; return 0; } int32_t AudioDeviceModuleImpl::MicrophoneMuteIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->MicrophoneMuteIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetMicrophoneMute(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); return (audio_device_->SetMicrophoneMute(enable)); } int32_t AudioDeviceModuleImpl::MicrophoneMute(bool* enabled) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool muted = false; if (audio_device_->MicrophoneMute(muted) == -1) { return -1; } *enabled = muted; - RTC_LOG(INFO) << "output: " << muted; + RTC_LOG(LS_INFO) << "output: " << muted; return 0; } int32_t AudioDeviceModuleImpl::MicrophoneVolumeIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->MicrophoneVolumeIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetMicrophoneVolume(uint32_t volume) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << volume << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << volume << ")"; CHECKinitialized_(); return (audio_device_->SetMicrophoneVolume(volume)); } int32_t AudioDeviceModuleImpl::MicrophoneVolume(uint32_t* volume) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); uint32_t level = 0; if (audio_device_->MicrophoneVolume(level) == -1) { return -1; } *volume = level; - RTC_LOG(INFO) << "output: " << *volume; + RTC_LOG(LS_INFO) << "output: " << *volume; return 0; } int32_t AudioDeviceModuleImpl::StereoRecordingIsAvailable( bool* available) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->StereoRecordingIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetStereoRecording(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); if (audio_device_->RecordingIsInitialized()) { - RTC_LOG(LERROR) + RTC_LOG(LS_ERROR) << "unable to set stereo mode after recording is initialized"; return -1; } if (audio_device_->SetStereoRecording(enable) == -1) { if (enable) { - RTC_LOG(WARNING) << "failed to enable stereo recording"; + RTC_LOG(LS_WARNING) << "failed to enable stereo recording"; } return -1; } @@ -565,39 +565,39 @@ int32_t AudioDeviceModuleImpl::SetStereoRecording(bool enable) { } int32_t AudioDeviceModuleImpl::StereoRecording(bool* enabled) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool stereo = false; if (audio_device_->StereoRecording(stereo) == -1) { return -1; } *enabled = stereo; - RTC_LOG(INFO) << "output: " << stereo; + RTC_LOG(LS_INFO) << "output: " << stereo; return 0; } int32_t AudioDeviceModuleImpl::StereoPlayoutIsAvailable(bool* available) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->StereoPlayoutIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::SetStereoPlayout(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); if (audio_device_->PlayoutIsInitialized()) { - RTC_LOG(LERROR) + RTC_LOG(LS_ERROR) << "unable to set stereo mode while playing side is initialized"; return -1; } if (audio_device_->SetStereoPlayout(enable)) { - RTC_LOG(WARNING) << "stereo playout is not supported"; + RTC_LOG(LS_WARNING) << "stereo playout is not supported"; return -1; } int8_t nChannels(1); @@ -609,38 +609,38 @@ int32_t AudioDeviceModuleImpl::SetStereoPlayout(bool enable) { } int32_t AudioDeviceModuleImpl::StereoPlayout(bool* enabled) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool stereo = false; if (audio_device_->StereoPlayout(stereo) == -1) { return -1; } *enabled = stereo; - RTC_LOG(INFO) << "output: " << stereo; + RTC_LOG(LS_INFO) << "output: " << stereo; return 0; } int32_t AudioDeviceModuleImpl::PlayoutIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->PlayoutIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } int32_t AudioDeviceModuleImpl::RecordingIsAvailable(bool* available) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); bool isAvailable = false; if (audio_device_->RecordingIsAvailable(isAvailable) == -1) { return -1; } *available = isAvailable; - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return 0; } @@ -666,21 +666,21 @@ int32_t AudioDeviceModuleImpl::MinMicrophoneVolume(uint32_t* minVolume) const { // RingRTC changes to add some useful logging int16_t AudioDeviceModuleImpl::PlayoutDevices() { - RTC_LOG(LS_VERBOSE) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); uint16_t nPlayoutDevices = audio_device_->PlayoutDevices(); - RTC_LOG(LS_VERBOSE) << "output: " << nPlayoutDevices; + RTC_LOG(LS_INFO) << "output: " << nPlayoutDevices; return (int16_t)(nPlayoutDevices); } int32_t AudioDeviceModuleImpl::SetPlayoutDevice(uint16_t index) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << index << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << index << ")"; CHECKinitialized_(); return audio_device_->SetPlayoutDevice(index); } int32_t AudioDeviceModuleImpl::SetPlayoutDevice(WindowsDeviceType device) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); return audio_device_->SetPlayoutDevice(device); } @@ -689,7 +689,7 @@ int32_t AudioDeviceModuleImpl::PlayoutDeviceName( uint16_t index, char name[kAdmMaxDeviceNameSize], char guid[kAdmMaxGuidSize]) { - RTC_LOG(LS_VERBOSE) << __FUNCTION__ << "(" << index << ", ...)"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << index << ", ...)"; CHECKinitialized_(); if (name == NULL) { return -1; @@ -698,10 +698,10 @@ int32_t AudioDeviceModuleImpl::PlayoutDeviceName( return -1; } if (name != NULL) { - RTC_LOG(LS_VERBOSE) << "output: name = " << name; + RTC_LOG(LS_INFO) << "output: name = " << name; } if (guid != NULL) { - RTC_LOG(LS_VERBOSE) << "output: guid = " << guid; + RTC_LOG(LS_INFO) << "output: guid = " << guid; } return 0; } @@ -710,7 +710,7 @@ int32_t AudioDeviceModuleImpl::RecordingDeviceName( uint16_t index, char name[kAdmMaxDeviceNameSize], char guid[kAdmMaxGuidSize]) { - RTC_LOG(LS_VERBOSE) << __FUNCTION__ << "(" << index << ", ...)"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << index << ", ...)"; CHECKinitialized_(); if (name == NULL) { return -1; @@ -719,137 +719,137 @@ int32_t AudioDeviceModuleImpl::RecordingDeviceName( return -1; } if (name != NULL) { - RTC_LOG(LS_VERBOSE) << "output: name = " << name; + RTC_LOG(LS_INFO) << "output: name = " << name; } if (guid != NULL) { - RTC_LOG(LS_VERBOSE) << "output: guid = " << guid; + RTC_LOG(LS_INFO) << "output: guid = " << guid; } return 0; } int16_t AudioDeviceModuleImpl::RecordingDevices() { - RTC_LOG(LS_VERBOSE) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); uint16_t nRecordingDevices = audio_device_->RecordingDevices(); - RTC_LOG(LS_VERBOSE) << "output: " << nRecordingDevices; + RTC_LOG(LS_INFO) << "output: " << nRecordingDevices; return (int16_t)nRecordingDevices; } int32_t AudioDeviceModuleImpl::SetRecordingDevice(uint16_t index) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << index << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << index << ")"; CHECKinitialized_(); return audio_device_->SetRecordingDevice(index); } int32_t AudioDeviceModuleImpl::SetRecordingDevice(WindowsDeviceType device) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); return audio_device_->SetRecordingDevice(device); } int32_t AudioDeviceModuleImpl::InitPlayout() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); if (PlayoutIsInitialized()) { return 0; } int32_t result = audio_device_->InitPlayout(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.InitPlayoutSuccess", static_cast(result == 0)); return result; } int32_t AudioDeviceModuleImpl::InitRecording() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); if (RecordingIsInitialized()) { return 0; } int32_t result = audio_device_->InitRecording(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.InitRecordingSuccess", static_cast(result == 0)); return result; } bool AudioDeviceModuleImpl::PlayoutIsInitialized() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); return audio_device_->PlayoutIsInitialized(); } bool AudioDeviceModuleImpl::RecordingIsInitialized() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); return audio_device_->RecordingIsInitialized(); } int32_t AudioDeviceModuleImpl::StartPlayout() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); if (Playing()) { return 0; } audio_device_buffer_.StartPlayout(); int32_t result = audio_device_->StartPlayout(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StartPlayoutSuccess", static_cast(result == 0)); return result; } int32_t AudioDeviceModuleImpl::StopPlayout() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); int32_t result = audio_device_->StopPlayout(); audio_device_buffer_.StopPlayout(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StopPlayoutSuccess", static_cast(result == 0)); return result; } bool AudioDeviceModuleImpl::Playing() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); return audio_device_->Playing(); } int32_t AudioDeviceModuleImpl::StartRecording() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); if (Recording()) { return 0; } audio_device_buffer_.StartRecording(); int32_t result = audio_device_->StartRecording(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StartRecordingSuccess", static_cast(result == 0)); return result; } int32_t AudioDeviceModuleImpl::StopRecording() { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); int32_t result = audio_device_->StopRecording(); audio_device_buffer_.StopRecording(); - RTC_LOG(INFO) << "output: " << result; + RTC_LOG(LS_INFO) << "output: " << result; RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StopRecordingSuccess", static_cast(result == 0)); return result; } bool AudioDeviceModuleImpl::Recording() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); return audio_device_->Recording(); } int32_t AudioDeviceModuleImpl::RegisterAudioCallback( AudioTransport* audioCallback) { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; return audio_device_buffer_.RegisterAudioCallback(audioCallback); } @@ -857,7 +857,7 @@ int32_t AudioDeviceModuleImpl::PlayoutDelay(uint16_t* delayMS) const { CHECKinitialized_(); uint16_t delay = 0; if (audio_device_->PlayoutDelay(delay) == -1) { - RTC_LOG(LERROR) << "failed to retrieve the playout delay"; + RTC_LOG(LS_ERROR) << "failed to retrieve the playout delay"; return -1; } *delayMS = delay; @@ -865,87 +865,87 @@ int32_t AudioDeviceModuleImpl::PlayoutDelay(uint16_t* delayMS) const { } bool AudioDeviceModuleImpl::BuiltInAECIsAvailable() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); bool isAvailable = audio_device_->BuiltInAECIsAvailable(); - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return isAvailable; } int32_t AudioDeviceModuleImpl::EnableBuiltInAEC(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); int32_t ok = audio_device_->EnableBuiltInAEC(enable); - RTC_LOG(INFO) << "output: " << ok; + RTC_LOG(LS_INFO) << "output: " << ok; return ok; } bool AudioDeviceModuleImpl::BuiltInAGCIsAvailable() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); bool isAvailable = audio_device_->BuiltInAGCIsAvailable(); - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return isAvailable; } int32_t AudioDeviceModuleImpl::EnableBuiltInAGC(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); int32_t ok = audio_device_->EnableBuiltInAGC(enable); - RTC_LOG(INFO) << "output: " << ok; + RTC_LOG(LS_INFO) << "output: " << ok; return ok; } bool AudioDeviceModuleImpl::BuiltInNSIsAvailable() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized__BOOL(); bool isAvailable = audio_device_->BuiltInNSIsAvailable(); - RTC_LOG(INFO) << "output: " << isAvailable; + RTC_LOG(LS_INFO) << "output: " << isAvailable; return isAvailable; } int32_t AudioDeviceModuleImpl::EnableBuiltInNS(bool enable) { - RTC_LOG(INFO) << __FUNCTION__ << "(" << enable << ")"; + RTC_LOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; CHECKinitialized_(); int32_t ok = audio_device_->EnableBuiltInNS(enable); - RTC_LOG(INFO) << "output: " << ok; + RTC_LOG(LS_INFO) << "output: " << ok; return ok; } int32_t AudioDeviceModuleImpl::GetPlayoutUnderrunCount() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; CHECKinitialized_(); int32_t underrunCount = audio_device_->GetPlayoutUnderrunCount(); - RTC_LOG(INFO) << "output: " << underrunCount; + RTC_LOG(LS_INFO) << "output: " << underrunCount; return underrunCount; } #if defined(WEBRTC_IOS) int AudioDeviceModuleImpl::GetPlayoutAudioParameters( AudioParameters* params) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; int r = audio_device_->GetPlayoutAudioParameters(params); - RTC_LOG(INFO) << "output: " << r; + RTC_LOG(LS_INFO) << "output: " << r; return r; } int AudioDeviceModuleImpl::GetRecordAudioParameters( AudioParameters* params) const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; int r = audio_device_->GetRecordAudioParameters(params); - RTC_LOG(INFO) << "output: " << r; + RTC_LOG(LS_INFO) << "output: " << r; return r; } #endif // WEBRTC_IOS AudioDeviceModuleImpl::PlatformType AudioDeviceModuleImpl::Platform() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; return platform_type_; } AudioDeviceModule::AudioLayer AudioDeviceModuleImpl::PlatformAudioLayer() const { - RTC_LOG(INFO) << __FUNCTION__; + RTC_LOG(LS_INFO) << __FUNCTION__; return audio_layer_; } diff --git a/modules/audio_device/audio_device_unittest.cc b/modules/audio_device/audio_device_unittest.cc index a0ec149e69..51a8575b66 100644 --- a/modules/audio_device/audio_device_unittest.cc +++ b/modules/audio_device/audio_device_unittest.cc @@ -176,7 +176,7 @@ class FifoAudioStream : public AudioStream { (static_cast(buffer[2 * i]) + buffer[2 * i + 1]) / 2; } } else { - RTC_NOTREACHED() << "Required conversion is not support"; + RTC_DCHECK_NOTREACHED() << "Required conversion is not support"; } fifo_.pop_front(); } diff --git a/modules/audio_device/fine_audio_buffer.cc b/modules/audio_device/fine_audio_buffer.cc index 4f3f48c677..86240da196 100644 --- a/modules/audio_device/fine_audio_buffer.cc +++ b/modules/audio_device/fine_audio_buffer.cc @@ -29,21 +29,21 @@ FineAudioBuffer::FineAudioBuffer(AudioDeviceBuffer* audio_device_buffer) playout_channels_(audio_device_buffer->PlayoutChannels()), record_channels_(audio_device_buffer->RecordingChannels()) { RTC_DCHECK(audio_device_buffer_); - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (IsReadyForPlayout()) { - RTC_DLOG(INFO) << "playout_samples_per_channel_10ms: " - << playout_samples_per_channel_10ms_; - RTC_DLOG(INFO) << "playout_channels: " << playout_channels_; + RTC_DLOG(LS_INFO) << "playout_samples_per_channel_10ms: " + << playout_samples_per_channel_10ms_; + RTC_DLOG(LS_INFO) << "playout_channels: " << playout_channels_; } if (IsReadyForRecord()) { - RTC_DLOG(INFO) << "record_samples_per_channel_10ms: " - << record_samples_per_channel_10ms_; - RTC_DLOG(INFO) << "record_channels: " << record_channels_; + RTC_DLOG(LS_INFO) << "record_samples_per_channel_10ms: " + << record_samples_per_channel_10ms_; + RTC_DLOG(LS_INFO) << "record_channels: " << record_channels_; } } FineAudioBuffer::~FineAudioBuffer() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; } void FineAudioBuffer::ResetPlayout() { diff --git a/modules/audio_device/include/audio_device_data_observer.h b/modules/audio_device/include/audio_device_data_observer.h index b59cafcb5d..36dc45f19e 100644 --- a/modules/audio_device/include/audio_device_data_observer.h +++ b/modules/audio_device/include/audio_device_data_observer.h @@ -26,16 +26,16 @@ namespace webrtc { class AudioDeviceDataObserver { public: virtual void OnCaptureData(const void* audio_samples, - const size_t num_samples, - const size_t bytes_per_sample, - const size_t num_channels, - const uint32_t samples_per_sec) = 0; + size_t num_samples, + size_t bytes_per_sample, + size_t num_channels, + uint32_t samples_per_sec) = 0; virtual void OnRenderData(const void* audio_samples, - const size_t num_samples, - const size_t bytes_per_sample, - const size_t num_channels, - const uint32_t samples_per_sec) = 0; + size_t num_samples, + size_t bytes_per_sample, + size_t num_channels, + uint32_t samples_per_sec) = 0; AudioDeviceDataObserver() = default; virtual ~AudioDeviceDataObserver() = default; @@ -56,14 +56,14 @@ rtc::scoped_refptr CreateAudioDeviceWithDataObserver( // Creates an ADM instance with AudioDeviceDataObserver registered. rtc::scoped_refptr CreateAudioDeviceWithDataObserver( - const AudioDeviceModule::AudioLayer audio_layer, + AudioDeviceModule::AudioLayer audio_layer, TaskQueueFactory* task_queue_factory, std::unique_ptr observer); // Creates an ADM instance with AudioDeviceDataObserver registered. ABSL_DEPRECATED("") rtc::scoped_refptr CreateAudioDeviceWithDataObserver( - const AudioDeviceModule::AudioLayer audio_layer, + AudioDeviceModule::AudioLayer audio_layer, TaskQueueFactory* task_queue_factory, AudioDeviceDataObserver* observer); diff --git a/modules/audio_device/include/audio_device_defines.h b/modules/audio_device/include/audio_device_defines.h index 01129a47a9..89d33f8538 100644 --- a/modules/audio_device/include/audio_device_defines.h +++ b/modules/audio_device/include/audio_device_defines.h @@ -33,22 +33,43 @@ static const int kAdmMaxPlayoutBufferSizeMs = 250; class AudioTransport { public: + // TODO(bugs.webrtc.org/13620) Deprecate this function virtual int32_t RecordedDataIsAvailable(const void* audioSamples, - const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, - const uint32_t totalDelayMS, - const int32_t clockDrift, - const uint32_t currentMicLevel, - const bool keyPressed, + 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, + int64_t 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(const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, + 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, diff --git a/modules/audio_device/include/audio_device_factory.cc b/modules/audio_device/include/audio_device_factory.cc index d5b381029e..130e096e6d 100644 --- a/modules/audio_device/include/audio_device_factory.cc +++ b/modules/audio_device/include/audio_device_factory.cc @@ -27,7 +27,7 @@ namespace webrtc { rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModule( TaskQueueFactory* task_queue_factory, bool automatic_restart) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return CreateWindowsCoreAudioAudioDeviceModuleForTest(task_queue_factory, automatic_restart); } @@ -36,7 +36,7 @@ rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModuleForTest( TaskQueueFactory* task_queue_factory, bool automatic_restart) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // Returns NULL if Core Audio is not supported or if COM has not been // initialized correctly using ScopedCOMInitializer. if (!webrtc_win::core_audio_utility::IsSupported()) { diff --git a/modules/audio_device/include/mock_audio_transport.h b/modules/audio_device/include/mock_audio_transport.h index 8f71a2d71f..e1be5f422f 100644 --- a/modules/audio_device/include/mock_audio_transport.h +++ b/modules/audio_device/include/mock_audio_transport.h @@ -25,23 +25,38 @@ class MockAudioTransport : public AudioTransport { MOCK_METHOD(int32_t, RecordedDataIsAvailable, (const void* audioSamples, - const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, - const uint32_t totalDelayMS, - const int32_t clockDrift, - const uint32_t currentMicLevel, - const bool keyPressed, + 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), (override)); + MOCK_METHOD(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, + int64_t estimated_capture_time_ns), + (override)); + MOCK_METHOD(int32_t, NeedMorePlayData, - (const size_t nSamples, - const size_t nBytesPerSample, - const size_t nChannels, - const uint32_t samplesPerSec, + (size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, void* audioSamples, size_t& nSamplesOut, int64_t* elapsed_time_ms, diff --git a/modules/audio_device/linux/audio_device_alsa_linux.h b/modules/audio_device/linux/audio_device_alsa_linux.h index 1f4a231640..23e21d3ce9 100644 --- a/modules/audio_device/linux/audio_device_alsa_linux.h +++ b/modules/audio_device/linux/audio_device_alsa_linux.h @@ -131,11 +131,11 @@ class AudioDeviceLinuxALSA : public AudioDeviceGeneric { int32_t InitPlayoutLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); int32_t InitSpeakerLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); int32_t InitMicrophoneLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - int32_t GetDevicesInfo(const int32_t function, - const bool playback, - const int32_t enumDeviceNo = 0, + int32_t GetDevicesInfo(int32_t function, + bool playback, + int32_t enumDeviceNo = 0, char* enumDeviceName = NULL, - const int32_t ednLen = 0) const; + int32_t ednLen = 0) const; int32_t ErrorRecovery(int32_t error, snd_pcm_t* deviceHandle); bool KeyPressed() const; diff --git a/modules/audio_device/linux/latebindingsymboltable_linux.h b/modules/audio_device/linux/latebindingsymboltable_linux.h index 6cfb659749..9484b075ef 100644 --- a/modules/audio_device/linux/latebindingsymboltable_linux.h +++ b/modules/audio_device/linux/latebindingsymboltable_linux.h @@ -15,7 +15,6 @@ #include #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" // This file provides macros for creating "symbol table" classes to simplify the // dynamic loading of symbols from DLLs. Currently the implementation only @@ -55,6 +54,9 @@ class LateBindingSymbolTable { ~LateBindingSymbolTable() { Unload(); } + LateBindingSymbolTable(const LateBindingSymbolTable&) = delete; + LateBindingSymbolTable& operator=(LateBindingSymbolTable&) = delete; + static int NumSymbols() { return SYMBOL_TABLE_SIZE; } // We do not use this, but we offer it for theoretical convenience. @@ -109,8 +111,6 @@ class LateBindingSymbolTable { DllHandle handle_; bool undefined_symbols_; void* symbols_[SYMBOL_TABLE_SIZE]; - - RTC_DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable); }; // This macro must be invoked in a header to declare a symbol table class. diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc index eaaa30c2d6..c4f26f2527 100644 --- a/modules/audio_device/mac/audio_device_mac.cc +++ b/modules/audio_device/mac/audio_device_mac.cc @@ -11,7 +11,6 @@ #include "modules/audio_device/mac/audio_device_mac.h" #include -#include // OSAtomicCompareAndSwap() #include // mach_task_self() #include // sysctlbyname() @@ -53,24 +52,6 @@ namespace webrtc { enum { MaxNumberDevices = 64 }; -void AudioDeviceMac::AtomicSet32(int32_t* theValue, int32_t newValue) { - while (1) { - int32_t oldValue = *theValue; - if (OSAtomicCompareAndSwap32Barrier(oldValue, newValue, theValue) == true) { - return; - } - } -} - -int32_t AudioDeviceMac::AtomicGet32(int32_t* theValue) { - while (1) { - int32_t value = *theValue; - if (OSAtomicCompareAndSwap32Barrier(value, value, theValue) == true) { - return value; - } - } -} - // CoreAudio errors are best interpreted as four character strings. void AudioDeviceMac::logCAMsg(const rtc::LoggingSeverity sev, const char* msg, @@ -1297,7 +1278,7 @@ int32_t AudioDeviceMac::StopRecording() { } OSStatus err = noErr; - int32_t captureDeviceIsAlive = AtomicGet32(&_captureDeviceIsAlive); + int32_t captureDeviceIsAlive = _captureDeviceIsAlive; if (_twoDevices && captureDeviceIsAlive == 1) { // Recording side uses its own dedicated device and IOProc. if (_recording) { @@ -1353,7 +1334,7 @@ int32_t AudioDeviceMac::StopRecording() { } // Setting this signal will allow the worker thread to be stopped. - AtomicSet32(&_captureDeviceIsAlive, 0); + _captureDeviceIsAlive = 0; if (!capture_worker_thread_.empty()) { mutex_.Unlock(); @@ -1430,7 +1411,7 @@ int32_t AudioDeviceMac::StopPlayout() { } OSStatus err = noErr; - int32_t renderDeviceIsAlive = AtomicGet32(&_renderDeviceIsAlive); + int32_t renderDeviceIsAlive = _renderDeviceIsAlive; if (_playing && renderDeviceIsAlive == 1) { // We signal a stop for a shared device even when capturing has not // yet ended. This is to ensure the IOProc will return early as @@ -1467,7 +1448,7 @@ int32_t AudioDeviceMac::StopPlayout() { } // Setting this signal will allow the worker thread to be stopped. - AtomicSet32(&_renderDeviceIsAlive, 0); + _renderDeviceIsAlive = 0; if (!render_worker_thread_.empty()) { mutex_.Unlock(); render_worker_thread_.Finalize(); @@ -1493,7 +1474,7 @@ int32_t AudioDeviceMac::StopPlayout() { } int32_t AudioDeviceMac::PlayoutDelay(uint16_t& delayMS) const { - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs); + int32_t renderDelayUs = _renderDelayUs; delayMS = static_cast(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5); return 0; @@ -1900,7 +1881,7 @@ int32_t AudioDeviceMac::HandleDeviceChange() { if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) { RTC_LOG(LS_WARNING) << "Capture device is not alive (probably removed)"; - AtomicSet32(&_captureDeviceIsAlive, 0); + _captureDeviceIsAlive = 0; _mixerManager.CloseMicrophone(); } else if (err != noErr) { logCAMsg(rtc::LS_ERROR, "Error in AudioDeviceGetProperty()", @@ -1919,7 +1900,7 @@ int32_t AudioDeviceMac::HandleDeviceChange() { if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) { RTC_LOG(LS_WARNING) << "Render device is not alive (probably removed)"; - AtomicSet32(&_renderDeviceIsAlive, 0); + _renderDeviceIsAlive = 0; _mixerManager.CloseSpeaker(); } else if (err != noErr) { logCAMsg(rtc::LS_ERROR, "Error in AudioDeviceGetProperty()", @@ -2171,7 +2152,7 @@ OSStatus AudioDeviceMac::implDeviceIOProc(const AudioBufferList* inputData, _outDesiredFormat.mSampleRate + 0.5); - AtomicSet32(&_renderDelayUs, renderDelayUs); + _renderDelayUs = renderDelayUs; return 0; } @@ -2240,7 +2221,7 @@ OSStatus AudioDeviceMac::implInDeviceIOProc(const AudioBufferList* inputData, _inStreamFormat.mSampleRate + 0.5); - AtomicSet32(&_captureDelayUs, captureDelayUs); + _captureDelayUs = captureDelayUs; RTC_DCHECK(inputData->mNumberBuffers == 1); PaRingBufferSize numSamples = inputData->mBuffers->mDataByteSize * @@ -2270,7 +2251,7 @@ OSStatus AudioDeviceMac::implInConverterProc(UInt32* numberDataPackets, kern_return_t kernErr = semaphore_timedwait(_captureSemaphore, timeout); if (kernErr == KERN_OPERATION_TIMED_OUT) { - int32_t signal = AtomicGet32(&_captureDeviceIsAlive); + int32_t signal = _captureDeviceIsAlive; if (signal == 0) { // The capture device is no longer alive; stop the worker thread. *numberDataPackets = 0; @@ -2309,7 +2290,7 @@ bool AudioDeviceMac::RenderWorkerThread() { kern_return_t kernErr = semaphore_timedwait(_renderSemaphore, timeout); if (kernErr == KERN_OPERATION_TIMED_OUT) { - int32_t signal = AtomicGet32(&_renderDeviceIsAlive); + int32_t signal = _renderDeviceIsAlive; if (signal == 0) { // The render device is no longer alive; stop the worker thread. return false; @@ -2377,8 +2358,8 @@ bool AudioDeviceMac::CaptureWorkerThread() { int32_t msecOnPlaySide; int32_t msecOnRecordSide; - int32_t captureDelayUs = AtomicGet32(&_captureDelayUs); - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs); + int32_t captureDelayUs = _captureDelayUs; + int32_t renderDelayUs = _renderDelayUs; msecOnPlaySide = static_cast(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5); diff --git a/modules/audio_device/mac/audio_device_mac.h b/modules/audio_device/mac/audio_device_mac.h index 7a6e9cd37a..0878f1a273 100644 --- a/modules/audio_device/mac/audio_device_mac.h +++ b/modules/audio_device/mac/audio_device_mac.h @@ -15,6 +15,7 @@ #include #include +#include #include #include "modules/audio_device/audio_device_generic.h" @@ -168,16 +169,16 @@ class AudioDeviceMac : public AudioDeviceGeneric { static void AtomicSet32(int32_t* theValue, int32_t newValue); static int32_t AtomicGet32(int32_t* theValue); - static void logCAMsg(const rtc::LoggingSeverity sev, + static void logCAMsg(rtc::LoggingSeverity sev, const char* msg, const char* err); - int32_t GetNumberDevices(const AudioObjectPropertyScope scope, + int32_t GetNumberDevices(AudioObjectPropertyScope scope, AudioDeviceID scopedDeviceIds[], - const uint32_t deviceListLength); + uint32_t deviceListLength); - int32_t GetDeviceName(const AudioObjectPropertyScope scope, - const uint16_t index, + int32_t GetDeviceName(AudioObjectPropertyScope scope, + uint16_t index, char* name); int32_t InitDevice(uint16_t userDeviceIndex, @@ -303,8 +304,8 @@ class AudioDeviceMac : public AudioDeviceGeneric { bool _playIsInitialized; // Atomically set varaibles - int32_t _renderDeviceIsAlive; - int32_t _captureDeviceIsAlive; + std::atomic _renderDeviceIsAlive; + std::atomic _captureDeviceIsAlive; bool _twoDevices; bool _doStop; // For play if not shared device or play+rec if shared device @@ -322,8 +323,8 @@ class AudioDeviceMac : public AudioDeviceGeneric { uint32_t _renderLatencyUs; // Atomically set variables - mutable int32_t _captureDelayUs; - mutable int32_t _renderDelayUs; + mutable std::atomic _captureDelayUs; + mutable std::atomic _renderDelayUs; int32_t _renderDelayOffsetSamples; diff --git a/modules/audio_device/mac/audio_mixer_manager_mac.h b/modules/audio_device/mac/audio_mixer_manager_mac.h index 17e34ff17d..0ccab4879b 100644 --- a/modules/audio_device/mac/audio_mixer_manager_mac.h +++ b/modules/audio_device/mac/audio_mixer_manager_mac.h @@ -54,7 +54,7 @@ class AudioMixerManagerMac { private: int32_t CloseSpeakerLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); int32_t CloseMicrophoneLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - static void logCAMsg(const rtc::LoggingSeverity sev, + static void logCAMsg(rtc::LoggingSeverity sev, const char* msg, const char* err); diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc index 66dc20274d..01bdcc985a 100644 --- a/modules/audio_device/win/audio_device_core_win.cc +++ b/modules/audio_device/win/audio_device_core_win.cc @@ -343,33 +343,33 @@ bool AudioDeviceWindowsCore::CoreAudioIsSupported() { // ---------------------------------------------------------------------------- AudioDeviceWindowsCore::AudioDeviceWindowsCore() - : _avrtLibrary(NULL), + : _avrtLibrary(nullptr), _winSupportAvrt(false), _comInit(ScopedCOMInitializer::kMTA), - _ptrAudioBuffer(NULL), - _ptrEnumerator(NULL), - _ptrRenderCollection(NULL), - _ptrCaptureCollection(NULL), - _ptrDeviceOut(NULL), - _ptrDeviceIn(NULL), - _ptrClientOut(NULL), - _ptrClientIn(NULL), - _ptrRenderClient(NULL), - _ptrCaptureClient(NULL), - _ptrCaptureVolume(NULL), - _ptrRenderSimpleVolume(NULL), - _dmo(NULL), - _mediaBuffer(NULL), + _ptrAudioBuffer(nullptr), + _ptrEnumerator(nullptr), + _ptrRenderCollection(nullptr), + _ptrCaptureCollection(nullptr), + _ptrDeviceOut(nullptr), + _ptrDeviceIn(nullptr), + _ptrClientOut(nullptr), + _ptrClientIn(nullptr), + _ptrRenderClient(nullptr), + _ptrCaptureClient(nullptr), + _ptrCaptureVolume(nullptr), + _ptrRenderSimpleVolume(nullptr), + _dmo(nullptr), + _mediaBuffer(nullptr), _builtInAecEnabled(false), - _hRenderSamplesReadyEvent(NULL), - _hPlayThread(NULL), - _hRenderStartedEvent(NULL), - _hShutdownRenderEvent(NULL), - _hCaptureSamplesReadyEvent(NULL), - _hRecThread(NULL), - _hCaptureStartedEvent(NULL), - _hShutdownCaptureEvent(NULL), - _hMmTask(NULL), + _hRenderSamplesReadyEvent(nullptr), + _hPlayThread(nullptr), + _hRenderStartedEvent(nullptr), + _hShutdownRenderEvent(nullptr), + _hCaptureSamplesReadyEvent(nullptr), + _hRecThread(nullptr), + _hCaptureStartedEvent(nullptr), + _hShutdownCaptureEvent(nullptr), + _hMmTask(nullptr), _playAudioFrameSize(0), _playSampleRate(0), _playBlockSize(0), @@ -1887,18 +1887,18 @@ int32_t AudioDeviceWindowsCore::InitPlayout() { break; } else { if (pWfxClosestMatch) { - RTC_LOG(INFO) << "nChannels=" << Wfx.nChannels - << ", nSamplesPerSec=" << Wfx.nSamplesPerSec - << " is not supported. Closest match: " - "nChannels=" - << pWfxClosestMatch->nChannels << ", nSamplesPerSec=" - << pWfxClosestMatch->nSamplesPerSec; + RTC_LOG(LS_INFO) << "nChannels=" << Wfx.nChannels + << ", nSamplesPerSec=" << Wfx.nSamplesPerSec + << " is not supported. Closest match: " + "nChannels=" + << pWfxClosestMatch->nChannels << ", nSamplesPerSec=" + << pWfxClosestMatch->nSamplesPerSec; CoTaskMemFree(pWfxClosestMatch); pWfxClosestMatch = NULL; } else { - RTC_LOG(INFO) << "nChannels=" << Wfx.nChannels - << ", nSamplesPerSec=" << Wfx.nSamplesPerSec - << " is not supported. No closest match."; + RTC_LOG(LS_INFO) << "nChannels=" << Wfx.nChannels + << ", nSamplesPerSec=" << Wfx.nSamplesPerSec + << " is not supported. No closest match."; } } } @@ -2208,18 +2208,18 @@ int32_t AudioDeviceWindowsCore::InitRecording() { break; } else { if (pWfxClosestMatch) { - RTC_LOG(INFO) << "nChannels=" << Wfx.Format.nChannels - << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec - << " is not supported. Closest match: " - "nChannels=" - << pWfxClosestMatch->nChannels << ", nSamplesPerSec=" - << pWfxClosestMatch->nSamplesPerSec; + RTC_LOG(LS_INFO) << "nChannels=" << Wfx.Format.nChannels + << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec + << " is not supported. Closest match: " + "nChannels=" + << pWfxClosestMatch->nChannels << ", nSamplesPerSec=" + << pWfxClosestMatch->nSamplesPerSec; CoTaskMemFree(pWfxClosestMatch); pWfxClosestMatch = NULL; } else { - RTC_LOG(INFO) << "nChannels=" << Wfx.Format.nChannels - << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec - << " is not supported. No closest match."; + RTC_LOG(LS_INFO) << "nChannels=" << Wfx.Format.nChannels + << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec + << " is not supported. No closest match."; } } } @@ -3009,7 +3009,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() { if (FAILED(hr)) { _TraceCOMError(hr); keepRecording = false; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } @@ -3021,7 +3021,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() { if (FAILED(hr)) { _TraceCOMError(hr); keepRecording = false; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } @@ -3046,7 +3046,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() { if (FAILED(hr)) { _TraceCOMError(hr); keepRecording = false; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } diff --git a/modules/audio_device/win/audio_device_module_win.cc b/modules/audio_device/win/audio_device_module_win.cc index ad26953ef2..6643d8479e 100644 --- a/modules/audio_device/win/audio_device_module_win.cc +++ b/modules/audio_device/win/audio_device_module_win.cc @@ -95,12 +95,12 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { task_queue_factory_(task_queue_factory) { RTC_CHECK(input_); RTC_CHECK(output_); - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); } ~WindowsAudioDeviceModule() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); Terminate(); } @@ -110,7 +110,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t ActiveAudioLayer( AudioDeviceModule::AudioLayer* audioLayer) const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); // TODO(henrika): it might be possible to remove this unique signature. *audioLayer = AudioDeviceModule::kWindowsCoreAudio2; @@ -118,14 +118,14 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int32_t RegisterAudioCallback(AudioTransport* audioCallback) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(audio_device_buffer_); RTC_DCHECK_RUN_ON(&thread_checker_); return audio_device_buffer_->RegisterAudioCallback(audioCallback); } int32_t Init() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); RETURN_IF_INPUT_RESTARTS(0); @@ -153,7 +153,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int32_t Terminate() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); RETURN_IF_INPUT_RESTARTS(0); @@ -172,14 +172,14 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int16_t PlayoutDevices() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); return output_->NumDevices(); } int16_t RecordingDevices() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(0); return input_->NumDevices(); @@ -188,7 +188,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t PlayoutDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], char guid[kAdmMaxGuidSize]) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); std::string name_str, guid_str; @@ -205,7 +205,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t RecordingDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], char guid[kAdmMaxGuidSize]) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(0); std::string name_str, guid_str; @@ -221,7 +221,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int32_t SetPlayoutDevice(uint16_t index) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); return output_->SetDevice(index); @@ -229,33 +229,33 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t SetPlayoutDevice( AudioDeviceModule::WindowsDeviceType device) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); return output_->SetDevice(device); } int32_t SetRecordingDevice(uint16_t index) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return input_->SetDevice(index); } int32_t SetRecordingDevice( AudioDeviceModule::WindowsDeviceType device) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return input_->SetDevice(device); } int32_t PlayoutIsAvailable(bool* available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = true; return 0; } int32_t InitPlayout() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); RETURN_IF_OUTPUT_IS_INITIALIZED(0); @@ -263,21 +263,21 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } bool PlayoutIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(true); return output_->PlayoutIsInitialized(); } int32_t RecordingIsAvailable(bool* available) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = true; return 0; } int32_t InitRecording() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(0); RETURN_IF_INPUT_IS_INITIALIZED(0); @@ -285,14 +285,14 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } bool RecordingIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(true); return input_->RecordingIsInitialized(); } int32_t StartPlayout() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); RETURN_IF_OUTPUT_IS_ACTIVE(0); @@ -300,21 +300,21 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int32_t StopPlayout() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(-1); return output_->StopPlayout(); } bool Playing() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(true); return output_->Playing(); } int32_t StartRecording() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(0); RETURN_IF_INPUT_IS_ACTIVE(0); @@ -322,41 +322,41 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { } int32_t StopRecording() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_INPUT_RESTARTS(-1); return input_->StopRecording(); } bool Recording() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RETURN_IF_INPUT_RESTARTS(true); return input_->Recording(); } int32_t InitSpeaker() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DLOG(LS_WARNING) << "This method has no effect"; return initialized_ ? 0 : -1; } bool SpeakerIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DLOG(LS_WARNING) << "This method has no effect"; return initialized_; } int32_t InitMicrophone() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DLOG(LS_WARNING) << "This method has no effect"; return initialized_ ? 0 : -1; } bool MicrophoneIsInitialized() const override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DLOG(LS_WARNING) << "This method has no effect"; return initialized_; @@ -364,7 +364,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t SpeakerVolumeIsAvailable(bool* available) override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = false; return 0; @@ -377,7 +377,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t MicrophoneVolumeIsAvailable(bool* available) override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = false; return 0; @@ -398,7 +398,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t StereoPlayoutIsAvailable(bool* available) const override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = true; return 0; @@ -406,14 +406,14 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t SetStereoPlayout(bool enable) override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return 0; } int32_t StereoPlayout(bool* enabled) const override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *enabled = true; return 0; @@ -421,7 +421,7 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t StereoRecordingIsAvailable(bool* available) const override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *available = true; return 0; @@ -429,14 +429,14 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t SetStereoRecording(bool enable) override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return 0; } int32_t StereoRecording(bool* enabled) const override { // TODO(henrika): improve support. - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); *enabled = true; return 0; @@ -453,33 +453,33 @@ class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { int32_t EnableBuiltInNS(bool enable) override { return 0; } int32_t AttachAudioBuffer() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; output_->AttachAudioBuffer(audio_device_buffer_.get()); input_->AttachAudioBuffer(audio_device_buffer_.get()); return 0; } int RestartPlayoutInternally() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); RETURN_IF_OUTPUT_RESTARTS(0); return output_->RestartPlayout(); } int RestartRecordingInternally() override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return input_->RestartRecording(); } int SetPlayoutSampleRate(uint32_t sample_rate) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return output_->SetSampleRate(sample_rate); } int SetRecordingSampleRate(uint32_t sample_rate) override { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return input_->SetSampleRate(sample_rate); } @@ -513,7 +513,7 @@ CreateWindowsCoreAudioAudioDeviceModuleFromInputAndOutput( std::unique_ptr audio_input, std::unique_ptr audio_output, TaskQueueFactory* task_queue_factory) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; return rtc::make_ref_counted( std::move(audio_input), std::move(audio_output), task_queue_factory); } diff --git a/modules/audio_device/win/core_audio_base_win.cc b/modules/audio_device/win/core_audio_base_win.cc index 152de67057..6b5ca64eb3 100644 --- a/modules/audio_device/win/core_audio_base_win.cc +++ b/modules/audio_device/win/core_audio_base_win.cc @@ -125,7 +125,7 @@ const char* SessionDisconnectReasonToString( bool IsLowLatencySupported(IAudioClient3* client3, const WAVEFORMATEXTENSIBLE* format, uint32_t* min_period_in_frames) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // Get the range of periodicities supported by the engine for the specified // stream format. @@ -143,7 +143,7 @@ bool IsLowLatencySupported(IAudioClient3* client3, // default engine period. // TODO(henrika): verify that this assumption is correct. const bool low_latency = min_period < default_period; - RTC_LOG(INFO) << "low_latency: " << low_latency; + RTC_LOG(LS_INFO) << "low_latency: " << low_latency; *min_period_in_frames = low_latency ? min_period : 0; return low_latency; } @@ -161,9 +161,10 @@ CoreAudioBase::CoreAudioBase(Direction direction, on_error_callback_(error_callback), device_index_(kUndefined), is_restarting_(false) { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction) << "]"; - RTC_DLOG(INFO) << "Automatic restart: " << automatic_restart; - RTC_DLOG(INFO) << "Windows version: " << rtc::rtc_win::GetVersion(); + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction) + << "]"; + RTC_DLOG(LS_INFO) << "Automatic restart: " << automatic_restart; + RTC_DLOG(LS_INFO) << "Windows version: " << rtc::rtc_win::GetVersion(); // Create the event which the audio engine will signal each time a buffer // becomes ready to be processed by the client. @@ -181,7 +182,7 @@ CoreAudioBase::CoreAudioBase(Direction direction, } CoreAudioBase::~CoreAudioBase() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_EQ(ref_count_, 1); } @@ -207,7 +208,7 @@ int CoreAudioBase::NumberOfEnumeratedDevices() const { } void CoreAudioBase::ReleaseCOMObjects() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; // ComPtr::Reset() sets the ComPtr to nullptr releasing any previous // reference. if (audio_client_) { @@ -288,15 +289,15 @@ std::string CoreAudioBase::GetDeviceID(int index) const { } int CoreAudioBase::SetDevice(int index) { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]: index=" << IndexToString(index); + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]: index=" << IndexToString(index); if (initialized_) { return -1; } std::string device_id = GetDeviceID(index); - RTC_DLOG(INFO) << "index=" << IndexToString(index) - << " => device_id: " << device_id; + RTC_DLOG(LS_INFO) << "index=" << IndexToString(index) + << " => device_id: " << device_id; device_index_ = index; device_id_ = device_id; @@ -306,8 +307,8 @@ int CoreAudioBase::SetDevice(int index) { int CoreAudioBase::DeviceName(int index, std::string* name, std::string* guid) const { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]: index=" << IndexToString(index); + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]: index=" << IndexToString(index); if (index > NumberOfEnumeratedDevices() - 1) { RTC_LOG(LS_ERROR) << "Invalid device index"; return -1; @@ -324,17 +325,17 @@ int CoreAudioBase::DeviceName(int index, } *name = device_names[index].device_name; - RTC_DLOG(INFO) << "name: " << *name; + RTC_DLOG(LS_INFO) << "name: " << *name; if (guid != nullptr) { *guid = device_names[index].unique_id; - RTC_DLOG(INFO) << "guid: " << *guid; + RTC_DLOG(LS_INFO) << "guid: " << *guid; } return 0; } bool CoreAudioBase::Init() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; RTC_DCHECK_GE(device_index_, 0); RTC_DCHECK(!device_id_.empty()); RTC_DCHECK(audio_device_buffer_); @@ -360,15 +361,15 @@ bool CoreAudioBase::Init() { // an audio stream between an audio application and the audio engine. ComPtr audio_client; if (core_audio_utility::GetAudioClientVersion() == 3) { - RTC_DLOG(INFO) << "Using IAudioClient3"; + RTC_DLOG(LS_INFO) << "Using IAudioClient3"; audio_client = core_audio_utility::CreateClient3(device_id, GetDataFlow(), role); } else if (core_audio_utility::GetAudioClientVersion() == 2) { - RTC_DLOG(INFO) << "Using IAudioClient2"; + RTC_DLOG(LS_INFO) << "Using IAudioClient2"; audio_client = core_audio_utility::CreateClient2(device_id, GetDataFlow(), role); } else { - RTC_DLOG(INFO) << "Using IAudioClient"; + RTC_DLOG(LS_INFO) << "Using IAudioClient"; audio_client = core_audio_utility::CreateClient(device_id, GetDataFlow(), role); } @@ -429,7 +430,7 @@ bool CoreAudioBase::Init() { format_.dwChannelMask = format->nChannels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO; format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - RTC_DLOG(INFO) << core_audio_utility::WaveFormatToString(&format_); + RTC_DLOG(LS_INFO) << core_audio_utility::WaveFormatToString(&format_); // Verify that the format is supported but exclude the test if the default // sample rate has been overridden. If so, the WASAPI audio engine will do @@ -516,12 +517,12 @@ bool CoreAudioBase::Init() { 1000.0L; const int preferred_frames_per_buffer = static_cast(params.sample_rate() * device_period_in_seconds + 0.5); - RTC_DLOG(INFO) << "preferred_frames_per_buffer: " - << preferred_frames_per_buffer; + RTC_DLOG(LS_INFO) << "preferred_frames_per_buffer: " + << preferred_frames_per_buffer; if (preferred_frames_per_buffer % params.frames_per_buffer()) { - RTC_LOG(WARNING) << "Buffer size of " << params.frames_per_buffer() - << " is not an even divisor of " - << preferred_frames_per_buffer; + RTC_LOG(LS_WARNING) << "Buffer size of " << params.frames_per_buffer() + << " is not an even divisor of " + << preferred_frames_per_buffer; } // Create an AudioSessionControl interface given the initialized client. @@ -539,7 +540,7 @@ bool CoreAudioBase::Init() { if (FAILED(audio_session_control->GetState(&state))) { return false; } - RTC_DLOG(INFO) << "audio session state: " << SessionStateToString(state); + RTC_DLOG(LS_INFO) << "audio session state: " << SessionStateToString(state); RTC_DCHECK_EQ(state, AudioSessionStateInactive); // Register the client to receive notifications of session events, including @@ -556,8 +557,8 @@ bool CoreAudioBase::Init() { } bool CoreAudioBase::Start() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; if (IsRestarting()) { // Audio thread should be alive during internal restart since the restart // callback is triggered on that thread and it also makes the restart @@ -573,8 +574,8 @@ bool CoreAudioBase::Start() { audio_thread_ = rtc::PlatformThread::SpawnJoinable( [this] { ThreadRun(); }, name, rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kRealtime)); - RTC_DLOG(INFO) << "Started thread with name: " << name - << " and handle: " << *audio_thread_.GetHandle(); + RTC_DLOG(LS_INFO) << "Started thread with name: " << name + << " and handle: " << *audio_thread_.GetHandle(); } // Start streaming data between the endpoint buffer and the audio engine. @@ -593,9 +594,9 @@ bool CoreAudioBase::Start() { } bool CoreAudioBase::Stop() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; - RTC_DLOG(INFO) << "total activity time: " << TimeSinceStart(); + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; + RTC_DLOG(LS_INFO) << "total activity time: " << TimeSinceStart(); // Stop audio streaming. _com_error error = audio_client_->Stop(); @@ -628,8 +629,8 @@ bool CoreAudioBase::Stop() { // Delete the previous registration by the client to receive notifications // about audio session events. - RTC_DLOG(INFO) << "audio session state: " - << SessionStateToString(GetAudioSessionState()); + RTC_DLOG(LS_INFO) << "audio session state: " + << SessionStateToString(GetAudioSessionState()); error = audio_session_control_->UnregisterAudioSessionNotification(this); if (FAILED(error.Error())) { RTC_LOG(LS_ERROR) @@ -677,7 +678,7 @@ bool CoreAudioBase::IsVolumeControlAvailable(bool* available) const { << core_audio_utility::ErrorToString(error); *available = false; } - RTC_DLOG(INFO) << "master volume for output audio session: " << volume; + RTC_DLOG(LS_INFO) << "master volume for output audio session: " << volume; *available = true; return false; @@ -688,8 +689,8 @@ bool CoreAudioBase::IsVolumeControlAvailable(bool* available) const { // device notifications. Hence, the emulated restart sequence covers most parts // of a real sequence expect the actual device switch. bool CoreAudioBase::Restart() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; if (!automatic_restart()) { return false; } @@ -699,12 +700,12 @@ bool CoreAudioBase::Restart() { } void CoreAudioBase::StopThread() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(!IsRestarting()); if (!audio_thread_.empty()) { - RTC_DLOG(INFO) << "Sets stop_event..."; + RTC_DLOG(LS_INFO) << "Sets stop_event..."; SetEvent(stop_event_.Get()); - RTC_DLOG(INFO) << "PlatformThread::Finalize..."; + RTC_DLOG(LS_INFO) << "PlatformThread::Finalize..."; audio_thread_.Finalize(); // Ensure that we don't quit the main thread loop immediately next @@ -715,8 +716,8 @@ void CoreAudioBase::StopThread() { } bool CoreAudioBase::HandleRestartEvent() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; RTC_DCHECK_RUN_ON(&thread_checker_audio_); RTC_DCHECK(!audio_thread_.empty()); RTC_DCHECK(IsRestarting()); @@ -730,13 +731,13 @@ bool CoreAudioBase::HandleRestartEvent() { } bool CoreAudioBase::SwitchDeviceIfNeeded() { - RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) - << "]"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << "[" << DirectionToString(direction()) + << "]"; RTC_DCHECK_RUN_ON(&thread_checker_audio_); RTC_DCHECK(IsRestarting()); - RTC_DLOG(INFO) << "device_index=" << device_index_ - << " => device_id: " << device_id_; + RTC_DLOG(LS_INFO) << "device_index=" << device_index_ + << " => device_id: " << device_id_; // Ensure that at least one device exists and can be utilized. The most // probable cause for ending up here is that a device has been removed. @@ -760,7 +761,7 @@ bool CoreAudioBase::SwitchDeviceIfNeeded() { return false; } } else { - RTC_LOG(INFO) + RTC_LOG(LS_INFO) << "Device configuration has not changed => keeping selected device"; } return true; @@ -780,14 +781,14 @@ AudioSessionState CoreAudioBase::GetAudioSessionState() const { // TODO(henrika): only used for debugging purposes currently. ULONG CoreAudioBase::AddRef() { ULONG new_ref = InterlockedIncrement(&ref_count_); - // RTC_DLOG(INFO) << "__AddRef => " << new_ref; + // RTC_DLOG(LS_INFO) << "__AddRef => " << new_ref; return new_ref; } // TODO(henrika): does not call delete this. ULONG CoreAudioBase::Release() { ULONG new_ref = InterlockedDecrement(&ref_count_); - // RTC_DLOG(INFO) << "__Release => " << new_ref; + // RTC_DLOG(LS_INFO) << "__Release => " << new_ref; return new_ref; } @@ -806,9 +807,9 @@ HRESULT CoreAudioBase::QueryInterface(REFIID iid, void** object) { // IAudioSessionEvents::OnStateChanged. HRESULT CoreAudioBase::OnStateChanged(AudioSessionState new_state) { - RTC_DLOG(INFO) << "___" << __FUNCTION__ << "[" - << DirectionToString(direction()) - << "] new_state: " << SessionStateToString(new_state); + RTC_DLOG(LS_INFO) << "___" << __FUNCTION__ << "[" + << DirectionToString(direction()) + << "] new_state: " << SessionStateToString(new_state); return S_OK; } @@ -820,9 +821,9 @@ HRESULT CoreAudioBase::OnStateChanged(AudioSessionState new_state) { // same event. HRESULT CoreAudioBase::OnSessionDisconnected( AudioSessionDisconnectReason disconnect_reason) { - RTC_DLOG(INFO) << "___" << __FUNCTION__ << "[" - << DirectionToString(direction()) << "] reason: " - << SessionDisconnectReasonToString(disconnect_reason); + RTC_DLOG(LS_INFO) << "___" << __FUNCTION__ << "[" + << DirectionToString(direction()) << "] reason: " + << SessionDisconnectReasonToString(disconnect_reason); // Ignore changes in the audio session (don't try to restart) if the user // has explicitly asked for this type of ADM during construction. if (!automatic_restart()) { @@ -883,8 +884,8 @@ void CoreAudioBase::ThreadRun() { RTC_LOG(LS_ERROR) << "MMCSS is not supported"; return; } - RTC_DLOG(INFO) << "[" << DirectionToString(direction()) - << "] ThreadRun starts..."; + RTC_DLOG(LS_INFO) << "[" << DirectionToString(direction()) + << "] ThreadRun starts..."; // TODO(henrika): difference between "Pro Audio" and "Audio"? ScopedMMCSSRegistration mmcss_registration(L"Pro Audio"); ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA); @@ -952,8 +953,8 @@ void CoreAudioBase::ThreadRun() { // this stream should be destroyed instead of reused in the future. } - RTC_DLOG(INFO) << "[" << DirectionToString(direction()) - << "] ...ThreadRun stops"; + RTC_DLOG(LS_INFO) << "[" << DirectionToString(direction()) + << "] ...ThreadRun stops"; } } // namespace webrtc_win diff --git a/modules/audio_device/win/core_audio_input_win.cc b/modules/audio_device/win/core_audio_input_win.cc index 416336a3e8..8a2da79fcf 100644 --- a/modules/audio_device/win/core_audio_input_win.cc +++ b/modules/audio_device/win/core_audio_input_win.cc @@ -33,24 +33,24 @@ CoreAudioInput::CoreAudioInput(bool automatic_restart) automatic_restart, [this](uint64_t freq) { return OnDataCallback(freq); }, [this](ErrorType err) { return OnErrorCallback(err); }) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); thread_checker_audio_.Detach(); } CoreAudioInput::~CoreAudioInput() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); } int CoreAudioInput::Init() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return 0; } int CoreAudioInput::Terminate() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); StopRecording(); return 0; @@ -62,17 +62,17 @@ int CoreAudioInput::NumDevices() const { } int CoreAudioInput::SetDevice(int index) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << index; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << index; RTC_DCHECK_GE(index, 0); RTC_DCHECK_RUN_ON(&thread_checker_); return CoreAudioBase::SetDevice(index); } int CoreAudioInput::SetDevice(AudioDeviceModule::WindowsDeviceType device) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " - << ((device == AudioDeviceModule::kDefaultDevice) - ? "Default" - : "DefaultCommunication"); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " + << ((device == AudioDeviceModule::kDefaultDevice) + ? "Default" + : "DefaultCommunication"); RTC_DCHECK_RUN_ON(&thread_checker_); return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1); } @@ -80,26 +80,26 @@ int CoreAudioInput::SetDevice(AudioDeviceModule::WindowsDeviceType device) { int CoreAudioInput::DeviceName(int index, std::string* name, std::string* guid) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << index; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << index; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DCHECK(name); return CoreAudioBase::DeviceName(index, name, guid); } void CoreAudioInput::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); audio_device_buffer_ = audio_buffer; } bool CoreAudioInput::RecordingIsInitialized() const { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << initialized_; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << initialized_; RTC_DCHECK_RUN_ON(&thread_checker_); return initialized_; } int CoreAudioInput::InitRecording() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(!initialized_); RTC_DCHECK(!Recording()); RTC_DCHECK(!audio_capture_client_); @@ -158,7 +158,7 @@ int CoreAudioInput::InitRecording() { } int CoreAudioInput::StartRecording() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK(!Recording()); RTC_DCHECK(fine_audio_buffer_); RTC_DCHECK(audio_device_buffer_); @@ -182,7 +182,7 @@ int CoreAudioInput::StartRecording() { } int CoreAudioInput::StopRecording() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; if (!initialized_) { return 0; } @@ -190,7 +190,7 @@ int CoreAudioInput::StopRecording() { // Release resources allocated in InitRecording() and then return if this // method is called without any active input audio. if (!Recording()) { - RTC_DLOG(WARNING) << "No input stream is active"; + RTC_DLOG(LS_WARNING) << "No input stream is active"; ReleaseCOMObjects(); initialized_ = false; return 0; @@ -217,7 +217,7 @@ int CoreAudioInput::StopRecording() { } bool CoreAudioInput::Recording() { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << is_active_; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << is_active_; return is_active_; } @@ -225,7 +225,7 @@ bool CoreAudioInput::Recording() { // are not compatible with the old ADM implementation since it allows accessing // the volume control with any active audio output stream. int CoreAudioInput::VolumeIsAvailable(bool* available) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return IsVolumeControlAvailable(available) ? 0 : -1; } @@ -233,7 +233,7 @@ int CoreAudioInput::VolumeIsAvailable(bool* available) { // Triggers the restart sequence. Only used for testing purposes to emulate // a real event where e.g. an active input device is removed. int CoreAudioInput::RestartRecording() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); if (!Recording()) { return 0; @@ -252,14 +252,14 @@ bool CoreAudioInput::Restarting() const { } int CoreAudioInput::SetSampleRate(uint32_t sample_rate) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); sample_rate_ = sample_rate; return 0; } void CoreAudioInput::ReleaseCOMObjects() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; CoreAudioBase::ReleaseCOMObjects(); if (audio_capture_client_.Get()) { audio_capture_client_.Reset(); @@ -278,7 +278,7 @@ bool CoreAudioInput::OnDataCallback(uint64_t device_frequency) { return true; } if (num_data_callbacks_ == 0) { - RTC_LOG(INFO) << "--- Input audio stream is alive ---"; + RTC_LOG(LS_INFO) << "--- Input audio stream is alive ---"; } UINT32 num_frames_in_next_packet = 0; _com_error error = @@ -333,7 +333,7 @@ bool CoreAudioInput::OnDataCallback(uint64_t device_frequency) { } } if (num_data_callbacks_ % 500 == 0) { - RTC_DLOG(INFO) << "latency: " << latency_ms_; + RTC_DLOG(LS_INFO) << "latency: " << latency_ms_; } // The data in the packet is not correlated with the previous packet's @@ -402,12 +402,12 @@ bool CoreAudioInput::OnDataCallback(uint64_t device_frequency) { } bool CoreAudioInput::OnErrorCallback(ErrorType error) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << as_integer(error); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << as_integer(error); RTC_DCHECK_RUN_ON(&thread_checker_audio_); if (error == CoreAudioBase::ErrorType::kStreamDisconnected) { HandleStreamDisconnected(); } else { - RTC_DLOG(WARNING) << "Unsupported error type"; + RTC_DLOG(LS_WARNING) << "Unsupported error type"; } return true; } @@ -446,7 +446,7 @@ absl::optional CoreAudioInput::EstimateLatencyMillis( // safe. // TODO(henrika): add more details. bool CoreAudioInput::HandleStreamDisconnected() { - RTC_DLOG(INFO) << "<<<--- " << __FUNCTION__; + RTC_DLOG(LS_INFO) << "<<<--- " << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_audio_); RTC_DCHECK(automatic_restart()); @@ -465,7 +465,7 @@ bool CoreAudioInput::HandleStreamDisconnected() { return false; } - RTC_DLOG(INFO) << __FUNCTION__ << " --->>>"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << " --->>>"; return true; } diff --git a/modules/audio_device/win/core_audio_output_win.cc b/modules/audio_device/win/core_audio_output_win.cc index bd4132a961..c92fedf0e9 100644 --- a/modules/audio_device/win/core_audio_output_win.cc +++ b/modules/audio_device/win/core_audio_output_win.cc @@ -29,25 +29,25 @@ CoreAudioOutput::CoreAudioOutput(bool automatic_restart) automatic_restart, [this](uint64_t freq) { return OnDataCallback(freq); }, [this](ErrorType err) { return OnErrorCallback(err); }) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); thread_checker_audio_.Detach(); } CoreAudioOutput::~CoreAudioOutput() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); Terminate(); } int CoreAudioOutput::Init() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return 0; } int CoreAudioOutput::Terminate() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); StopPlayout(); return 0; @@ -59,17 +59,17 @@ int CoreAudioOutput::NumDevices() const { } int CoreAudioOutput::SetDevice(int index) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << index; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << index; RTC_DCHECK_GE(index, 0); RTC_DCHECK_RUN_ON(&thread_checker_); return CoreAudioBase::SetDevice(index); } int CoreAudioOutput::SetDevice(AudioDeviceModule::WindowsDeviceType device) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " - << ((device == AudioDeviceModule::kDefaultDevice) - ? "Default" - : "DefaultCommunication"); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " + << ((device == AudioDeviceModule::kDefaultDevice) + ? "Default" + : "DefaultCommunication"); RTC_DCHECK_RUN_ON(&thread_checker_); return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1); } @@ -77,26 +77,26 @@ int CoreAudioOutput::SetDevice(AudioDeviceModule::WindowsDeviceType device) { int CoreAudioOutput::DeviceName(int index, std::string* name, std::string* guid) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << index; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << index; RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DCHECK(name); return CoreAudioBase::DeviceName(index, name, guid); } void CoreAudioOutput::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); audio_device_buffer_ = audio_buffer; } bool CoreAudioOutput::PlayoutIsInitialized() const { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return initialized_; } int CoreAudioOutput::InitPlayout() { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting(); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << IsRestarting(); RTC_DCHECK(!initialized_); RTC_DCHECK(!Playing()); RTC_DCHECK(!audio_render_client_); @@ -150,7 +150,7 @@ int CoreAudioOutput::InitPlayout() { } int CoreAudioOutput::StartPlayout() { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting(); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << IsRestarting(); RTC_DCHECK(!Playing()); RTC_DCHECK(fine_audio_buffer_); RTC_DCHECK(audio_device_buffer_); @@ -180,7 +180,7 @@ int CoreAudioOutput::StartPlayout() { } int CoreAudioOutput::StopPlayout() { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting(); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << IsRestarting(); if (!initialized_) { return 0; } @@ -188,7 +188,7 @@ int CoreAudioOutput::StopPlayout() { // Release resources allocated in InitPlayout() and then return if this // method is called without any active output audio. if (!Playing()) { - RTC_DLOG(WARNING) << "No output stream is active"; + RTC_DLOG(LS_WARNING) << "No output stream is active"; ReleaseCOMObjects(); initialized_ = false; return 0; @@ -214,7 +214,7 @@ int CoreAudioOutput::StopPlayout() { } bool CoreAudioOutput::Playing() { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << is_active_; + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << is_active_; return is_active_; } @@ -222,7 +222,7 @@ bool CoreAudioOutput::Playing() { // are not compatible with the old ADM implementation since it allows accessing // the volume control with any active audio output stream. int CoreAudioOutput::VolumeIsAvailable(bool* available) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return IsVolumeControlAvailable(available) ? 0 : -1; } @@ -230,7 +230,7 @@ int CoreAudioOutput::VolumeIsAvailable(bool* available) { // Triggers the restart sequence. Only used for testing purposes to emulate // a real event where e.g. an active output device is removed. int CoreAudioOutput::RestartPlayout() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); if (!Playing()) { return 0; @@ -243,20 +243,20 @@ int CoreAudioOutput::RestartPlayout() { } bool CoreAudioOutput::Restarting() const { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); return IsRestarting(); } int CoreAudioOutput::SetSampleRate(uint32_t sample_rate) { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_); sample_rate_ = sample_rate; return 0; } void CoreAudioOutput::ReleaseCOMObjects() { - RTC_DLOG(INFO) << __FUNCTION__; + RTC_DLOG(LS_INFO) << __FUNCTION__; CoreAudioBase::ReleaseCOMObjects(); if (audio_render_client_.Get()) { audio_render_client_.Reset(); @@ -264,7 +264,7 @@ void CoreAudioOutput::ReleaseCOMObjects() { } bool CoreAudioOutput::OnErrorCallback(ErrorType error) { - RTC_DLOG(INFO) << __FUNCTION__ << ": " << as_integer(error); + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << as_integer(error); RTC_DCHECK_RUN_ON(&thread_checker_audio_); if (!initialized_ || !Playing()) { return true; @@ -273,7 +273,7 @@ bool CoreAudioOutput::OnErrorCallback(ErrorType error) { if (error == CoreAudioBase::ErrorType::kStreamDisconnected) { HandleStreamDisconnected(); } else { - RTC_DLOG(WARNING) << "Unsupported error type"; + RTC_DLOG(LS_WARNING) << "Unsupported error type"; } return true; } @@ -281,7 +281,7 @@ bool CoreAudioOutput::OnErrorCallback(ErrorType error) { bool CoreAudioOutput::OnDataCallback(uint64_t device_frequency) { RTC_DCHECK_RUN_ON(&thread_checker_audio_); if (num_data_callbacks_ == 0) { - RTC_LOG(INFO) << "--- Output audio stream is alive ---"; + RTC_LOG(LS_INFO) << "--- Output audio stream is alive ---"; } // Get the padding value which indicates the amount of valid unread data that // the endpoint buffer currently contains. @@ -329,7 +329,7 @@ bool CoreAudioOutput::OnDataCallback(uint64_t device_frequency) { // TODO(henrika): note that FineAudioBuffer adds latency as well. latency_ms_ = EstimateOutputLatencyMillis(device_frequency); if (num_data_callbacks_ % 500 == 0) { - RTC_DLOG(INFO) << "latency: " << latency_ms_; + RTC_DLOG(LS_INFO) << "latency: " << latency_ms_; } } @@ -394,7 +394,7 @@ int CoreAudioOutput::EstimateOutputLatencyMillis(uint64_t device_frequency) { // safe. // TODO(henrika): add more details. bool CoreAudioOutput::HandleStreamDisconnected() { - RTC_DLOG(INFO) << "<<<--- " << __FUNCTION__; + RTC_DLOG(LS_INFO) << "<<<--- " << __FUNCTION__; RTC_DCHECK_RUN_ON(&thread_checker_audio_); RTC_DCHECK(automatic_restart()); @@ -413,7 +413,7 @@ bool CoreAudioOutput::HandleStreamDisconnected() { return false; } - RTC_DLOG(INFO) << __FUNCTION__ << " --->>>"; + RTC_DLOG(LS_INFO) << __FUNCTION__ << " --->>>"; return true; } diff --git a/modules/audio_device/win/core_audio_utility_win.cc b/modules/audio_device/win/core_audio_utility_win.cc index 5950c8dced..c5a3520868 100644 --- a/modules/audio_device/win/core_audio_utility_win.cc +++ b/modules/audio_device/win/core_audio_utility_win.cc @@ -205,7 +205,7 @@ bool LoadAudiosesDll() { L"%WINDIR%\\system32\\audioses.dll"; wchar_t path[MAX_PATH] = {0}; ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); - RTC_DLOG(INFO) << rtc::ToUtf8(path); + RTC_DLOG(LS_INFO) << rtc::ToUtf8(path); return (LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) != nullptr); } @@ -214,7 +214,7 @@ bool LoadAvrtDll() { static const wchar_t* const kAvrtDLL = L"%WINDIR%\\system32\\Avrt.dll"; wchar_t path[MAX_PATH] = {0}; ExpandEnvironmentStringsW(kAvrtDLL, path, arraysize(path)); - RTC_DLOG(INFO) << rtc::ToUtf8(path); + RTC_DLOG(LS_INFO) << rtc::ToUtf8(path); return (LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) != nullptr); } @@ -283,10 +283,10 @@ bool IsDeviceActive(IMMDevice* device) { ComPtr CreateDeviceInternal(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "CreateDeviceInternal: " - "id=" - << device_id << ", flow=" << FlowToString(data_flow) - << ", role=" << RoleToString(role); + RTC_DLOG(LS_INFO) << "CreateDeviceInternal: " + "id=" + << device_id << ", flow=" << FlowToString(data_flow) + << ", role=" << RoleToString(role); ComPtr audio_endpoint_device; // Create the IMMDeviceEnumerator interface. @@ -587,10 +587,10 @@ bool GetDeviceNamesInternal(EDataFlow data_flow, // [2] friendly name: Headset Microphone (2- Arctis 7 Chat) // [2] unique id : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c} for (size_t i = 0; i < device_names->size(); ++i) { - RTC_DLOG(INFO) << "[" << i - << "] friendly name: " << (*device_names)[i].device_name; - RTC_DLOG(INFO) << "[" << i - << "] unique id : " << (*device_names)[i].unique_id; + RTC_DLOG(LS_INFO) << "[" << i + << "] friendly name: " << (*device_names)[i].device_name; + RTC_DLOG(LS_INFO) << "[" << i + << "] unique id : " << (*device_names)[i].unique_id; } return true; @@ -614,8 +614,8 @@ HRESULT GetPreferredAudioParametersInternal(IAudioClient* client, // Override default sample rate if `fixed_sample_rate` is set and different // from the default rate. if (fixed_sample_rate > 0 && fixed_sample_rate != sample_rate) { - RTC_DLOG(INFO) << "Using fixed sample rate instead of the preferred: " - << sample_rate << " is replaced by " << fixed_sample_rate; + RTC_DLOG(LS_INFO) << "Using fixed sample rate instead of the preferred: " + << sample_rate << " is replaced by " << fixed_sample_rate; sample_rate = fixed_sample_rate; } // TODO(henrika): utilize full mix_format.Format.wBitsPerSample. @@ -634,7 +634,7 @@ HRESULT GetPreferredAudioParametersInternal(IAudioClient* client, AudioParameters audio_params(sample_rate, channels, frames_per_buffer); *params = audio_params; - RTC_DLOG(INFO) << audio_params.ToString(); + RTC_DLOG(LS_INFO) << audio_params.ToString(); return hr; } @@ -669,13 +669,13 @@ size_t WaveFormatWrapper::size() const { } bool IsSupported() { - RTC_DLOG(INFO) << "IsSupported"; + RTC_DLOG(LS_INFO) << "IsSupported"; static bool g_is_supported = IsSupportedInternal(); return g_is_supported; } bool IsMMCSSSupported() { - RTC_DLOG(INFO) << "IsMMCSSSupported"; + RTC_DLOG(LS_INFO) << "IsMMCSSSupported"; return LoadAvrtDll(); } @@ -698,7 +698,7 @@ int NumberOfActiveDevices(EDataFlow data_flow) { } else if (data_flow == eAll) { str = "Total number of devices: "; } - RTC_DLOG(INFO) << str << number_of_active_devices; + RTC_DLOG(LS_INFO) << str << number_of_active_devices; return static_cast(number_of_active_devices); } @@ -713,33 +713,33 @@ uint32_t GetAudioClientVersion() { } ComPtr CreateDeviceEnumerator() { - RTC_DLOG(INFO) << "CreateDeviceEnumerator"; + RTC_DLOG(LS_INFO) << "CreateDeviceEnumerator"; return CreateDeviceEnumeratorInternal(true); } std::string GetDefaultInputDeviceID() { - RTC_DLOG(INFO) << "GetDefaultInputDeviceID"; + RTC_DLOG(LS_INFO) << "GetDefaultInputDeviceID"; ComPtr device( CreateDevice(AudioDeviceName::kDefaultDeviceId, eCapture, eConsole)); return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string(); } std::string GetDefaultOutputDeviceID() { - RTC_DLOG(INFO) << "GetDefaultOutputDeviceID"; + RTC_DLOG(LS_INFO) << "GetDefaultOutputDeviceID"; ComPtr device( CreateDevice(AudioDeviceName::kDefaultDeviceId, eRender, eConsole)); return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string(); } std::string GetCommunicationsInputDeviceID() { - RTC_DLOG(INFO) << "GetCommunicationsInputDeviceID"; + RTC_DLOG(LS_INFO) << "GetCommunicationsInputDeviceID"; ComPtr device(CreateDevice(AudioDeviceName::kDefaultDeviceId, eCapture, eCommunications)); return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string(); } std::string GetCommunicationsOutputDeviceID() { - RTC_DLOG(INFO) << "GetCommunicationsOutputDeviceID"; + RTC_DLOG(LS_INFO) << "GetCommunicationsOutputDeviceID"; ComPtr device(CreateDevice(AudioDeviceName::kDefaultDeviceId, eRender, eCommunications)); return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string(); @@ -748,24 +748,24 @@ std::string GetCommunicationsOutputDeviceID() { ComPtr CreateDevice(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "CreateDevice"; + RTC_DLOG(LS_INFO) << "CreateDevice"; return CreateDeviceInternal(device_id, data_flow, role); } AudioDeviceName GetDeviceName(IMMDevice* device) { - RTC_DLOG(INFO) << "GetDeviceName"; + RTC_DLOG(LS_INFO) << "GetDeviceName"; RTC_DCHECK(device); AudioDeviceName device_name(GetDeviceFriendlyNameInternal(device), GetDeviceIdInternal(device)); - RTC_DLOG(INFO) << "friendly name: " << device_name.device_name; - RTC_DLOG(INFO) << "unique id : " << device_name.unique_id; + RTC_DLOG(LS_INFO) << "friendly name: " << device_name.device_name; + RTC_DLOG(LS_INFO) << "unique id : " << device_name.unique_id; return device_name; } std::string GetFriendlyName(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "GetFriendlyName"; + RTC_DLOG(LS_INFO) << "GetFriendlyName"; ComPtr audio_device = CreateDevice(device_id, data_flow, role); if (!audio_device.Get()) return std::string(); @@ -775,7 +775,7 @@ std::string GetFriendlyName(const std::string& device_id, } EDataFlow GetDataFlow(IMMDevice* device) { - RTC_DLOG(INFO) << "GetDataFlow"; + RTC_DLOG(LS_INFO) << "GetDataFlow"; RTC_DCHECK(device); ComPtr endpoint; _com_error error = device->QueryInterface(endpoint.GetAddressOf()); @@ -796,32 +796,32 @@ EDataFlow GetDataFlow(IMMDevice* device) { } bool GetInputDeviceNames(webrtc::AudioDeviceNames* device_names) { - RTC_DLOG(INFO) << "GetInputDeviceNames"; + RTC_DLOG(LS_INFO) << "GetInputDeviceNames"; RTC_DCHECK(device_names); RTC_DCHECK(device_names->empty()); return GetDeviceNamesInternal(eCapture, device_names); } bool GetOutputDeviceNames(webrtc::AudioDeviceNames* device_names) { - RTC_DLOG(INFO) << "GetOutputDeviceNames"; + RTC_DLOG(LS_INFO) << "GetOutputDeviceNames"; RTC_DCHECK(device_names); RTC_DCHECK(device_names->empty()); return GetDeviceNamesInternal(eRender, device_names); } ComPtr CreateSessionManager2(IMMDevice* device) { - RTC_DLOG(INFO) << "CreateSessionManager2"; + RTC_DLOG(LS_INFO) << "CreateSessionManager2"; return CreateSessionManager2Internal(device); } Microsoft::WRL::ComPtr CreateSessionEnumerator( IMMDevice* device) { - RTC_DLOG(INFO) << "CreateSessionEnumerator"; + RTC_DLOG(LS_INFO) << "CreateSessionEnumerator"; return CreateSessionEnumeratorInternal(device); } int NumberOfActiveSessions(IMMDevice* device) { - RTC_DLOG(INFO) << "NumberOfActiveSessions"; + RTC_DLOG(LS_INFO) << "NumberOfActiveSessions"; ComPtr session_enumerator = CreateSessionEnumerator(device); @@ -833,7 +833,7 @@ int NumberOfActiveSessions(IMMDevice* device) { << ErrorToString(error); return 0; } - RTC_DLOG(INFO) << "Total number of audio sessions: " << session_count; + RTC_DLOG(LS_INFO) << "Total number of audio sessions: " << session_count; int num_active = 0; for (int session = 0; session < session_count; session++) { @@ -849,8 +849,8 @@ int NumberOfActiveSessions(IMMDevice* device) { // Log the display name of the audio session for debugging purposes. LPWSTR display_name; if (SUCCEEDED(session_control->GetDisplayName(&display_name))) { - RTC_DLOG(INFO) << "display name: " - << rtc::ToUtf8(display_name, wcslen(display_name)); + RTC_DLOG(LS_INFO) << "display name: " + << rtc::ToUtf8(display_name, wcslen(display_name)); CoTaskMemFree(display_name); } @@ -867,14 +867,14 @@ int NumberOfActiveSessions(IMMDevice* device) { } } - RTC_DLOG(INFO) << "Number of active audio sessions: " << num_active; + RTC_DLOG(LS_INFO) << "Number of active audio sessions: " << num_active; return num_active; } ComPtr CreateClient(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "CreateClient"; + RTC_DLOG(LS_INFO) << "CreateClient"; ComPtr device(CreateDevice(device_id, data_flow, role)); return CreateClientInternal(device.Get()); } @@ -882,7 +882,7 @@ ComPtr CreateClient(const std::string& device_id, ComPtr CreateClient2(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "CreateClient2"; + RTC_DLOG(LS_INFO) << "CreateClient2"; ComPtr device(CreateDevice(device_id, data_flow, role)); return CreateClient2Internal(device.Get()); } @@ -890,13 +890,13 @@ ComPtr CreateClient2(const std::string& device_id, ComPtr CreateClient3(const std::string& device_id, EDataFlow data_flow, ERole role) { - RTC_DLOG(INFO) << "CreateClient3"; + RTC_DLOG(LS_INFO) << "CreateClient3"; ComPtr device(CreateDevice(device_id, data_flow, role)); return CreateClient3Internal(device.Get()); } HRESULT SetClientProperties(IAudioClient2* client) { - RTC_DLOG(INFO) << "SetClientProperties"; + RTC_DLOG(LS_INFO) << "SetClientProperties"; RTC_DCHECK(client); if (GetAudioClientVersion() < 2) { RTC_LOG(LS_WARNING) << "Requires IAudioClient2 or higher"; @@ -919,10 +919,10 @@ HRESULT SetClientProperties(IAudioClient2* client) { RTC_LOG(LS_ERROR) << "IAudioClient2::IsOffloadCapable failed: " << ErrorToString(error); } - RTC_DLOG(INFO) << "supports_offload: " << supports_offload; + RTC_DLOG(LS_INFO) << "supports_offload: " << supports_offload; props.bIsOffload = false; #if (NTDDI_VERSION < NTDDI_WINBLUE) - RTC_DLOG(INFO) << "options: Not supported in this build"; + RTC_DLOG(LS_INFO) << "options: Not supported in this build"; #else // TODO(henrika): pros and cons compared with AUDCLNT_STREAMOPTIONS_NONE? props.Options |= AUDCLNT_STREAMOPTIONS_NONE; @@ -939,7 +939,7 @@ HRESULT SetClientProperties(IAudioClient2* client) { // an appropriate interface to use for communications scenarios. // This interface is mainly meant for pro audio scenarios. // props.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT; - RTC_DLOG(INFO) << "options: 0x" << rtc::ToHex(props.Options); + RTC_DLOG(LS_INFO) << "options: 0x" << rtc::ToHex(props.Options); #endif error = client->SetClientProperties(&props); if (FAILED(error.Error())) { @@ -953,7 +953,7 @@ HRESULT GetBufferSizeLimits(IAudioClient2* client, const WAVEFORMATEXTENSIBLE* format, REFERENCE_TIME* min_buffer_duration, REFERENCE_TIME* max_buffer_duration) { - RTC_DLOG(INFO) << "GetBufferSizeLimits"; + RTC_DLOG(LS_INFO) << "GetBufferSizeLimits"; RTC_DCHECK(client); if (GetAudioClientVersion() < 2) { RTC_LOG(LS_WARNING) << "Requires IAudioClient2 or higher"; @@ -975,15 +975,15 @@ HRESULT GetBufferSizeLimits(IAudioClient2* client, } else { *min_buffer_duration = min_duration; *max_buffer_duration = max_duration; - RTC_DLOG(INFO) << "min_buffer_duration: " << min_buffer_duration; - RTC_DLOG(INFO) << "max_buffer_duration: " << max_buffer_duration; + RTC_DLOG(LS_INFO) << "min_buffer_duration: " << min_buffer_duration; + RTC_DLOG(LS_INFO) << "max_buffer_duration: " << max_buffer_duration; } return error.Error(); } HRESULT GetSharedModeMixFormat(IAudioClient* client, WAVEFORMATEXTENSIBLE* format) { - RTC_DLOG(INFO) << "GetSharedModeMixFormat"; + RTC_DLOG(LS_INFO) << "GetSharedModeMixFormat"; RTC_DCHECK(client); // The GetMixFormat method retrieves the stream format that the audio engine @@ -1020,7 +1020,7 @@ HRESULT GetSharedModeMixFormat(IAudioClient* client, // Log a warning for the rare case where `mix_format` only contains a // stand-alone WAVEFORMATEX structure but don't return. if (!wrapped_format.IsExtensible()) { - RTC_DLOG(WARNING) + RTC_DLOG(LS_WARNING) << "The returned format contains no extended information. " "The size is " << wrapped_format.size() << " bytes."; @@ -1030,7 +1030,7 @@ HRESULT GetSharedModeMixFormat(IAudioClient* client, // the returned structure is correctly extended or not. RTC_CHECK_LE(wrapped_format.size(), sizeof(WAVEFORMATEXTENSIBLE)); memcpy(format, wrapped_format.get(), wrapped_format.size()); - RTC_DLOG(INFO) << WaveFormatToString(format); + RTC_DLOG(LS_INFO) << WaveFormatToString(format); return error.Error(); } @@ -1038,7 +1038,7 @@ HRESULT GetSharedModeMixFormat(IAudioClient* client, bool IsFormatSupported(IAudioClient* client, AUDCLNT_SHAREMODE share_mode, const WAVEFORMATEXTENSIBLE* format) { - RTC_DLOG(INFO) << "IsFormatSupported"; + RTC_DLOG(LS_INFO) << "IsFormatSupported"; RTC_DCHECK(client); ScopedCoMem closest_match; // This method provides a way for a client to determine, before calling @@ -1049,23 +1049,23 @@ bool IsFormatSupported(IAudioClient* client, _com_error error = client->IsFormatSupported( share_mode, reinterpret_cast(format), &closest_match); - RTC_LOG(INFO) << WaveFormatToString( + RTC_LOG(LS_INFO) << WaveFormatToString( const_cast(format)); if ((error.Error() == S_OK) && (closest_match == nullptr)) { - RTC_DLOG(INFO) + RTC_DLOG(LS_INFO) << "The audio endpoint device supports the specified stream format"; } else if ((error.Error() == S_FALSE) && (closest_match != nullptr)) { // Call succeeded with a closest match to the specified format. This log can // only be triggered for shared mode. RTC_LOG(LS_WARNING) << "Exact format is not supported, but a closest match exists"; - RTC_LOG(INFO) << WaveFormatToString(closest_match.Get()); + RTC_LOG(LS_INFO) << WaveFormatToString(closest_match.Get()); } else if ((error.Error() == AUDCLNT_E_UNSUPPORTED_FORMAT) && (closest_match == nullptr)) { // The audio engine does not support the caller-specified format or any // similar format. - RTC_DLOG(INFO) << "The audio endpoint device does not support the " - "specified stream format"; + RTC_DLOG(LS_INFO) << "The audio endpoint device does not support the " + "specified stream format"; } else { RTC_LOG(LS_ERROR) << "IAudioClient::IsFormatSupported failed: " << ErrorToString(error); @@ -1077,7 +1077,7 @@ bool IsFormatSupported(IAudioClient* client, HRESULT GetDevicePeriod(IAudioClient* client, AUDCLNT_SHAREMODE share_mode, REFERENCE_TIME* device_period) { - RTC_DLOG(INFO) << "GetDevicePeriod"; + RTC_DLOG(LS_INFO) << "GetDevicePeriod"; RTC_DCHECK(client); // The `default_period` parameter specifies the default scheduling period // for a shared-mode stream. The `minimum_period` parameter specifies the @@ -1094,10 +1094,10 @@ HRESULT GetDevicePeriod(IAudioClient* client, *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : minimum_period; - RTC_LOG(INFO) << "device_period: " - << ReferenceTimeToTimeDelta(*device_period).ms() << " [ms]"; - RTC_LOG(INFO) << "minimum_period: " - << ReferenceTimeToTimeDelta(minimum_period).ms() << " [ms]"; + RTC_LOG(LS_INFO) << "device_period: " + << ReferenceTimeToTimeDelta(*device_period).ms() << " [ms]"; + RTC_LOG(LS_INFO) << "minimum_period: " + << ReferenceTimeToTimeDelta(minimum_period).ms() << " [ms]"; return error.Error(); } @@ -1107,7 +1107,7 @@ HRESULT GetSharedModeEnginePeriod(IAudioClient3* client3, uint32_t* fundamental_period_in_frames, uint32_t* min_period_in_frames, uint32_t* max_period_in_frames) { - RTC_DLOG(INFO) << "GetSharedModeEnginePeriod"; + RTC_DLOG(LS_INFO) << "GetSharedModeEnginePeriod"; RTC_DCHECK(client3); UINT32 default_period = 0; @@ -1125,15 +1125,17 @@ HRESULT GetSharedModeEnginePeriod(IAudioClient3* client3, WAVEFORMATEX format_ex = format->Format; const WORD sample_rate = format_ex.nSamplesPerSec; - RTC_LOG(INFO) << "default_period_in_frames: " << default_period << " (" - << FramesToMilliseconds(default_period, sample_rate) << " ms)"; - RTC_LOG(INFO) << "fundamental_period_in_frames: " << fundamental_period - << " (" << FramesToMilliseconds(fundamental_period, sample_rate) - << " ms)"; - RTC_LOG(INFO) << "min_period_in_frames: " << min_period << " (" - << FramesToMilliseconds(min_period, sample_rate) << " ms)"; - RTC_LOG(INFO) << "max_period_in_frames: " << max_period << " (" - << FramesToMilliseconds(max_period, sample_rate) << " ms)"; + RTC_LOG(LS_INFO) << "default_period_in_frames: " << default_period << " (" + << FramesToMilliseconds(default_period, sample_rate) + << " ms)"; + RTC_LOG(LS_INFO) << "fundamental_period_in_frames: " << fundamental_period + << " (" + << FramesToMilliseconds(fundamental_period, sample_rate) + << " ms)"; + RTC_LOG(LS_INFO) << "min_period_in_frames: " << min_period << " (" + << FramesToMilliseconds(min_period, sample_rate) << " ms)"; + RTC_LOG(LS_INFO) << "max_period_in_frames: " << max_period << " (" + << FramesToMilliseconds(max_period, sample_rate) << " ms)"; *default_period_in_frames = default_period; *fundamental_period_in_frames = fundamental_period; *min_period_in_frames = min_period; @@ -1143,7 +1145,7 @@ HRESULT GetSharedModeEnginePeriod(IAudioClient3* client3, HRESULT GetPreferredAudioParameters(IAudioClient* client, AudioParameters* params) { - RTC_DLOG(INFO) << "GetPreferredAudioParameters"; + RTC_DLOG(LS_INFO) << "GetPreferredAudioParameters"; RTC_DCHECK(client); return GetPreferredAudioParametersInternal(client, params, -1); } @@ -1151,7 +1153,7 @@ HRESULT GetPreferredAudioParameters(IAudioClient* client, HRESULT GetPreferredAudioParameters(IAudioClient* client, webrtc::AudioParameters* params, uint32_t sample_rate) { - RTC_DLOG(INFO) << "GetPreferredAudioParameters: " << sample_rate; + RTC_DLOG(LS_INFO) << "GetPreferredAudioParameters: " << sample_rate; RTC_DCHECK(client); return GetPreferredAudioParametersInternal(client, params, sample_rate); } @@ -1162,8 +1164,9 @@ HRESULT SharedModeInitialize(IAudioClient* client, REFERENCE_TIME buffer_duration, bool auto_convert_pcm, uint32_t* endpoint_buffer_size) { - RTC_DLOG(INFO) << "SharedModeInitialize: buffer_duration=" << buffer_duration - << ", auto_convert_pcm=" << auto_convert_pcm; + RTC_DLOG(LS_INFO) << "SharedModeInitialize: buffer_duration=" + << buffer_duration + << ", auto_convert_pcm=" << auto_convert_pcm; RTC_DCHECK(client); RTC_DCHECK_GE(buffer_duration, 0); if (buffer_duration != 0) { @@ -1188,7 +1191,7 @@ HRESULT SharedModeInitialize(IAudioClient* client, (event_handle != nullptr && event_handle != INVALID_HANDLE_VALUE); if (use_event) { stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - RTC_DLOG(INFO) << "The stream is initialized to be event driven"; + RTC_DLOG(LS_INFO) << "The stream is initialized to be event driven"; } // Check if sample-rate conversion is requested. @@ -1197,11 +1200,11 @@ HRESULT SharedModeInitialize(IAudioClient* client, // from our (the client's) format to the audio engine mix format. // Currently only supported for testing, i.e., not possible to enable using // public APIs. - RTC_DLOG(INFO) << "The stream is initialized to support rate conversion"; + RTC_DLOG(LS_INFO) << "The stream is initialized to support rate conversion"; stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; stream_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; } - RTC_DLOG(INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags); + RTC_DLOG(LS_INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags); // Initialize the shared mode client for minimal delay if `buffer_duration` // is 0 or possibly a higher delay (more robust) if `buffer_duration` is @@ -1243,23 +1246,23 @@ HRESULT SharedModeInitialize(IAudioClient* client, } *endpoint_buffer_size = buffer_size_in_frames; - RTC_DLOG(INFO) << "endpoint buffer size: " << buffer_size_in_frames - << " [audio frames]"; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " << buffer_size_in_frames + << " [audio frames]"; const double size_in_ms = static_cast(buffer_size_in_frames) / (format->Format.nSamplesPerSec / 1000.0); - RTC_DLOG(INFO) << "endpoint buffer size: " - << static_cast(size_in_ms + 0.5) << " [ms]"; - RTC_DLOG(INFO) << "bytes per audio frame: " << format->Format.nBlockAlign; - RTC_DLOG(INFO) << "endpoint buffer size: " - << buffer_size_in_frames * format->Format.nChannels * - (format->Format.wBitsPerSample / 8) - << " [bytes]"; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " + << static_cast(size_in_ms + 0.5) << " [ms]"; + RTC_DLOG(LS_INFO) << "bytes per audio frame: " << format->Format.nBlockAlign; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " + << buffer_size_in_frames * format->Format.nChannels * + (format->Format.wBitsPerSample / 8) + << " [bytes]"; // TODO(henrika): utilize when delay measurements are added. REFERENCE_TIME latency = 0; error = client->GetStreamLatency(&latency); - RTC_DLOG(INFO) << "stream latency: " << ReferenceTimeToTimeDelta(latency).ms() - << " [ms]"; + RTC_DLOG(LS_INFO) << "stream latency: " + << ReferenceTimeToTimeDelta(latency).ms() << " [ms]"; return error.Error(); } @@ -1269,9 +1272,9 @@ HRESULT SharedModeInitializeLowLatency(IAudioClient3* client, uint32_t period_in_frames, bool auto_convert_pcm, uint32_t* endpoint_buffer_size) { - RTC_DLOG(INFO) << "SharedModeInitializeLowLatency: period_in_frames=" - << period_in_frames - << ", auto_convert_pcm=" << auto_convert_pcm; + RTC_DLOG(LS_INFO) << "SharedModeInitializeLowLatency: period_in_frames=" + << period_in_frames + << ", auto_convert_pcm=" << auto_convert_pcm; RTC_DCHECK(client); RTC_DCHECK_GT(period_in_frames, 0); if (auto_convert_pcm) { @@ -1284,13 +1287,13 @@ HRESULT SharedModeInitializeLowLatency(IAudioClient3* client, (event_handle != nullptr && event_handle != INVALID_HANDLE_VALUE); if (use_event) { stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - RTC_DLOG(INFO) << "The stream is initialized to be event driven"; + RTC_DLOG(LS_INFO) << "The stream is initialized to be event driven"; } if (auto_convert_pcm) { stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; stream_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; } - RTC_DLOG(INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags); + RTC_DLOG(LS_INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags); // Initialize the shared mode client for lowest possible latency. // It is assumed that GetSharedModeEnginePeriod() has been used to query the @@ -1324,17 +1327,17 @@ HRESULT SharedModeInitializeLowLatency(IAudioClient3* client, } *endpoint_buffer_size = buffer_size_in_frames; - RTC_DLOG(INFO) << "endpoint buffer size: " << buffer_size_in_frames - << " [audio frames]"; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " << buffer_size_in_frames + << " [audio frames]"; const double size_in_ms = static_cast(buffer_size_in_frames) / (format->Format.nSamplesPerSec / 1000.0); - RTC_DLOG(INFO) << "endpoint buffer size: " - << static_cast(size_in_ms + 0.5) << " [ms]"; - RTC_DLOG(INFO) << "bytes per audio frame: " << format->Format.nBlockAlign; - RTC_DLOG(INFO) << "endpoint buffer size: " - << buffer_size_in_frames * format->Format.nChannels * - (format->Format.wBitsPerSample / 8) - << " [bytes]"; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " + << static_cast(size_in_ms + 0.5) << " [ms]"; + RTC_DLOG(LS_INFO) << "bytes per audio frame: " << format->Format.nBlockAlign; + RTC_DLOG(LS_INFO) << "endpoint buffer size: " + << buffer_size_in_frames * format->Format.nChannels * + (format->Format.wBitsPerSample / 8) + << " [bytes]"; // TODO(henrika): utilize when delay measurements are added. REFERENCE_TIME latency = 0; @@ -1343,14 +1346,14 @@ HRESULT SharedModeInitializeLowLatency(IAudioClient3* client, RTC_LOG(LS_WARNING) << "IAudioClient::GetStreamLatency failed: " << ErrorToString(error); } else { - RTC_DLOG(INFO) << "stream latency: " - << ReferenceTimeToTimeDelta(latency).ms() << " [ms]"; + RTC_DLOG(LS_INFO) << "stream latency: " + << ReferenceTimeToTimeDelta(latency).ms() << " [ms]"; } return error.Error(); } ComPtr CreateRenderClient(IAudioClient* client) { - RTC_DLOG(INFO) << "CreateRenderClient"; + RTC_DLOG(LS_INFO) << "CreateRenderClient"; RTC_DCHECK(client); // Get access to the IAudioRenderClient interface. This interface // enables us to write output data to a rendering endpoint buffer. @@ -1366,7 +1369,7 @@ ComPtr CreateRenderClient(IAudioClient* client) { } ComPtr CreateCaptureClient(IAudioClient* client) { - RTC_DLOG(INFO) << "CreateCaptureClient"; + RTC_DLOG(LS_INFO) << "CreateCaptureClient"; RTC_DCHECK(client); // Get access to the IAudioCaptureClient interface. This interface // enables us to read input data from a capturing endpoint buffer. @@ -1382,7 +1385,7 @@ ComPtr CreateCaptureClient(IAudioClient* client) { } ComPtr CreateAudioClock(IAudioClient* client) { - RTC_DLOG(INFO) << "CreateAudioClock"; + RTC_DLOG(LS_INFO) << "CreateAudioClock"; RTC_DCHECK(client); // Get access to the IAudioClock interface. This interface enables us to // monitor a stream's data rate and the current position in the stream. @@ -1397,7 +1400,7 @@ ComPtr CreateAudioClock(IAudioClient* client) { } ComPtr CreateAudioSessionControl(IAudioClient* client) { - RTC_DLOG(INFO) << "CreateAudioSessionControl"; + RTC_DLOG(LS_INFO) << "CreateAudioSessionControl"; RTC_DCHECK(client); ComPtr audio_session_control; _com_error error = client->GetService(IID_PPV_ARGS(&audio_session_control)); @@ -1410,7 +1413,7 @@ ComPtr CreateAudioSessionControl(IAudioClient* client) { } ComPtr CreateSimpleAudioVolume(IAudioClient* client) { - RTC_DLOG(INFO) << "CreateSimpleAudioVolume"; + RTC_DLOG(LS_INFO) << "CreateSimpleAudioVolume"; RTC_DCHECK(client); // Get access to the ISimpleAudioVolume interface. This interface enables a // client to control the master volume level of an audio session. @@ -1427,7 +1430,7 @@ ComPtr CreateSimpleAudioVolume(IAudioClient* client) { bool FillRenderEndpointBufferWithSilence(IAudioClient* client, IAudioRenderClient* render_client) { - RTC_DLOG(INFO) << "FillRenderEndpointBufferWithSilence"; + RTC_DLOG(LS_INFO) << "FillRenderEndpointBufferWithSilence"; RTC_DCHECK(client); RTC_DCHECK(render_client); UINT32 endpoint_buffer_size = 0; @@ -1447,11 +1450,11 @@ bool FillRenderEndpointBufferWithSilence(IAudioClient* client, << ErrorToString(error); return false; } - RTC_DLOG(INFO) << "num_queued_frames: " << num_queued_frames; + RTC_DLOG(LS_INFO) << "num_queued_frames: " << num_queued_frames; BYTE* data = nullptr; int num_frames_to_fill = endpoint_buffer_size - num_queued_frames; - RTC_DLOG(INFO) << "num_frames_to_fill: " << num_frames_to_fill; + RTC_DLOG(LS_INFO) << "num_frames_to_fill: " << num_frames_to_fill; error = render_client->GetBuffer(num_frames_to_fill, &data); if (FAILED(error.Error())) { RTC_LOG(LS_ERROR) << "IAudioRenderClient::GetBuffer failed: " diff --git a/modules/audio_device/win/core_audio_utility_win.h b/modules/audio_device/win/core_audio_utility_win.h index 95ed91176d..754b4ddafa 100644 --- a/modules/audio_device/win/core_audio_utility_win.h +++ b/modules/audio_device/win/core_audio_utility_win.h @@ -83,7 +83,7 @@ class ScopedMMCSSRegistration { } explicit ScopedMMCSSRegistration(const wchar_t* task_name) { - RTC_DLOG(INFO) << "ScopedMMCSSRegistration: " << rtc::ToUtf8(task_name); + RTC_DLOG(LS_INFO) << "ScopedMMCSSRegistration: " << rtc::ToUtf8(task_name); // Register the calling thread with MMCSS for the supplied `task_name`. DWORD mmcss_task_index = 0; mmcss_handle_ = AvSetMmThreadCharacteristicsW(task_name, &mmcss_task_index); @@ -93,18 +93,18 @@ class ScopedMMCSSRegistration { } else { const DWORD priority_class = GetPriorityClass(GetCurrentProcess()); const int priority = GetThreadPriority(GetCurrentThread()); - RTC_DLOG(INFO) << "priority class: " - << PriorityClassToString(priority_class) << "(" - << priority_class << ")"; - RTC_DLOG(INFO) << "priority: " << PriorityToString(priority) << "(" - << priority << ")"; + RTC_DLOG(LS_INFO) << "priority class: " + << PriorityClassToString(priority_class) << "(" + << priority_class << ")"; + RTC_DLOG(LS_INFO) << "priority: " << PriorityToString(priority) << "(" + << priority << ")"; } } ~ScopedMMCSSRegistration() { if (Succeeded()) { // Deregister with MMCSS. - RTC_DLOG(INFO) << "~ScopedMMCSSRegistration"; + RTC_DLOG(LS_INFO) << "~ScopedMMCSSRegistration"; AvRevertMmThreadCharacteristics(mmcss_handle_); } } @@ -244,7 +244,7 @@ class ScopedHandle { void Close() { if (handle_) { if (!::CloseHandle(handle_)) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } handle_ = nullptr; } @@ -537,7 +537,7 @@ bool FillRenderEndpointBufferWithSilence(IAudioClient* client, // Prints/logs all fields of the format structure in `format`. // Also supports extended versions (WAVEFORMATEXTENSIBLE). -std::string WaveFormatToString(const WaveFormatWrapper format); +std::string WaveFormatToString(WaveFormatWrapper format); // Converts Windows internal REFERENCE_TIME (100 nanosecond units) into // generic webrtc::TimeDelta which then can be converted to any time unit. diff --git a/modules/audio_mixer/audio_mixer_impl.h b/modules/audio_mixer/audio_mixer_impl.h index 737fcbdc43..76b1131777 100644 --- a/modules/audio_mixer/audio_mixer_impl.h +++ b/modules/audio_mixer/audio_mixer_impl.h @@ -22,7 +22,6 @@ #include "api/scoped_refptr.h" #include "modules/audio_mixer/frame_combiner.h" #include "modules/audio_mixer/output_rate_calculator.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/race_checker.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -48,6 +47,9 @@ class AudioMixerImpl : public AudioMixer { ~AudioMixerImpl() override; + AudioMixerImpl(const AudioMixerImpl&) = delete; + AudioMixerImpl& operator=(const AudioMixerImpl&) = delete; + // AudioMixer functions bool AddSource(Source* audio_source) override; void RemoveSource(Source* audio_source) override; @@ -92,8 +94,6 @@ class AudioMixerImpl : public AudioMixer { // Component that handles actual adding of audio frames. FrameCombiner frame_combiner_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AudioMixerImpl); }; } // namespace webrtc diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 506c8212fd..ee6b579617 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -19,15 +19,6 @@ config("apm_debug_dump") { } } -rtc_library("config") { - visibility = [ ":*" ] - sources = [ - "include/config.cc", - "include/config.h", - ] - deps = [ "../../rtc_base/system:rtc_export" ] -} - rtc_library("api") { visibility = [ "*" ] sources = [ @@ -37,7 +28,6 @@ rtc_library("api") { deps = [ ":audio_frame_view", ":audio_processing_statistics", - ":config", "../../api:array_view", "../../api:scoped_refptr", "../../api/audio:aec3_config", @@ -122,6 +112,33 @@ rtc_source_set("aec_dump_interface") { absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] } +rtc_library("gain_controller2") { + configs += [ ":apm_debug_dump" ] + sources = [ + "gain_controller2.cc", + "gain_controller2.h", + ] + defines = [] + deps = [ + ":aec_dump_interface", + ":api", + ":apm_logging", + ":audio_buffer", + ":audio_frame_view", + "../../common_audio", + "../../rtc_base:atomicops", + "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:stringutils", + "../../system_wrappers:field_trial", + "agc2:adaptive_digital", + "agc2:cpu_features", + "agc2:fixed_digital", + "agc2:gain_applier", + "agc2:vad_wrapper", + ] +} + rtc_library("audio_processing") { visibility = [ "*" ] configs += [ ":apm_debug_dump" ] @@ -129,26 +146,11 @@ rtc_library("audio_processing") { "audio_processing_builder_impl.cc", "audio_processing_impl.cc", "audio_processing_impl.h", - "common.h", "echo_control_mobile_impl.cc", "echo_control_mobile_impl.h", - "echo_detector/circular_buffer.cc", - "echo_detector/circular_buffer.h", - "echo_detector/mean_variance_estimator.cc", - "echo_detector/mean_variance_estimator.h", - "echo_detector/moving_max.cc", - "echo_detector/moving_max.h", - "echo_detector/normalized_covariance_estimator.cc", - "echo_detector/normalized_covariance_estimator.h", "gain_control_impl.cc", "gain_control_impl.h", - "gain_controller2.cc", - "gain_controller2.h", - "level_estimator.cc", - "level_estimator.h", "render_queue_item_verifier.h", - "residual_echo_detector.cc", - "residual_echo_detector.h", "typing_detection.cc", "typing_detection.h", ] @@ -162,11 +164,10 @@ rtc_library("audio_processing") { ":audio_frame_proxies", ":audio_frame_view", ":audio_processing_statistics", - ":config", + ":gain_controller2", ":high_pass_filter", ":optionally_built_submodule_creators", ":rms_level", - ":voice_detection", "../../api:array_view", "../../api:function_view", "../../api/audio:aec3_config", @@ -191,11 +192,9 @@ rtc_library("audio_processing") { "aec_dump:aec_dump", "aecm:aecm_core", "agc", + "agc:analog_gain_stats_reporter", "agc:gain_control_interface", "agc:legacy_agc", - "agc2:adaptive_digital", - "agc2:fixed_digital", - "agc2:gain_applier", "capture_levels_adjuster", "ns", "transient:transient_suppressor_api", @@ -218,18 +217,31 @@ rtc_library("audio_processing") { } } -rtc_library("voice_detection") { +rtc_library("residual_echo_detector") { + poisonous = [ "default_echo_detector" ] + configs += [ ":apm_debug_dump" ] sources = [ - "voice_detection.cc", - "voice_detection.h", + "echo_detector/circular_buffer.cc", + "echo_detector/circular_buffer.h", + "echo_detector/mean_variance_estimator.cc", + "echo_detector/mean_variance_estimator.h", + "echo_detector/moving_max.cc", + "echo_detector/moving_max.h", + "echo_detector/normalized_covariance_estimator.cc", + "echo_detector/normalized_covariance_estimator.h", + "residual_echo_detector.cc", + "residual_echo_detector.h", ] deps = [ ":api", - ":audio_buffer", - "../../api/audio:audio_frame_api", - "../../common_audio:common_audio_c", + ":apm_logging", + "../../api:array_view", "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers:metrics", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("optionally_built_submodule_creators") { @@ -335,7 +347,6 @@ if (rtc_include_tests) { sources = [ "audio_buffer_unittest.cc", "audio_frame_view_unittest.cc", - "config_unittest.cc", "echo_control_mobile_unittest.cc", "gain_controller2_unittest.cc", "splitting_filter_unittest.cc", @@ -350,14 +361,14 @@ if (rtc_include_tests) { ":audio_frame_view", ":audio_processing", ":audioproc_test_utils", - ":config", + ":gain_controller2", ":high_pass_filter", ":mocks", - ":voice_detection", "../../api:array_view", "../../api:scoped_refptr", "../../api/audio:aec3_config", "../../api/audio:aec3_factory", + "../../api/audio:echo_detector_creator", "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base", @@ -385,8 +396,8 @@ if (rtc_include_tests) { "agc2:biquad_filter_unittests", "agc2:fixed_digital_unittests", "agc2:noise_estimator_unittests", - "agc2:rnn_vad_with_level_unittests", "agc2:test_utils", + "agc2:vad_wrapper_unittests", "agc2/rnn_vad:unittests", "capture_levels_adjuster", "capture_levels_adjuster:capture_levels_adjuster_unittests", @@ -415,6 +426,7 @@ if (rtc_include_tests) { ":audioproc_test_utils", ":audioproc_unittest_proto", ":optionally_built_submodule_creators", + ":residual_echo_detector", ":rms_level", ":runtime_settings_protobuf_utils", "../../api/audio:audio_frame_api", @@ -436,7 +448,6 @@ if (rtc_include_tests) { "echo_detector/normalized_covariance_estimator_unittest.cc", "gain_control_unittest.cc", "high_pass_filter_unittest.cc", - "level_estimator_unittest.cc", "residual_echo_detector_unittest.cc", "rms_level_unittest.cc", "test/debug_dump_replayer.cc", @@ -447,7 +458,6 @@ if (rtc_include_tests) { "test/echo_canceller_test_tools_unittest.cc", "test/echo_control_mock.h", "test/test_utils.h", - "voice_detection_unittest.cc", ] } } @@ -515,6 +525,7 @@ if (rtc_include_tests) { ":runtime_settings_protobuf_utils", "../../api/audio:aec3_config_json", "../../api/audio:aec3_factory", + "../../api/audio:echo_detector_creator", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.cc b/modules/audio_processing/aec3/adaptive_fir_filter.cc index bf3a7809f4..917aa951ee 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter.cc @@ -68,19 +68,21 @@ void ComputeFrequencyResponse_Neon( 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]); + 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]); + 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); + 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); + 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); } } } @@ -101,20 +103,22 @@ void ComputeFrequencyResponse_Sse2( // 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 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 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]); + __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); + _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); + 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); } } } diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc b/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc index 245b45ac31..6c8c948026 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc @@ -31,19 +31,21 @@ void ComputeFrequencyResponse_Avx2( 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 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]); + __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]); + __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); + _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); + 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); } } } diff --git a/modules/audio_processing/aec3/aec3_common.h b/modules/audio_processing/aec3/aec3_common.h index 3bfff967a0..32b564f14b 100644 --- a/modules/audio_processing/aec3/aec3_common.h +++ b/modules/audio_processing/aec3/aec3_common.h @@ -85,10 +85,10 @@ constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor, Aec3Optimization DetectOptimization(); // Computes the log2 of the input in a fast an approximate manner. -float FastApproxLog2f(const float in); +float FastApproxLog2f(float in); // Returns dB from a power quantity expressed in log2. -float Log2TodB(const float in_log2); +float Log2TodB(float in_log2); static_assert(1 << kBlockSizeLog2 == kBlockSize, "Proper number of shifts for blocksize"); diff --git a/modules/audio_processing/aec3/aec3_fft.cc b/modules/audio_processing/aec3/aec3_fft.cc index 8dfa183367..9cc8016f0b 100644 --- a/modules/audio_processing/aec3/aec3_fft.cc +++ b/modules/audio_processing/aec3/aec3_fft.cc @@ -101,10 +101,10 @@ void Aec3Fft::ZeroPaddedFft(rtc::ArrayView x, [](float a, float b) { return a * b; }); break; case Window::kSqrtHanning: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } Fft(&fft, X); @@ -125,7 +125,7 @@ void Aec3Fft::PaddedFft(rtc::ArrayView x, std::copy(x.begin(), x.end(), fft.begin() + x_old.size()); break; case Window::kHanning: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case Window::kSqrtHanning: std::transform(x_old.begin(), x_old.end(), std::begin(kSqrtHanning128), @@ -135,7 +135,7 @@ void Aec3Fft::PaddedFft(rtc::ArrayView x, fft.begin() + x_old.size(), std::multiplies()); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } Fft(&fft, X); diff --git a/modules/audio_processing/aec3/aec3_fft.h b/modules/audio_processing/aec3/aec3_fft.h index 6f7fbe4d0e..c68de53963 100644 --- a/modules/audio_processing/aec3/aec3_fft.h +++ b/modules/audio_processing/aec3/aec3_fft.h @@ -18,7 +18,6 @@ #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -30,6 +29,9 @@ class Aec3Fft { 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); @@ -66,8 +68,6 @@ class Aec3Fft { private: const OouraFft ooura_fft_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Aec3Fft); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/alignment_mixer.h b/modules/audio_processing/aec3/alignment_mixer.h index 682aec9124..aa1830fc03 100644 --- a/modules/audio_processing/aec3/alignment_mixer.h +++ b/modules/audio_processing/aec3/alignment_mixer.h @@ -49,7 +49,7 @@ class AlignmentMixer { int selected_channel_ = 0; size_t block_counter_ = 0; - void Downmix(const rtc::ArrayView> x, + void Downmix(rtc::ArrayView> x, rtc::ArrayView y) const; int SelectChannel(rtc::ArrayView> x); }; diff --git a/modules/audio_processing/aec3/block_delay_buffer.cc b/modules/audio_processing/aec3/block_delay_buffer.cc index b9eb3c9f93..059bbafcdb 100644 --- a/modules/audio_processing/aec3/block_delay_buffer.cc +++ b/modules/audio_processing/aec3/block_delay_buffer.cc @@ -41,17 +41,24 @@ void BlockDelayBuffer::DelaySignal(AudioBuffer* frame) { RTC_DCHECK_EQ(buf_[ch].size(), frame->num_bands()); RTC_DCHECK_EQ(buf_[ch].size(), num_bands); rtc::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; - for (size_t k = 0; k < frame_length_; ++k) { - const float tmp = buf_[ch][band][i]; - buf_[ch][band][i] = frame_ch[band][k]; - frame_ch[band][k] = tmp; + // 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]; - i = i < delay_ - 1 ? i + 1 : 0; + 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; } } } diff --git a/modules/audio_processing/aec3/block_processor_metrics.h b/modules/audio_processing/aec3/block_processor_metrics.h index 4ba053683b..a70d0dac5b 100644 --- a/modules/audio_processing/aec3/block_processor_metrics.h +++ b/modules/audio_processing/aec3/block_processor_metrics.h @@ -11,8 +11,6 @@ #ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ #define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ -#include "rtc_base/constructor_magic.h" - namespace webrtc { // Handles the reporting of metrics for the block_processor. @@ -20,6 +18,9 @@ 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); @@ -38,8 +39,6 @@ class BlockProcessorMetrics { int render_buffer_underruns_ = 0; int render_buffer_overruns_ = 0; int buffer_render_calls_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(BlockProcessorMetrics); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/comfort_noise_generator.h b/modules/audio_processing/aec3/comfort_noise_generator.h index 16eaf3550f..2785b765c5 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator.h +++ b/modules/audio_processing/aec3/comfort_noise_generator.h @@ -19,7 +19,6 @@ #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/constructor_magic.h" #include "rtc_base/system/arch.h" namespace webrtc { diff --git a/modules/audio_processing/aec3/decimator.h b/modules/audio_processing/aec3/decimator.h index 3ccd292f08..dbff3d9fff 100644 --- a/modules/audio_processing/aec3/decimator.h +++ b/modules/audio_processing/aec3/decimator.h @@ -17,7 +17,6 @@ #include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -26,6 +25,9 @@ class Decimator { public: explicit Decimator(size_t down_sampling_factor); + Decimator(const Decimator&) = delete; + Decimator& operator=(const Decimator&) = delete; + // Downsamples the signal. void Decimate(rtc::ArrayView in, rtc::ArrayView out); @@ -33,8 +35,6 @@ class Decimator { const size_t down_sampling_factor_; CascadedBiQuadFilter anti_aliasing_filter_; CascadedBiQuadFilter noise_reduction_filter_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Decimator); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/echo_audibility.h b/modules/audio_processing/aec3/echo_audibility.h index 1ffc017b7d..b9d6f87d2a 100644 --- a/modules/audio_processing/aec3/echo_audibility.h +++ b/modules/audio_processing/aec3/echo_audibility.h @@ -19,7 +19,6 @@ #include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/spectrum_buffer.h" #include "modules/audio_processing/aec3/stationarity_estimator.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc index dfa932f49c..419a056d20 100644 --- a/modules/audio_processing/aec3/echo_canceller3.cc +++ b/modules/audio_processing/aec3/echo_canceller3.cc @@ -27,8 +27,8 @@ namespace { enum class EchoCanceller3ApiCall { kCapture, kRender }; bool DetectSaturation(rtc::ArrayView y) { - for (auto y_k : y) { - if (y_k >= 32700.0f || y_k <= -32700.0f) { + for (size_t k = 0; k < y.size(); ++k) { + if (y[k] >= 32700.0f || y[k] <= -32700.0f) { return true; } } @@ -290,6 +290,11 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) { adjusted_cfg.ep_strength.use_conservative_tail_frequency_response = true; } + if (field_trial::IsDisabled("WebRTC-Aec3ConservativeTailFreqResponse")) { + adjusted_cfg.ep_strength.use_conservative_tail_frequency_response = false; + } + + if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) { // Two blocks headroom. adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2; @@ -797,7 +802,7 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, if (linear_output && !linear_output_framer_) { RTC_LOG(LS_ERROR) << "Trying to retrieve the linear AEC output without " "properly configuring AEC3."; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // Report capture call in the metrics and periodically update API call diff --git a/modules/audio_processing/aec3/echo_path_delay_estimator.h b/modules/audio_processing/aec3/echo_path_delay_estimator.h index 6c8c21282e..d8f97757bb 100644 --- a/modules/audio_processing/aec3/echo_path_delay_estimator.h +++ b/modules/audio_processing/aec3/echo_path_delay_estimator.h @@ -21,7 +21,6 @@ #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" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -37,6 +36,9 @@ class EchoPathDelayEstimator { 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); @@ -71,8 +73,6 @@ class EchoPathDelayEstimator { // Internal reset method with more granularity. void Reset(bool reset_lag_aggregator, bool reset_delay_confidence); - - RTC_DISALLOW_COPY_AND_ASSIGN(EchoPathDelayEstimator); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/echo_remover_metrics.cc b/modules/audio_processing/aec3/echo_remover_metrics.cc index 1ceb329d38..c3fc80773a 100644 --- a/modules/audio_processing/aec3/echo_remover_metrics.cc +++ b/modules/audio_processing/aec3/echo_remover_metrics.cc @@ -113,7 +113,7 @@ void EchoRemoverMetrics::Update( ResetMetrics(); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } diff --git a/modules/audio_processing/aec3/echo_remover_metrics.h b/modules/audio_processing/aec3/echo_remover_metrics.h index c3d8e20da1..aec8084d78 100644 --- a/modules/audio_processing/aec3/echo_remover_metrics.h +++ b/modules/audio_processing/aec3/echo_remover_metrics.h @@ -15,7 +15,6 @@ #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec_state.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,9 @@ class EchoRemoverMetrics { EchoRemoverMetrics(); + EchoRemoverMetrics(const EchoRemoverMetrics&) = delete; + EchoRemoverMetrics& operator=(const EchoRemoverMetrics&) = delete; + // Updates the metric with new data. void Update( const AecState& aec_state, @@ -52,8 +54,6 @@ class EchoRemoverMetrics { DbMetric erle_time_domain_; bool saturated_capture_ = false; bool metrics_reported_ = false; - - RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverMetrics); }; namespace aec3 { diff --git a/modules/audio_processing/aec3/erl_estimator.h b/modules/audio_processing/aec3/erl_estimator.h index 89bf6ace36..639a52c561 100644 --- a/modules/audio_processing/aec3/erl_estimator.h +++ b/modules/audio_processing/aec3/erl_estimator.h @@ -18,7 +18,6 @@ #include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -28,6 +27,9 @@ class ErlEstimator { explicit ErlEstimator(size_t startup_phase_length_blocks_); ~ErlEstimator(); + ErlEstimator(const ErlEstimator&) = delete; + ErlEstimator& operator=(const ErlEstimator&) = delete; + // Resets the ERL estimation. void Reset(); @@ -49,7 +51,6 @@ class ErlEstimator { float erl_time_domain_; int hold_counter_time_domain_; size_t blocks_since_reset_ = 0; - RTC_DISALLOW_COPY_AND_ASSIGN(ErlEstimator); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc index be954d3a18..c07e5c8648 100644 --- a/modules/audio_processing/aec3/filter_analyzer.cc +++ b/modules/audio_processing/aec3/filter_analyzer.cc @@ -170,11 +170,16 @@ void FilterAnalyzer::PreProcessFilters( 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_sample_; ++k) { + k <= region_end; ++k) { + float tmp = h_highpass_ch[k]; for (size_t j = 0; j < h.size(); ++j) { - h_highpass_[ch][k] += filters_time_domain[ch][k - j] * h[j]; + tmp += filters_time_domain_ch[k - j] * h[j]; } + h_highpass_ch[k] = tmp; } } } @@ -230,19 +235,23 @@ bool FilterAnalyzer::ConsistentFilterDetector::Detect( 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); + 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 += 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_ / diff --git a/modules/audio_processing/aec3/filter_analyzer.h b/modules/audio_processing/aec3/filter_analyzer.h index b0b7070119..e05fb71138 100644 --- a/modules/audio_processing/aec3/filter_analyzer.h +++ b/modules/audio_processing/aec3/filter_analyzer.h @@ -20,7 +20,6 @@ #include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { diff --git a/modules/audio_processing/aec3/fullband_erle_estimator.h b/modules/audio_processing/aec3/fullband_erle_estimator.h index 2b720a4de4..7a082176d6 100644 --- a/modules/audio_processing/aec3/fullband_erle_estimator.h +++ b/modules/audio_processing/aec3/fullband_erle_estimator.h @@ -67,7 +67,7 @@ class FullBandErleEstimator { // 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(const float Y2_sum, const float E2_sum); + 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. diff --git a/modules/audio_processing/aec3/matched_filter.cc b/modules/audio_processing/aec3/matched_filter.cc index 1721e9c983..faca933856 100644 --- a/modules/audio_processing/aec3/matched_filter.cc +++ b/modules/audio_processing/aec3/matched_filter.cc @@ -166,7 +166,9 @@ void MatchedFilterCore_SSE2(size_t x_start_index, // 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; @@ -179,20 +181,26 @@ void MatchedFilterCore_SSE2(size_t x_start_index, 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) { + 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_4 * 4; k > 0; --k, ++h_p, ++x_p) { + 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; @@ -202,8 +210,10 @@ void MatchedFilterCore_SSE2(size_t x_start_index, } // 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]; @@ -298,6 +308,41 @@ void MatchedFilterCore(size_t x_start_index, } } +size_t MaxSquarePeakIndex(rtc::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, @@ -390,17 +435,15 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, } // Compute anchor for the matched filter error. - const float error_sum_anchor = - std::inner_product(y.begin(), y.end(), y.begin(), 0.f); + float error_sum_anchor = 0.0f; + for (size_t k = 0; k < y.size(); ++k) { + error_sum_anchor += y[k] * y[k]; + } // 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 = std::distance( - filters_[n].begin(), - std::max_element( - filters_[n].begin(), filters_[n].end(), - [](float a, float b) -> bool { return a * a < b * b; })); + const size_t lag_estimate = aec3::MaxSquarePeakIndex(filters_[n]); // Update the lag estimates for the matched filter. lag_estimates_[n] = LagEstimate( @@ -442,7 +485,7 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, data_dumper_->DumpRaw("aec3_correlator_9_h", filters_[9]); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } alignment_shift += filter_intra_lag_shift_; diff --git a/modules/audio_processing/aec3/matched_filter.h b/modules/audio_processing/aec3/matched_filter.h index c6410ab4ee..dd4a678394 100644 --- a/modules/audio_processing/aec3/matched_filter.h +++ b/modules/audio_processing/aec3/matched_filter.h @@ -74,6 +74,9 @@ void MatchedFilterCore(size_t x_start_index, bool* filters_updated, float* error_sum); +// Find largest peak of squared values in array. +size_t MaxSquarePeakIndex(rtc::ArrayView h); + } // namespace aec3 // Produces recursively updated cross-correlation estimates for several signal diff --git a/modules/audio_processing/aec3/matched_filter_avx2.cc b/modules/audio_processing/aec3/matched_filter_avx2.cc index ed32102aa4..8b7010f1dc 100644 --- a/modules/audio_processing/aec3/matched_filter_avx2.cc +++ b/modules/audio_processing/aec3/matched_filter_avx2.cc @@ -39,7 +39,9 @@ void MatchedFilterCore_AVX2(size_t x_start_index, // 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; @@ -52,18 +54,22 @@ void MatchedFilterCore_AVX2(size_t x_start_index, const int chunk2 = h_size - chunk1; 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_p += 8, x_p += 8) { + 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_8 * 8; k > 0; --k, ++h_p, ++x_p) { + 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; @@ -73,6 +79,8 @@ void MatchedFilterCore_AVX2(size_t x_start_index, } // 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 x2_sum_128 = _mm_add_ps(_mm256_extractf128_ps(x2_sum_256, 0), _mm256_extractf128_ps(x2_sum_256, 1)); __m128 s_128 = _mm_add_ps(_mm256_extractf128_ps(s_256, 0), diff --git a/modules/audio_processing/aec3/matched_filter_unittest.cc b/modules/audio_processing/aec3/matched_filter_unittest.cc index 37b51fa624..8abfb69a7a 100644 --- a/modules/audio_processing/aec3/matched_filter_unittest.cc +++ b/modules/audio_processing/aec3/matched_filter_unittest.cc @@ -176,6 +176,28 @@ TEST(MatchedFilter, TestAvx2Optimizations) { #endif +// Verifies that the (optimized) function MaxSquarePeakIndex() produces output +// equal to the corresponding std-functions. +TEST(MatchedFilter, MaxSquarePeakIndex) { + Random random_generator(42U); + constexpr int kMaxLength = 128; + constexpr int kNumIterationsPerLength = 256; + for (int length = 1; length < kMaxLength; ++length) { + std::vector y(length); + for (int i = 0; i < kNumIterationsPerLength; ++i) { + RandomizeSampleVector(&random_generator, y); + + size_t lag_from_function = MaxSquarePeakIndex(y); + size_t lag_from_std = std::distance( + y.begin(), + std::max_element(y.begin(), y.end(), [](float a, float b) -> bool { + return a * a < b * b; + })); + EXPECT_EQ(lag_from_function, lag_from_std); + } + } +} + // Verifies that the matched filter produces proper lag estimates for // artificially // delayed signals. diff --git a/modules/audio_processing/aec3/render_buffer.cc b/modules/audio_processing/aec3/render_buffer.cc index 60ea69cce1..aa511e2b6b 100644 --- a/modules/audio_processing/aec3/render_buffer.cc +++ b/modules/audio_processing/aec3/render_buffer.cc @@ -42,8 +42,9 @@ void RenderBuffer::SpectralSum( int position = spectrum_buffer_->read; for (size_t j = 0; j < num_spectra; ++j) { for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { - std::transform(X2->begin(), X2->end(), channel_spectrum.begin(), - X2->begin(), std::plus()); + for (size_t k = 0; k < X2->size(); ++k) { + (*X2)[k] += channel_spectrum[k]; + } } position = spectrum_buffer_->IncIndex(position); } @@ -60,18 +61,18 @@ void RenderBuffer::SpectralSums( size_t j = 0; for (; j < num_spectra_shorter; ++j) { for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { - std::transform(X2_shorter->begin(), X2_shorter->end(), - channel_spectrum.begin(), X2_shorter->begin(), - std::plus()); + 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]) { - std::transform(X2_longer->begin(), X2_longer->end(), - channel_spectrum.begin(), X2_longer->begin(), - std::plus()); + for (size_t k = 0; k < X2_longer->size(); ++k) { + (*X2_longer)[k] += channel_spectrum[k]; + } } position = spectrum_buffer_->IncIndex(position); } diff --git a/modules/audio_processing/aec3/render_delay_controller_metrics.h b/modules/audio_processing/aec3/render_delay_controller_metrics.h index 8c527a142e..309122d80d 100644 --- a/modules/audio_processing/aec3/render_delay_controller_metrics.h +++ b/modules/audio_processing/aec3/render_delay_controller_metrics.h @@ -15,7 +15,6 @@ #include "absl/types/optional.h" #include "modules/audio_processing/aec3/clockdrift_detector.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -24,6 +23,10 @@ class RenderDelayControllerMetrics { public: RenderDelayControllerMetrics(); + RenderDelayControllerMetrics(const RenderDelayControllerMetrics&) = delete; + RenderDelayControllerMetrics& operator=(const RenderDelayControllerMetrics&) = + delete; + // Updates the metric with new data. void Update(absl::optional delay_samples, size_t buffer_delay_blocks, @@ -46,8 +49,6 @@ class RenderDelayControllerMetrics { bool metrics_reported_ = false; bool initial_update = true; int skew_shift_count_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(RenderDelayControllerMetrics); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/render_signal_analyzer.h b/modules/audio_processing/aec3/render_signal_analyzer.h index c7a3d8b7a0..2e4aaa4ba7 100644 --- a/modules/audio_processing/aec3/render_signal_analyzer.h +++ b/modules/audio_processing/aec3/render_signal_analyzer.h @@ -20,7 +20,6 @@ #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/render_buffer.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -30,6 +29,9 @@ class RenderSignalAnalyzer { 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 absl::optional& delay_partitions); @@ -53,8 +55,6 @@ class RenderSignalAnalyzer { std::array narrow_band_counters_; absl::optional narrow_peak_band_; size_t narrow_peak_counter_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RenderSignalAnalyzer); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/reverb_decay_estimator.cc b/modules/audio_processing/aec3/reverb_decay_estimator.cc index 24f579bcf9..2daf376911 100644 --- a/modules/audio_processing/aec3/reverb_decay_estimator.cc +++ b/modules/audio_processing/aec3/reverb_decay_estimator.cc @@ -296,7 +296,7 @@ void ReverbDecayEstimator::LateReverbLinearRegressor::Accumulate(float z) { float ReverbDecayEstimator::LateReverbLinearRegressor::Estimate() { RTC_DCHECK(EstimateAvailable()); if (nn_ == 0.f) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0.f; } return nz_ / nn_; diff --git a/modules/audio_processing/aec3/suppression_filter.cc b/modules/audio_processing/aec3/suppression_filter.cc index 8a813d9bd9..1312fa892b 100644 --- a/modules/audio_processing/aec3/suppression_filter.cc +++ b/modules/audio_processing/aec3/suppression_filter.cc @@ -108,12 +108,12 @@ void SuppressionFilter::ApplyGain( for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { // Apply suppression gains. - E.re[i] *= suppression_gain[i]; - E.im[i] *= suppression_gain[i]; + 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] += noise_gain[i] * comfort_noise[ch].re[i]; - E.im[i] += noise_gain[i] * comfort_noise[ch].im[i]; + 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. @@ -121,24 +121,25 @@ void SuppressionFilter::ApplyGain( constexpr float kIfftNormalization = 2.f / kFftLength; fft_.Ifft(E, &e_extended); - auto& e0 = (*e)[0][ch]; - auto& e0_old = e_output_old_[0][ch]; + float* e0 = (*e)[0][ch].data(); + 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) { - e0[i] = e0_old[i] * kSqrtHanning[kFftLengthBy2 + i]; - e0[i] += e_extended[i] * kSqrtHanning[i]; - e0[i] *= kIfftNormalization; + 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(e0_old)); + e_extended.begin() + kFftLength, + std::begin(e_output_old_[0][ch])); // Apply suppression gain to upper bands. for (size_t b = 1; b < e->size(); ++b) { - auto& e_band = (*e)[b][ch]; + float* e_band = (*e)[b][ch].data(); for (size_t i = 0; i < kFftLengthBy2; ++i) { e_band[i] *= high_bands_gain; } @@ -150,7 +151,7 @@ void SuppressionFilter::ApplyGain( std::array time_domain_high_band_noise; fft_.Ifft(E, &time_domain_high_band_noise); - auto& e1 = (*e)[1][ch]; + float* e1 = (*e)[1][ch].data(); 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; @@ -159,8 +160,8 @@ void SuppressionFilter::ApplyGain( // Delay upper bands to match the delay of the filter bank. for (size_t b = 1; b < e->size(); ++b) { - auto& e_band = (*e)[b][ch]; - auto& e_band_old = e_output_old_[b][ch]; + float* e_band = (*e)[b][ch].data(); + 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]); } @@ -168,7 +169,7 @@ void SuppressionFilter::ApplyGain( // Clamp output of all bands. for (size_t b = 0; b < e->size(); ++b) { - auto& e_band = (*e)[b][ch]; + float* e_band = (*e)[b][ch].data(); for (size_t i = 0; i < kFftLengthBy2; ++i) { e_band[i] = rtc::SafeClamp(e_band[i], -32768.f, 32767.f); } diff --git a/modules/audio_processing/aec3/suppression_filter.h b/modules/audio_processing/aec3/suppression_filter.h index dcf2292c7f..375bfda5a7 100644 --- a/modules/audio_processing/aec3/suppression_filter.h +++ b/modules/audio_processing/aec3/suppression_filter.h @@ -17,7 +17,6 @@ #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 "rtc_base/constructor_magic.h" namespace webrtc { @@ -27,6 +26,10 @@ class SuppressionFilter { int sample_rate_hz, size_t num_capture_channels_); ~SuppressionFilter(); + + SuppressionFilter(const SuppressionFilter&) = delete; + SuppressionFilter& operator=(const SuppressionFilter&) = delete; + void ApplyGain(rtc::ArrayView comfort_noise, rtc::ArrayView comfort_noise_high_bands, const std::array& suppression_gain, @@ -40,7 +43,6 @@ class SuppressionFilter { const size_t num_capture_channels_; const Aec3Fft fft_; std::vector>> e_output_old_; - RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionFilter); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc index 6405d71c2d..14366f1aec 100644 --- a/modules/audio_processing/aec3/suppression_gain.cc +++ b/modules/audio_processing/aec3/suppression_gain.cc @@ -28,10 +28,6 @@ namespace webrtc { namespace { -bool UseUnboundedEchoSpectrum() { - return field_trial::IsEnabled("WebRTC-Aec3UseUnboundedEchoSpectrum"); -} - 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. @@ -348,7 +344,8 @@ SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, normal_params_(config_.suppressor.last_lf_band, config_.suppressor.first_hf_band, config_.suppressor.normal_tuning), - use_unbounded_echo_spectrum_(UseUnboundedEchoSpectrum()) { + 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) { @@ -382,7 +379,7 @@ void SuppressionGain::GetGain( RTC_DCHECK(high_bands_gain); RTC_DCHECK(low_band_gain); - // Choose residual echo spectrum for the dominant nearend detector. + // Choose residual echo spectrum for dominant nearend detection. const auto echo = use_unbounded_echo_spectrum_ ? residual_echo_spectrum_unbounded : residual_echo_spectrum; diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h index 7c4a1c9f7d..c8e13f7cf4 100644 --- a/modules/audio_processing/aec3/suppression_gain.h +++ b/modules/audio_processing/aec3/suppression_gain.h @@ -25,7 +25,6 @@ #include "modules/audio_processing/aec3/nearend_detector.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -36,6 +35,10 @@ class SuppressionGain { int sample_rate_hz, size_t num_capture_channels); ~SuppressionGain(); + + SuppressionGain(const SuppressionGain&) = delete; + SuppressionGain& operator=(const SuppressionGain&) = delete; + void GetGain( rtc::ArrayView> nearend_spectrum, @@ -134,8 +137,6 @@ class SuppressionGain { // echo spectrum. const bool use_unbounded_echo_spectrum_; std::unique_ptr dominant_nearend_detector_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionGain); }; } // namespace webrtc diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc index db61b36c29..160583e7c3 100644 --- a/modules/audio_processing/aec_dump/aec_dump_impl.cc +++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc @@ -155,7 +155,7 @@ void AecDumpImpl::WriteRenderStreamMessage( audioproc::ReverseStream* msg = event->mutable_reverse_stream(); - for (size_t i = 0; i < src.num_channels(); ++i) { + for (int i = 0; i < src.num_channels(); ++i) { const auto& channel_view = src.channel(i); msg->add_channel(channel_view.begin(), sizeof(float) * channel_view.size()); } @@ -230,7 +230,7 @@ void AecDumpImpl::WriteRuntimeSetting( break; } case AudioProcessing::RuntimeSetting::Type::kNotSpecified: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } worker_queue_->PostTask(std::move(task)); diff --git a/modules/audio_processing/aec_dump/aec_dump_integration_test.cc b/modules/audio_processing/aec_dump/aec_dump_integration_test.cc index 6d5b9879b9..503135d87f 100644 --- a/modules/audio_processing/aec_dump/aec_dump_integration_test.cc +++ b/modules/audio_processing/aec_dump/aec_dump_integration_test.cc @@ -24,9 +24,8 @@ using ::testing::StrictMock; namespace { rtc::scoped_refptr CreateAudioProcessing() { - webrtc::Config config; rtc::scoped_refptr apm( - webrtc::AudioProcessingBuilderForTesting().Create(config)); + webrtc::AudioProcessingBuilderForTesting().Create()); RTC_DCHECK(apm); return apm; } @@ -56,8 +55,7 @@ TEST(AecDumpIntegration, constexpr int kNumSamplesPerChannel = kNumSampleRateHz / 100; std::array frame; frame.fill(0.f); - webrtc::StreamConfig stream_config(kNumSampleRateHz, kNumChannels, - /*has_keyboard=*/false); + webrtc::StreamConfig stream_config(kNumSampleRateHz, kNumChannels); EXPECT_CALL(*mock_aec_dump.get(), WriteRenderStreamMessage(_, _, _)) .Times(Exactly(1)); @@ -76,8 +74,7 @@ TEST(AecDumpIntegration, CaptureStreamShouldBeLoggedOnceEveryProcessStream) { std::array frame; frame.fill(0.f); - webrtc::StreamConfig stream_config(kNumSampleRateHz, kNumChannels, - /*has_keyboard=*/false); + webrtc::StreamConfig stream_config(kNumSampleRateHz, kNumChannels); EXPECT_CALL(*mock_aec_dump.get(), AddCaptureStreamInput(_, _, _)) .Times(AtLeast(1)); diff --git a/modules/audio_processing/aec_dump/capture_stream_info.cc b/modules/audio_processing/aec_dump/capture_stream_info.cc index 907cd97793..efdda2570f 100644 --- a/modules/audio_processing/aec_dump/capture_stream_info.cc +++ b/modules/audio_processing/aec_dump/capture_stream_info.cc @@ -23,7 +23,7 @@ void CaptureStreamInfo::AddInput(const AudioFrameView& src) { RTC_DCHECK(task_); auto* stream = task_->GetEvent()->mutable_stream(); - for (size_t i = 0; i < src.num_channels(); ++i) { + for (int i = 0; i < src.num_channels(); ++i) { const auto& channel_view = src.channel(i); stream->add_input_channel(channel_view.begin(), sizeof(float) * channel_view.size()); @@ -34,7 +34,7 @@ void CaptureStreamInfo::AddOutput(const AudioFrameView& src) { RTC_DCHECK(task_); auto* stream = task_->GetEvent()->mutable_stream(); - for (size_t i = 0; i < src.num_channels(); ++i) { + for (int i = 0; i < src.num_channels(); ++i) { const auto& channel_view = src.channel(i); stream->add_output_channel(channel_view.begin(), sizeof(float) * channel_view.size()); diff --git a/modules/audio_processing/aec_dump/write_to_file_task.cc b/modules/audio_processing/aec_dump/write_to_file_task.cc index 4839a0927c..cd083d295b 100644 --- a/modules/audio_processing/aec_dump/write_to_file_task.cc +++ b/modules/audio_processing/aec_dump/write_to_file_task.cc @@ -55,10 +55,10 @@ bool WriteToFileTask::Run() { // Write message preceded by its size. if (!debug_file_->Write(&event_byte_size, sizeof(int32_t))) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (!debug_file_->Write(event_string.data(), event_string.length())) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return true; // Delete task from queue at once. } diff --git a/modules/audio_processing/aecm/aecm_core.h b/modules/audio_processing/aecm/aecm_core.h index d6d0d8dafc..3de49315c4 100644 --- a/modules/audio_processing/aecm/aecm_core.h +++ b/modules/audio_processing/aecm/aecm_core.h @@ -248,7 +248,7 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, // void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, const int16_t* const farend, - const int farLen); + int farLen); //////////////////////////////////////////////////////////////////////////////// // WebRtcAecm_FetchFarFrame() @@ -263,8 +263,8 @@ void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, // void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, int16_t* const farend, - const int farLen, - const int knownDelay); + int farLen, + int knownDelay); // All the functions below are intended to be private @@ -339,8 +339,8 @@ int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm); // void WebRtcAecm_CalcEnergies(AecmCore* aecm, const uint16_t* far_spectrum, - const int16_t far_q, - const uint32_t nearEner, + int16_t far_q, + uint32_t nearEner, int32_t* echoEst); /////////////////////////////////////////////////////////////////////////////// @@ -374,9 +374,9 @@ int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm); // void WebRtcAecm_UpdateChannel(AecmCore* aecm, const uint16_t* far_spectrum, - const int16_t far_q, + int16_t far_q, const uint16_t* const dfa, - const int16_t mu, + int16_t mu, int32_t* echoEst); extern const int16_t WebRtcAecm_kCosTable[]; diff --git a/modules/audio_processing/agc/BUILD.gn b/modules/audio_processing/agc/BUILD.gn index 4bb8c5494b..eef1b77560 100644 --- a/modules/audio_processing/agc/BUILD.gn +++ b/modules/audio_processing/agc/BUILD.gn @@ -27,6 +27,7 @@ rtc_library("agc") { "..:apm_logging", "..:audio_buffer", "..:audio_frame_view", + "../../../api:array_view", "../../../common_audio", "../../../common_audio:common_audio_c", "../../../rtc_base:checks", @@ -41,6 +42,20 @@ rtc_library("agc") { absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } +rtc_library("analog_gain_stats_reporter") { + sources = [ + "analog_gain_stats_reporter.cc", + "analog_gain_stats_reporter.h", + ] + deps = [ + "../../../rtc_base:gtest_prod", + "../../../rtc_base:logging", + "../../../rtc_base:safe_minmax", + "../../../system_wrappers:metrics", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + rtc_library("clipping_predictor") { sources = [ "clipping_predictor.cc", @@ -94,6 +109,7 @@ rtc_library("level_estimation") { "utility.h", ] deps = [ + "../../../api:array_view", "../../../rtc_base:checks", "../vad", ] @@ -142,6 +158,7 @@ if (rtc_include_tests) { testonly = true sources = [ "agc_manager_direct_unittest.cc", + "analog_gain_stats_reporter_unittest.cc", "clipping_predictor_evaluator_unittest.cc", "clipping_predictor_level_buffer_unittest.cc", "clipping_predictor_unittest.cc", @@ -152,15 +169,19 @@ if (rtc_include_tests) { deps = [ ":agc", + ":analog_gain_stats_reporter", ":clipping_predictor", ":clipping_predictor_evaluator", ":clipping_predictor_level_buffer", ":gain_control_interface", ":level_estimation", "..:mocks", + "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_conversions", + "../../../rtc_base:stringutils", + "../../../system_wrappers:metrics", "../../../test:field_trial", "../../../test:fileutils", "../../../test:test_support", diff --git a/modules/audio_processing/agc/agc.cc b/modules/audio_processing/agc/agc.cc index a89ae111ea..a018ff9f93 100644 --- a/modules/audio_processing/agc/agc.cc +++ b/modules/audio_processing/agc/agc.cc @@ -21,9 +21,11 @@ namespace webrtc { namespace { -const int kDefaultLevelDbfs = -18; -const int kNumAnalysisFrames = 100; -const double kActivityThreshold = 0.3; +constexpr int kDefaultLevelDbfs = -18; +constexpr int kNumAnalysisFrames = 100; +constexpr double kActivityThreshold = 0.3; +constexpr int kNum10msFramesInOneSecond = 100; +constexpr int kMaxSampleRateHz = 384000; } // namespace @@ -35,8 +37,10 @@ Agc::Agc() Agc::~Agc() = default; -void Agc::Process(const int16_t* audio, size_t length, int sample_rate_hz) { - vad_.ProcessChunk(audio, length, sample_rate_hz); +void Agc::Process(rtc::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(); @@ -48,7 +52,7 @@ void Agc::Process(const int16_t* audio, size_t length, int sample_rate_hz) { bool Agc::GetRmsErrorDb(int* error) { if (!error) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/modules/audio_processing/agc/agc.h b/modules/audio_processing/agc/agc.h index 2693d94880..da42808225 100644 --- a/modules/audio_processing/agc/agc.h +++ b/modules/audio_processing/agc/agc.h @@ -13,6 +13,7 @@ #include +#include "api/array_view.h" #include "modules/audio_processing/vad/voice_activity_detector.h" namespace webrtc { @@ -26,7 +27,7 @@ class Agc { // `audio` must be mono; in a multi-channel stream, provide the first (usually // left) channel. - virtual void Process(const int16_t* audio, size_t length, int sample_rate_hz); + virtual void Process(rtc::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 diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc index bda1cae327..fb54cf52f6 100644 --- a/modules/audio_processing/agc/agc_manager_direct.cc +++ b/modules/audio_processing/agc/agc_manager_direct.cc @@ -13,6 +13,7 @@ #include #include +#include "api/array_view.h" #include "common_audio/include/audio_util.h" #include "modules/audio_processing/agc/gain_control.h" #include "modules/audio_processing/agc/gain_map_internal.h" @@ -51,7 +52,7 @@ constexpr int kSurplusCompressionGain = 6; // History size for the clipping predictor evaluator (unit: number of 10 ms // frames). -constexpr int kClippingPredictorEvaluatorHistorySize = 32; +constexpr int kClippingPredictorEvaluatorHistorySize = 500; using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: AnalogGainController::ClippingPredictor; @@ -62,28 +63,27 @@ bool UseMaxAnalogChannelLevel() { return field_trial::IsEnabled("WebRTC-UseMaxAnalogAgcChannelLevel"); } -// Returns kMinMicLevel if no field trial exists or if it has been disabled. -// Returns a value between 0 and 255 depending on the field-trial string. -// Example: 'WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-80' => returns 80. -int GetMinMicLevel() { - RTC_LOG(LS_INFO) << "[agc] GetMinMicLevel"; +// If the "WebRTC-Audio-AgcMinMicLevelExperiment" 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-AgcMinMicLevelExperiment/Enabled-80' => returns 80. +absl::optional GetMinMicLevelOverride() { constexpr char kMinMicLevelFieldTrial[] = "WebRTC-Audio-AgcMinMicLevelExperiment"; if (!webrtc::field_trial::IsEnabled(kMinMicLevelFieldTrial)) { - RTC_LOG(LS_INFO) << "[agc] Using default min mic level: " << kMinMicLevel; - return kMinMicLevel; + return absl::nullopt; } const auto field_trial_string = webrtc::field_trial::FindFullName(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) { - RTC_LOG(LS_INFO) << "[agc] Experimental min mic level: " << min_mic_level; return min_mic_level; } else { RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for " << kMinMicLevelFieldTrial << ", ignored."; - return kMinMicLevel; + return absl::nullopt; } } @@ -124,7 +124,7 @@ float ComputeClippedRatio(const float* const* audio, 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.f || audio[ch][i] <= -32768.f) { + if (audio[ch][i] >= 32767.0f || audio[ch][i] <= -32768.0f) { ++num_clipped_in_ch; } } @@ -142,12 +142,28 @@ void LogClippingPredictorMetrics(const ClippingPredictorEvaluator& evaluator) { << metrics->f1_score; RTC_DCHECK_GE(metrics->f1_score, 0.0f); RTC_DCHECK_LE(metrics->f1_score, 1.0f); + RTC_DCHECK_GE(metrics->precision, 0.0f); + RTC_DCHECK_LE(metrics->precision, 1.0f); + RTC_DCHECK_GE(metrics->recall, 0.0f); + RTC_DCHECK_LE(metrics->recall, 1.0f); RTC_HISTOGRAM_COUNTS_LINEAR( /*name=*/"WebRTC.Audio.Agc.ClippingPredictor.F1Score", /*sample=*/std::round(metrics->f1_score * 100.0f), /*min=*/0, /*max=*/100, /*bucket_count=*/50); + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.Agc.ClippingPredictor.Precision", + /*sample=*/std::round(metrics->precision * 100.0f), + /*min=*/0, + /*max=*/100, + /*bucket_count=*/50); + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.Agc.ClippingPredictor.Recall", + /*sample=*/std::round(metrics->recall * 100.0f), + /*min=*/0, + /*max=*/100, + /*bucket_count=*/50); } } @@ -188,9 +204,7 @@ void MonoAgc::Initialize() { check_volume_on_next_process_ = true; } -void MonoAgc::Process(const int16_t* audio, - size_t samples_per_channel, - int sample_rate_hz) { +void MonoAgc::Process(rtc::ArrayView audio) { new_compression_to_set_ = absl::nullopt; if (check_volume_on_next_process_) { @@ -200,7 +214,7 @@ void MonoAgc::Process(const int16_t* audio, CheckVolumeAndReset(); } - agc_->Process(audio, samples_per_channel, sample_rate_hz); + agc_->Process(audio); UpdateGain(); if (!disable_digital_adaptive_) { @@ -431,7 +445,6 @@ AgcManagerDirect::AgcManagerDirect( Agc* agc, int startup_min_level, int clipped_level_min, - int sample_rate_hz, int clipped_level_step, float clipped_ratio_threshold, int clipped_wait_frames, @@ -440,7 +453,6 @@ AgcManagerDirect::AgcManagerDirect( startup_min_level, clipped_level_min, /*disable_digital_adaptive*/ false, - sample_rate_hz, clipped_level_step, clipped_ratio_threshold, clipped_wait_frames, @@ -455,15 +467,14 @@ AgcManagerDirect::AgcManagerDirect( int startup_min_level, int clipped_level_min, bool disable_digital_adaptive, - int sample_rate_hz, int clipped_level_step, float clipped_ratio_threshold, int clipped_wait_frames, const ClippingPredictorConfig& clipping_config) - : data_dumper_( + : min_mic_level_override_(GetMinMicLevelOverride()), + data_dumper_( new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))), use_min_channel_level_(!UseMaxAnalogChannelLevel()), - sample_rate_hz_(sample_rate_hz), num_capture_channels_(num_capture_channels), disable_digital_adaptive_(disable_digital_adaptive), frames_since_clipped_(clipped_wait_frames), @@ -481,7 +492,11 @@ AgcManagerDirect::AgcManagerDirect( clipping_predictor_log_counter_(0), clipping_rate_log_(0.0f), clipping_rate_log_counter_(0) { - const int min_mic_level = GetMinMicLevel(); + 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; @@ -594,9 +609,14 @@ void AgcManagerDirect::AnalyzePreProcess(const float* const* audio, } } // Clipping prediction evaluation. + // `clipping_detected` is not used to evaluate the clipping predictor + // since for this purpose a single clipping sample counts as clipping. + const bool one_or_more_clipped_samples = + clipped_ratio >= (1.0f / samples_per_channel); absl::optional prediction_interval = - clipping_predictor_evaluator_.Observe(clipping_detected, - clipping_predicted); + clipping_predictor_evaluator_.Observe( + /*clipping_detected=*/one_or_more_clipped_samples, + clipping_predicted); if (prediction_interval.has_value()) { RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.Agc.ClippingPredictor.PredictionInterval", @@ -636,27 +656,20 @@ void AgcManagerDirect::AnalyzePreProcess(const float* const* audio, } void AgcManagerDirect::Process(const AudioBuffer* audio) { + RTC_DCHECK(audio); AggregateChannelLevels(); if (!capture_output_used_) { return; } + const size_t num_frames_per_band = audio->num_frames_per_band(); for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - int16_t* audio_use = nullptr; std::array audio_data; - int num_frames_per_band; - if (audio) { - FloatS16ToS16(audio->split_bands_const_f(ch)[0], - audio->num_frames_per_band(), audio_data.data()); - audio_use = audio_data.data(); - num_frames_per_band = audio->num_frames_per_band(); - } else { - // Only used for testing. - // TODO(peah): Change unittests to only allow on non-null audio input. - num_frames_per_band = 320; - } - channel_agcs_[ch]->Process(audio_use, num_frames_per_band, sample_rate_hz_); + int16_t* audio_use = audio_data.data(); + FloatS16ToS16(audio->split_bands_const_f(ch)[0], num_frames_per_band, + audio_use); + channel_agcs_[ch]->Process({audio_use, num_frames_per_band}); new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression(); } @@ -711,6 +724,10 @@ void AgcManagerDirect::AggregateChannelLevels() { } } } + if (min_mic_level_override_.has_value()) { + stream_analog_level_ = + std::max(stream_analog_level_, *min_mic_level_override_); + } } } // namespace webrtc diff --git a/modules/audio_processing/agc/agc_manager_direct.h b/modules/audio_processing/agc/agc_manager_direct.h index a452ee1c43..ce67a971b4 100644 --- a/modules/audio_processing/agc/agc_manager_direct.h +++ b/modules/audio_processing/agc/agc_manager_direct.h @@ -14,6 +14,7 @@ #include #include "absl/types/optional.h" +#include "api/array_view.h" #include "modules/audio_processing/agc/agc.h" #include "modules/audio_processing/agc/clipping_predictor.h" #include "modules/audio_processing/agc/clipping_predictor_evaluator.h" @@ -47,7 +48,6 @@ class AgcManagerDirect final { int startup_min_level, int clipped_level_min, bool disable_digital_adaptive, - int sample_rate_hz, int clipped_level_step, float clipped_ratio_threshold, int clipped_wait_frames, @@ -72,7 +72,6 @@ class AgcManagerDirect final { int stream_analog_level() const { return stream_analog_level_; } void set_stream_analog_level(int level); int num_channels() const { return num_capture_channels_; } - int sample_rate_hz() const { return sample_rate_hz_; } // If available, returns a new compression gain for the digital gain control. absl::optional GetDigitalComressionGain(); @@ -117,7 +116,6 @@ class AgcManagerDirect final { Agc* agc, int startup_min_level, int clipped_level_min, - int sample_rate_hz, int clipped_level_step, float clipped_ratio_threshold, int clipped_wait_frames, @@ -128,10 +126,10 @@ class AgcManagerDirect final { void AggregateChannelLevels(); + const absl::optional min_mic_level_override_; std::unique_ptr data_dumper_; static int instance_counter_; const bool use_min_channel_level_; - const int sample_rate_hz_; const int num_capture_channels_; const bool disable_digital_adaptive_; @@ -171,9 +169,7 @@ class MonoAgc { void HandleClipping(int clipped_level_step); - void Process(const int16_t* audio, - size_t samples_per_channel, - int sample_rate_hz); + void Process(rtc::ArrayView audio); void set_stream_analog_level(int level) { stream_analog_level_ = level; } int stream_analog_level() const { return stream_analog_level_; } diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc index 14f9cd7314..5c9b383f78 100644 --- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc +++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc @@ -10,9 +10,12 @@ #include "modules/audio_processing/agc/agc_manager_direct.h" +#include + #include "modules/audio_processing/agc/gain_control.h" #include "modules/audio_processing/agc/mock_agc.h" #include "modules/audio_processing/include/mock_audio_processing.h" +#include "rtc_base/strings/string_builder.h" #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -37,6 +40,9 @@ constexpr int kClippedLevelStep = 15; constexpr float kClippedRatioThreshold = 0.1f; constexpr int kClippedWaitFrames = 300; +constexpr AudioProcessing::Config::GainController1::AnalogGainController + kDefaultAnalogConfig{}; + using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: AnalogGainController::ClippingPredictor; @@ -69,8 +75,9 @@ std::unique_ptr CreateAgcManagerDirect( int clipped_wait_frames) { return std::make_unique( /*num_capture_channels=*/1, startup_min_level, kClippedMin, - /*disable_digital_adaptive=*/true, kSampleRateHz, clipped_level_step, - clipped_ratio_threshold, clipped_wait_frames, ClippingPredictorConfig()); + /*disable_digital_adaptive=*/true, clipped_level_step, + clipped_ratio_threshold, clipped_wait_frames, + kDefaultAnalogConfig.clipping_predictor); } std::unique_ptr CreateAgcManagerDirect( @@ -81,30 +88,39 @@ std::unique_ptr CreateAgcManagerDirect( const ClippingPredictorConfig& clipping_cfg) { return std::make_unique( /*num_capture_channels=*/1, startup_min_level, kClippedMin, - /*disable_digital_adaptive=*/true, kSampleRateHz, clipped_level_step, + /*disable_digital_adaptive=*/true, clipped_level_step, clipped_ratio_threshold, clipped_wait_frames, clipping_cfg); } +// Calls `AnalyzePreProcess()` on `manager` `num_calls` times. `peak_ratio` is a +// value in [0, 1] which determines the amplitude of the samples (1 maps to full +// scale). The first half of the calls is made on frames which are half filled +// with zeros in order to simulate a signal with different crest factors. void CallPreProcessAudioBuffer(int num_calls, float peak_ratio, AgcManagerDirect& manager) { - RTC_DCHECK_GE(1.f, peak_ratio); + RTC_DCHECK_LE(peak_ratio, 1.0f); AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, 1); const int num_channels = audio_buffer.num_channels(); const int num_frames = audio_buffer.num_frames(); + + // Make half of the calls with half zeroed frames. for (int ch = 0; ch < num_channels; ++ch) { + // 50% of the samples in one frame are zero. for (int i = 0; i < num_frames; i += 2) { - audio_buffer.channels()[ch][i] = peak_ratio * 32767.f; + audio_buffer.channels()[ch][i] = peak_ratio * 32767.0f; audio_buffer.channels()[ch][i + 1] = 0.0f; } } for (int n = 0; n < num_calls / 2; ++n) { manager.AnalyzePreProcess(&audio_buffer); } + + // Make the remaining half of the calls with frames whose samples are all set. for (int ch = 0; ch < num_channels; ++ch) { for (int i = 0; i < num_frames; ++i) { - audio_buffer.channels()[ch][i] = peak_ratio * 32767.f; + audio_buffer.channels()[ch][i] = peak_ratio * 32767.0f; } } for (int n = 0; n < num_calls - num_calls / 2; ++n) { @@ -112,6 +128,51 @@ void CallPreProcessAudioBuffer(int num_calls, } } +std::string GetAgcMinMicLevelExperimentFieldTrial( + int enabled_value, + const std::string& suffix = "") { + RTC_DCHECK_GE(enabled_value, 0); + RTC_DCHECK_LE(enabled_value, 255); + char field_trial_buffer[64]; + rtc::SimpleStringBuilder builder(field_trial_buffer); + builder << "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-" << enabled_value + << suffix << "/"; + return builder.str(); +} + +// (Over)writes `samples_value` for the samples in `audio_buffer`. +// When `clipped_ratio`, a value in [0, 1], is greater than 0, the corresponding +// fraction of the frame is set to a full scale value to simulate clipping. +void WriteAudioBufferSamples(float samples_value, + float clipped_ratio, + AudioBuffer& audio_buffer) { + RTC_DCHECK_GE(samples_value, std::numeric_limits::min()); + RTC_DCHECK_LE(samples_value, std::numeric_limits::max()); + RTC_DCHECK_GE(clipped_ratio, 0.0f); + RTC_DCHECK_LE(clipped_ratio, 1.0f); + int num_channels = audio_buffer.num_channels(); + int num_samples = audio_buffer.num_frames(); + int num_clipping_samples = clipped_ratio * num_samples; + for (int ch = 0; ch < num_channels; ++ch) { + int i = 0; + for (; i < num_clipping_samples; ++i) { + audio_buffer.channels()[ch][i] = 32767.0f; + } + for (; i < num_samples; ++i) { + audio_buffer.channels()[ch][i] = samples_value; + } + } +} + +void CallPreProcessAndProcess(int num_calls, + const AudioBuffer& audio_buffer, + AgcManagerDirect& manager) { + for (int n = 0; n < num_calls; ++n) { + manager.AnalyzePreProcess(&audio_buffer); + manager.Process(&audio_buffer); + } +} + } // namespace class AgcManagerDirectTest : public ::testing::Test { @@ -121,11 +182,16 @@ class AgcManagerDirectTest : public ::testing::Test { manager_(agc_, kInitialVolume, kClippedMin, - kSampleRateHz, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, ClippingPredictorConfig()), + audio_buffer(kSampleRateHz, + kNumChannels, + kSampleRateHz, + kNumChannels, + kSampleRateHz, + kNumChannels), audio(kNumChannels), audio_data(kNumChannels * kSamplesPerChannel, 0.f) { ExpectInitialize(); @@ -134,6 +200,8 @@ class AgcManagerDirectTest : public ::testing::Test { for (size_t ch = 0; ch < kNumChannels; ++ch) { audio[ch] = &audio_data[ch * kSamplesPerChannel]; } + WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipped_ratio=*/0.0f, + audio_buffer); } void FirstProcess() { @@ -161,8 +229,8 @@ class AgcManagerDirectTest : public ::testing::Test { void CallProcess(int num_calls) { for (int i = 0; i < num_calls; ++i) { - EXPECT_CALL(*agc_, Process(_, _, _)).WillOnce(Return()); - manager_.Process(nullptr); + EXPECT_CALL(*agc_, Process(_)).WillOnce(Return()); + manager_.Process(&audio_buffer); absl::optional new_digital_gain = manager_.GetDigitalComressionGain(); if (new_digital_gain) { @@ -172,12 +240,13 @@ class AgcManagerDirectTest : public ::testing::Test { } void CallPreProc(int num_calls, float clipped_ratio) { - RTC_DCHECK_GE(1.f, clipped_ratio); + RTC_DCHECK_GE(clipped_ratio, 0.0f); + RTC_DCHECK_LE(clipped_ratio, 1.0f); const int num_clipped = kSamplesPerChannel * clipped_ratio; std::fill(audio_data.begin(), audio_data.end(), 0.f); for (size_t ch = 0; ch < kNumChannels; ++ch) { for (int k = 0; k < num_clipped; ++k) { - audio[ch][k] = 32767.f; + audio[ch][k] = 32767.0f; } } for (int i = 0; i < num_calls; ++i) { @@ -209,6 +278,7 @@ class AgcManagerDirectTest : public ::testing::Test { MockAgc* agc_; MockGainControl gctrl_; AgcManagerDirect manager_; + AudioBuffer audio_buffer; std::vector audio; std::vector audio_data; }; @@ -452,7 +522,7 @@ TEST_F(AgcManagerDirectTest, CompressorReachesMinimum) { TEST_F(AgcManagerDirectTest, NoActionWhileMuted) { manager_.HandleCaptureOutputUsedChange(false); - manager_.Process(nullptr); + manager_.Process(&audio_buffer); absl::optional new_digital_gain = manager_.GetDigitalComressionGain(); if (new_digital_gain) { gctrl_.set_compression_gain_db(*new_digital_gain); @@ -815,13 +885,16 @@ TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperiment) { } TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentDisabled) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled/"); - std::unique_ptr manager = - CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, - kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + for (const std::string& field_trial_suffix : {"", "_20220210"}) { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled" + field_trial_suffix + + "/"); + std::unique_ptr manager = + CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, + kClippedRatioThreshold, kClippedWaitFrames); + EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); + EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + } } // Checks that a field-trial parameter outside of the valid range [0,255] is @@ -852,27 +925,124 @@ TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentOutOfRangeBelow) { // start volume is larger than the min level and should therefore not be // changed. TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentEnabled50) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/"); - std::unique_ptr manager = - CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, - kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + constexpr int kMinMicLevelOverride = 50; + for (const std::string& field_trial_suffix : {"", "_20220210"}) { + SCOPED_TRACE(field_trial_suffix); + test::ScopedFieldTrials field_trial(GetAgcMinMicLevelExperimentFieldTrial( + kMinMicLevelOverride, field_trial_suffix)); + std::unique_ptr manager = + CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, + kClippedRatioThreshold, kClippedWaitFrames); + EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevelOverride); + EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + } } -// Uses experiment to reduce the default minimum microphone level, start at a -// lower level and ensure that the startup level is increased to the min level -// set by the experiment. +// Checks that, when the "WebRTC-Audio-AgcMinMicLevelExperiment" field trial is +// specified with a valid value, the mic level never gets lowered beyond the +// override value in the presence of clipping. TEST(AgcManagerDirectStandaloneTest, - AgcMinMicLevelExperimentEnabledAboveStartupLevel) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/"); - std::unique_ptr manager = - CreateAgcManagerDirect(/*startup_min_level=*/30, kClippedLevelStep, - kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), 50); + AgcMinMicLevelExperimentCheckMinLevelWithClipping) { + constexpr int kMinMicLevelOverride = 250; + + // Create and initialize two AGCs by specifying and leaving unspecified the + // relevant field trial. + const auto factory = []() { + std::unique_ptr manager = + CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, + kClippedRatioThreshold, kClippedWaitFrames); + manager->Initialize(); + manager->set_stream_analog_level(kInitialVolume); + return manager; + }; + std::unique_ptr manager = factory(); + std::unique_ptr manager_with_override; + { + test::ScopedFieldTrials field_trial( + GetAgcMinMicLevelExperimentFieldTrial(kMinMicLevelOverride)); + manager_with_override = factory(); + } + + // Create a test input signal which containts 80% of clipped samples. + AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, + 1); + WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, + audio_buffer); + + // Simulate 4 seconds of clipping; it is expected to trigger a downward + // adjustment of the analog gain. + CallPreProcessAndProcess(/*num_calls=*/400, audio_buffer, *manager); + CallPreProcessAndProcess(/*num_calls=*/400, audio_buffer, + *manager_with_override); + + // Make sure that an adaptation occurred. + ASSERT_GT(manager->stream_analog_level(), 0); + + // Check that the test signal triggers a larger downward adaptation for + // `manager`, which is allowed to reach a lower gain. + EXPECT_GT(manager_with_override->stream_analog_level(), + manager->stream_analog_level()); + // Check that the gain selected by `manager_with_override` equals the minimum + // value overridden via field trial. + EXPECT_EQ(manager_with_override->stream_analog_level(), kMinMicLevelOverride); +} + +// Checks that, when the "WebRTC-Audio-AgcMinMicLevelExperiment" field trial is +// specified with a value lower than the `clipped_level_min`, the behavior of +// the analog gain controller is the same as that obtained when the field trial +// is not specified. +TEST(AgcManagerDirectStandaloneTest, + AgcMinMicLevelExperimentCompareMicLevelWithClipping) { + // Create and initialize two AGCs by specifying and leaving unspecified the + // relevant field trial. + const auto factory = []() { + // Use a large clipped level step to more quickly decrease the analog gain + // with clipping. + auto controller = std::make_unique( + /*num_capture_channels=*/1, kInitialVolume, + kDefaultAnalogConfig.clipped_level_min, + /*disable_digital_adaptive=*/true, /*clipped_level_step=*/64, + kClippedRatioThreshold, kClippedWaitFrames, + kDefaultAnalogConfig.clipping_predictor); + controller->Initialize(); + controller->set_stream_analog_level(kInitialVolume); + return controller; + }; + std::unique_ptr manager = factory(); + std::unique_ptr manager_with_override; + { + constexpr int kMinMicLevelOverride = 20; + static_assert( + kDefaultAnalogConfig.clipped_level_min >= kMinMicLevelOverride, + "Use a lower override value."); + test::ScopedFieldTrials field_trial( + GetAgcMinMicLevelExperimentFieldTrial(kMinMicLevelOverride)); + manager_with_override = factory(); + } + + // Create a test input signal which containts 80% of clipped samples. + AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, + 1); + WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, + audio_buffer); + + // Simulate 4 seconds of clipping; it is expected to trigger a downward + // adjustment of the analog gain. + CallPreProcessAndProcess(/*num_calls=*/400, audio_buffer, *manager); + CallPreProcessAndProcess(/*num_calls=*/400, audio_buffer, + *manager_with_override); + + // Make sure that an adaptation occurred. + ASSERT_GT(manager->stream_analog_level(), 0); + + // Check that the selected analog gain is the same for both controllers and + // that it equals the minimum level reached when clipping is handled. That is + // expected because the minimum microphone level override is less than the + // minimum level used when clipping is detected. + EXPECT_EQ(manager->stream_analog_level(), + manager_with_override->stream_analog_level()); + EXPECT_EQ(manager_with_override->stream_analog_level(), + kDefaultAnalogConfig.clipped_level_min); } // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`. @@ -931,17 +1101,20 @@ TEST(AgcManagerDirectStandaloneTest, TEST(AgcManagerDirectStandaloneTest, DisableClippingPredictorDoesNotLowerVolume) { + AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, + kNumChannels, kSampleRateHz, kNumChannels); + // TODO(bugs.webrtc.org/12874): Use designated initializers one fixed. constexpr ClippingPredictorConfig kConfig{/*enabled=*/false}; AgcManagerDirect manager(new ::testing::NiceMock(), kInitialVolume, - kClippedMin, kSampleRateHz, kClippedLevelStep, + kClippedMin, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, kConfig); manager.Initialize(); manager.set_stream_analog_level(/*level=*/255); EXPECT_FALSE(manager.clipping_predictor_enabled()); EXPECT_FALSE(manager.use_clipping_predictor_step()); EXPECT_EQ(manager.stream_analog_level(), 255); - manager.Process(nullptr); + manager.Process(&audio_buffer); CallPreProcessAudioBuffer(/*num_calls=*/10, /*peak_ratio=*/0.99f, manager); EXPECT_EQ(manager.stream_analog_level(), 255); CallPreProcessAudioBuffer(/*num_calls=*/300, /*peak_ratio=*/0.99f, manager); @@ -952,20 +1125,23 @@ TEST(AgcManagerDirectStandaloneTest, TEST(AgcManagerDirectStandaloneTest, UsedClippingPredictionsProduceLowerAnalogLevels) { + AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, + kNumChannels, kSampleRateHz, kNumChannels); + // TODO(bugs.webrtc.org/12874): Use designated initializers once fixed. ClippingPredictorConfig config_with_prediction; config_with_prediction.enabled = true; config_with_prediction.use_predicted_step = true; AgcManagerDirect manager_with_prediction( new ::testing::NiceMock(), kInitialVolume, kClippedMin, - kSampleRateHz, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config_with_prediction); + kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, + config_with_prediction); ClippingPredictorConfig config_without_prediction; config_without_prediction.enabled = false; AgcManagerDirect manager_without_prediction( new ::testing::NiceMock(), kInitialVolume, kClippedMin, - kSampleRateHz, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config_without_prediction); + kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, + config_without_prediction); manager_with_prediction.Initialize(); manager_without_prediction.Initialize(); constexpr int kInitialLevel = 255; @@ -974,8 +1150,8 @@ TEST(AgcManagerDirectStandaloneTest, constexpr float kZeroPeakRatio = 0.0f; manager_with_prediction.set_stream_analog_level(kInitialLevel); manager_without_prediction.set_stream_analog_level(kInitialLevel); - manager_with_prediction.Process(nullptr); - manager_without_prediction.Process(nullptr); + manager_with_prediction.Process(&audio_buffer); + manager_without_prediction.Process(&audio_buffer); EXPECT_TRUE(manager_with_prediction.clipping_predictor_enabled()); EXPECT_FALSE(manager_without_prediction.clipping_predictor_enabled()); EXPECT_TRUE(manager_with_prediction.use_clipping_predictor_step()); @@ -1044,20 +1220,23 @@ TEST(AgcManagerDirectStandaloneTest, TEST(AgcManagerDirectStandaloneTest, UnusedClippingPredictionsProduceEqualAnalogLevels) { + AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, + kNumChannels, kSampleRateHz, kNumChannels); + // TODO(bugs.webrtc.org/12874): Use designated initializers once fixed. ClippingPredictorConfig config_with_prediction; config_with_prediction.enabled = true; config_with_prediction.use_predicted_step = false; AgcManagerDirect manager_with_prediction( new ::testing::NiceMock(), kInitialVolume, kClippedMin, - kSampleRateHz, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config_with_prediction); + kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, + config_with_prediction); ClippingPredictorConfig config_without_prediction; config_without_prediction.enabled = false; AgcManagerDirect manager_without_prediction( new ::testing::NiceMock(), kInitialVolume, kClippedMin, - kSampleRateHz, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config_without_prediction); + kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames, + config_without_prediction); constexpr int kInitialLevel = 255; constexpr float kClippingPeakRatio = 1.0f; constexpr float kCloseToClippingPeakRatio = 0.99f; @@ -1066,8 +1245,8 @@ TEST(AgcManagerDirectStandaloneTest, manager_without_prediction.Initialize(); manager_with_prediction.set_stream_analog_level(kInitialLevel); manager_without_prediction.set_stream_analog_level(kInitialLevel); - manager_with_prediction.Process(nullptr); - manager_without_prediction.Process(nullptr); + manager_with_prediction.Process(&audio_buffer); + manager_without_prediction.Process(&audio_buffer); EXPECT_TRUE(manager_with_prediction.clipping_predictor_enabled()); EXPECT_FALSE(manager_without_prediction.clipping_predictor_enabled()); EXPECT_FALSE(manager_with_prediction.use_clipping_predictor_step()); diff --git a/modules/audio_processing/agc/analog_gain_stats_reporter.cc b/modules/audio_processing/agc/analog_gain_stats_reporter.cc new file mode 100644 index 0000000000..0d8753a7c8 --- /dev/null +++ b/modules/audio_processing/agc/analog_gain_stats_reporter.cc @@ -0,0 +1,130 @@ +/* + * 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/agc/analog_gain_stats_reporter.h" + +#include + +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +constexpr int kFramesIn60Seconds = 6000; +constexpr int kMinGain = 0; +constexpr int kMaxGain = 255; +constexpr int kMaxUpdate = kMaxGain - kMinGain; + +float 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.0f; + } + return std::round(static_cast(sum_updates) / + static_cast(num_updates)); +} +} // namespace + +AnalogGainStatsReporter::AnalogGainStatsReporter() = default; + +AnalogGainStatsReporter::~AnalogGainStatsReporter() = default; + +void AnalogGainStatsReporter::UpdateStatistics(int analog_mic_level) { + RTC_DCHECK_GE(analog_mic_level, kMinGain); + RTC_DCHECK_LE(analog_mic_level, kMaxGain); + if (previous_analog_mic_level_.has_value() && + analog_mic_level != previous_analog_mic_level_.value()) { + const int level_change = + analog_mic_level - previous_analog_mic_level_.value(); + if (level_change < 0) { + ++level_update_stats_.num_decreases; + level_update_stats_.sum_decreases -= level_change; + } else { + ++level_update_stats_.num_increases; + level_update_stats_.sum_increases += level_change; + } + } + // Periodically log analog gain change metrics. + if (++log_level_update_stats_counter_ >= kFramesIn60Seconds) { + LogLevelUpdateStats(); + level_update_stats_ = {}; + log_level_update_stats_counter_ = 0; + } + previous_analog_mic_level_ = analog_mic_level; +} + +void AnalogGainStatsReporter::LogLevelUpdateStats() const { + const float average_decrease = ComputeAverageUpdate( + level_update_stats_.sum_decreases, level_update_stats_.num_decreases); + const float average_increase = ComputeAverageUpdate( + level_update_stats_.sum_increases, level_update_stats_.num_increases); + const int num_updates = + level_update_stats_.num_decreases + level_update_stats_.num_increases; + const float average_update = ComputeAverageUpdate( + level_update_stats_.sum_decreases + level_update_stats_.sum_increases, + num_updates); + RTC_DLOG(LS_INFO) << "Analog gain update rate: " + << "num_updates=" << num_updates + << ", num_decreases=" << level_update_stats_.num_decreases + << ", num_increases=" << level_update_stats_.num_increases; + RTC_DLOG(LS_INFO) << "Analog gain update average: " + << "average_update=" << average_update + << ", average_decrease=" << average_decrease + << ", average_increase=" << average_increase; + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainDecreaseRate", + /*sample=*/level_update_stats_.num_decreases, + /*min=*/1, + /*max=*/kFramesIn60Seconds, + /*bucket_count=*/50); + if (level_update_stats_.num_decreases > 0) { + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainDecreaseAverage", + /*sample=*/average_decrease, + /*min=*/1, + /*max=*/kMaxUpdate, + /*bucket_count=*/50); + } + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainIncreaseRate", + /*sample=*/level_update_stats_.num_increases, + /*min=*/1, + /*max=*/kFramesIn60Seconds, + /*bucket_count=*/50); + if (level_update_stats_.num_increases > 0) { + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainIncreaseAverage", + /*sample=*/average_increase, + /*min=*/1, + /*max=*/kMaxUpdate, + /*bucket_count=*/50); + } + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainUpdateRate", + /*sample=*/num_updates, + /*min=*/1, + /*max=*/kFramesIn60Seconds, + /*bucket_count=*/50); + if (num_updates > 0) { + RTC_HISTOGRAM_COUNTS_LINEAR( + /*name=*/"WebRTC.Audio.ApmAnalogGainUpdateAverage", + /*sample=*/average_update, + /*min=*/1, + /*max=*/kMaxUpdate, + /*bucket_count=*/50); + } +} + +} // namespace webrtc diff --git a/modules/audio_processing/agc/analog_gain_stats_reporter.h b/modules/audio_processing/agc/analog_gain_stats_reporter.h new file mode 100644 index 0000000000..c9442e8a43 --- /dev/null +++ b/modules/audio_processing/agc/analog_gain_stats_reporter.h @@ -0,0 +1,67 @@ +/* + * 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_AGC_ANALOG_GAIN_STATS_REPORTER_H_ +#define MODULES_AUDIO_PROCESSING_AGC_ANALOG_GAIN_STATS_REPORTER_H_ + +#include "absl/types/optional.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +// Analog gain statistics calculator. Computes aggregate stats based on the +// framewise mic levels processed in `UpdateStatistics()`. Periodically logs the +// statistics into a histogram. +class AnalogGainStatsReporter { + public: + AnalogGainStatsReporter(); + AnalogGainStatsReporter(const AnalogGainStatsReporter&) = delete; + AnalogGainStatsReporter operator=(const AnalogGainStatsReporter&) = delete; + ~AnalogGainStatsReporter(); + + // Updates the stats based on the `analog_mic_level`. Periodically logs the + // stats into a histogram. + void UpdateStatistics(int analog_mic_level); + + private: + FRIEND_TEST_ALL_PREFIXES(AnalogGainStatsReporterTest, + CheckLevelUpdateStatsForEmptyStats); + FRIEND_TEST_ALL_PREFIXES(AnalogGainStatsReporterTest, + CheckLevelUpdateStatsAfterNoGainChange); + FRIEND_TEST_ALL_PREFIXES(AnalogGainStatsReporterTest, + CheckLevelUpdateStatsAfterGainIncrease); + FRIEND_TEST_ALL_PREFIXES(AnalogGainStatsReporterTest, + CheckLevelUpdateStatsAfterGainDecrease); + FRIEND_TEST_ALL_PREFIXES(AnalogGainStatsReporterTest, + CheckLevelUpdateStatsAfterReset); + + // Stores analog gain update stats to enable calculation of update rate and + // average update separately for gain increases and decreases. + struct LevelUpdateStats { + int num_decreases = 0; + int num_increases = 0; + int sum_decreases = 0; + int sum_increases = 0; + } level_update_stats_; + + // Returns a copy of the stored statistics. Use only for testing. + const LevelUpdateStats level_update_stats() const { + return level_update_stats_; + } + + // Computes aggregate stat and logs them into a histogram. + void LogLevelUpdateStats() const; + + int log_level_update_stats_counter_ = 0; + absl::optional previous_analog_mic_level_ = absl::nullopt; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_ANALOG_GAIN_STATS_REPORTER_H_ diff --git a/modules/audio_processing/agc/analog_gain_stats_reporter_unittest.cc b/modules/audio_processing/agc/analog_gain_stats_reporter_unittest.cc new file mode 100644 index 0000000000..cab52876f6 --- /dev/null +++ b/modules/audio_processing/agc/analog_gain_stats_reporter_unittest.cc @@ -0,0 +1,161 @@ +/* + * 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/agc/analog_gain_stats_reporter.h" + +#include "system_wrappers/include/metrics.h" +#include "test/gmock.h" + +namespace webrtc { +namespace { + +constexpr int kFramesIn60Seconds = 6000; + +TEST(AnalogGainStatsReporterTest, CheckLogLevelUpdateStatsEmpty) { + AnalogGainStatsReporter stats_reporter; + constexpr int kMicLevel = 10; + stats_reporter.UpdateStatistics(kMicLevel); + // Update almost until the periodic logging and reset. + for (int i = 0; i < kFramesIn60Seconds - 2; i += 2) { + stats_reporter.UpdateStatistics(kMicLevel + 2); + stats_reporter.UpdateStatistics(kMicLevel); + } + EXPECT_METRIC_THAT(metrics::Samples("WebRTC.Audio.ApmAnalogGainUpdateRate"), + ::testing::ElementsAre()); + EXPECT_METRIC_THAT(metrics::Samples("WebRTC.Audio.ApmAnalogGainDecreaseRate"), + ::testing::ElementsAre()); + EXPECT_METRIC_THAT(metrics::Samples("WebRTC.Audio.ApmAnalogGainIncreaseRate"), + ::testing::ElementsAre()); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainUpdateAverage"), + ::testing::ElementsAre()); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainDecreaseAverage"), + ::testing::ElementsAre()); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainIncreaseAverage"), + ::testing::ElementsAre()); +} + +TEST(AnalogGainStatsReporterTest, CheckLogLevelUpdateStatsNotEmpty) { + AnalogGainStatsReporter stats_reporter; + constexpr int kMicLevel = 10; + stats_reporter.UpdateStatistics(kMicLevel); + // Update until periodic logging. + for (int i = 0; i < kFramesIn60Seconds; i += 2) { + stats_reporter.UpdateStatistics(kMicLevel + 2); + stats_reporter.UpdateStatistics(kMicLevel); + } + // Update until periodic logging. + for (int i = 0; i < kFramesIn60Seconds; i += 2) { + stats_reporter.UpdateStatistics(kMicLevel + 3); + stats_reporter.UpdateStatistics(kMicLevel); + } + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainUpdateRate"), + ::testing::ElementsAre(::testing::Pair(kFramesIn60Seconds - 1, 1), + ::testing::Pair(kFramesIn60Seconds, 1))); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainDecreaseRate"), + ::testing::ElementsAre(::testing::Pair(kFramesIn60Seconds / 2 - 1, 1), + ::testing::Pair(kFramesIn60Seconds / 2, 1))); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainIncreaseRate"), + ::testing::ElementsAre(::testing::Pair(kFramesIn60Seconds / 2, 2))); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainUpdateAverage"), + ::testing::ElementsAre(::testing::Pair(2, 1), ::testing::Pair(3, 1))); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainDecreaseAverage"), + ::testing::ElementsAre(::testing::Pair(2, 1), ::testing::Pair(3, 1))); + EXPECT_METRIC_THAT( + metrics::Samples("WebRTC.Audio.ApmAnalogGainIncreaseAverage"), + ::testing::ElementsAre(::testing::Pair(2, 1), ::testing::Pair(3, 1))); +} +} // namespace + +TEST(AnalogGainStatsReporterTest, CheckLevelUpdateStatsForEmptyStats) { + AnalogGainStatsReporter stats_reporter; + const auto& update_stats = stats_reporter.level_update_stats(); + EXPECT_EQ(update_stats.num_decreases, 0); + EXPECT_EQ(update_stats.sum_decreases, 0); + EXPECT_EQ(update_stats.num_increases, 0); + EXPECT_EQ(update_stats.sum_increases, 0); +} + +TEST(AnalogGainStatsReporterTest, CheckLevelUpdateStatsAfterNoGainChange) { + constexpr int kMicLevel = 10; + AnalogGainStatsReporter stats_reporter; + stats_reporter.UpdateStatistics(kMicLevel); + stats_reporter.UpdateStatistics(kMicLevel); + stats_reporter.UpdateStatistics(kMicLevel); + const auto& update_stats = stats_reporter.level_update_stats(); + EXPECT_EQ(update_stats.num_decreases, 0); + EXPECT_EQ(update_stats.sum_decreases, 0); + EXPECT_EQ(update_stats.num_increases, 0); + EXPECT_EQ(update_stats.sum_increases, 0); +} + +TEST(AnalogGainStatsReporterTest, CheckLevelUpdateStatsAfterGainIncrease) { + constexpr int kMicLevel = 10; + AnalogGainStatsReporter stats_reporter; + stats_reporter.UpdateStatistics(kMicLevel); + stats_reporter.UpdateStatistics(kMicLevel + 4); + stats_reporter.UpdateStatistics(kMicLevel + 5); + const auto& update_stats = stats_reporter.level_update_stats(); + EXPECT_EQ(update_stats.num_decreases, 0); + EXPECT_EQ(update_stats.sum_decreases, 0); + EXPECT_EQ(update_stats.num_increases, 2); + EXPECT_EQ(update_stats.sum_increases, 5); +} + +TEST(AnalogGainStatsReporterTest, CheckLevelUpdateStatsAfterGainDecrease) { + constexpr int kMicLevel = 10; + AnalogGainStatsReporter stats_reporter; + stats_reporter.UpdateStatistics(kMicLevel); + stats_reporter.UpdateStatistics(kMicLevel - 4); + stats_reporter.UpdateStatistics(kMicLevel - 5); + const auto& stats_update = stats_reporter.level_update_stats(); + EXPECT_EQ(stats_update.num_decreases, 2); + EXPECT_EQ(stats_update.sum_decreases, 5); + EXPECT_EQ(stats_update.num_increases, 0); + EXPECT_EQ(stats_update.sum_increases, 0); +} + +TEST(AnalogGainStatsReporterTest, CheckLevelUpdateStatsAfterReset) { + AnalogGainStatsReporter stats_reporter; + constexpr int kMicLevel = 10; + stats_reporter.UpdateStatistics(kMicLevel); + // Update until the periodic reset. + for (int i = 0; i < kFramesIn60Seconds - 2; i += 2) { + stats_reporter.UpdateStatistics(kMicLevel + 2); + stats_reporter.UpdateStatistics(kMicLevel); + } + const auto& stats_before_reset = stats_reporter.level_update_stats(); + EXPECT_EQ(stats_before_reset.num_decreases, kFramesIn60Seconds / 2 - 1); + EXPECT_EQ(stats_before_reset.sum_decreases, kFramesIn60Seconds - 2); + EXPECT_EQ(stats_before_reset.num_increases, kFramesIn60Seconds / 2 - 1); + EXPECT_EQ(stats_before_reset.sum_increases, kFramesIn60Seconds - 2); + stats_reporter.UpdateStatistics(kMicLevel + 2); + const auto& stats_during_reset = stats_reporter.level_update_stats(); + EXPECT_EQ(stats_during_reset.num_decreases, 0); + EXPECT_EQ(stats_during_reset.sum_decreases, 0); + EXPECT_EQ(stats_during_reset.num_increases, 0); + EXPECT_EQ(stats_during_reset.sum_increases, 0); + stats_reporter.UpdateStatistics(kMicLevel); + stats_reporter.UpdateStatistics(kMicLevel + 3); + const auto& stats_after_reset = stats_reporter.level_update_stats(); + EXPECT_EQ(stats_after_reset.num_decreases, 1); + EXPECT_EQ(stats_after_reset.sum_decreases, 2); + EXPECT_EQ(stats_after_reset.num_increases, 1); + EXPECT_EQ(stats_after_reset.sum_increases, 3); +} + +} // namespace webrtc diff --git a/modules/audio_processing/agc/clipping_predictor.cc b/modules/audio_processing/agc/clipping_predictor.cc index 982bbca2ee..58b3a2769c 100644 --- a/modules/audio_processing/agc/clipping_predictor.cc +++ b/modules/audio_processing/agc/clipping_predictor.cc @@ -377,7 +377,7 @@ std::unique_ptr CreateClippingPredictor( config.reference_window_delay, config.clipping_threshold, /*adaptive_step_estimation=*/false); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } // namespace webrtc diff --git a/modules/audio_processing/agc/mock_agc.h b/modules/audio_processing/agc/mock_agc.h index 0ef41c6e52..3080e1563c 100644 --- a/modules/audio_processing/agc/mock_agc.h +++ b/modules/audio_processing/agc/mock_agc.h @@ -11,6 +11,7 @@ #ifndef MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ #define MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ +#include "api/array_view.h" #include "modules/audio_processing/agc/agc.h" #include "test/gmock.h" @@ -19,10 +20,7 @@ namespace webrtc { class MockAgc : public Agc { public: virtual ~MockAgc() {} - MOCK_METHOD(void, - Process, - (const int16_t* audio, size_t length, int sample_rate_hz), - (override)); + MOCK_METHOD(void, Process, (rtc::ArrayView audio), (override)); MOCK_METHOD(bool, GetRmsErrorDb, (int* error), (override)); MOCK_METHOD(void, Reset, (), (override)); MOCK_METHOD(int, set_target_level_dbfs, (int level), (override)); diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn index 6dd8babd78..e12252806b 100644 --- a/modules/audio_processing/agc2/BUILD.gn +++ b/modules/audio_processing/agc2/BUILD.gn @@ -17,10 +17,10 @@ group("agc2") { rtc_library("adaptive_digital") { sources = [ - "adaptive_agc.cc", - "adaptive_agc.h", "adaptive_digital_gain_applier.cc", "adaptive_digital_gain_applier.h", + "adaptive_digital_gain_controller.cc", + "adaptive_digital_gain_controller.h", "adaptive_mode_level_estimator.cc", "adaptive_mode_level_estimator.h", "saturation_protector.cc", @@ -29,6 +29,11 @@ rtc_library("adaptive_digital") { "saturation_protector_buffer.h", ] + visibility = [ + "..:gain_controller2", + "./*", + ] + configs += [ "..:apm_debug_dump" ] deps = [ @@ -36,7 +41,7 @@ rtc_library("adaptive_digital") { ":cpu_features", ":gain_applier", ":noise_level_estimator", - ":rnn_vad_with_level", + ":vad_wrapper", "..:api", "..:apm_logging", "..:audio_frame_view", @@ -79,6 +84,12 @@ rtc_library("fixed_digital") { "limiter.h", ] + visibility = [ + "..:gain_controller2", + "../../audio_mixer:audio_mixer_impl", + "./*", + ] + configs += [ "..:apm_debug_dump" ] deps = [ @@ -90,6 +101,7 @@ rtc_library("fixed_digital") { "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", "../../../system_wrappers:metrics", ] @@ -100,6 +112,12 @@ rtc_library("gain_applier") { "gain_applier.cc", "gain_applier.h", ] + + visibility = [ + "..:gain_controller2", + "./*", + ] + deps = [ ":common", "..:audio_frame_view", @@ -113,6 +131,7 @@ rtc_library("noise_level_estimator") { "noise_level_estimator.cc", "noise_level_estimator.h", ] + visibility = [ "./*" ] deps = [ ":biquad_filter", "..:apm_logging", @@ -125,10 +144,15 @@ rtc_library("noise_level_estimator") { configs += [ "..:apm_debug_dump" ] } -rtc_library("rnn_vad_with_level") { +rtc_library("vad_wrapper") { sources = [ - "vad_with_level.cc", - "vad_with_level.h", + "vad_wrapper.cc", + "vad_wrapper.h", + ] + + visibility = [ + "..:gain_controller2", + "./*", ] defines = [] @@ -154,7 +178,12 @@ rtc_library("cpu_features") { "cpu_features.cc", "cpu_features.h", ] - visibility = [ "./*" ] + + visibility = [ + "..:gain_controller2", + "./*", + ] + deps = [ "../../../rtc_base:stringutils", "../../../rtc_base/system:arch", @@ -178,6 +207,7 @@ rtc_library("adaptive_digital_unittests") { ":common", ":gain_applier", ":test_utils", + "..:api", "..:apm_logging", "..:audio_frame_view", "../../../api:array_view", @@ -246,13 +276,14 @@ rtc_library("noise_estimator_unittests") { ] } -rtc_library("rnn_vad_with_level_unittests") { +rtc_library("vad_wrapper_unittests") { testonly = true - sources = [ "vad_with_level_unittest.cc" ] + sources = [ "vad_wrapper_unittest.cc" ] deps = [ ":common", - ":rnn_vad_with_level", + ":vad_wrapper", "..:audio_frame_view", + "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", "../../../rtc_base:safe_compare", "../../../test:test_support", diff --git a/modules/audio_processing/agc2/adaptive_agc.cc b/modules/audio_processing/agc2/adaptive_agc.cc deleted file mode 100644 index a9f9622d00..0000000000 --- a/modules/audio_processing/agc2/adaptive_agc.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/agc2/adaptive_agc.h" - -#include "common_audio/include/audio_util.h" -#include "modules/audio_processing/agc2/cpu_features.h" -#include "modules/audio_processing/agc2/vad_with_level.h" -#include "modules/audio_processing/logging/apm_data_dumper.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" - -namespace webrtc { -namespace { - -using AdaptiveDigitalConfig = - AudioProcessing::Config::GainController2::AdaptiveDigital; -using NoiseEstimatorType = - AudioProcessing::Config::GainController2::NoiseEstimator; - -// Detects the available CPU features and applies any kill-switches. -AvailableCpuFeatures GetAllowedCpuFeatures( - const AdaptiveDigitalConfig& config) { - AvailableCpuFeatures features = GetAvailableCpuFeatures(); - if (!config.sse2_allowed) { - features.sse2 = false; - } - if (!config.avx2_allowed) { - features.avx2 = false; - } - if (!config.neon_allowed) { - features.neon = false; - } - return features; -} - -} // namespace - -AdaptiveAgc::AdaptiveAgc(ApmDataDumper* apm_data_dumper, - const AdaptiveDigitalConfig& config) - : speech_level_estimator_(apm_data_dumper, - config.adjacent_speech_frames_threshold), - vad_(config.vad_reset_period_ms, GetAllowedCpuFeatures(config)), - gain_controller_(apm_data_dumper, - config.adjacent_speech_frames_threshold, - config.max_gain_change_db_per_second, - config.max_output_noise_level_dbfs, - config.dry_run), - apm_data_dumper_(apm_data_dumper), - noise_level_estimator_(CreateNoiseFloorEstimator(apm_data_dumper)), - saturation_protector_( - CreateSaturationProtector(kSaturationProtectorInitialHeadroomDb, - kSaturationProtectorExtraHeadroomDb, - config.adjacent_speech_frames_threshold, - apm_data_dumper)) { - RTC_DCHECK(apm_data_dumper); - RTC_DCHECK(noise_level_estimator_); - RTC_DCHECK(saturation_protector_); - if (!config.use_saturation_protector) { - RTC_LOG(LS_WARNING) << "The saturation protector cannot be disabled."; - } -} - -AdaptiveAgc::~AdaptiveAgc() = default; - -void AdaptiveAgc::Initialize(int sample_rate_hz, int num_channels) { - gain_controller_.Initialize(sample_rate_hz, num_channels); -} - -void AdaptiveAgc::Process(AudioFrameView frame, float limiter_envelope) { - AdaptiveDigitalGainApplier::FrameInfo info; - - VadLevelAnalyzer::Result vad_result = vad_.AnalyzeFrame(frame); - info.speech_probability = vad_result.speech_probability; - apm_data_dumper_->DumpRaw("agc2_speech_probability", - vad_result.speech_probability); - apm_data_dumper_->DumpRaw("agc2_input_rms_dbfs", vad_result.rms_dbfs); - apm_data_dumper_->DumpRaw("agc2_input_peak_dbfs", vad_result.peak_dbfs); - - speech_level_estimator_.Update(vad_result); - info.speech_level_dbfs = speech_level_estimator_.level_dbfs(); - info.speech_level_reliable = speech_level_estimator_.IsConfident(); - apm_data_dumper_->DumpRaw("agc2_speech_level_dbfs", info.speech_level_dbfs); - apm_data_dumper_->DumpRaw("agc2_speech_level_reliable", - info.speech_level_reliable); - - info.noise_rms_dbfs = noise_level_estimator_->Analyze(frame); - apm_data_dumper_->DumpRaw("agc2_noise_rms_dbfs", info.noise_rms_dbfs); - - saturation_protector_->Analyze(info.speech_probability, vad_result.peak_dbfs, - info.speech_level_dbfs); - info.headroom_db = saturation_protector_->HeadroomDb(); - apm_data_dumper_->DumpRaw("agc2_headroom_db", info.headroom_db); - - info.limiter_envelope_dbfs = FloatS16ToDbfs(limiter_envelope); - apm_data_dumper_->DumpRaw("agc2_limiter_envelope_dbfs", - info.limiter_envelope_dbfs); - - gain_controller_.Process(info, frame); -} - -void AdaptiveAgc::HandleInputGainChange() { - speech_level_estimator_.Reset(); - saturation_protector_->Reset(); -} - -} // namespace webrtc diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc index e59b110efe..a34f598874 100644 --- a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc +++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc @@ -23,31 +23,38 @@ namespace webrtc { namespace { +using AdaptiveDigitalConfig = + AudioProcessing::Config::GainController2::AdaptiveDigital; + constexpr int kHeadroomHistogramMin = 0; constexpr int kHeadroomHistogramMax = 50; +constexpr int kGainDbHistogramMax = 30; -// This function maps input level to desired applied gain. We want to -// boost the signal so that peaks are at -kHeadroomDbfs. We can't -// apply more than kMaxGainDb gain. -float ComputeGainDb(float input_level_dbfs) { - // If the level is very low, boost it as much as we can. - if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) { - return kMaxGainDb; +// 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 < -kHeadroomDbfs) { - return -kHeadroomDbfs - input_level_dbfs; + if (input_level_dbfs < -config.headroom_db) { + return -config.headroom_db - input_level_dbfs; } - // Otherwise, the level is too high and we can't boost. - RTC_DCHECK_GE(input_level_dbfs, -kHeadroomDbfs); - return 0.f; + // 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` if the output noise level is below -// `max_output_noise_level_dbfs`; otherwise returns a capped gain so that the -// output noise level equals `max_output_noise_level_dbfs`. -float LimitGainByNoise(float target_gain, +// 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) { @@ -55,24 +62,25 @@ float LimitGainByNoise(float target_gain, 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, std::max(max_allowed_gain_db, 0.f)); + return std::min(target_gain_db, std::max(max_allowed_gain_db, 0.0f)); } -float LimitGainByLowConfidence(float target_gain, - float last_gain, +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; + return target_gain_db; } - const float limiter_level_before_gain = limiter_audio_level_dbfs - last_gain; + const float limiter_level_dbfs_before_gain = + limiter_audio_level_dbfs - last_gain_db; - // Compute a new gain so that `limiter_level_before_gain` + `new_target_gain` - // is not great than `kLimiterThresholdForAgcGainDbfs`. - const float new_target_gain = std::max( - kLimiterThresholdForAgcGainDbfs - limiter_level_before_gain, 0.f); - return std::min(new_target_gain, target_gain); + // 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. @@ -86,7 +94,7 @@ float ComputeGainChangeThisFrameDb(float target_gain_db, 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.f); + target_gain_difference_db = std::min(target_gain_difference_db, 0.0f); } return rtc::SafeClamp(target_gain_difference_db, -max_gain_decrease_db, max_gain_increase_db); @@ -98,7 +106,7 @@ void CopyAudio(AudioFrameView src, RTC_DCHECK_GT(src.num_channels(), 0); RTC_DCHECK_GT(src.samples_per_channel(), 0); RTC_DCHECK_EQ(dst.size(), src.num_channels()); - for (size_t c = 0; c < src.num_channels(); ++c) { + for (int c = 0; c < src.num_channels(); ++c) { rtc::ArrayView channel_view = src.channel(c); RTC_DCHECK_EQ(channel_view.size(), src.samples_per_channel()); RTC_DCHECK_EQ(dst[c].size(), src.samples_per_channel()); @@ -110,32 +118,30 @@ void CopyAudio(AudioFrameView src, AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier( ApmDataDumper* apm_data_dumper, - int adjacent_speech_frames_threshold, - float max_gain_change_db_per_second, - float max_output_noise_level_dbfs, - bool dry_run) + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int sample_rate_hz, + int num_channels) : apm_data_dumper_(apm_data_dumper), gain_applier_( /*hard_clip_samples=*/false, - /*initial_gain_factor=*/DbToRatio(kInitialAdaptiveDigitalGainDb)), - adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), - max_gain_change_db_per_10ms_(max_gain_change_db_per_second * - kFrameDurationMs / 1000.f), - max_output_noise_level_dbfs_(max_output_noise_level_dbfs), - dry_run_(dry_run), + /*initial_gain_factor=*/DbToRatio(config.initial_gain_db)), + config_(config), + 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_(kInitialAdaptiveDigitalGainDb) { - RTC_DCHECK_GT(max_gain_change_db_per_second, 0.0f); + frames_to_gain_increase_allowed_( + config_.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(max_output_noise_level_dbfs_, -90.0f); - RTC_DCHECK_LE(max_output_noise_level_dbfs_, 0.0f); - Initialize(/*sample_rate_hz=*/48000, /*num_channels=*/1); + RTC_DCHECK_GE(config_.max_output_noise_level_dbfs, -90.0f); + RTC_DCHECK_LE(config_.max_output_noise_level_dbfs, 0.0f); + Initialize(sample_rate_hz, num_channels); } void AdaptiveDigitalGainApplier::Initialize(int sample_rate_hz, int num_channels) { - if (!dry_run_) { + if (!config_.dry_run) { return; } RTC_DCHECK_GT(sample_rate_hz, 0); @@ -159,7 +165,7 @@ void AdaptiveDigitalGainApplier::Initialize(int sample_rate_hz, void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, AudioFrameView frame) { - RTC_DCHECK_GE(info.speech_level_dbfs, -150.f); + 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 || @@ -172,15 +178,16 @@ void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, const float input_level_dbfs = info.speech_level_dbfs + info.headroom_db; const float target_gain_db = LimitGainByLowConfidence( - LimitGainByNoise(ComputeGainDb(input_level_dbfs), info.noise_rms_dbfs, - max_output_noise_level_dbfs_, *apm_data_dumper_), + 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_; + frames_to_gain_increase_allowed_ = config_.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; @@ -196,7 +203,7 @@ void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, // 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_; + max_gain_increase_db *= config_.adjacent_speech_frames_threshold; } const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb( @@ -217,7 +224,7 @@ void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, } // Modify `frame` only if not running in "dry run" mode. - if (!dry_run_) { + if (!config_.dry_run) { gain_applier_.ApplyGain(frame); } else { // Copy `frame` so that `ApplyGain()` is called (on a copy). @@ -247,7 +254,8 @@ void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, kHeadroomHistogramMax, kHeadroomHistogramMax - kHeadroomHistogramMin + 1); RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied", - last_gain_db_, 0, kMaxGainDb, kMaxGainDb + 1); + 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 diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.h b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h index 8b58ea00b2..dc84c1e238 100644 --- a/modules/audio_processing/agc2/adaptive_digital_gain_applier.h +++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h @@ -15,12 +15,13 @@ #include "modules/audio_processing/agc2/gain_applier.h" #include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/include/audio_processing.h" namespace webrtc { class ApmDataDumper; -// TODO(bugs.webrtc.org): Split into `GainAdaptor` and `GainApplier`. +// TODO(bugs.webrtc.org/7494): Split into `GainAdaptor` and `GainApplier`. // 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 AdaptiveDigitalGainApplier { @@ -35,16 +36,11 @@ class AdaptiveDigitalGainApplier { float limiter_envelope_dbfs; // Envelope level from the limiter (dBFS). }; - // Ctor. `adjacent_speech_frames_threshold` indicates how many adjacent speech - // frames must be observed in order to consider the sequence as speech. - // `max_gain_change_db_per_second` limits the adaptation speed (uniformly - // operated across frames). `max_output_noise_level_dbfs` limits the output - // noise level. If `dry_run` is true, `Process()` will not modify the audio. - AdaptiveDigitalGainApplier(ApmDataDumper* apm_data_dumper, - int adjacent_speech_frames_threshold, - float max_gain_change_db_per_second, - float max_output_noise_level_dbfs, - bool dry_run); + AdaptiveDigitalGainApplier( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int sample_rate_hz, + int num_channels); AdaptiveDigitalGainApplier(const AdaptiveDigitalGainApplier&) = delete; AdaptiveDigitalGainApplier& operator=(const AdaptiveDigitalGainApplier&) = delete; @@ -59,10 +55,8 @@ class AdaptiveDigitalGainApplier { ApmDataDumper* const apm_data_dumper_; GainApplier gain_applier_; - const int adjacent_speech_frames_threshold_; + const AudioProcessing::Config::GainController2::AdaptiveDigital config_; const float max_gain_change_db_per_10ms_; - const float max_output_noise_level_dbfs_; - const bool dry_run_; int calls_since_last_gain_log_; int frames_to_gain_increase_allowed_; diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc index f4a23a92b9..ea7485f512 100644 --- a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc +++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc @@ -16,6 +16,7 @@ #include "common_audio/include/audio_util.h" #include "modules/audio_processing/agc2/agc2_common.h" #include "modules/audio_processing/agc2/vector_float_frame.h" +#include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/gunit.h" @@ -31,59 +32,72 @@ constexpr float kMaxSpeechProbability = 1.0f; // Constants used in place of estimated noise levels. constexpr float kNoNoiseDbfs = kMinLevelDbfs; -constexpr float kWithNoiseDbfs = -20.f; +constexpr float kWithNoiseDbfs = -20.0f; -constexpr float kMaxGainChangePerSecondDb = 3.0f; -constexpr float kMaxGainChangePerFrameDb = - kMaxGainChangePerSecondDb * kFrameDurationMs / 1000.0f; -constexpr float kMaxOutputNoiseLevelDbfs = -50.0f; +// Number of additional frames to process in the tests to ensure that the tested +// adaptation processes have converged. +constexpr int kNumExtraFrames = 10; + +constexpr float GetMaxGainChangePerFrameDb( + float max_gain_change_db_per_second) { + return max_gain_change_db_per_second * kFrameDurationMs / 1000.0f; +} + +using AdaptiveDigitalConfig = + AudioProcessing::Config::GainController2::AdaptiveDigital; + +constexpr AdaptiveDigitalConfig kDefaultConfig{}; // Helper to create initialized `AdaptiveDigitalGainApplier` objects. struct GainApplierHelper { - GainApplierHelper() - : GainApplierHelper(/*adjacent_speech_frames_threshold=*/1) {} - explicit GainApplierHelper(int adjacent_speech_frames_threshold) + GainApplierHelper(const AdaptiveDigitalConfig& config, + int sample_rate_hz, + int num_channels) : apm_data_dumper(0), - gain_applier(std::make_unique( - &apm_data_dumper, - adjacent_speech_frames_threshold, - kMaxGainChangePerSecondDb, - kMaxOutputNoiseLevelDbfs, - /*dry_run=*/false)) {} + gain_applier( + std::make_unique(&apm_data_dumper, + config, + sample_rate_hz, + num_channels)) {} ApmDataDumper apm_data_dumper; std::unique_ptr gain_applier; }; -// Voice on, no noise, low limiter, confident level. -static_assert(std::is_trivially_destructible< - AdaptiveDigitalGainApplier::FrameInfo>::value, - ""); -constexpr AdaptiveDigitalGainApplier::FrameInfo kFrameInfo{ - /*speech_probability=*/kMaxSpeechProbability, - /*speech_level_dbfs=*/kInitialSpeechLevelEstimateDbfs, - /*speech_level_reliable=*/true, - /*noise_rms_dbfs=*/kNoNoiseDbfs, - /*headroom_db=*/kSaturationProtectorInitialHeadroomDb, - /*limiter_envelope_dbfs=*/-2.0f}; +// Returns a `FrameInfo` sample to simulate noiseless speech detected with +// maximum probability and with level, headroom and limiter envelope chosen +// so that the resulting gain equals the default initial adaptive digital gain +// i.e., no gain adaptation is expected. +AdaptiveDigitalGainApplier::FrameInfo GetFrameInfoToNotAdapt( + const AdaptiveDigitalConfig& config) { + AdaptiveDigitalGainApplier::FrameInfo info; + info.speech_probability = kMaxSpeechProbability; + info.speech_level_dbfs = -config.initial_gain_db - config.headroom_db; + info.speech_level_reliable = true; + info.noise_rms_dbfs = kNoNoiseDbfs; + info.headroom_db = config.headroom_db; + info.limiter_envelope_dbfs = -2.0f; + return info; +} TEST(GainController2AdaptiveGainApplier, GainApplierShouldNotCrash) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kStereo); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/48000, kStereo); // Make one call with reasonable audio level values and settings. VectorFloatFrame fake_audio(kStereo, kFrameLen10ms48kHz, 10000.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; - info.speech_level_dbfs = -5.0f; - helper.gain_applier->Process(kFrameInfo, fake_audio.float_frame_view()); + helper.gain_applier->Process(GetFrameInfoToNotAdapt(kDefaultConfig), + fake_audio.float_frame_view()); } // Checks that the maximum allowed gain is applied. TEST(GainController2AdaptiveGainApplier, MaxGainApplied) { constexpr int kNumFramesToAdapt = - static_cast(kMaxGainDb / kMaxGainChangePerFrameDb) + 10; + static_cast(kDefaultConfig.max_gain_db / + GetMaxGainChangePerFrameDb( + kDefaultConfig.max_gain_change_db_per_second)) + + kNumExtraFrames; - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/8000, kMono); + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = -60.0f; float applied_gain; for (int i = 0; i < kNumFramesToAdapt; ++i) { @@ -92,30 +106,32 @@ TEST(GainController2AdaptiveGainApplier, MaxGainApplied) { applied_gain = fake_audio.float_frame_view().channel(0)[0]; } const float applied_gain_db = 20.0f * std::log10f(applied_gain); - EXPECT_NEAR(applied_gain_db, kMaxGainDb, 0.1f); + EXPECT_NEAR(applied_gain_db, kDefaultConfig.max_gain_db, 0.1f); } TEST(GainController2AdaptiveGainApplier, GainDoesNotChangeFast) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/8000, kMono); constexpr float initial_level_dbfs = -25.0f; - // A few extra frames for safety. + constexpr float kMaxGainChangeDbPerFrame = + GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second); constexpr int kNumFramesToAdapt = - static_cast(initial_level_dbfs / kMaxGainChangePerFrameDb) + 10; + static_cast(initial_level_dbfs / kMaxGainChangeDbPerFrame) + + kNumExtraFrames; - const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb); + const float max_change_per_frame_linear = DbToRatio(kMaxGainChangeDbPerFrame); float last_gain_linear = 1.f; for (int i = 0; i < kNumFramesToAdapt; ++i) { SCOPED_TRACE(i); VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, 1.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = initial_level_dbfs; helper.gain_applier->Process(info, fake_audio.float_frame_view()); float current_gain_linear = fake_audio.float_frame_view().channel(0)[0]; EXPECT_LE(std::abs(current_gain_linear - last_gain_linear), - kMaxChangePerFrameLinear); + max_change_per_frame_linear); last_gain_linear = current_gain_linear; } @@ -123,56 +139,59 @@ TEST(GainController2AdaptiveGainApplier, GainDoesNotChangeFast) { for (int i = 0; i < kNumFramesToAdapt; ++i) { SCOPED_TRACE(i); VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, 1.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = 0.f; helper.gain_applier->Process(info, fake_audio.float_frame_view()); float current_gain_linear = fake_audio.float_frame_view().channel(0)[0]; EXPECT_LE(std::abs(current_gain_linear - last_gain_linear), - kMaxChangePerFrameLinear); + max_change_per_frame_linear); last_gain_linear = current_gain_linear; } } TEST(GainController2AdaptiveGainApplier, GainIsRampedInAFrame) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/48000, kMono); constexpr float initial_level_dbfs = -25.0f; VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = initial_level_dbfs; helper.gain_applier->Process(info, fake_audio.float_frame_view()); float maximal_difference = 0.0f; - float current_value = 1.0f * DbToRatio(kInitialAdaptiveDigitalGainDb); + float current_value = 1.0f * DbToRatio(kDefaultConfig.initial_gain_db); for (const auto& x : fake_audio.float_frame_view().channel(0)) { const float difference = std::abs(x - current_value); maximal_difference = std::max(maximal_difference, difference); current_value = x; } - const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb); - const float kMaxChangePerSample = - kMaxChangePerFrameLinear / kFrameLen10ms48kHz; + const float max_change_per_frame_linear = DbToRatio( + GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second)); + const float max_change_per_sample = + max_change_per_frame_linear / kFrameLen10ms48kHz; - EXPECT_LE(maximal_difference, kMaxChangePerSample); + EXPECT_LE(maximal_difference, max_change_per_sample); } TEST(GainController2AdaptiveGainApplier, NoiseLimitsGain) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/48000, kMono); constexpr float initial_level_dbfs = -25.0f; constexpr int num_initial_frames = - kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb; + kDefaultConfig.initial_gain_db / + GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second); constexpr int num_frames = 50; - ASSERT_GT(kWithNoiseDbfs, kMaxOutputNoiseLevelDbfs) + ASSERT_GT(kWithNoiseDbfs, kDefaultConfig.max_output_noise_level_dbfs) << "kWithNoiseDbfs is too low"; for (int i = 0; i < num_initial_frames + num_frames; ++i) { VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = initial_level_dbfs; info.noise_rms_dbfs = kWithNoiseDbfs; helper.gain_applier->Process(info, fake_audio.float_frame_view()); @@ -189,31 +208,32 @@ TEST(GainController2AdaptiveGainApplier, NoiseLimitsGain) { } TEST(GainController2GainApplier, CanHandlePositiveSpeechLevels) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kStereo); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/48000, kStereo); // Make one call with positive audio level values and settings. VectorFloatFrame fake_audio(kStereo, kFrameLen10ms48kHz, 10000.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = 5.0f; helper.gain_applier->Process(info, fake_audio.float_frame_view()); } TEST(GainController2GainApplier, AudioLevelLimitsGain) { - GainApplierHelper helper; - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); + GainApplierHelper helper(kDefaultConfig, /*sample_rate_hz=*/48000, kMono); constexpr float initial_level_dbfs = -25.0f; constexpr int num_initial_frames = - kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb; + kDefaultConfig.initial_gain_db / + GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second); constexpr int num_frames = 50; - ASSERT_GT(kWithNoiseDbfs, kMaxOutputNoiseLevelDbfs) + ASSERT_GT(kWithNoiseDbfs, kDefaultConfig.max_output_noise_level_dbfs) << "kWithNoiseDbfs is too low"; for (int i = 0; i < num_initial_frames + num_frames; ++i) { VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = + GetFrameInfoToNotAdapt(kDefaultConfig); info.speech_level_dbfs = initial_level_dbfs; info.limiter_envelope_dbfs = 1.0f; info.speech_level_reliable = false; @@ -232,46 +252,54 @@ TEST(GainController2GainApplier, AudioLevelLimitsGain) { class AdaptiveDigitalGainApplierTest : public ::testing::TestWithParam { protected: - int AdjacentSpeechFramesThreshold() const { return GetParam(); } + int adjacent_speech_frames_threshold() const { return GetParam(); } }; TEST_P(AdaptiveDigitalGainApplierTest, DoNotIncreaseGainWithTooFewSpeechFrames) { - const int adjacent_speech_frames_threshold = AdjacentSpeechFramesThreshold(); - GainApplierHelper helper(adjacent_speech_frames_threshold); - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); + AdaptiveDigitalConfig config; + config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold(); + GainApplierHelper helper(config, /*sample_rate_hz=*/48000, kMono); + + // Lower the speech level so that the target gain will be increased. + AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config); + info.speech_level_dbfs -= 12.0f; float prev_gain = 0.0f; - for (int i = 0; i < adjacent_speech_frames_threshold; ++i) { + for (int i = 0; i < config.adjacent_speech_frames_threshold; ++i) { SCOPED_TRACE(i); VectorFloatFrame audio(kMono, kFrameLen10ms48kHz, 1.0f); - helper.gain_applier->Process(kFrameInfo, audio.float_frame_view()); + helper.gain_applier->Process(info, audio.float_frame_view()); const float gain = audio.float_frame_view().channel(0)[0]; if (i > 0) { - EXPECT_EQ(prev_gain, gain); // No gain increase. + EXPECT_EQ(prev_gain, gain); // No gain increase applied. } prev_gain = gain; } } TEST_P(AdaptiveDigitalGainApplierTest, IncreaseGainWithEnoughSpeechFrames) { - const int adjacent_speech_frames_threshold = AdjacentSpeechFramesThreshold(); - GainApplierHelper helper(adjacent_speech_frames_threshold); - helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); + AdaptiveDigitalConfig config; + config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold(); + GainApplierHelper helper(config, /*sample_rate_hz=*/48000, kMono); + + // Lower the speech level so that the target gain will be increased. + AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config); + info.speech_level_dbfs -= 12.0f; float prev_gain = 0.0f; - for (int i = 0; i < adjacent_speech_frames_threshold; ++i) { + for (int i = 0; i < config.adjacent_speech_frames_threshold; ++i) { SCOPED_TRACE(i); VectorFloatFrame audio(kMono, kFrameLen10ms48kHz, 1.0f); - helper.gain_applier->Process(kFrameInfo, audio.float_frame_view()); + helper.gain_applier->Process(info, audio.float_frame_view()); prev_gain = audio.float_frame_view().channel(0)[0]; } // Process one more speech frame. VectorFloatFrame audio(kMono, kFrameLen10ms48kHz, 1.0f); - helper.gain_applier->Process(kFrameInfo, audio.float_frame_view()); + helper.gain_applier->Process(info, audio.float_frame_view()); - // The gain has increased. + // An increased gain has been applied. EXPECT_GT(audio.float_frame_view().channel(0)[0], prev_gain); } @@ -281,63 +309,62 @@ INSTANTIATE_TEST_SUITE_P(GainController2, // Checks that the input is never modified when running in dry run mode. TEST(GainController2GainApplier, DryRunDoesNotChangeInput) { - ApmDataDumper apm_data_dumper(0); - AdaptiveDigitalGainApplier gain_applier( - &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1, - kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true); + AdaptiveDigitalConfig config; + config.dry_run = true; + GainApplierHelper helper(config, /*sample_rate_hz=*/8000, kMono); + // Simulate an input signal with log speech level. - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config); info.speech_level_dbfs = -60.0f; - // Allow enough time to reach the maximum gain. - constexpr int kNumFramesToAdapt = - static_cast(kMaxGainDb / kMaxGainChangePerFrameDb) + 10; + const int num_frames_to_adapt = + static_cast( + config.max_gain_db / + GetMaxGainChangePerFrameDb(config.max_gain_change_db_per_second)) + + kNumExtraFrames; constexpr float kPcmSamples = 123.456f; // Run the gain applier and check that the PCM samples are not modified. - gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono); - for (int i = 0; i < kNumFramesToAdapt; ++i) { + for (int i = 0; i < num_frames_to_adapt; ++i) { SCOPED_TRACE(i); VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, kPcmSamples); - gain_applier.Process(info, fake_audio.float_frame_view()); + helper.gain_applier->Process(info, fake_audio.float_frame_view()); EXPECT_FLOAT_EQ(fake_audio.float_frame_view().channel(0)[0], kPcmSamples); } } // Checks that no sample is modified before and after the sample rate changes. TEST(GainController2GainApplier, DryRunHandlesSampleRateChange) { - ApmDataDumper apm_data_dumper(0); - AdaptiveDigitalGainApplier gain_applier( - &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1, - kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalConfig config; + config.dry_run = true; + GainApplierHelper helper(config, /*sample_rate_hz=*/8000, kMono); + + AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config); info.speech_level_dbfs = -60.0f; constexpr float kPcmSamples = 123.456f; VectorFloatFrame fake_audio_8k(kMono, kFrameLen10ms8kHz, kPcmSamples); - gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono); - gain_applier.Process(info, fake_audio_8k.float_frame_view()); + helper.gain_applier->Process(info, fake_audio_8k.float_frame_view()); EXPECT_FLOAT_EQ(fake_audio_8k.float_frame_view().channel(0)[0], kPcmSamples); - gain_applier.Initialize(/*sample_rate_hz=*/48000, kMono); + helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono); VectorFloatFrame fake_audio_48k(kMono, kFrameLen10ms48kHz, kPcmSamples); - gain_applier.Process(info, fake_audio_48k.float_frame_view()); + helper.gain_applier->Process(info, fake_audio_48k.float_frame_view()); EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(0)[0], kPcmSamples); } // Checks that no sample is modified before and after the number of channels // changes. TEST(GainController2GainApplier, DryRunHandlesNumChannelsChange) { - ApmDataDumper apm_data_dumper(0); - AdaptiveDigitalGainApplier gain_applier( - &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1, - kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true); - AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo; + AdaptiveDigitalConfig config; + config.dry_run = true; + GainApplierHelper helper(config, /*sample_rate_hz=*/8000, kMono); + + AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config); info.speech_level_dbfs = -60.0f; constexpr float kPcmSamples = 123.456f; VectorFloatFrame fake_audio_8k(kMono, kFrameLen10ms8kHz, kPcmSamples); - gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono); - gain_applier.Process(info, fake_audio_8k.float_frame_view()); + helper.gain_applier->Process(info, fake_audio_8k.float_frame_view()); EXPECT_FLOAT_EQ(fake_audio_8k.float_frame_view().channel(0)[0], kPcmSamples); VectorFloatFrame fake_audio_48k(kStereo, kFrameLen10ms8kHz, kPcmSamples); - gain_applier.Initialize(/*sample_rate_hz=*/8000, kStereo); - gain_applier.Process(info, fake_audio_48k.float_frame_view()); + helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kStereo); + helper.gain_applier->Process(info, fake_audio_48k.float_frame_view()); EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(0)[0], kPcmSamples); EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(1)[0], kPcmSamples); } diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc b/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc new file mode 100644 index 0000000000..381e454868 --- /dev/null +++ b/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc @@ -0,0 +1,108 @@ +/* + * 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/vad_wrapper.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +// Peak and RMS audio levels in dBFS. +struct AudioLevels { + float peak_dbfs; + float rms_dbfs; +}; + +// Computes the audio levels for the first channel in `frame`. +AudioLevels ComputeAudioLevels(AudioFrameView frame) { + float peak = 0.0f; + float rms = 0.0f; + for (const auto& x : frame.channel(0)) { + peak = std::max(std::fabs(x), peak); + rms += x * x; + } + return {FloatS16ToDbfs(peak), + FloatS16ToDbfs(std::sqrt(rms / frame.samples_per_channel()))}; +} + +} // namespace + +AdaptiveDigitalGainController::AdaptiveDigitalGainController( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int sample_rate_hz, + int num_channels) + : speech_level_estimator_(apm_data_dumper, config), + gain_controller_(apm_data_dumper, config, sample_rate_hz, num_channels), + apm_data_dumper_(apm_data_dumper), + noise_level_estimator_(CreateNoiseFloorEstimator(apm_data_dumper)), + saturation_protector_( + CreateSaturationProtector(kSaturationProtectorInitialHeadroomDb, + config.adjacent_speech_frames_threshold, + apm_data_dumper)) { + RTC_DCHECK(apm_data_dumper); + RTC_DCHECK(noise_level_estimator_); + RTC_DCHECK(saturation_protector_); +} + +AdaptiveDigitalGainController::~AdaptiveDigitalGainController() = default; + +void AdaptiveDigitalGainController::Initialize(int sample_rate_hz, + int num_channels) { + gain_controller_.Initialize(sample_rate_hz, num_channels); +} + +void AdaptiveDigitalGainController::Process(AudioFrameView frame, + float speech_probability, + float limiter_envelope) { + AudioLevels levels = ComputeAudioLevels(frame); + apm_data_dumper_->DumpRaw("agc2_input_rms_dbfs", levels.rms_dbfs); + apm_data_dumper_->DumpRaw("agc2_input_peak_dbfs", levels.peak_dbfs); + + AdaptiveDigitalGainApplier::FrameInfo info; + + info.speech_probability = speech_probability; + + speech_level_estimator_.Update(levels.rms_dbfs, levels.peak_dbfs, + info.speech_probability); + info.speech_level_dbfs = speech_level_estimator_.level_dbfs(); + info.speech_level_reliable = speech_level_estimator_.IsConfident(); + apm_data_dumper_->DumpRaw("agc2_speech_level_dbfs", info.speech_level_dbfs); + apm_data_dumper_->DumpRaw("agc2_speech_level_reliable", + info.speech_level_reliable); + + info.noise_rms_dbfs = noise_level_estimator_->Analyze(frame); + apm_data_dumper_->DumpRaw("agc2_noise_rms_dbfs", info.noise_rms_dbfs); + + saturation_protector_->Analyze(info.speech_probability, levels.peak_dbfs, + info.speech_level_dbfs); + info.headroom_db = saturation_protector_->HeadroomDb(); + apm_data_dumper_->DumpRaw("agc2_headroom_db", info.headroom_db); + + info.limiter_envelope_dbfs = FloatS16ToDbfs(limiter_envelope); + apm_data_dumper_->DumpRaw("agc2_limiter_envelope_dbfs", + info.limiter_envelope_dbfs); + + gain_controller_.Process(info, frame); +} + +void AdaptiveDigitalGainController::HandleInputGainChange() { + speech_level_estimator_.Reset(); + saturation_protector_->Reset(); +} + +} // namespace webrtc diff --git a/modules/audio_processing/agc2/adaptive_agc.h b/modules/audio_processing/agc2/adaptive_digital_gain_controller.h similarity index 57% rename from modules/audio_processing/agc2/adaptive_agc.h rename to modules/audio_processing/agc2/adaptive_digital_gain_controller.h index 43c7787e36..75ea44591e 100644 --- a/modules/audio_processing/agc2/adaptive_agc.h +++ b/modules/audio_processing/agc2/adaptive_digital_gain_controller.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ -#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ #include @@ -17,37 +17,41 @@ #include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h" #include "modules/audio_processing/agc2/noise_level_estimator.h" #include "modules/audio_processing/agc2/saturation_protector.h" -#include "modules/audio_processing/agc2/vad_with_level.h" #include "modules/audio_processing/include/audio_frame_view.h" #include "modules/audio_processing/include/audio_processing.h" namespace webrtc { class ApmDataDumper; -// Adaptive digital gain controller. -// TODO(crbug.com/webrtc/7494): Rename to `AdaptiveDigitalGainController`. -class AdaptiveAgc { +// Gain controller that adapts and applies a variable digital gain to meet the +// target level, which is determined by the given configuration. +class AdaptiveDigitalGainController { public: - AdaptiveAgc( + AdaptiveDigitalGainController( ApmDataDumper* apm_data_dumper, - const AudioProcessing::Config::GainController2::AdaptiveDigital& config); - ~AdaptiveAgc(); + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int sample_rate_hz, + int num_channels); + AdaptiveDigitalGainController(const AdaptiveDigitalGainController&) = delete; + AdaptiveDigitalGainController& operator=( + const AdaptiveDigitalGainController&) = delete; + ~AdaptiveDigitalGainController(); + // Detects and handles changes of sample rate and or number of channels. void Initialize(int sample_rate_hz, int num_channels); - // TODO(crbug.com/webrtc/7494): Add `SetLimiterEnvelope()`. - - // Analyzes `frame` and applies a digital adaptive gain to it. Takes into - // account the envelope measured by the limiter. - // TODO(crbug.com/webrtc/7494): Remove `limiter_envelope`. - void Process(AudioFrameView frame, float limiter_envelope); + // Analyzes `frame`, adapts the current digital gain and applies it to + // `frame`. + // TODO(bugs.webrtc.org/7494): Remove `limiter_envelope`. + void Process(AudioFrameView frame, + float speech_probability, + float limiter_envelope); // Handles a gain change applied to the input signal (e.g., analog gain). void HandleInputGainChange(); private: AdaptiveModeLevelEstimator speech_level_estimator_; - VadLevelAnalyzer vad_; AdaptiveDigitalGainApplier gain_controller_; ApmDataDumper* const apm_data_dumper_; std::unique_ptr noise_level_estimator_; @@ -56,4 +60,4 @@ class AdaptiveAgc { } // namespace webrtc -#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc index 507aa12cb4..fe021fec05 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc @@ -19,11 +19,15 @@ namespace webrtc { namespace { -using LevelEstimatorType = - AudioProcessing::Config::GainController2::LevelEstimator; - float ClampLevelEstimateDbfs(float level_estimate_dbfs) { - return rtc::SafeClamp(level_estimate_dbfs, -90.f, 30.f); + return rtc::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 @@ -40,32 +44,29 @@ float AdaptiveModeLevelEstimator::LevelEstimatorState::Ratio::GetRatio() const { return numerator / denominator; } -AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator( - ApmDataDumper* apm_data_dumper) - : AdaptiveModeLevelEstimator( - apm_data_dumper, - kDefaultLevelEstimatorAdjacentSpeechFramesThreshold) {} - AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator( ApmDataDumper* apm_data_dumper, - int adjacent_speech_frames_threshold) + const AudioProcessing::Config::GainController2::AdaptiveDigital& config) : apm_data_dumper_(apm_data_dumper), - adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), - level_dbfs_(ClampLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs)) { + initial_speech_level_dbfs_(GetInitialSpeechLevelEstimateDbfs(config)), + adjacent_speech_frames_threshold_( + config.adjacent_speech_frames_threshold), + level_dbfs_(initial_speech_level_dbfs_) { RTC_DCHECK(apm_data_dumper_); RTC_DCHECK_GE(adjacent_speech_frames_threshold_, 1); Reset(); } -void AdaptiveModeLevelEstimator::Update( - const VadLevelAnalyzer::Result& vad_level) { - RTC_DCHECK_GT(vad_level.rms_dbfs, -150.f); - RTC_DCHECK_LT(vad_level.rms_dbfs, 50.f); - RTC_DCHECK_GT(vad_level.peak_dbfs, -150.f); - RTC_DCHECK_LT(vad_level.peak_dbfs, 50.f); - RTC_DCHECK_GE(vad_level.speech_probability, 0.f); - RTC_DCHECK_LE(vad_level.speech_probability, 1.f); - if (vad_level.speech_probability < kVadConfidenceThreshold) { +void AdaptiveModeLevelEstimator::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 @@ -93,14 +94,14 @@ void AdaptiveModeLevelEstimator::Update( preliminary_state_.time_to_confidence_ms -= kFrameDurationMs; } // Weighted average of levels with speech probability as weight. - RTC_DCHECK_GT(vad_level.speech_probability, 0.f); - const float leak_factor = buffer_is_full ? kLevelEstimatorLeakFactor : 1.f; + 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 + - vad_level.rms_dbfs * vad_level.speech_probability; + rms_dbfs * speech_probability; preliminary_state_.level_dbfs.denominator = preliminary_state_.level_dbfs.denominator * leak_factor + - vad_level.speech_probability; + speech_probability; const float level_dbfs = preliminary_state_.level_dbfs.GetRatio(); @@ -131,14 +132,14 @@ bool AdaptiveModeLevelEstimator::IsConfident() const { void AdaptiveModeLevelEstimator::Reset() { ResetLevelEstimatorState(preliminary_state_); ResetLevelEstimatorState(reliable_state_); - level_dbfs_ = ClampLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs); + level_dbfs_ = initial_speech_level_dbfs_; num_adjacent_speech_frames_ = 0; } void AdaptiveModeLevelEstimator::ResetLevelEstimatorState( LevelEstimatorState& state) const { state.time_to_confidence_ms = kLevelEstimatorTimeToConfidenceMs; - state.level_dbfs.numerator = kInitialSpeechLevelEstimateDbfs; + state.level_dbfs.numerator = initial_speech_level_dbfs_; state.level_dbfs.denominator = 1.0f; } diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h index 6d44938587..989c8c3572 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h @@ -12,10 +12,11 @@ #define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_ #include + #include #include "modules/audio_processing/agc2/agc2_common.h" -#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/agc2/vad_wrapper.h" #include "modules/audio_processing/include/audio_processing.h" namespace webrtc { @@ -24,15 +25,15 @@ class ApmDataDumper; // Level estimator for the digital adaptive gain controller. class AdaptiveModeLevelEstimator { public: - explicit AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper); + AdaptiveModeLevelEstimator( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config); AdaptiveModeLevelEstimator(const AdaptiveModeLevelEstimator&) = delete; AdaptiveModeLevelEstimator& operator=(const AdaptiveModeLevelEstimator&) = delete; - AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper, - int adjacent_speech_frames_threshold); // Updates the level estimation. - void Update(const VadLevelAnalyzer::Result& vad_data); + 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. @@ -47,14 +48,13 @@ class AdaptiveModeLevelEstimator { 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; - }; - // TODO(crbug.com/webrtc/7494): Remove time_to_confidence_ms if redundant. - int time_to_confidence_ms; - Ratio level_dbfs; + } level_dbfs; }; static_assert(std::is_trivially_copyable::value, ""); @@ -64,6 +64,7 @@ class AdaptiveModeLevelEstimator { ApmDataDumper* const apm_data_dumper_; + const float initial_speech_level_dbfs_; const int adjacent_speech_frames_threshold_; LevelEstimatorState preliminary_state_; LevelEstimatorState reliable_state_; diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc index c55950ac29..684fca188a 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc @@ -13,73 +13,79 @@ #include #include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/gunit.h" namespace webrtc { namespace { +using AdaptiveDigitalConfig = + AudioProcessing::Config::GainController2::AdaptiveDigital; + // Number of speech frames that the level estimator must observe in order to // become confident about the estimated level. constexpr int kNumFramesToConfidence = kLevelEstimatorTimeToConfidenceMs / kFrameDurationMs; static_assert(kNumFramesToConfidence > 0, ""); -// Fake levels and speech probabilities used in the tests. -static_assert(kInitialSpeechLevelEstimateDbfs < 0.0f, ""); -constexpr float kVadLevelRms = kInitialSpeechLevelEstimateDbfs / 2.0f; -constexpr float kVadLevelPeak = kInitialSpeechLevelEstimateDbfs / 3.0f; -static_assert(kVadLevelRms < kVadLevelPeak, ""); -static_assert(kVadLevelRms > kInitialSpeechLevelEstimateDbfs, ""); -static_assert(kVadLevelRms - kInitialSpeechLevelEstimateDbfs > 5.0f, - "Adjust `kVadLevelRms` so that the difference from the initial " - "level is wide enough for the tests."); - -constexpr VadLevelAnalyzer::Result kVadDataSpeech{/*speech_probability=*/1.0f, - kVadLevelRms, kVadLevelPeak}; -constexpr VadLevelAnalyzer::Result kVadDataNonSpeech{ - /*speech_probability=*/kVadConfidenceThreshold / 2.0f, kVadLevelRms, - kVadLevelPeak}; - -constexpr float kMinSpeechProbability = 0.0f; -constexpr float kMaxSpeechProbability = 1.0f; - constexpr float kConvergenceSpeedTestsLevelTolerance = 0.5f; // Provides the `vad_level` value `num_iterations` times to `level_estimator`. void RunOnConstantLevel(int num_iterations, - const VadLevelAnalyzer::Result& vad_level, + float rms_dbfs, + float peak_dbfs, + float speech_probability, AdaptiveModeLevelEstimator& level_estimator) { for (int i = 0; i < num_iterations; ++i) { - level_estimator.Update(vad_level); + level_estimator.Update(rms_dbfs, peak_dbfs, speech_probability); } } +constexpr AdaptiveDigitalConfig GetAdaptiveDigitalConfig( + int adjacent_speech_frames_threshold) { + AdaptiveDigitalConfig config; + config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold; + return config; +} + +constexpr float kNoSpeechProbability = 0.0f; +constexpr float kLowSpeechProbability = kVadConfidenceThreshold / 2.0f; +constexpr float kMaxSpeechProbability = 1.0f; + // Level estimator with data dumper. struct TestLevelEstimator { - TestLevelEstimator() + explicit TestLevelEstimator(int adjacent_speech_frames_threshold) : data_dumper(0), estimator(std::make_unique( &data_dumper, - /*adjacent_speech_frames_threshold=*/1)) {} + GetAdaptiveDigitalConfig(adjacent_speech_frames_threshold))), + initial_speech_level_dbfs(estimator->level_dbfs()), + level_rms_dbfs(initial_speech_level_dbfs / 2.0f), + level_peak_dbfs(initial_speech_level_dbfs / 3.0f) { + RTC_DCHECK_LT(level_rms_dbfs, level_peak_dbfs); + RTC_DCHECK_LT(initial_speech_level_dbfs, level_rms_dbfs); + RTC_DCHECK_GT(level_rms_dbfs - initial_speech_level_dbfs, 5.0f) + << "Adjust `level_rms_dbfs` so that the difference from the initial " + "level is wide enough for the tests"; + } ApmDataDumper data_dumper; std::unique_ptr estimator; + const float initial_speech_level_dbfs; + const float level_rms_dbfs; + const float level_peak_dbfs; }; -// Checks the initially estimated level. -TEST(GainController2AdaptiveModeLevelEstimator, CheckInitialEstimate) { - TestLevelEstimator level_estimator; - EXPECT_FLOAT_EQ(level_estimator.estimator->level_dbfs(), - kInitialSpeechLevelEstimateDbfs); -} - // Checks that the level estimator converges to a constant input speech level. TEST(GainController2AdaptiveModeLevelEstimator, LevelStabilizes) { - TestLevelEstimator level_estimator; - RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech, + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); + RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, + level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, *level_estimator.estimator); const float estimated_level_dbfs = level_estimator.estimator->level_dbfs(); - RunOnConstantLevel(/*num_iterations=*/1, kVadDataSpeech, + RunOnConstantLevel(/*num_iterations=*/1, level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, *level_estimator.estimator); EXPECT_NEAR(level_estimator.estimator->level_dbfs(), estimated_level_dbfs, 0.1f); @@ -88,17 +94,21 @@ TEST(GainController2AdaptiveModeLevelEstimator, LevelStabilizes) { // Checks that the level controller does not become confident when too few // speech frames are observed. TEST(GainController2AdaptiveModeLevelEstimator, IsNotConfident) { - TestLevelEstimator level_estimator; + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence / 2, - kVadDataSpeech, *level_estimator.estimator); + level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, + *level_estimator.estimator); EXPECT_FALSE(level_estimator.estimator->IsConfident()); } // Checks that the level controller becomes confident when enough speech frames // are observed. TEST(GainController2AdaptiveModeLevelEstimator, IsConfident) { - TestLevelEstimator level_estimator; - RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech, + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); + RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, + level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, *level_estimator.estimator); EXPECT_TRUE(level_estimator.estimator->IsConfident()); } @@ -107,17 +117,17 @@ TEST(GainController2AdaptiveModeLevelEstimator, IsConfident) { // frames. TEST(GainController2AdaptiveModeLevelEstimator, EstimatorIgnoresNonSpeechFrames) { - TestLevelEstimator level_estimator; + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); // Simulate speech. - RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech, + RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, + level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, *level_estimator.estimator); const float estimated_level_dbfs = level_estimator.estimator->level_dbfs(); // Simulate full-scale non-speech. RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, - VadLevelAnalyzer::Result{kMinSpeechProbability, - /*rms_dbfs=*/0.0f, - /*peak_dbfs=*/0.0f}, - *level_estimator.estimator); + /*rms_dbfs=*/0.0f, /*peak_dbfs=*/0.0f, + kNoSpeechProbability, *level_estimator.estimator); // No estimated level change is expected. EXPECT_FLOAT_EQ(level_estimator.estimator->level_dbfs(), estimated_level_dbfs); @@ -126,28 +136,29 @@ TEST(GainController2AdaptiveModeLevelEstimator, // Checks the convergence speed of the estimator before it becomes confident. TEST(GainController2AdaptiveModeLevelEstimator, ConvergenceSpeedBeforeConfidence) { - TestLevelEstimator level_estimator; - RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech, + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); + RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, + level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, kMaxSpeechProbability, *level_estimator.estimator); - EXPECT_NEAR(level_estimator.estimator->level_dbfs(), kVadDataSpeech.rms_dbfs, + EXPECT_NEAR(level_estimator.estimator->level_dbfs(), + level_estimator.level_rms_dbfs, kConvergenceSpeedTestsLevelTolerance); } // Checks the convergence speed of the estimator after it becomes confident. TEST(GainController2AdaptiveModeLevelEstimator, ConvergenceSpeedAfterConfidence) { - TestLevelEstimator level_estimator; + TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1); // Reach confidence using the initial level estimate. RunOnConstantLevel( /*num_iterations=*/kNumFramesToConfidence, - VadLevelAnalyzer::Result{ - kMaxSpeechProbability, - /*rms_dbfs=*/kInitialSpeechLevelEstimateDbfs, - /*peak_dbfs=*/kInitialSpeechLevelEstimateDbfs + 6.0f}, - *level_estimator.estimator); + /*rms_dbfs=*/level_estimator.initial_speech_level_dbfs, + /*peak_dbfs=*/level_estimator.initial_speech_level_dbfs + 6.0f, + kMaxSpeechProbability, *level_estimator.estimator); // No estimate change should occur, but confidence is achieved. ASSERT_FLOAT_EQ(level_estimator.estimator->level_dbfs(), - kInitialSpeechLevelEstimateDbfs); + level_estimator.initial_speech_level_dbfs); ASSERT_TRUE(level_estimator.estimator->IsConfident()); // After confidence. constexpr float kConvergenceTimeAfterConfidenceNumFrames = 600; // 6 seconds. @@ -155,8 +166,10 @@ TEST(GainController2AdaptiveModeLevelEstimator, kConvergenceTimeAfterConfidenceNumFrames > kNumFramesToConfidence, ""); RunOnConstantLevel( /*num_iterations=*/kConvergenceTimeAfterConfidenceNumFrames, - kVadDataSpeech, *level_estimator.estimator); - EXPECT_NEAR(level_estimator.estimator->level_dbfs(), kVadDataSpeech.rms_dbfs, + level_estimator.level_rms_dbfs, level_estimator.level_peak_dbfs, + kMaxSpeechProbability, *level_estimator.estimator); + EXPECT_NEAR(level_estimator.estimator->level_dbfs(), + level_estimator.level_rms_dbfs, kConvergenceSpeedTestsLevelTolerance); } @@ -168,30 +181,32 @@ class AdaptiveModeLevelEstimatorParametrization TEST_P(AdaptiveModeLevelEstimatorParametrization, DoNotAdaptToShortSpeechSegments) { - ApmDataDumper apm_data_dumper(0); - AdaptiveModeLevelEstimator level_estimator( - &apm_data_dumper, adjacent_speech_frames_threshold()); - const float initial_level = level_estimator.level_dbfs(); - ASSERT_LT(initial_level, kVadDataSpeech.peak_dbfs); + TestLevelEstimator level_estimator(adjacent_speech_frames_threshold()); + const float initial_level = level_estimator.estimator->level_dbfs(); + ASSERT_LT(initial_level, level_estimator.level_peak_dbfs); for (int i = 0; i < adjacent_speech_frames_threshold() - 1; ++i) { SCOPED_TRACE(i); - level_estimator.Update(kVadDataSpeech); - EXPECT_EQ(initial_level, level_estimator.level_dbfs()); + level_estimator.estimator->Update(level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, + kMaxSpeechProbability); + EXPECT_EQ(initial_level, level_estimator.estimator->level_dbfs()); } - level_estimator.Update(kVadDataNonSpeech); - EXPECT_EQ(initial_level, level_estimator.level_dbfs()); + level_estimator.estimator->Update(level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, + kLowSpeechProbability); + EXPECT_EQ(initial_level, level_estimator.estimator->level_dbfs()); } TEST_P(AdaptiveModeLevelEstimatorParametrization, AdaptToEnoughSpeechSegments) { - ApmDataDumper apm_data_dumper(0); - AdaptiveModeLevelEstimator level_estimator( - &apm_data_dumper, adjacent_speech_frames_threshold()); - const float initial_level = level_estimator.level_dbfs(); - ASSERT_LT(initial_level, kVadDataSpeech.peak_dbfs); + TestLevelEstimator level_estimator(adjacent_speech_frames_threshold()); + const float initial_level = level_estimator.estimator->level_dbfs(); + ASSERT_LT(initial_level, level_estimator.level_peak_dbfs); for (int i = 0; i < adjacent_speech_frames_threshold(); ++i) { - level_estimator.Update(kVadDataSpeech); + level_estimator.estimator->Update(level_estimator.level_rms_dbfs, + level_estimator.level_peak_dbfs, + kMaxSpeechProbability); } - EXPECT_LT(initial_level, level_estimator.level_dbfs()); + EXPECT_LT(initial_level, level_estimator.estimator->level_dbfs()); } INSTANTIATE_TEST_SUITE_P(GainController2, diff --git a/modules/audio_processing/agc2/agc2_common.h b/modules/audio_processing/agc2/agc2_common.h index adb1614926..4af85527b8 100644 --- a/modules/audio_processing/agc2/agc2_common.h +++ b/modules/audio_processing/agc2/agc2_common.h @@ -24,40 +24,26 @@ constexpr int kFrameDurationMs = 10; constexpr int kSubFramesInFrame = 20; constexpr int kMaximalNumberOfSamplesPerChannel = 480; -// Adaptive digital gain applier settings below. -constexpr float kHeadroomDbfs = 1.0f; -constexpr float kMaxGainDb = 30.0f; -constexpr float kInitialAdaptiveDigitalGainDb = 8.0f; +// Adaptive digital gain applier settings. + // At what limiter levels should we start decreasing the adaptive digital gain. -constexpr float kLimiterThresholdForAgcGainDbfs = -kHeadroomDbfs; +constexpr float kLimiterThresholdForAgcGainDbfs = -1.0f; // This is the threshold for speech. Speech frames are used for updating the // speech level, measuring the amount of speech, and decide when to allow target -// gain reduction. +// gain changes. constexpr float kVadConfidenceThreshold = 0.95f; -// Adaptive digital level estimator parameters. // 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; -// Robust VAD probability and speech decisions. -constexpr int kDefaultLevelEstimatorAdjacentSpeechFramesThreshold = 12; - // Saturation Protector settings. constexpr float kSaturationProtectorInitialHeadroomDb = 20.0f; -constexpr float kSaturationProtectorExtraHeadroomDb = 5.0f; constexpr int kSaturationProtectorBufferSize = 4; -// Set the initial speech level estimate so that `kInitialAdaptiveDigitalGainDb` -// is applied at the beginning of the call. -constexpr float kInitialSpeechLevelEstimateDbfs = - -kSaturationProtectorExtraHeadroomDb - - kSaturationProtectorInitialHeadroomDb - kInitialAdaptiveDigitalGainDb - - kHeadroomDbfs; - // 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. diff --git a/modules/audio_processing/agc2/agc2_testing_common.h b/modules/audio_processing/agc2/agc2_testing_common.h index 4572d9cffd..afed97e83b 100644 --- a/modules/audio_processing/agc2/agc2_testing_common.h +++ b/modules/audio_processing/agc2/agc2_testing_common.h @@ -25,7 +25,7 @@ constexpr float kMaxS16 = static_cast(std::numeric_limits::max()); // Level Estimator test parameters. -constexpr float kDecayMs = 500.f; +constexpr float kDecayMs = 20.0f; // Limiter parameters. constexpr float kLimiterMaxInputLevelDbFs = 1.f; diff --git a/modules/audio_processing/agc2/biquad_filter.cc b/modules/audio_processing/agc2/biquad_filter.cc index ccb7807e6b..453125fde7 100644 --- a/modules/audio_processing/agc2/biquad_filter.cc +++ b/modules/audio_processing/agc2/biquad_filter.cc @@ -10,26 +10,37 @@ #include "modules/audio_processing/agc2/biquad_filter.h" -#include +#include "rtc_base/arraysize.h" namespace webrtc { -// Transposed direct form I implementation of a bi-quad filter applied to an -// input signal `x` to produce an output signal `y`. +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(rtc::ArrayView x, rtc::ArrayView y) { + RTC_DCHECK_EQ(x.size(), y.size()); for (size_t k = 0; k < x.size(); ++k) { - // Use temporary variable for x[k] to allow in-place function call - // (that x and y refer to the same array). + // Use a temporary variable for `x[k]` to allow in-place processing. const float tmp = x[k]; - y[k] = coefficients_.b[0] * tmp + coefficients_.b[1] * biquad_state_.b[0] + - coefficients_.b[2] * biquad_state_.b[1] - - coefficients_.a[0] * biquad_state_.a[0] - - coefficients_.a[1] * biquad_state_.a[1]; - biquad_state_.b[1] = biquad_state_.b[0]; - biquad_state_.b[0] = tmp; - biquad_state_.a[1] = biquad_state_.a[0]; - biquad_state_.a[0] = y[k]; + y[k] = config_.b[0] * tmp + config_.b[1] * state_.b[0] + + config_.b[2] * state_.b[1] - config_.a[0] * state_.a[0] - + config_.a[1] * state_.a[1]; + state_.b[1] = state_.b[0]; + state_.b[0] = tmp; + state_.a[1] = state_.a[0]; + state_.a[0] = y[k]; } } diff --git a/modules/audio_processing/agc2/biquad_filter.h b/modules/audio_processing/agc2/biquad_filter.h index 7bf3301e4b..5273ff9386 100644 --- a/modules/audio_processing/agc2/biquad_filter.h +++ b/modules/audio_processing/agc2/biquad_filter.h @@ -11,54 +11,44 @@ #ifndef MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ #define MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ -#include - #include "api/array_view.h" -#include "rtc_base/arraysize.h" -#include "rtc_base/constructor_magic.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. - // b_0 + b_1 • z^(-1) + b_2 • z^(-2) - // H(z) = --------------------------------- - // 1 + a_1 • z^(-1) + a_2 • z^(-2) - struct BiQuadCoefficients { - float b[3]; - float a[2]; + // 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]. }; - BiQuadFilter() = default; + explicit BiQuadFilter(const Config& config); + BiQuadFilter(const BiQuadFilter&) = delete; + BiQuadFilter& operator=(const BiQuadFilter&) = delete; + ~BiQuadFilter(); - void Initialize(const BiQuadCoefficients& coefficients) { - coefficients_ = coefficients; - } + // Sets the filter configuration and resets the internal state. + void SetConfig(const Config& config); - void Reset() { biquad_state_.Reset(); } + // Zeroes the filter state. + void Reset(); - // Produces a filtered output y of the input x. Both x and y need to - // have the same length. In-place modification is allowed. + // Filters `x` and writes the output in `y`, which must have the same length + // of `x`. In-place processing is supported. void Process(rtc::ArrayView x, rtc::ArrayView y); private: - struct BiQuadState { - BiQuadState() { Reset(); } - - void Reset() { - std::fill(b, b + arraysize(b), 0.f); - std::fill(a, a + arraysize(a), 0.f); - } - + Config config_; + struct State { float b[2]; float a[2]; - }; - - BiQuadState biquad_state_; - BiQuadCoefficients coefficients_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BiQuadFilter); + } state_; }; } // namespace webrtc diff --git a/modules/audio_processing/agc2/biquad_filter_unittest.cc b/modules/audio_processing/agc2/biquad_filter_unittest.cc index 55ca1a5acc..a53036b08e 100644 --- a/modules/audio_processing/agc2/biquad_filter_unittest.cc +++ b/modules/audio_processing/agc2/biquad_filter_unittest.cc @@ -19,11 +19,10 @@ #include "rtc_base/gunit.h" namespace webrtc { -namespace test { namespace { -constexpr size_t kFrameSize = 8; -constexpr size_t kNumFrames = 4; +constexpr int kFrameSize = 8; +constexpr int kNumFrames = 4; using FloatArraySequence = std::array, kNumFrames>; @@ -37,8 +36,8 @@ constexpr FloatArraySequence kBiQuadInputSeq = { {{22.645832f, -64.597153f, 55.462521f, -109.393188f, 10.117825f, -40.019642f, -98.612228f, -8.330326f}}}}; -// Generated via "B, A = scipy.signal.butter(2, 30/12000, btype='highpass')" -const BiQuadFilter::BiQuadCoefficients kBiQuadConfig = { +// Computed as `scipy.signal.butter(N=2, Wn=60/24000, btype='highpass')`. +constexpr BiQuadFilter::Config kBiQuadConfig{ {0.99446179f, -1.98892358f, 0.99446179f}, {-1.98889291f, 0.98895425f}}; @@ -57,22 +56,23 @@ constexpr FloatArraySequence kBiQuadOutputSeq = { {{24.84286614f, -62.18094158f, 57.91488056f, -106.65685933f, 13.38760103f, -36.60367134f, -94.44880104f, -3.59920354f}}}}; -// Fail for every pair from two equally sized rtc::ArrayView views such +// Fails for every pair from two equally sized rtc::ArrayView views such // that their relative error is above a given threshold. If the expected value -// of a pair is 0, the tolerance is used to check the absolute error. +// of a pair is 0, `tolerance` is used to check the absolute error. void ExpectNearRelative(rtc::ArrayView expected, rtc::ArrayView computed, const float tolerance) { // The relative error is undefined when the expected value is 0. // When that happens, check the absolute error instead. `safe_den` is used // below to implement such logic. - auto safe_den = [](float x) { return (x == 0.f) ? 1.f : std::fabs(x); }; + auto safe_den = [](float x) { return (x == 0.0f) ? 1.0f : std::fabs(x); }; ASSERT_EQ(expected.size(), computed.size()); for (size_t i = 0; i < expected.size(); ++i) { const float abs_diff = std::fabs(expected[i] - computed[i]); // No failure when the values are equal. - if (abs_diff == 0.f) + if (abs_diff == 0.0f) { continue; + } SCOPED_TRACE(i); SCOPED_TRACE(expected[i]); SCOPED_TRACE(computed[i]); @@ -80,32 +80,32 @@ void ExpectNearRelative(rtc::ArrayView expected, } } -} // namespace - +// Checks that filtering works when different containers are used both as input +// and as output. TEST(BiQuadFilterTest, FilterNotInPlace) { - BiQuadFilter filter; - filter.Initialize(kBiQuadConfig); + BiQuadFilter filter(kBiQuadConfig); std::array samples; // TODO(https://bugs.webrtc.org/8948): Add when the issue is fixed. // FloatingPointExceptionObserver fpe_observer; - for (size_t i = 0; i < kNumFrames; ++i) { + for (int i = 0; i < kNumFrames; ++i) { SCOPED_TRACE(i); filter.Process(kBiQuadInputSeq[i], samples); ExpectNearRelative(kBiQuadOutputSeq[i], samples, 2e-4f); } } +// Checks that filtering works when the same container is used both as input and +// as output. TEST(BiQuadFilterTest, FilterInPlace) { - BiQuadFilter filter; - filter.Initialize(kBiQuadConfig); + BiQuadFilter filter(kBiQuadConfig); std::array samples; // TODO(https://bugs.webrtc.org/8948): Add when the issue is fixed. // FloatingPointExceptionObserver fpe_observer; - for (size_t i = 0; i < kNumFrames; ++i) { + for (int i = 0; i < kNumFrames; ++i) { SCOPED_TRACE(i); std::copy(kBiQuadInputSeq[i].begin(), kBiQuadInputSeq[i].end(), samples.begin()); @@ -114,23 +114,62 @@ TEST(BiQuadFilterTest, FilterInPlace) { } } -TEST(BiQuadFilterTest, Reset) { - BiQuadFilter filter; - filter.Initialize(kBiQuadConfig); +// Checks that different configurations produce different outputs. +TEST(BiQuadFilterTest, SetConfigDifferentOutput) { + BiQuadFilter filter(/*config=*/{{0.97803048f, -1.95606096f, 0.97803048f}, + {-1.95557824f, 0.95654368f}}); std::array samples1; - for (size_t i = 0; i < kNumFrames; ++i) { + for (int i = 0; i < kNumFrames; ++i) { filter.Process(kBiQuadInputSeq[i], samples1); } - filter.Reset(); + filter.SetConfig( + {{0.09763107f, 0.19526215f, 0.09763107f}, {-0.94280904f, 0.33333333f}}); std::array samples2; - for (size_t i = 0; i < kNumFrames; ++i) { + for (int i = 0; i < kNumFrames; ++i) { + filter.Process(kBiQuadInputSeq[i], samples2); + } + + EXPECT_NE(samples1, samples2); +} + +// Checks that when `SetConfig()` is called but the filter coefficients are the +// same the filter state is reset. +TEST(BiQuadFilterTest, SetConfigResetsState) { + BiQuadFilter filter(kBiQuadConfig); + + std::array samples1; + for (int i = 0; i < kNumFrames; ++i) { + filter.Process(kBiQuadInputSeq[i], samples1); + } + + filter.SetConfig(kBiQuadConfig); + std::array samples2; + for (int i = 0; i < kNumFrames; ++i) { filter.Process(kBiQuadInputSeq[i], samples2); } EXPECT_EQ(samples1, samples2); } -} // namespace test +// Checks that when `Reset()` is called the filter state is reset. +TEST(BiQuadFilterTest, Reset) { + BiQuadFilter filter(kBiQuadConfig); + + std::array samples1; + for (int i = 0; i < kNumFrames; ++i) { + filter.Process(kBiQuadInputSeq[i], samples1); + } + + filter.Reset(); + std::array samples2; + for (int i = 0; i < kNumFrames; ++i) { + filter.Process(kBiQuadInputSeq[i], samples2); + } + + EXPECT_EQ(samples1, samples2); +} + +} // namespace } // namespace webrtc diff --git a/modules/audio_processing/agc2/fixed_digital_level_estimator.cc b/modules/audio_processing/agc2/fixed_digital_level_estimator.cc index eb8a64a9b5..1995b24913 100644 --- a/modules/audio_processing/agc2/fixed_digital_level_estimator.cc +++ b/modules/audio_processing/agc2/fixed_digital_level_estimator.cc @@ -20,15 +20,16 @@ namespace webrtc { namespace { -constexpr float kInitialFilterStateLevel = 0.f; +constexpr float kInitialFilterStateLevel = 0.0f; // Instant attack. -constexpr float kAttackFilterConstant = 0.f; -// This is computed from kDecayMs by -// 10 ** (-1/20 * subframe_duration / kDecayMs). -// `subframe_duration` is |kFrameDurationMs / kSubFramesInFrame|. -// kDecayMs is defined in agc2_testing_common.h -constexpr float kDecayFilterConstant = 0.9998848773724686f; +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 @@ -57,7 +58,7 @@ std::array FixedDigitalLevelEstimator::ComputeLevel( // Compute max envelope without smoothing. std::array envelope{}; - for (size_t channel_idx = 0; channel_idx < float_frame.num_channels(); + for (int channel_idx = 0; channel_idx < float_frame.num_channels(); ++channel_idx) { const auto channel = float_frame.channel(channel_idx); for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { diff --git a/modules/audio_processing/agc2/fixed_digital_level_estimator.h b/modules/audio_processing/agc2/fixed_digital_level_estimator.h index d96aedaf9e..d26b55950c 100644 --- a/modules/audio_processing/agc2/fixed_digital_level_estimator.h +++ b/modules/audio_processing/agc2/fixed_digital_level_estimator.h @@ -16,7 +16,6 @@ #include "modules/audio_processing/agc2/agc2_common.h" #include "modules/audio_processing/include/audio_frame_view.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,10 @@ class FixedDigitalLevelEstimator { FixedDigitalLevelEstimator(int sample_rate_hz, 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 @@ -57,8 +60,6 @@ class FixedDigitalLevelEstimator { float filter_state_level_; int samples_in_frame_; int samples_in_sub_frame_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FixedDigitalLevelEstimator); }; } // namespace webrtc diff --git a/modules/audio_processing/agc2/gain_applier.cc b/modules/audio_processing/agc2/gain_applier.cc index 8c437177e3..f9e276d3a8 100644 --- a/modules/audio_processing/agc2/gain_applier.cc +++ b/modules/audio_processing/agc2/gain_applier.cc @@ -25,7 +25,7 @@ bool GainCloseToOne(float gain_factor) { } void ClipSignal(AudioFrameView signal) { - for (size_t k = 0; k < signal.num_channels(); ++k) { + for (int k = 0; k < signal.num_channels(); ++k) { rtc::ArrayView channel_view = signal.channel(k); for (auto& sample : channel_view) { sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); @@ -45,7 +45,7 @@ void ApplyGainWithRamping(float last_gain_linear, // 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) { + for (int k = 0; k < float_frame.num_channels(); ++k) { rtc::ArrayView channel_view = float_frame.channel(k); for (auto& sample : channel_view) { sample *= gain_at_end_of_frame_linear; @@ -58,8 +58,8 @@ void ApplyGainWithRamping(float last_gain_linear, const float increment = (gain_at_end_of_frame_linear - last_gain_linear) * inverse_samples_per_channel; float gain = last_gain_linear; - for (size_t i = 0; i < float_frame.samples_per_channel(); ++i) { - for (size_t ch = 0; ch < float_frame.num_channels(); ++ch) { + for (int i = 0; i < float_frame.samples_per_channel(); ++i) { + for (int ch = 0; ch < float_frame.num_channels(); ++ch) { float_frame.channel(ch)[i] *= gain; } gain += increment; @@ -88,12 +88,13 @@ void GainApplier::ApplyGain(AudioFrameView 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(size_t samples_per_channel) { +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_; diff --git a/modules/audio_processing/agc2/gain_applier.h b/modules/audio_processing/agc2/gain_applier.h index d9aa19d1aa..ba8a4a4cd2 100644 --- a/modules/audio_processing/agc2/gain_applier.h +++ b/modules/audio_processing/agc2/gain_applier.h @@ -25,7 +25,7 @@ class GainApplier { float GetGainFactor() const { return current_gain_factor_; } private: - void Initialize(size_t samples_per_channel); + void Initialize(int samples_per_channel); // Whether to clip samples after gain is applied. If 'true', result // will fit in FloatS16 range. diff --git a/modules/audio_processing/agc2/interpolated_gain_curve.cc b/modules/audio_processing/agc2/interpolated_gain_curve.cc index ac7fbecb8e..b522ec372c 100644 --- a/modules/audio_processing/agc2/interpolated_gain_curve.cc +++ b/modules/audio_processing/agc2/interpolated_gain_curve.cc @@ -115,7 +115,7 @@ void InterpolatedGainCurve::RegionLogger::LogRegionStats( break; } default: { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } } diff --git a/modules/audio_processing/agc2/interpolated_gain_curve.h b/modules/audio_processing/agc2/interpolated_gain_curve.h index af993204ce..b1a5cf473b 100644 --- a/modules/audio_processing/agc2/interpolated_gain_curve.h +++ b/modules/audio_processing/agc2/interpolated_gain_curve.h @@ -15,7 +15,6 @@ #include #include "modules/audio_processing/agc2/agc2_common.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/gtest_prod_util.h" #include "system_wrappers/include/metrics.h" @@ -64,6 +63,9 @@ class InterpolatedGainCurve { const std::string& 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 @@ -143,8 +145,6 @@ class InterpolatedGainCurve { // Stats. mutable Stats stats_; - - RTC_DISALLOW_COPY_AND_ASSIGN(InterpolatedGainCurve); }; } // namespace webrtc diff --git a/modules/audio_processing/agc2/limiter.cc b/modules/audio_processing/agc2/limiter.cc index ed7d3ee5f2..57580924dc 100644 --- a/modules/audio_processing/agc2/limiter.cc +++ b/modules/audio_processing/agc2/limiter.cc @@ -18,6 +18,7 @@ #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 { @@ -29,14 +30,14 @@ namespace { // 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.f; +constexpr float kAttackFirstSubframeInterpolationPower = 8.0f; void InterpolateFirstSubframe(float last_factor, float current_factor, rtc::ArrayView subframe) { - const auto n = subframe.size(); - constexpr auto p = kAttackFirstSubframeInterpolationPower; - for (size_t i = 0; i < n; ++i) { + const int n = rtc::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; } @@ -44,10 +45,10 @@ void InterpolateFirstSubframe(float last_factor, void ComputePerSampleSubframeFactors( const std::array& scaling_factors, - size_t samples_per_channel, + int samples_per_channel, rtc::ArrayView per_sample_scaling_factors) { - const size_t num_subframes = scaling_factors.size() - 1; - const size_t subframe_size = + const int num_subframes = scaling_factors.size() - 1; + const int subframe_size = rtc::CheckedDivExact(samples_per_channel, num_subframes); // Handle first sub-frame differently in case of attack. @@ -59,12 +60,12 @@ void ComputePerSampleSubframeFactors( per_sample_scaling_factors.subview(0, subframe_size))); } - for (size_t i = is_attack ? 1 : 0; i < num_subframes; ++i) { - const size_t subframe_start = i * subframe_size; + for (int 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 (size_t j = 0; j < subframe_size; ++j) { + for (int j = 0; j < subframe_size; ++j) { per_sample_scaling_factors[subframe_start + j] = scaling_start + scaling_diff * j; } @@ -73,18 +74,18 @@ void ComputePerSampleSubframeFactors( void ScaleSamples(rtc::ArrayView per_sample_scaling_factors, AudioFrameView signal) { - const size_t samples_per_channel = signal.samples_per_channel(); + const int samples_per_channel = signal.samples_per_channel(); RTC_DCHECK_EQ(samples_per_channel, per_sample_scaling_factors.size()); - for (size_t i = 0; i < signal.num_channels(); ++i) { - auto channel = signal.channel(i); - for (size_t j = 0; j < samples_per_channel; ++j) { + for (int i = 0; i < signal.num_channels(); ++i) { + rtc::ArrayView channel = signal.channel(i); + for (int j = 0; j < samples_per_channel; ++j) { channel[j] = rtc::SafeClamp(channel[j] * per_sample_scaling_factors[j], kMinFloatS16Value, kMaxFloatS16Value); } } } -void CheckLimiterSampleRate(size_t sample_rate_hz) { +void CheckLimiterSampleRate(int sample_rate_hz) { // Check that per_sample_scaling_factors_ is large enough. RTC_DCHECK_LE(sample_rate_hz, kMaximalNumberOfSamplesPerChannel * 1000 / kFrameDurationMs); @@ -92,7 +93,7 @@ void CheckLimiterSampleRate(size_t sample_rate_hz) { } // namespace -Limiter::Limiter(size_t sample_rate_hz, +Limiter::Limiter(int sample_rate_hz, ApmDataDumper* apm_data_dumper, const std::string& histogram_name) : interp_gain_curve_(apm_data_dumper, histogram_name), @@ -104,7 +105,8 @@ Limiter::Limiter(size_t sample_rate_hz, Limiter::~Limiter() = default; void Limiter::Process(AudioFrameView signal) { - const auto level_estimate = level_estimator_.ComputeLevel(signal); + 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_; @@ -113,7 +115,7 @@ void Limiter::Process(AudioFrameView signal) { return interp_gain_curve_.LookUpGainToApply(x); }); - const size_t samples_per_channel = signal.samples_per_channel(); + const int samples_per_channel = signal.samples_per_channel(); RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel); auto per_sample_scaling_factors = rtc::ArrayView( @@ -136,7 +138,7 @@ InterpolatedGainCurve::Stats Limiter::GetGainCurveStats() const { return interp_gain_curve_.get_stats(); } -void Limiter::SetSampleRate(size_t sample_rate_hz) { +void Limiter::SetSampleRate(int sample_rate_hz) { CheckLimiterSampleRate(sample_rate_hz); level_estimator_.SetSampleRate(sample_rate_hz); } diff --git a/modules/audio_processing/agc2/limiter.h b/modules/audio_processing/agc2/limiter.h index f8eec3dc79..669e202c20 100644 --- a/modules/audio_processing/agc2/limiter.h +++ b/modules/audio_processing/agc2/limiter.h @@ -17,14 +17,13 @@ #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" -#include "rtc_base/constructor_magic.h" namespace webrtc { class ApmDataDumper; class Limiter { public: - Limiter(size_t sample_rate_hz, + Limiter(int sample_rate_hz, ApmDataDumper* apm_data_dumper, const std::string& histogram_name_prefix); Limiter(const Limiter& limiter) = delete; @@ -40,7 +39,7 @@ class Limiter { // * below kMaximalNumberOfSamplesPerChannel*1000/kFrameDurationMs // so that samples_per_channel fit in the // per_sample_scaling_factors_ array. - void SetSampleRate(size_t sample_rate_hz); + void SetSampleRate(int sample_rate_hz); // Resets the internal state. void Reset(); diff --git a/modules/audio_processing/agc2/noise_level_estimator.cc b/modules/audio_processing/agc2/noise_level_estimator.cc index 3d9aa1a4ae..9fb1c24b65 100644 --- a/modules/audio_processing/agc2/noise_level_estimator.cc +++ b/modules/audio_processing/agc2/noise_level_estimator.cc @@ -27,7 +27,7 @@ constexpr int kFramesPerSecond = 100; float FrameEnergy(const AudioFrameView& audio) { float energy = 0.0f; - for (size_t k = 0; k < audio.num_channels(); ++k) { + for (int k = 0; k < audio.num_channels(); ++k) { float channel_energy = std::accumulate(audio.channel(k).begin(), audio.channel(k).end(), 0.0f, [](float a, float b) -> float { return a + b * b; }); diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn index bc848b3e13..f0d7093783 100644 --- a/modules/audio_processing/agc2/rnn_vad/BUILD.gn +++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn @@ -57,7 +57,7 @@ rtc_source_set("rnn_vad_common") { # TODO(alessiob): Make this target visibility private. visibility = [ ":*", - "..:rnn_vad_with_level", + "..:vad_wrapper", ] sources = [ "common.h" ] deps = [ diff --git a/modules/audio_processing/agc2/rnn_vad/features_extraction.cc b/modules/audio_processing/agc2/rnn_vad/features_extraction.cc index 5c276c87a9..502023428d 100644 --- a/modules/audio_processing/agc2/rnn_vad/features_extraction.cc +++ b/modules/audio_processing/agc2/rnn_vad/features_extraction.cc @@ -19,8 +19,8 @@ namespace webrtc { namespace rnn_vad { namespace { -// Generated via "B, A = scipy.signal.butter(2, 30/12000, btype='highpass')" -const BiQuadFilter::BiQuadCoefficients kHpfConfig24k = { +// 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}}; @@ -28,6 +28,7 @@ const BiQuadFilter::BiQuadCoefficients kHpfConfig24k = { 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), @@ -35,7 +36,6 @@ FeaturesExtractor::FeaturesExtractor(const AvailableCpuFeatures& cpu_features) pitch_estimator_(cpu_features), reference_frame_view_(pitch_buf_24kHz_.GetMostRecentValuesView()) { RTC_DCHECK_EQ(kBufSize24kHz, lp_residual_.size()); - hpf_.Initialize(kHpfConfig24k); Reset(); } @@ -44,8 +44,9 @@ FeaturesExtractor::~FeaturesExtractor() = default; void FeaturesExtractor::Reset() { pitch_buf_24kHz_.Reset(); spectral_features_extractor_.Reset(); - if (use_high_pass_filter_) + if (use_high_pass_filter_) { hpf_.Reset(); + } } bool FeaturesExtractor::CheckSilenceComputeFeatures( diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc index 9f41e96e5e..324d694957 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc @@ -118,12 +118,11 @@ TEST(RnnVadTest, CepstralFeaturesConstantAverageZeroDerivative) { std::array samples; rtc::ArrayView samples_view(samples); WriteTestData(samples); - bool is_silence; // Fill the spectral features with test data. std::array feature_vector; for (int i = 0; i < kCepstralCoeffsHistorySize; ++i) { - is_silence = sfe.CheckSilenceComputeFeatures( + sfe.CheckSilenceComputeFeatures( samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector), GetAverage(&feature_vector), GetFirstDerivative(&feature_vector), GetSecondDerivative(&feature_vector), @@ -133,7 +132,7 @@ TEST(RnnVadTest, CepstralFeaturesConstantAverageZeroDerivative) { // Feed the test data one last time but using a different output vector. std::array feature_vector_last; - is_silence = sfe.CheckSilenceComputeFeatures( + sfe.CheckSilenceComputeFeatures( samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector_last), GetAverage(&feature_vector_last), GetFirstDerivative(&feature_vector_last), diff --git a/modules/audio_processing/agc2/saturation_protector.cc b/modules/audio_processing/agc2/saturation_protector.cc index d6f21ef891..961baf4cd3 100644 --- a/modules/audio_processing/agc2/saturation_protector.cc +++ b/modules/audio_processing/agc2/saturation_protector.cc @@ -95,12 +95,10 @@ void UpdateSaturationProtectorState(float peak_dbfs, class SaturationProtectorImpl : public SaturationProtector { public: explicit SaturationProtectorImpl(float initial_headroom_db, - float extra_headroom_db, int adjacent_speech_frames_threshold, ApmDataDumper* apm_data_dumper) : apm_data_dumper_(apm_data_dumper), initial_headroom_db_(initial_headroom_db), - extra_headroom_db_(extra_headroom_db), adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold) { Reset(); } @@ -140,7 +138,7 @@ class SaturationProtectorImpl : public SaturationProtector { if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { // `preliminary_state_` is now reliable. Update the headroom. - headroom_db_ = preliminary_state_.headroom_db + extra_headroom_db_; + headroom_db_ = preliminary_state_.headroom_db; } } DumpDebugData(); @@ -148,7 +146,7 @@ class SaturationProtectorImpl : public SaturationProtector { void Reset() override { num_adjacent_speech_frames_ = 0; - headroom_db_ = initial_headroom_db_ + extra_headroom_db_; + headroom_db_ = initial_headroom_db_; ResetSaturationProtectorState(initial_headroom_db_, preliminary_state_); ResetSaturationProtectorState(initial_headroom_db_, reliable_state_); } @@ -165,7 +163,6 @@ class SaturationProtectorImpl : public SaturationProtector { ApmDataDumper* const apm_data_dumper_; const float initial_headroom_db_; - const float extra_headroom_db_; const int adjacent_speech_frames_threshold_; int num_adjacent_speech_frames_; float headroom_db_; @@ -177,12 +174,10 @@ class SaturationProtectorImpl : public SaturationProtector { std::unique_ptr CreateSaturationProtector( float initial_headroom_db, - float extra_headroom_db, int adjacent_speech_frames_threshold, ApmDataDumper* apm_data_dumper) { return std::make_unique( - initial_headroom_db, extra_headroom_db, adjacent_speech_frames_threshold, - apm_data_dumper); + initial_headroom_db, adjacent_speech_frames_threshold, apm_data_dumper); } } // namespace webrtc diff --git a/modules/audio_processing/agc2/saturation_protector.h b/modules/audio_processing/agc2/saturation_protector.h index 0c384f1fa0..ef22145d5f 100644 --- a/modules/audio_processing/agc2/saturation_protector.h +++ b/modules/audio_processing/agc2/saturation_protector.h @@ -38,7 +38,6 @@ class SaturationProtector { // Creates a saturation protector that starts at `initial_headroom_db`. std::unique_ptr CreateSaturationProtector( float initial_headroom_db, - float extra_headroom_db, int adjacent_speech_frames_threshold, ApmDataDumper* apm_data_dumper); diff --git a/modules/audio_processing/agc2/saturation_protector_unittest.cc b/modules/audio_processing/agc2/saturation_protector_unittest.cc index dc16dc254c..3b104be8cd 100644 --- a/modules/audio_processing/agc2/saturation_protector_unittest.cc +++ b/modules/audio_processing/agc2/saturation_protector_unittest.cc @@ -18,7 +18,6 @@ namespace webrtc { namespace { constexpr float kInitialHeadroomDb = 20.0f; -constexpr float kNoExtraHeadroomDb = 0.0f; constexpr int kNoAdjacentSpeechFramesRequired = 1; constexpr float kMaxSpeechProbability = 1.0f; @@ -47,8 +46,7 @@ float RunOnConstantLevel(int num_iterations, TEST(GainController2SaturationProtector, Reset) { ApmDataDumper apm_data_dumper(0); auto saturation_protector = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, kNoAdjacentSpeechFramesRequired, - &apm_data_dumper); + kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); const float initial_headroom_db = saturation_protector->HeadroomDb(); RunOnConstantLevel(/*num_iterations=*/10, kMaxSpeechProbability, /*peak_dbfs=*/0.0f, @@ -71,43 +69,13 @@ TEST(GainController2SaturationProtector, EstimatesCrestRatio) { ApmDataDumper apm_data_dumper(0); auto saturation_protector = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, kNoAdjacentSpeechFramesRequired, - &apm_data_dumper); + kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); RunOnConstantLevel(kNumIterations, kMaxSpeechProbability, kPeakLevelDbfs, kSpeechLevelDbfs, *saturation_protector); EXPECT_NEAR(saturation_protector->HeadroomDb(), kCrestFactorDb, kMaxDifferenceDb); } -// Checks that the extra headroom is applied. -TEST(GainController2SaturationProtector, ExtraHeadroomApplied) { - constexpr float kExtraHeadroomDb = 5.1234f; - constexpr int kNumIterations = 10; - constexpr float kPeakLevelDbfs = -20.0f; - constexpr float kSpeechLevelDbfs = kPeakLevelDbfs - 15.0f; - - ApmDataDumper apm_data_dumper(0); - - auto saturation_protector_no_extra = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, kNoAdjacentSpeechFramesRequired, - &apm_data_dumper); - for (int i = 0; i < kNumIterations; ++i) { - saturation_protector_no_extra->Analyze(kMaxSpeechProbability, - kPeakLevelDbfs, kSpeechLevelDbfs); - } - - auto saturation_protector_extra = CreateSaturationProtector( - kInitialHeadroomDb, kExtraHeadroomDb, kNoAdjacentSpeechFramesRequired, - &apm_data_dumper); - for (int i = 0; i < kNumIterations; ++i) { - saturation_protector_extra->Analyze(kMaxSpeechProbability, kPeakLevelDbfs, - kSpeechLevelDbfs); - } - - EXPECT_EQ(saturation_protector_no_extra->HeadroomDb() + kExtraHeadroomDb, - saturation_protector_extra->HeadroomDb()); -} - // Checks that the headroom does not change too quickly. TEST(GainController2SaturationProtector, ChangeSlowly) { constexpr int kNumIterations = 1000; @@ -119,8 +87,7 @@ TEST(GainController2SaturationProtector, ChangeSlowly) { ApmDataDumper apm_data_dumper(0); auto saturation_protector = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, kNoAdjacentSpeechFramesRequired, - &apm_data_dumper); + kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); float max_difference_db = RunOnConstantLevel(kNumIterations, kMaxSpeechProbability, kPeakLevelDbfs, kSpeechLevelDbfs, *saturation_protector); @@ -142,8 +109,7 @@ class SaturationProtectorParametrization TEST_P(SaturationProtectorParametrization, DoNotAdaptToShortSpeechSegments) { ApmDataDumper apm_data_dumper(0); auto saturation_protector = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, - adjacent_speech_frames_threshold(), &apm_data_dumper); + kInitialHeadroomDb, adjacent_speech_frames_threshold(), &apm_data_dumper); const float initial_headroom_db = saturation_protector->HeadroomDb(); RunOnConstantLevel(/*num_iterations=*/adjacent_speech_frames_threshold() - 1, kMaxSpeechProbability, @@ -156,8 +122,7 @@ TEST_P(SaturationProtectorParametrization, DoNotAdaptToShortSpeechSegments) { TEST_P(SaturationProtectorParametrization, AdaptToEnoughSpeechSegments) { ApmDataDumper apm_data_dumper(0); auto saturation_protector = CreateSaturationProtector( - kInitialHeadroomDb, kNoExtraHeadroomDb, - adjacent_speech_frames_threshold(), &apm_data_dumper); + kInitialHeadroomDb, adjacent_speech_frames_threshold(), &apm_data_dumper); const float initial_headroom_db = saturation_protector->HeadroomDb(); RunOnConstantLevel(/*num_iterations=*/adjacent_speech_frames_threshold() + 1, kMaxSpeechProbability, diff --git a/modules/audio_processing/agc2/vad_with_level.cc b/modules/audio_processing/agc2/vad_with_level.cc deleted file mode 100644 index 9747ca2370..0000000000 --- a/modules/audio_processing/agc2/vad_with_level.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/agc2/vad_with_level.h" - -#include -#include -#include - -#include "api/array_view.h" -#include "common_audio/include/audio_util.h" -#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 { - -using VoiceActivityDetector = VadLevelAnalyzer::VoiceActivityDetector; - -// Default VAD that combines a resampler and the RNN VAD. -// Computes the speech probability on the first channel. -class Vad : public VoiceActivityDetector { - public: - explicit Vad(const AvailableCpuFeatures& cpu_features) - : features_extractor_(cpu_features), rnn_vad_(cpu_features) {} - Vad(const Vad&) = delete; - Vad& operator=(const Vad&) = delete; - ~Vad() = default; - - void Reset() override { rnn_vad_.Reset(); } - - float ComputeProbability(AudioFrameView frame) override { - // The source number of channels is 1, because we always use the 1st - // channel. - resampler_.InitializeIfNeeded( - /*sample_rate_hz=*/static_cast(frame.samples_per_channel() * 100), - rnn_vad::kSampleRate24kHz, - /*num_channels=*/1); - - std::array work_frame; - // Feed the 1st channel to the resampler. - resampler_.Resample(frame.channel(0).data(), frame.samples_per_channel(), - work_frame.data(), rnn_vad::kFrameSize10ms24kHz); - - std::array feature_vector; - const bool is_silence = features_extractor_.CheckSilenceComputeFeatures( - work_frame, feature_vector); - return rnn_vad_.ComputeVadProbability(feature_vector, is_silence); - } - - private: - PushResampler resampler_; - rnn_vad::FeaturesExtractor features_extractor_; - rnn_vad::RnnVad rnn_vad_; -}; - -} // namespace - -VadLevelAnalyzer::VadLevelAnalyzer(int vad_reset_period_ms, - const AvailableCpuFeatures& cpu_features) - : VadLevelAnalyzer(vad_reset_period_ms, - std::make_unique(cpu_features)) {} - -VadLevelAnalyzer::VadLevelAnalyzer(int vad_reset_period_ms, - std::unique_ptr vad) - : vad_(std::move(vad)), - vad_reset_period_frames_( - rtc::CheckedDivExact(vad_reset_period_ms, kFrameDurationMs)), - time_to_vad_reset_(vad_reset_period_frames_) { - RTC_DCHECK(vad_); - RTC_DCHECK_GT(vad_reset_period_frames_, 1); -} - -VadLevelAnalyzer::~VadLevelAnalyzer() = default; - -VadLevelAnalyzer::Result VadLevelAnalyzer::AnalyzeFrame( - AudioFrameView 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_; - } - // Compute levels. - float peak = 0.0f; - float rms = 0.0f; - for (const auto& x : frame.channel(0)) { - peak = std::max(std::fabs(x), peak); - rms += x * x; - } - return {vad_->ComputeProbability(frame), - FloatS16ToDbfs(std::sqrt(rms / frame.samples_per_channel())), - FloatS16ToDbfs(peak)}; -} - -} // namespace webrtc diff --git a/modules/audio_processing/agc2/vad_with_level.h b/modules/audio_processing/agc2/vad_with_level.h deleted file mode 100644 index 8d2ae45762..0000000000 --- a/modules/audio_processing/agc2/vad_with_level.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ -#define MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ - -#include - -#include "modules/audio_processing/agc2/cpu_features.h" -#include "modules/audio_processing/include/audio_frame_view.h" - -namespace webrtc { - -// Class to analyze voice activity and audio levels. -class VadLevelAnalyzer { - public: - struct Result { - float speech_probability; // Range: [0, 1]. - float rms_dbfs; // Root mean square power (dBFS). - float peak_dbfs; // Peak power (dBFS). - }; - - // Voice Activity Detector (VAD) interface. - class VoiceActivityDetector { - public: - virtual ~VoiceActivityDetector() = default; - // Resets the internal state. - virtual void Reset() = 0; - // Analyzes an audio frame and returns the speech probability. - virtual float ComputeProbability(AudioFrameView frame) = 0; - }; - - // Ctor. `vad_reset_period_ms` indicates the period in milliseconds to call - // `VadLevelAnalyzer::Reset()`; it must be equal to or greater than the - // duration of two frames. Uses `cpu_features` to instantiate the default VAD. - VadLevelAnalyzer(int vad_reset_period_ms, - const AvailableCpuFeatures& cpu_features); - // Ctor. Uses a custom `vad`. - VadLevelAnalyzer(int vad_reset_period_ms, - std::unique_ptr vad); - - VadLevelAnalyzer(const VadLevelAnalyzer&) = delete; - VadLevelAnalyzer& operator=(const VadLevelAnalyzer&) = delete; - ~VadLevelAnalyzer(); - - // Computes the speech probability and the level for `frame`. - Result AnalyzeFrame(AudioFrameView frame); - - private: - std::unique_ptr vad_; - const int vad_reset_period_frames_; - int time_to_vad_reset_; -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ diff --git a/modules/audio_processing/agc2/vad_with_level_unittest.cc b/modules/audio_processing/agc2/vad_with_level_unittest.cc deleted file mode 100644 index ec8e476965..0000000000 --- a/modules/audio_processing/agc2/vad_with_level_unittest.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/agc2/vad_with_level.h" - -#include -#include -#include - -#include "modules/audio_processing/agc2/agc2_common.h" -#include "modules/audio_processing/include/audio_frame_view.h" -#include "rtc_base/gunit.h" -#include "rtc_base/numerics/safe_compare.h" -#include "test/gmock.h" - -namespace webrtc { -namespace { - -using ::testing::AnyNumber; -using ::testing::ReturnRoundRobin; - -constexpr int kNoVadPeriodicReset = - kFrameDurationMs * (std::numeric_limits::max() / kFrameDurationMs); - -constexpr int kSampleRateHz = 8000; - -class MockVad : public VadLevelAnalyzer::VoiceActivityDetector { - public: - MOCK_METHOD(void, Reset, (), (override)); - MOCK_METHOD(float, - ComputeProbability, - (AudioFrameView frame), - (override)); -}; - -// Creates a `VadLevelAnalyzer` injecting a mock VAD which repeatedly returns -// the next value from `speech_probabilities` until it reaches the end and will -// restart from the beginning. -std::unique_ptr CreateVadLevelAnalyzerWithMockVad( - int vad_reset_period_ms, - const std::vector& speech_probabilities, - int expected_vad_reset_calls = 0) { - auto vad = std::make_unique(); - EXPECT_CALL(*vad, ComputeProbability) - .Times(AnyNumber()) - .WillRepeatedly(ReturnRoundRobin(speech_probabilities)); - if (expected_vad_reset_calls >= 0) { - EXPECT_CALL(*vad, Reset).Times(expected_vad_reset_calls); - } - return std::make_unique(vad_reset_period_ms, - std::move(vad)); -} - -// 10 ms mono frame. -struct FrameWithView { - // Ctor. Initializes the frame samples with `value`. - FrameWithView(float value = 0.0f) - : channel0(samples.data()), - view(&channel0, /*num_channels=*/1, samples.size()) { - samples.fill(value); - } - std::array samples; - const float* const channel0; - const AudioFrameView view; -}; - -TEST(GainController2VadLevelAnalyzer, RmsLessThanPeakLevel) { - auto analyzer = CreateVadLevelAnalyzerWithMockVad( - /*vad_reset_period_ms=*/1500, - /*speech_probabilities=*/{1.0f}, - /*expected_vad_reset_calls=*/0); - // Handcrafted frame so that the average is lower than the peak value. - FrameWithView frame(1000.0f); // Constant frame. - frame.samples[10] = 2000.0f; // Except for one peak value. - // Compute audio frame levels. - auto levels_and_vad_prob = analyzer->AnalyzeFrame(frame.view); - EXPECT_LT(levels_and_vad_prob.rms_dbfs, levels_and_vad_prob.peak_dbfs); -} - -// Checks that the expect VAD probabilities are returned. -TEST(GainController2VadLevelAnalyzer, NoSpeechProbabilitySmoothing) { - const std::vector speech_probabilities{0.709f, 0.484f, 0.882f, 0.167f, - 0.44f, 0.525f, 0.858f, 0.314f, - 0.653f, 0.965f, 0.413f, 0.0f}; - auto analyzer = CreateVadLevelAnalyzerWithMockVad(kNoVadPeriodicReset, - speech_probabilities); - FrameWithView frame; - for (int i = 0; rtc::SafeLt(i, speech_probabilities.size()); ++i) { - SCOPED_TRACE(i); - EXPECT_EQ(speech_probabilities[i], - analyzer->AnalyzeFrame(frame.view).speech_probability); - } -} - -// Checks that the VAD is not periodically reset. -TEST(GainController2VadLevelAnalyzer, VadNoPeriodicReset) { - constexpr int kNumFrames = 19; - auto analyzer = CreateVadLevelAnalyzerWithMockVad( - kNoVadPeriodicReset, /*speech_probabilities=*/{1.0f}, - /*expected_vad_reset_calls=*/0); - FrameWithView frame; - for (int i = 0; i < kNumFrames; ++i) { - analyzer->AnalyzeFrame(frame.view); - } -} - -class VadPeriodResetParametrization - : public ::testing::TestWithParam> { - protected: - int num_frames() const { return std::get<0>(GetParam()); } - int vad_reset_period_frames() const { return std::get<1>(GetParam()); } -}; - -// Checks that the VAD is periodically reset with the expected period. -TEST_P(VadPeriodResetParametrization, VadPeriodicReset) { - auto analyzer = CreateVadLevelAnalyzerWithMockVad( - /*vad_reset_period_ms=*/vad_reset_period_frames() * kFrameDurationMs, - /*speech_probabilities=*/{1.0f}, - /*expected_vad_reset_calls=*/num_frames() / vad_reset_period_frames()); - FrameWithView frame; - for (int i = 0; i < num_frames(); ++i) { - analyzer->AnalyzeFrame(frame.view); - } -} - -INSTANTIATE_TEST_SUITE_P(GainController2VadLevelAnalyzer, - VadPeriodResetParametrization, - ::testing::Combine(::testing::Values(1, 19, 123), - ::testing::Values(2, 5, 20, 53))); - -} // namespace -} // namespace webrtc diff --git a/modules/audio_processing/agc2/vad_wrapper.cc b/modules/audio_processing/agc2/vad_wrapper.cc new file mode 100644 index 0000000000..91448f8d86 --- /dev/null +++ b/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 "api/array_view.h" +#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(rtc::ArrayView 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( + 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_( + rtc::CheckedDivExact(vad_reset_period_ms, kFrameDurationMs)), + time_to_vad_reset_(vad_reset_period_frames_), + vad_(std::move(vad)) { + RTC_DCHECK(vad_); + RTC_DCHECK_GT(vad_reset_period_frames_, 1); + resampled_buffer_.resize( + rtc::CheckedDivExact(vad_->SampleRateHz(), kNumFramesPerSecond)); + Initialize(sample_rate_hz); +} + +VoiceActivityDetectorWrapper::~VoiceActivityDetectorWrapper() = default; + +void VoiceActivityDetectorWrapper::Initialize(int sample_rate_hz) { + RTC_DCHECK_GT(sample_rate_hz, 0); + frame_size_ = rtc::CheckedDivExact(sample_rate_hz, kNumFramesPerSecond); + int status = + resampler_.InitializeIfNeeded(sample_rate_hz, vad_->SampleRateHz(), + /*num_channels=*/1); + constexpr int kStatusOk = 0; + RTC_DCHECK_EQ(status, kStatusOk); + vad_->Reset(); +} + +float VoiceActivityDetectorWrapper::Analyze(AudioFrameView 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_); + resampler_.Resample(frame.channel(0).data(), frame_size_, + resampled_buffer_.data(), resampled_buffer_.size()); + + return vad_->Analyze(resampled_buffer_); +} + +} // namespace webrtc diff --git a/modules/audio_processing/agc2/vad_wrapper.h b/modules/audio_processing/agc2/vad_wrapper.h new file mode 100644 index 0000000000..6df0ead271 --- /dev/null +++ b/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/array_view.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/include/audio_frame_view.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(rtc::ArrayView frame) = 0; + }; + + // 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(); + + // Initializes the VAD wrapper. + void Initialize(int sample_rate_hz); + + // 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(AudioFrameView frame); + + private: + const int vad_reset_period_frames_; + int frame_size_; + int time_to_vad_reset_; + PushResampler resampler_; + std::unique_ptr vad_; + std::vector resampled_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_VAD_WRAPPER_H_ diff --git a/modules/audio_processing/agc2/vad_wrapper_unittest.cc b/modules/audio_processing/agc2/vad_wrapper_unittest.cc new file mode 100644 index 0000000000..91efdb566e --- /dev/null +++ b/modules/audio_processing/agc2/vad_wrapper_unittest.cc @@ -0,0 +1,181 @@ +/* + * 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 +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/gunit.h" +#include "rtc_base/numerics/safe_compare.h" +#include "test/gmock.h" + +namespace webrtc { +namespace { + +using ::testing::AnyNumber; +using ::testing::Return; +using ::testing::ReturnRoundRobin; +using ::testing::Truly; + +constexpr int kNumFramesPerSecond = 100; + +constexpr int kNoVadPeriodicReset = + kFrameDurationMs * (std::numeric_limits::max() / kFrameDurationMs); + +constexpr int kSampleRate8kHz = 8000; + +class MockVad : public VoiceActivityDetectorWrapper::MonoVad { + public: + MOCK_METHOD(int, SampleRateHz, (), (const, override)); + MOCK_METHOD(void, Reset, (), (override)); + MOCK_METHOD(float, Analyze, (rtc::ArrayView frame), (override)); +}; + +// Checks that the ctor and `Initialize()` read the sample rate of the wrapped +// VAD. +TEST(GainController2VoiceActivityDetectorWrapper, CtorAndInitReadSampleRate) { + auto vad = std::make_unique(); + EXPECT_CALL(*vad, SampleRateHz) + .Times(2) + .WillRepeatedly(Return(kSampleRate8kHz)); + EXPECT_CALL(*vad, Reset).Times(AnyNumber()); + auto vad_wrapper = std::make_unique( + kNoVadPeriodicReset, std::move(vad), kSampleRate8kHz); +} + +// Creates a `VoiceActivityDetectorWrapper` injecting a mock VAD that +// repeatedly returns the next value from `speech_probabilities` and that +// restarts from the beginning when after the last element is returned. +std::unique_ptr CreateMockVadWrapper( + int vad_reset_period_ms, + int sample_rate_hz, + const std::vector& speech_probabilities, + int expected_vad_reset_calls) { + auto vad = std::make_unique(); + EXPECT_CALL(*vad, SampleRateHz) + .Times(AnyNumber()) + .WillRepeatedly(Return(sample_rate_hz)); + if (expected_vad_reset_calls >= 0) { + EXPECT_CALL(*vad, Reset).Times(expected_vad_reset_calls); + } + EXPECT_CALL(*vad, Analyze) + .Times(AnyNumber()) + .WillRepeatedly(ReturnRoundRobin(speech_probabilities)); + return std::make_unique( + vad_reset_period_ms, std::move(vad), kSampleRate8kHz); +} + +// 10 ms mono frame. +struct FrameWithView { + // Ctor. Initializes the frame samples with `value`. + explicit FrameWithView(int sample_rate_hz) + : samples(rtc::CheckedDivExact(sample_rate_hz, kNumFramesPerSecond), + 0.0f), + channel0(samples.data()), + view(&channel0, /*num_channels=*/1, samples.size()) {} + std::vector samples; + const float* const channel0; + const AudioFrameView view; +}; + +// Checks that the expected speech probabilities are returned. +TEST(GainController2VoiceActivityDetectorWrapper, CheckSpeechProbabilities) { + const std::vector speech_probabilities{0.709f, 0.484f, 0.882f, 0.167f, + 0.44f, 0.525f, 0.858f, 0.314f, + 0.653f, 0.965f, 0.413f, 0.0f}; + auto vad_wrapper = CreateMockVadWrapper(kNoVadPeriodicReset, kSampleRate8kHz, + speech_probabilities, + /*expected_vad_reset_calls=*/1); + FrameWithView frame(kSampleRate8kHz); + for (int i = 0; rtc::SafeLt(i, speech_probabilities.size()); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(speech_probabilities[i], vad_wrapper->Analyze(frame.view)); + } +} + +// Checks that the VAD is not periodically reset. +TEST(GainController2VoiceActivityDetectorWrapper, VadNoPeriodicReset) { + constexpr int kNumFrames = 19; + auto vad_wrapper = CreateMockVadWrapper(kNoVadPeriodicReset, kSampleRate8kHz, + /*speech_probabilities=*/{1.0f}, + /*expected_vad_reset_calls=*/1); + FrameWithView frame(kSampleRate8kHz); + for (int i = 0; i < kNumFrames; ++i) { + vad_wrapper->Analyze(frame.view); + } +} + +class VadPeriodResetParametrization + : public ::testing::TestWithParam> { + protected: + int num_frames() const { return std::get<0>(GetParam()); } + int vad_reset_period_frames() const { return std::get<1>(GetParam()); } +}; + +// Checks that the VAD is periodically reset with the expected period. +TEST_P(VadPeriodResetParametrization, VadPeriodicReset) { + auto vad_wrapper = CreateMockVadWrapper( + /*vad_reset_period_ms=*/vad_reset_period_frames() * kFrameDurationMs, + kSampleRate8kHz, + /*speech_probabilities=*/{1.0f}, + /*expected_vad_reset_calls=*/1 + + num_frames() / vad_reset_period_frames()); + FrameWithView frame(kSampleRate8kHz); + for (int i = 0; i < num_frames(); ++i) { + vad_wrapper->Analyze(frame.view); + } +} + +INSTANTIATE_TEST_SUITE_P(GainController2VoiceActivityDetectorWrapper, + VadPeriodResetParametrization, + ::testing::Combine(::testing::Values(1, 19, 123), + ::testing::Values(2, 5, 20, 53))); + +class VadResamplingParametrization + : public ::testing::TestWithParam> { + protected: + int input_sample_rate_hz() const { return std::get<0>(GetParam()); } + int vad_sample_rate_hz() const { return std::get<1>(GetParam()); } +}; + +// Checks that regardless of the input audio sample rate, the wrapped VAD +// analyzes frames having the expected size, that is according to its internal +// sample rate. +TEST_P(VadResamplingParametrization, CheckResampledFrameSize) { + auto vad = std::make_unique(); + EXPECT_CALL(*vad, SampleRateHz) + .Times(AnyNumber()) + .WillRepeatedly(Return(vad_sample_rate_hz())); + EXPECT_CALL(*vad, Reset).Times(1); + EXPECT_CALL(*vad, Analyze(Truly([this](rtc::ArrayView frame) { + return rtc::SafeEq(frame.size(), rtc::CheckedDivExact(vad_sample_rate_hz(), + kNumFramesPerSecond)); + }))).Times(1); + auto vad_wrapper = std::make_unique( + kNoVadPeriodicReset, std::move(vad), input_sample_rate_hz()); + FrameWithView frame(input_sample_rate_hz()); + vad_wrapper->Analyze(frame.view); +} + +INSTANTIATE_TEST_SUITE_P( + GainController2VoiceActivityDetectorWrapper, + VadResamplingParametrization, + ::testing::Combine(::testing::Values(8000, 16000, 44100, 48000), + ::testing::Values(6000, 8000, 12000, 16000, 24000))); + +} // namespace +} // namespace webrtc diff --git a/modules/audio_processing/audio_frame_view_unittest.cc b/modules/audio_processing/audio_frame_view_unittest.cc index a4ad4ccd8c..fd25bc3b0b 100644 --- a/modules/audio_processing/audio_frame_view_unittest.cc +++ b/modules/audio_processing/audio_frame_view_unittest.cc @@ -19,7 +19,7 @@ TEST(AudioFrameTest, ConstructFromAudioBuffer) { constexpr int kNumChannels = 2; constexpr float kFloatConstant = 1272.f; constexpr float kIntConstant = 17252; - const webrtc::StreamConfig stream_config(kSampleRateHz, kNumChannels, false); + const webrtc::StreamConfig stream_config(kSampleRateHz, kNumChannels); webrtc::AudioBuffer buffer( stream_config.sample_rate_hz(), stream_config.num_channels(), stream_config.sample_rate_hz(), stream_config.num_channels(), diff --git a/modules/audio_processing/audio_processing_builder_impl.cc b/modules/audio_processing/audio_processing_builder_impl.cc index a6b7d5f23e..79c6fbf524 100644 --- a/modules/audio_processing/audio_processing_builder_impl.cc +++ b/modules/audio_processing/audio_processing_builder_impl.cc @@ -21,23 +21,12 @@ AudioProcessingBuilder::AudioProcessingBuilder() = default; AudioProcessingBuilder::~AudioProcessingBuilder() = default; rtc::scoped_refptr AudioProcessingBuilder::Create() { - webrtc::Config config; - return Create(config); -} - -rtc::scoped_refptr AudioProcessingBuilder::Create( - const webrtc::Config& config) { #ifdef WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE - - // Implementation returning a null pointer for using when the APM is excluded - // from the build.. + // Return a null pointer when the APM is excluded from the build. return nullptr; - -#else - - // Standard implementation. +#else // WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE return rtc::make_ref_counted( - config, std::move(capture_post_processing_), + config_, std::move(capture_post_processing_), std::move(render_pre_processing_), std::move(echo_control_factory_), std::move(echo_detector_), std::move(capture_analyzer_)); #endif diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index a1de2d7675..f10bf904f0 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -24,13 +24,11 @@ #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/common.h" #include "modules/audio_processing/include/audio_frame_view.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/optionally_built_submodule_creators.h" #include "rtc_base/atomic_ops.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/time_utils.h" @@ -51,20 +49,6 @@ namespace webrtc { namespace { -static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) { - switch (layout) { - case AudioProcessing::kMono: - case AudioProcessing::kStereo: - return false; - case AudioProcessing::kMonoAndKeyboard: - case AudioProcessing::kStereoAndKeyboard: - return true; - } - - RTC_NOTREACHED(); - return false; -} - bool SampleRateSupportsMultiBand(int sample_rate_hz) { return sample_rate_hz == AudioProcessing::kSampleRate32kHz || sample_rate_hz == AudioProcessing::kSampleRate48kHz; @@ -97,7 +81,7 @@ int SuitableProcessRate(int minimum_rate, return rate; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return uppermost_native_rate; } @@ -129,6 +113,13 @@ static const size_t kMaxAllowedValuesOfSamplesPerFrame = 480; // 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()); +} + } // namespace // Throughout webrtc, it's assumed that success is represented by zero. @@ -145,38 +136,31 @@ AudioProcessingImpl::SubmoduleStates::SubmoduleStates( bool AudioProcessingImpl::SubmoduleStates::Update( bool high_pass_filter_enabled, bool mobile_echo_controller_enabled, - bool residual_echo_detector_enabled, bool noise_suppressor_enabled, bool adaptive_gain_controller_enabled, bool gain_controller2_enabled, bool gain_adjustment_enabled, bool echo_controller_enabled, - bool voice_detector_enabled, bool transient_suppressor_enabled) { bool changed = false; changed |= (high_pass_filter_enabled != high_pass_filter_enabled_); changed |= (mobile_echo_controller_enabled != mobile_echo_controller_enabled_); - changed |= - (residual_echo_detector_enabled != residual_echo_detector_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_); - changed |= (voice_detector_enabled != voice_detector_enabled_); changed |= (transient_suppressor_enabled != transient_suppressor_enabled_); if (changed) { high_pass_filter_enabled_ = high_pass_filter_enabled; mobile_echo_controller_enabled_ = mobile_echo_controller_enabled; - residual_echo_detector_enabled_ = residual_echo_detector_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; - voice_detector_enabled_ = voice_detector_enabled; transient_suppressor_enabled_ = transient_suppressor_enabled; } @@ -187,7 +171,7 @@ bool AudioProcessingImpl::SubmoduleStates::Update( bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandSubModulesActive() const { - return CaptureMultiBandProcessingPresent() || voice_detector_enabled_; + return CaptureMultiBandProcessingPresent(); } bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingPresent() @@ -234,8 +218,8 @@ bool AudioProcessingImpl::SubmoduleStates::HighPassFilteringRequired() const { noise_suppressor_enabled_; } -AudioProcessingImpl::AudioProcessingImpl(const webrtc::Config& config) - : AudioProcessingImpl(config, +AudioProcessingImpl::AudioProcessingImpl() + : AudioProcessingImpl(/*config=*/{}, /*capture_post_processor=*/nullptr, /*render_pre_processor=*/nullptr, /*echo_control_factory=*/nullptr, @@ -245,7 +229,7 @@ AudioProcessingImpl::AudioProcessingImpl(const webrtc::Config& config) int AudioProcessingImpl::instance_count_ = 0; AudioProcessingImpl::AudioProcessingImpl( - const webrtc::Config& config, + const AudioProcessing::Config& config, std::unique_ptr capture_post_processor, std::unique_ptr render_pre_processor, std::unique_ptr echo_control_factory, @@ -262,6 +246,7 @@ AudioProcessingImpl::AudioProcessingImpl( 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), @@ -295,28 +280,6 @@ AudioProcessingImpl::AudioProcessingImpl( capture_nonlocked_.echo_controller_enabled = static_cast(echo_control_factory_); - // If no echo detector is injected, use the ResidualEchoDetector. - if (!submodules_.echo_detector) { - submodules_.echo_detector = rtc::make_ref_counted(); - } - -#if !(defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)) - // TODO(webrtc:5298): Remove once the use of ExperimentalNs has been - // deprecated. - config_.transient_suppression.enabled = config.Get().enabled; - - // TODO(webrtc:5298): Remove once the use of ExperimentalAgc has been - // deprecated. - config_.gain_controller1.analog_gain_controller.enabled = - config.Get().enabled; - config_.gain_controller1.analog_gain_controller.startup_min_volume = - config.Get().startup_min_volume; - config_.gain_controller1.analog_gain_controller.clipped_level_min = - config.Get().clipped_level_min; - config_.gain_controller1.analog_gain_controller.enable_digital_adaptive = - !config.Get().digital_adaptive_disabled; -#endif - Initialize(); } @@ -330,26 +293,6 @@ int AudioProcessingImpl::Initialize() { return kNoError; } -int AudioProcessingImpl::Initialize(int capture_input_sample_rate_hz, - int capture_output_sample_rate_hz, - int render_input_sample_rate_hz, - ChannelLayout capture_input_layout, - ChannelLayout capture_output_layout, - ChannelLayout render_input_layout) { - const ProcessingConfig processing_config = { - {{capture_input_sample_rate_hz, ChannelsFromLayout(capture_input_layout), - LayoutHasKeyboard(capture_input_layout)}, - {capture_output_sample_rate_hz, - ChannelsFromLayout(capture_output_layout), - LayoutHasKeyboard(capture_output_layout)}, - {render_input_sample_rate_hz, ChannelsFromLayout(render_input_layout), - LayoutHasKeyboard(render_input_layout)}, - {render_input_sample_rate_hz, ChannelsFromLayout(render_input_layout), - LayoutHasKeyboard(render_input_layout)}}}; - - return Initialize(processing_config); -} - int AudioProcessingImpl::Initialize(const ProcessingConfig& processing_config) { // Run in a single-threaded manner during initialization. MutexLock lock_render(&mutex_render_); @@ -425,10 +368,9 @@ void AudioProcessingImpl::InitializeLocked() { InitializeGainController1(); InitializeTransientSuppressor(); InitializeHighPassFilter(true); - InitializeVoiceDetector(); InitializeResidualEchoDetector(); InitializeEchoController(); - InitializeGainController2(); + InitializeGainController2(/*config_has_changed=*/true); InitializeNoiseSuppressor(); InitializeAnalyzer(); InitializePostProcessor(); @@ -560,9 +502,6 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { const bool agc2_config_changed = config_.gain_controller2 != config.gain_controller2; - const bool voice_detection_config_changed = - config_.voice_detection.enabled != config.voice_detection.enabled; - const bool ns_config_changed = config_.noise_suppression.enabled != config.noise_suppression.enabled || config_.noise_suppression.level != config.noise_suppression.level; @@ -605,22 +544,12 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { config_.gain_controller2 = AudioProcessing::Config::GainController2(); } - if (agc2_config_changed) { - InitializeGainController2(); - } + InitializeGainController2(agc2_config_changed); if (pre_amplifier_config_changed || gain_adjustment_config_changed) { InitializeCaptureLevelsAdjuster(); } - if (config_.level_estimation.enabled && !submodules_.output_level_estimator) { - submodules_.output_level_estimator = std::make_unique(); - } - - if (voice_detection_config_changed) { - InitializeVoiceDetector(); - } - // Reinitialization must happen after all submodule configuration to avoid // additional reinitializations on the next capture / render processing call. if (pipeline_config_changed) { @@ -730,12 +659,12 @@ bool AudioProcessingImpl::PostRuntimeSetting(RuntimeSetting setting) { return enqueueing_successful; } case RuntimeSetting::Type::kNotSpecified: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return true; } // The language allows the enum to have a non-enumerator // value. Check that this doesn't happen. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return true; } @@ -809,7 +738,6 @@ int AudioProcessingImpl::ProcessStream(const float* const* src, RecordUnprocessedCaptureStream(src); } - capture_.keyboard_info.Extract(src, formats_.api_format.input_stream()); capture_.capture_audio->CopyFrom(src, formats_.api_format.input_stream()); if (capture_.capture_fullband_audio) { capture_.capture_fullband_audio->CopyFrom( @@ -895,7 +823,7 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() { float value; setting.GetFloat(&value); config_.gain_controller2.fixed_digital.gain_db = value; - submodules_.gain_controller2->ApplyConfig(config_.gain_controller2); + submodules_.gain_controller2->SetFixedGainDb(value); } break; } @@ -906,13 +834,13 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() { break; } case RuntimeSetting::Type::kPlayoutAudioDeviceChange: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case RuntimeSetting::Type::kNotSpecified: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case RuntimeSetting::Type::kCaptureOutputUsed: bool value; @@ -956,7 +884,7 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() { case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through case RuntimeSetting::Type::kCaptureOutputUsed: // fall-through case RuntimeSetting::Type::kNotSpecified: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } @@ -997,16 +925,18 @@ void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) { } void AudioProcessingImpl::QueueNonbandedRenderAudio(AudioBuffer* audio) { - ResidualEchoDetector::PackRenderAudioBuffer(audio, &red_render_queue_buffer_); + 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(); - // 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); + // Retry the insert (should always work). + bool result = red_render_signal_queue_->Insert(&red_render_queue_buffer_); + RTC_DCHECK(result); + } } } @@ -1039,23 +969,26 @@ void AudioProcessingImpl::AllocateRenderQueue() { agc_render_signal_queue_->Clear(); } - 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; + 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_); + 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_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(); + 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(); + } } } @@ -1079,9 +1012,10 @@ void AudioProcessingImpl::EmptyQueuedRenderAudioLocked() { } } - while (red_render_signal_queue_->Remove(&red_capture_queue_buffer_)) { - RTC_DCHECK(submodules_.echo_detector); - submodules_.echo_detector->AnalyzeRenderAudio(red_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_); + } } } @@ -1171,13 +1105,16 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { levels.peak, 1, RmsLevel::kMinLevelDb, 64); } + // Detect an analog gain change. + int analog_mic_level = recommended_stream_analog_level_locked(); + const bool analog_mic_level_changed = + capture_.prev_analog_mic_level != analog_mic_level && + capture_.prev_analog_mic_level != -1; + capture_.prev_analog_mic_level = analog_mic_level; + analog_gain_stats_reporter_.UpdateStatistics(analog_mic_level); + if (submodules_.echo_controller) { - // Detect and flag any change in the analog gain. - int analog_mic_level = recommended_stream_analog_level_locked(); - capture_.echo_path_gain_change = - capture_.prev_analog_mic_level != analog_mic_level && - capture_.prev_analog_mic_level != -1; - capture_.prev_analog_mic_level = analog_mic_level; + capture_.echo_path_gain_change = analog_mic_level_changed; // Detect and flag any change in the capture level adjustment pre-gain. if (submodules_.capture_levels_adjuster) { @@ -1273,13 +1210,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } } - if (config_.voice_detection.enabled) { - capture_.stats.voice_detected = - submodules_.voice_detector->ProcessCaptureAudio(capture_buffer); - } else { - capture_.stats.voice_detected = absl::nullopt; - } - if (submodules_.agc_manager) { submodules_.agc_manager->Process(capture_buffer); @@ -1302,7 +1232,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { capture_buffer->MergeFrequencyBands(); } - capture_.stats.output_rms_dbfs = absl::nullopt; if (capture_.capture_output_used) { if (capture_.capture_fullband_audio) { const auto& ec = submodules_.echo_controller; @@ -1315,8 +1244,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { capture_buffer = capture_.capture_fullband_audio.get(); } - if (config_.residual_echo_detector.enabled) { - RTC_DCHECK(submodules_.echo_detector); + if (submodules_.echo_detector) { submodules_.echo_detector->AnalyzeCaptureAudio( rtc::ArrayView(capture_buffer->channels()[0], capture_buffer->num_frames())); @@ -1335,8 +1263,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { capture_buffer->num_channels(), capture_buffer->split_bands_const(0)[kBand0To8kHz], capture_buffer->num_frames_per_band(), - capture_.keyboard_info.keyboard_data, - capture_.keyboard_info.num_keyboard_frames, voice_probability, + /*reference_data=*/nullptr, /*reference_length=*/0, voice_probability, capture_.key_pressed); } @@ -1355,13 +1282,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { submodules_.capture_post_processor->Process(capture_buffer); } - // The level estimator operates on the recombined data. - if (config_.level_estimation.enabled) { - submodules_.output_level_estimator->ProcessStream(*capture_buffer); - capture_.stats.output_rms_dbfs = - submodules_.output_level_estimator->RMS(); - } - capture_output_rms_.Analyze(rtc::ArrayView( capture_buffer->channels_const()[0], capture_nonlocked_.capture_processing_format.num_frames())); @@ -1381,8 +1301,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } // Compute echo-detector stats. - if (config_.residual_echo_detector.enabled) { - RTC_DCHECK(submodules_.echo_detector); + 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 = @@ -1625,7 +1544,7 @@ bool AudioProcessingImpl::GetLinearAecOutput( return true; } RTC_LOG(LS_ERROR) << "No linear AEC output available"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -1747,11 +1666,11 @@ AudioProcessing::Config AudioProcessingImpl::GetConfig() const { bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { return submodule_states_.Update( config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile, - config_.residual_echo_detector.enabled, !!submodules_.noise_suppressor, - !!submodules_.gain_control, !!submodules_.gain_controller2, + !!submodules_.noise_suppressor, !!submodules_.gain_control, + !!submodules_.gain_controller2, config_.pre_amplifier.enabled || config_.capture_level_adjustment.enabled, capture_nonlocked_.echo_controller_enabled, - config_.voice_detection.enabled, !!submodules_.transient_suppressor); + !!submodules_.transient_suppressor); } void AudioProcessingImpl::InitializeTransientSuppressor() { @@ -1801,14 +1720,6 @@ void AudioProcessingImpl::InitializeHighPassFilter(bool forced_reset) { } } -void AudioProcessingImpl::InitializeVoiceDetector() { - if (config_.voice_detection.enabled) { - submodules_.voice_detector = std::make_unique( - proc_split_sample_rate_hz(), VoiceDetection::kVeryLowLikelihood); - } else { - submodules_.voice_detector.reset(); - } -} void AudioProcessingImpl::InitializeEchoController() { bool use_echo_controller = echo_control_factory_ || @@ -1901,7 +1812,6 @@ void AudioProcessingImpl::InitializeGainController1() { 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)); @@ -1915,9 +1825,10 @@ void AudioProcessingImpl::InitializeGainController1() { 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( - config_.gain_controller1.analog_level_minimum, - config_.gain_controller1.analog_level_maximum); + kAnalogLevelMinimum, kAnalogLevelMaximum); RTC_DCHECK_EQ(kNoError, error); submodules_.agc_manager.reset(); @@ -1926,9 +1837,7 @@ void AudioProcessingImpl::InitializeGainController1() { if (!submodules_.agc_manager.get() || submodules_.agc_manager->num_channels() != - static_cast(num_proc_channels()) || - submodules_.agc_manager->sample_rate_hz() != - capture_nonlocked_.split_rate) { + static_cast(num_proc_channels())) { int stream_analog_level = -1; const bool re_creation = !!submodules_.agc_manager; if (re_creation) { @@ -1940,7 +1849,6 @@ void AudioProcessingImpl::InitializeGainController1() { config_.gain_controller1.analog_gain_controller.clipped_level_min, !config_.gain_controller1.analog_gain_controller .enable_digital_adaptive, - capture_nonlocked_.split_rate, config_.gain_controller1.analog_gain_controller.clipped_level_step, config_.gain_controller1.analog_gain_controller.clipped_ratio_threshold, config_.gain_controller1.analog_gain_controller.clipped_wait_frames, @@ -1956,19 +1864,18 @@ void AudioProcessingImpl::InitializeGainController1() { capture_.capture_output_used); } -void AudioProcessingImpl::InitializeGainController2() { - if (config_.gain_controller2.enabled) { - if (!submodules_.gain_controller2) { - // TODO(alessiob): Move the injected gain controller once injection is - // implemented. - submodules_.gain_controller2.reset(new GainController2()); - } - - submodules_.gain_controller2->Initialize(proc_fullband_sample_rate_hz(), - num_input_channels()); - submodules_.gain_controller2->ApplyConfig(config_.gain_controller2); - } else { +void AudioProcessingImpl::InitializeGainController2(bool config_has_changed) { + if (!config_has_changed) { + return; + } + if (!config_.gain_controller2.enabled) { submodules_.gain_controller2.reset(); + return; + } + if (!submodules_.gain_controller2 || config_has_changed) { + submodules_.gain_controller2 = std::make_unique( + config_.gain_controller2, proc_fullband_sample_rate_hz(), + num_input_channels()); } } @@ -2025,10 +1932,11 @@ void AudioProcessingImpl::InitializeCaptureLevelsAdjuster() { } void AudioProcessingImpl::InitializeResidualEchoDetector() { - RTC_DCHECK(submodules_.echo_detector); - submodules_.echo_detector->Initialize( - proc_fullband_sample_rate_hz(), 1, - formats_.render_processing_format.sample_rate_hz(), 1); + 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() { @@ -2193,17 +2101,6 @@ AudioProcessingImpl::ApmCaptureState::ApmCaptureState() AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default; -void AudioProcessingImpl::ApmCaptureState::KeyboardInfo::Extract( - const float* const* data, - const StreamConfig& stream_config) { - if (stream_config.has_keyboard()) { - keyboard_data = data[stream_config.num_channels()]; - } else { - keyboard_data = NULL; - } - num_keyboard_frames = stream_config.num_frames(); -} - AudioProcessingImpl::ApmRenderState::ApmRenderState() = default; AudioProcessingImpl::ApmRenderState::~ApmRenderState() = default; diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index 73e045b2c6..a5a4668705 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -19,9 +19,11 @@ #include #include +#include "api/array_view.h" #include "api/function_view.h" #include "modules/audio_processing/aec3/echo_canceller3.h" #include "modules/audio_processing/agc/agc_manager_direct.h" +#include "modules/audio_processing/agc/analog_gain_stats_reporter.h" #include "modules/audio_processing/agc/gain_control.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h" @@ -33,14 +35,11 @@ #include "modules/audio_processing/include/audio_frame_proxies.h" #include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/include/audio_processing_statistics.h" -#include "modules/audio_processing/level_estimator.h" #include "modules/audio_processing/ns/noise_suppressor.h" #include "modules/audio_processing/optionally_built_submodule_creators.h" #include "modules/audio_processing/render_queue_item_verifier.h" -#include "modules/audio_processing/residual_echo_detector.h" #include "modules/audio_processing/rms_level.h" #include "modules/audio_processing/transient/transient_suppressor.h" -#include "modules/audio_processing/voice_detection.h" #include "rtc_base/gtest_prod_util.h" #include "rtc_base/ignore_wundef.h" #include "rtc_base/swap_queue.h" @@ -52,13 +51,16 @@ 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 webrtc::Config& config); - // AudioProcessingImpl takes ownership of capture post processor. - AudioProcessingImpl(const webrtc::Config& config, + AudioProcessingImpl(); + AudioProcessingImpl(const AudioProcessing::Config& config, std::unique_ptr capture_post_processor, std::unique_ptr render_pre_processor, std::unique_ptr echo_control_factory, @@ -66,12 +68,6 @@ class AudioProcessingImpl : public AudioProcessing { std::unique_ptr capture_analyzer); ~AudioProcessingImpl() override; int Initialize() override; - int Initialize(int capture_input_sample_rate_hz, - int capture_output_sample_rate_hz, - int render_sample_rate_hz, - ChannelLayout capture_input_layout, - ChannelLayout capture_output_layout, - ChannelLayout render_input_layout) override; int Initialize(const ProcessingConfig& processing_config) override; void ApplyConfig(const AudioProcessing::Config& config) override; bool CreateAndAttachAecDump(const std::string& file_name, @@ -186,7 +182,7 @@ class AudioProcessingImpl : public AudioProcessing { SwapQueue& runtime_settings_; }; - std::unique_ptr data_dumper_; + const std::unique_ptr data_dumper_; static int instance_count_; const bool use_setup_specific_default_aec3_config_; @@ -199,7 +195,7 @@ class AudioProcessingImpl : public AudioProcessing { RuntimeSettingEnqueuer render_runtime_settings_enqueuer_; // EchoControl factory. - std::unique_ptr echo_control_factory_; + const std::unique_ptr echo_control_factory_; class SubmoduleStates { public: @@ -209,13 +205,11 @@ class AudioProcessingImpl : public AudioProcessing { // Updates the submodule state and returns true if it has changed. bool Update(bool high_pass_filter_enabled, bool mobile_echo_controller_enabled, - bool residual_echo_detector_enabled, bool noise_suppressor_enabled, bool adaptive_gain_controller_enabled, bool gain_controller2_enabled, bool gain_adjustment_enabled, bool echo_controller_enabled, - bool voice_detector_enabled, bool transient_suppressor_enabled); bool CaptureMultiBandSubModulesActive() const; bool CaptureMultiBandProcessingPresent() const; @@ -233,13 +227,11 @@ class AudioProcessingImpl : public AudioProcessing { const bool capture_analyzer_enabled_ = false; bool high_pass_filter_enabled_ = false; bool mobile_echo_controller_enabled_ = false; - bool residual_echo_detector_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 voice_detector_enabled_ = false; bool transient_suppressor_enabled_ = false; bool first_update_ = true; }; @@ -271,15 +263,17 @@ class AudioProcessingImpl : public AudioProcessing { void InitializeEchoController() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); - // Initializations of capture-only submodules, requiring the capture lock + // Initializations of capture-only sub-modules, requiring the capture lock // already acquired. void InitializeHighPassFilter(bool forced_reset) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - void InitializeVoiceDetector() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); void InitializeGainController1() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); void InitializeTransientSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + // Initializes the `GainController2` sub-module. If the sub-module is enabled + // and `config_has_changed` is true, recreates the sub-module. + void InitializeGainController2(bool config_has_changed) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); void InitializeNoiseSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); void InitializeCaptureLevelsAdjuster() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); @@ -393,20 +387,18 @@ class AudioProcessingImpl : public AudioProcessing { render_pre_processor(std::move(render_pre_processor)), capture_analyzer(std::move(capture_analyzer)) {} // Accessed internally from capture or during initialization. + const rtc::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; - rtc::scoped_refptr echo_detector; std::unique_ptr echo_controller; std::unique_ptr echo_control_mobile; std::unique_ptr noise_suppressor; std::unique_ptr transient_suppressor; - std::unique_ptr capture_post_processor; - std::unique_ptr render_pre_processor; - std::unique_ptr capture_analyzer; - std::unique_ptr output_level_estimator; - std::unique_ptr voice_detector; std::unique_ptr capture_levels_adjuster; } submodules_; @@ -418,10 +410,10 @@ class AudioProcessingImpl : public AudioProcessing { struct ApmFormatState { ApmFormatState() : // Format of processing streams at input/output call sites. - api_format({{{kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}}}), + api_format({{{kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}}}), render_processing_format(kSampleRate16kHz, 1) {} ProcessingConfig api_format; StreamConfig render_processing_format; @@ -468,11 +460,6 @@ class AudioProcessingImpl : public AudioProcessing { int playout_volume; int prev_playout_volume; AudioProcessingStats stats; - struct KeyboardInfo { - void Extract(const float* const* data, const StreamConfig& stream_config); - size_t num_keyboard_frames = 0; - const float* keyboard_data = nullptr; - } keyboard_info; int cached_stream_analog_level_ = 0; } capture_ RTC_GUARDED_BY(mutex_capture_); @@ -534,6 +521,9 @@ class AudioProcessingImpl : public AudioProcessing { RmsLevel capture_output_rms_ RTC_GUARDED_BY(mutex_capture_); int capture_rms_interval_counter_ RTC_GUARDED_BY(mutex_capture_) = 0; + AnalogGainStatsReporter analog_gain_stats_reporter_ + RTC_GUARDED_BY(mutex_capture_); + // RingRTC change to RingRTC change to make it possible to share an APM. // See set_capture_output_used in audio_processing.h. // Each "user" (could be anything) has a separate state of whether or not diff --git a/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/modules/audio_processing/audio_processing_impl_locking_unittest.cc index 14c0bd1313..7557e919d6 100644 --- a/modules/audio_processing/audio_processing_impl_locking_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_locking_unittest.cc @@ -24,9 +24,11 @@ #include "test/gtest.h" namespace webrtc { - namespace { +constexpr int kMaxFrameSize = 480; +constexpr int kTestTimeOutLimit = 10 * 60 * 1000; + class AudioProcessingImplLockTest; // Type of the render thread APM API call to use in the test. @@ -305,14 +307,14 @@ class CaptureProcessor { rtc::Event* render_call_event, rtc::Event* capture_call_event, FrameCounters* shared_counters_state, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm); void Process(); private: - static const int kMaxCallDifference = 10; - static const float kCaptureInputFloatLevel; - static const int kCaptureInputFixLevel = 1024; + static constexpr int kMaxCallDifference = 10; + static constexpr float kCaptureInputFloatLevel = 0.03125f; + static constexpr int kCaptureInputFixLevel = 1024; void PrepareFrame(); void CallApmCaptureSide(); @@ -331,13 +333,13 @@ class CaptureProcessor { class StatsProcessor { public: StatsProcessor(RandomGenerator* rand_gen, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm); void Process(); private: RandomGenerator* rand_gen_ = nullptr; - TestConfig* test_config_ = nullptr; + const TestConfig* const test_config_ = nullptr; AudioProcessing* apm_ = nullptr; }; @@ -349,14 +351,14 @@ class RenderProcessor { rtc::Event* render_call_event, rtc::Event* capture_call_event, FrameCounters* shared_counters_state, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm); void Process(); private: - static const int kMaxCallDifference = 10; - static const int kRenderInputFixLevel = 16384; - static const float kRenderInputFloatLevel; + static constexpr int kMaxCallDifference = 10; + static constexpr int kRenderInputFixLevel = 16384; + static constexpr float kRenderInputFloatLevel = 0.5f; void PrepareFrame(); void CallApmRenderSide(); @@ -380,10 +382,6 @@ class AudioProcessingImplLockTest bool MaybeEndTest(); private: - static const int kTestTimeOutLimit = 10 * 60 * 1000; - static const int kMaxFrameSize = 480; - - // ::testing::TestWithParam<> implementation void SetUp() override; void TearDown() override; @@ -428,8 +426,8 @@ class AudioProcessingImplLockTest // Thread related variables. mutable RandomGenerator rand_gen_; + const TestConfig test_config_; rtc::scoped_refptr apm_; - TestConfig test_config_; FrameCounters frame_counters_; RenderProcessor render_thread_state_; CaptureProcessor capture_thread_state_; @@ -476,8 +474,23 @@ void PopulateAudioFrame(float amplitude, } } +AudioProcessing::Config GetApmTestConfig(AecType aec_type) { + AudioProcessing::Config apm_config; + apm_config.echo_canceller.enabled = aec_type != AecType::AecTurnedOff; + apm_config.echo_canceller.mobile_mode = + aec_type == AecType::BasicWebRtcAecSettingsWithAecMobile; + apm_config.gain_controller1.enabled = true; + apm_config.gain_controller1.mode = + AudioProcessing::Config::GainController1::kAdaptiveDigital; + apm_config.noise_suppression.enabled = true; + return apm_config; +} + AudioProcessingImplLockTest::AudioProcessingImplLockTest() - : apm_(AudioProcessingBuilderForTesting().Create()), + : test_config_(GetParam()), + apm_(AudioProcessingBuilderForTesting() + .SetConfig(GetApmTestConfig(test_config_.aec_type)) + .Create()), render_thread_state_(kMaxFrameSize, &rand_gen_, &render_call_event_, @@ -508,23 +521,7 @@ bool AudioProcessingImplLockTest::MaybeEndTest() { return false; } -// Setup of test and APM. -void AudioProcessingImplLockTest::SetUp() { - test_config_ = static_cast(GetParam()); - - AudioProcessing::Config apm_config = apm_->GetConfig(); - apm_config.echo_canceller.enabled = - (test_config_.aec_type != AecType::AecTurnedOff); - apm_config.echo_canceller.mobile_mode = - (test_config_.aec_type == AecType::BasicWebRtcAecSettingsWithAecMobile); - apm_config.gain_controller1.enabled = true; - apm_config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveDigital; - apm_config.noise_suppression.enabled = true; - apm_config.voice_detection.enabled = true; - apm_config.level_estimation.enabled = true; - apm_->ApplyConfig(apm_config); -} +void AudioProcessingImplLockTest::SetUp() {} void AudioProcessingImplLockTest::TearDown() { render_call_event_.Set(); @@ -532,7 +529,7 @@ void AudioProcessingImplLockTest::TearDown() { } StatsProcessor::StatsProcessor(RandomGenerator* rand_gen, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm) : rand_gen_(rand_gen), test_config_(test_config), apm_(apm) {} @@ -557,14 +554,12 @@ void StatsProcessor::Process() { apm_->GetStatistics(); } -const float CaptureProcessor::kCaptureInputFloatLevel = 0.03125f; - CaptureProcessor::CaptureProcessor(int max_frame_size, RandomGenerator* rand_gen, rtc::Event* render_call_event, rtc::Event* capture_call_event, FrameCounters* shared_counters_state, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm) : rand_gen_(rand_gen), render_call_event_(render_call_event), @@ -611,8 +606,7 @@ void CaptureProcessor::PrepareFrame() { // Prepare the audio data. StreamConfig input_stream_config(frame_data_.input_sample_rate_hz, - frame_data_.input_number_of_channels, - /*has_keyboard=*/false); + frame_data_.input_number_of_channels); PopulateAudioFrame(kCaptureInputFixLevel, input_stream_config.num_channels(), input_stream_config.num_frames(), frame_data_.frame, @@ -636,11 +630,9 @@ void CaptureProcessor::CallApmCaptureSide() { // Call the specified capture side API processing method. StreamConfig input_stream_config(frame_data_.input_sample_rate_hz, - frame_data_.input_number_of_channels, - /*has_keyboard=*/false); + frame_data_.input_number_of_channels); StreamConfig output_stream_config(frame_data_.output_sample_rate_hz, - frame_data_.output_number_of_channels, - /*has_keyboard=*/false); + frame_data_.output_number_of_channels); int result = AudioProcessing::kNoError; switch (test_config_->capture_api_function) { case CaptureApiImpl::ProcessStreamImplInteger: @@ -788,14 +780,12 @@ void CaptureProcessor::ApplyRuntimeSettingScheme() { frame_data_.input_number_of_channels); } -const float RenderProcessor::kRenderInputFloatLevel = 0.5f; - RenderProcessor::RenderProcessor(int max_frame_size, RandomGenerator* rand_gen, rtc::Event* render_call_event, rtc::Event* capture_call_event, FrameCounters* shared_counters_state, - TestConfig* test_config, + const TestConfig* test_config, AudioProcessing* apm) : rand_gen_(rand_gen), render_call_event_(render_call_event), @@ -853,8 +843,7 @@ void RenderProcessor::PrepareFrame() { // Prepare the audio data. StreamConfig input_stream_config(frame_data_.input_sample_rate_hz, - frame_data_.input_number_of_channels, - /*has_keyboard=*/false); + frame_data_.input_number_of_channels); PopulateAudioFrame(kRenderInputFixLevel, input_stream_config.num_channels(), input_stream_config.num_frames(), frame_data_.frame, @@ -872,11 +861,9 @@ void RenderProcessor::CallApmRenderSide() { // Call the specified render side API processing method. StreamConfig input_stream_config(frame_data_.input_sample_rate_hz, - frame_data_.input_number_of_channels, - /*has_keyboard=*/false); + frame_data_.input_number_of_channels); StreamConfig output_stream_config(frame_data_.output_sample_rate_hz, - frame_data_.output_number_of_channels, - /*has_keyboard=*/false); + frame_data_.output_number_of_channels); int result = AudioProcessing::kNoError; switch (test_config_->render_api_function) { case RenderApiImpl::ProcessReverseStreamImplInteger: @@ -1004,7 +991,7 @@ void RenderProcessor::ApplyRuntimeSettingScheme() { frame_data_.input_number_of_channels); } -} // anonymous namespace +} // namespace TEST_P(AudioProcessingImplLockTest, LockTest) { // Run test and verify that it did not time out. diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index c68ce41652..33d46fb4f8 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -14,7 +14,6 @@ #include #include "api/scoped_refptr.h" -#include "modules/audio_processing/common.h" #include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/optionally_built_submodule_creators.h" #include "modules/audio_processing/test/audio_processing_builder_for_testing.h" @@ -35,8 +34,7 @@ using ::testing::NotNull; class MockInitialize : public AudioProcessingImpl { public: - explicit MockInitialize(const webrtc::Config& config) - : AudioProcessingImpl(config) {} + MockInitialize() : AudioProcessingImpl() {} MOCK_METHOD(void, InitializeLocked, (), (override)); void RealInitializeLocked() { @@ -132,42 +130,41 @@ class TestRenderPreProcessor : public CustomProcessing { } // namespace TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) { - webrtc::Config webrtc_config; - MockInitialize mock(webrtc_config); - ON_CALL(mock, InitializeLocked()) + MockInitialize mock; + ON_CALL(mock, InitializeLocked) .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked)); - EXPECT_CALL(mock, InitializeLocked()).Times(1); + EXPECT_CALL(mock, InitializeLocked).Times(1); mock.Initialize(); constexpr size_t kMaxSampleRateHz = 32000; constexpr size_t kMaxNumChannels = 2; std::array frame; frame.fill(0); - StreamConfig config(16000, 1, /*has_keyboard=*/false); + StreamConfig config(16000, 1); // Call with the default parameters; there should be an init. - EXPECT_CALL(mock, InitializeLocked()).Times(0); + EXPECT_CALL(mock, InitializeLocked).Times(0); EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); EXPECT_NOERR( mock.ProcessReverseStream(frame.data(), config, config, frame.data())); // New sample rate. (Only impacts ProcessStream). - config = StreamConfig(32000, 1, /*has_keyboard=*/false); - EXPECT_CALL(mock, InitializeLocked()).Times(1); + config = StreamConfig(32000, 1); + EXPECT_CALL(mock, InitializeLocked).Times(1); EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); // New number of channels. // TODO(peah): Investigate why this causes 2 inits. - config = StreamConfig(32000, 2, /*has_keyboard=*/false); - EXPECT_CALL(mock, InitializeLocked()).Times(2); + config = StreamConfig(32000, 2); + EXPECT_CALL(mock, InitializeLocked).Times(2); EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); // ProcessStream sets num_channels_ == num_output_channels. EXPECT_NOERR( mock.ProcessReverseStream(frame.data(), config, config, frame.data())); // A new sample rate passed to ProcessReverseStream should cause an init. - config = StreamConfig(16000, 2, /*has_keyboard=*/false); - EXPECT_CALL(mock, InitializeLocked()).Times(1); + config = StreamConfig(16000, 2); + EXPECT_CALL(mock, InitializeLocked).Times(1); EXPECT_NOERR( mock.ProcessReverseStream(frame.data(), config, config, frame.data())); } @@ -185,7 +182,7 @@ TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) { constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); apm->ProcessStream(frame.data(), config, config, frame.data()); EXPECT_EQ(frame[100], kAudioLevel) @@ -218,7 +215,7 @@ TEST(AudioProcessingImplTest, constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); apm->ProcessStream(frame.data(), config, config, frame.data()); EXPECT_EQ(frame[100], kAudioLevel) @@ -251,7 +248,7 @@ TEST(AudioProcessingImplTest, constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); apm->ProcessStream(frame.data(), config, config, frame.data()); EXPECT_EQ(frame[100], kAudioLevel) @@ -286,7 +283,7 @@ TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) { constexpr int kSampleRateHz = 48000; constexpr int kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); @@ -375,7 +372,7 @@ TEST(AudioProcessingImplTest, constexpr size_t kSampleRateHz = 48000; constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); @@ -418,7 +415,7 @@ TEST(AudioProcessingImplTest, constexpr size_t kSampleRateHz = 48000; constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false); + StreamConfig config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); @@ -462,8 +459,7 @@ TEST(AudioProcessingImplTest, constexpr size_t kSampleRateHz = 48000; constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig stream_config(kSampleRateHz, kNumChannels, - /*has_keyboard=*/false); + StreamConfig stream_config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); @@ -505,8 +501,7 @@ TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) { constexpr size_t kSampleRateHz = 48000; constexpr size_t kNumChannels = 2; std::array frame; - StreamConfig stream_config(kSampleRateHz, kNumChannels, - /*has_keyboard=*/false); + StreamConfig stream_config(kSampleRateHz, kNumChannels); frame.fill(kAudioLevel); MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); @@ -556,7 +551,6 @@ TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { .Create(); webrtc::AudioProcessing::Config apm_config; apm_config.pre_amplifier.enabled = true; - apm_config.residual_echo_detector.enabled = true; apm->ApplyConfig(apm_config); constexpr int16_t kAudioLevel = 1000; @@ -564,16 +558,15 @@ TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { constexpr size_t kNumChannels = 1; // Explicitly initialize APM to ensure no render frames are discarded. const ProcessingConfig processing_config = {{ - {kSampleRateHz, kNumChannels, /*has_keyboard=*/false}, - {kSampleRateHz, kNumChannels, /*has_keyboard=*/false}, - {kSampleRateHz, kNumChannels, /*has_keyboard=*/false}, - {kSampleRateHz, kNumChannels, /*has_keyboard=*/false}, + {kSampleRateHz, kNumChannels}, + {kSampleRateHz, kNumChannels}, + {kSampleRateHz, kNumChannels}, + {kSampleRateHz, kNumChannels}, }}; apm->Initialize(processing_config); std::array frame; - StreamConfig stream_config(kSampleRateHz, kNumChannels, - /*has_keyboard=*/false); + StreamConfig stream_config(kSampleRateHz, kNumChannels); constexpr float kAudioLevelFloat = static_cast(kAudioLevel); constexpr float kExpectedPreprocessedAudioLevel = @@ -605,7 +598,7 @@ TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { // config should be bit-exact with running APM with said submodules disabled. // This mainly tests that SetCreateOptionalSubmodulesForTesting has an effect. TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) { - auto apm = rtc::make_ref_counted(webrtc::Config()); + auto apm = rtc::make_ref_counted(); ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError); ApmSubmoduleCreationOverrides overrides; @@ -629,8 +622,7 @@ TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) { float* channel_pointers[] = {buffer.data()}; float* channel_pointers_reference[] = {buffer_reference.data()}; StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz, - /*num_channels=*/kNumChannels, - /*has_keyboard=*/false); + /*num_channels=*/kNumChannels); Random random_generator(2341U); constexpr int kFramesToProcessPerConfiguration = 10; @@ -653,7 +645,7 @@ TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) { // Disable transient suppressor creation and run APM in ways that should trigger // calls to the transient suppressor API. TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) { - auto apm = rtc::make_ref_counted(webrtc::Config()); + auto apm = rtc::make_ref_counted(); ASSERT_EQ(apm->Initialize(), kNoErr); ApmSubmoduleCreationOverrides overrides; @@ -670,8 +662,7 @@ TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) { constexpr int kFramesToProcessPerConfiguration = 3; StreamConfig initial_stream_config(/*sample_rate_hz=*/16000, - /*num_channels=*/1, - /*has_keyboard=*/false); + /*num_channels=*/1); for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) { RandomizeSampleVector(&random_generator, buffer); EXPECT_EQ(apm->ProcessStream(channel_pointers, initial_stream_config, @@ -680,8 +671,7 @@ TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) { } StreamConfig stereo_stream_config(/*sample_rate_hz=*/16000, - /*num_channels=*/2, - /*has_keyboard=*/false); + /*num_channels=*/2); for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) { RandomizeSampleVector(&random_generator, buffer); EXPECT_EQ(apm->ProcessStream(channel_pointers, stereo_stream_config, @@ -690,8 +680,7 @@ TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) { } StreamConfig high_sample_rate_stream_config(/*sample_rate_hz=*/48000, - /*num_channels=*/1, - /*has_keyboard=*/false); + /*num_channels=*/2); for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) { RandomizeSampleVector(&random_generator, buffer); EXPECT_EQ( @@ -699,22 +688,12 @@ TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) { high_sample_rate_stream_config, channel_pointers), kNoErr); } - - StreamConfig keyboard_stream_config(/*sample_rate_hz=*/16000, - /*num_channels=*/1, - /*has_keyboard=*/true); - for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) { - RandomizeSampleVector(&random_generator, buffer); - EXPECT_EQ(apm->ProcessStream(channel_pointers, keyboard_stream_config, - keyboard_stream_config, channel_pointers), - kNoErr); - } } // Disable transient suppressor creation and run APM in ways that should trigger // calls to the transient suppressor API. TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) { - auto apm = rtc::make_ref_counted(webrtc::Config()); + auto apm = rtc::make_ref_counted(); ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError); ApmSubmoduleCreationOverrides overrides; @@ -727,8 +706,7 @@ TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) { Random random_generator(2341U); constexpr int kFramesToProcessPerConfiguration = 3; StreamConfig stream_config(/*sample_rate_hz=*/16000, - /*num_channels=*/1, - /*has_keyboard=*/false); + /*num_channels=*/1); AudioProcessing::Config config = apm->GetConfig(); config.transient_suppression.enabled = true; diff --git a/modules/audio_processing/audio_processing_performance_unittest.cc b/modules/audio_processing/audio_processing_performance_unittest.cc index b79acc7182..57655aea6d 100644 --- a/modules/audio_processing/audio_processing_performance_unittest.cc +++ b/modules/audio_processing/audio_processing_performance_unittest.cc @@ -343,7 +343,6 @@ class TimedThreadApiProcessor { frame_data_.input_stream_config.set_sample_rate_hz( simulation_config_->sample_rate_hz); frame_data_.input_stream_config.set_num_channels(num_channels_); - frame_data_.input_stream_config.set_has_keyboard(false); populate_audio_frame(input_level_, num_channels_, (simulation_config_->sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000), @@ -353,7 +352,6 @@ class TimedThreadApiProcessor { frame_data_.output_stream_config.set_sample_rate_hz( simulation_config_->sample_rate_hz); frame_data_.output_stream_config.set_num_channels(1); - frame_data_.output_stream_config.set_has_keyboard(false); } bool ReadyToProcess() { @@ -367,7 +365,7 @@ class TimedThreadApiProcessor { // Should not be reached, but the return statement is needed for the code to // build successfully on Android. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -443,8 +441,6 @@ class CallSimulator : public ::testing::TestWithParam { apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveDigital; - apm_config.level_estimation.enabled = true; - apm_config.voice_detection.enabled = true; apm->ApplyConfig(apm_config); }; @@ -456,8 +452,6 @@ class CallSimulator : public ::testing::TestWithParam { apm_config.noise_suppression.enabled = true; apm_config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveDigital; - apm_config.level_estimation.enabled = true; - apm_config.voice_detection.enabled = true; apm->ApplyConfig(apm_config); }; @@ -467,9 +461,7 @@ class CallSimulator : public ::testing::TestWithParam { AudioProcessing::Config apm_config = apm->GetConfig(); apm_config.echo_canceller.enabled = false; apm_config.gain_controller1.enabled = false; - apm_config.level_estimation.enabled = false; apm_config.noise_suppression.enabled = false; - apm_config.voice_detection.enabled = false; apm->ApplyConfig(apm_config); }; @@ -482,8 +474,7 @@ class CallSimulator : public ::testing::TestWithParam { break; } case SettingsType::kDefaultApmDesktop: { - Config config; - apm_ = AudioProcessingBuilderForTesting().Create(config); + apm_ = AudioProcessingBuilderForTesting().Create(); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); break; @@ -495,15 +486,13 @@ class CallSimulator : public ::testing::TestWithParam { break; } case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic: { - Config config; - apm_ = AudioProcessingBuilderForTesting().Create(config); + apm_ = AudioProcessingBuilderForTesting().Create(); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); break; } case SettingsType::kDefaultApmDesktopWithoutExtendedFilter: { - Config config; - apm_ = AudioProcessingBuilderForTesting().Create(config); + apm_ = AudioProcessingBuilderForTesting().Create(); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); break; diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index 5af8aa9ddb..b21a0227c5 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -20,13 +20,13 @@ #include #include "absl/flags/flag.h" +#include "api/audio/echo_detector_creator.h" #include "common_audio/include/audio_util.h" #include "common_audio/resampler/include/push_resampler.h" #include "common_audio/resampler/push_sinc_resampler.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "modules/audio_processing/aec_dump/aec_dump_factory.h" #include "modules/audio_processing/audio_processing_impl.h" -#include "modules/audio_processing/common.h" #include "modules/audio_processing/include/mock_audio_processing.h" #include "modules/audio_processing/test/audio_processing_builder_for_testing.h" #include "modules/audio_processing/test/protobuf_utils.h" @@ -94,21 +94,6 @@ void ConvertToFloat(const Int16FrameData& frame, ChannelBuffer* cb) { ConvertToFloat(frame.data.data(), cb); } -// Number of channels including the keyboard channel. -size_t TotalChannelsFromLayout(AudioProcessing::ChannelLayout layout) { - switch (layout) { - case AudioProcessing::kMono: - return 1; - case AudioProcessing::kMonoAndKeyboard: - case AudioProcessing::kStereo: - return 2; - case AudioProcessing::kStereoAndKeyboard: - return 3; - } - RTC_NOTREACHED(); - return 0; -} - void MixStereoToMono(const float* stereo, float* mono, size_t samples_per_channel) { @@ -200,15 +185,11 @@ void EnableAllAPComponents(AudioProcessing* ap) { apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveAnalog; - apm_config.gain_controller1.analog_level_minimum = 0; - apm_config.gain_controller1.analog_level_maximum = 255; #endif apm_config.noise_suppression.enabled = true; apm_config.high_pass_filter.enabled = true; - apm_config.level_estimation.enabled = true; - apm_config.voice_detection.enabled = true; apm_config.pipeline.maximum_internal_processing_rate = 48000; ap->ApplyConfig(apm_config); } @@ -275,7 +256,7 @@ std::string OutputFilePath(const std::string& name, } else if (num_output_channels == 2) { ss << "stereo"; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } ss << output_rate / 1000; if (num_reverse_output_channels == 1) { @@ -283,7 +264,7 @@ std::string OutputFilePath(const std::string& name, } else if (num_reverse_output_channels == 2) { ss << "_rstereo"; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } ss << reverse_output_rate / 1000; ss << "_d" << file_direction << "_pcm"; @@ -418,8 +399,8 @@ class ApmTest : public ::testing::Test { rtc::scoped_refptr apm_; Int16FrameData frame_; Int16FrameData revframe_; - std::unique_ptr > float_cb_; - std::unique_ptr > revfloat_cb_; + std::unique_ptr> float_cb_; + std::unique_ptr> revfloat_cb_; int output_sample_rate_hz_; size_t num_output_channels_; FILE* far_file_; @@ -1080,18 +1061,15 @@ TEST_F(ApmTest, GainControl) { EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kFloatFormat)); } - // Testing level limits - std::array kMinLevels = {0, 0, 255, 65000}; - std::array kMaxLevels = {255, 1024, 65535, 65535}; - for (size_t i = 0; i < kMinLevels.size(); ++i) { - int min_level = kMinLevels[i]; - int max_level = kMaxLevels[i]; - config.gain_controller1.analog_level_minimum = min_level; - config.gain_controller1.analog_level_maximum = max_level; - apm_->ApplyConfig(config); - apm_->set_stream_analog_level((min_level + max_level) / 2); - EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kFloatFormat)); - } + // Testing level limits. + constexpr int kMinLevel = 0; + constexpr int kMaxLevel = 255; + apm_->set_stream_analog_level(kMinLevel); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kFloatFormat)); + apm_->set_stream_analog_level((kMinLevel + kMaxLevel) / 2); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kFloatFormat)); + apm_->set_stream_analog_level(kMaxLevel); + EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kFloatFormat)); } #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) @@ -1125,44 +1103,18 @@ TEST_F(ApmDeathTest, GainControlDiesOnTooHighCompressionGainDb) { EXPECT_DEATH(apm_->ApplyConfig(config), ""); } -TEST_F(ApmDeathTest, GainControlDiesOnTooLowAnalogLevelLowerLimit) { - auto config = apm_->GetConfig(); - config.gain_controller1.enabled = true; - config.gain_controller1.analog_level_minimum = -1; - EXPECT_DEATH(apm_->ApplyConfig(config), ""); -} - -TEST_F(ApmDeathTest, GainControlDiesOnTooHighAnalogLevelUpperLimit) { - auto config = apm_->GetConfig(); - config.gain_controller1.enabled = true; - config.gain_controller1.analog_level_maximum = 65536; - EXPECT_DEATH(apm_->ApplyConfig(config), ""); -} - -TEST_F(ApmDeathTest, GainControlDiesOnInvertedAnalogLevelLimits) { - auto config = apm_->GetConfig(); - config.gain_controller1.enabled = true; - config.gain_controller1.analog_level_minimum = 512; - config.gain_controller1.analog_level_maximum = 255; - EXPECT_DEATH(apm_->ApplyConfig(config), ""); -} - TEST_F(ApmDeathTest, ApmDiesOnTooLowAnalogLevel) { auto config = apm_->GetConfig(); config.gain_controller1.enabled = true; - config.gain_controller1.analog_level_minimum = 255; - config.gain_controller1.analog_level_maximum = 512; apm_->ApplyConfig(config); - EXPECT_DEATH(apm_->set_stream_analog_level(254), ""); + EXPECT_DEATH(apm_->set_stream_analog_level(-1), ""); } TEST_F(ApmDeathTest, ApmDiesOnTooHighAnalogLevel) { auto config = apm_->GetConfig(); config.gain_controller1.enabled = true; - config.gain_controller1.analog_level_minimum = 255; - config.gain_controller1.analog_level_maximum = 512; apm_->ApplyConfig(config); - EXPECT_DEATH(apm_->set_stream_analog_level(513), ""); + EXPECT_DEATH(apm_->set_stream_analog_level(256), ""); } #endif @@ -1272,9 +1224,7 @@ TEST_F(ApmTest, AllProcessingDisabledByDefault) { EXPECT_FALSE(config.echo_canceller.enabled); EXPECT_FALSE(config.high_pass_filter.enabled); EXPECT_FALSE(config.gain_controller1.enabled); - EXPECT_FALSE(config.level_estimation.enabled); EXPECT_FALSE(config.noise_suppression.enabled); - EXPECT_FALSE(config.voice_detection.enabled); } TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabled) { @@ -1399,7 +1349,6 @@ TEST_F(ApmTest, SplittingFilter) { auto apm_config = apm_->GetConfig(); SetFrameTo(&frame_, 1000); frame_copy.CopyFrom(frame_); - apm_config.level_estimation.enabled = true; apm_->ApplyConfig(apm_config); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream( @@ -1414,51 +1363,6 @@ TEST_F(ApmTest, SplittingFilter) { StreamConfig(frame_.sample_rate_hz, frame_.num_channels), frame_.data.data())); EXPECT_TRUE(FrameDataAreEqual(frame_, frame_copy)); - apm_config.level_estimation.enabled = false; - apm_->ApplyConfig(apm_config); - - // 3. Only GetStatistics-reporting VAD is enabled... - SetFrameTo(&frame_, 1000); - frame_copy.CopyFrom(frame_); - apm_config.voice_detection.enabled = true; - apm_->ApplyConfig(apm_config); - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream( - frame_.data.data(), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - frame_.data.data())); - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream( - frame_.data.data(), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - frame_.data.data())); - EXPECT_TRUE(FrameDataAreEqual(frame_, frame_copy)); - apm_config.voice_detection.enabled = false; - apm_->ApplyConfig(apm_config); - - // 4. Both the VAD and the level estimator are enabled... - SetFrameTo(&frame_, 1000); - frame_copy.CopyFrom(frame_); - apm_config.voice_detection.enabled = true; - apm_config.level_estimation.enabled = true; - apm_->ApplyConfig(apm_config); - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream( - frame_.data.data(), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - frame_.data.data())); - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream( - frame_.data.data(), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - StreamConfig(frame_.sample_rate_hz, frame_.num_channels), - frame_.data.data())); - EXPECT_TRUE(FrameDataAreEqual(frame_, frame_copy)); - apm_config.voice_detection.enabled = false; - apm_config.level_estimation.enabled = false; apm_->ApplyConfig(apm_config); // Check the test is valid. We should have distortion from the filter @@ -1773,7 +1677,9 @@ TEST_F(ApmTest, Process) { if (test->num_input_channels() != test->num_output_channels()) continue; - apm_ = AudioProcessingBuilderForTesting().Create(); + apm_ = AudioProcessingBuilderForTesting() + .SetEchoDetector(CreateEchoDetector()) + .Create(); AudioProcessing::Config apm_config = apm_->GetConfig(); apm_config.gain_controller1.analog_gain_controller.enabled = false; apm_->ApplyConfig(apm_config); @@ -1786,11 +1692,9 @@ TEST_F(ApmTest, Process) { static_cast(test->num_reverse_channels()), true); int frame_count = 0; - int has_voice_count = 0; int analog_level = 127; int analog_level_average = 0; int max_output_average = 0; - float rms_dbfs_average = 0.0f; #if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) int stats_index = 0; #endif @@ -1823,10 +1727,6 @@ TEST_F(ApmTest, Process) { analog_level = apm_->recommended_stream_analog_level(); analog_level_average += analog_level; AudioProcessingStats stats = apm_->GetStatistics(); - EXPECT_TRUE(stats.voice_detected); - EXPECT_TRUE(stats.output_rms_dbfs); - has_voice_count += *stats.voice_detected ? 1 : 0; - rms_dbfs_average += *stats.output_rms_dbfs; size_t frame_size = frame_.samples_per_channel * frame_.num_channels; size_t write_count = @@ -1841,16 +1741,16 @@ TEST_F(ApmTest, Process) { const int kStatsAggregationFrameNum = 100; // 1 second. if (frame_count % kStatsAggregationFrameNum == 0) { // Get echo and delay metrics. - AudioProcessingStats stats = apm_->GetStatistics(); + AudioProcessingStats stats2 = apm_->GetStatistics(); // Echo metrics. - const float echo_return_loss = stats.echo_return_loss.value_or(-1.0f); + const float echo_return_loss = stats2.echo_return_loss.value_or(-1.0f); const float echo_return_loss_enhancement = - stats.echo_return_loss_enhancement.value_or(-1.0f); + stats2.echo_return_loss_enhancement.value_or(-1.0f); const float residual_echo_likelihood = - stats.residual_echo_likelihood.value_or(-1.0f); + stats2.residual_echo_likelihood.value_or(-1.0f); const float residual_echo_likelihood_recent_max = - stats.residual_echo_likelihood_recent_max.value_or(-1.0f); + stats2.residual_echo_likelihood_recent_max.value_or(-1.0f); if (!absl::GetFlag(FLAGS_write_apm_ref_data)) { const audioproc::Test::EchoMetrics& reference = @@ -1879,47 +1779,28 @@ TEST_F(ApmTest, Process) { } max_output_average /= frame_count; analog_level_average /= frame_count; - rms_dbfs_average /= frame_count; if (!absl::GetFlag(FLAGS_write_apm_ref_data)) { const int kIntNear = 1; - // When running the test on a N7 we get a {2, 6} difference of - // `has_voice_count` and `max_output_average` is up to 18 higher. - // All numbers being consistently higher on N7 compare to ref_data. + // All numbers being consistently higher on N7 compare to the reference + // data. // TODO(bjornv): If we start getting more of these offsets on Android we // should consider a different approach. Either using one slack for all, // or generate a separate android reference. #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) - const int kHasVoiceCountOffset = 3; - const int kHasVoiceCountNear = 8; const int kMaxOutputAverageOffset = 9; const int kMaxOutputAverageNear = 26; #else - const int kHasVoiceCountOffset = 0; - const int kHasVoiceCountNear = kIntNear; const int kMaxOutputAverageOffset = 0; const int kMaxOutputAverageNear = kIntNear; #endif - EXPECT_NEAR(test->has_voice_count(), - has_voice_count - kHasVoiceCountOffset, kHasVoiceCountNear); - EXPECT_NEAR(test->analog_level_average(), analog_level_average, kIntNear); EXPECT_NEAR(test->max_output_average(), max_output_average - kMaxOutputAverageOffset, kMaxOutputAverageNear); -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - const double kFloatNear = 0.002; - EXPECT_NEAR(test->rms_dbfs_average(), rms_dbfs_average, kFloatNear); -#endif } else { - test->set_has_voice_count(has_voice_count); - test->set_analog_level_average(analog_level_average); test->set_max_output_average(max_output_average); - -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - test->set_rms_dbfs_average(rms_dbfs_average); -#endif } rewind(far_file_); @@ -1931,44 +1812,6 @@ TEST_F(ApmTest, Process) { } } -TEST_F(ApmTest, NoErrorsWithKeyboardChannel) { - struct ChannelFormat { - AudioProcessing::ChannelLayout in_layout; - AudioProcessing::ChannelLayout out_layout; - }; - ChannelFormat cf[] = { - {AudioProcessing::kMonoAndKeyboard, AudioProcessing::kMono}, - {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kMono}, - {AudioProcessing::kStereoAndKeyboard, AudioProcessing::kStereo}, - }; - - rtc::scoped_refptr ap = - AudioProcessingBuilderForTesting().Create(); - // Enable one component just to ensure some processing takes place. - AudioProcessing::Config config; - config.noise_suppression.enabled = true; - ap->ApplyConfig(config); - for (size_t i = 0; i < arraysize(cf); ++i) { - const int in_rate = 44100; - const int out_rate = 48000; - ChannelBuffer in_cb(SamplesFromRate(in_rate), - TotalChannelsFromLayout(cf[i].in_layout)); - ChannelBuffer out_cb(SamplesFromRate(out_rate), - ChannelsFromLayout(cf[i].out_layout)); - bool has_keyboard = cf[i].in_layout == AudioProcessing::kMonoAndKeyboard || - cf[i].in_layout == AudioProcessing::kStereoAndKeyboard; - StreamConfig in_sc(in_rate, ChannelsFromLayout(cf[i].in_layout), - has_keyboard); - StreamConfig out_sc(out_rate, ChannelsFromLayout(cf[i].out_layout)); - - // Run over a few chunks. - for (int j = 0; j < 10; ++j) { - EXPECT_NOERR(ap->ProcessStream(in_cb.channels(), in_sc, out_sc, - out_cb.channels())); - } - } -} - // Compares the reference and test arrays over a region around the expected // delay. Finds the highest SNR in that region and adds the variance and squared // error results to the supplied accumulators. @@ -2489,7 +2332,6 @@ void RunApmRateAndChannelTest( std::vector* frame_data) { cfg->set_sample_rate_hz(sample_rate_hz); cfg->set_num_channels(num_channels); - cfg->set_has_keyboard(false); size_t max_frame_size = ceil(sample_rate_hz / 100.f); channels_data->resize(num_channels * max_frame_size); @@ -2673,14 +2515,13 @@ class MyEchoControlFactory : public EchoControlFactory { TEST(ApmConfiguration, EchoControlInjection) { // Verify that apm uses an injected echo controller if one is provided. - webrtc::Config webrtc_config; std::unique_ptr echo_control_factory( new MyEchoControlFactory()); rtc::scoped_refptr apm = AudioProcessingBuilderForTesting() .SetEchoControlFactory(std::move(echo_control_factory)) - .Create(webrtc_config); + .Create(); Int16FrameData audio; audio.num_channels = 1; @@ -2699,10 +2540,75 @@ TEST(ApmConfiguration, EchoControlInjection) { audio.data.data()); } -rtc::scoped_refptr CreateApm(bool mobile_aec) { - Config old_config; +TEST(ApmConfiguration, EchoDetectorInjection) { + using ::testing::_; + rtc::scoped_refptr mock_echo_detector = + rtc::make_ref_counted<::testing::StrictMock>(); + EXPECT_CALL(*mock_echo_detector, + Initialize(/*capture_sample_rate_hz=*/16000, _, + /*render_sample_rate_hz=*/16000, _)) + .Times(1); rtc::scoped_refptr apm = - AudioProcessingBuilderForTesting().Create(old_config); + AudioProcessingBuilderForTesting() + .SetEchoDetector(mock_echo_detector) + .Create(); + + // The echo detector is included in processing when enabled. + EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_)) + .WillOnce([](rtc::ArrayView render_audio) { + EXPECT_EQ(render_audio.size(), 160u); + }); + EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_)) + .WillOnce([](rtc::ArrayView capture_audio) { + EXPECT_EQ(capture_audio.size(), 160u); + }); + EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(1); + + Int16FrameData frame; + frame.num_channels = 1; + SetFrameSampleRate(&frame, 16000); + + apm->ProcessReverseStream(frame.data.data(), StreamConfig(16000, 1), + StreamConfig(16000, 1), frame.data.data()); + apm->ProcessStream(frame.data.data(), StreamConfig(16000, 1), + StreamConfig(16000, 1), frame.data.data()); + + // When processing rates change, the echo detector is also reinitialized to + // match those. + EXPECT_CALL(*mock_echo_detector, + Initialize(/*capture_sample_rate_hz=*/48000, _, + /*render_sample_rate_hz=*/16000, _)) + .Times(1); + EXPECT_CALL(*mock_echo_detector, + Initialize(/*capture_sample_rate_hz=*/48000, _, + /*render_sample_rate_hz=*/48000, _)) + .Times(1); + EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_)) + .WillOnce([](rtc::ArrayView render_audio) { + EXPECT_EQ(render_audio.size(), 480u); + }); + EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_)) + .Times(2) + .WillRepeatedly([](rtc::ArrayView capture_audio) { + EXPECT_EQ(capture_audio.size(), 480u); + }); + EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(2); + + SetFrameSampleRate(&frame, 48000); + apm->ProcessStream(frame.data.data(), StreamConfig(48000, 1), + StreamConfig(48000, 1), frame.data.data()); + apm->ProcessReverseStream(frame.data.data(), StreamConfig(48000, 1), + StreamConfig(48000, 1), frame.data.data()); + apm->ProcessStream(frame.data.data(), StreamConfig(48000, 1), + StreamConfig(48000, 1), frame.data.data()); +} + +rtc::scoped_refptr CreateApm(bool mobile_aec) { + // Enable residual echo detection, for stats. + rtc::scoped_refptr apm = + AudioProcessingBuilderForTesting() + .SetEchoDetector(CreateEchoDetector()) + .Create(); if (!apm) { return apm; } @@ -2714,17 +2620,14 @@ rtc::scoped_refptr CreateApm(bool mobile_aec) { return nullptr; } - // Disable all components except for an AEC and the residual echo detector. + // Disable all components except for an AEC. AudioProcessing::Config apm_config; - apm_config.residual_echo_detector.enabled = true; apm_config.high_pass_filter.enabled = false; apm_config.gain_controller1.enabled = false; apm_config.gain_controller2.enabled = false; apm_config.echo_canceller.enabled = true; apm_config.echo_canceller.mobile_mode = mobile_aec; apm_config.noise_suppression.enabled = false; - apm_config.level_estimation.enabled = false; - apm_config.voice_detection.enabled = false; apm->ApplyConfig(apm_config); return apm; } @@ -2774,15 +2677,15 @@ TEST(MAYBE_ApmStatistics, AECEnabledTest) { // Test statistics interface. AudioProcessingStats stats = apm->GetStatistics(); // We expect all statistics to be set and have a sensible value. - ASSERT_TRUE(stats.residual_echo_likelihood); + ASSERT_TRUE(stats.residual_echo_likelihood.has_value()); EXPECT_GE(*stats.residual_echo_likelihood, 0.0); EXPECT_LE(*stats.residual_echo_likelihood, 1.0); - ASSERT_TRUE(stats.residual_echo_likelihood_recent_max); + ASSERT_TRUE(stats.residual_echo_likelihood_recent_max.has_value()); EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0); EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0); - ASSERT_TRUE(stats.echo_return_loss); + ASSERT_TRUE(stats.echo_return_loss.has_value()); EXPECT_NE(*stats.echo_return_loss, -100.0); - ASSERT_TRUE(stats.echo_return_loss_enhancement); + ASSERT_TRUE(stats.echo_return_loss_enhancement.has_value()); EXPECT_NE(*stats.echo_return_loss_enhancement, -100.0); } @@ -2823,24 +2726,19 @@ TEST(MAYBE_ApmStatistics, AECMEnabledTest) { AudioProcessingStats stats = apm->GetStatistics(); // We expect only the residual echo detector statistics to be set and have a // sensible value. - EXPECT_TRUE(stats.residual_echo_likelihood); - if (stats.residual_echo_likelihood) { - EXPECT_GE(*stats.residual_echo_likelihood, 0.0); - EXPECT_LE(*stats.residual_echo_likelihood, 1.0); - } - EXPECT_TRUE(stats.residual_echo_likelihood_recent_max); - if (stats.residual_echo_likelihood_recent_max) { - EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0); - EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0); - } - EXPECT_FALSE(stats.echo_return_loss); - EXPECT_FALSE(stats.echo_return_loss_enhancement); + ASSERT_TRUE(stats.residual_echo_likelihood.has_value()); + EXPECT_GE(*stats.residual_echo_likelihood, 0.0); + EXPECT_LE(*stats.residual_echo_likelihood, 1.0); + ASSERT_TRUE(stats.residual_echo_likelihood_recent_max.has_value()); + EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0); + EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0); + EXPECT_FALSE(stats.echo_return_loss.has_value()); + EXPECT_FALSE(stats.echo_return_loss_enhancement.has_value()); } -TEST(ApmStatistics, ReportOutputRmsDbfs) { +TEST(ApmStatistics, DoNotReportVoiceDetectedStat) { ProcessingConfig processing_config = { {{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}}; - AudioProcessing::Config config; // Set up an audioframe. Int16FrameData frame; @@ -2857,91 +2755,53 @@ TEST(ApmStatistics, ReportOutputRmsDbfs) { AudioProcessingBuilderForTesting().Create(); apm->Initialize(processing_config); - // If not enabled, no metric should be reported. + // No metric should be reported. EXPECT_EQ( apm->ProcessStream(frame.data.data(), StreamConfig(frame.sample_rate_hz, frame.num_channels), StreamConfig(frame.sample_rate_hz, frame.num_channels), frame.data.data()), 0); - EXPECT_FALSE(apm->GetStatistics().output_rms_dbfs); - - // If enabled, metrics should be reported. - config.level_estimation.enabled = true; - apm->ApplyConfig(config); - EXPECT_EQ( - apm->ProcessStream(frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - frame.data.data()), - 0); - auto stats = apm->GetStatistics(); - EXPECT_TRUE(stats.output_rms_dbfs); - EXPECT_GE(*stats.output_rms_dbfs, 0); - - // If re-disabled, the value is again not reported. - config.level_estimation.enabled = false; - apm->ApplyConfig(config); - EXPECT_EQ( - apm->ProcessStream(frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - frame.data.data()), - 0); - EXPECT_FALSE(apm->GetStatistics().output_rms_dbfs); + EXPECT_FALSE(apm->GetStatistics().voice_detected.has_value()); } -TEST(ApmStatistics, ReportHasVoice) { - ProcessingConfig processing_config = { - {{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}}; - AudioProcessing::Config config; - - // Set up an audioframe. +TEST(ApmStatistics, GetStatisticsReportsNoEchoDetectorStatsWhenDisabled) { + rtc::scoped_refptr apm = + AudioProcessingBuilderForTesting().Create(); Int16FrameData frame; frame.num_channels = 1; SetFrameSampleRate(&frame, AudioProcessing::NativeRate::kSampleRate32kHz); + ASSERT_EQ( + apm->ProcessStream(frame.data.data(), + StreamConfig(frame.sample_rate_hz, frame.num_channels), + StreamConfig(frame.sample_rate_hz, frame.num_channels), + frame.data.data()), + 0); + // Echo detector is disabled by default, no stats reported. + AudioProcessingStats stats = apm->GetStatistics(); + EXPECT_FALSE(stats.residual_echo_likelihood.has_value()); + EXPECT_FALSE(stats.residual_echo_likelihood_recent_max.has_value()); +} - // Fill the audio frame with a sawtooth pattern. - int16_t* ptr = frame.data.data(); - for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) { - ptr[i] = 10000 * ((i % 3) - 1); - } - +TEST(ApmStatistics, GetStatisticsReportsEchoDetectorStatsWhenEnabled) { + // Create APM with an echo detector injected. rtc::scoped_refptr apm = - AudioProcessingBuilderForTesting().Create(); - apm->Initialize(processing_config); - - // If not enabled, no metric should be reported. - EXPECT_EQ( + AudioProcessingBuilderForTesting() + .SetEchoDetector(CreateEchoDetector()) + .Create(); + Int16FrameData frame; + frame.num_channels = 1; + SetFrameSampleRate(&frame, AudioProcessing::NativeRate::kSampleRate32kHz); + // Echo detector enabled: Report stats. + ASSERT_EQ( apm->ProcessStream(frame.data.data(), StreamConfig(frame.sample_rate_hz, frame.num_channels), StreamConfig(frame.sample_rate_hz, frame.num_channels), frame.data.data()), 0); - EXPECT_FALSE(apm->GetStatistics().voice_detected); - - // If enabled, metrics should be reported. - config.voice_detection.enabled = true; - apm->ApplyConfig(config); - EXPECT_EQ( - apm->ProcessStream(frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - frame.data.data()), - 0); - auto stats = apm->GetStatistics(); - EXPECT_TRUE(stats.voice_detected); - - // If re-disabled, the value is again not reported. - config.voice_detection.enabled = false; - apm->ApplyConfig(config); - EXPECT_EQ( - apm->ProcessStream(frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - StreamConfig(frame.sample_rate_hz, frame.num_channels), - frame.data.data()), - 0); - EXPECT_FALSE(apm->GetStatistics().voice_detected); + AudioProcessingStats stats = apm->GetStatistics(); + EXPECT_TRUE(stats.residual_echo_likelihood.has_value()); + EXPECT_TRUE(stats.residual_echo_likelihood_recent_max.has_value()); } TEST(ApmConfiguration, HandlingOfRateAndChannelCombinations) { @@ -3003,14 +2863,6 @@ TEST(AudioProcessing, GainController1ConfigEqual) { b.enable_limiter = a.enable_limiter; EXPECT_EQ(a, b); - a.analog_level_minimum++; - b.analog_level_minimum = a.analog_level_minimum; - EXPECT_EQ(a, b); - - a.analog_level_maximum--; - b.analog_level_maximum = a.analog_level_maximum; - EXPECT_EQ(a, b); - auto& a_analog = a.analog_gain_controller; auto& b_analog = b.analog_gain_controller; @@ -3057,14 +2909,6 @@ TEST(AudioProcessing, GainController1ConfigNotEqual) { EXPECT_NE(a, b); a = b; - a.analog_level_minimum++; - EXPECT_NE(a, b); - a = b; - - a.analog_level_maximum--; - EXPECT_NE(a, b); - a = b; - auto& a_analog = a.analog_gain_controller; const auto& b_analog = b.analog_gain_controller; @@ -3109,6 +2953,18 @@ TEST(AudioProcessing, GainController2ConfigEqual) { b_adaptive.dry_run = a_adaptive.dry_run; EXPECT_EQ(a, b); + a_adaptive.headroom_db += 1.0f; + b_adaptive.headroom_db = a_adaptive.headroom_db; + EXPECT_EQ(a, b); + + a_adaptive.max_gain_db += 1.0f; + b_adaptive.max_gain_db = a_adaptive.max_gain_db; + EXPECT_EQ(a, b); + + a_adaptive.initial_gain_db += 1.0f; + b_adaptive.initial_gain_db = a_adaptive.initial_gain_db; + EXPECT_EQ(a, b); + a_adaptive.vad_reset_period_ms++; b_adaptive.vad_reset_period_ms = a_adaptive.vad_reset_period_ms; EXPECT_EQ(a, b); @@ -3127,18 +2983,6 @@ TEST(AudioProcessing, GainController2ConfigEqual) { b_adaptive.max_output_noise_level_dbfs = a_adaptive.max_output_noise_level_dbfs; EXPECT_EQ(a, b); - - Toggle(a_adaptive.sse2_allowed); - b_adaptive.sse2_allowed = a_adaptive.sse2_allowed; - EXPECT_EQ(a, b); - - Toggle(a_adaptive.avx2_allowed); - b_adaptive.avx2_allowed = a_adaptive.avx2_allowed; - EXPECT_EQ(a, b); - - Toggle(a_adaptive.neon_allowed); - b_adaptive.neon_allowed = a_adaptive.neon_allowed; - EXPECT_EQ(a, b); } // Checks that one differing parameter is sufficient to make two configs @@ -3166,6 +3010,18 @@ TEST(AudioProcessing, GainController2ConfigNotEqual) { EXPECT_NE(a, b); a_adaptive = b_adaptive; + a_adaptive.headroom_db += 1.0f; + EXPECT_NE(a, b); + a_adaptive = b_adaptive; + + a_adaptive.max_gain_db += 1.0f; + EXPECT_NE(a, b); + a_adaptive = b_adaptive; + + a_adaptive.initial_gain_db += 1.0f; + EXPECT_NE(a, b); + a_adaptive = b_adaptive; + a_adaptive.vad_reset_period_ms++; EXPECT_NE(a, b); a_adaptive = b_adaptive; @@ -3181,18 +3037,6 @@ TEST(AudioProcessing, GainController2ConfigNotEqual) { a_adaptive.max_output_noise_level_dbfs += 1.0f; EXPECT_NE(a, b); a_adaptive = b_adaptive; - - Toggle(a_adaptive.sse2_allowed); - EXPECT_NE(a, b); - a_adaptive = b_adaptive; - - Toggle(a_adaptive.avx2_allowed); - EXPECT_NE(a, b); - a_adaptive = b_adaptive; - - Toggle(a_adaptive.neon_allowed); - EXPECT_NE(a, b); - a_adaptive = b_adaptive; } } // namespace webrtc diff --git a/modules/audio_processing/common.h b/modules/audio_processing/common.h deleted file mode 100644 index 2c88c4e46c..0000000000 --- a/modules/audio_processing/common.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_COMMON_H_ -#define MODULES_AUDIO_PROCESSING_COMMON_H_ - -#include "modules/audio_processing/include/audio_processing.h" -#include "rtc_base/checks.h" - -namespace webrtc { - -constexpr int RuntimeSettingQueueSize() { - return 100; -} - -static inline size_t ChannelsFromLayout(AudioProcessing::ChannelLayout layout) { - switch (layout) { - case AudioProcessing::kMono: - case AudioProcessing::kMonoAndKeyboard: - return 1; - case AudioProcessing::kStereo: - case AudioProcessing::kStereoAndKeyboard: - return 2; - } - RTC_NOTREACHED(); - return 0; -} - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_COMMON_H_ diff --git a/modules/audio_processing/config_unittest.cc b/modules/audio_processing/config_unittest.cc deleted file mode 100644 index 19e9ab37d2..0000000000 --- a/modules/audio_processing/config_unittest.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/audio_processing/include/config.h" - -#include "test/gtest.h" - -namespace webrtc { -namespace { - -struct MyExperiment { - static const ConfigOptionID identifier = ConfigOptionID::kMyExperimentForTest; - static const int kDefaultFactor; - static const int kDefaultOffset; - - MyExperiment() : factor(kDefaultFactor), offset(kDefaultOffset) {} - - MyExperiment(int factor, int offset) : factor(factor), offset(offset) {} - - int factor; - int offset; -}; - -const int MyExperiment::kDefaultFactor = 1; -const int MyExperiment::kDefaultOffset = 2; - -TEST(Config, ReturnsDefaultInstanceIfNotConfigured) { - Config config; - const MyExperiment& my_exp = config.Get(); - EXPECT_EQ(MyExperiment::kDefaultFactor, my_exp.factor); - EXPECT_EQ(MyExperiment::kDefaultOffset, my_exp.offset); -} - -TEST(Config, ReturnOptionWhenSet) { - Config config; - config.Set(new MyExperiment(5, 1)); - const MyExperiment& my_exp = config.Get(); - EXPECT_EQ(5, my_exp.factor); - EXPECT_EQ(1, my_exp.offset); -} - -TEST(Config, SetNullSetsTheOptionBackToDefault) { - Config config; - config.Set(new MyExperiment(5, 1)); - config.Set(NULL); - const MyExperiment& my_exp = config.Get(); - EXPECT_EQ(MyExperiment::kDefaultFactor, my_exp.factor); - EXPECT_EQ(MyExperiment::kDefaultOffset, my_exp.offset); -} - -struct Algo1_CostFunction { - static const ConfigOptionID identifier = - ConfigOptionID::kAlgo1CostFunctionForTest; - Algo1_CostFunction() {} - - virtual int cost(int x) const { return x; } - - virtual ~Algo1_CostFunction() {} -}; - -struct SqrCost : Algo1_CostFunction { - virtual int cost(int x) const { return x * x; } -}; - -TEST(Config, SupportsPolymorphism) { - Config config; - config.Set(new SqrCost()); - EXPECT_EQ(25, config.Get().cost(5)); -} -} // namespace -} // namespace webrtc diff --git a/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc b/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc index 41a8cb8cee..f351811e08 100644 --- a/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc +++ b/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc @@ -68,7 +68,7 @@ void RunBitexactnessTest(int sample_rate_hz, &echo_control_mobile); const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - const StreamConfig render_config(sample_rate_hz, num_channels, false); + const StreamConfig render_config(sample_rate_hz, num_channels); AudioBuffer render_buffer( render_config.sample_rate_hz(), render_config.num_channels(), render_config.sample_rate_hz(), 1, render_config.sample_rate_hz(), 1); @@ -76,7 +76,7 @@ void RunBitexactnessTest(int sample_rate_hz, test::GetApmRenderTestVectorFileName(sample_rate_hz)); std::vector render_input(samples_per_channel * num_channels); - const StreamConfig capture_config(sample_rate_hz, num_channels, false); + const StreamConfig capture_config(sample_rate_hz, num_channels); AudioBuffer capture_buffer( capture_config.sample_rate_hz(), capture_config.num_channels(), capture_config.sample_rate_hz(), 1, capture_config.sample_rate_hz(), 1); diff --git a/modules/audio_processing/echo_control_mobile_impl.cc b/modules/audio_processing/echo_control_mobile_impl.cc index 8116608390..fa5cb8ffec 100644 --- a/modules/audio_processing/echo_control_mobile_impl.cc +++ b/modules/audio_processing/echo_control_mobile_impl.cc @@ -18,7 +18,6 @@ #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/include/audio_processing.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -36,7 +35,7 @@ int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) { case EchoControlMobileImpl::kLoudSpeakerphone: return 4; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } @@ -85,6 +84,9 @@ class EchoControlMobileImpl::Canceller { WebRtcAecm_Free(state_); } + Canceller(const Canceller&) = delete; + Canceller& operator=(const Canceller&) = delete; + void* state() { RTC_DCHECK(state_); return state_; @@ -98,7 +100,6 @@ class EchoControlMobileImpl::Canceller { private: void* state_; - RTC_DISALLOW_COPY_AND_ASSIGN(Canceller); }; EchoControlMobileImpl::EchoControlMobileImpl() diff --git a/modules/audio_processing/gain_control_impl.cc b/modules/audio_processing/gain_control_impl.cc index b5454c05ed..3fac1f7f56 100644 --- a/modules/audio_processing/gain_control_impl.cc +++ b/modules/audio_processing/gain_control_impl.cc @@ -35,7 +35,7 @@ int16_t MapSetting(GainControl::Mode mode) { case GainControl::kFixedDigital: return kAgcModeFixedDigital; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } diff --git a/modules/audio_processing/gain_control_unittest.cc b/modules/audio_processing/gain_control_unittest.cc index 6e0149915c..1662dc506f 100644 --- a/modules/audio_processing/gain_control_unittest.cc +++ b/modules/audio_processing/gain_control_unittest.cc @@ -77,7 +77,7 @@ void RunBitExactnessTest(int sample_rate_hz, analog_level_max, &gain_controller); const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - const StreamConfig render_config(sample_rate_hz, num_channels, false); + const StreamConfig render_config(sample_rate_hz, num_channels); AudioBuffer render_buffer( render_config.sample_rate_hz(), render_config.num_channels(), render_config.sample_rate_hz(), 1, render_config.sample_rate_hz(), 1); @@ -85,7 +85,7 @@ void RunBitExactnessTest(int sample_rate_hz, test::GetApmRenderTestVectorFileName(sample_rate_hz)); std::vector render_input(samples_per_channel * num_channels); - const StreamConfig capture_config(sample_rate_hz, num_channels, false); + const StreamConfig capture_config(sample_rate_hz, num_channels); AudioBuffer capture_buffer( capture_config.sample_rate_hz(), capture_config.num_channels(), capture_config.sample_rate_hz(), 1, capture_config.sample_rate_hz(), 1); diff --git a/modules/audio_processing/gain_controller2.cc b/modules/audio_processing/gain_controller2.cc index 74b63c9432..466e4b0eb4 100644 --- a/modules/audio_processing/gain_controller2.cc +++ b/modules/audio_processing/gain_controller2.cc @@ -10,7 +10,11 @@ #include "modules/audio_processing/gain_controller2.h" +#include +#include + #include "common_audio/include/audio_util.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" @@ -18,20 +22,76 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { +namespace { + +using Agc2Config = AudioProcessing::Config::GainController2; + +constexpr int kUnspecifiedAnalogLevel = -1; +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() { + AvailableCpuFeatures features = GetAvailableCpuFeatures(); + if (field_trial::IsEnabled("WebRTC-Agc2SimdSse2KillSwitch")) { + features.sse2 = false; + } + if (field_trial::IsEnabled("WebRTC-Agc2SimdAvx2KillSwitch")) { + features.avx2 = false; + } + if (field_trial::IsEnabled("WebRTC-Agc2SimdNeonKillSwitch")) { + features.neon = false; + } + return features; +} + +// Creates an adaptive digital gain controller if enabled. +std::unique_ptr CreateAdaptiveDigitalController( + const Agc2Config::AdaptiveDigital& config, + int sample_rate_hz, + int num_channels, + ApmDataDumper* data_dumper) { + if (config.enabled) { + return std::make_unique( + data_dumper, config, sample_rate_hz, num_channels); + } + return nullptr; +} + +} // namespace int GainController2::instance_count_ = 0; -GainController2::GainController2() - : data_dumper_(rtc::AtomicOps::Increment(&instance_count_)), - gain_applier_(/*hard_clip_samples=*/false, - /*initial_gain_factor=*/0.0f), - limiter_(static_cast(48000), &data_dumper_, "Agc2"), - calls_since_last_limiter_log_(0) { - if (config_.adaptive_digital.enabled) { - adaptive_agc_ = - std::make_unique(&data_dumper_, config_.adaptive_digital); +GainController2::GainController2(const Agc2Config& config, + int sample_rate_hz, + int num_channels) + : cpu_features_(GetAllowedCpuFeatures()), + data_dumper_(rtc::AtomicOps::Increment(&instance_count_)), + fixed_gain_applier_( + /*hard_clip_samples=*/false, + /*initial_gain_factor=*/DbToRatio(config.fixed_digital.gain_db)), + adaptive_digital_controller_( + CreateAdaptiveDigitalController(config.adaptive_digital, + sample_rate_hz, + num_channels, + &data_dumper_)), + limiter_(sample_rate_hz, &data_dumper_, /*histogram_name_prefix=*/"Agc2"), + calls_since_last_limiter_log_(0), + analog_level_(kUnspecifiedAnalogLevel) { + RTC_DCHECK(Validate(config)); + data_dumper_.InitiateNewSetOfRecordings(); + const bool use_vad = config.adaptive_digital.enabled; + if (use_vad) { + // TODO(bugs.webrtc.org/7494): Move `vad_reset_period_ms` from adaptive + // digital to gain controller 2 config. + vad_ = std::make_unique( + config.adaptive_digital.vad_reset_period_ms, cpu_features_, + sample_rate_hz); } } @@ -42,29 +102,48 @@ void GainController2::Initialize(int sample_rate_hz, int num_channels) { sample_rate_hz == AudioProcessing::kSampleRate16kHz || sample_rate_hz == AudioProcessing::kSampleRate32kHz || sample_rate_hz == AudioProcessing::kSampleRate48kHz); + // TODO(bugs.webrtc.org/7494): Initialize `fixed_gain_applier_`. limiter_.SetSampleRate(sample_rate_hz); - if (adaptive_agc_) { - adaptive_agc_->Initialize(sample_rate_hz, num_channels); + if (vad_) { + vad_->Initialize(sample_rate_hz); + } + if (adaptive_digital_controller_) { + adaptive_digital_controller_->Initialize(sample_rate_hz, num_channels); } data_dumper_.InitiateNewSetOfRecordings(); - data_dumper_.DumpRaw("sample_rate_hz", sample_rate_hz); calls_since_last_limiter_log_ = 0; + analog_level_ = kUnspecifiedAnalogLevel; +} + +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::Process(AudioBuffer* audio) { data_dumper_.DumpRaw("agc2_notified_analog_level", analog_level_); AudioFrameView float_frame(audio->channels(), audio->num_channels(), audio->num_frames()); - // Apply fixed gain first, then the adaptive one. - gain_applier_.ApplyGain(float_frame); - if (adaptive_agc_) { - adaptive_agc_->Process(float_frame, limiter_.LastAudioLevel()); + absl::optional speech_probability; + if (vad_) { + speech_probability = vad_->Analyze(float_frame); + data_dumper_.DumpRaw("agc2_speech_probability", speech_probability.value()); + } + fixed_gain_applier_.ApplyGain(float_frame); + if (adaptive_digital_controller_) { + RTC_DCHECK(speech_probability.has_value()); + adaptive_digital_controller_->Process( + float_frame, speech_probability.value(), limiter_.LastAudioLevel()); } limiter_.Process(float_frame); - // Log limiter stats every 30 seconds. - ++calls_since_last_limiter_log_; - if (calls_since_last_limiter_log_ == 3000) { + // 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" @@ -76,46 +155,21 @@ void GainController2::Process(AudioBuffer* audio) { } void GainController2::NotifyAnalogLevel(int level) { - if (analog_level_ != level && adaptive_agc_) { - adaptive_agc_->HandleInputGainChange(); + if (analog_level_ != level && adaptive_digital_controller_) { + adaptive_digital_controller_->HandleInputGainChange(); } analog_level_ = level; } -void GainController2::ApplyConfig( - const AudioProcessing::Config::GainController2& config) { - RTC_DCHECK(Validate(config)); - - config_ = config; - if (config.fixed_digital.gain_db != config_.fixed_digital.gain_db) { - // Reset the limiter to quickly react on abrupt level changes caused by - // large changes of the fixed gain. - limiter_.Reset(); - } - gain_applier_.SetGainFactor(DbToRatio(config_.fixed_digital.gain_db)); - if (config_.adaptive_digital.enabled) { - adaptive_agc_ = - std::make_unique(&data_dumper_, config_.adaptive_digital); - } else { - adaptive_agc_.reset(); - } -} - 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.f && fixed.gain_db < 50.f && - adaptive.vad_probability_attack > 0.f && - adaptive.vad_probability_attack <= 1.f && - adaptive.level_estimator_adjacent_speech_frames_threshold >= 1 && - adaptive.initial_saturation_margin_db >= 0.f && - adaptive.initial_saturation_margin_db <= 100.f && - adaptive.extra_saturation_margin_db >= 0.f && - adaptive.extra_saturation_margin_db <= 100.f && - adaptive.gain_applier_adjacent_speech_frames_threshold >= 1 && - adaptive.max_gain_change_db_per_second > 0.f && - adaptive.max_output_noise_level_dbfs <= 0.f; + return fixed.gain_db >= 0.0f && fixed.gain_db < 50.f && + 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/modules/audio_processing/gain_controller2.h b/modules/audio_processing/gain_controller2.h index ce758c7834..8c82d745b5 100644 --- a/modules/audio_processing/gain_controller2.h +++ b/modules/audio_processing/gain_controller2.h @@ -14,12 +14,13 @@ #include #include -#include "modules/audio_processing/agc2/adaptive_agc.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/limiter.h" +#include "modules/audio_processing/agc2/vad_wrapper.h" #include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/logging/apm_data_dumper.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,27 +30,37 @@ class AudioBuffer; // microphone gain and/or applying digital gain. class GainController2 { public: - GainController2(); + GainController2(const AudioProcessing::Config::GainController2& config, + int sample_rate_hz, + int num_channels); GainController2(const GainController2&) = delete; GainController2& operator=(const GainController2&) = delete; ~GainController2(); + // Detects and handles changes of sample rate and/or number of channels. void Initialize(int sample_rate_hz, int num_channels); + + // Sets the fixed digital gain. + void SetFixedGainDb(float gain_db); + + // Applies fixed and adaptive digital gains to `audio` and runs a limiter. void Process(AudioBuffer* audio); + + // Handles analog level changes. void NotifyAnalogLevel(int level); - void ApplyConfig(const AudioProcessing::Config::GainController2& config); static bool Validate(const AudioProcessing::Config::GainController2& config); private: static int instance_count_; + const AvailableCpuFeatures cpu_features_; ApmDataDumper data_dumper_; - AudioProcessing::Config::GainController2 config_; - GainApplier gain_applier_; - std::unique_ptr adaptive_agc_; + GainApplier fixed_gain_applier_; + std::unique_ptr vad_; + std::unique_ptr adaptive_digital_controller_; Limiter limiter_; int calls_since_last_limiter_log_; - int analog_level_ = -1; + int analog_level_; }; } // namespace webrtc diff --git a/modules/audio_processing/gain_controller2_unittest.cc b/modules/audio_processing/gain_controller2_unittest.cc index 8f65a89cde..850562f8dd 100644 --- a/modules/audio_processing/gain_controller2_unittest.cc +++ b/modules/audio_processing/gain_controller2_unittest.cc @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "api/array_view.h" #include "modules/audio_processing/agc2/agc2_testing_common.h" @@ -26,212 +28,138 @@ namespace webrtc { namespace test { namespace { -void SetAudioBufferSamples(float value, AudioBuffer* ab) { - // Sets all the samples in `ab` to `value`. - for (size_t k = 0; k < ab->num_channels(); ++k) { - std::fill(ab->channels()[k], ab->channels()[k] + ab->num_frames(), value); +using Agc2Config = AudioProcessing::Config::GainController2; + +// Sets all the samples in `ab` to `value`. +void SetAudioBufferSamples(float value, AudioBuffer& ab) { + for (size_t k = 0; k < ab.num_channels(); ++k) { + std::fill(ab.channels()[k], ab.channels()[k] + ab.num_frames(), value); } } -float RunAgc2WithConstantInput(GainController2* agc2, +float RunAgc2WithConstantInput(GainController2& agc2, float input_level, - size_t num_frames, - int sample_rate) { - const int num_samples = rtc::CheckedDivExact(sample_rate, 100); - AudioBuffer ab(sample_rate, 1, sample_rate, 1, sample_rate, 1); + int num_frames, + int sample_rate_hz) { + const int num_samples = rtc::CheckedDivExact(sample_rate_hz, 100); + AudioBuffer ab(sample_rate_hz, 1, sample_rate_hz, 1, sample_rate_hz, 1); // Give time to the level estimator to converge. - for (size_t i = 0; i < num_frames + 1; ++i) { - SetAudioBufferSamples(input_level, &ab); - agc2->Process(&ab); + for (int i = 0; i < num_frames + 1; ++i) { + SetAudioBufferSamples(input_level, ab); + agc2.Process(&ab); } // Return the last sample from the last processed frame. return ab.channels()[0][num_samples - 1]; } -AudioProcessing::Config::GainController2 CreateAgc2FixedDigitalModeConfig( - float fixed_gain_db) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.enabled = false; - config.fixed_digital.gain_db = fixed_gain_db; - // TODO(alessiob): Check why ASSERT_TRUE() below does not compile. - EXPECT_TRUE(GainController2::Validate(config)); - return config; -} - std::unique_ptr CreateAgc2FixedDigitalMode( float fixed_gain_db, - size_t sample_rate_hz) { - auto agc2 = std::make_unique(); - agc2->ApplyConfig(CreateAgc2FixedDigitalModeConfig(fixed_gain_db)); - agc2->Initialize(sample_rate_hz, /*num_channels=*/1); - return agc2; -} - -float GainDbAfterProcessingFile(GainController2& gain_controller, - int max_duration_ms) { - // Set up an AudioBuffer to be filled from the speech file. - constexpr size_t kStereo = 2u; - const StreamConfig capture_config(AudioProcessing::kSampleRate48kHz, kStereo, - false); - AudioBuffer ab(capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), - capture_config.num_channels()); - test::InputAudioFile capture_file( - test::GetApmCaptureTestVectorFileName(AudioProcessing::kSampleRate48kHz)); - std::vector capture_input(capture_config.num_frames() * - capture_config.num_channels()); - - // Process the input file which must be long enough to cover - // `max_duration_ms`. - RTC_DCHECK_GT(max_duration_ms, 0); - const int num_frames = rtc::CheckedDivExact(max_duration_ms, 10); - for (int i = 0; i < num_frames; ++i) { - ReadFloatSamplesFromStereoFile(capture_config.num_frames(), - capture_config.num_channels(), &capture_file, - capture_input); - test::CopyVectorToAudioBuffer(capture_config, capture_input, &ab); - gain_controller.Process(&ab); - } - - // Send in a last frame with minimum dBFS level. - constexpr float sample_value = 1.f; - SetAudioBufferSamples(sample_value, &ab); - gain_controller.Process(&ab); - // Measure the RMS level after processing. - float rms = 0.0f; - for (size_t i = 0; i < capture_config.num_frames(); ++i) { - rms += ab.channels()[0][i] * ab.channels()[0][i]; - } - // Return the applied gain in dB. - return 20.0f * std::log10(std::sqrt(rms / capture_config.num_frames())); + int sample_rate_hz) { + Agc2Config config; + config.adaptive_digital.enabled = false; + config.fixed_digital.gain_db = fixed_gain_db; + EXPECT_TRUE(GainController2::Validate(config)); + return std::make_unique(config, sample_rate_hz, + /*num_channels=*/1); } } // namespace TEST(GainController2, CheckDefaultConfig) { - AudioProcessing::Config::GainController2 config; + Agc2Config config; EXPECT_TRUE(GainController2::Validate(config)); } TEST(GainController2, CheckFixedDigitalConfig) { - AudioProcessing::Config::GainController2 config; + Agc2Config config; // Attenuation is not allowed. - config.fixed_digital.gain_db = -5.f; + config.fixed_digital.gain_db = -5.0f; EXPECT_FALSE(GainController2::Validate(config)); // No gain is allowed. - config.fixed_digital.gain_db = 0.f; + config.fixed_digital.gain_db = 0.0f; EXPECT_TRUE(GainController2::Validate(config)); // Positive gain is allowed. - config.fixed_digital.gain_db = 15.f; + config.fixed_digital.gain_db = 15.0f; EXPECT_TRUE(GainController2::Validate(config)); } -TEST(GainController2, CheckAdaptiveDigitalVadProbabilityAttackConfig) { - AudioProcessing::Config::GainController2 config; - // Reject invalid attack. - config.adaptive_digital.vad_probability_attack = -123.f; +TEST(GainController2, CheckHeadroomDb) { + Agc2Config config; + config.adaptive_digital.headroom_db = -1.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.vad_probability_attack = 0.f; - EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.vad_probability_attack = 42.f; - EXPECT_FALSE(GainController2::Validate(config)); - // Accept valid attack. - config.adaptive_digital.vad_probability_attack = 0.1f; + config.adaptive_digital.headroom_db = 0.0f; EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.vad_probability_attack = 1.f; + config.adaptive_digital.headroom_db = 5.0f; EXPECT_TRUE(GainController2::Validate(config)); } -TEST(GainController2, - CheckAdaptiveDigitalLevelEstimatorSpeechFramesThresholdConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.level_estimator_adjacent_speech_frames_threshold = 0; +TEST(GainController2, CheckMaxGainDb) { + Agc2Config config; + config.adaptive_digital.max_gain_db = -1.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.level_estimator_adjacent_speech_frames_threshold = 1; - EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.level_estimator_adjacent_speech_frames_threshold = 7; + config.adaptive_digital.max_gain_db = 0.0f; + EXPECT_FALSE(GainController2::Validate(config)); + config.adaptive_digital.max_gain_db = 5.0f; EXPECT_TRUE(GainController2::Validate(config)); } -TEST(GainController2, CheckAdaptiveDigitalInitialSaturationMarginConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.initial_saturation_margin_db = -1.f; +TEST(GainController2, CheckInitialGainDb) { + Agc2Config config; + config.adaptive_digital.initial_gain_db = -1.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.initial_saturation_margin_db = 0.f; + config.adaptive_digital.initial_gain_db = 0.0f; EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.initial_saturation_margin_db = 50.f; - EXPECT_TRUE(GainController2::Validate(config)); -} - -TEST(GainController2, CheckAdaptiveDigitalExtraSaturationMarginConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.extra_saturation_margin_db = -1.f; - EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.extra_saturation_margin_db = 0.f; - EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.extra_saturation_margin_db = 50.f; - EXPECT_TRUE(GainController2::Validate(config)); -} - -TEST(GainController2, - CheckAdaptiveDigitalGainApplierSpeechFramesThresholdConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.gain_applier_adjacent_speech_frames_threshold = 0; - EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.gain_applier_adjacent_speech_frames_threshold = 1; - EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.gain_applier_adjacent_speech_frames_threshold = 7; + config.adaptive_digital.initial_gain_db = 5.0f; EXPECT_TRUE(GainController2::Validate(config)); } TEST(GainController2, CheckAdaptiveDigitalMaxGainChangeSpeedConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.max_gain_change_db_per_second = -1.f; + Agc2Config config; + config.adaptive_digital.max_gain_change_db_per_second = -1.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.max_gain_change_db_per_second = 0.f; + config.adaptive_digital.max_gain_change_db_per_second = 0.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.max_gain_change_db_per_second = 5.f; + config.adaptive_digital.max_gain_change_db_per_second = 5.0f; EXPECT_TRUE(GainController2::Validate(config)); } TEST(GainController2, CheckAdaptiveDigitalMaxOutputNoiseLevelConfig) { - AudioProcessing::Config::GainController2 config; - config.adaptive_digital.max_output_noise_level_dbfs = 5.f; + Agc2Config config; + config.adaptive_digital.max_output_noise_level_dbfs = 5.0f; EXPECT_FALSE(GainController2::Validate(config)); - config.adaptive_digital.max_output_noise_level_dbfs = 0.f; + config.adaptive_digital.max_output_noise_level_dbfs = 0.0f; EXPECT_TRUE(GainController2::Validate(config)); - config.adaptive_digital.max_output_noise_level_dbfs = -5.f; + config.adaptive_digital.max_output_noise_level_dbfs = -5.0f; EXPECT_TRUE(GainController2::Validate(config)); } // Checks that the default config is applied. TEST(GainController2, ApplyDefaultConfig) { - auto gain_controller2 = std::make_unique(); - AudioProcessing::Config::GainController2 config; - gain_controller2->ApplyConfig(config); + auto gain_controller2 = std::make_unique( + Agc2Config{}, /*sample_rate_hz=*/16000, /*num_channels=*/2); + EXPECT_TRUE(gain_controller2.get()); } TEST(GainController2FixedDigital, GainShouldChangeOnSetGain) { - constexpr float kInputLevel = 1000.f; + constexpr float kInputLevel = 1000.0f; constexpr size_t kNumFrames = 5; constexpr size_t kSampleRateHz = 8000; - constexpr float kGain0Db = 0.f; - constexpr float kGain20Db = 20.f; + constexpr float kGain0Db = 0.0f; + constexpr float kGain20Db = 20.0f; auto agc2_fixed = CreateAgc2FixedDigitalMode(kGain0Db, kSampleRateHz); // Signal level is unchanged with 0 db gain. - EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel, - kNumFrames, kSampleRateHz), + EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(*agc2_fixed, kInputLevel, kNumFrames, + kSampleRateHz), kInputLevel); // +20 db should increase signal by a factor of 10. - agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGain20Db)); - EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel, - kNumFrames, kSampleRateHz), + agc2_fixed->SetFixedGainDb(kGain20Db); + EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(*agc2_fixed, kInputLevel, kNumFrames, + kSampleRateHz), kInputLevel * 10); } @@ -240,67 +168,51 @@ TEST(GainController2FixedDigital, ChangeFixedGainShouldBeFastAndTimeInvariant) { // input signal when the gain changes. constexpr size_t kNumFrames = 5; - constexpr float kInputLevel = 1000.f; + constexpr float kInputLevel = 1000.0f; constexpr size_t kSampleRateHz = 8000; - constexpr float kGainDbLow = 0.f; - constexpr float kGainDbHigh = 25.f; + constexpr float kGainDbLow = 0.0f; + constexpr float kGainDbHigh = 25.0f; static_assert(kGainDbLow < kGainDbHigh, ""); auto agc2_fixed = CreateAgc2FixedDigitalMode(kGainDbLow, kSampleRateHz); // Start with a lower gain. const float output_level_pre = RunAgc2WithConstantInput( - agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz); + *agc2_fixed, kInputLevel, kNumFrames, kSampleRateHz); // Increase gain. - agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbHigh)); - static_cast(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel, + agc2_fixed->SetFixedGainDb(kGainDbHigh); + static_cast(RunAgc2WithConstantInput(*agc2_fixed, kInputLevel, kNumFrames, kSampleRateHz)); // Back to the lower gain. - agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbLow)); + agc2_fixed->SetFixedGainDb(kGainDbLow); const float output_level_post = RunAgc2WithConstantInput( - agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz); + *agc2_fixed, kInputLevel, kNumFrames, kSampleRateHz); EXPECT_EQ(output_level_pre, output_level_post); } -struct FixedDigitalTestParams { - FixedDigitalTestParams(float gain_db_min, - float gain_db_max, - size_t sample_rate, - bool saturation_expected) - : gain_db_min(gain_db_min), - gain_db_max(gain_db_max), - sample_rate(sample_rate), - saturation_expected(saturation_expected) {} - float gain_db_min; - float gain_db_max; - size_t sample_rate; - bool saturation_expected; +class FixedDigitalTest + : public ::testing::TestWithParam> { + protected: + float gain_db_min() const { return std::get<0>(GetParam()); } + float gain_db_max() const { return std::get<1>(GetParam()); } + int sample_rate_hz() const { return std::get<2>(GetParam()); } + bool saturation_expected() const { return std::get<3>(GetParam()); } }; -class FixedDigitalTest - : public ::testing::Test, - public ::testing::WithParamInterface {}; - TEST_P(FixedDigitalTest, CheckSaturationBehaviorWithLimiter) { - const float kInputLevel = 32767.f; - const size_t kNumFrames = 5; - - const auto params = GetParam(); - - const auto gains_db = - test::LinSpace(params.gain_db_min, params.gain_db_max, 10); - for (const auto gain_db : gains_db) { - SCOPED_TRACE(std::to_string(gain_db)); - auto agc2_fixed = CreateAgc2FixedDigitalMode(gain_db, params.sample_rate); - const float processed_sample = RunAgc2WithConstantInput( - agc2_fixed.get(), kInputLevel, kNumFrames, params.sample_rate); - if (params.saturation_expected) { - EXPECT_FLOAT_EQ(processed_sample, 32767.f); + for (const float gain_db : test::LinSpace(gain_db_min(), gain_db_max(), 10)) { + SCOPED_TRACE(gain_db); + auto agc2_fixed = CreateAgc2FixedDigitalMode(gain_db, sample_rate_hz()); + const float processed_sample = + RunAgc2WithConstantInput(*agc2_fixed, /*input_level=*/32767.0f, + /*num_frames=*/5, sample_rate_hz()); + if (saturation_expected()) { + EXPECT_FLOAT_EQ(processed_sample, 32767.0f); } else { - EXPECT_LT(processed_sample, 32767.f); + EXPECT_LT(processed_sample, 32767.0f); } } } @@ -312,40 +224,74 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values( // When gain < `test::kLimiterMaxInputLevelDbFs`, the limiter will not // saturate the signal (at any sample rate). - FixedDigitalTestParams(0.1f, - test::kLimiterMaxInputLevelDbFs - 0.01f, - 8000, - false), - FixedDigitalTestParams(0.1, - test::kLimiterMaxInputLevelDbFs - 0.01f, - 48000, - false), + std::make_tuple(0.1f, + test::kLimiterMaxInputLevelDbFs - 0.01f, + 8000, + false), + std::make_tuple(0.1, + test::kLimiterMaxInputLevelDbFs - 0.01f, + 48000, + false), // When gain > `test::kLimiterMaxInputLevelDbFs`, the limiter will // saturate the signal (at any sample rate). - FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f, - 10.f, - 8000, - true), - FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f, - 10.f, - 48000, - true))); + std::make_tuple(test::kLimiterMaxInputLevelDbFs + 0.01f, + 10.0f, + 8000, + true), + std::make_tuple(test::kLimiterMaxInputLevelDbFs + 0.01f, + 10.0f, + 48000, + true))); -// Checks that the gain applied at the end of a PCM samples file is close to the -// expected value. -TEST(GainController2, CheckGainAdaptiveDigital) { - constexpr float kExpectedGainDb = 4.3f; - constexpr float kToleranceDb = 0.5f; - GainController2 gain_controller2; - gain_controller2.Initialize(AudioProcessing::kSampleRate48kHz, - /*num_channels=*/1); - AudioProcessing::Config::GainController2 config; +// Processes a test audio file and checks that the gain applied at the end of +// the recording is close to the expected value. +TEST(GainController2, CheckFinalGainWithAdaptiveDigitalController) { + constexpr int kSampleRateHz = AudioProcessing::kSampleRate48kHz; + constexpr int kStereo = 2; + + // Create AGC2 enabling only the adaptive digital controller. + Agc2Config config; config.fixed_digital.gain_db = 0.0f; config.adaptive_digital.enabled = true; - gain_controller2.ApplyConfig(config); - EXPECT_NEAR( - GainDbAfterProcessingFile(gain_controller2, /*max_duration_ms=*/2000), - kExpectedGainDb, kToleranceDb); + GainController2 agc2(config, kSampleRateHz, kStereo); + + test::InputAudioFile input_file( + test::GetApmCaptureTestVectorFileName(kSampleRateHz), + /*loop_at_end=*/true); + const StreamConfig stream_config(kSampleRateHz, kStereo); + + // Init buffers. + constexpr int kFrameDurationMs = 10; + std::vector frame(kStereo * stream_config.num_frames()); + AudioBuffer audio_buffer(kSampleRateHz, kStereo, kSampleRateHz, kStereo, + kSampleRateHz, kStereo); + + // Simulate. + constexpr float kGainDb = -6.0f; + const float gain = std::pow(10.0f, kGainDb / 20.0f); + constexpr int kDurationMs = 10000; + constexpr int kNumFramesToProcess = kDurationMs / kFrameDurationMs; + for (int i = 0; i < kNumFramesToProcess; ++i) { + ReadFloatSamplesFromStereoFile(stream_config.num_frames(), + stream_config.num_channels(), &input_file, + frame); + // Apply a fixed gain to the input audio. + for (float& x : frame) + x *= gain; + test::CopyVectorToAudioBuffer(stream_config, frame, &audio_buffer); + // Process. + agc2.Process(&audio_buffer); + } + + // Estimate the applied gain by processing a probing frame. + SetAudioBufferSamples(/*value=*/1.0f, audio_buffer); + agc2.Process(&audio_buffer); + const float applied_gain_db = + 20.0f * std::log10(audio_buffer.channels_const()[0][0]); + + constexpr float kExpectedGainDb = 5.6f; + constexpr float kToleranceDb = 0.3f; + EXPECT_NEAR(applied_gain_db, kExpectedGainDb, kToleranceDb); } } // namespace test diff --git a/modules/audio_processing/high_pass_filter.cc b/modules/audio_processing/high_pass_filter.cc index bff7209e96..3b4740f6a5 100644 --- a/modules/audio_processing/high_pass_filter.cc +++ b/modules/audio_processing/high_pass_filter.cc @@ -44,9 +44,9 @@ const CascadedBiQuadFilter::BiQuadCoefficients& ChooseCoefficients( case 48000: return kHighPassFilterCoefficients48kHz; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kHighPassFilterCoefficients16kHz; } diff --git a/modules/audio_processing/high_pass_filter_unittest.cc b/modules/audio_processing/high_pass_filter_unittest.cc index f8e7226b6c..9f3c8fe595 100644 --- a/modules/audio_processing/high_pass_filter_unittest.cc +++ b/modules/audio_processing/high_pass_filter_unittest.cc @@ -75,7 +75,7 @@ void RunBitexactnessTest(int num_channels, bool use_audio_buffer_interface, const std::vector& input, const std::vector& reference) { - const StreamConfig stream_config(16000, num_channels, false); + const StreamConfig stream_config(16000, num_channels); HighPassFilter high_pass_filter(16000, num_channels); std::vector output; @@ -131,8 +131,8 @@ std::vector CreateVector(const rtc::ArrayView& array_view) { } // namespace TEST(HighPassFilterAccuracyTest, ResetWithAudioBufferInterface) { - const StreamConfig stream_config_stereo(16000, 2, false); - const StreamConfig stream_config_mono(16000, 1, false); + const StreamConfig stream_config_stereo(16000, 2); + const StreamConfig stream_config_mono(16000, 1); std::vector x_mono(160, 1.f); std::vector x_stereo(320, 1.f); HighPassFilter hpf(16000, 1); @@ -147,8 +147,8 @@ TEST(HighPassFilterAccuracyTest, ResetWithAudioBufferInterface) { } TEST(HighPassFilterAccuracyTest, ResetWithVectorInterface) { - const StreamConfig stream_config_stereo(16000, 2, false); - const StreamConfig stream_config_mono(16000, 1, false); + const StreamConfig stream_config_stereo(16000, 2); + const StreamConfig stream_config_mono(16000, 1); std::vector x_mono(160, 1.f); std::vector x_stereo(320, 1.f); HighPassFilter hpf(16000, 1); diff --git a/modules/audio_processing/include/audio_frame_proxies.cc b/modules/audio_processing/include/audio_frame_proxies.cc index b960e72e86..7cc4fb75e4 100644 --- a/modules/audio_processing/include/audio_frame_proxies.cc +++ b/modules/audio_processing/include/audio_frame_proxies.cc @@ -20,10 +20,8 @@ int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame) { return AudioProcessing::Error::kNullPointerError; } - StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_, - /*has_keyboard=*/false); - StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_, - /*has_keyboard=*/false); + 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, @@ -57,10 +55,8 @@ int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame) { return AudioProcessing::Error::kBadNumberChannelsError; } - StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_, - /*has_keyboard=*/false); - StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_, - /*has_keyboard=*/false); + 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()); diff --git a/modules/audio_processing/include/audio_frame_view.h b/modules/audio_processing/include/audio_frame_view.h index 9786cd9d9d..164784a7cc 100644 --- a/modules/audio_processing/include/audio_frame_view.h +++ b/modules/audio_processing/include/audio_frame_view.h @@ -22,12 +22,13 @@ class AudioFrameView { // `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. - AudioFrameView(T* const* audio_samples, - size_t num_channels, - size_t channel_size) + AudioFrameView(T* const* audio_samples, int num_channels, int channel_size) : audio_samples_(audio_samples), num_channels_(num_channels), - channel_size_(channel_size) {} + channel_size_(channel_size) { + RTC_DCHECK_GE(num_channels_, 0); + RTC_DCHECK_GE(channel_size_, 0); + } // Implicit cast to allow converting Frame to // Frame. @@ -39,17 +40,17 @@ class AudioFrameView { AudioFrameView() = delete; - size_t num_channels() const { return num_channels_; } + int num_channels() const { return num_channels_; } - size_t samples_per_channel() const { return channel_size_; } + int samples_per_channel() const { return channel_size_; } - rtc::ArrayView channel(size_t idx) { + rtc::ArrayView channel(int idx) { RTC_DCHECK_LE(0, idx); RTC_DCHECK_LE(idx, num_channels_); return rtc::ArrayView(audio_samples_[idx], channel_size_); } - rtc::ArrayView channel(size_t idx) const { + rtc::ArrayView channel(int idx) const { RTC_DCHECK_LE(0, idx); RTC_DCHECK_LE(idx, num_channels_); return rtc::ArrayView(audio_samples_[idx], channel_size_); @@ -59,8 +60,8 @@ class AudioFrameView { private: T* const* audio_samples_; - size_t num_channels_; - size_t channel_size_; + int num_channels_; + int channel_size_; }; } // namespace webrtc diff --git a/modules/audio_processing/include/audio_processing.cc b/modules/audio_processing/include/audio_processing.cc index 21270a76af..86edaee087 100644 --- a/modules/audio_processing/include/audio_processing.cc +++ b/modules/audio_processing/include/audio_processing.cc @@ -60,8 +60,6 @@ bool Agc1Config::operator==(const Agc1Config& rhs) const { target_level_dbfs == rhs.target_level_dbfs && compression_gain_db == rhs.compression_gain_db && enable_limiter == rhs.enable_limiter && - analog_level_minimum == rhs.analog_level_minimum && - analog_level_maximum == rhs.analog_level_maximum && 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 && @@ -82,19 +80,21 @@ bool Agc1Config::operator==(const Agc1Config& rhs) const { 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_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 && dry_run == rhs.dry_run && + headroom_db == rhs.headroom_db && max_gain_db == rhs.max_gain_db && + initial_gain_db == rhs.initial_gain_db && vad_reset_period_ms == rhs.vad_reset_period_ms && adjacent_speech_frames_threshold == rhs.adjacent_speech_frames_threshold && max_gain_change_db_per_second == rhs.max_gain_change_db_per_second && - max_output_noise_level_dbfs == rhs.max_output_noise_level_dbfs && - sse2_allowed == rhs.sse2_allowed && avx2_allowed == rhs.avx2_allowed && - neon_allowed == rhs.neon_allowed; + max_output_noise_level_dbfs == rhs.max_output_noise_level_dbfs; } bool Agc2Config::operator==(const Agc2Config& rhs) const { @@ -106,7 +106,7 @@ bool Agc2Config::operator==(const Agc2Config& rhs) const { 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 && + post_gain_factor == rhs.post_gain_factor && analog_mic_gain_emulation == rhs.analog_mic_gain_emulation; } @@ -119,94 +119,92 @@ operator==(const AudioProcessing::Config::CaptureLevelAdjustment:: std::string AudioProcessing::Config::ToString() const { char buf[2048]; rtc::SimpleStringBuilder builder(buf); - builder - << "AudioProcessing::Config{ " - "pipeline: { " - "maximum_internal_processing_rate: " - << pipeline.maximum_internal_processing_rate - << ", multi_channel_render: " << pipeline.multi_channel_render - << ", multi_channel_capture: " << pipeline.multi_channel_capture - << " }, pre_amplifier: { enabled: " << pre_amplifier.enabled - << ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor - << " },capture_level_adjustment: { enabled: " - << capture_level_adjustment.enabled - << ", pre_gain_factor: " << capture_level_adjustment.pre_gain_factor - << ", post_gain_factor: " << capture_level_adjustment.post_gain_factor - << ", analog_mic_gain_emulation: { enabled: " - << capture_level_adjustment.analog_mic_gain_emulation.enabled - << ", initial_level: " - << capture_level_adjustment.analog_mic_gain_emulation.initial_level - << " }}, high_pass_filter: { enabled: " << high_pass_filter.enabled - << " }, echo_canceller: { enabled: " << echo_canceller.enabled - << ", mobile_mode: " << echo_canceller.mobile_mode - << ", enforce_high_pass_filtering: " - << echo_canceller.enforce_high_pass_filtering - << " }, noise_suppression: { enabled: " << noise_suppression.enabled - << ", level: " << NoiseSuppressionLevelToString(noise_suppression.level) - << " }, transient_suppression: { enabled: " - << transient_suppression.enabled - << " }, voice_detection: { enabled: " << voice_detection.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_level_minimum: " << gain_controller1.analog_level_minimum - << ", analog_level_maximum: " << gain_controller1.analog_level_maximum - << ", 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 - << " }}}, gain_controller2: { enabled: " << gain_controller2.enabled - << ", fixed_digital: { gain_db: " - << gain_controller2.fixed_digital.gain_db - << " }, adaptive_digital: { enabled: " - << gain_controller2.adaptive_digital.enabled - << ", dry_run: " << gain_controller2.adaptive_digital.dry_run - << ", vad_reset_period_ms: " - << gain_controller2.adaptive_digital.vad_reset_period_ms - << ", adjacent_speech_frames_threshold: " - << gain_controller2.adaptive_digital.adjacent_speech_frames_threshold - << ", 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 - << ", sse2_allowed: " << gain_controller2.adaptive_digital.sse2_allowed - << ", avx2_allowed: " << gain_controller2.adaptive_digital.avx2_allowed - << ", neon_allowed: " << gain_controller2.adaptive_digital.neon_allowed - << "}}, residual_echo_detector: { enabled: " - << residual_echo_detector.enabled - << " }, level_estimation: { enabled: " << level_estimation.enabled - << " }}"; + 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 + << ", dry_run: " << gain_controller2.adaptive_digital.dry_run + << ", 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 + << ", vad_reset_period_ms: " + << gain_controller2.adaptive_digital.vad_reset_period_ms + << ", adjacent_speech_frames_threshold: " + << gain_controller2.adaptive_digital.adjacent_speech_frames_threshold + << ", 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 + << "}}"; return builder.str(); } diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index 8870a06a8d..1c0a53b254 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -29,9 +29,7 @@ #include "api/audio/echo_control.h" #include "api/scoped_refptr.h" #include "modules/audio_processing/include/audio_processing_statistics.h" -#include "modules/audio_processing/include/config.h" #include "rtc_base/arraysize.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_count.h" #include "rtc_base/system/file_wrapper.h" #include "rtc_base/system/rtc_export.h" @@ -65,35 +63,6 @@ static constexpr int kAgcStartupMinVolume = 0; #endif // defined(WEBRTC_CHROMIUM_BUILD) static constexpr int kClippedLevelMin = 70; -// To be deprecated: Please instead use the flag in the -// AudioProcessing::Config::AnalogGainController. -// TODO(webrtc:5298): Remove. -struct ExperimentalAgc { - ExperimentalAgc() = default; - explicit ExperimentalAgc(bool enabled) : enabled(enabled) {} - ExperimentalAgc(bool enabled, int startup_min_volume) - : enabled(enabled), startup_min_volume(startup_min_volume) {} - static const ConfigOptionID identifier = ConfigOptionID::kExperimentalAgc; - bool enabled = true; - int startup_min_volume = kAgcStartupMinVolume; - // Lowest microphone level that will be applied in response to clipping. - int clipped_level_min = kClippedLevelMin; - bool digital_adaptive_disabled = false; -}; - -// To be deprecated: Please instead use the flag in the -// AudioProcessing::Config::TransientSuppression. -// -// Use to enable experimental noise suppression. It can be set in the -// constructor. -// TODO(webrtc:5298): Remove. -struct ExperimentalNs { - ExperimentalNs() : enabled(false) {} - explicit ExperimentalNs(bool enabled) : enabled(enabled) {} - static const ConfigOptionID identifier = ConfigOptionID::kExperimentalNs; - bool enabled; -}; - // The Audio Processing Module (APM) provides a collection of voice processing // components designed for real-time communications software. // @@ -144,8 +113,6 @@ struct ExperimentalNs { // // config.high_pass_filter.enabled = true; // -// config.voice_detection.enabled = true; -// // apm->ApplyConfig(config) // // apm->noise_reduction()->set_level(kHighSuppression); @@ -191,7 +158,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { // 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 { // Maximum allowed processing rate used internally. May only be set to @@ -264,11 +230,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { bool enabled = false; } transient_suppression; - // Enables reporting of `voice_detected` in webrtc::AudioProcessingStats. - struct VoiceDetection { - bool enabled = false; - } voice_detection; - // 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, @@ -321,10 +282,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { // target level. Otherwise, the signal will be compressed but not limited // above the target level. bool enable_limiter = true; - // Sets the minimum and maximum analog levels of the audio capture device. - // Must be set if an analog mode is used. Limited to [0, 65535]. - int analog_level_minimum = 0; - int analog_level_maximum = 255; // Enables the analog gain controller functionality. struct AnalogGainController { @@ -386,9 +343,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { return !(*this == rhs); } - // TODO(crbug.com/webrtc/7494): Remove `LevelEstimator`. - enum LevelEstimator { kRms, kPeak }; - enum NoiseEstimator { kStationaryNoise, kNoiseFloor }; bool enabled = false; struct FixedDigital { float gain_db = 0.0f; @@ -400,50 +354,30 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { } bool enabled = false; - // Run the adaptive digital controller but the signal is not modified. + // When true, the adaptive digital controller runs but the signal is not + // modified. bool dry_run = false; + float headroom_db = 6.0f; + // TODO(bugs.webrtc.org/7494): Consider removing and inferring from + // `max_output_noise_level_dbfs`. + float max_gain_db = 30.0f; + float initial_gain_db = 8.0f; int vad_reset_period_ms = 1500; int adjacent_speech_frames_threshold = 12; float max_gain_change_db_per_second = 3.0f; float max_output_noise_level_dbfs = -50.0f; - bool sse2_allowed = true; - bool avx2_allowed = true; - bool neon_allowed = true; - // TODO(crbug.com/webrtc/7494): Remove deprecated settings below. - NoiseEstimator noise_estimator = kNoiseFloor; - float vad_probability_attack = 1.0f; - LevelEstimator level_estimator = kRms; - int level_estimator_adjacent_speech_frames_threshold = 12; - bool use_saturation_protector = true; - float initial_saturation_margin_db = 25.0f; - float extra_saturation_margin_db = 5.0f; - int gain_applier_adjacent_speech_frames_threshold = 12; } adaptive_digital; } gain_controller2; + // TODO(bugs.webrtc.org/11539): Deprecated. Delete this flag. Replaced by + // injectable submodule. struct ResidualEchoDetector { - bool enabled = true; + bool enabled = false; } residual_echo_detector; - // Enables reporting of `output_rms_dbfs` in webrtc::AudioProcessingStats. - struct LevelEstimation { - bool enabled = false; - } level_estimation; - std::string ToString() const; }; - // TODO(mgraczyk): Remove once all methods that use ChannelLayout are gone. - enum ChannelLayout { - kMono, - // Left, right. - kStereo, - // Mono, keyboard, and mic. - kMonoAndKeyboard, - // Left, right, keyboard, and mic. - kStereoAndKeyboard - }; - // Specifies the properties of a setting to be passed to AudioProcessing at // runtime. class RuntimeSetting { @@ -578,16 +512,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { // number of channels as the input. virtual int Initialize(const ProcessingConfig& processing_config) = 0; - // Initialize with unpacked parameters. See Initialize() above for details. - // - // TODO(mgraczyk): Remove once clients are updated to use the new interface. - virtual int Initialize(int capture_input_sample_rate_hz, - int capture_output_sample_rate_hz, - int render_sample_rate_hz, - ChannelLayout capture_input_layout, - ChannelLayout capture_output_layout, - ChannelLayout render_input_layout) = 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; @@ -675,7 +599,7 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { // 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 provided in Config::GainController1. + // 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 ProcessStream() @@ -792,71 +716,73 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { class RTC_EXPORT AudioProcessingBuilder { public: AudioProcessingBuilder(); + AudioProcessingBuilder(const AudioProcessingBuilder&) = delete; + AudioProcessingBuilder& operator=(const AudioProcessingBuilder&) = delete; ~AudioProcessingBuilder(); - // The AudioProcessingBuilder takes ownership of the echo_control_factory. + + // Sets the APM configuration. + AudioProcessingBuilder& SetConfig(const AudioProcessing::Config& config) { + config_ = config; + return *this; + } + + // Sets the echo controller factory to inject when APM is created. AudioProcessingBuilder& SetEchoControlFactory( std::unique_ptr echo_control_factory) { echo_control_factory_ = std::move(echo_control_factory); return *this; } - // The AudioProcessingBuilder takes ownership of the capture_post_processing. + + // Sets the capture post-processing sub-module to inject when APM is created. AudioProcessingBuilder& SetCapturePostProcessing( std::unique_ptr capture_post_processing) { capture_post_processing_ = std::move(capture_post_processing); return *this; } - // The AudioProcessingBuilder takes ownership of the render_pre_processing. + + // Sets the render pre-processing sub-module to inject when APM is created. AudioProcessingBuilder& SetRenderPreProcessing( std::unique_ptr render_pre_processing) { render_pre_processing_ = std::move(render_pre_processing); return *this; } - // The AudioProcessingBuilder takes ownership of the echo_detector. + + // Sets the echo detector to inject when APM is created. AudioProcessingBuilder& SetEchoDetector( rtc::scoped_refptr echo_detector) { echo_detector_ = std::move(echo_detector); return *this; } - // The AudioProcessingBuilder takes ownership of the capture_analyzer. + + // Sets the capture analyzer sub-module to inject when APM is created. AudioProcessingBuilder& SetCaptureAnalyzer( std::unique_ptr capture_analyzer) { capture_analyzer_ = std::move(capture_analyzer); return *this; } - // This creates an APM instance using the previously set components. Calling - // the Create function resets the AudioProcessingBuilder to its initial state. + + // Creates an APM instance with the specified config or the default one if + // unspecified. Injects the specified components transferring the ownership + // to the newly created APM instance - i.e., except for the config, the + // builder is reset to its initial state. rtc::scoped_refptr Create(); - rtc::scoped_refptr Create(const webrtc::Config& config); private: + AudioProcessing::Config config_; std::unique_ptr echo_control_factory_; std::unique_ptr capture_post_processing_; std::unique_ptr render_pre_processing_; rtc::scoped_refptr echo_detector_; std::unique_ptr capture_analyzer_; - RTC_DISALLOW_COPY_AND_ASSIGN(AudioProcessingBuilder); }; class StreamConfig { public: // sample_rate_hz: The sampling rate of the stream. - // - // num_channels: The number of audio channels in the stream, excluding the - // keyboard channel if it is present. When passing a - // StreamConfig with an array of arrays T*[N], - // - // N == {num_channels + 1 if has_keyboard - // {num_channels if !has_keyboard - // - // has_keyboard: True if the stream has a keyboard channel. When has_keyboard - // is true, the last channel in any corresponding list of - // channels is the keyboard channel. - StreamConfig(int sample_rate_hz = 0, - size_t num_channels = 0, - bool has_keyboard = false) + // num_channels: The number of audio channels in the stream. + StreamConfig(int sample_rate_hz = 0, size_t num_channels = 0) : sample_rate_hz_(sample_rate_hz), num_channels_(num_channels), - has_keyboard_(has_keyboard), num_frames_(calculate_frames(sample_rate_hz)) {} void set_sample_rate_hz(int value) { @@ -864,22 +790,18 @@ class StreamConfig { num_frames_ = calculate_frames(value); } void set_num_channels(size_t value) { num_channels_ = value; } - void set_has_keyboard(bool value) { has_keyboard_ = value; } int sample_rate_hz() const { return sample_rate_hz_; } - // The number of channels in the stream, not including the keyboard channel if - // present. + // The number of channels in the stream. size_t num_channels() const { return num_channels_; } - bool has_keyboard() const { return has_keyboard_; } 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_ && - has_keyboard_ == other.has_keyboard_; + num_channels_ == other.num_channels_; } bool operator!=(const StreamConfig& other) const { return !(*this == other); } @@ -892,7 +814,6 @@ class StreamConfig { int sample_rate_hz_; size_t num_channels_; - bool has_keyboard_; size_t num_frames_; }; @@ -982,17 +903,13 @@ class EchoDetector : public rtc::RefCountInterface { int render_sample_rate_hz, int num_render_channels) = 0; - // Analysis (not changing) of the render signal. + // Analysis (not changing) of the first channel of the render signal. virtual void AnalyzeRenderAudio(rtc::ArrayView render_audio) = 0; // Analysis (not changing) of the capture signal. virtual void AnalyzeCaptureAudio( rtc::ArrayView capture_audio) = 0; - // Pack an AudioBuffer into a vector. - static void PackRenderAudioBuffer(AudioBuffer* audio, - std::vector* packed_buffer); - struct Metrics { absl::optional echo_likelihood; absl::optional echo_likelihood_recent_max; diff --git a/modules/audio_processing/include/audio_processing_statistics.h b/modules/audio_processing/include/audio_processing_statistics.h index c81d7eb9d8..3b43319951 100644 --- a/modules/audio_processing/include/audio_processing_statistics.h +++ b/modules/audio_processing/include/audio_processing_statistics.h @@ -24,14 +24,8 @@ struct RTC_EXPORT AudioProcessingStats { AudioProcessingStats(const AudioProcessingStats& other); ~AudioProcessingStats(); - // The root mean square (RMS) level in dBFS (decibels from digital - // full-scale) of the last capture frame, after processing. It is - // constrained to [-127, 0]. - // The computation follows: https://tools.ietf.org/html/rfc6465 - // with the intent that it can provide the RTP audio level indication. - // Only reported if level estimation is enabled in AudioProcessing::Config. - absl::optional output_rms_dbfs; - + // 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. diff --git a/modules/audio_processing/include/config.h b/modules/audio_processing/include/config.h deleted file mode 100644 index 7fab17831c..0000000000 --- a/modules/audio_processing/include/config.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ -#define MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ - -#include - -#include "rtc_base/system/rtc_export.h" - -namespace webrtc { - -// Only add new values to the end of the enumeration and never remove (only -// deprecate) to maintain binary compatibility. -enum class ConfigOptionID { - kMyExperimentForTest, - kAlgo1CostFunctionForTest, - kTemporalLayersFactory, // Deprecated - kNetEqCapacityConfig, // Deprecated - kNetEqFastAccelerate, // Deprecated - kVoicePacing, // Deprecated - kExtendedFilter, // Deprecated - kDelayAgnostic, // Deprecated - kExperimentalAgc, - kExperimentalNs, - kBeamforming, // Deprecated - kIntelligibility, // Deprecated - kEchoCanceller3, // Deprecated - kAecRefinedAdaptiveFilter, // Deprecated - kLevelControl // Deprecated -}; - -// Class Config is designed to ease passing a set of options across webrtc code. -// Options are identified by typename in order to avoid incorrect casts. -// -// Usage: -// * declaring an option: -// struct Algo1_CostFunction { -// virtual float cost(int x) const { return x; } -// virtual ~Algo1_CostFunction() {} -// }; -// -// * accessing an option: -// config.Get().cost(value); -// -// * setting an option: -// struct SqrCost : Algo1_CostFunction { -// virtual float cost(int x) const { return x*x; } -// }; -// config.Set(new SqrCost()); -// -// Note: This class is thread-compatible (like STL containers). -class RTC_EXPORT Config { - public: - // Returns the option if set or a default constructed one. - // Callers that access options too often are encouraged to cache the result. - // Returned references are owned by this. - // - // Requires std::is_default_constructible - template - const T& Get() const; - - // Set the option, deleting any previous instance of the same. - // This instance gets ownership of the newly set value. - template - void Set(T* value); - - Config(); - ~Config(); - - private: - struct BaseOption { - virtual ~BaseOption() {} - }; - - template - struct Option : BaseOption { - explicit Option(T* v) : value(v) {} - ~Option() { delete value; } - T* value; - }; - - template - static ConfigOptionID identifier() { - return T::identifier; - } - - // Used to instantiate a default constructed object that doesn't needs to be - // owned. This allows Get to be implemented without requiring explicitly - // locks. - template - static const T& default_value() { - static const T* const def = new T(); - return *def; - } - - typedef std::map OptionMap; - OptionMap options_; - - Config(const Config&); - void operator=(const Config&); -}; - -template -const T& Config::Get() const { - OptionMap::const_iterator it = options_.find(identifier()); - if (it != options_.end()) { - const T* t = static_cast*>(it->second)->value; - if (t) { - return *t; - } - } - return default_value(); -} - -template -void Config::Set(T* value) { - BaseOption*& it = options_[identifier()]; - delete it; - it = new Option(value); -} -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h index 32c94f783b..225fa762b6 100644 --- a/modules/audio_processing/include/mock_audio_processing.h +++ b/modules/audio_processing/include/mock_audio_processing.h @@ -67,6 +67,27 @@ class MockEchoControl : public EchoControl { 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, + (rtc::ArrayView render_audio), + (override)); + MOCK_METHOD(void, + AnalyzeCaptureAudio, + (rtc::ArrayView capture_audio), + (override)); + MOCK_METHOD(Metrics, GetMetrics, (), (const, override)); +}; + class MockAudioProcessing : public AudioProcessing { public: MockAudioProcessing() {} @@ -74,15 +95,6 @@ class MockAudioProcessing : public AudioProcessing { virtual ~MockAudioProcessing() {} MOCK_METHOD(int, Initialize, (), (override)); - MOCK_METHOD(int, - Initialize, - (int capture_input_sample_rate_hz, - int capture_output_sample_rate_hz, - int render_sample_rate_hz, - ChannelLayout capture_input_layout, - ChannelLayout capture_output_layout, - ChannelLayout render_input_layout), - (override)); MOCK_METHOD(int, Initialize, (const ProcessingConfig& processing_config), diff --git a/modules/audio_processing/level_estimator.cc b/modules/audio_processing/level_estimator.cc deleted file mode 100644 index e70728843a..0000000000 --- a/modules/audio_processing/level_estimator.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/level_estimator.h" - -#include "api/array_view.h" - -namespace webrtc { - -LevelEstimator::LevelEstimator() { - rms_.Reset(); -} - -LevelEstimator::~LevelEstimator() = default; - -void LevelEstimator::ProcessStream(const AudioBuffer& audio) { - for (size_t i = 0; i < audio.num_channels(); i++) { - rms_.Analyze(rtc::ArrayView(audio.channels_const()[i], - audio.num_frames())); - } -} -} // namespace webrtc diff --git a/modules/audio_processing/level_estimator.h b/modules/audio_processing/level_estimator.h deleted file mode 100644 index d2bcfa1f0f..0000000000 --- a/modules/audio_processing/level_estimator.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ -#define MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ - -#include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/rms_level.h" - -namespace webrtc { - -// An estimation component used to retrieve level metrics. -class LevelEstimator { - public: - LevelEstimator(); - ~LevelEstimator(); - - LevelEstimator(LevelEstimator&) = delete; - LevelEstimator& operator=(LevelEstimator&) = delete; - - void ProcessStream(const AudioBuffer& audio); - - // Returns the root mean square (RMS) level in dBFs (decibels from digital - // full-scale), or alternately dBov. It is computed over all primary stream - // frames since the last call to RMS(). The returned value is positive but - // should be interpreted as negative. It is constrained to [0, 127]. - // - // The computation follows: https://tools.ietf.org/html/rfc6465 - // with the intent that it can provide the RTP audio level indication. - // - // Frames passed to ProcessStream() with an `_energy` of zero are considered - // to have been muted. The RMS of the frame will be interpreted as -127. - int RMS() { return rms_.Average(); } - - private: - RmsLevel rms_; -}; -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ diff --git a/modules/audio_processing/level_estimator_unittest.cc b/modules/audio_processing/level_estimator_unittest.cc deleted file mode 100644 index 7660b677fd..0000000000 --- a/modules/audio_processing/level_estimator_unittest.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include - -#include "api/array_view.h" -#include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/level_estimator.h" -#include "modules/audio_processing/test/audio_buffer_tools.h" -#include "modules/audio_processing/test/bitexactness_tools.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -const int kNumFramesToProcess = 1000; - -// Processes a specified amount of frames, verifies the results and reports -// any errors. -void RunBitexactnessTest(int sample_rate_hz, - size_t num_channels, - int rms_reference) { - LevelEstimator level_estimator; - int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - StreamConfig capture_config(sample_rate_hz, num_channels, false); - AudioBuffer capture_buffer( - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), capture_config.num_channels()); - - test::InputAudioFile capture_file( - test::GetApmCaptureTestVectorFileName(sample_rate_hz)); - std::vector capture_input(samples_per_channel * num_channels); - for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &capture_file, capture_input); - - test::CopyVectorToAudioBuffer(capture_config, capture_input, - &capture_buffer); - - level_estimator.ProcessStream(capture_buffer); - } - - // Extract test results. - int rms = level_estimator.RMS(); - - // Compare the output to the reference. - EXPECT_EQ(rms_reference, rms); -} - -} // namespace - -TEST(LevelEstimatorBitExactnessTest, Mono8kHz) { - const int kRmsReference = 31; - - RunBitexactnessTest(8000, 1, kRmsReference); -} - -TEST(LevelEstimatorBitExactnessTest, Mono16kHz) { - const int kRmsReference = 31; - - RunBitexactnessTest(16000, 1, kRmsReference); -} - -TEST(LevelEstimatorBitExactnessTest, Mono32kHz) { - const int kRmsReference = 31; - - RunBitexactnessTest(32000, 1, kRmsReference); -} - -TEST(LevelEstimatorBitExactnessTest, Mono48kHz) { - const int kRmsReference = 31; - - RunBitexactnessTest(48000, 1, kRmsReference); -} - -TEST(LevelEstimatorBitExactnessTest, Stereo16kHz) { - const int kRmsReference = 30; - - RunBitexactnessTest(16000, 2, kRmsReference); -} - -} // namespace webrtc diff --git a/modules/audio_processing/ns/suppression_params.cc b/modules/audio_processing/ns/suppression_params.cc index 9a6bd5a587..7bf18346f9 100644 --- a/modules/audio_processing/ns/suppression_params.cc +++ b/modules/audio_processing/ns/suppression_params.cc @@ -42,7 +42,7 @@ SuppressionParams::SuppressionParams( use_attenuation_adjustment = true; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/modules/audio_processing/residual_echo_detector.cc b/modules/audio_processing/residual_echo_detector.cc index 618888361f..fe1149a896 100644 --- a/modules/audio_processing/residual_echo_detector.cc +++ b/modules/audio_processing/residual_echo_detector.cc @@ -14,7 +14,6 @@ #include #include "absl/types/optional.h" -#include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/atomic_ops.h" #include "rtc_base/checks.h" @@ -199,13 +198,6 @@ void ResidualEchoDetector::Initialize(int /*capture_sample_rate_hz*/, reliability_ = 0.f; } -void EchoDetector::PackRenderAudioBuffer(AudioBuffer* audio, - std::vector* packed_buffer) { - packed_buffer->clear(); - packed_buffer->insert(packed_buffer->end(), audio->channels()[0], - audio->channels()[0] + audio->num_frames()); -} - EchoDetector::Metrics ResidualEchoDetector::GetMetrics() const { EchoDetector::Metrics metrics; metrics.echo_likelihood = echo_likelihood_; diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index 4703ee30c7..ec35dd345c 100644 --- a/modules/audio_processing/test/aec_dump_based_simulator.cc +++ b/modules/audio_processing/test/aec_dump_based_simulator.cc @@ -164,15 +164,14 @@ void AecDumpBasedSimulator::PrepareProcessStreamCall( } } - if (!settings_.use_ts || *settings_.use_ts == 1) { - // Transient suppressor activated (1) or not specified. + if (settings_.override_key_pressed.has_value()) { + // Key pressed state overridden. + ap_->set_stream_key_pressed(*settings_.override_key_pressed); + } else { + // Set the recorded key pressed state. if (msg.has_keypress()) { ap_->set_stream_key_pressed(msg.keypress()); } - } else { - // Transient suppressor deactivated (0) or activated with continuous key - // events (2). - ap_->set_stream_key_pressed(*settings_.use_ts == 2); } // Level is always logged in AEC dumps. @@ -505,10 +504,6 @@ void AecDumpBasedSimulator::HandleMessage( << msg.experiments_description() << std::endl; } - if (settings_.use_ed) { - apm_config.residual_echo_detector.enabled = *settings_.use_ed; - } - ap_->ApplyConfig(apm_config); } } diff --git a/modules/audio_processing/test/audio_processing_builder_for_testing.cc b/modules/audio_processing/test/audio_processing_builder_for_testing.cc index 05c3d3e95f..72f75ed1e0 100644 --- a/modules/audio_processing/test/audio_processing_builder_for_testing.cc +++ b/modules/audio_processing/test/audio_processing_builder_for_testing.cc @@ -24,14 +24,8 @@ AudioProcessingBuilderForTesting::~AudioProcessingBuilderForTesting() = default; #ifdef WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE rtc::scoped_refptr AudioProcessingBuilderForTesting::Create() { - webrtc::Config config; - return Create(config); -} - -rtc::scoped_refptr AudioProcessingBuilderForTesting::Create( - const webrtc::Config& config) { return rtc::make_ref_counted( - config, std::move(capture_post_processing_), + config_, std::move(capture_post_processing_), std::move(render_pre_processing_), std::move(echo_control_factory_), std::move(echo_detector_), std::move(capture_analyzer_)); } @@ -41,14 +35,7 @@ rtc::scoped_refptr AudioProcessingBuilderForTesting::Create( rtc::scoped_refptr AudioProcessingBuilderForTesting::Create() { AudioProcessingBuilder builder; TransferOwnershipsToBuilder(&builder); - return builder.Create(); -} - -rtc::scoped_refptr AudioProcessingBuilderForTesting::Create( - const webrtc::Config& config) { - AudioProcessingBuilder builder; - TransferOwnershipsToBuilder(&builder); - return builder.Create(config); + return builder.SetConfig(config_).Create(); } #endif @@ -57,9 +44,9 @@ void AudioProcessingBuilderForTesting::TransferOwnershipsToBuilder( AudioProcessingBuilder* builder) { builder->SetCapturePostProcessing(std::move(capture_post_processing_)); builder->SetRenderPreProcessing(std::move(render_pre_processing_)); - builder->SetCaptureAnalyzer(std::move(capture_analyzer_)); builder->SetEchoControlFactory(std::move(echo_control_factory_)); builder->SetEchoDetector(std::move(echo_detector_)); + builder->SetCaptureAnalyzer(std::move(capture_analyzer_)); } } // namespace webrtc diff --git a/modules/audio_processing/test/audio_processing_builder_for_testing.h b/modules/audio_processing/test/audio_processing_builder_for_testing.h index ba0f7b110e..e73706c1b6 100644 --- a/modules/audio_processing/test/audio_processing_builder_for_testing.h +++ b/modules/audio_processing/test/audio_processing_builder_for_testing.h @@ -24,51 +24,65 @@ namespace webrtc { class AudioProcessingBuilderForTesting { public: AudioProcessingBuilderForTesting(); + AudioProcessingBuilderForTesting(const AudioProcessingBuilderForTesting&) = + delete; + AudioProcessingBuilderForTesting& operator=( + const AudioProcessingBuilderForTesting&) = delete; ~AudioProcessingBuilderForTesting(); - // The AudioProcessingBuilderForTesting takes ownership of the - // echo_control_factory. + + // Sets the APM configuration. + AudioProcessingBuilderForTesting& SetConfig( + const AudioProcessing::Config& config) { + config_ = config; + return *this; + } + + // Sets the echo controller factory to inject when APM is created. AudioProcessingBuilderForTesting& SetEchoControlFactory( std::unique_ptr echo_control_factory) { echo_control_factory_ = std::move(echo_control_factory); return *this; } - // The AudioProcessingBuilderForTesting takes ownership of the - // capture_post_processing. + + // Sets the capture post-processing sub-module to inject when APM is created. AudioProcessingBuilderForTesting& SetCapturePostProcessing( std::unique_ptr capture_post_processing) { capture_post_processing_ = std::move(capture_post_processing); return *this; } - // The AudioProcessingBuilderForTesting takes ownership of the - // render_pre_processing. + + // Sets the render pre-processing sub-module to inject when APM is created. AudioProcessingBuilderForTesting& SetRenderPreProcessing( std::unique_ptr render_pre_processing) { render_pre_processing_ = std::move(render_pre_processing); return *this; } - // The AudioProcessingBuilderForTesting takes ownership of the echo_detector. + + // Sets the echo detector to inject when APM is created. AudioProcessingBuilderForTesting& SetEchoDetector( rtc::scoped_refptr echo_detector) { echo_detector_ = std::move(echo_detector); return *this; } - // The AudioProcessingBuilderForTesting takes ownership of the - // capture_analyzer. + + // Sets the capture analyzer sub-module to inject when APM is created. AudioProcessingBuilderForTesting& SetCaptureAnalyzer( std::unique_ptr capture_analyzer) { capture_analyzer_ = std::move(capture_analyzer); return *this; } - // This creates an APM instance using the previously set components. Calling - // the Create function resets the AudioProcessingBuilderForTesting to its - // initial state. + + // Creates an APM instance with the specified config or the default one if + // unspecified. Injects the specified components transferring the ownership + // to the newly created APM instance - i.e., except for the config, the + // builder is reset to its initial state. rtc::scoped_refptr Create(); - rtc::scoped_refptr Create(const webrtc::Config& config); private: // Transfers the ownership to a non-testing builder. void TransferOwnershipsToBuilder(AudioProcessingBuilder* builder); + AudioProcessing::Config config_; std::unique_ptr echo_control_factory_; std::unique_ptr capture_post_processing_; std::unique_ptr render_pre_processing_; diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc index c61110fff0..5923fc34f6 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -20,6 +20,7 @@ #include "api/audio/echo_canceller3_config_json.h" #include "api/audio/echo_canceller3_factory.h" +#include "api/audio/echo_detector_creator.h" #include "modules/audio_processing/aec_dump/aec_dump_factory.h" #include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/include/audio_processing.h" @@ -188,6 +189,10 @@ AudioProcessingSimulator::AudioProcessingSimulator( builder->SetEchoControlFactory(std::move(echo_control_factory)); } + if (settings_.use_ed && *settings.use_ed) { + builder->SetEchoDetector(CreateEchoDetector()); + } + // Create an audio processing object. ap_ = builder->Create(); RTC_CHECK(ap_); @@ -487,8 +492,6 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { if (settings_.agc2_use_adaptive_gain) { apm_config.gain_controller2.adaptive_digital.enabled = *settings_.agc2_use_adaptive_gain; - apm_config.gain_controller2.adaptive_digital.level_estimator = - settings_.agc2_adaptive_level_estimator; } } if (settings_.use_pre_amplifier) { @@ -540,14 +543,6 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { apm_config.high_pass_filter.enabled = *settings_.use_hpf; } - if (settings_.use_le) { - apm_config.level_estimation.enabled = *settings_.use_le; - } - - if (settings_.use_vad) { - apm_config.voice_detection.enabled = *settings_.use_vad; - } - if (settings_.use_agc) { apm_config.gain_controller1.enabled = *settings_.use_agc; } @@ -570,13 +565,9 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { apm_config.gain_controller1.analog_gain_controller.enabled = *settings_.use_analog_agc; } - if (settings_.analog_agc_disable_digital_adaptive) { + if (settings_.analog_agc_use_digital_adaptive_controller) { apm_config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - *settings_.analog_agc_disable_digital_adaptive; - } - - if (settings_.use_ed) { - apm_config.residual_echo_detector.enabled = *settings_.use_ed; + *settings_.analog_agc_use_digital_adaptive_controller; } if (settings_.maximum_internal_processing_rate) { diff --git a/modules/audio_processing/test/audio_processing_simulator.h b/modules/audio_processing/test/audio_processing_simulator.h index 9539e58b1b..b63bc12d6f 100644 --- a/modules/audio_processing/test/audio_processing_simulator.h +++ b/modules/audio_processing/test/audio_processing_simulator.h @@ -38,7 +38,7 @@ struct Int16Frame { samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, kChunksPerSecond); this->num_channels = num_channels; - config = StreamConfig(sample_rate_hz, num_channels, /*has_keyboard=*/false); + config = StreamConfig(sample_rate_hz, num_channels); data.resize(num_channels * samples_per_channel); } @@ -105,24 +105,21 @@ struct SimulationSettings { absl::optional use_ns; absl::optional use_ts; absl::optional use_analog_agc; - absl::optional use_vad; - absl::optional use_le; absl::optional use_all; - absl::optional analog_agc_disable_digital_adaptive; + absl::optional analog_agc_use_digital_adaptive_controller; absl::optional agc_mode; absl::optional agc_target_level; absl::optional use_agc_limiter; absl::optional agc_compression_gain; absl::optional agc2_use_adaptive_gain; absl::optional agc2_fixed_gain_db; - AudioProcessing::Config::GainController2::LevelEstimator - agc2_adaptive_level_estimator; absl::optional pre_amplifier_gain_factor; absl::optional pre_gain_factor; absl::optional post_gain_factor; absl::optional analog_mic_gain_emulation_initial_level; absl::optional ns_level; absl::optional ns_analysis_on_linear_aec_output; + absl::optional override_key_pressed; absl::optional maximum_internal_processing_rate; int initial_mic_level; bool simulate_mic_gain = false; diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc index 1fc39bb6b9..dc38172666 100644 --- a/modules/audio_processing/test/audioproc_float_impl.cc +++ b/modules/audio_processing/test/audioproc_float_impl.cc @@ -111,30 +111,21 @@ ABSL_FLAG(int, ABSL_FLAG(int, ts, kParameterNotSpecifiedValue, - "Activate (1), deactivate (0) or activate the transient suppressor " - "with continuous key events (2)"); + "Activate (1) or deactivate (0) the transient suppressor"); ABSL_FLAG(int, analog_agc, kParameterNotSpecifiedValue, "Activate (1) or deactivate (0) the analog AGC"); -ABSL_FLAG(int, - vad, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate (0) the voice activity detector"); -ABSL_FLAG(int, - le, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate (0) the level estimator"); ABSL_FLAG(bool, all_default, false, "Activate all of the default components (will be overridden by any " "other settings)"); ABSL_FLAG(int, - analog_agc_disable_digital_adaptive, + analog_agc_use_digital_adaptive_controller, kParameterNotSpecifiedValue, - "Force-deactivate (1) digital adaptation in " - "experimental AGC. Digital adaptation is active by default (0)."); + "Activate (1) or deactivate (0) digital adaptation in AGC1. " + "Digital adaptation is active by default."); ABSL_FLAG(int, agc_mode, kParameterNotSpecifiedValue, @@ -159,10 +150,6 @@ ABSL_FLAG(float, agc2_fixed_gain_db, kParameterNotSpecifiedValue, "AGC2 fixed gain (dB) to apply"); -ABSL_FLAG(std::string, - agc2_adaptive_level_estimator, - "RMS", - "AGC2 adaptive digital level estimator to use [RMS, peak]"); ABSL_FLAG(float, pre_amplifier_gain_factor, kParameterNotSpecifiedValue, @@ -230,6 +217,12 @@ ABSL_FLAG(int, simulated_mic_kind, kParameterNotSpecifiedValue, "Specify which microphone kind to use for microphone simulation"); +ABSL_FLAG(int, + override_key_pressed, + kParameterNotSpecifiedValue, + "Always set to true (1) or to false (0) the key press state. If " + "unspecified, false is set with Wav files or, with AEC dumps, the " + "recorded event is used."); ABSL_FLAG(int, frame_for_sending_capture_output_used_false, kParameterNotSpecifiedValue, @@ -341,10 +334,6 @@ const char kUsageDescription[] = "processing module, either based on wav files or " "protobuf debug dump recordings.\n"; -std::vector GetAgc2AdaptiveLevelEstimatorNames() { - return {"RMS", "peak"}; -} - void SetSettingIfSpecified(const std::string& value, absl::optional* parameter) { if (value.compare("") != 0) { @@ -374,32 +363,9 @@ void SetSettingIfFlagSet(int32_t flag, absl::optional* parameter) { } } -AudioProcessing::Config::GainController2::LevelEstimator -MapAgc2AdaptiveLevelEstimator(absl::string_view name) { - if (name.compare("RMS") == 0) { - return AudioProcessing::Config::GainController2::LevelEstimator::kRms; - } - if (name.compare("peak") == 0) { - return AudioProcessing::Config::GainController2::LevelEstimator::kPeak; - } - auto concat_strings = - [](const std::vector& strings) -> std::string { - rtc::StringBuilder ss; - for (const auto& s : strings) { - ss << " " << s; - } - return ss.Release(); - }; - RTC_CHECK(false) - << "Invalid value for agc2_adaptive_level_estimator, valid options:" - << concat_strings(GetAgc2AdaptiveLevelEstimatorNames()) << "."; -} - SimulationSettings CreateSettings() { SimulationSettings settings; if (absl::GetFlag(FLAGS_all_default)) { - settings.use_le = true; - settings.use_vad = true; settings.use_ts = true; settings.use_analog_agc = true; settings.use_ns = true; @@ -451,10 +417,9 @@ SimulationSettings CreateSettings() { SetSettingIfSpecified(absl::GetFlag(FLAGS_ts), &settings.use_ts); SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc), &settings.use_analog_agc); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_vad), &settings.use_vad); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_le), &settings.use_le); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc_disable_digital_adaptive), - &settings.analog_agc_disable_digital_adaptive); + SetSettingIfFlagSet( + absl::GetFlag(FLAGS_analog_agc_use_digital_adaptive_controller), + &settings.analog_agc_use_digital_adaptive_controller); SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_mode), &settings.agc_mode); SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_target_level), &settings.agc_target_level); @@ -467,8 +432,6 @@ SimulationSettings CreateSettings() { SetSettingIfSpecified(absl::GetFlag(FLAGS_agc2_fixed_gain_db), &settings.agc2_fixed_gain_db); - settings.agc2_adaptive_level_estimator = MapAgc2AdaptiveLevelEstimator( - absl::GetFlag(FLAGS_agc2_adaptive_level_estimator)); SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_amplifier_gain_factor), &settings.pre_amplifier_gain_factor); SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_gain_factor), @@ -501,6 +464,8 @@ SimulationSettings CreateSettings() { settings.simulate_mic_gain = absl::GetFlag(FLAGS_simulate_mic_gain); SetSettingIfSpecified(absl::GetFlag(FLAGS_simulated_mic_kind), &settings.simulated_mic_kind); + SetSettingIfFlagSet(absl::GetFlag(FLAGS_override_key_pressed), + &settings.override_key_pressed); SetSettingIfSpecified( absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_false), &settings.frame_for_sending_capture_output_used_false); diff --git a/modules/audio_processing/test/bitexactness_tools.cc b/modules/audio_processing/test/bitexactness_tools.cc index f245c2cf19..0464345364 100644 --- a/modules/audio_processing/test/bitexactness_tools.cc +++ b/modules/audio_processing/test/bitexactness_tools.cc @@ -33,7 +33,7 @@ std::string GetApmRenderTestVectorFileName(int sample_rate_hz) { case 48000: return ResourcePath("far48_stereo", "pcm"); default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return ""; } @@ -49,7 +49,7 @@ std::string GetApmCaptureTestVectorFileName(int sample_rate_hz) { case 48000: return ResourcePath("near48_stereo", "pcm"); default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return ""; } diff --git a/modules/audio_processing/test/conversational_speech/multiend_call.h b/modules/audio_processing/test/conversational_speech/multiend_call.h index 5b6300f0f1..693f00edd9 100644 --- a/modules/audio_processing/test/conversational_speech/multiend_call.h +++ b/modules/audio_processing/test/conversational_speech/multiend_call.h @@ -24,7 +24,6 @@ #include "modules/audio_processing/test/conversational_speech/timing.h" #include "modules/audio_processing/test/conversational_speech/wavreader_abstract_factory.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { @@ -57,6 +56,9 @@ class MultiEndCall { std::unique_ptr wavreader_abstract_factory); ~MultiEndCall(); + MultiEndCall(const MultiEndCall&) = delete; + MultiEndCall& operator=(const MultiEndCall&) = delete; + const std::set& speaker_names() const { return speaker_names_; } const std::map>& audiotrack_readers() const { @@ -92,8 +94,6 @@ class MultiEndCall { int sample_rate_hz_; size_t total_duration_samples_; std::vector speaking_turns_; - - RTC_DISALLOW_COPY_AND_ASSIGN(MultiEndCall); }; } // namespace conversational_speech diff --git a/modules/audio_processing/test/conversational_speech/simulator.cc b/modules/audio_processing/test/conversational_speech/simulator.cc index 20c8608c9e..c0fb589852 100644 --- a/modules/audio_processing/test/conversational_speech/simulator.cc +++ b/modules/audio_processing/test/conversational_speech/simulator.cc @@ -22,7 +22,6 @@ #include "common_audio/include/audio_util.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "test/testsupport/file_utils.h" diff --git a/modules/audio_processing/test/conversational_speech/simulator.h b/modules/audio_processing/test/conversational_speech/simulator.h index a9992eb5d8..2584782c22 100644 --- a/modules/audio_processing/test/conversational_speech/simulator.h +++ b/modules/audio_processing/test/conversational_speech/simulator.h @@ -17,7 +17,6 @@ #include #include "modules/audio_processing/test/conversational_speech/multiend_call.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace test { diff --git a/modules/audio_processing/test/debug_dump_replayer.cc b/modules/audio_processing/test/debug_dump_replayer.cc index b202469cb0..cab6966765 100644 --- a/modules/audio_processing/test/debug_dump_replayer.cc +++ b/modules/audio_processing/test/debug_dump_replayer.cc @@ -178,14 +178,13 @@ void DebugDumpReplayer::OnRuntimeSettingEvent( void DebugDumpReplayer::MaybeRecreateApm(const audioproc::Config& msg) { // These configurations cannot be changed on the fly. - Config config; RTC_CHECK(msg.has_aec_delay_agnostic_enabled()); RTC_CHECK(msg.has_aec_extended_filter_enabled()); // We only create APM once, since changes on these fields should not // happen in current implementation. if (!apm_.get()) { - apm_ = AudioProcessingBuilderForTesting().Create(config); + apm_ = AudioProcessingBuilderForTesting().Create(); } } diff --git a/modules/audio_processing/test/debug_dump_test.cc b/modules/audio_processing/test/debug_dump_test.cc index 6c02571a15..b7351609ec 100644 --- a/modules/audio_processing/test/debug_dump_test.cc +++ b/modules/audio_processing/test/debug_dump_test.cc @@ -47,13 +47,11 @@ class DebugDumpGenerator { const std::string& reverse_file_name, int reverse_rate_hz, int reverse_channels, - const Config& config, const std::string& dump_file_name, bool enable_pre_amplifier); // Constructor that uses default input files. - explicit DebugDumpGenerator(const Config& config, - const AudioProcessing::Config& apm_config); + explicit DebugDumpGenerator(const AudioProcessing::Config& apm_config); ~DebugDumpGenerator(); @@ -123,7 +121,6 @@ DebugDumpGenerator::DebugDumpGenerator(const std::string& input_file_name, const std::string& reverse_file_name, int reverse_rate_hz, int reverse_channels, - const Config& config, const std::string& dump_file_name, bool enable_pre_amplifier) : input_config_(input_rate_hz, input_channels), @@ -143,11 +140,10 @@ DebugDumpGenerator::DebugDumpGenerator(const std::string& input_file_name, worker_queue_("debug_dump_generator_worker_queue"), dump_file_name_(dump_file_name) { AudioProcessingBuilderForTesting apm_builder; - apm_ = apm_builder.Create(config); + apm_ = apm_builder.Create(); } DebugDumpGenerator::DebugDumpGenerator( - const Config& config, const AudioProcessing::Config& apm_config) : DebugDumpGenerator(ResourcePath("near32_stereo", "pcm"), 32000, @@ -155,7 +151,6 @@ DebugDumpGenerator::DebugDumpGenerator( ResourcePath("far32_stereo", "pcm"), 32000, 2, - config, TempFilename(OutputPath(), "debug_aec"), apm_config.pre_amplifier.enabled) { apm_->ApplyConfig(apm_config); @@ -290,8 +285,7 @@ void DebugDumpTest::VerifyDebugDump(const std::string& in_filename) { } TEST_F(DebugDumpTest, SimpleCase) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -299,8 +293,7 @@ TEST_F(DebugDumpTest, SimpleCase) { } TEST_F(DebugDumpTest, ChangeInputFormat) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); @@ -317,8 +310,7 @@ TEST_F(DebugDumpTest, ChangeInputFormat) { } TEST_F(DebugDumpTest, ChangeReverseFormat) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); generator.SetReverseRate(48000); @@ -329,8 +321,7 @@ TEST_F(DebugDumpTest, ChangeReverseFormat) { } TEST_F(DebugDumpTest, ChangeOutputFormat) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); generator.SetOutputRate(48000); @@ -341,10 +332,9 @@ TEST_F(DebugDumpTest, ChangeOutputFormat) { } TEST_F(DebugDumpTest, ToggleAec) { - Config config; AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); @@ -357,14 +347,13 @@ TEST_F(DebugDumpTest, ToggleAec) { } TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { - Config config; AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; apm_config.gain_controller1.analog_gain_controller.enabled = true; apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0; // Arbitrarily set clipping gain to 17, which will never be the default. apm_config.gain_controller1.analog_gain_controller.clipped_level_min = 17; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -388,10 +377,9 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { } TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) { - Config config; AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -414,10 +402,9 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) { } TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) { - Config config; AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -439,13 +426,12 @@ TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) { } TEST_F(DebugDumpTest, VerifyAgcClippingLevelExperimentalString) { - Config config; AudioProcessing::Config apm_config; apm_config.gain_controller1.analog_gain_controller.enabled = true; apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0; // Arbitrarily set clipping gain to 17, which will never be the default. apm_config.gain_controller1.analog_gain_controller.clipped_level_min = 17; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -467,8 +453,7 @@ TEST_F(DebugDumpTest, VerifyAgcClippingLevelExperimentalString) { } TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); generator.StopRecording(); @@ -495,8 +480,7 @@ TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) { #define MAYBE_ToggleAgc ToggleAgc #endif TEST_F(DebugDumpTest, MAYBE_ToggleAgc) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); @@ -510,8 +494,7 @@ TEST_F(DebugDumpTest, MAYBE_ToggleAgc) { } TEST_F(DebugDumpTest, ToggleNs) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); generator.StartRecording(); generator.Process(100); @@ -525,8 +508,7 @@ TEST_F(DebugDumpTest, ToggleNs) { } TEST_F(DebugDumpTest, TransientSuppressionOn) { - Config config; - DebugDumpGenerator generator(config, AudioProcessing::Config()); + DebugDumpGenerator generator(/*apm_config=*/{}); AudioProcessing::Config apm_config = generator.apm()->GetConfig(); apm_config.transient_suppression.enabled = true; @@ -539,10 +521,9 @@ TEST_F(DebugDumpTest, TransientSuppressionOn) { } TEST_F(DebugDumpTest, PreAmplifierIsOn) { - Config config; AudioProcessing::Config apm_config; apm_config.pre_amplifier.enabled = true; - DebugDumpGenerator generator(config, apm_config); + DebugDumpGenerator generator(apm_config); generator.StartRecording(); generator.Process(100); generator.StopRecording(); diff --git a/modules/audio_processing/test/fake_recording_device.cc b/modules/audio_processing/test/fake_recording_device.cc index 5202014b52..3a35ee9d74 100644 --- a/modules/audio_processing/test/fake_recording_device.cc +++ b/modules/audio_processing/test/fake_recording_device.cc @@ -148,7 +148,7 @@ FakeRecordingDevice::FakeRecordingDevice(int initial_mic_level, worker_ = std::make_unique(initial_mic_level); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } diff --git a/modules/audio_processing/test/fake_recording_device.h b/modules/audio_processing/test/fake_recording_device.h index 4017037137..da3c0cf794 100644 --- a/modules/audio_processing/test/fake_recording_device.h +++ b/modules/audio_processing/test/fake_recording_device.h @@ -48,8 +48,8 @@ class FakeRecordingDevice final { ~FakeRecordingDevice(); int MicLevel() const; - void SetMicLevel(const int level); - void SetUndoMicLevel(const int level); + void SetMicLevel(int level); + void SetUndoMicLevel(int level); // Simulates the analog gain. // If `real_device_level` is a valid level, the unmodified mic signal is diff --git a/modules/audio_processing/test/simulator_buffers.cc b/modules/audio_processing/test/simulator_buffers.cc index e6bd6c1c19..458f6ced76 100644 --- a/modules/audio_processing/test/simulator_buffers.cc +++ b/modules/audio_processing/test/simulator_buffers.cc @@ -58,7 +58,7 @@ void SimulatorBuffers::CreateConfigAndBuffer( std::vector* buffer_data, std::vector* buffer_data_samples) { int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - *config = StreamConfig(sample_rate_hz, num_channels, false); + *config = StreamConfig(sample_rate_hz, num_channels); buffer->reset( new AudioBuffer(config->sample_rate_hz(), config->num_channels(), config->sample_rate_hz(), config->num_channels(), diff --git a/modules/audio_processing/test/test_utils.cc b/modules/audio_processing/test/test_utils.cc index 839358d497..dcd5869e0b 100644 --- a/modules/audio_processing/test/test_utils.cc +++ b/modules/audio_processing/test/test_utils.cc @@ -139,15 +139,4 @@ void SetFrameSampleRate(Int16FrameData* frame, int sample_rate_hz) { AudioProcessing::kChunkSizeMs * sample_rate_hz / 1000; } -AudioProcessing::ChannelLayout LayoutFromChannels(size_t num_channels) { - switch (num_channels) { - case 1: - return AudioProcessing::kMono; - case 2: - return AudioProcessing::kStereo; - default: - RTC_CHECK_NOTREACHED(); - } -} - } // namespace webrtc diff --git a/modules/audio_processing/test/test_utils.h b/modules/audio_processing/test/test_utils.h index 30674cb143..de0fc11893 100644 --- a/modules/audio_processing/test/test_utils.h +++ b/modules/audio_processing/test/test_utils.h @@ -23,7 +23,6 @@ #include "common_audio/channel_buffer.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/include/audio_processing.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -35,13 +34,14 @@ class RawFile final { explicit RawFile(const std::string& filename); ~RawFile(); + RawFile(const RawFile&) = delete; + RawFile& operator=(const RawFile&) = delete; + void WriteSamples(const int16_t* samples, size_t num_samples); void WriteSamples(const float* samples, size_t num_samples); private: FILE* file_handle_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RawFile); }; // Encapsulates samples and metadata for an integer frame. @@ -78,6 +78,9 @@ class ChannelBufferWavReader final { explicit ChannelBufferWavReader(std::unique_ptr file); ~ChannelBufferWavReader(); + ChannelBufferWavReader(const ChannelBufferWavReader&) = delete; + ChannelBufferWavReader& operator=(const ChannelBufferWavReader&) = delete; + // Reads data from the file according to the `buffer` format. Returns false if // a full buffer can't be read from the file. bool Read(ChannelBuffer* buffer); @@ -85,8 +88,6 @@ class ChannelBufferWavReader final { private: std::unique_ptr file_; std::vector interleaved_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ChannelBufferWavReader); }; // Writes ChannelBuffers to a provided WavWriter. @@ -95,13 +96,14 @@ class ChannelBufferWavWriter final { explicit ChannelBufferWavWriter(std::unique_ptr file); ~ChannelBufferWavWriter(); + ChannelBufferWavWriter(const ChannelBufferWavWriter&) = delete; + ChannelBufferWavWriter& operator=(const ChannelBufferWavWriter&) = delete; + void Write(const ChannelBuffer& buffer); private: std::unique_ptr file_; std::vector interleaved_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ChannelBufferWavWriter); }; // Takes a pointer to a vector. Allows appending the samples of channel buffers @@ -152,8 +154,6 @@ void SetContainerFormat(int sample_rate_hz, cb->reset(new ChannelBuffer(frame->samples_per_channel, num_channels)); } -AudioProcessing::ChannelLayout LayoutFromChannels(size_t num_channels); - template float ComputeSNR(const T* ref, const T* test, size_t length, float* variance) { float mse = 0; diff --git a/modules/audio_processing/test/wav_based_simulator.cc b/modules/audio_processing/test/wav_based_simulator.cc index 10c0d3717e..52a05a3871 100644 --- a/modules/audio_processing/test/wav_based_simulator.cc +++ b/modules/audio_processing/test/wav_based_simulator.cc @@ -82,7 +82,7 @@ void WavBasedSimulator::PrepareProcessStreamCall() { if (settings_.fixed_interface) { fwd_frame_.CopyFrom(*in_buf_); } - ap_->set_stream_key_pressed(settings_.use_ts && (*settings_.use_ts)); + ap_->set_stream_key_pressed(settings_.override_key_pressed.value_or(false)); if (!settings_.use_stream_delay || *settings_.use_stream_delay) { RTC_CHECK_EQ(AudioProcessing::kNoError, diff --git a/modules/audio_processing/three_band_filter_bank.cc b/modules/audio_processing/three_band_filter_bank.cc index fc665efcc1..bd1c50477a 100644 --- a/modules/audio_processing/three_band_filter_bank.cc +++ b/modules/audio_processing/three_band_filter_bank.cc @@ -211,8 +211,9 @@ void ThreeBandFilterBank::Analysis( // 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]; + out_band[n] += dct_modulation[band] * out_subsampled[n]; } } } @@ -254,8 +255,9 @@ void ThreeBandFilterBank::Synthesis( 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]; + in_subsampled[n] += dct_modulation[band] * in_band[n]; } } diff --git a/modules/audio_processing/transient/transient_suppression_test.cc b/modules/audio_processing/transient/transient_suppression_test.cc index d06fd96bac..21409132d2 100644 --- a/modules/audio_processing/transient/transient_suppression_test.cc +++ b/modules/audio_processing/transient/transient_suppression_test.cc @@ -191,8 +191,7 @@ void void_main() { in_file, audio_buffer_size, absl::GetFlag(FLAGS_num_channels), audio_buffer_i.get(), detection_file, detection_buffer_size, detection_buffer.get(), reference_file, reference_buffer.get())) { - agc.Process(audio_buffer_i.get(), static_cast(audio_buffer_size), - absl::GetFlag(FLAGS_sample_rate_hz)); + agc.Process({audio_buffer_i.get(), audio_buffer_size}); for (size_t i = 0; i < absl::GetFlag(FLAGS_num_channels) * audio_buffer_size; ++i) { diff --git a/modules/audio_processing/transient/transient_suppressor_impl.cc b/modules/audio_processing/transient/transient_suppressor_impl.cc index 8e43d78ea7..f8161f6428 100644 --- a/modules/audio_processing/transient/transient_suppressor_impl.cc +++ b/modules/audio_processing/transient/transient_suppressor_impl.cc @@ -102,7 +102,7 @@ int TransientSuppressorImpl::Initialize(int sample_rate_hz, detector_.reset(new TransientDetector(detection_rate_hz)); data_length_ = sample_rate_hz * ts::kChunkSizeMs / 1000; if (data_length_ > analysis_length_) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } buffer_delay_ = analysis_length_ - data_length_; diff --git a/modules/audio_processing/utility/cascaded_biquad_filter.cc b/modules/audio_processing/utility/cascaded_biquad_filter.cc index 08b9464387..0d236ce0be 100644 --- a/modules/audio_processing/utility/cascaded_biquad_filter.cc +++ b/modules/audio_processing/utility/cascaded_biquad_filter.cc @@ -99,19 +99,28 @@ void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView x, rtc::ArrayView y, CascadedBiQuadFilter::BiQuad* biquad) { RTC_DCHECK_EQ(x.size(), y.size()); - const auto* c_b = biquad->coefficients.b; - const auto* c_a = biquad->coefficients.a; - auto* m_x = biquad->x; - auto* m_y = biquad->y; + 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]; + 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/modules/audio_processing/vad/voice_activity_detector.cc b/modules/audio_processing/vad/voice_activity_detector.cc index ce4d46b9ae..02023d6a72 100644 --- a/modules/audio_processing/vad/voice_activity_detector.cc +++ b/modules/audio_processing/vad/voice_activity_detector.cc @@ -38,6 +38,7 @@ 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) { diff --git a/modules/audio_processing/vad/voice_activity_detector.h b/modules/audio_processing/vad/voice_activity_detector.h index a19883d51c..92b9a8c208 100644 --- a/modules/audio_processing/vad/voice_activity_detector.h +++ b/modules/audio_processing/vad/voice_activity_detector.h @@ -33,6 +33,8 @@ class VoiceActivityDetector { ~VoiceActivityDetector(); // Processes each audio chunk and estimates the voice probability. + // TODO(bugs.webrtc.org/7494): Switch to rtc::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 diff --git a/modules/audio_processing/voice_detection.cc b/modules/audio_processing/voice_detection.cc deleted file mode 100644 index e6c92ae934..0000000000 --- a/modules/audio_processing/voice_detection.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/voice_detection.h" - -#include "common_audio/vad/include/webrtc_vad.h" -#include "modules/audio_processing/audio_buffer.h" -#include "rtc_base/checks.h" - -namespace webrtc { -class VoiceDetection::Vad { - public: - Vad() { - state_ = WebRtcVad_Create(); - RTC_CHECK(state_); - int error = WebRtcVad_Init(state_); - RTC_DCHECK_EQ(0, error); - } - ~Vad() { WebRtcVad_Free(state_); } - - Vad(Vad&) = delete; - Vad& operator=(Vad&) = delete; - - VadInst* state() { return state_; } - - private: - VadInst* state_ = nullptr; -}; - -VoiceDetection::VoiceDetection(int sample_rate_hz, Likelihood likelihood) - : sample_rate_hz_(sample_rate_hz), - frame_size_samples_(static_cast(sample_rate_hz_ / 100)), - likelihood_(likelihood), - vad_(new Vad()) { - int mode = 2; - switch (likelihood) { - case VoiceDetection::kVeryLowLikelihood: - mode = 3; - break; - case VoiceDetection::kLowLikelihood: - mode = 2; - break; - case VoiceDetection::kModerateLikelihood: - mode = 1; - break; - case VoiceDetection::kHighLikelihood: - mode = 0; - break; - default: - RTC_NOTREACHED(); - break; - } - int error = WebRtcVad_set_mode(vad_->state(), mode); - RTC_DCHECK_EQ(0, error); -} - -VoiceDetection::~VoiceDetection() {} - -bool VoiceDetection::ProcessCaptureAudio(AudioBuffer* audio) { - RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, - audio->num_frames_per_band()); - std::array mixed_low_pass_data; - rtc::ArrayView mixed_low_pass(mixed_low_pass_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_low_pass_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 value = - FloatS16ToS16(audio->split_channels_const(kBand0To8kHz)[0][i]); - for (int j = 1; j < num_channels; ++j) { - value += FloatS16ToS16(audio->split_channels_const(kBand0To8kHz)[j][i]); - } - mixed_low_pass_data[i] = value / num_channels; - } - } - - int vad_ret = WebRtcVad_Process(vad_->state(), sample_rate_hz_, - mixed_low_pass.data(), frame_size_samples_); - RTC_DCHECK(vad_ret == 0 || vad_ret == 1); - return vad_ret == 0 ? false : true; -} -} // namespace webrtc diff --git a/modules/audio_processing/voice_detection.h b/modules/audio_processing/voice_detection.h deleted file mode 100644 index 79d44e647c..0000000000 --- a/modules/audio_processing/voice_detection.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ -#define MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ - -#include - -#include - -#include "modules/audio_processing/include/audio_processing.h" - -namespace webrtc { - -class AudioBuffer; - -// The voice activity detection (VAD) component analyzes the stream to -// determine if voice is present. -class VoiceDetection { - public: - // Specifies the likelihood that a frame will be declared to contain voice. - // A higher value makes it more likely that speech will not be clipped, at - // the expense of more noise being detected as voice. - enum Likelihood { - kVeryLowLikelihood, - kLowLikelihood, - kModerateLikelihood, - kHighLikelihood - }; - - VoiceDetection(int sample_rate_hz, Likelihood likelihood); - ~VoiceDetection(); - - VoiceDetection(VoiceDetection&) = delete; - VoiceDetection& operator=(VoiceDetection&) = delete; - - // Returns true if voice is detected in the current frame. - bool ProcessCaptureAudio(AudioBuffer* audio); - - Likelihood likelihood() const { return likelihood_; } - - private: - class Vad; - - int sample_rate_hz_; - size_t frame_size_samples_; - Likelihood likelihood_; - std::unique_ptr vad_; -}; -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ diff --git a/modules/audio_processing/voice_detection_unittest.cc b/modules/audio_processing/voice_detection_unittest.cc deleted file mode 100644 index 9a52fa60a0..0000000000 --- a/modules/audio_processing/voice_detection_unittest.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include - -#include "api/array_view.h" -#include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/test/audio_buffer_tools.h" -#include "modules/audio_processing/test/bitexactness_tools.h" -#include "modules/audio_processing/voice_detection.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -const int kNumFramesToProcess = 1000; - -// Process one frame of data and produce the output. -bool ProcessOneFrame(int sample_rate_hz, - AudioBuffer* audio_buffer, - VoiceDetection* voice_detection) { - if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { - audio_buffer->SplitIntoFrequencyBands(); - } - - return voice_detection->ProcessCaptureAudio(audio_buffer); -} - -// Processes a specified amount of frames, verifies the results and reports -// any errors. -void RunBitexactnessTest(int sample_rate_hz, - size_t num_channels, - bool stream_has_voice_reference) { - int sample_rate_to_use = std::min(sample_rate_hz, 16000); - VoiceDetection voice_detection(sample_rate_to_use, - VoiceDetection::kLowLikelihood); - - int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - const StreamConfig capture_config(sample_rate_hz, num_channels, false); - AudioBuffer capture_buffer( - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), capture_config.num_channels()); - test::InputAudioFile capture_file( - test::GetApmCaptureTestVectorFileName(sample_rate_hz)); - std::vector capture_input(samples_per_channel * num_channels); - bool stream_has_voice = false; - for (int frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &capture_file, capture_input); - - test::CopyVectorToAudioBuffer(capture_config, capture_input, - &capture_buffer); - - stream_has_voice = - ProcessOneFrame(sample_rate_hz, &capture_buffer, &voice_detection); - } - - EXPECT_EQ(stream_has_voice_reference, stream_has_voice); -} - -const bool kStreamHasVoiceReference = true; - -} // namespace - -TEST(VoiceDetectionBitExactnessTest, Mono8kHz) { - RunBitexactnessTest(8000, 1, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Mono16kHz) { - RunBitexactnessTest(16000, 1, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Mono32kHz) { - RunBitexactnessTest(32000, 1, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Mono48kHz) { - RunBitexactnessTest(48000, 1, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Stereo8kHz) { - RunBitexactnessTest(8000, 2, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Stereo16kHz) { - RunBitexactnessTest(16000, 2, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Stereo32kHz) { - RunBitexactnessTest(32000, 2, kStreamHasVoiceReference); -} - -TEST(VoiceDetectionBitExactnessTest, Stereo48kHz) { - RunBitexactnessTest(48000, 2, kStreamHasVoiceReference); -} - -} // namespace webrtc diff --git a/modules/congestion_controller/OWNERS b/modules/congestion_controller/OWNERS index 3304c672cb..c74790f876 100644 --- a/modules/congestion_controller/OWNERS +++ b/modules/congestion_controller/OWNERS @@ -1,7 +1,6 @@ -srte@webrtc.org stefan@webrtc.org terelius@webrtc.org -crodbro@webrtc.org philipel@webrtc.org mflodman@webrtc.org yinwa@webrtc.org +perkj@webrtc.org diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 9aafedbcb4..2c50c32759 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -153,6 +153,7 @@ rtc_library("loss_based_bwe_v2") { ] deps = [ "../../../api:array_view", + "../../../api:network_state_predictor_api", "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", @@ -195,6 +196,7 @@ rtc_library("send_side_bwe") { deps = [ ":loss_based_bwe_v1", ":loss_based_bwe_v2", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", @@ -325,6 +327,7 @@ if (rtc_include_tests) { ":probe_controller", ":pushback_controller", ":send_side_bwe", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/test/network_emulation", "../../../api/test/network_emulation:create_cross_traffic", diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.cc b/modules/congestion_controller/goog_cc/delay_based_bwe.cc index 185b09d8ab..95d98b2675 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.cc @@ -32,21 +32,8 @@ namespace webrtc { namespace { constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2); - -// Used with field trial "WebRTC-Bwe-NewInterArrivalDelta/Enabled/ constexpr TimeDelta kSendTimeGroupLength = TimeDelta::Millis(5); -// Used unless field trial "WebRTC-Bwe-NewInterArrivalDelta/Enabled/" -constexpr int kTimestampGroupLengthMs = 5; -constexpr int kAbsSendTimeFraction = 18; -constexpr int kAbsSendTimeInterArrivalUpshift = 8; -constexpr int kInterArrivalShift = - kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift; -constexpr int kTimestampGroupTicks = - (kTimestampGroupLengthMs << kInterArrivalShift) / 1000; -constexpr double kTimestampToMs = - 1000.0 / static_cast(1 << kInterArrivalShift); - // This ssrc is used to fulfill the current API but will be removed // after the API has been changed. constexpr uint32_t kFixedSsrc = 0; @@ -95,9 +82,6 @@ DelayBasedBwe::DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config, prev_bitrate_(DataRate::Zero()), has_once_detected_overuse_(false), prev_state_(BandwidthUsage::kBwNormal), - use_new_inter_arrival_delta_(absl::StartsWith( - key_value_config->Lookup("WebRTC-Bwe-NewInterArrivalDelta"), - "Enabled")), alr_limited_backoff_enabled_(absl::StartsWith( key_value_config->Lookup("WebRTC-Bwe-AlrLimitedBackoff"), "Enabled")) { @@ -162,17 +146,11 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, // Reset if the stream has timed out. if (last_seen_packet_.IsInfinite() || at_time - last_seen_packet_ > kStreamTimeOut) { - if (use_new_inter_arrival_delta_) { - video_inter_arrival_delta_ = - std::make_unique(kSendTimeGroupLength); - audio_inter_arrival_delta_ = - std::make_unique(kSendTimeGroupLength); - } else { - video_inter_arrival_ = std::make_unique( - kTimestampGroupTicks, kTimestampToMs, true); - audio_inter_arrival_ = std::make_unique( - kTimestampGroupTicks, kTimestampToMs, true); - } + video_inter_arrival_delta_ = + std::make_unique(kSendTimeGroupLength); + audio_inter_arrival_delta_ = + std::make_unique(kSendTimeGroupLength); + video_delay_detector_.reset( new TrendlineEstimator(key_value_config_, network_state_predictor_)); audio_delay_detector_.reset( @@ -203,57 +181,22 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, } DataSize packet_size = packet_feedback.sent_packet.size; - if (use_new_inter_arrival_delta_) { - TimeDelta send_delta = TimeDelta::Zero(); - TimeDelta recv_delta = TimeDelta::Zero(); - int size_delta = 0; + TimeDelta send_delta = TimeDelta::Zero(); + TimeDelta recv_delta = TimeDelta::Zero(); + int size_delta = 0; - InterArrivalDelta* inter_arrival_for_packet = - (separate_audio_.enabled && packet_feedback.sent_packet.audio) - ? video_inter_arrival_delta_.get() - : audio_inter_arrival_delta_.get(); - bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( - packet_feedback.sent_packet.send_time, packet_feedback.receive_time, - at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); + InterArrivalDelta* inter_arrival_for_packet = + (separate_audio_.enabled && packet_feedback.sent_packet.audio) + ? video_inter_arrival_delta_.get() + : audio_inter_arrival_delta_.get(); + bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( + packet_feedback.sent_packet.send_time, packet_feedback.receive_time, + at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); - delay_detector_for_packet->Update( - recv_delta.ms(), send_delta.ms(), - packet_feedback.sent_packet.send_time.ms(), - packet_feedback.receive_time.ms(), packet_size.bytes(), - calculated_deltas); - } else { - InterArrival* inter_arrival_for_packet = - (separate_audio_.enabled && packet_feedback.sent_packet.audio) - ? video_inter_arrival_.get() - : audio_inter_arrival_.get(); - - uint32_t send_time_24bits = - static_cast( - ((static_cast(packet_feedback.sent_packet.send_time.ms()) - << kAbsSendTimeFraction) + - 500) / - 1000) & - 0x00FFFFFF; - // Shift up send time to use the full 32 bits that inter_arrival works with, - // so wrapping works properly. - uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift; - - uint32_t timestamp_delta = 0; - int64_t recv_delta_ms = 0; - int size_delta = 0; - - bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( - timestamp, packet_feedback.receive_time.ms(), at_time.ms(), - packet_size.bytes(), ×tamp_delta, &recv_delta_ms, &size_delta); - double send_delta_ms = - (1000.0 * timestamp_delta) / (1 << kInterArrivalShift); - - delay_detector_for_packet->Update( - recv_delta_ms, send_delta_ms, - packet_feedback.sent_packet.send_time.ms(), - packet_feedback.receive_time.ms(), packet_size.bytes(), - calculated_deltas); - } + delay_detector_for_packet->Update(recv_delta.ms(), send_delta.ms(), + packet_feedback.sent_packet.send_time.ms(), + packet_feedback.receive_time.ms(), + packet_size.bytes(), calculated_deltas); } DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time, @@ -299,8 +242,8 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( if (probe_bitrate) { result.probe = true; result.updated = true; - result.target_bitrate = *probe_bitrate; rate_control_.SetEstimate(*probe_bitrate, at_time); + result.target_bitrate = rate_control_.LatestEstimate(); } else { result.updated = UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate); @@ -322,6 +265,8 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( prev_bitrate_ = bitrate; prev_state_ = detector_state; } + + result.delay_detector_state = detector_state; return result; } diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.h b/modules/congestion_controller/goog_cc/delay_based_bwe.h index 85ce6eaa82..d37e05f8dd 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.h @@ -56,6 +56,7 @@ class DelayBasedBwe { DataRate target_bitrate = DataRate::Zero(); bool recovered_from_overuse; bool backoff_in_alr; + BandwidthUsage delay_detector_state; }; explicit DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config, @@ -127,7 +128,6 @@ class DelayBasedBwe { DataRate prev_bitrate_; bool has_once_detected_overuse_; BandwidthUsage prev_state_; - const bool use_new_inter_arrival_delta_; bool alr_limited_backoff_enabled_; }; diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc index 06345c4d9b..71b7ee7f90 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc @@ -28,15 +28,7 @@ const PacedPacketInfo kPacingInfo1(1, kNumProbesCluster1, 4000); constexpr float kTargetUtilizationFraction = 0.95f; } // namespace -INSTANTIATE_TEST_SUITE_P( - , - DelayBasedBweTest, - ::testing::Values("", "WebRTC-Bwe-NewInterArrivalDelta/Enabled/"), - [](::testing::TestParamInfo info) { - return info.param == "" ? "Default" : "NewInterArrival"; - }); - -TEST_P(DelayBasedBweTest, ProbeDetection) { +TEST_F(DelayBasedBweTest, ProbeDetection) { int64_t now_ms = clock_.TimeInMilliseconds(); // First burst sent at 8 * 1000 / 10 = 800 kbps. @@ -58,7 +50,7 @@ TEST_P(DelayBasedBweTest, ProbeDetection) { EXPECT_GT(bitrate_observer_.latest_bitrate(), 1500000u); } -TEST_P(DelayBasedBweTest, ProbeDetectionNonPacedPackets) { +TEST_F(DelayBasedBweTest, ProbeDetectionNonPacedPackets) { int64_t now_ms = clock_.TimeInMilliseconds(); // First burst sent at 8 * 1000 / 10 = 800 kbps, but with every other packet // not being paced which could mess things up. @@ -75,7 +67,7 @@ TEST_P(DelayBasedBweTest, ProbeDetectionNonPacedPackets) { EXPECT_GT(bitrate_observer_.latest_bitrate(), 800000u); } -TEST_P(DelayBasedBweTest, ProbeDetectionFasterArrival) { +TEST_F(DelayBasedBweTest, ProbeDetectionFasterArrival) { int64_t now_ms = clock_.TimeInMilliseconds(); // First burst sent at 8 * 1000 / 10 = 800 kbps. // Arriving at 8 * 1000 / 5 = 1600 kbps. @@ -90,7 +82,7 @@ TEST_P(DelayBasedBweTest, ProbeDetectionFasterArrival) { EXPECT_FALSE(bitrate_observer_.updated()); } -TEST_P(DelayBasedBweTest, ProbeDetectionSlowerArrival) { +TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrival) { int64_t now_ms = clock_.TimeInMilliseconds(); // First burst sent at 8 * 1000 / 5 = 1600 kbps. // Arriving at 8 * 1000 / 7 = 1142 kbps. @@ -109,7 +101,7 @@ TEST_P(DelayBasedBweTest, ProbeDetectionSlowerArrival) { kTargetUtilizationFraction * 1140000u, 10000u); } -TEST_P(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) { +TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) { int64_t now_ms = clock_.TimeInMilliseconds(); // Burst sent at 8 * 1000 / 1 = 8000 kbps. // Arriving at 8 * 1000 / 2 = 4000 kbps. @@ -128,7 +120,7 @@ TEST_P(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) { kTargetUtilizationFraction * 4000000u, 10000u); } -TEST_P(DelayBasedBweTest, GetExpectedBwePeriodMs) { +TEST_F(DelayBasedBweTest, GetExpectedBwePeriodMs) { auto default_interval = bitrate_estimator_->GetExpectedBwePeriod(); EXPECT_GT(default_interval.ms(), 0); CapacityDropTestHelper(1, true, 333, 0); @@ -137,45 +129,45 @@ TEST_P(DelayBasedBweTest, GetExpectedBwePeriodMs) { EXPECT_NE(interval.ms(), default_interval.ms()); } -TEST_P(DelayBasedBweTest, InitialBehavior) { +TEST_F(DelayBasedBweTest, InitialBehavior) { InitialBehaviorTestHelper(730000); } -TEST_P(DelayBasedBweTest, RateIncreaseReordering) { +TEST_F(DelayBasedBweTest, RateIncreaseReordering) { RateIncreaseReorderingTestHelper(730000); } -TEST_P(DelayBasedBweTest, RateIncreaseRtpTimestamps) { +TEST_F(DelayBasedBweTest, RateIncreaseRtpTimestamps) { RateIncreaseRtpTimestampsTestHelper(622); } -TEST_P(DelayBasedBweTest, CapacityDropOneStream) { +TEST_F(DelayBasedBweTest, CapacityDropOneStream) { CapacityDropTestHelper(1, false, 300, 0); } -TEST_P(DelayBasedBweTest, CapacityDropPosOffsetChange) { +TEST_F(DelayBasedBweTest, CapacityDropPosOffsetChange) { CapacityDropTestHelper(1, false, 867, 30000); } -TEST_P(DelayBasedBweTest, CapacityDropNegOffsetChange) { +TEST_F(DelayBasedBweTest, CapacityDropNegOffsetChange) { CapacityDropTestHelper(1, false, 933, -30000); } -TEST_P(DelayBasedBweTest, CapacityDropOneStreamWrap) { +TEST_F(DelayBasedBweTest, CapacityDropOneStreamWrap) { CapacityDropTestHelper(1, true, 333, 0); } -TEST_P(DelayBasedBweTest, TestTimestampGrouping) { +TEST_F(DelayBasedBweTest, TestTimestampGrouping) { TestTimestampGroupingTestHelper(); } -TEST_P(DelayBasedBweTest, TestShortTimeoutAndWrap) { +TEST_F(DelayBasedBweTest, TestShortTimeoutAndWrap) { // Simulate a client leaving and rejoining the call after 35 seconds. This // will make abs send time wrap, so if streams aren't timed out properly // the next 30 seconds of packets will be out of order. TestWrappingHelper(35); } -TEST_P(DelayBasedBweTest, TestLongTimeoutAndWrap) { +TEST_F(DelayBasedBweTest, TestLongTimeoutAndWrap) { // Simulate a client leaving and rejoining the call after some multiple of // 64 seconds later. This will cause a zero difference in abs send times due // to the wrap, but a big difference in arrival time, if streams aren't @@ -183,7 +175,7 @@ TEST_P(DelayBasedBweTest, TestLongTimeoutAndWrap) { TestWrappingHelper(10 * 64); } -TEST_P(DelayBasedBweTest, TestInitialOveruse) { +TEST_F(DelayBasedBweTest, TestInitialOveruse) { const DataRate kStartBitrate = DataRate::KilobitsPerSec(300); const DataRate kInitialCapacity = DataRate::KilobitsPerSec(200); const uint32_t kDummySsrc = 0; @@ -223,16 +215,15 @@ TEST_P(DelayBasedBweTest, TestInitialOveruse) { } class DelayBasedBweTestWithBackoffTimeoutExperiment : public DelayBasedBweTest { + public: + DelayBasedBweTestWithBackoffTimeoutExperiment() + : DelayBasedBweTest( + "WebRTC-BweAimdRateControlConfig/initial_backoff_interval:200ms/") { + } }; -INSTANTIATE_TEST_SUITE_P( - , - DelayBasedBweTestWithBackoffTimeoutExperiment, - ::testing::Values( - "WebRTC-BweAimdRateControlConfig/initial_backoff_interval:200ms/")); - // This test subsumes and improves DelayBasedBweTest.TestInitialOveruse above. -TEST_P(DelayBasedBweTestWithBackoffTimeoutExperiment, TestInitialOveruse) { +TEST_F(DelayBasedBweTestWithBackoffTimeoutExperiment, TestInitialOveruse) { const DataRate kStartBitrate = DataRate::KilobitsPerSec(300); const DataRate kInitialCapacity = DataRate::KilobitsPerSec(200); const uint32_t kDummySsrc = 0; diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc index 4c72277aa8..3eb0ae38e5 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc @@ -145,8 +145,11 @@ int64_t StreamGenerator::GenerateFrame(std::vector* packets, } } // namespace test -DelayBasedBweTest::DelayBasedBweTest() - : field_trial(std::make_unique(GetParam())), +DelayBasedBweTest::DelayBasedBweTest() : DelayBasedBweTest("") {} + +DelayBasedBweTest::DelayBasedBweTest(const std::string& field_trial_string) + : field_trial( + std::make_unique(field_trial_string)), clock_(100000000), acknowledged_bitrate_estimator_( AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)), diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h index 5fb048b7a4..474d2970df 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h @@ -22,7 +22,6 @@ #include "api/transport/network_types.h" #include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h" #include "modules/congestion_controller/goog_cc/delay_based_bwe.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" @@ -54,6 +53,9 @@ class RtpStream { RtpStream(int fps, int bitrate_bps); + RtpStream(const RtpStream&) = delete; + RtpStream& operator=(const RtpStream&) = delete; + // Generates a new frame for this stream. If called too soon after the // previous frame, no frame will be generated. The frame is split into // packets. @@ -74,8 +76,6 @@ class RtpStream { int fps_; int bitrate_bps_; int64_t next_rtp_time_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpStream); }; class StreamGenerator { @@ -83,6 +83,9 @@ class StreamGenerator { StreamGenerator(int capacity, int64_t time_now); ~StreamGenerator(); + StreamGenerator(const StreamGenerator&) = delete; + StreamGenerator& operator=(const StreamGenerator&) = delete; + // Add a new stream. void AddStream(RtpStream* stream); @@ -108,14 +111,13 @@ class StreamGenerator { int64_t prev_arrival_time_us_; // All streams being transmitted on this simulated channel. std::vector> streams_; - - RTC_DISALLOW_COPY_AND_ASSIGN(StreamGenerator); }; } // namespace test -class DelayBasedBweTest : public ::testing::TestWithParam { +class DelayBasedBweTest : public ::testing::Test { public: DelayBasedBweTest(); + explicit DelayBasedBweTest(const std::string& field_trial_string); ~DelayBasedBweTest() override; protected: diff --git a/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h b/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h index eaadb0d124..fc12cff7d5 100644 --- a/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h +++ b/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h @@ -13,7 +13,6 @@ #include #include "api/network_state_predictor.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -22,6 +21,11 @@ class DelayIncreaseDetectorInterface { DelayIncreaseDetectorInterface() {} virtual ~DelayIncreaseDetectorInterface() {} + DelayIncreaseDetectorInterface(const DelayIncreaseDetectorInterface&) = + delete; + DelayIncreaseDetectorInterface& operator=( + const DelayIncreaseDetectorInterface&) = delete; + // Update the detector with a new sample. The deltas should represent deltas // between timestamp groups as defined by the InterArrival class. virtual void Update(double recv_delta_ms, @@ -32,8 +36,6 @@ class DelayIncreaseDetectorInterface { bool calculated_deltas) = 0; virtual BandwidthUsage State() const = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(DelayIncreaseDetectorInterface); }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index c949f187fe..1269522e65 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -22,6 +22,7 @@ #include #include "absl/strings/match.h" +#include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" #include "modules/congestion_controller/goog_cc/alr_detector.h" @@ -88,6 +89,9 @@ GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config, RateControlSettings::ParseFromKeyValueConfig(key_value_config_)), loss_based_stable_rate_( IsEnabled(key_value_config_, "WebRTC-Bwe-LossBasedStableRate")), + pace_at_max_of_bwe_and_lower_link_capacity_( + IsEnabled(key_value_config_, + "WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity")), probe_controller_( new ProbeController(key_value_config_, config.event_log)), congestion_window_pushback_controller_( @@ -491,7 +495,6 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, report.feedback_time); - bandwidth_estimation_->IncomingPacketFeedbackVector(report); for (const auto& feedback : report.SortedByReceiveTime()) { if (feedback.sent_packet.pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { @@ -549,11 +552,13 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( } // Since SetSendBitrate now resets the delay-based estimate, we have to // call UpdateDelayBasedEstimate after SetSendBitrate. - bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time, - result.target_bitrate); + bandwidth_estimation_->UpdateDelayBasedEstimate( + report.feedback_time, result.target_bitrate, + result.delay_detector_state); // Update the estimate in the ProbeController, in case we want to probe. MaybeTriggerOnNetworkChanged(&update, report.feedback_time); } + bandwidth_estimation_->UpdateLossBasedEstimatorFromFeedbackVector(report); recovered_from_overuse = result.recovered_from_overuse; backoff_in_alr = result.backoff_in_alr; @@ -694,9 +699,17 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( PacerConfig GoogCcNetworkController::GetPacingRates(Timestamp at_time) const { // Pacing rate is based on target rate before congestion window pushback, // because we don't want to build queues in the pacer when pushback occurs. - DataRate pacing_rate = - std::max(min_total_allocated_bitrate_, last_loss_based_target_rate_) * - pacing_factor_; + DataRate pacing_rate = DataRate::Zero(); + if (pace_at_max_of_bwe_and_lower_link_capacity_ && estimate_) { + pacing_rate = + std::max({min_total_allocated_bitrate_, estimate_->link_capacity_lower, + last_loss_based_target_rate_}) * + pacing_factor_; + } else { + pacing_rate = + std::max(min_total_allocated_bitrate_, last_loss_based_target_rate_) * + pacing_factor_; + } DataRate padding_rate = std::min(max_padding_rate_, last_pushback_target_rate_); PacerConfig msg; diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/modules/congestion_controller/goog_cc/goog_cc_network_control.h index 6dd70c8969..946c076939 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.h +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.h @@ -94,6 +94,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { const bool limit_probes_lower_than_throughput_estimate_; const RateControlSettings rate_control_settings_; const bool loss_based_stable_rate_; + const bool pace_at_max_of_bwe_and_lower_link_capacity_; const std::unique_ptr probe_controller_; const std::unique_ptr diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc index 7e8d7b9ac6..0552109a0d 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc @@ -13,17 +13,14 @@ #include "api/test/network_emulation/create_cross_traffic.h" #include "api/test/network_emulation/cross_traffic.h" #include "api/transport/goog_cc_factory.h" +#include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "test/field_trial.h" #include "test/gtest.h" #include "test/scenario/scenario.h" -using ::testing::_; -using ::testing::Field; -using ::testing::Matcher; using ::testing::NiceMock; -using ::testing::Property; namespace webrtc { namespace test { @@ -75,6 +72,72 @@ CallClient* CreateVideoSendingClient( return client; } +NetworkRouteChange CreateRouteChange( + Timestamp time, + absl::optional start_rate = absl::nullopt, + absl::optional min_rate = absl::nullopt, + absl::optional max_rate = absl::nullopt) { + NetworkRouteChange route_change; + route_change.at_time = time; + route_change.constraints.at_time = time; + route_change.constraints.min_data_rate = min_rate; + route_change.constraints.max_data_rate = max_rate; + route_change.constraints.starting_rate = start_rate; + return route_change; +} + +PacketResult CreatePacketResult(Timestamp arrival_time, + Timestamp send_time, + size_t payload_size, + PacedPacketInfo pacing_info) { + PacketResult packet_result; + packet_result.sent_packet = SentPacket(); + packet_result.sent_packet.send_time = send_time; + packet_result.sent_packet.size = DataSize::Bytes(payload_size); + packet_result.sent_packet.pacing_info = pacing_info; + packet_result.receive_time = arrival_time; + return packet_result; +} + +// Simulate sending packets and receiving transport feedback during +// `runtime_ms`. +absl::optional PacketTransmissionAndFeedbackBlock( + NetworkControllerInterface* controller, + int64_t runtime_ms, + int64_t delay, + Timestamp& current_time) { + NetworkControlUpdate update; + absl::optional target_bitrate; + int64_t delay_buildup = 0; + int64_t start_time_ms = current_time.ms(); + while (current_time.ms() - start_time_ms < runtime_ms) { + constexpr size_t kPayloadSize = 1000; + PacketResult packet = + CreatePacketResult(current_time + TimeDelta::Millis(delay_buildup), + current_time, kPayloadSize, PacedPacketInfo()); + delay_buildup += delay; + update = controller->OnSentPacket(packet.sent_packet); + if (update.target_rate) { + target_bitrate = update.target_rate->target_rate; + } + TransportPacketsFeedback feedback; + feedback.feedback_time = packet.receive_time; + feedback.packet_feedbacks.push_back(packet); + update = controller->OnTransportPacketsFeedback(feedback); + if (update.target_rate) { + target_bitrate = update.target_rate->target_rate; + } + current_time += TimeDelta::Millis(50); + update = controller->OnProcessInterval({.at_time = current_time}); + if (update.target_rate) { + target_bitrate = update.target_rate->target_rate; + } + } + return target_bitrate; +} + +// Scenarios: + void UpdatesTargetRateBasedOnLinkCapacity(std::string test_name = "") { ScopedFieldTrials trial("WebRTC-SendSideBwe-WithOverhead/Enabled/"); auto factory = CreateFeedbackOnlyFactory(); @@ -153,40 +216,27 @@ DataRate RunRembDipScenario(std::string test_name) { return client->send_bandwidth(); } + } // namespace -class GoogCcNetworkControllerTest : public ::testing::Test { - protected: - GoogCcNetworkControllerTest() - : current_time_(Timestamp::Millis(123456)), factory_() {} - ~GoogCcNetworkControllerTest() override {} +class NetworkControllerTestFixture { + public: + NetworkControllerTestFixture() : factory_() {} - void SetUp() override { - controller_ = factory_.Create(InitialConfig()); - NetworkControlUpdate update = - controller_->OnProcessInterval(DefaultInterval()); - EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate); - EXPECT_EQ(update.pacer_config->data_rate(), - kInitialBitrate * kDefaultPacingRate); - - EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate, - kInitialBitrate * 3); - EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate, - kInitialBitrate * 5); - } - // Custom setup - use an observer that tracks the target bitrate, without - // prescribing on which iterations it must change (like a mock would). - void TargetBitrateTrackingSetup() { - controller_ = factory_.Create(InitialConfig()); - OnUpdate(controller_->OnProcessInterval(DefaultInterval())); + std::unique_ptr CreateController() { + NetworkControllerConfig config = InitialConfig(); + std::unique_ptr controller = + factory_.Create(config); + return controller; } + private: NetworkControllerConfig InitialConfig( int starting_bandwidth_kbps = kInitialBitrateKbps, int min_data_rate_kbps = 0, int max_data_rate_kbps = 5 * kInitialBitrateKbps) { NetworkControllerConfig config; - config.constraints.at_time = current_time_; + config.constraints.at_time = Timestamp::Millis(0); config.constraints.min_data_rate = DataRate::KilobitsPerSec(min_data_rate_kbps); config.constraints.max_data_rate = @@ -196,102 +246,173 @@ class GoogCcNetworkControllerTest : public ::testing::Test { config.event_log = &event_log_; return config; } - ProcessInterval DefaultInterval() { - ProcessInterval interval; - interval.at_time = current_time_; - return interval; - } - RemoteBitrateReport CreateBitrateReport(DataRate rate) { - RemoteBitrateReport report; - report.receive_time = current_time_; - report.bandwidth = rate; - return report; - } - PacketResult CreateResult(int64_t arrival_time_ms, - int64_t send_time_ms, - size_t payload_size, - PacedPacketInfo pacing_info) { - PacketResult packet_result; - packet_result.sent_packet = SentPacket(); - packet_result.sent_packet.send_time = Timestamp::Millis(send_time_ms); - packet_result.sent_packet.size = DataSize::Bytes(payload_size); - packet_result.sent_packet.pacing_info = pacing_info; - packet_result.receive_time = Timestamp::Millis(arrival_time_ms); - return packet_result; - } - NetworkRouteChange CreateRouteChange( - absl::optional start_rate = absl::nullopt, - absl::optional min_rate = absl::nullopt, - absl::optional max_rate = absl::nullopt) { - NetworkRouteChange route_change; - route_change.at_time = current_time_; - route_change.constraints.at_time = current_time_; - route_change.constraints.min_data_rate = min_rate; - route_change.constraints.max_data_rate = max_rate; - route_change.constraints.starting_rate = start_rate; - return route_change; - } - - void AdvanceTimeMilliseconds(int timedelta_ms) { - current_time_ += TimeDelta::Millis(timedelta_ms); - } - - void OnUpdate(NetworkControlUpdate update) { - if (update.target_rate) - target_bitrate_ = update.target_rate->target_rate; - } - - void PacketTransmissionAndFeedbackBlock(int64_t runtime_ms, int64_t delay) { - int64_t delay_buildup = 0; - int64_t start_time_ms = current_time_.ms(); - while (current_time_.ms() - start_time_ms < runtime_ms) { - constexpr size_t kPayloadSize = 1000; - PacketResult packet = - CreateResult(current_time_.ms() + delay_buildup, current_time_.ms(), - kPayloadSize, PacedPacketInfo()); - delay_buildup += delay; - OnUpdate(controller_->OnSentPacket(packet.sent_packet)); - TransportPacketsFeedback feedback; - feedback.feedback_time = packet.receive_time; - feedback.packet_feedbacks.push_back(packet); - OnUpdate(controller_->OnTransportPacketsFeedback(feedback)); - AdvanceTimeMilliseconds(50); - OnUpdate(controller_->OnProcessInterval(DefaultInterval())); - } - } - Timestamp current_time_; - absl::optional target_bitrate_; NiceMock event_log_; GoogCcNetworkControllerFactory factory_; - std::unique_ptr controller_; }; -TEST_F(GoogCcNetworkControllerTest, ReactsToChangedNetworkConditions) { - // Test no change. - AdvanceTimeMilliseconds(25); - OnUpdate(controller_->OnProcessInterval(DefaultInterval())); +TEST(GoogCcNetworkControllerTest, InitializeTargetRateOnFirstProcessInterval) { + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); - NetworkControlUpdate update; - OnUpdate(controller_->OnRemoteBitrateReport( - CreateBitrateReport(kInitialBitrate * 2))); - AdvanceTimeMilliseconds(25); - update = controller_->OnProcessInterval(DefaultInterval()); + NetworkControlUpdate update = + controller->OnProcessInterval({.at_time = Timestamp::Millis(123456)}); + + EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate); + EXPECT_EQ(update.pacer_config->data_rate(), + kInitialBitrate * kDefaultPacingRate); + EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate, + kInitialBitrate * 3); + EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate, + kInitialBitrate * 5); +} + +TEST(GoogCcNetworkControllerTest, ReactsToChangedNetworkConditions) { + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); + Timestamp current_time = Timestamp::Millis(123); + NetworkControlUpdate update = + controller->OnProcessInterval({.at_time = current_time}); + update = controller->OnRemoteBitrateReport( + {.receive_time = current_time, .bandwidth = kInitialBitrate * 2}); + + current_time += TimeDelta::Millis(25); + update = controller->OnProcessInterval({.at_time = current_time}); EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate * 2); EXPECT_EQ(update.pacer_config->data_rate(), kInitialBitrate * 2 * kDefaultPacingRate); - OnUpdate( - controller_->OnRemoteBitrateReport(CreateBitrateReport(kInitialBitrate))); - AdvanceTimeMilliseconds(25); - update = controller_->OnProcessInterval(DefaultInterval()); + update = controller->OnRemoteBitrateReport( + {.receive_time = current_time, .bandwidth = kInitialBitrate}); + current_time += TimeDelta::Millis(25); + update = controller->OnProcessInterval({.at_time = current_time}); EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate); EXPECT_EQ(update.pacer_config->data_rate(), kInitialBitrate * kDefaultPacingRate); } +TEST(GoogCcNetworkControllerTest, OnNetworkRouteChanged) { + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); + Timestamp current_time = Timestamp::Millis(123); + DataRate new_bitrate = DataRate::BitsPerSec(200000); + NetworkControlUpdate update = controller->OnNetworkRouteChange( + CreateRouteChange(current_time, new_bitrate)); + EXPECT_EQ(update.target_rate->target_rate, new_bitrate); + EXPECT_EQ(update.pacer_config->data_rate(), new_bitrate * kDefaultPacingRate); + EXPECT_EQ(update.probe_cluster_configs.size(), 2u); + + // If the bitrate is reset to -1, the new starting bitrate will be + // the minimum default bitrate. + const DataRate kDefaultMinBitrate = DataRate::KilobitsPerSec(5); + update = controller->OnNetworkRouteChange(CreateRouteChange(current_time)); + EXPECT_EQ(update.target_rate->target_rate, kDefaultMinBitrate); + EXPECT_NEAR(update.pacer_config->data_rate().bps(), + kDefaultMinBitrate.bps() * kDefaultPacingRate, 10); + EXPECT_EQ(update.probe_cluster_configs.size(), 2u); +} + +TEST(GoogCcNetworkControllerTest, ProbeOnRouteChange) { + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); + Timestamp current_time = Timestamp::Millis(123); + NetworkControlUpdate update = controller->OnNetworkRouteChange( + CreateRouteChange(current_time, 2 * kInitialBitrate, DataRate::Zero(), + 20 * kInitialBitrate)); + + EXPECT_TRUE(update.pacer_config.has_value()); + EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate * 2); + EXPECT_EQ(update.probe_cluster_configs.size(), 2u); + EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate, + kInitialBitrate * 6); + EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate, + kInitialBitrate * 12); + + update = controller->OnProcessInterval({.at_time = current_time}); +} + +// Bandwidth estimation is updated when feedbacks are received. +// Feedbacks which show an increasing delay cause the estimation to be reduced. +TEST(GoogCcNetworkControllerTest, UpdatesDelayBasedEstimate) { + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); + const int64_t kRunTimeMs = 6000; + Timestamp current_time = Timestamp::Millis(123); + + // The test must run and insert packets/feedback long enough that the + // BWE computes a valid estimate. This is first done in an environment which + // simulates no bandwidth limitation, and therefore not built-up delay. + absl::optional target_bitrate_before_delay = + PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 0, + current_time); + ASSERT_TRUE(target_bitrate_before_delay.has_value()); + + // Repeat, but this time with a building delay, and make sure that the + // estimation is adjusted downwards. + absl::optional target_bitrate_after_delay = + PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 50, + current_time); + EXPECT_LT(*target_bitrate_after_delay, *target_bitrate_before_delay); +} + +TEST(GoogCcNetworkControllerTest, PaceAtMaxOfLowerLinkCapacityAndBwe) { + ScopedFieldTrials trial( + "WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity/Enabled/"); + NetworkControllerTestFixture fixture; + std::unique_ptr controller = + fixture.CreateController(); + Timestamp current_time = Timestamp::Millis(123); + NetworkControlUpdate update = + controller->OnProcessInterval({.at_time = current_time}); + current_time += TimeDelta::Millis(100); + NetworkStateEstimate network_estimate = {.link_capacity_lower = + 10 * kInitialBitrate}; + update = controller->OnNetworkStateEstimate(network_estimate); + // OnNetworkStateEstimate does not trigger processing a new estimate. So add a + // dummy loss report to trigger a BWE update in the next process interval. + TransportLossReport loss_report; + loss_report.start_time = current_time; + loss_report.end_time = current_time; + loss_report.receive_time = current_time; + loss_report.packets_received_delta = 50; + loss_report.packets_lost_delta = 1; + update = controller->OnTransportLossReport(loss_report); + update = controller->OnProcessInterval({.at_time = current_time}); + ASSERT_TRUE(update.pacer_config); + ASSERT_TRUE(update.target_rate); + ASSERT_LT(update.target_rate->target_rate, + network_estimate.link_capacity_lower); + EXPECT_EQ(update.pacer_config->data_rate().kbps(), + network_estimate.link_capacity_lower.kbps() * kDefaultPacingRate); + + current_time += TimeDelta::Millis(100); + // Set a low link capacity estimate and verify that pacing rate is set + // relative to loss based/delay based estimate. + network_estimate = {.link_capacity_lower = 0.5 * kInitialBitrate}; + update = controller->OnNetworkStateEstimate(network_estimate); + // Again, we need to inject a dummy loss report to trigger an update of the + // BWE in the next process interval. + loss_report.start_time = current_time; + loss_report.end_time = current_time; + loss_report.receive_time = current_time; + loss_report.packets_received_delta = 50; + loss_report.packets_lost_delta = 0; + update = controller->OnTransportLossReport(loss_report); + update = controller->OnProcessInterval({.at_time = current_time}); + ASSERT_TRUE(update.target_rate); + ASSERT_GT(update.target_rate->target_rate, + network_estimate.link_capacity_lower); + EXPECT_EQ(update.pacer_config->data_rate().kbps(), + update.target_rate->target_rate.kbps() * kDefaultPacingRate); +} + // Test congestion window pushback on network delay happens. -TEST_F(GoogCcNetworkControllerTest, CongestionWindowPushbackOnNetworkDelay) { +TEST(GoogCcScenario, CongestionWindowPushbackOnNetworkDelay) { auto factory = CreateFeedbackOnlyFactory(); ScopedFieldTrials trial( "WebRTC-CongestionWindow/QueueSize:800,MinBitrate:30000/"); @@ -325,8 +446,7 @@ TEST_F(GoogCcNetworkControllerTest, CongestionWindowPushbackOnNetworkDelay) { } // Test congestion window pushback on network delay happens. -TEST_F(GoogCcNetworkControllerTest, - CongestionWindowPushbackDropFrameOnNetworkDelay) { +TEST(GoogCcScenario, CongestionWindowPushbackDropFrameOnNetworkDelay) { auto factory = CreateFeedbackOnlyFactory(); ScopedFieldTrials trial( "WebRTC-CongestionWindow/QueueSize:800,MinBitrate:30000,DropFrame:true/"); @@ -358,61 +478,7 @@ TEST_F(GoogCcNetworkControllerTest, EXPECT_GT(client->target_rate().kbps(), 300); } -TEST_F(GoogCcNetworkControllerTest, OnNetworkRouteChanged) { - NetworkControlUpdate update; - DataRate new_bitrate = DataRate::BitsPerSec(200000); - update = controller_->OnNetworkRouteChange(CreateRouteChange(new_bitrate)); - EXPECT_EQ(update.target_rate->target_rate, new_bitrate); - EXPECT_EQ(update.pacer_config->data_rate(), new_bitrate * kDefaultPacingRate); - EXPECT_EQ(update.probe_cluster_configs.size(), 2u); - - // If the bitrate is reset to -1, the new starting bitrate will be - // the minimum default bitrate. - const DataRate kDefaultMinBitrate = DataRate::KilobitsPerSec(5); - update = controller_->OnNetworkRouteChange(CreateRouteChange()); - EXPECT_EQ(update.target_rate->target_rate, kDefaultMinBitrate); - EXPECT_NEAR(update.pacer_config->data_rate().bps(), - kDefaultMinBitrate.bps() * kDefaultPacingRate, 10); - EXPECT_EQ(update.probe_cluster_configs.size(), 2u); -} - -TEST_F(GoogCcNetworkControllerTest, ProbeOnRouteChange) { - NetworkControlUpdate update; - update = controller_->OnNetworkRouteChange(CreateRouteChange( - 2 * kInitialBitrate, DataRate::Zero(), 20 * kInitialBitrate)); - - EXPECT_TRUE(update.pacer_config.has_value()); - EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate * 2); - EXPECT_EQ(update.probe_cluster_configs.size(), 2u); - EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate, - kInitialBitrate * 6); - EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate, - kInitialBitrate * 12); - - update = controller_->OnProcessInterval(DefaultInterval()); -} - -// Bandwidth estimation is updated when feedbacks are received. -// Feedbacks which show an increasing delay cause the estimation to be reduced. -TEST_F(GoogCcNetworkControllerTest, UpdatesDelayBasedEstimate) { - TargetBitrateTrackingSetup(); - const int64_t kRunTimeMs = 6000; - - // The test must run and insert packets/feedback long enough that the - // BWE computes a valid estimate. This is first done in an environment which - // simulates no bandwidth limitation, and therefore not built-up delay. - PacketTransmissionAndFeedbackBlock(kRunTimeMs, 0); - ASSERT_TRUE(target_bitrate_.has_value()); - - // Repeat, but this time with a building delay, and make sure that the - // estimation is adjusted downwards. - DataRate bitrate_before_delay = *target_bitrate_; - PacketTransmissionAndFeedbackBlock(kRunTimeMs, 50); - EXPECT_LT(*target_bitrate_, bitrate_before_delay); -} - -TEST_F(GoogCcNetworkControllerTest, - PaddingRateLimitedByCongestionWindowInTrial) { +TEST(GoogCcScenario, PaddingRateLimitedByCongestionWindowInTrial) { ScopedFieldTrials trial( "WebRTC-CongestionWindow/QueueSize:200,MinBitrate:30000/"); @@ -447,7 +513,7 @@ TEST_F(GoogCcNetworkControllerTest, EXPECT_NEAR(client->padding_rate().kbps(), client->target_rate().kbps(), 1); } -TEST_F(GoogCcNetworkControllerTest, LimitsToFloorIfRttIsHighInTrial) { +TEST(GoogCcScenario, LimitsToFloorIfRttIsHighInTrial) { // The field trial limits maximum RTT to 2 seconds, higher RTT means that the // controller backs off until it reaches the minimum configured bitrate. This // allows the RTT to recover faster than the regular control mechanism would @@ -484,11 +550,11 @@ TEST_F(GoogCcNetworkControllerTest, LimitsToFloorIfRttIsHighInTrial) { EXPECT_NEAR(client->target_rate().kbps(), kBandwidthFloor.kbps(), 5); } -TEST_F(GoogCcNetworkControllerTest, UpdatesTargetRateBasedOnLinkCapacity) { +TEST(GoogCcScenario, UpdatesTargetRateBasedOnLinkCapacity) { UpdatesTargetRateBasedOnLinkCapacity(); } -TEST_F(GoogCcNetworkControllerTest, StableEstimateDoesNotVaryInSteadyState) { +TEST(GoogCcScenario, StableEstimateDoesNotVaryInSteadyState) { auto factory = CreateFeedbackOnlyFactory(); Scenario s("googcc_unit/stable_target", false); CallClientConfig config; @@ -525,15 +591,13 @@ TEST_F(GoogCcNetworkControllerTest, StableEstimateDoesNotVaryInSteadyState) { EXPECT_GE(min_stable_target / max_stable_target, min_target / max_target); } -TEST_F(GoogCcNetworkControllerTest, - LossBasedControlUpdatesTargetRateBasedOnLinkCapacity) { +TEST(GoogCcScenario, LossBasedControlUpdatesTargetRateBasedOnLinkCapacity) { ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); // TODO(srte): Should the behavior be unaffected at low loss rates? UpdatesTargetRateBasedOnLinkCapacity("_loss_based"); } -TEST_F(GoogCcNetworkControllerTest, - LossBasedControlDoesModestBackoffToHighLoss) { +TEST(GoogCcScenario, LossBasedControlDoesModestBackoffToHighLoss) { ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/high_loss_channel", false); CallClientConfig config; @@ -600,8 +664,7 @@ DataRate AverageBitrateAfterCrossInducedLoss(std::string name) { s.TimeSinceStart(); } -TEST_F(GoogCcNetworkControllerTest, - LossBasedRecoversFasterAfterCrossInducedLoss) { +TEST(GoogCcScenario, LossBasedRecoversFasterAfterCrossInducedLoss) { // This test acts as a reference for the test below, showing that without the // trial, we have worse behavior. DataRate average_bitrate_without_loss_based = @@ -610,7 +673,6 @@ TEST_F(GoogCcNetworkControllerTest, // We recover bitrate better when subject to loss spikes from cross traffic // when loss based controller is used. ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); - SetUp(); DataRate average_bitrate_with_loss_based = AverageBitrateAfterCrossInducedLoss("googcc_unit/cross_loss_based"); @@ -618,7 +680,7 @@ TEST_F(GoogCcNetworkControllerTest, average_bitrate_without_loss_based * 1.1); } -TEST_F(GoogCcNetworkControllerTest, LossBasedEstimatorCapsRateAtModerateLoss) { +TEST(GoogCcScenario, LossBasedEstimatorCapsRateAtModerateLoss) { ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/moderate_loss_channel", false); CallClientConfig config; @@ -651,7 +713,7 @@ TEST_F(GoogCcNetworkControllerTest, LossBasedEstimatorCapsRateAtModerateLoss) { EXPECT_LT(client->target_rate().kbps(), 2500); } -TEST_F(GoogCcNetworkControllerTest, MaintainsLowRateInSafeResetTrial) { +TEST(GoogCcScenario, MaintainsLowRateInSafeResetTrial) { const DataRate kLinkCapacity = DataRate::KilobitsPerSec(200); const DataRate kStartRate = DataRate::KilobitsPerSec(300); @@ -678,7 +740,7 @@ TEST_F(GoogCcNetworkControllerTest, MaintainsLowRateInSafeResetTrial) { EXPECT_NEAR(client->send_bandwidth().kbps(), kLinkCapacity.kbps(), 50); } -TEST_F(GoogCcNetworkControllerTest, CutsHighRateInSafeResetTrial) { +TEST(GoogCcScenario, CutsHighRateInSafeResetTrial) { const DataRate kLinkCapacity = DataRate::KilobitsPerSec(1000); const DataRate kStartRate = DataRate::KilobitsPerSec(300); @@ -705,7 +767,7 @@ TEST_F(GoogCcNetworkControllerTest, CutsHighRateInSafeResetTrial) { EXPECT_NEAR(client->send_bandwidth().kbps(), kStartRate.kbps(), 30); } -TEST_F(GoogCcNetworkControllerTest, DetectsHighRateInSafeResetTrial) { +TEST(GoogCcScenario, DetectsHighRateInSafeResetTrial) { ScopedFieldTrials trial( "WebRTC-Bwe-SafeResetOnRouteChange/Enabled,ack/" "WebRTC-SendSideBwe-WithOverhead/Enabled/"); @@ -745,8 +807,7 @@ TEST_F(GoogCcNetworkControllerTest, DetectsHighRateInSafeResetTrial) { EXPECT_GT(client->send_bandwidth().kbps(), kNewLinkCapacity.kbps() - 300); } -TEST_F(GoogCcNetworkControllerTest, - TargetRateReducedOnPacingBufferBuildupInTrial) { +TEST(GoogCcScenario, TargetRateReducedOnPacingBufferBuildupInTrial) { // Configure strict pacing to ensure build-up. ScopedFieldTrials trial( "WebRTC-CongestionWindow/QueueSize:100,MinBitrate:30000/" @@ -775,7 +836,7 @@ TEST_F(GoogCcNetworkControllerTest, EXPECT_LT(client->GetStats().pacer_delay_ms, 150); } -TEST_F(GoogCcNetworkControllerTest, NoBandwidthTogglingInLossControlTrial) { +TEST(GoogCcScenario, NoBandwidthTogglingInLossControlTrial) { ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/no_toggling"); auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) { @@ -809,7 +870,7 @@ TEST_F(GoogCcNetworkControllerTest, NoBandwidthTogglingInLossControlTrial) { } } -TEST_F(GoogCcNetworkControllerTest, NoRttBackoffCollapseWhenVideoStops) { +TEST(GoogCcScenario, NoRttBackoffCollapseWhenVideoStops) { ScopedFieldTrials trial("WebRTC-Bwe-MaxRttLimit/limit:2s/"); Scenario s("googcc_unit/rttbackoff_video_stop"); auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) { @@ -831,7 +892,7 @@ TEST_F(GoogCcNetworkControllerTest, NoRttBackoffCollapseWhenVideoStops) { EXPECT_GT(client->send_bandwidth().kbps(), 1000); } -TEST_F(GoogCcNetworkControllerTest, NoCrashOnVeryLateFeedback) { +TEST(GoogCcScenario, NoCrashOnVeryLateFeedback) { Scenario s; auto ret_net = s.CreateMutableSimulationNode(NetworkSimulationConfig()); auto* route = s.CreateRoutes( @@ -855,7 +916,7 @@ TEST_F(GoogCcNetworkControllerTest, NoCrashOnVeryLateFeedback) { s.RunFor(TimeDelta::Seconds(2)); } -TEST_F(GoogCcNetworkControllerTest, IsFairToTCP) { +TEST(GoogCcScenario, IsFairToTCP) { Scenario s("googcc_unit/tcp_fairness"); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::KilobitsPerSec(1000); @@ -879,16 +940,16 @@ TEST_F(GoogCcNetworkControllerTest, IsFairToTCP) { EXPECT_LT(client->send_bandwidth().kbps(), 750); } -TEST(GoogCcScenario, FastRampupOnRembCapLiftedWithFieldTrial) { - ScopedFieldTrials trial("WebRTC-Bwe-ReceiverLimitCapsOnly/Enabled/"); +TEST(GoogCcScenario, FastRampupOnRembCapLifted) { DataRate final_estimate = - RunRembDipScenario("googcc_unit/fast_rampup_on_remb_cap_lifted"); + RunRembDipScenario("googcc_unit/default_fast_rampup_on_remb_cap_lifted"); EXPECT_GT(final_estimate.kbps(), 1500); } -TEST(GoogCcScenario, SlowRampupOnRembCapLifted) { +TEST(GoogCcScenario, SlowRampupOnRembCapLiftedWithFieldTrial) { + ScopedFieldTrials trial("WebRTC-Bwe-ReceiverLimitCapsOnly/Disabled/"); DataRate final_estimate = - RunRembDipScenario("googcc_unit/default_slow_rampup_on_remb_cap_lifted"); + RunRembDipScenario("googcc_unit/legacy_slow_rampup_on_remb_cap_lifted"); EXPECT_LT(final_estimate.kbps(), 1000); } diff --git a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc index c7f53c62f2..33974dc900 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc @@ -36,7 +36,7 @@ double GetIncreaseFactor(const LossBasedControlConfig& config, TimeDelta rtt) { } auto rtt_range = config.increase_high_rtt.Get() - config.increase_low_rtt; if (rtt_range <= TimeDelta::Zero()) { - RTC_NOTREACHED(); // Only on misconfiguration. + RTC_DCHECK_NOTREACHED(); // Only on misconfiguration. return config.min_increase_factor; } auto rtt_offset = rtt - config.increase_low_rtt; @@ -57,7 +57,7 @@ DataRate BitrateFromLoss(double loss, DataRate loss_bandwidth_balance, double exponent) { if (exponent <= 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return DataRate::Infinity(); } if (loss < 1e-5) @@ -69,7 +69,7 @@ double ExponentialUpdate(TimeDelta window, TimeDelta interval) { // Use the convention that exponential window length (which is really // infinite) is the time it takes to dampen to 1/e. if (window <= TimeDelta::Zero()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 1.0f; } return 1.0f - exp(interval / window * -1.0); @@ -134,7 +134,7 @@ void LossBasedBandwidthEstimation::UpdateLossStatistics( const std::vector& packet_results, Timestamp at_time) { if (packet_results.empty()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } int loss_count = 0; diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc index 63202d879a..03822e1710 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -21,6 +21,7 @@ #include "absl/algorithm/container.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -89,18 +90,11 @@ double GetLossProbability(double inherent_loss, << ToString(loss_limited_bandwidth); } - // We approximate the loss model - // loss_probability = inherent_loss + (1 - inherent_loss) * - // max(0, sending_rate - bandwidth) / sending_rate - // by - // loss_probability = inherent_loss + - // max(0, sending_rate - bandwidth) / sending_rate - // as it allows for simpler calculations and makes little difference in - // practice. double loss_probability = inherent_loss; if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) && (sending_rate > loss_limited_bandwidth)) { - loss_probability += (sending_rate - loss_limited_bandwidth) / sending_rate; + loss_probability += (1 - inherent_loss) * + (sending_rate - loss_limited_bandwidth) / sending_rate; } return std::min(std::max(loss_probability, 1.0e-6), 1.0 - 1.0e-6); } @@ -124,7 +118,8 @@ LossBasedBweV2::LossBasedBweV2(const WebRtcKeyValueConfig* key_value_config) current_estimate_.inherent_loss = config_->initial_inherent_loss_estimate; observations_.resize(config_->observation_window_size); temporal_weights_.resize(config_->observation_window_size); - tcp_fairness_temporal_weights_.resize(config_->observation_window_size); + instant_upper_bound_temporal_weights_.resize( + config_->observation_window_size); CalculateTemporalWeights(); } @@ -137,7 +132,8 @@ bool LossBasedBweV2::IsReady() const { num_observations_ > 0; } -DataRate LossBasedBweV2::GetBandwidthEstimate() const { +DataRate LossBasedBweV2::GetBandwidthEstimate( + DataRate delay_based_limit) const { if (!IsReady()) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) @@ -155,8 +151,14 @@ DataRate LossBasedBweV2::GetBandwidthEstimate() const { return DataRate::PlusInfinity(); } - return std::min(current_estimate_.loss_limited_bandwidth, - GetTcpFairnessBandwidthUpperBound()); + if (delay_based_limit.IsFinite()) { + return std::min({current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound(), + delay_based_limit * config_->delay_based_limit_factor}); + } else { + return std::min(current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound()); + } } void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { @@ -179,7 +181,8 @@ void LossBasedBweV2::SetBandwidthEstimate(DataRate bandwidth_estimate) { void LossBasedBweV2::UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate) { + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) << "The estimator must be enabled before it can be used."; @@ -191,7 +194,7 @@ void LossBasedBweV2::UpdateBandwidthEstimate( return; } - if (!PushBackObservation(packet_results)) { + if (!PushBackObservation(packet_results, delay_detector_state)) { return; } @@ -212,7 +215,10 @@ void LossBasedBweV2::UpdateBandwidthEstimate( best_candidate = candidate; } } - + if (best_candidate.loss_limited_bandwidth < + current_estimate_.loss_limited_bandwidth) { + last_time_estimate_reduced_ = last_send_time_most_recent_observation_; + } current_estimate_ = best_candidate; } @@ -223,6 +229,10 @@ absl::optional LossBasedBweV2::CreateConfig( FieldTrialParameter enabled("Enabled", false); FieldTrialParameter bandwidth_rampup_upper_bound_factor( "BwRampupUpperBoundFactor", 1.1); + FieldTrialParameter rampup_acceleration_max_factor( + "BwRampupAccelMaxFactor", 0.0); + FieldTrialParameter rampup_acceleration_maxout_time( + "BwRampupAccelMaxoutTime", TimeDelta::Seconds(60)); FieldTrialList candidate_factors("CandidateFactors", {1.05, 1.0, 0.95}); FieldTrialParameter higher_bandwidth_bias_factor("HigherBwBiasFactor", @@ -248,18 +258,29 @@ absl::optional LossBasedBweV2::CreateConfig( FieldTrialParameter observation_window_size("ObservationWindowSize", 20); FieldTrialParameter sending_rate_smoothing_factor( "SendingRateSmoothingFactor", 0.0); - FieldTrialParameter tcp_fairness_temporal_weight_factor( - "TcpFairnessTemporalWeightFactor", 0.99); - FieldTrialParameter tcp_fairness_upper_bound_bandwidth_balance( - "TcpFairnessUpperBoundBwBalance", DataRate::KilobitsPerSec(15.0)); - FieldTrialParameter tcp_fairness_upper_bound_loss_offset( - "TcpFairnessUpperBoundLossOffset", 0.05); + FieldTrialParameter instant_upper_bound_temporal_weight_factor( + "InstantUpperBoundTemporalWeightFactor", 0.99); + FieldTrialParameter instant_upper_bound_bandwidth_balance( + "InstantUpperBoundBwBalance", DataRate::KilobitsPerSec(15.0)); + FieldTrialParameter instant_upper_bound_loss_offset( + "InstantUpperBoundLossOffset", 0.05); FieldTrialParameter temporal_weight_factor("TemporalWeightFactor", 0.99); + FieldTrialParameter bandwidth_backoff_lower_bound_factor( + "BwBackoffLowerBoundFactor", 1.0); + FieldTrialParameter trendline_integration_enabled( + "TrendlineIntegrationEnabled", false); + FieldTrialParameter delay_based_limit_factor("DelayBasedLimitFactor", + 1.0); + FieldTrialParameter trendline_window_size("TrendlineWindowSize", 20); + FieldTrialParameter backoff_when_overusing("BackoffWhenOverusing", + false); if (key_value_config) { ParseFieldTrial({&enabled, &bandwidth_rampup_upper_bound_factor, + &rampup_acceleration_max_factor, + &rampup_acceleration_maxout_time, &candidate_factors, &higher_bandwidth_bias_factor, &higher_log_bandwidth_bias_factor, @@ -274,10 +295,15 @@ absl::optional LossBasedBweV2::CreateConfig( &observation_duration_lower_bound, &observation_window_size, &sending_rate_smoothing_factor, - &tcp_fairness_temporal_weight_factor, - &tcp_fairness_upper_bound_bandwidth_balance, - &tcp_fairness_upper_bound_loss_offset, - &temporal_weight_factor}, + &instant_upper_bound_temporal_weight_factor, + &instant_upper_bound_bandwidth_balance, + &instant_upper_bound_loss_offset, + &temporal_weight_factor, + &bandwidth_backoff_lower_bound_factor, + &trendline_integration_enabled, + &delay_based_limit_factor, + &trendline_window_size, + &backoff_when_overusing}, key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); } @@ -288,6 +314,9 @@ absl::optional LossBasedBweV2::CreateConfig( config.emplace(); config->bandwidth_rampup_upper_bound_factor = bandwidth_rampup_upper_bound_factor.Get(); + config->rampup_acceleration_max_factor = rampup_acceleration_max_factor.Get(); + config->rampup_acceleration_maxout_time = + rampup_acceleration_maxout_time.Get(); config->candidate_factors = candidate_factors.Get(); config->higher_bandwidth_bias_factor = higher_bandwidth_bias_factor.Get(); config->higher_log_bandwidth_bias_factor = @@ -308,13 +337,19 @@ absl::optional LossBasedBweV2::CreateConfig( observation_duration_lower_bound.Get(); config->observation_window_size = observation_window_size.Get(); config->sending_rate_smoothing_factor = sending_rate_smoothing_factor.Get(); - config->tcp_fairness_temporal_weight_factor = - tcp_fairness_temporal_weight_factor.Get(); - config->tcp_fairness_upper_bound_bandwidth_balance = - tcp_fairness_upper_bound_bandwidth_balance.Get(); - config->tcp_fairness_upper_bound_loss_offset = - tcp_fairness_upper_bound_loss_offset.Get(); + config->instant_upper_bound_temporal_weight_factor = + instant_upper_bound_temporal_weight_factor.Get(); + config->instant_upper_bound_bandwidth_balance = + instant_upper_bound_bandwidth_balance.Get(); + config->instant_upper_bound_loss_offset = + instant_upper_bound_loss_offset.Get(); config->temporal_weight_factor = temporal_weight_factor.Get(); + config->bandwidth_backoff_lower_bound_factor = + bandwidth_backoff_lower_bound_factor.Get(); + config->trendline_integration_enabled = trendline_integration_enabled.Get(); + config->delay_based_limit_factor = delay_based_limit_factor.Get(); + config->trendline_window_size = trendline_window_size.Get(); + config->backoff_when_overusing = backoff_when_overusing.Get(); return config; } @@ -331,6 +366,40 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->bandwidth_rampup_upper_bound_factor; valid = false; } + if (config_->rampup_acceleration_max_factor < 0.0) { + RTC_LOG(LS_WARNING) + << "The rampup acceleration max factor must be non-negative.: " + << config_->rampup_acceleration_max_factor; + valid = false; + } + if (config_->rampup_acceleration_maxout_time <= TimeDelta::Zero()) { + RTC_LOG(LS_WARNING) + << "The rampup acceleration maxout time must be above zero: " + << config_->rampup_acceleration_maxout_time.seconds(); + valid = false; + } + for (double candidate_factor : config_->candidate_factors) { + if (candidate_factor <= 0.0) { + RTC_LOG(LS_WARNING) << "All candidate factors must be greater than zero: " + << candidate_factor; + valid = false; + } + } + + // Ensure that the configuration allows generation of at least one candidate + // other than the current estimate. + if (!config_->append_acknowledged_rate_candidate && + !config_->append_delay_based_estimate_candidate && + !absl::c_any_of(config_->candidate_factors, + [](double cf) { return cf != 1.0; })) { + RTC_LOG(LS_WARNING) + << "The configuration does not allow generating candidates. Specify " + "a candidate factor other than 1.0, allow the acknowledged rate " + "to be a candidate, and/or allow the delay based estimate to be a " + "candidate."; + valid = false; + } + if (config_->higher_bandwidth_bias_factor < 0.0) { RTC_LOG(LS_WARNING) << "The higher bandwidth bias factor must be non-negative: " @@ -397,24 +466,24 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->sending_rate_smoothing_factor; valid = false; } - if (config_->tcp_fairness_temporal_weight_factor <= 0.0 || - config_->tcp_fairness_temporal_weight_factor > 1.0) { + if (config_->instant_upper_bound_temporal_weight_factor <= 0.0 || + config_->instant_upper_bound_temporal_weight_factor > 1.0) { RTC_LOG(LS_WARNING) - << "The TCP fairness temporal weight factor must be in (0, 1]" - << config_->tcp_fairness_temporal_weight_factor; + << "The instant upper bound temporal weight factor must be in (0, 1]" + << config_->instant_upper_bound_temporal_weight_factor; valid = false; } - if (config_->tcp_fairness_upper_bound_bandwidth_balance <= DataRate::Zero()) { + if (config_->instant_upper_bound_bandwidth_balance <= DataRate::Zero()) { RTC_LOG(LS_WARNING) - << "The TCP fairness upper bound bandwidth balance must be positive: " - << ToString(config_->tcp_fairness_upper_bound_bandwidth_balance); + << "The instant upper bound bandwidth balance must be positive: " + << ToString(config_->instant_upper_bound_bandwidth_balance); valid = false; } - if (config_->tcp_fairness_upper_bound_loss_offset < 0.0 || - config_->tcp_fairness_upper_bound_loss_offset >= 1.0) { + if (config_->instant_upper_bound_loss_offset < 0.0 || + config_->instant_upper_bound_loss_offset >= 1.0) { RTC_LOG(LS_WARNING) - << "The TCP fairness upper bound loss offset must be in [0, 1): " - << config_->tcp_fairness_upper_bound_loss_offset; + << "The instant upper bound loss offset must be in [0, 1): " + << config_->instant_upper_bound_loss_offset; valid = false; } if (config_->temporal_weight_factor <= 0.0 || @@ -423,7 +492,24 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->temporal_weight_factor; valid = false; } - + if (config_->bandwidth_backoff_lower_bound_factor > 1.0) { + RTC_LOG(LS_WARNING) + << "The bandwidth backoff lower bound factor must not be greater than " + "1: " + << config_->bandwidth_backoff_lower_bound_factor; + valid = false; + } + if (config_->delay_based_limit_factor < 1.0) { + RTC_LOG(LS_WARNING) + << "The delay based limit factor must not be less than 1: " + << config_->delay_based_limit_factor; + valid = false; + } + if (config_->trendline_window_size < 2) { + RTC_LOG(LS_WARNING) << "The trendline window size must be at least 2: " + << config_->trendline_window_size; + valid = false; + } return valid; } @@ -439,40 +525,74 @@ double LossBasedBweV2::GetAverageReportedLossRatio() const { continue; } - double tcp_fairness_temporal_weight = - tcp_fairness_temporal_weights_[(num_observations_ - 1) - - observation.id]; - num_packets += tcp_fairness_temporal_weight * observation.num_packets; - num_lost_packets += - tcp_fairness_temporal_weight * observation.num_lost_packets; + double instant_temporal_weight = + instant_upper_bound_temporal_weights_[(num_observations_ - 1) - + observation.id]; + num_packets += instant_temporal_weight * observation.num_packets; + num_lost_packets += instant_temporal_weight * observation.num_lost_packets; } return static_cast(num_lost_packets) / num_packets; } +DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { + if (!acknowledged_bitrate_.has_value()) + return DataRate::PlusInfinity(); + + DataRate candidate_bandwidth_upper_bound = + config_->bandwidth_rampup_upper_bound_factor * (*acknowledged_bitrate_); + + if (config_->rampup_acceleration_max_factor > 0.0) { + const TimeDelta time_since_bandwidth_reduced = std::min( + config_->rampup_acceleration_maxout_time, + std::max(TimeDelta::Zero(), last_send_time_most_recent_observation_ - + last_time_estimate_reduced_)); + const double rampup_acceleration = config_->rampup_acceleration_max_factor * + time_since_bandwidth_reduced / + config_->rampup_acceleration_maxout_time; + + candidate_bandwidth_upper_bound += + rampup_acceleration * (*acknowledged_bitrate_); + } + return candidate_bandwidth_upper_bound; +} + std::vector LossBasedBweV2::GetCandidates( DataRate delay_based_estimate) const { std::vector bandwidths; + bool can_increase_bitrate = TrendlineEsimateAllowBitrateIncrease(); + bool can_decrease_bitrate = TrendlineEsimateAllowBitrateDecrease(); for (double candidate_factor : config_->candidate_factors) { + if (!can_increase_bitrate && candidate_factor >= 1.0) { + // When the network is overusing, the estimate is forced to decrease + // even if there is no loss yet. + if (candidate_factor > 1 || config_->backoff_when_overusing) { + continue; + } + } + if (!can_decrease_bitrate && candidate_factor < 1.0) { + continue; + } bandwidths.push_back(candidate_factor * current_estimate_.loss_limited_bandwidth); } if (acknowledged_bitrate_.has_value() && - config_->append_acknowledged_rate_candidate) { - bandwidths.push_back(*acknowledged_bitrate_); + config_->append_acknowledged_rate_candidate && can_decrease_bitrate) { + bandwidths.push_back(*acknowledged_bitrate_ * + config_->bandwidth_backoff_lower_bound_factor); } if (IsValid(delay_based_estimate) && config_->append_delay_based_estimate_candidate) { - bandwidths.push_back(delay_based_estimate); + if (can_increase_bitrate && + delay_based_estimate > current_estimate_.loss_limited_bandwidth) { + bandwidths.push_back(delay_based_estimate); + } } const DataRate candidate_bandwidth_upper_bound = - acknowledged_bitrate_.has_value() - ? config_->bandwidth_rampup_upper_bound_factor * - (*acknowledged_bitrate_) - : DataRate::PlusInfinity(); + GetCandidateBandwidthUpperBound(); std::vector candidates; candidates.resize(bandwidths.size()); @@ -601,27 +721,26 @@ DataRate LossBasedBweV2::GetSendingRate( instantaneous_sending_rate; } -DataRate LossBasedBweV2::GetTcpFairnessBandwidthUpperBound() const { - return cached_tcp_fairness_limit_.value_or(DataRate::PlusInfinity()); +DataRate LossBasedBweV2::GetInstantUpperBound() const { + return cached_instant_upper_bound_.value_or(DataRate::PlusInfinity()); } -void LossBasedBweV2::CalculateTcpFairnessBandwidthUpperBound() { - DataRate tcp_fairness_limit = DataRate::PlusInfinity(); +void LossBasedBweV2::CalculateInstantUpperBound() { + DataRate instant_limit = DataRate::PlusInfinity(); const double average_reported_loss_ratio = GetAverageReportedLossRatio(); - if (average_reported_loss_ratio > - config_->tcp_fairness_upper_bound_loss_offset) { - tcp_fairness_limit = config_->tcp_fairness_upper_bound_bandwidth_balance / - (average_reported_loss_ratio - - config_->tcp_fairness_upper_bound_loss_offset); + if (average_reported_loss_ratio > config_->instant_upper_bound_loss_offset) { + instant_limit = config_->instant_upper_bound_bandwidth_balance / + (average_reported_loss_ratio - + config_->instant_upper_bound_loss_offset); } - cached_tcp_fairness_limit_ = tcp_fairness_limit; + cached_instant_upper_bound_ = instant_limit; } void LossBasedBweV2::CalculateTemporalWeights() { for (int i = 0; i < config_->observation_window_size; ++i) { temporal_weights_[i] = std::pow(config_->temporal_weight_factor, i); - tcp_fairness_temporal_weights_[i] = - std::pow(config_->tcp_fairness_temporal_weight_factor, i); + instant_upper_bound_temporal_weights_[i] = + std::pow(config_->instant_upper_bound_temporal_weight_factor, i); } } @@ -640,8 +759,47 @@ void LossBasedBweV2::NewtonsMethodUpdate( } } +bool LossBasedBweV2::TrendlineEsimateAllowBitrateDecrease() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing) { + return true; + } + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwUnderusing) { + return false; + } + } + return true; +} + +bool LossBasedBweV2::TrendlineEsimateAllowBitrateIncrease() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing) { + return false; + } + } + return true; +} + bool LossBasedBweV2::PushBackObservation( - rtc::ArrayView packet_results) { + rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state) { + delay_detector_states_.push_front(delay_detector_state); + if (static_cast(delay_detector_states_.size()) > + config_->trendline_window_size) { + delay_detector_states_.pop_back(); + } + if (packet_results.empty()) { return false; } @@ -665,7 +823,9 @@ bool LossBasedBweV2::PushBackObservation( last_send_time - last_send_time_most_recent_observation_; // Too small to be meaningful. - if (observation_duration < config_->observation_duration_lower_bound) { + if (observation_duration < config_->observation_duration_lower_bound && + (delay_detector_state == BandwidthUsage::kBwNormal || + !config_->trendline_integration_enabled)) { return false; } @@ -684,7 +844,7 @@ bool LossBasedBweV2::PushBackObservation( partial_observation_ = PartialObservation(); - CalculateTcpFairnessBandwidthUpperBound(); + CalculateInstantUpperBound(); return true; } diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h index 10fb33f537..7e42b3e827 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -12,10 +12,12 @@ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ #include +#include #include #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -42,14 +44,15 @@ class LossBasedBweV2 { bool IsReady() const; // Returns `DataRate::PlusInfinity` if no BWE can be calculated. - DataRate GetBandwidthEstimate() const; + DataRate GetBandwidthEstimate(DataRate delay_based_limit) const; void SetAcknowledgedBitrate(DataRate acknowledged_bitrate); void SetBandwidthEstimate(DataRate bandwidth_estimate); void UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate); + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state); private: struct ChannelParameters { @@ -59,6 +62,8 @@ class LossBasedBweV2 { struct Config { double bandwidth_rampup_upper_bound_factor = 0.0; + double rampup_acceleration_max_factor = 0.0; + TimeDelta rampup_acceleration_maxout_time = TimeDelta::Zero(); std::vector candidate_factors; double higher_bandwidth_bias_factor = 0.0; double higher_log_bandwidth_bias_factor = 0.0; @@ -74,11 +79,15 @@ class LossBasedBweV2 { TimeDelta observation_duration_lower_bound = TimeDelta::Zero(); int observation_window_size = 0; double sending_rate_smoothing_factor = 0.0; - double tcp_fairness_temporal_weight_factor = 0.0; - DataRate tcp_fairness_upper_bound_bandwidth_balance = - DataRate::MinusInfinity(); - double tcp_fairness_upper_bound_loss_offset = 0.0; + double instant_upper_bound_temporal_weight_factor = 0.0; + DataRate instant_upper_bound_bandwidth_balance = DataRate::MinusInfinity(); + double instant_upper_bound_loss_offset = 0.0; double temporal_weight_factor = 0.0; + double bandwidth_backoff_lower_bound_factor = 0.0; + bool trendline_integration_enabled = false; + double delay_based_limit_factor = 1.0; + int trendline_window_size = 0; + bool backoff_when_overusing = false; }; struct Derivatives { @@ -110,6 +119,7 @@ class LossBasedBweV2 { double GetAverageReportedLossRatio() const; std::vector GetCandidates( DataRate delay_based_estimate) const; + DataRate GetCandidateBandwidthUpperBound() const; Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const; double GetFeasibleInherentLoss( const ChannelParameters& channel_parameters) const; @@ -117,14 +127,26 @@ class LossBasedBweV2 { double GetHighBandwidthBias(DataRate bandwidth) const; double GetObjective(const ChannelParameters& channel_parameters) const; DataRate GetSendingRate(DataRate instantaneous_sending_rate) const; - DataRate GetTcpFairnessBandwidthUpperBound() const; - void CalculateTcpFairnessBandwidthUpperBound(); + DataRate GetInstantUpperBound() const; + void CalculateInstantUpperBound(); void CalculateTemporalWeights(); void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; + // Returns true if either + // 1. At least one of states in the window is kBwOverusing, or + // 2. There are no kBwUnderusing states in the window. + bool TrendlineEsimateAllowBitrateDecrease() const; + + // Returns false if there exists an overusing state in the window. + bool TrendlineEsimateAllowBitrateIncrease() const; // Returns false if no observation was created. - bool PushBackObservation(rtc::ArrayView packet_results); + bool PushBackObservation(rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state); + void UpdateTrendlineEstimator( + const std::vector& packet_feedbacks, + Timestamp at_time); + void UpdateDelayDetector(BandwidthUsage delay_detector_state); absl::optional acknowledged_bitrate_; absl::optional config_; @@ -133,9 +155,11 @@ class LossBasedBweV2 { std::vector observations_; PartialObservation partial_observation_; Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity(); - absl::optional cached_tcp_fairness_limit_; - std::vector tcp_fairness_temporal_weights_; + Timestamp last_time_estimate_reduced_ = Timestamp::MinusInfinity(); + absl::optional cached_instant_upper_bound_; + std::vector instant_upper_bound_temporal_weights_; std::vector temporal_weights_; + std::deque delay_detector_states_; }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc index bb71668d8b..bf0a7e492e 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc @@ -12,6 +12,7 @@ #include +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -25,6 +26,8 @@ namespace webrtc { namespace { +using ::webrtc::test::ExplicitKeyValueConfig; + constexpr TimeDelta kObservationDurationLowerBound = TimeDelta::Millis(200); std::string Config(bool enabled, bool valid) { @@ -46,13 +49,16 @@ std::string Config(bool enabled, bool valid) { } config_string - << ",CandidateFactors:0.9|1.1,HigherBwBiasFactor:0.01," + << ",CandidateFactors:1.1|1.0|0.95,HigherBwBiasFactor:0.01," + "DelayBasedCandidate:true," "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:14kbps," "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01," "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15," - "SendingRateSmoothingFactor:0.01,TcpFairnessTemporalWeightFactor:0.97," - "TcpFairnessUpperBoundBwBalance:90kbps," - "TcpFairnessUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98"; + "SendingRateSmoothingFactor:0.01," + "InstantUpperBoundTemporalWeightFactor:0.97," + "InstantUpperBoundBwBalance:90kbps," + "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98," + "BackoffWhenOverusing:true"; config_string.AppendFormat( ",ObservationDurationLowerBound:%dms", @@ -64,7 +70,7 @@ std::string Config(bool enabled, bool valid) { } TEST(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) { - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); @@ -72,7 +78,7 @@ TEST(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) { } TEST(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) { - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/false, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); @@ -80,13 +86,37 @@ TEST(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) { } TEST(LossBasedBweV2Test, DisabledWhenGivenNonValidConfigurationValues) { - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/false)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); } +TEST(LossBasedBweV2Test, DisabledWhenGivenNonPositiveCandidateFactor) { + ExplicitKeyValueConfig key_value_config_negative_candidate_factor( + "WebRTC-Bwe-LossBasedBweV2/Enabled:true,CandidateFactors:-1.3|1.1/"); + LossBasedBweV2 loss_based_bandwidth_estimator_1( + &key_value_config_negative_candidate_factor); + EXPECT_FALSE(loss_based_bandwidth_estimator_1.IsEnabled()); + + ExplicitKeyValueConfig key_value_config_zero_candidate_factor( + "WebRTC-Bwe-LossBasedBweV2/Enabled:true,CandidateFactors:0.0|1.1/"); + LossBasedBweV2 loss_based_bandwidth_estimator_2( + &key_value_config_zero_candidate_factor); + EXPECT_FALSE(loss_based_bandwidth_estimator_2.IsEnabled()); +} + +TEST(LossBasedBweV2Test, + DisabledWhenGivenConfigurationThatDoesNotAllowGeneratingCandidates) { + ExplicitKeyValueConfig key_value_config( + "WebRTC-Bwe-LossBasedBweV2/" + "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false," + "DelayBasedCandidate:false/"); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); +} + TEST(LossBasedBweV2Test, BandwidthEstimateGivenInitializationAndThenFeedback) { PacketResult enough_feedback[2]; enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); @@ -99,17 +129,20 @@ TEST(LossBasedBweV2Test, BandwidthEstimateGivenInitializationAndThenFeedback) { enough_feedback[1].receive_time = Timestamp::Zero() + 2 * kObservationDurationLowerBound; - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_TRUE(loss_based_bandwidth_estimator.IsReady()); - EXPECT_TRUE(loss_based_bandwidth_estimator.GetBandwidthEstimate().IsFinite()); + EXPECT_TRUE( + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsFinite()); } TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { @@ -124,16 +157,18 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { enough_feedback[1].receive_time = Timestamp::Zero() + 2 * kObservationDurationLowerBound; - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { @@ -150,7 +185,7 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { not_enough_feedback[1].receive_time = Timestamp::Zero() + kObservationDurationLowerBound; - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); @@ -159,14 +194,18 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - not_enough_feedback, DataRate::PlusInfinity()); + not_enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } TEST(LossBasedBweV2Test, @@ -193,28 +232,31 @@ TEST(LossBasedBweV2Test, enough_feedback_2[1].receive_time = Timestamp::Zero() + 4 * kObservationDurationLowerBound; - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); } @@ -242,7 +284,7 @@ TEST(LossBasedBweV2Test, enough_feedback_2[1].receive_time = Timestamp::Zero() + 4 * kObservationDurationLowerBound; - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator_1(&key_value_config); LossBasedBweV2 loss_based_bandwidth_estimator_2(&key_value_config); @@ -252,26 +294,30 @@ TEST(LossBasedBweV2Test, loss_based_bandwidth_estimator_2.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.SetAcknowledgedBitrate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), - loss_based_bandwidth_estimator_2.GetBandwidthEstimate()); + EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + loss_based_bandwidth_estimator_2.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity())); } TEST(LossBasedBweV2Test, @@ -290,19 +336,194 @@ TEST(LossBasedBweV2Test, enough_feedback_no_received_packets[1].receive_time = Timestamp::PlusInfinity(); - test::ExplicitKeyValueConfig key_value_config( + ExplicitKeyValueConfig key_value_config( Config(/*enabled=*/true, /*valid=*/true)); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_no_received_packets, DataRate::PlusInfinity()); + enough_feedback_no_received_packets, DataRate::PlusInfinity(), + BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(100)); } +// When network is overusing and flag `BackoffWhenOverusing` is true, +// the bandwidth estimate is forced to decrease even if there is no loss yet. +TEST(LossBasedBweV2Test, BandwidthEstimateDecreasesWhenOverusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = Timestamp::PlusInfinity(); + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = Timestamp::PlusInfinity(); + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwOverusing); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); +} + +TEST(LossBasedBweV2Test, BandwidthEstimateIncreasesWhenUnderusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); +} + +TEST(LossBasedBweV2Test, + BandwidthEstimateCappedByDelayBasedEstimateWhenUnderusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::KilobitsPerSec(500)), + DataRate::KilobitsPerSec(500)); +} + +TEST(LossBasedBweV2Test, NotUseAckedBitrateInNormalState) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + DataRate acked_bitrate = DataRate::KilobitsPerSec(300); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_bitrate); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + acked_bitrate); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + acked_bitrate); +} + } // namespace } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc index cb75456fde..df753ed0c9 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.cc +++ b/modules/congestion_controller/goog_cc/probe_controller.cc @@ -225,7 +225,7 @@ std::vector ProbeController::OnMaxTotalAllocatedBitrate( probes.push_back(second_probe_rate.bps()); } return InitiateProbing(at_time_ms, probes, - config_.allocation_allow_further_probing); + config_.allocation_allow_further_probing.Get()); } max_total_allocated_bitrate_ = max_total_allocated_bitrate; return std::vector(); diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h index 7f24ff98c8..d0f1458ece 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.h +++ b/modules/congestion_controller/goog_cc/probe_controller.h @@ -22,7 +22,6 @@ #include "api/transport/network_control.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -63,6 +62,9 @@ class ProbeController { RtcEventLog* event_log); ~ProbeController(); + ProbeController(const ProbeController&) = delete; + ProbeController& operator=(const ProbeController&) = delete; + ABSL_MUST_USE_RESULT std::vector SetBitrates( int64_t min_bitrate_bps, int64_t start_bitrate_bps, @@ -143,8 +145,6 @@ class ProbeController { int32_t next_probe_cluster_id_ = 1; ProbeControllerConfig config_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ProbeController); }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc index 8fb3275cc9..631ef86c65 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc @@ -17,6 +17,7 @@ #include #include "absl/strings/match.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/webrtc_key_value_config.h" @@ -229,7 +230,8 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation( bitrate_threshold_(kDefaultBitrateThreshold), loss_based_bandwidth_estimator_v1_(key_value_config), loss_based_bandwidth_estimator_v2_(key_value_config), - receiver_limit_caps_only_("Enabled") { + disable_receiver_limit_caps_only_("Disabled"), + delay_detector_state_(BandwidthUsage::kBwNormal) { RTC_DCHECK(event_log); if (BweLossExperimentIsEnabled()) { uint32_t bitrate_threshold_kbps; @@ -242,7 +244,7 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation( bitrate_threshold_ = DataRate::KilobitsPerSec(bitrate_threshold_kbps); } } - ParseFieldTrial({&receiver_limit_caps_only_}, + ParseFieldTrial({&disable_receiver_limit_caps_only_}, key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly")); } @@ -271,6 +273,7 @@ void SendSideBandwidthEstimation::OnRouteChange() { uma_update_state_ = kNoUpdate; uma_rtt_state_ = kNoUpdate; last_rtc_event_log_ = Timestamp::MinusInfinity(); + delay_detector_state_ = BandwidthUsage::kBwNormal; } void SendSideBandwidthEstimation::SetBitrates( @@ -313,7 +316,7 @@ int SendSideBandwidthEstimation::GetMinBitrate() const { DataRate SendSideBandwidthEstimation::target_rate() const { DataRate target = current_target_; - if (receiver_limit_caps_only_) + if (!disable_receiver_limit_caps_only_) target = std::min(target, receiver_limit_); return std::max(min_bitrate_configured_, target); } @@ -330,9 +333,12 @@ void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, ApplyTargetLimits(at_time); } -void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time, - DataRate bitrate) { +void SendSideBandwidthEstimation::UpdateDelayBasedEstimate( + Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state) { link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate); + delay_detector_state_ = delay_detector_state; // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no // limitation. delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate; @@ -356,7 +362,7 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( } } -void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( +void SendSideBandwidthEstimation::UpdateLossBasedEstimatorFromFeedbackVector( const TransportPacketsFeedback& report) { if (LossBasedBandwidthEstimatorV1Enabled()) { loss_based_bandwidth_estimator_v1_.UpdateLossStatistics( @@ -364,7 +370,7 @@ void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( } if (LossBasedBandwidthEstimatorV2Enabled()) { loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate( - report.packet_feedbacks, delay_based_limit_); + report.packet_feedbacks, delay_based_limit_, delay_detector_state_); } } @@ -509,8 +515,8 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { if (LossBasedBandwidthEstimatorV2ReadyForUse()) { DataRate new_bitrate = - loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate(); - new_bitrate = std::min(new_bitrate, delay_based_limit_); + loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate( + delay_based_limit_); UpdateTargetBitrate(new_bitrate, at_time); return; } @@ -609,7 +615,7 @@ void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) { DataRate SendSideBandwidthEstimation::GetUpperLimit() const { DataRate upper_limit = delay_based_limit_; - if (!receiver_limit_caps_only_) + if (disable_receiver_limit_caps_only_) upper_limit = std::min(upper_limit, receiver_limit_); return std::min(upper_limit, max_bitrate_configured_); } diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h index 63be1f03ef..bb53be873b 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h @@ -20,6 +20,7 @@ #include #include "absl/types/optional.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -97,7 +98,9 @@ class SendSideBandwidthEstimation { void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth); // Call when a new delay-based estimate is available. - void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate); + void UpdateDelayBasedEstimate(Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state); // Call when we receive a RTCP message with a ReceiveBlock. void UpdatePacketsLost(int64_t packets_lost, @@ -116,7 +119,8 @@ class SendSideBandwidthEstimation { int GetMinBitrate() const; void SetAcknowledgedRate(absl::optional acknowledged_rate, Timestamp at_time); - void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report); + void UpdateLossBasedEstimatorFromFeedbackVector( + const TransportPacketsFeedback& report); private: friend class GoogCcStatePrinter; @@ -198,7 +202,8 @@ class SendSideBandwidthEstimation { DataRate bitrate_threshold_; LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_; LossBasedBweV2 loss_based_bandwidth_estimator_v2_; - FieldTrialFlag receiver_limit_caps_only_; + FieldTrialFlag disable_receiver_limit_caps_only_; + BandwidthUsage delay_detector_state_; }; } // namespace webrtc #endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc index 85ce401098..e3db866cf7 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc @@ -10,6 +10,7 @@ #include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" #include "logging/rtc_event_log/mock/mock_rtc_event_log.h" @@ -54,7 +55,8 @@ void TestProbing(bool use_delay_based) { // Initial REMB applies immediately. if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kRembBps)); + DataRate::BitsPerSec(kRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kRembBps)); @@ -66,7 +68,8 @@ void TestProbing(bool use_delay_based) { now_ms += 2001; if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kSecondRembBps)); + DataRate::BitsPerSec(kSecondRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kSecondRembBps)); @@ -157,7 +160,8 @@ TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) { Timestamp::Millis(now_ms)); bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kDelayBasedBitrateBps)); + DataRate::BitsPerSec(kDelayBasedBitrateBps), + BandwidthUsage::kBwNormal); bwe.UpdateEstimate(Timestamp::Millis(now_ms)); EXPECT_GE(bwe.target_rate().bps(), kInitialBitrateBps); EXPECT_LE(bwe.target_rate().bps(), kDelayBasedBitrateBps); diff --git a/modules/congestion_controller/goog_cc/test/goog_cc_printer.h b/modules/congestion_controller/goog_cc/test/goog_cc_printer.h index 3eee7814cf..16fa657e71 100644 --- a/modules/congestion_controller/goog_cc/test/goog_cc_printer.h +++ b/modules/congestion_controller/goog_cc/test/goog_cc_printer.h @@ -61,7 +61,7 @@ class GoogCcDebugFactory : public GoogCcNetworkControllerFactory { std::unique_ptr Create( NetworkControllerConfig config) override; - void PrintState(const Timestamp at_time); + void PrintState(Timestamp at_time); void AttachWriter(std::unique_ptr log_writer); diff --git a/modules/congestion_controller/goog_cc/trendline_estimator.cc b/modules/congestion_controller/goog_cc/trendline_estimator.cc index 1008badf6a..7fdf66c518 100644 --- a/modules/congestion_controller/goog_cc/trendline_estimator.cc +++ b/modules/congestion_controller/goog_cc/trendline_estimator.cc @@ -44,7 +44,7 @@ size_t ReadTrendlineFilterWindowSize( if (parsed_values == 1) { if (window_size > 1) return window_size; - RTC_LOG(WARNING) << "Window size must be greater than 1."; + RTC_LOG(LS_WARNING) << "Window size must be greater than 1."; } RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweWindowSizeInPackets" " experiment from field trial string. Using default."; diff --git a/modules/congestion_controller/goog_cc/trendline_estimator.h b/modules/congestion_controller/goog_cc/trendline_estimator.h index 75b971d187..6fd442498b 100644 --- a/modules/congestion_controller/goog_cc/trendline_estimator.h +++ b/modules/congestion_controller/goog_cc/trendline_estimator.h @@ -20,7 +20,6 @@ #include "api/network_state_predictor.h" #include "api/transport/webrtc_key_value_config.h" #include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/experiments/struct_parameters_parser.h" namespace webrtc { @@ -57,6 +56,9 @@ class TrendlineEstimator : public DelayIncreaseDetectorInterface { ~TrendlineEstimator() override; + TrendlineEstimator(const TrendlineEstimator&) = delete; + TrendlineEstimator& operator=(const TrendlineEstimator&) = delete; + // Update the estimator with a new sample. The deltas should represent deltas // between timestamp groups as defined by the InterArrival class. void Update(double recv_delta_ms, @@ -118,8 +120,6 @@ class TrendlineEstimator : public DelayIncreaseDetectorInterface { BandwidthUsage hypothesis_; BandwidthUsage hypothesis_predicted_; NetworkStatePredictor* network_state_predictor_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TrendlineEstimator); }; } // namespace webrtc diff --git a/modules/congestion_controller/pcc/monitor_interval.cc b/modules/congestion_controller/pcc/monitor_interval.cc index 6bc9f4a7ef..de1e2d5e69 100644 --- a/modules/congestion_controller/pcc/monitor_interval.cc +++ b/modules/congestion_controller/pcc/monitor_interval.cc @@ -70,13 +70,10 @@ double PccMonitorInterval::ComputeDelayGradient( return 0; } double sum_times = 0; - double sum_delays = 0; for (const ReceivedPacket& packet : received_packets_) { double time_delta_us = (packet.sent_time - received_packets_[0].sent_time).us(); - double delay = packet.delay.us(); sum_times += time_delta_us; - sum_delays += delay; } double sum_squared_scaled_time_deltas = 0; double sum_scaled_time_delta_dot_delay = 0; diff --git a/modules/congestion_controller/receive_side_congestion_controller_unittest.cc b/modules/congestion_controller/receive_side_congestion_controller_unittest.cc index 2aade06cbc..f2fd6d11d7 100644 --- a/modules/congestion_controller/receive_side_congestion_controller_unittest.cc +++ b/modules/congestion_controller/receive_side_congestion_controller_unittest.cc @@ -81,7 +81,7 @@ TEST(ReceiveSideCongestionControllerTest, } TEST(ReceiveSideCongestionControllerTest, ConvergesToCapacity) { - Scenario s("recieve_cc_unit/converge"); + Scenario s("receive_cc_unit/converge"); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::KilobitsPerSec(1000); net_conf.delay = TimeDelta::Millis(50); @@ -100,7 +100,7 @@ TEST(ReceiveSideCongestionControllerTest, ConvergesToCapacity) { } TEST(ReceiveSideCongestionControllerTest, IsFairToTCP) { - Scenario s("recieve_cc_unit/tcp_fairness"); + Scenario s("receive_cc_unit/tcp_fairness"); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::KilobitsPerSec(1000); net_conf.delay = TimeDelta::Millis(50); diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn index 1a70447307..39d4d68192 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -61,6 +61,7 @@ rtc_library("transport_feedback") { "../../../rtc_base:rtc_base_approved", "../../../rtc_base/network:sent_packet", "../../../rtc_base/synchronization:mutex", + "../../../rtc_base/system:no_unique_address", "../../../system_wrappers", "../../../system_wrappers:field_trial", "../../rtp_rtcp:rtp_rtcp_format", diff --git a/modules/congestion_controller/rtp/control_handler.h b/modules/congestion_controller/rtp/control_handler.h index 1da6463219..16ffc32a44 100644 --- a/modules/congestion_controller/rtp/control_handler.h +++ b/modules/congestion_controller/rtp/control_handler.h @@ -19,7 +19,6 @@ #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "modules/pacing/paced_sender.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/no_unique_address.h" namespace webrtc { @@ -33,6 +32,9 @@ class CongestionControlHandler { CongestionControlHandler(); ~CongestionControlHandler(); + CongestionControlHandler(const CongestionControlHandler&) = delete; + CongestionControlHandler& operator=(const CongestionControlHandler&) = delete; + void SetTargetRate(TargetTransferRate new_target_rate); void SetNetworkAvailability(bool network_available); void SetPacerQueue(TimeDelta expected_queue_time); @@ -48,7 +50,6 @@ class CongestionControlHandler { int64_t pacer_expected_queue_ms_ = 0; RTC_NO_UNIQUE_ADDRESS SequenceChecker sequenced_checker_; - RTC_DISALLOW_COPY_AND_ASSIGN(CongestionControlHandler); }; } // namespace webrtc #endif // MODULES_CONGESTION_CONTROLLER_RTP_CONTROL_HANDLER_H_ diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc index 1130e39e61..d86de42ac4 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc @@ -22,7 +22,6 @@ #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { diff --git a/modules/congestion_controller/rtp/transport_feedback_demuxer.cc b/modules/congestion_controller/rtp/transport_feedback_demuxer.cc index 6ab3ad80fa..50987b2302 100644 --- a/modules/congestion_controller/rtp/transport_feedback_demuxer.cc +++ b/modules/congestion_controller/rtp/transport_feedback_demuxer.cc @@ -15,10 +15,17 @@ namespace webrtc { namespace { static const size_t kMaxPacketsInHistory = 5000; } + +TransportFeedbackDemuxer::TransportFeedbackDemuxer() { + // In case the construction thread is different from where the registration + // and callbacks occur, detach from the construction thread. + observer_checker_.Detach(); +} + void TransportFeedbackDemuxer::RegisterStreamFeedbackObserver( std::vector ssrcs, StreamFeedbackObserver* observer) { - MutexLock lock(&observers_lock_); + RTC_DCHECK_RUN_ON(&observer_checker_); RTC_DCHECK(observer); RTC_DCHECK(absl::c_find_if(observers_, [=](const auto& pair) { return pair.second == observer; @@ -28,7 +35,7 @@ void TransportFeedbackDemuxer::RegisterStreamFeedbackObserver( void TransportFeedbackDemuxer::DeRegisterStreamFeedbackObserver( StreamFeedbackObserver* observer) { - MutexLock lock(&observers_lock_); + RTC_DCHECK_RUN_ON(&observer_checker_); RTC_DCHECK(observer); const auto it = absl::c_find_if( observers_, [=](const auto& pair) { return pair.second == observer; }); @@ -37,7 +44,7 @@ void TransportFeedbackDemuxer::DeRegisterStreamFeedbackObserver( } void TransportFeedbackDemuxer::AddPacket(const RtpPacketSendInfo& packet_info) { - MutexLock lock(&lock_); + RTC_DCHECK_RUN_ON(&observer_checker_); StreamFeedbackObserver::StreamPacketInfo info; info.ssrc = packet_info.media_ssrc; @@ -55,24 +62,22 @@ void TransportFeedbackDemuxer::AddPacket(const RtpPacketSendInfo& packet_info) { void TransportFeedbackDemuxer::OnTransportFeedback( const rtcp::TransportFeedback& feedback) { + RTC_DCHECK_RUN_ON(&observer_checker_); + std::vector stream_feedbacks; - { - MutexLock lock(&lock_); - for (const auto& packet : feedback.GetAllPackets()) { - int64_t seq_num = - seq_num_unwrapper_.UnwrapWithoutUpdate(packet.sequence_number()); - auto it = history_.find(seq_num); - if (it != history_.end()) { - auto packet_info = it->second; - packet_info.received = packet.received(); - stream_feedbacks.push_back(packet_info); - if (packet.received()) - history_.erase(it); - } + for (const auto& packet : feedback.GetAllPackets()) { + int64_t seq_num = + seq_num_unwrapper_.UnwrapWithoutUpdate(packet.sequence_number()); + auto it = history_.find(seq_num); + if (it != history_.end()) { + auto packet_info = it->second; + packet_info.received = packet.received(); + stream_feedbacks.push_back(std::move(packet_info)); + if (packet.received()) + history_.erase(it); } } - MutexLock lock(&observers_lock_); for (auto& observer : observers_) { std::vector selected_feedback; for (const auto& packet_info : stream_feedbacks) { diff --git a/modules/congestion_controller/rtp/transport_feedback_demuxer.h b/modules/congestion_controller/rtp/transport_feedback_demuxer.h index 634a37ea1a..7f4f5750d2 100644 --- a/modules/congestion_controller/rtp/transport_feedback_demuxer.h +++ b/modules/congestion_controller/rtp/transport_feedback_demuxer.h @@ -14,14 +14,26 @@ #include #include +#include "api/sequence_checker.h" #include "modules/include/module_common_types_public.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/no_unique_address.h" namespace webrtc { -class TransportFeedbackDemuxer : public StreamFeedbackProvider { +// Implementation of StreamFeedbackProvider that provides a way for +// implementations of StreamFeedbackObserver to register for feedback callbacks +// for a given set of SSRCs. +// Registration methods need to be called from the same execution context +// (thread or task queue) and callbacks to +// StreamFeedbackObserver::OnPacketFeedbackVector will be made in that same +// context. +// TODO(tommi): This appears to be the only implementation of this interface. +// Do we need the interface? +class TransportFeedbackDemuxer final : public StreamFeedbackProvider { public: + TransportFeedbackDemuxer(); + // Implements StreamFeedbackProvider interface void RegisterStreamFeedbackObserver( std::vector ssrcs, @@ -32,17 +44,16 @@ class TransportFeedbackDemuxer : public StreamFeedbackProvider { void OnTransportFeedback(const rtcp::TransportFeedback& feedback); private: - Mutex lock_; - SequenceNumberUnwrapper seq_num_unwrapper_ RTC_GUARDED_BY(&lock_); + RTC_NO_UNIQUE_ADDRESS SequenceChecker observer_checker_; + SequenceNumberUnwrapper seq_num_unwrapper_ RTC_GUARDED_BY(&observer_checker_); std::map history_ - RTC_GUARDED_BY(&lock_); + RTC_GUARDED_BY(&observer_checker_); // Maps a set of ssrcs to corresponding observer. Vectors are used rather than // set/map to ensure that the processing order is consistent independently of // the randomized ssrcs. - Mutex observers_lock_; std::vector, StreamFeedbackObserver*>> - observers_ RTC_GUARDED_BY(&observers_lock_); + observers_ RTC_GUARDED_BY(&observer_checker_); }; } // namespace webrtc diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index d2b4eea652..0faad43a1f 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -77,9 +77,6 @@ if (rtc_include_tests) { "window_finder_unittest.cc", ] public_configs = [ ":x11_config" ] - if (is_win) { - deps += [ "../../rtc_base:win32" ] - } } } @@ -225,6 +222,23 @@ if (is_linux || is_chromeos) { } } + pkg_config("gbm") { + packages = [ "gbm" ] + } + pkg_config("egl") { + packages = [ "egl" ] + } + pkg_config("epoxy") { + packages = [ "epoxy" ] + ignore_libs = true + } + pkg_config("libdrm") { + packages = [ "libdrm" ] + if (!rtc_link_pipewire) { + ignore_libs = true + } + } + if (!rtc_link_pipewire) { # When libpipewire is not directly linked, use stubs to allow for dlopening of # the binary. @@ -232,14 +246,18 @@ if (is_linux || is_chromeos) { configs = [ "../../:common_config", ":pipewire", + ":libdrm", ] deps = [ "../../rtc_base" ] - extra_header = "linux/pipewire_stub_header.fragment" + extra_header = "linux/wayland/pipewire_stub_header.fragment" logging_function = "RTC_LOG(LS_VERBOSE)" logging_include = "rtc_base/logging.h" - output_name = "linux/pipewire_stubs" - path_from_source = "modules/desktop_capture/linux" - sigs = [ "linux/pipewire03.sigs" ] + output_name = "linux/wayland/pipewire_stubs" + path_from_source = "modules/desktop_capture/linux/wayland" + sigs = [ + "linux/wayland/pipewire.sigs", + "linux/wayland/drm.sigs", + ] } } @@ -388,37 +406,37 @@ rtc_library("desktop_capture_generic") { if (build_with_mozilla && (is_linux || is_chromeos)) { sources += [ "app_capturer_linux.cc", - "linux/app_capturer_x11.cc", - "linux/desktop_device_info_linux.cc", - "linux/desktop_device_info_linux.h", - "linux/shared_x_util.cc", - "linux/shared_x_util.h", + "linux/x11/app_capturer_x11.cc", + "linux/x11/desktop_device_info_linux.cc", + "linux/x11/desktop_device_info_linux.h", + "linux/x11/shared_x_util.cc", + "linux/x11/shared_x_util.h", ] } } if (rtc_use_x11_extensions) { sources += [ - "linux/mouse_cursor_monitor_x11.cc", - "linux/mouse_cursor_monitor_x11.h", - "linux/screen_capturer_x11.cc", - "linux/screen_capturer_x11.h", - "linux/shared_x_display.cc", - "linux/shared_x_display.h", - "linux/window_capturer_x11.cc", - "linux/window_capturer_x11.h", - "linux/window_finder_x11.cc", - "linux/window_finder_x11.h", - "linux/window_list_utils.cc", - "linux/window_list_utils.h", - "linux/x_atom_cache.cc", - "linux/x_atom_cache.h", - "linux/x_error_trap.cc", - "linux/x_error_trap.h", - "linux/x_server_pixel_buffer.cc", - "linux/x_server_pixel_buffer.h", - "linux/x_window_property.cc", - "linux/x_window_property.h", + "linux/x11/mouse_cursor_monitor_x11.cc", + "linux/x11/mouse_cursor_monitor_x11.h", + "linux/x11/screen_capturer_x11.cc", + "linux/x11/screen_capturer_x11.h", + "linux/x11/shared_x_display.cc", + "linux/x11/shared_x_display.h", + "linux/x11/window_capturer_x11.cc", + "linux/x11/window_capturer_x11.h", + "linux/x11/window_finder_x11.cc", + "linux/x11/window_finder_x11.h", + "linux/x11/window_list_utils.cc", + "linux/x11/window_list_utils.h", + "linux/x11/x_atom_cache.cc", + "linux/x11/x_atom_cache.h", + "linux/x11/x_error_trap.cc", + "linux/x11/x_error_trap.h", + "linux/x11/x_server_pixel_buffer.cc", + "linux/x11/x_server_pixel_buffer.h", + "linux/x11/x_window_property.cc", + "linux/x11/x_window_property.h", ] libs = [ "X11", @@ -450,6 +468,7 @@ rtc_library("desktop_capture_generic") { "../../rtc_base:checks", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:arch", + "../../rtc_base/system:no_unique_address", "../../rtc_base/system:rtc_export", "../../system_wrappers", "../../system_wrappers:metrics", @@ -515,7 +534,10 @@ rtc_library("desktop_capture_generic") { "d3d11.lib", "dxgi.lib", ] - deps += [ "../../rtc_base:win32" ] + deps += [ + "../../rtc_base:rtc_base_approved", + "../../rtc_base:win32", + ] } absl_deps = [ @@ -540,19 +562,38 @@ rtc_library("desktop_capture_generic") { if (rtc_use_pipewire) { sources += [ - "linux/base_capturer_pipewire.cc", - "linux/base_capturer_pipewire.h", + "linux/wayland/base_capturer_pipewire.cc", + "linux/wayland/base_capturer_pipewire.h", + "linux/wayland/egl_dmabuf.cc", + "linux/wayland/egl_dmabuf.h", + "linux/wayland/mouse_cursor_monitor_pipewire.cc", + "linux/wayland/mouse_cursor_monitor_pipewire.h", + "linux/wayland/scoped_glib.cc", + "linux/wayland/scoped_glib.h", + "linux/wayland/screencast_portal.cc", + "linux/wayland/screencast_portal.h", + "linux/wayland/shared_screencast_stream.cc", + "linux/wayland/shared_screencast_stream.h", + "linux/wayland/xdg_desktop_portal_utils.cc", + "linux/wayland/xdg_desktop_portal_utils.h", ] configs += [ - ":pipewire_config", ":gio", ":pipewire", + ":gbm", + ":egl", + ":epoxy", + ":libdrm", ] if (!rtc_link_pipewire) { deps += [ ":pipewire_stubs" ] } + + public_configs += [ ":pipewire_config" ] + + deps += [ "../../rtc_base:sanitizer" ] } if (rtc_enable_win_wgc) { diff --git a/modules/desktop_capture/OWNERS b/modules/desktop_capture/OWNERS index 79df492e69..e3bc32ee5c 100644 --- a/modules/desktop_capture/OWNERS +++ b/modules/desktop_capture/OWNERS @@ -1,2 +1,2 @@ -jamiewalch@chromium.org -joedow@chromium.org +alcooper@chromium.org +mfoltz@chromium.org diff --git a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc index ca3a89f49b..c20843414b 100644 --- a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc +++ b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc @@ -23,8 +23,11 @@ namespace webrtc { BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper( std::unique_ptr capturer, - RgbaColor blank_pixel) - : capturer_(std::move(capturer)), blank_pixel_(blank_pixel) { + RgbaColor blank_pixel, + bool check_per_capture) + : capturer_(std::move(capturer)), + blank_pixel_(blank_pixel), + check_per_capture_(check_per_capture) { RTC_DCHECK(capturer_); } @@ -56,6 +59,13 @@ bool BlankDetectorDesktopCapturerWrapper::GetSourceList(SourceList* sources) { } bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) { + if (check_per_capture_) { + // If we start capturing a new source, we must reset these members + // so we don't short circuit the blank detection logic. + is_first_frame_ = true; + non_blank_frame_received_ = false; + } + return capturer_->SelectSource(id); } diff --git a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h index f5c2ce201b..d10f9cf725 100644 --- a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h +++ b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h @@ -34,7 +34,8 @@ class BlankDetectorDesktopCapturerWrapper final // takes ownership of `capturer`. The `blank_pixel` is the unmodified color // returned by the `capturer`. BlankDetectorDesktopCapturerWrapper(std::unique_ptr capturer, - RgbaColor blank_pixel); + RgbaColor blank_pixel, + bool check_per_capture = false); ~BlankDetectorDesktopCapturerWrapper() override; // DesktopCapturer interface. @@ -70,6 +71,10 @@ class BlankDetectorDesktopCapturerWrapper final // Whether current frame is the first frame. bool is_first_frame_ = true; + // Blank inspection is made per capture instead of once for all + // screens or windows. + bool check_per_capture_ = false; + DesktopCapturer::Callback* callback_ = nullptr; }; diff --git a/modules/desktop_capture/cropped_desktop_frame.cc b/modules/desktop_capture/cropped_desktop_frame.cc index 1ab0355075..54488b7d62 100644 --- a/modules/desktop_capture/cropped_desktop_frame.cc +++ b/modules/desktop_capture/cropped_desktop_frame.cc @@ -15,7 +15,6 @@ #include "modules/desktop_capture/desktop_region.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -25,10 +24,11 @@ class CroppedDesktopFrame : public DesktopFrame { CroppedDesktopFrame(std::unique_ptr frame, const DesktopRect& rect); + CroppedDesktopFrame(const CroppedDesktopFrame&) = delete; + CroppedDesktopFrame& operator=(const CroppedDesktopFrame&) = delete; + private: const std::unique_ptr frame_; - - RTC_DISALLOW_COPY_AND_ASSIGN(CroppedDesktopFrame); }; std::unique_ptr CreateCroppedDesktopFrame( @@ -36,7 +36,9 @@ std::unique_ptr CreateCroppedDesktopFrame( const DesktopRect& rect) { RTC_DCHECK(frame); - if (!DesktopRect::MakeSize(frame->size()).ContainsRect(rect)) { + DesktopRect intersection = DesktopRect::MakeSize(frame->size()); + intersection.IntersectWith(rect); + if (intersection.is_empty()) { return nullptr; } @@ -45,7 +47,7 @@ std::unique_ptr CreateCroppedDesktopFrame( } return std::unique_ptr( - new CroppedDesktopFrame(std::move(frame), rect)); + new CroppedDesktopFrame(std::move(frame), intersection)); } CroppedDesktopFrame::CroppedDesktopFrame(std::unique_ptr frame, diff --git a/modules/desktop_capture/cropped_desktop_frame_unittest.cc b/modules/desktop_capture/cropped_desktop_frame_unittest.cc index ff9b8ca456..9becf69636 100644 --- a/modules/desktop_capture/cropped_desktop_frame_unittest.cc +++ b/modules/desktop_capture/cropped_desktop_frame_unittest.cc @@ -32,9 +32,14 @@ TEST(CroppedDesktopFrameTest, DoNotCreateWrapperIfSizeIsNotChanged) { ASSERT_EQ(cropped.get(), raw_original); } -TEST(CroppedDesktopFrameTest, ReturnNullptrIfSizeIsNotSufficient) { - ASSERT_EQ(nullptr, CreateCroppedDesktopFrame(CreateTestFrame(), - DesktopRect::MakeWH(11, 10))); +TEST(CroppedDesktopFrameTest, CropWhenPartiallyOutOfBounds) { + std::unique_ptr cropped = + CreateCroppedDesktopFrame(CreateTestFrame(), DesktopRect::MakeWH(11, 10)); + ASSERT_NE(nullptr, cropped); + ASSERT_EQ(cropped->size().width(), 10); + ASSERT_EQ(cropped->size().height(), 10); + ASSERT_EQ(cropped->top_left().x(), 0); + ASSERT_EQ(cropped->top_left().y(), 0); } TEST(CroppedDesktopFrameTest, ReturnNullIfCropRegionIsOutOfBounds) { diff --git a/modules/desktop_capture/cropping_window_capturer.cc b/modules/desktop_capture/cropping_window_capturer.cc index bd1ba46315..5e0faaade9 100644 --- a/modules/desktop_capture/cropping_window_capturer.cc +++ b/modules/desktop_capture/cropping_window_capturer.cc @@ -99,9 +99,16 @@ void CroppingWindowCapturer::OnCaptureResult( return; } - callback_->OnCaptureResult( - Result::SUCCESS, - CreateCroppedDesktopFrame(std::move(screen_frame), window_rect)); + std::unique_ptr cropped_frame = + CreateCroppedDesktopFrame(std::move(screen_frame), window_rect); + + if (!cropped_frame) { + RTC_LOG(LS_WARNING) << "Window is outside of the captured display"; + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + + callback_->OnCaptureResult(Result::SUCCESS, std::move(cropped_frame)); } bool CroppingWindowCapturer::IsOccluded(const DesktopVector& pos) { diff --git a/modules/desktop_capture/cropping_window_capturer_win.cc b/modules/desktop_capture/cropping_window_capturer_win.cc index a8eacde62e..64d9219e24 100644 --- a/modules/desktop_capture/cropping_window_capturer_win.cc +++ b/modules/desktop_capture/cropping_window_capturer_win.cc @@ -15,7 +15,7 @@ #include "modules/desktop_capture/win/window_capture_utils.h" #include "rtc_base/logging.h" #include "rtc_base/trace_event.h" -#include "rtc_base/win32.h" +#include "rtc_base/win/windows_version.h" namespace webrtc { @@ -118,7 +118,7 @@ struct TopWindowVerifierContext : public SelectedWindowContext { // firing an assert when enabled, report that the selected window isn't // topmost to avoid inadvertent capture of other windows. RTC_LOG(LS_ERROR) << "Failed to enumerate windows: " << lastError; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } } @@ -196,7 +196,8 @@ void CroppingWindowCapturerWin::CaptureFrame() { } bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() { - if (!rtc::IsWindows8OrLater() && window_capture_helper_.IsAeroEnabled()) { + if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN8 && + window_capture_helper_.IsAeroEnabled()) { return false; } diff --git a/modules/desktop_capture/desktop_and_cursor_composer.cc b/modules/desktop_capture/desktop_and_cursor_composer.cc index 7be8982abe..7ca0af038c 100644 --- a/modules/desktop_capture/desktop_and_cursor_composer.cc +++ b/modules/desktop_capture/desktop_and_cursor_composer.cc @@ -21,7 +21,6 @@ #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/mouse_cursor_monitor.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -72,6 +71,9 @@ class DesktopFrameWithCursor : public DesktopFrame { bool cursor_changed); ~DesktopFrameWithCursor() override; + DesktopFrameWithCursor(const DesktopFrameWithCursor&) = delete; + DesktopFrameWithCursor& operator=(const DesktopFrameWithCursor&) = delete; + DesktopRect cursor_rect() const { return cursor_rect_; } private: @@ -80,8 +82,6 @@ class DesktopFrameWithCursor : public DesktopFrame { DesktopVector restore_position_; std::unique_ptr restore_frame_; DesktopRect cursor_rect_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor); }; DesktopFrameWithCursor::DesktopFrameWithCursor( diff --git a/modules/desktop_capture/desktop_and_cursor_composer.h b/modules/desktop_capture/desktop_and_cursor_composer.h index a02705810c..edb764d168 100644 --- a/modules/desktop_capture/desktop_and_cursor_composer.h +++ b/modules/desktop_capture/desktop_and_cursor_composer.h @@ -21,7 +21,6 @@ #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/mouse_cursor_monitor.h" #include "modules/desktop_capture/shared_memory.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -41,6 +40,9 @@ class RTC_EXPORT DesktopAndCursorComposer ~DesktopAndCursorComposer() override; + DesktopAndCursorComposer(const DesktopAndCursorComposer&) = delete; + DesktopAndCursorComposer& operator=(const DesktopAndCursorComposer&) = delete; + // Creates a new composer that relies on an external source for cursor shape // and position information via the MouseCursorMonitor::Callback interface. static std::unique_ptr @@ -84,8 +86,6 @@ class RTC_EXPORT DesktopAndCursorComposer DesktopVector cursor_position_; DesktopRect previous_cursor_rect_; bool cursor_changed_ = false; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopAndCursorComposer); }; } // namespace webrtc diff --git a/modules/desktop_capture/desktop_capture_options.cc b/modules/desktop_capture/desktop_capture_options.cc index c89896d5fd..ab7932195c 100644 --- a/modules/desktop_capture/desktop_capture_options.cc +++ b/modules/desktop_capture/desktop_capture_options.cc @@ -14,6 +14,11 @@ #elif defined(WEBRTC_WIN) #include "modules/desktop_capture/win/full_screen_win_application_handler.h" #endif +#if defined(WEBRTC_USE_PIPEWIRE) +#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" +#endif + +#include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -35,13 +40,19 @@ DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() { #if defined(WEBRTC_USE_X11) result.set_x_display(SharedXDisplay::CreateDefault()); #endif +#if defined(WEBRTC_USE_PIPEWIRE) + result.set_screencast_stream(SharedScreenCastStream::CreateDefault()); +#endif #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) - result.set_configuration_monitor(new DesktopConfigurationMonitor()); + result.set_configuration_monitor( + rtc::make_ref_counted()); result.set_full_screen_window_detector( - new FullScreenWindowDetector(CreateFullScreenMacApplicationHandler)); + rtc::make_ref_counted( + CreateFullScreenMacApplicationHandler)); #elif defined(WEBRTC_WIN) result.set_full_screen_window_detector( - new FullScreenWindowDetector(CreateFullScreenWinApplicationHandler)); + rtc::make_ref_counted( + CreateFullScreenWinApplicationHandler)); #endif return result; } diff --git a/modules/desktop_capture/desktop_capture_options.h b/modules/desktop_capture/desktop_capture_options.h index 26eb0f535f..dd3cde0145 100644 --- a/modules/desktop_capture/desktop_capture_options.h +++ b/modules/desktop_capture/desktop_capture_options.h @@ -14,7 +14,11 @@ #include "rtc_base/system/rtc_export.h" #if defined(WEBRTC_USE_X11) -#include "modules/desktop_capture/linux/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" +#endif + +#if defined(WEBRTC_USE_PIPEWIRE) +#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" #endif #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) @@ -43,7 +47,9 @@ class RTC_EXPORT DesktopCaptureOptions { DesktopCaptureOptions& operator=(DesktopCaptureOptions&& options); #if defined(WEBRTC_USE_X11) - SharedXDisplay* x_display() const { return x_display_; } + const rtc::scoped_refptr& x_display() const { + return x_display_; + } void set_x_display(rtc::scoped_refptr x_display) { x_display_ = x_display; } @@ -53,7 +59,8 @@ class RTC_EXPORT DesktopCaptureOptions { // TODO(zijiehe): Remove both DesktopConfigurationMonitor and // FullScreenChromeWindowDetector out of DesktopCaptureOptions. It's not // reasonable for external consumers to set these two parameters. - DesktopConfigurationMonitor* configuration_monitor() const { + const rtc::scoped_refptr& configuration_monitor() + const { return configuration_monitor_; } // If nullptr is set, ScreenCapturer won't work and WindowCapturer may return @@ -67,7 +74,8 @@ class RTC_EXPORT DesktopCaptureOptions { void set_allow_iosurface(bool allow) { allow_iosurface_ = allow; } #endif - FullScreenWindowDetector* full_screen_window_detector() const { + const rtc::scoped_refptr& + full_screen_window_detector() const { return full_screen_window_detector_; } void set_full_screen_window_detector( @@ -155,19 +163,43 @@ class RTC_EXPORT DesktopCaptureOptions { // precedence over the cropping, directx, and magnification flags. bool allow_wgc_capturer() const { return allow_wgc_capturer_; } void set_allow_wgc_capturer(bool allow) { allow_wgc_capturer_ = allow; } + + // This flag enables the WGC capturer for fallback capturer. + // The flag is useful when the first capturer (eg. WindowCapturerWinGdi) is + // unreliable in certain devices where WGC is supported, but not used by + // default. + bool allow_wgc_capturer_fallback() const { + return allow_wgc_capturer_fallback_; + } + void set_allow_wgc_capturer_fallback(bool allow) { + allow_wgc_capturer_fallback_ = allow; + } #endif // defined(RTC_ENABLE_WIN_WGC) #endif // defined(WEBRTC_WIN) #if defined(WEBRTC_USE_PIPEWIRE) bool allow_pipewire() const { return allow_pipewire_; } void set_allow_pipewire(bool allow) { allow_pipewire_ = allow; } + + const rtc::scoped_refptr& screencast_stream() const { + return screencast_stream_; + } + void set_screencast_stream( + rtc::scoped_refptr stream) { + screencast_stream_ = stream; + } #endif private: #if defined(WEBRTC_USE_X11) rtc::scoped_refptr x_display_; #endif - +#if defined(WEBRTC_USE_PIPEWIRE) + // An instance of shared PipeWire ScreenCast stream we share between + // BaseCapturerPipeWire and MouseCursorMonitorPipeWire as cursor information + // is sent together with screen content. + rtc::scoped_refptr screencast_stream_; +#endif #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) rtc::scoped_refptr configuration_monitor_; bool allow_iosurface_ = false; @@ -182,6 +214,7 @@ class RTC_EXPORT DesktopCaptureOptions { bool allow_cropping_window_capturer_ = false; #if defined(RTC_ENABLE_WIN_WGC) bool allow_wgc_capturer_ = false; + bool allow_wgc_capturer_fallback_ = false; #endif #endif #if defined(WEBRTC_USE_X11) diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h index 4dcfc82708..bc26db7cc4 100644 --- a/modules/desktop_capture/desktop_capture_types.h +++ b/modules/desktop_capture/desktop_capture_types.h @@ -27,9 +27,14 @@ const WindowId kNullWindowId = 0; // - On Windows: integer display device index. // - On OSX: CGDirectDisplayID cast to intptr_t. // - On Linux (with X11): TBD. +// - On ChromeOS: display::Display::id() is an int64_t. // On Windows, ScreenId is implementation dependent: sending a ScreenId from one // implementation to another usually won't work correctly. -typedef intptr_t ScreenId; +#if defined(CHROMEOS) + typedef int64_t ScreenId; +#else + typedef intptr_t ScreenId; +#endif // The screen id corresponds to all screen combined together. const ScreenId kFullDesktopScreenId = -1; diff --git a/modules/desktop_capture/desktop_capturer.h b/modules/desktop_capture/desktop_capturer.h index 8a65e5759c..822a75d947 100644 --- a/modules/desktop_capture/desktop_capturer.h +++ b/modules/desktop_capture/desktop_capturer.h @@ -59,7 +59,11 @@ class RTC_EXPORT DesktopCapturer { virtual ~Callback() {} }; +#if defined(CHROMEOS) + typedef int64_t SourceId; +#else typedef intptr_t SourceId; +#endif static_assert(std::is_same::value, "SourceId should be a same type as ScreenId."); diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h index 9c53b836cb..3ee1867e70 100644 --- a/modules/desktop_capture/desktop_frame.h +++ b/modules/desktop_capture/desktop_frame.h @@ -19,7 +19,6 @@ #include "modules/desktop_capture/desktop_geometry.h" #include "modules/desktop_capture/desktop_region.h" #include "modules/desktop_capture/shared_memory.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,6 +33,9 @@ class RTC_EXPORT DesktopFrame { virtual ~DesktopFrame(); + DesktopFrame(const DesktopFrame&) = delete; + DesktopFrame& operator=(const DesktopFrame&) = delete; + // Returns the rectangle in full desktop coordinates to indicate it covers // the area of top_left() to top_letf() + size() / scale_factor(). DesktopRect rect() const; @@ -163,8 +165,6 @@ class RTC_EXPORT DesktopFrame { int64_t capture_time_ms_; uint32_t capturer_id_; std::vector icc_profile_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrame); }; // A DesktopFrame that stores data in the heap. @@ -175,12 +175,12 @@ class RTC_EXPORT BasicDesktopFrame : public DesktopFrame { ~BasicDesktopFrame() override; + BasicDesktopFrame(const BasicDesktopFrame&) = delete; + BasicDesktopFrame& operator=(const BasicDesktopFrame&) = delete; + // Creates a BasicDesktopFrame that contains copy of `frame`. // TODO(zijiehe): Return std::unique_ptr static DesktopFrame* CopyOf(const DesktopFrame& frame); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(BasicDesktopFrame); }; // A DesktopFrame that stores data in shared memory. @@ -206,6 +206,9 @@ class RTC_EXPORT SharedMemoryDesktopFrame : public DesktopFrame { ~SharedMemoryDesktopFrame() override; + SharedMemoryDesktopFrame(const SharedMemoryDesktopFrame&) = delete; + SharedMemoryDesktopFrame& operator=(const SharedMemoryDesktopFrame&) = delete; + private: // Avoid unexpected order of parameter evaluation. // Executing both std::unique_ptr::operator->() and @@ -217,8 +220,6 @@ class RTC_EXPORT SharedMemoryDesktopFrame : public DesktopFrame { SharedMemoryDesktopFrame(DesktopRect rect, int stride, SharedMemory* shared_memory); - - RTC_DISALLOW_COPY_AND_ASSIGN(SharedMemoryDesktopFrame); }; } // namespace webrtc diff --git a/modules/desktop_capture/desktop_frame_rotation.cc b/modules/desktop_capture/desktop_frame_rotation.cc index daef385f27..f2745cf70b 100644 --- a/modules/desktop_capture/desktop_frame_rotation.cc +++ b/modules/desktop_capture/desktop_frame_rotation.cc @@ -28,7 +28,7 @@ libyuv::RotationMode ToLibyuvRotationMode(Rotation rotation) { case Rotation::CLOCK_WISE_270: return libyuv::kRotate270; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return libyuv::kRotate0; } @@ -54,7 +54,7 @@ Rotation ReverseRotation(Rotation rotation) { case Rotation::CLOCK_WISE_270: return Rotation::CLOCK_WISE_90; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return Rotation::CLOCK_WISE_0; } @@ -67,7 +67,7 @@ DesktopSize RotateSize(DesktopSize size, Rotation rotation) { case Rotation::CLOCK_WISE_270: return DesktopSize(size.height(), size.width()); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return DesktopSize(); } @@ -86,7 +86,7 @@ DesktopRect RotateRect(DesktopRect rect, DesktopSize size, Rotation rotation) { return DesktopRect::MakeXYWH(rect.top(), size.width() - rect.right(), rect.height(), rect.width()); } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return DesktopRect(); } diff --git a/modules/desktop_capture/desktop_frame_win.cc b/modules/desktop_capture/desktop_frame_win.cc index 58ebac91d5..262ebbdec0 100644 --- a/modules/desktop_capture/desktop_frame_win.cc +++ b/modules/desktop_capture/desktop_frame_win.cc @@ -50,6 +50,10 @@ std::unique_ptr DesktopFrameWin::Create( HANDLE section_handle = nullptr; if (shared_memory_factory) { shared_memory = shared_memory_factory->CreateSharedMemory(buffer_size); + if (!shared_memory) { + RTC_LOG(LS_WARNING) << "Failed to allocate shared memory"; + return nullptr; + } section_handle = shared_memory->handle(); } void* data = nullptr; diff --git a/modules/desktop_capture/desktop_frame_win.h b/modules/desktop_capture/desktop_frame_win.h index 73e864868d..f8faad6777 100644 --- a/modules/desktop_capture/desktop_frame_win.h +++ b/modules/desktop_capture/desktop_frame_win.h @@ -16,7 +16,6 @@ #include #include "modules/desktop_capture/desktop_frame.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -26,6 +25,9 @@ class DesktopFrameWin : public DesktopFrame { public: ~DesktopFrameWin() override; + DesktopFrameWin(const DesktopFrameWin&) = delete; + DesktopFrameWin& operator=(const DesktopFrameWin&) = delete; + static std::unique_ptr Create(DesktopSize size, SharedMemoryFactory* shared_memory_factory, HDC hdc); @@ -40,8 +42,6 @@ class DesktopFrameWin : public DesktopFrame { HBITMAP bitmap_; std::unique_ptr owned_shared_memory_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameWin); }; } // namespace webrtc diff --git a/modules/desktop_capture/full_screen_application_handler.h b/modules/desktop_capture/full_screen_application_handler.h index 849cb2c761..b7e097a474 100644 --- a/modules/desktop_capture/full_screen_application_handler.h +++ b/modules/desktop_capture/full_screen_application_handler.h @@ -12,8 +12,8 @@ #define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_ #include + #include "modules/desktop_capture/desktop_capturer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -25,6 +25,10 @@ class FullScreenApplicationHandler { public: virtual ~FullScreenApplicationHandler() {} + FullScreenApplicationHandler(const FullScreenApplicationHandler&) = delete; + FullScreenApplicationHandler& operator=(const FullScreenApplicationHandler&) = + delete; + explicit FullScreenApplicationHandler(DesktopCapturer::SourceId sourceId); // Returns the full-screen window in place of the original window if all the @@ -39,8 +43,6 @@ class FullScreenApplicationHandler { private: const DesktopCapturer::SourceId source_id_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FullScreenApplicationHandler); }; } // namespace webrtc diff --git a/modules/desktop_capture/full_screen_window_detector.h b/modules/desktop_capture/full_screen_window_detector.h index ca30d95de4..998b720d90 100644 --- a/modules/desktop_capture/full_screen_window_detector.h +++ b/modules/desktop_capture/full_screen_window_detector.h @@ -12,12 +12,12 @@ #define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_ #include + #include "api/function_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/full_screen_application_handler.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -42,6 +42,9 @@ class FullScreenWindowDetector FullScreenWindowDetector( ApplicationHandlerFactory application_handler_factory); + FullScreenWindowDetector(const FullScreenWindowDetector&) = delete; + FullScreenWindowDetector& operator=(const FullScreenWindowDetector&) = delete; + // Returns the full-screen window in place of the original window if all the // criteria provided by FullScreenApplicationHandler are met, or 0 if no such // window found. @@ -73,7 +76,6 @@ class FullScreenWindowDetector DesktopCapturer::SourceId no_handler_source_id_; DesktopCapturer::SourceList window_list_; - RTC_DISALLOW_COPY_AND_ASSIGN(FullScreenWindowDetector); }; } // namespace webrtc diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.cc b/modules/desktop_capture/linux/base_capturer_pipewire.cc deleted file mode 100644 index 7212ad383e..0000000000 --- a/modules/desktop_capture/linux/base_capturer_pipewire.cc +++ /dev/null @@ -1,1065 +0,0 @@ -/* - * Copyright 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/desktop_capture/linux/base_capturer_pipewire.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "absl/memory/memory.h" -#include "modules/desktop_capture/desktop_capture_options.h" -#include "modules/desktop_capture/desktop_capturer.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" - -#if defined(WEBRTC_DLOPEN_PIPEWIRE) -#include "modules/desktop_capture/linux/pipewire_stubs.h" -using modules_desktop_capture_linux::InitializeStubs; -using modules_desktop_capture_linux::kModulePipewire03; -using modules_desktop_capture_linux::StubPathMap; -#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) - -namespace webrtc { - -const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; -const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; -const char kDesktopRequestObjectPath[] = - "/org/freedesktop/portal/desktop/request"; -const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; -const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; -const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; - -const int kBytesPerPixel = 4; - -#if defined(WEBRTC_DLOPEN_PIPEWIRE) -const char kPipeWireLib[] = "libpipewire-0.3.so.0"; -#endif - -// static -struct dma_buf_sync { - uint64_t flags; -}; -#define DMA_BUF_SYNC_READ (1 << 0) -#define DMA_BUF_SYNC_START (0 << 2) -#define DMA_BUF_SYNC_END (1 << 2) -#define DMA_BUF_BASE 'b' -#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) - -static void SyncDmaBuf(int fd, uint64_t start_or_end) { - struct dma_buf_sync sync = {0}; - - sync.flags = start_or_end | DMA_BUF_SYNC_READ; - - while (true) { - int ret; - ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync); - if (ret == -1 && errno == EINTR) { - continue; - } else if (ret == -1) { - RTC_LOG(LS_ERROR) << "Failed to synchronize DMA buffer: " - << g_strerror(errno); - break; - } else { - break; - } - } -} - -class ScopedBuf { - public: - ScopedBuf() {} - ScopedBuf(unsigned char* map, int map_size, bool is_dma_buf, int fd) - : map_(map), map_size_(map_size), is_dma_buf_(is_dma_buf), fd_(fd) {} - ~ScopedBuf() { - if (map_ != MAP_FAILED) { - if (is_dma_buf_) { - SyncDmaBuf(fd_, DMA_BUF_SYNC_END); - } - munmap(map_, map_size_); - } - } - - operator bool() { return map_ != MAP_FAILED; } - - void initialize(unsigned char* map, int map_size, bool is_dma_buf, int fd) { - map_ = map; - map_size_ = map_size; - is_dma_buf_ = is_dma_buf; - fd_ = fd; - } - - unsigned char* get() { return map_; } - - protected: - unsigned char* map_ = nullptr; - int map_size_; - bool is_dma_buf_; - int fd_; -}; - -template -class Scoped { - public: - Scoped() {} - explicit Scoped(T* val) { ptr_ = val; } - ~Scoped() { RTC_NOTREACHED(); } - - T* operator->() { return ptr_; } - - bool operator!() { return ptr_ == nullptr; } - - T* get() { return ptr_; } - - T** receive() { - RTC_CHECK(!ptr_); - return &ptr_; - } - - Scoped& operator=(T* val) { - ptr_ = val; - return *this; - } - - protected: - T* ptr_ = nullptr; -}; - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_error_free(ptr_); - } -} - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_free(ptr_); - } -} - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_variant_unref(ptr_); - } -} - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_variant_iter_free(ptr_); - } -} - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_object_unref(ptr_); - } -} - -template <> -Scoped::~Scoped() { - if (ptr_) { - g_object_unref(ptr_); - } -} - -void BaseCapturerPipeWire::OnCoreError(void* data, - uint32_t id, - int seq, - int res, - const char* message) { - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - - RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message; -} - -// static -void BaseCapturerPipeWire::OnStreamStateChanged(void* data, - pw_stream_state old_state, - pw_stream_state state, - const char* error_message) { - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - - switch (state) { - case PW_STREAM_STATE_ERROR: - RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; - break; - case PW_STREAM_STATE_PAUSED: - case PW_STREAM_STATE_STREAMING: - case PW_STREAM_STATE_UNCONNECTED: - case PW_STREAM_STATE_CONNECTING: - break; - } -} - -// static -void BaseCapturerPipeWire::OnStreamParamChanged(void* data, - uint32_t id, - const struct spa_pod* format) { - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - - RTC_LOG(LS_INFO) << "PipeWire stream format changed."; - - if (!format || id != SPA_PARAM_Format) { - return; - } - - spa_format_video_raw_parse(format, &that->spa_video_format_); - - auto width = that->spa_video_format_.size.width; - auto height = that->spa_video_format_.size.height; - auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4); - auto size = height * stride; - - that->desktop_size_ = DesktopSize(width, height); - - uint8_t buffer[1024] = {}; - auto builder = spa_pod_builder{buffer, sizeof(buffer)}; - - // Setup buffers and meta header for new format. - const struct spa_pod* params[3]; - params[0] = reinterpret_cast(spa_pod_builder_add_object( - &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, - SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers, - SPA_POD_CHOICE_RANGE_Int(8, 1, 32))); - params[1] = reinterpret_cast(spa_pod_builder_add_object( - &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, - SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size, - SPA_POD_Int(sizeof(struct spa_meta_header)))); - params[2] = reinterpret_cast(spa_pod_builder_add_object( - &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, - SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size, - SPA_POD_Int(sizeof(struct spa_meta_region)))); - pw_stream_update_params(that->pw_stream_, params, 3); -} - -// static -void BaseCapturerPipeWire::OnStreamProcess(void* data) { - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - - struct pw_buffer* next_buffer; - struct pw_buffer* buffer = nullptr; - - next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); - while (next_buffer) { - buffer = next_buffer; - next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); - - if (next_buffer) { - pw_stream_queue_buffer(that->pw_stream_, buffer); - } - } - - if (!buffer) { - return; - } - - that->HandleBuffer(buffer); - - pw_stream_queue_buffer(that->pw_stream_, buffer); -} - -BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type) - : capture_source_type_(source_type) {} - -BaseCapturerPipeWire::~BaseCapturerPipeWire() { - if (pw_main_loop_) { - pw_thread_loop_stop(pw_main_loop_); - } - - if (pw_stream_) { - pw_stream_destroy(pw_stream_); - } - - if (pw_core_) { - pw_core_disconnect(pw_core_); - } - - if (pw_context_) { - pw_context_destroy(pw_context_); - } - - if (pw_main_loop_) { - pw_thread_loop_destroy(pw_main_loop_); - } - - if (start_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); - } - if (sources_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(connection_, - sources_request_signal_id_); - } - if (session_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(connection_, - session_request_signal_id_); - } - - if (session_handle_) { - Scoped message(g_dbus_message_new_method_call( - kDesktopBusName, session_handle_, kSessionInterfaceName, "Close")); - if (message.get()) { - Scoped error; - g_dbus_connection_send_message(connection_, message.get(), - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - /*out_serial=*/nullptr, error.receive()); - if (error.get()) { - RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; - } - } - } - - g_free(start_handle_); - g_free(sources_handle_); - g_free(session_handle_); - g_free(portal_handle_); - - if (cancellable_) { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); - cancellable_ = nullptr; - } - - if (proxy_) { - g_object_unref(proxy_); - proxy_ = nullptr; - } -} - -void BaseCapturerPipeWire::InitPortal() { - cancellable_ = g_cancellable_new(); - g_dbus_proxy_new_for_bus( - G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, - kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, - cancellable_, - reinterpret_cast(OnProxyRequested), this); -} - -void BaseCapturerPipeWire::InitPipeWire() { -#if defined(WEBRTC_DLOPEN_PIPEWIRE) - StubPathMap paths; - - // Check if the PipeWire library is available. - paths[kModulePipewire03].push_back(kPipeWireLib); - if (!InitializeStubs(paths)) { - RTC_LOG(LS_ERROR) << "Failed to load the PipeWire library and symbols."; - portal_init_failed_ = true; - return; - } -#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) - - pw_init(/*argc=*/nullptr, /*argc=*/nullptr); - - pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr); - - pw_thread_loop_lock(pw_main_loop_); - - pw_context_ = - pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); - if (!pw_context_) { - RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; - return; - } - - pw_core_ = pw_context_connect(pw_context_, nullptr, 0); - if (!pw_core_) { - RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; - return; - } - - // Initialize event handlers, remote end and stream-related. - pw_core_events_.version = PW_VERSION_CORE_EVENTS; - pw_core_events_.error = &OnCoreError; - - pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; - pw_stream_events_.state_changed = &OnStreamStateChanged; - pw_stream_events_.param_changed = &OnStreamParamChanged; - pw_stream_events_.process = &OnStreamProcess; - - pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); - - pw_stream_ = CreateReceivingStream(); - if (!pw_stream_) { - RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; - return; - } - - if (pw_thread_loop_start(pw_main_loop_) < 0) { - RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; - portal_init_failed_ = true; - } - - pw_thread_loop_unlock(pw_main_loop_); - - RTC_LOG(LS_INFO) << "PipeWire remote opened."; -} - -pw_stream* BaseCapturerPipeWire::CreateReceivingStream() { - spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1}; - spa_rectangle pwMaxScreenBounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; - - pw_properties* reuseProps = - pw_properties_new_string("pipewire.client.reuse=1"); - auto stream = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps); - - uint8_t buffer[1024] = {}; - const spa_pod* params[1]; - spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; - - params[0] = reinterpret_cast(spa_pod_builder_add_object( - &builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_VIDEO_format, - SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, - SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, - SPA_VIDEO_FORMAT_BGRA), - SPA_FORMAT_VIDEO_size, - SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, &pwMinScreenBounds, - &pwMaxScreenBounds), - 0)); - - pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_, - this); - if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_, - PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) { - RTC_LOG(LS_ERROR) << "Could not connect receiving stream."; - portal_init_failed_ = true; - return nullptr; - } - - return stream; -} - -void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) { - spa_buffer* spaBuffer = buffer->buffer; - ScopedBuf map; - uint8_t* src = nullptr; - - if (spaBuffer->datas[0].chunk->size == 0) { - RTC_LOG(LS_ERROR) << "Failed to get video stream: Zero size."; - return; - } - - if (spaBuffer->datas[0].type == SPA_DATA_MemFd || - spaBuffer->datas[0].type == SPA_DATA_DmaBuf) { - map.initialize( - static_cast( - mmap(nullptr, - spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, - PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)), - spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, - spaBuffer->datas[0].type == SPA_DATA_DmaBuf, - spaBuffer->datas[0].fd); - - if (!map) { - RTC_LOG(LS_ERROR) << "Failed to mmap the memory: " - << std::strerror(errno); - return; - } - - if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) { - SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START); - } - - src = SPA_MEMBER(map.get(), spaBuffer->datas[0].mapoffset, uint8_t); - } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) { - src = static_cast(spaBuffer->datas[0].data); - } - - if (!src) { - return; - } - - struct spa_meta_region* video_metadata = - static_cast(spa_buffer_find_meta_data( - spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata))); - - // Video size from metadata is bigger than an actual video stream size. - // The metadata are wrong or we should up-scale the video...in both cases - // just quit now. - if (video_metadata && (video_metadata->region.size.width > - static_cast(desktop_size_.width()) || - video_metadata->region.size.height > - static_cast(desktop_size_.height()))) { - RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!"; - return; - } - - // Use video metadata when video size from metadata is set and smaller than - // video stream size, so we need to adjust it. - bool video_metadata_use = false; - - const struct spa_rectangle* video_metadata_size = - video_metadata ? &video_metadata->region.size : nullptr; - - if (video_metadata_size && video_metadata_size->width != 0 && - video_metadata_size->height != 0 && - (static_cast(video_metadata_size->width) < desktop_size_.width() || - static_cast(video_metadata_size->height) < - desktop_size_.height())) { - video_metadata_use = true; - } - - DesktopSize video_size_prev = video_size_; - if (video_metadata_use) { - video_size_ = - DesktopSize(video_metadata_size->width, video_metadata_size->height); - } else { - video_size_ = desktop_size_; - } - - webrtc::MutexLock lock(¤t_frame_lock_); - if (!current_frame_ || !video_size_.equals(video_size_prev)) { - current_frame_ = std::make_unique( - video_size_.width() * video_size_.height() * kBytesPerPixel); - } - - const int32_t dst_stride = video_size_.width() * kBytesPerPixel; - const int32_t src_stride = spaBuffer->datas[0].chunk->stride; - - if (src_stride != (desktop_size_.width() * kBytesPerPixel)) { - RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: " - << src_stride - << " != " << (desktop_size_.width() * kBytesPerPixel); - portal_init_failed_ = true; - - return; - } - - // Adjust source content based on metadata video position - if (video_metadata_use && - (video_metadata->region.position.y + video_size_.height() <= - desktop_size_.height())) { - src += src_stride * video_metadata->region.position.y; - } - const int x_offset = - video_metadata_use && - (video_metadata->region.position.x + video_size_.width() <= - desktop_size_.width()) - ? video_metadata->region.position.x * kBytesPerPixel - : 0; - - uint8_t* dst = current_frame_.get(); - for (int i = 0; i < video_size_.height(); ++i) { - // Adjust source content based on crop video position if needed - src += x_offset; - std::memcpy(dst, src, dst_stride); - // If both sides decided to go with the RGBx format we need to convert it to - // BGRx to match color format expected by WebRTC. - if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx || - spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { - ConvertRGBxToBGRx(dst, dst_stride); - } - src += src_stride - x_offset; - dst += dst_stride; - } -} - -void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) { - // Change color format for KDE KWin which uses RGBx and not BGRx - for (uint32_t i = 0; i < size; i += 4) { - uint8_t tempR = frame[i]; - uint8_t tempB = frame[i + 2]; - frame[i] = tempB; - frame[i + 2] = tempR; - } -} - -guint BaseCapturerPipeWire::SetupRequestResponseSignal( - const gchar* object_path, - GDBusSignalCallback callback) { - return g_dbus_connection_signal_subscribe( - connection_, kDesktopBusName, kRequestInterfaceName, "Response", - object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, - callback, this, /*user_data_free_func=*/nullptr); -} - -// static -void BaseCapturerPipeWire::OnProxyRequested(GObject* /*object*/, - GAsyncResult* result, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); - if (!proxy) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " - << error->message; - that->portal_init_failed_ = true; - return; - } - that->proxy_ = proxy; - that->connection_ = g_dbus_proxy_get_connection(that->proxy_); - - RTC_LOG(LS_INFO) << "Created proxy for the screen cast portal."; - that->SessionRequest(); -} - -// static -gchar* BaseCapturerPipeWire::PrepareSignalHandle(GDBusConnection* connection, - const gchar* token) { - Scoped sender( - g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); - for (int i = 0; sender.get()[i]; i++) { - if (sender.get()[i] == '.') { - sender.get()[i] = '_'; - } - } - - gchar* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), "/", - token, /*end of varargs*/ nullptr); - - return handle; -} - -void BaseCapturerPipeWire::SessionRequest() { - GVariantBuilder builder; - Scoped variant_string; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - variant_string = - g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "session_handle_token", - g_variant_new_string(variant_string.get())); - variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "handle_token", - g_variant_new_string(variant_string.get())); - - portal_handle_ = PrepareSignalHandle(connection_, variant_string.get()); - session_request_signal_id_ = SetupRequestResponseSignal( - portal_handle_, OnSessionRequestResponseSignal); - - RTC_LOG(LS_INFO) << "Screen cast session requested."; - g_dbus_proxy_call( - proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, - reinterpret_cast(OnSessionRequested), this); -} - -// static -void BaseCapturerPipeWire::OnSessionRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped variant( - g_dbus_proxy_call_finish(proxy, result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: " - << error->message; - that->portal_init_failed_ = true; - return; - } - RTC_LOG(LS_INFO) << "Initializing the screen cast session."; - - Scoped handle; - g_variant_get_child(variant.get(), 0, "o", &handle); - if (!handle) { - RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; - if (that->session_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(that->connection_, - that->session_request_signal_id_); - that->session_request_signal_id_ = 0; - } - that->portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "Subscribing to the screen cast session."; -} - -// static -void BaseCapturerPipeWire::OnSessionRequestResponseSignal( - GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - RTC_LOG(LS_INFO) - << "Received response for the screen cast session subscription."; - - guint32 portal_response; - Scoped response_data; - g_variant_get(parameters, "(u@a{sv})", &portal_response, - response_data.receive()); - g_variant_lookup(response_data.get(), "session_handle", "s", - &that->session_handle_); - - if (!that->session_handle_ || portal_response) { - RTC_LOG(LS_ERROR) - << "Failed to request the screen cast session subscription."; - that->portal_init_failed_ = true; - return; - } - - that->SourcesRequest(); -} - -void BaseCapturerPipeWire::SourcesRequest() { - GVariantBuilder builder; - Scoped variant_string; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - // We want to record monitor content. - g_variant_builder_add( - &builder, "{sv}", "types", - g_variant_new_uint32(static_cast(capture_source_type_))); - // We don't want to allow selection of multiple sources. - g_variant_builder_add(&builder, "{sv}", "multiple", - g_variant_new_boolean(false)); - - Scoped variant( - g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes")); - if (variant.get()) { - uint32_t modes = 0; - g_variant_get(variant.get(), "u", &modes); - // Request mouse cursor to be embedded as part of the stream, otherwise it - // is hidden by default. Make request only if this mode is advertised by - // the portal implementation. - if (modes & - static_cast(BaseCapturerPipeWire::CursorMode::kEmbedded)) { - g_variant_builder_add(&builder, "{sv}", "cursor_mode", - g_variant_new_uint32(static_cast( - BaseCapturerPipeWire::CursorMode::kEmbedded))); - } - } - - variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "handle_token", - g_variant_new_string(variant_string.get())); - - sources_handle_ = PrepareSignalHandle(connection_, variant_string.get()); - sources_request_signal_id_ = SetupRequestResponseSignal( - sources_handle_, OnSourcesRequestResponseSignal); - - RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session."; - g_dbus_proxy_call( - proxy_, "SelectSources", - g_variant_new("(oa{sv})", session_handle_, &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, - reinterpret_cast(OnSourcesRequested), this); -} - -// static -void BaseCapturerPipeWire::OnSourcesRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped variant( - g_dbus_proxy_call_finish(proxy, result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message; - that->portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "Sources requested from the screen cast session."; - - Scoped handle; - g_variant_get_child(variant.get(), 0, "o", handle.receive()); - if (!handle) { - RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; - if (that->sources_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(that->connection_, - that->sources_request_signal_id_); - that->sources_request_signal_id_ = 0; - } - that->portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "Subscribed to sources signal."; -} - -// static -void BaseCapturerPipeWire::OnSourcesRequestResponseSignal( - GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - RTC_LOG(LS_INFO) << "Received sources signal from session."; - - guint32 portal_response; - g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); - if (portal_response) { - RTC_LOG(LS_ERROR) - << "Failed to select sources for the screen cast session."; - that->portal_init_failed_ = true; - return; - } - - that->StartRequest(); -} - -void BaseCapturerPipeWire::StartRequest() { - GVariantBuilder builder; - Scoped variant_string; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "handle_token", - g_variant_new_string(variant_string.get())); - - start_handle_ = PrepareSignalHandle(connection_, variant_string.get()); - start_request_signal_id_ = - SetupRequestResponseSignal(start_handle_, OnStartRequestResponseSignal); - - // "Identifier for the application window", this is Wayland, so not "x11:...". - const gchar parent_window[] = ""; - - RTC_LOG(LS_INFO) << "Starting the screen cast session."; - g_dbus_proxy_call( - proxy_, "Start", - g_variant_new("(osa{sv})", session_handle_, parent_window, &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, - reinterpret_cast(OnStartRequested), this); -} - -// static -void BaseCapturerPipeWire::OnStartRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped variant( - g_dbus_proxy_call_finish(proxy, result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: " - << error->message; - that->portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "Initializing the start of the screen cast session."; - - Scoped handle; - g_variant_get_child(variant.get(), 0, "o", handle.receive()); - if (!handle) { - RTC_LOG(LS_ERROR) - << "Failed to initialize the start of the screen cast session."; - if (that->start_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(that->connection_, - that->start_request_signal_id_); - that->start_request_signal_id_ = 0; - } - that->portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "Subscribed to the start signal."; -} - -// static -void BaseCapturerPipeWire::OnStartRequestResponseSignal( - GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - RTC_LOG(LS_INFO) << "Start signal received."; - guint32 portal_response; - Scoped response_data; - Scoped iter; - g_variant_get(parameters, "(u@a{sv})", &portal_response, - response_data.receive()); - if (portal_response || !response_data) { - RTC_LOG(LS_ERROR) << "Failed to start the screen cast session."; - that->portal_init_failed_ = true; - return; - } - - // Array of PipeWire streams. See - // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml - // documentation for . - if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})", - iter.receive())) { - Scoped variant; - - while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) { - guint32 stream_id; - guint32 type; - Scoped options; - - g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive()); - RTC_DCHECK(options.get()); - - if (g_variant_lookup(options.get(), "source_type", "u", &type)) { - that->capture_source_type_ = - static_cast(type); - } - - that->pw_stream_node_id_ = stream_id; - - break; - } - } - - that->OpenPipeWireRemote(); -} - -void BaseCapturerPipeWire::OpenPipeWireRemote() { - GVariantBuilder builder; - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - - RTC_LOG(LS_INFO) << "Opening the PipeWire remote."; - - g_dbus_proxy_call_with_unix_fd_list( - proxy_, "OpenPipeWireRemote", - g_variant_new("(oa{sv})", session_handle_, &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, - cancellable_, - reinterpret_cast(OnOpenPipeWireRemoteRequested), - this); -} - -// static -void BaseCapturerPipeWire::OnOpenPipeWireRemoteRequested( - GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data) { - BaseCapturerPipeWire* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped outlist; - Scoped variant(g_dbus_proxy_call_with_unix_fd_list_finish( - proxy, outlist.receive(), result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: " - << error->message; - that->portal_init_failed_ = true; - return; - } - - gint32 index; - g_variant_get(variant.get(), "(h)", &index); - - if ((that->pw_fd_ = - g_unix_fd_list_get(outlist.get(), index, error.receive())) == -1) { - RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " - << error->message; - that->portal_init_failed_ = true; - return; - } - - that->InitPipeWire(); -} - -void BaseCapturerPipeWire::Start(Callback* callback) { - RTC_DCHECK(!callback_); - RTC_DCHECK(callback); - - InitPortal(); - - callback_ = callback; -} - -void BaseCapturerPipeWire::CaptureFrame() { - if (portal_init_failed_) { - callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); - return; - } - - webrtc::MutexLock lock(¤t_frame_lock_); - if (!current_frame_) { - callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); - return; - } - - DesktopSize frame_size = video_size_; - - std::unique_ptr result(new BasicDesktopFrame(frame_size)); - result->CopyPixelsFrom( - current_frame_.get(), (frame_size.width() * kBytesPerPixel), - DesktopRect::MakeWH(frame_size.width(), frame_size.height())); - if (!result) { - callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); - return; - } - - // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on the - // frame, see ScreenCapturerX11::CaptureFrame. - - callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); -} - -bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) { - RTC_DCHECK(sources->size() == 0); - // List of available screens is already presented by the xdg-desktop-portal. - // But we have to add an empty source as the code expects it. - sources->push_back({0}); - return true; -} - -bool BaseCapturerPipeWire::SelectSource(SourceId id) { - // Screen selection is handled by the xdg-desktop-portal. - return true; -} - -// static -std::unique_ptr BaseCapturerPipeWire::CreateRawCapturer( - const DesktopCaptureOptions& options) { - return std::make_unique( - BaseCapturerPipeWire::CaptureSourceType::kAny); -} - -} // namespace webrtc diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.h b/modules/desktop_capture/linux/base_capturer_pipewire.h deleted file mode 100644 index 7ec5ea6950..0000000000 --- a/modules/desktop_capture/linux/base_capturer_pipewire.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_ -#include -#define typeof __typeof__ -#include -#include -#include - -#include "absl/types/optional.h" -#include "modules/desktop_capture/desktop_capture_options.h" -#include "modules/desktop_capture/desktop_capturer.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/synchronization/mutex.h" - -namespace webrtc { - -class BaseCapturerPipeWire : public DesktopCapturer { - public: - // Values are set based on source type property in - // xdg-desktop-portal/screencast - // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml - enum class CaptureSourceType : uint32_t { - kScreen = 0b01, - kWindow = 0b10, - kAny = 0b11 - }; - - enum class CursorMode : uint32_t { - kHidden = 0b01, - kEmbedded = 0b10, - kMetadata = 0b100 - }; - - explicit BaseCapturerPipeWire(CaptureSourceType source_type); - ~BaseCapturerPipeWire() override; - - static std::unique_ptr CreateRawCapturer( - const DesktopCaptureOptions& options); - - // DesktopCapturer interface. - void Start(Callback* delegate) override; - void CaptureFrame() override; - bool GetSourceList(SourceList* sources) override; - bool SelectSource(SourceId id) override; - - private: - // PipeWire types --> - struct pw_context* pw_context_ = nullptr; - struct pw_core* pw_core_ = nullptr; - struct pw_stream* pw_stream_ = nullptr; - struct pw_thread_loop* pw_main_loop_ = nullptr; - - spa_hook spa_core_listener_; - spa_hook spa_stream_listener_; - - // event handlers - pw_core_events pw_core_events_ = {}; - pw_stream_events pw_stream_events_ = {}; - - struct spa_video_info_raw spa_video_format_; - - guint32 pw_stream_node_id_ = 0; - gint32 pw_fd_ = -1; - - CaptureSourceType capture_source_type_ = - BaseCapturerPipeWire::CaptureSourceType::kScreen; - - // <-- end of PipeWire types - - GDBusConnection* connection_ = nullptr; - GDBusProxy* proxy_ = nullptr; - GCancellable *cancellable_ = nullptr; - gchar* portal_handle_ = nullptr; - gchar* session_handle_ = nullptr; - gchar* sources_handle_ = nullptr; - gchar* start_handle_ = nullptr; - guint session_request_signal_id_ = 0; - guint sources_request_signal_id_ = 0; - guint start_request_signal_id_ = 0; - - DesktopSize video_size_; - DesktopSize desktop_size_ = {}; - DesktopCaptureOptions options_ = {}; - - webrtc::Mutex current_frame_lock_; - std::unique_ptr current_frame_; - Callback* callback_ = nullptr; - - bool portal_init_failed_ = false; - - void InitPortal(); - void InitPipeWire(); - void InitPipeWireTypes(); - - pw_stream* CreateReceivingStream(); - void HandleBuffer(pw_buffer* buffer); - - void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); - - static void OnCoreError(void* data, - uint32_t id, - int seq, - int res, - const char* message); - static void OnStreamParamChanged(void* data, - uint32_t id, - const struct spa_pod* format); - static void OnStreamStateChanged(void* data, - pw_stream_state old_state, - pw_stream_state state, - const char* error_message); - - static void OnStreamProcess(void* data); - static void OnNewBuffer(void* data, uint32_t id); - - guint SetupRequestResponseSignal(const gchar* object_path, - GDBusSignalCallback callback); - - static void OnProxyRequested(GObject* object, - GAsyncResult* result, - gpointer user_data); - - static gchar* PrepareSignalHandle(GDBusConnection* connection, - const gchar* token); - - void SessionRequest(); - static void OnSessionRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data); - static void OnSessionRequestResponseSignal(GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data); - - void SourcesRequest(); - static void OnSourcesRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data); - static void OnSourcesRequestResponseSignal(GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data); - - void StartRequest(); - static void OnStartRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data); - static void OnStartRequestResponseSignal(GDBusConnection* connection, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer user_data); - - void OpenPipeWireRemote(); - static void OnOpenPipeWireRemoteRequested(GDBusProxy *proxy, - GAsyncResult* result, - gpointer user_data); - - RTC_DISALLOW_COPY_AND_ASSIGN(BaseCapturerPipeWire); -}; - -} // namespace webrtc - -#endif // MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_ diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc new file mode 100644 index 0000000000..ae26570a7c --- /dev/null +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc @@ -0,0 +1,100 @@ +/* + * 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 "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h" + +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +namespace { + +using xdg_portal::RequestResponse; + +} // namespace + +BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options) + : options_(options) { + screencast_portal_ = std::make_unique( + ScreenCastPortal::CaptureSourceType::kAnyScreenContent, this); +} + +BaseCapturerPipeWire::~BaseCapturerPipeWire() {} + +void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result, + uint32_t stream_node_id, + int fd) { + if (result != RequestResponse::kSuccess || + !options_.screencast_stream()->StartScreenCastStream(stream_node_id, + fd)) { + capturer_failed_ = true; + RTC_LOG(LS_ERROR) << "ScreenCastPortal failed: " + << static_cast(result); + } +} + +void BaseCapturerPipeWire::OnScreenCastSessionClosed() { + if (!capturer_failed_) { + options_.screencast_stream()->StopScreenCastStream(); + } +} + +void BaseCapturerPipeWire::Start(Callback* callback) { + RTC_DCHECK(!callback_); + RTC_DCHECK(callback); + + callback_ = callback; + + screencast_portal_->Start(); +} + +void BaseCapturerPipeWire::CaptureFrame() { + if (capturer_failed_) { + callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); + return; + } + + std::unique_ptr frame = + options_.screencast_stream()->CaptureFrame(); + + if (!frame || !frame->data()) { + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + + // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on + // the frame, see ScreenCapturerX11::CaptureFrame. + + callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); +} + +bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) { + RTC_DCHECK(sources->size() == 0); + // List of available screens is already presented by the xdg-desktop-portal, + // so we just need a (valid) source id for any callers to pass around, even + // though it doesn't mean anything to us. Until the user selects a source in + // xdg-desktop-portal we'll just end up returning empty frames. Note that "0" + // is often treated as a null/placeholder id, so we shouldn't use that. + // TODO(https://crbug.com/1297671): Reconsider type of ID when plumbing + // token that will enable stream re-use. + sources->push_back({1}); + return true; +} + +bool BaseCapturerPipeWire::SelectSource(SourceId id) { + // Screen selection is handled by the xdg-desktop-portal. + return true; +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h new file mode 100644 index 0000000000..e9d67d04be --- /dev/null +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h @@ -0,0 +1,52 @@ +/* + * 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 MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_ + +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/linux/wayland/screencast_portal.h" +#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" + +namespace webrtc { + +class BaseCapturerPipeWire : public DesktopCapturer, + public ScreenCastPortal::PortalNotifier { + public: + BaseCapturerPipeWire(const DesktopCaptureOptions& options); + ~BaseCapturerPipeWire() override; + + BaseCapturerPipeWire(const BaseCapturerPipeWire&) = delete; + BaseCapturerPipeWire& operator=(const BaseCapturerPipeWire&) = delete; + + // DesktopCapturer interface. + void Start(Callback* delegate) override; + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + + // ScreenCastPortal::PortalNotifier interface. + void OnScreenCastRequestResult(xdg_portal::RequestResponse result, + uint32_t stream_node_id, + int fd) override; + void OnScreenCastSessionClosed() override; + + private: + DesktopCaptureOptions options_ = {}; + Callback* callback_ = nullptr; + bool capturer_failed_ = false; + std::unique_ptr screencast_portal_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_ diff --git a/modules/desktop_capture/linux/wayland/drm.sigs b/modules/desktop_capture/linux/wayland/drm.sigs new file mode 100644 index 0000000000..226979fe16 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/drm.sigs @@ -0,0 +1,11 @@ +// 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. + +//------------------------------------------------ +// Functions from DRM used in capturer code. +//-------- + +// xf86drm.h +int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); +void drmFreeDevices(drmDevicePtr devices[], int count); diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc new file mode 100644 index 0000000000..1d364da036 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc @@ -0,0 +1,703 @@ +/* + * 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. + */ + +#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/types/optional.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/sanitizer.h" +#include "rtc_base/string_encode.h" + +namespace webrtc { + +// EGL +typedef EGLBoolean (*eglBindAPI_func)(EGLenum api); +typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy, + EGLConfig config, + EGLContext share_context, + const EGLint* attrib_list); +typedef EGLBoolean (*eglDestroyContext_func)(EGLDisplay display, + EGLContext context); +typedef EGLBoolean (*eglTerminate_func)(EGLDisplay display); +typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy, + EGLContext ctx, + EGLenum target, + EGLClientBuffer buffer, + const EGLint* attrib_list); +typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy, + EGLImageKHR image); +typedef EGLint (*eglGetError_func)(void); +typedef void* (*eglGetProcAddress_func)(const char*); +typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform, + void* native_display, + const EGLint* attrib_list); +typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform, + void* native_display, + const EGLAttrib* attrib_list); + +typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy, + EGLint* major, + EGLint* minor); +typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy, + EGLSurface draw, + EGLSurface read, + EGLContext ctx); +typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, + EGLint max_formats, + EGLint* formats, + EGLint* num_formats); +typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, + EGLint format, + EGLint max_modifiers, + EGLuint64KHR* modifiers, + EGLBoolean* external_only, + EGLint* num_modifiers); +typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name); +typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target, + GLeglImageOES image); + +// This doesn't follow naming conventions in WebRTC, where the naming +// should look like e.g. egl_bind_api instead of EglBindAPI, however +// we named them according to the exported functions they map to for +// consistency. +eglBindAPI_func EglBindAPI = nullptr; +eglCreateContext_func EglCreateContext = nullptr; +eglDestroyContext_func EglDestroyContext = nullptr; +eglTerminate_func EglTerminate = nullptr; +eglCreateImageKHR_func EglCreateImageKHR = nullptr; +eglDestroyImageKHR_func EglDestroyImageKHR = nullptr; +eglGetError_func EglGetError = nullptr; +eglGetProcAddress_func EglGetProcAddress = nullptr; +eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr; +eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr; +eglInitialize_func EglInitialize = nullptr; +eglMakeCurrent_func EglMakeCurrent = nullptr; +eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr; +eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr; +eglQueryString_func EglQueryString = nullptr; +glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr; + +// GL +typedef void (*glBindTexture_func)(GLenum target, GLuint texture); +typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures); +typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures); +typedef GLenum (*glGetError_func)(void); +typedef const GLubyte* (*glGetString_func)(GLenum name); +typedef void (*glGetTexImage_func)(GLenum target, + GLint level, + GLenum format, + GLenum type, + void* pixels); +typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param); +typedef void* (*glXGetProcAddressARB_func)(const char*); + +// This doesn't follow naming conventions in WebRTC, where the naming +// should look like e.g. egl_bind_api instead of EglBindAPI, however +// we named them according to the exported functions they map to for +// consistency. +glBindTexture_func GlBindTexture = nullptr; +glDeleteTextures_func GlDeleteTextures = nullptr; +glGenTextures_func GlGenTextures = nullptr; +glGetError_func GlGetError = nullptr; +glGetString_func GlGetString = nullptr; +glGetTexImage_func GlGetTexImage = nullptr; +glTexParameteri_func GlTexParameteri = nullptr; +glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr; + +static const std::string FormatGLError(GLenum err) { + switch (err) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "GL error code: " + std::to_string(err); + } +} + +static const std::string FormatEGLError(EGLint err) { + switch (err) { + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "EGL error code: " + std::to_string(err); + } +} + +static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) { + switch (spa_format) { + case SPA_VIDEO_FORMAT_RGBA: + return DRM_FORMAT_ABGR8888; + case SPA_VIDEO_FORMAT_RGBx: + return DRM_FORMAT_XBGR8888; + case SPA_VIDEO_FORMAT_BGRA: + return DRM_FORMAT_ARGB8888; + case SPA_VIDEO_FORMAT_BGRx: + return DRM_FORMAT_XRGB8888; + default: + return DRM_FORMAT_INVALID; + } +} + +static void CloseLibrary(void* library) { + if (library) { + dlclose(library); + library = nullptr; + } +} + +static void* g_lib_egl = nullptr; + +RTC_NO_SANITIZE("cfi-icall") +static bool OpenEGL() { + g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL); + if (g_lib_egl) { + EglGetProcAddress = + (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress"); + return EglGetProcAddress; + } + + return false; +} + +RTC_NO_SANITIZE("cfi-icall") +static bool LoadEGL() { + if (OpenEGL()) { + EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI"); + EglCreateContext = + (eglCreateContext_func)EglGetProcAddress("eglCreateContext"); + EglDestroyContext = + (eglDestroyContext_func)EglGetProcAddress("eglDestroyContext"); + EglTerminate = (eglTerminate_func)EglGetProcAddress("eglTerminate"); + EglCreateImageKHR = + (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR"); + EglDestroyImageKHR = + (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR"); + EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError"); + EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress( + "eglGetPlatformDisplayEXT"); + EglGetPlatformDisplay = + (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay"); + EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize"); + EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent"); + EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString"); + GlEGLImageTargetTexture2DOES = + (glEGLImageTargetTexture2DOES_func)EglGetProcAddress( + "glEGLImageTargetTexture2DOES"); + + return EglBindAPI && EglCreateContext && EglCreateImageKHR && + EglTerminate && EglDestroyContext && EglDestroyImageKHR && + EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay && + EglInitialize && EglMakeCurrent && EglQueryString && + GlEGLImageTargetTexture2DOES; + } + + return false; +} + +static void* g_lib_gl = nullptr; + +RTC_NO_SANITIZE("cfi-icall") +static bool OpenGL() { + std::vector names = {"libGL.so.1", "libGL.so"}; + for (const std::string& name : names) { + g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); + if (g_lib_gl) { + GlXGetProcAddressARB = + (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB"); + return GlXGetProcAddressARB; + } + } + + return false; +} + +RTC_NO_SANITIZE("cfi-icall") +static bool LoadGL() { + if (OpenGL()) { + GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString"); + if (!GlGetString) { + return false; + } + + GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture"); + GlDeleteTextures = + (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures"); + GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures"); + GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError"); + GlGetTexImage = (glGetTexImage_func)GlXGetProcAddressARB("glGetTexImage"); + GlTexParameteri = + (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri"); + + return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError && + GlGetTexImage && GlTexParameteri; + } + + return false; +} + +RTC_NO_SANITIZE("cfi-icall") +EglDmaBuf::EglDmaBuf() { + if (!LoadEGL()) { + RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions."; + CloseLibrary(g_lib_egl); + return; + } + + if (!LoadGL()) { + RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions."; + CloseLibrary(g_lib_gl); + return; + } + + if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) { + return; + } + + bool has_platform_base_ext = false; + bool has_platform_gbm_ext = false; + bool has_khr_platform_gbm_ext = false; + + for (const auto& extension : egl_.extensions) { + if (extension == "EGL_EXT_platform_base") { + has_platform_base_ext = true; + continue; + } else if (extension == "EGL_MESA_platform_gbm") { + has_platform_gbm_ext = true; + continue; + } else if (extension == "EGL_KHR_platform_gbm") { + has_khr_platform_gbm_ext = true; + continue; + } + } + + if (!has_platform_base_ext || !has_platform_gbm_ext || + !has_khr_platform_gbm_ext) { + RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing"; + return; + } + + egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, + (void*)EGL_DEFAULT_DISPLAY, nullptr); + + if (egl_.display == EGL_NO_DISPLAY) { + RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: " + << FormatEGLError(EglGetError()) << "\n" + << "Defaulting to using first available render node"; + absl::optional render_node = GetRenderNode(); + if (!render_node) { + return; + } + + drm_fd_ = open(render_node->c_str(), O_RDWR); + + if (drm_fd_ < 0) { + RTC_LOG(LS_ERROR) << "Failed to open drm render node: " + << strerror(errno); + return; + } + + gbm_device_ = gbm_create_device(drm_fd_); + + if (!gbm_device_) { + RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno); + close(drm_fd_); + return; + } + + // Use eglGetPlatformDisplayEXT() to get the display pointer + // if the implementation supports it. + egl_.display = + EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr); + } + + if (egl_.display == EGL_NO_DISPLAY) { + RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: " + << FormatEGLError(EglGetError()); + return; + } + + EGLint major, minor; + if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) { + RTC_LOG(LS_ERROR) << "Error during eglInitialize: " + << FormatEGLError(EglGetError()); + return; + } + + if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { + RTC_LOG(LS_ERROR) << "bind OpenGL API failed"; + return; + } + + egl_.context = + EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr); + + if (egl_.context == EGL_NO_CONTEXT) { + RTC_LOG(LS_ERROR) << "Couldn't create EGL context: " + << FormatGLError(EglGetError()); + return; + } + + if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) { + return; + } + + bool has_image_dma_buf_import_modifiers_ext = false; + + for (const auto& extension : egl_.extensions) { + if (extension == "EGL_EXT_image_dma_buf_import") { + has_image_dma_buf_import_ext_ = true; + continue; + } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") { + has_image_dma_buf_import_modifiers_ext = true; + continue; + } + } + + if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) { + EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress( + "eglQueryDmaBufFormatsEXT"); + EglQueryDmaBufModifiersEXT = + (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress( + "eglQueryDmaBufModifiersEXT"); + } + + RTC_LOG(LS_INFO) << "Egl initialization succeeded"; + egl_initialized_ = true; +} + +RTC_NO_SANITIZE("cfi-icall") +EglDmaBuf::~EglDmaBuf() { + if (gbm_device_) { + gbm_device_destroy(gbm_device_); + close(drm_fd_); + } + + if (egl_.context != EGL_NO_CONTEXT) { + EglDestroyContext(egl_.display, egl_.context); + } + + if (egl_.display != EGL_NO_DISPLAY) { + EglTerminate(egl_.display); + } + + // BUG: crbug.com/1290566 + // Closing libEGL.so.1 when using NVidia drivers causes a crash + // when EglGetPlatformDisplayEXT() is used, at least this one is enough + // to be called to make it crash. + // It also looks that libepoxy and glad don't dlclose it either + // CloseLibrary(g_lib_egl); + // CloseLibrary(g_lib_gl); +} + +RTC_NO_SANITIZE("cfi-icall") +bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) { + // Get the list of client extensions + const char* client_extensions_cstring = EglQueryString(dpy, name); + if (!client_extensions_cstring) { + // If eglQueryString() returned NULL, the implementation doesn't support + // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. + RTC_LOG(LS_ERROR) << "No client extensions defined! " + << FormatEGLError(EglGetError()); + return false; + } + + std::vector client_extensions = + rtc::split(client_extensions_cstring, ' '); + for (const auto& extension : client_extensions) { + egl_.extensions.push_back(std::string(extension)); + } + + return true; +} + +RTC_NO_SANITIZE("cfi-icall") +std::unique_ptr EglDmaBuf::ImageFromDmaBuf( + const DesktopSize& size, + uint32_t format, + const std::vector& plane_datas, + uint64_t modifier) { + std::unique_ptr src; + + if (!egl_initialized_) { + return src; + } + + if (plane_datas.size() <= 0) { + RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes"; + return src; + } + + EGLint attribs[47]; + int atti = 0; + + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = static_cast(size.width()); + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = static_cast(size.height()); + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = SpaPixelFormatToDrmFormat(format); + + if (plane_datas.size() > 0) { + attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attribs[atti++] = plane_datas[0].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attribs[atti++] = plane_datas[0].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attribs[atti++] = plane_datas[0].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + if (plane_datas.size() > 1) { + attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; + attribs[atti++] = plane_datas[1].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; + attribs[atti++] = plane_datas[1].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; + attribs[atti++] = plane_datas[1].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + if (plane_datas.size() > 2) { + attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; + attribs[atti++] = plane_datas[2].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; + attribs[atti++] = plane_datas[2].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; + attribs[atti++] = plane_datas[2].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + if (plane_datas.size() > 3) { + attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT; + attribs[atti++] = plane_datas[3].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; + attribs[atti++] = plane_datas[3].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; + attribs[atti++] = plane_datas[3].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + attribs[atti++] = EGL_NONE; + + // bind context to render thread + EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context); + + // create EGL image from attribute list + EGLImageKHR image = EglCreateImageKHR( + egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs); + + if (image == EGL_NO_IMAGE) { + RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - " + << FormatEGLError(EglGetError()); + return src; + } + + // create GL 2D texture for framebuffer + GLuint texture; + GlGenTextures(1, &texture); + GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + GlBindTexture(GL_TEXTURE_2D, texture); + GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + + src = std::make_unique(plane_datas[0].stride * size.height()); + + GLenum gl_format = GL_BGRA; + switch (format) { + case SPA_VIDEO_FORMAT_RGBx: + gl_format = GL_RGBA; + break; + case SPA_VIDEO_FORMAT_RGBA: + gl_format = GL_RGBA; + break; + case SPA_VIDEO_FORMAT_BGRx: + gl_format = GL_BGRA; + break; + default: + gl_format = GL_BGRA; + break; + } + GlGetTexImage(GL_TEXTURE_2D, 0, gl_format, GL_UNSIGNED_BYTE, src.get()); + + if (GlGetError()) { + RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer."; + return src; + } + + GlDeleteTextures(1, &texture); + EglDestroyImageKHR(egl_.display, image); + + return src; +} + +RTC_NO_SANITIZE("cfi-icall") +std::vector EglDmaBuf::QueryDmaBufModifiers(uint32_t format) { + if (!egl_initialized_) { + return {}; + } + + // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we + // can still use modifier-less DMA-BUFs if we have required extension + if (EglQueryDmaBufFormatsEXT == nullptr || + EglQueryDmaBufModifiersEXT == nullptr) { + return has_image_dma_buf_import_ext_ + ? std::vector{DRM_FORMAT_MOD_INVALID} + : std::vector{}; + } + + uint32_t drm_format = SpaPixelFormatToDrmFormat(format); + // Should never happen as it's us who controls the list of supported formats + RTC_DCHECK(drm_format != DRM_FORMAT_INVALID); + + EGLint count = 0; + EGLBoolean success = + EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count); + + if (!success || !count) { + RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats."; + return {DRM_FORMAT_MOD_INVALID}; + } + + std::vector formats(count); + if (!EglQueryDmaBufFormatsEXT(egl_.display, count, + reinterpret_cast(formats.data()), + &count)) { + RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats."; + return {DRM_FORMAT_MOD_INVALID}; + } + + if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) { + RTC_LOG(LS_ERROR) << "Format " << drm_format + << " not supported for modifiers."; + return {DRM_FORMAT_MOD_INVALID}; + } + + success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr, + nullptr, &count); + + if (!success || !count) { + RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers."; + return {DRM_FORMAT_MOD_INVALID}; + } + + std::vector modifiers(count); + if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count, + modifiers.data(), nullptr, &count)) { + RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers."; + } + + // Support modifier-less buffers + modifiers.push_back(DRM_FORMAT_MOD_INVALID); + return modifiers; +} + +absl::optional EglDmaBuf::GetRenderNode() { + int max_devices = drmGetDevices2(0, nullptr, 0); + if (max_devices <= 0) { + RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno=" + << -max_devices << ")"; + return absl::nullopt; + } + + std::vector devices(max_devices); + int ret = drmGetDevices2(0, devices.data(), max_devices); + if (ret < 0) { + RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret; + return absl::nullopt; + } + + std::string render_node; + + for (const drmDevicePtr& device : devices) { + if (device->available_nodes & (1 << DRM_NODE_RENDER)) { + render_node = device->nodes[DRM_NODE_RENDER]; + break; + } + } + + drmFreeDevices(devices.data(), ret); + return render_node; +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.h b/modules/desktop_capture/linux/wayland/egl_dmabuf.h new file mode 100644 index 0000000000..f1d96b2f80 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.h @@ -0,0 +1,68 @@ +/* + * 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 MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_ + +#include +#include +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "modules/desktop_capture/desktop_geometry.h" + +namespace webrtc { + +class EglDmaBuf { + public: + struct EGLStruct { + std::vector extensions; + EGLDisplay display = EGL_NO_DISPLAY; + EGLContext context = EGL_NO_CONTEXT; + }; + + struct PlaneData { + int32_t fd; + uint32_t stride; + uint32_t offset; + }; + + EglDmaBuf(); + ~EglDmaBuf(); + + std::unique_ptr ImageFromDmaBuf( + const DesktopSize& size, + uint32_t format, + const std::vector& plane_datas, + uint64_t modifiers); + std::vector QueryDmaBufModifiers(uint32_t format); + + bool IsEglInitialized() const { return egl_initialized_; } + + private: + bool GetClientExtensions(EGLDisplay dpy, EGLint name); + + bool egl_initialized_ = false; + bool has_image_dma_buf_import_ext_ = false; + int32_t drm_fd_ = -1; // for GBM buffer mmap + gbm_device* gbm_device_ = nullptr; // for passed GBM buffer retrieval + + EGLStruct egl_; + + absl::optional GetRenderNode(); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_ diff --git a/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc new file mode 100644 index 0000000000..3d33b0fbb8 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc @@ -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. + */ + +#include "modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h" + +#include + +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capturer.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +MouseCursorMonitorPipeWire::MouseCursorMonitorPipeWire( + const DesktopCaptureOptions& options) + : options_(options) { + sequence_checker_.Detach(); +} + +MouseCursorMonitorPipeWire::~MouseCursorMonitorPipeWire() {} + +void MouseCursorMonitorPipeWire::Init(Callback* callback, Mode mode) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(!callback_); + RTC_DCHECK(callback); + + callback_ = callback; + mode_ = mode; +} + +void MouseCursorMonitorPipeWire::Capture() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(callback_); + + std::unique_ptr mouse_cursor = + options_.screencast_stream()->CaptureCursor(); + + if (mouse_cursor && mouse_cursor->image()->data()) { + callback_->OnMouseCursor(mouse_cursor.release()); + } + + if (mode_ == SHAPE_AND_POSITION) { + absl::optional mouse_cursor_position = + options_.screencast_stream()->CaptureCursorPosition(); + if (mouse_cursor_position) { + callback_->OnMouseCursorPosition(mouse_cursor_position.value()); + } + } +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h new file mode 100644 index 0000000000..da670bece9 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h @@ -0,0 +1,44 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ + +#include + +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capture_types.h" +#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" +#include "modules/desktop_capture/mouse_cursor.h" +#include "modules/desktop_capture/mouse_cursor_monitor.h" +#include "rtc_base/system/no_unique_address.h" + +namespace webrtc { + +class MouseCursorMonitorPipeWire : public MouseCursorMonitor { + public: + explicit MouseCursorMonitorPipeWire(const DesktopCaptureOptions& options); + ~MouseCursorMonitorPipeWire() override; + + // MouseCursorMonitor: + void Init(Callback* callback, Mode mode) override; + void Capture() override; + + DesktopCaptureOptions options_ RTC_GUARDED_BY(sequence_checker_); + Callback* callback_ RTC_GUARDED_BY(sequence_checker_) = nullptr; + Mode mode_ RTC_GUARDED_BY(sequence_checker_) = SHAPE_AND_POSITION; + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_ diff --git a/modules/desktop_capture/linux/pipewire03.sigs b/modules/desktop_capture/linux/wayland/pipewire.sigs similarity index 86% rename from modules/desktop_capture/linux/pipewire03.sigs rename to modules/desktop_capture/linux/wayland/pipewire.sigs index 78d241f40c..06a97b8f29 100644 --- a/modules/desktop_capture/linux/pipewire03.sigs +++ b/modules/desktop_capture/linux/wayland/pipewire.sigs @@ -16,6 +16,7 @@ pw_loop * pw_loop_new(const spa_dict *props); // pipewire.h void pw_init(int *argc, char **argv[]); +const char* pw_get_library_version(); // properties.h pw_properties * pw_properties_new_string(const char *args); @@ -23,6 +24,7 @@ pw_properties * pw_properties_new_string(const char *args); // stream.h void pw_stream_add_listener(pw_stream *stream, spa_hook *listener, const pw_stream_events *events, void *data); int pw_stream_connect(pw_stream *stream, enum pw_direction direction, uint32_t target_id, enum pw_stream_flags flags, const spa_pod **params, uint32_t n_params); +int pw_stream_disconnect(pw_stream *stream); pw_buffer *pw_stream_dequeue_buffer(pw_stream *stream); void pw_stream_destroy(pw_stream *stream); pw_stream * pw_stream_new(pw_core *core, const char *name, pw_properties *props); @@ -38,9 +40,11 @@ void pw_thread_loop_stop(pw_thread_loop *loop); void pw_thread_loop_lock(pw_thread_loop *loop); void pw_thread_loop_unlock(pw_thread_loop *loop); pw_loop * pw_thread_loop_get_loop(pw_thread_loop *loop); - +void pw_thread_loop_signal(pw_thread_loop *loop, bool wait_for_accept); +void pw_thread_loop_wait(pw_thread_loop *loop); // context.h void pw_context_destroy(pw_context *context); pw_context *pw_context_new(pw_loop *main_loop, pw_properties *props, size_t user_data_size); pw_core * pw_context_connect(pw_context *context, pw_properties *properties, size_t user_data_size); +pw_core * pw_context_connect_fd(pw_context *context, int fd, pw_properties *properties, size_t user_data_size); diff --git a/modules/desktop_capture/linux/pipewire_stub_header.fragment b/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment similarity index 87% rename from modules/desktop_capture/linux/pipewire_stub_header.fragment rename to modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment index 9d7dbd27c5..06ae18dfd4 100644 --- a/modules/desktop_capture/linux/pipewire_stub_header.fragment +++ b/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment @@ -5,4 +5,5 @@ extern "C" { #include +#include } diff --git a/modules/desktop_capture/linux/wayland/scoped_glib.cc b/modules/desktop_capture/linux/wayland/scoped_glib.cc new file mode 100644 index 0000000000..0d9a87d7fd --- /dev/null +++ b/modules/desktop_capture/linux/wayland/scoped_glib.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" + +namespace webrtc { + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_error_free(ptr_); + } +} + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_free(ptr_); + } +} + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_variant_unref(ptr_); + } +} + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_variant_iter_free(ptr_); + } +} + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_object_unref(ptr_); + } +} + +template <> +Scoped::~Scoped() { + if (ptr_) { + g_object_unref(ptr_); + } +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/scoped_glib.h b/modules/desktop_capture/linux/wayland/scoped_glib.h new file mode 100644 index 0000000000..908bd6f77d --- /dev/null +++ b/modules/desktop_capture/linux/wayland/scoped_glib.h @@ -0,0 +1,65 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_ + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +template +class Scoped { + public: + Scoped() {} + explicit Scoped(T* val) { ptr_ = val; } + ~Scoped() { RTC_DCHECK_NOTREACHED(); } + + T* operator->() const { return ptr_; } + + explicit operator bool() const { return ptr_ != nullptr; } + + bool operator!() const { return ptr_ == nullptr; } + + T* get() const { return ptr_; } + + T** receive() { + RTC_CHECK(!ptr_); + return &ptr_; + } + + Scoped& operator=(T* val) { + RTC_DCHECK(val); + ptr_ = val; + return *this; + } + + protected: + T* ptr_ = nullptr; +}; + +template <> +Scoped::~Scoped(); +template <> +Scoped::~Scoped(); +template <> +Scoped::~Scoped(); +template <> +Scoped::~Scoped(); +template <> +Scoped::~Scoped(); +template <> +Scoped::~Scoped(); + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_ diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.cc b/modules/desktop_capture/linux/wayland/screencast_portal.cc new file mode 100644 index 0000000000..0b9db6ffb0 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_portal.cc @@ -0,0 +1,369 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/linux/wayland/screencast_portal.h" + +#include +#include + +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +using xdg_portal::kScreenCastInterfaceName; +using xdg_portal::PrepareSignalHandle; +using xdg_portal::RequestResponse; +using xdg_portal::RequestSessionProxy; +using xdg_portal::RequestSessionUsingProxy; +using xdg_portal::SessionRequestHandler; +using xdg_portal::SessionRequestResponseSignalHelper; +using xdg_portal::SetupRequestResponseSignal; +using xdg_portal::SetupSessionRequestHandlers; +using xdg_portal::StartRequestedHandler; +using xdg_portal::StartSessionRequest; +using xdg_portal::TearDownSession; + +} // namespace + +ScreenCastPortal::ScreenCastPortal( + ScreenCastPortal::CaptureSourceType source_type, + PortalNotifier* notifier) + : notifier_(notifier), capture_source_type_(source_type) {} + +ScreenCastPortal::~ScreenCastPortal() { + UnsubscribeSignalHandlers(); + TearDownSession(std::move(session_handle_), proxy_, cancellable_, + connection_); + cancellable_ = nullptr; + proxy_ = nullptr; + + if (pw_fd_ != -1) { + close(pw_fd_); + } +} + +void ScreenCastPortal::UnsubscribeSignalHandlers() { + if (start_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); + start_request_signal_id_ = 0; + } + + if (sources_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, + sources_request_signal_id_); + sources_request_signal_id_ = 0; + } + + if (session_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, + session_request_signal_id_); + session_request_signal_id_ = 0; + } +} + +void ScreenCastPortal::Start() { + cancellable_ = g_cancellable_new(); + RequestSessionProxy(kScreenCastInterfaceName, OnProxyRequested, cancellable_, + this); +} + +void ScreenCastPortal::PortalFailed(RequestResponse result) { + notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_); +} + +// static +void ScreenCastPortal::OnProxyRequested(GObject* gobject, + GAsyncResult* result, + gpointer user_data) { + RequestSessionUsingProxy( + static_cast(user_data), gobject, result); +} + +void ScreenCastPortal::SessionRequest(GDBusProxy* proxy) { + proxy_ = proxy; + connection_ = g_dbus_proxy_get_connection(proxy_); + SetupSessionRequestHandlers( + "webrtc", OnSessionRequested, OnSessionRequestResponseSignal, connection_, + proxy_, cancellable_, portal_handle_, session_request_signal_id_, this); +} + +// static +void ScreenCastPortal::OnSessionRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + SessionRequestHandler(static_cast(user_data), proxy, + result, user_data); +} + +// static +void ScreenCastPortal::OnSessionRequestResponseSignal( + GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + SessionRequestResponseSignalHelper( + OnSessionClosedSignal, that, that->connection_, that->session_handle_, + parameters, that->session_closed_signal_id_); + that->SourcesRequest(); +} + +// static +void ScreenCastPortal::OnSessionClosedSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + + RTC_LOG(LS_INFO) << "Received closed signal from session."; + + that->notifier_->OnScreenCastSessionClosed(); + + // Unsubscribe from the signal and free the session handle to avoid calling + // Session::Close from the destructor since it's already closed + g_dbus_connection_signal_unsubscribe(that->connection_, + that->session_closed_signal_id_); +} + +void ScreenCastPortal::SourcesRequest() { + GVariantBuilder builder; + Scoped variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + // We want to record monitor content. + g_variant_builder_add( + &builder, "{sv}", "types", + g_variant_new_uint32(static_cast(capture_source_type_))); + // We don't want to allow selection of multiple sources. + g_variant_builder_add(&builder, "{sv}", "multiple", + g_variant_new_boolean(false)); + + Scoped variant( + g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes")); + if (variant.get()) { + uint32_t modes = 0; + g_variant_get(variant.get(), "u", &modes); + // Make request only if this mode is advertised by the portal + // implementation. + if (modes & static_cast(cursor_mode_)) { + g_variant_builder_add( + &builder, "{sv}", "cursor_mode", + g_variant_new_uint32(static_cast(cursor_mode_))); + } + } + + variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + sources_handle_ = PrepareSignalHandle(variant_string.get(), connection_); + sources_request_signal_id_ = SetupRequestResponseSignal( + sources_handle_.c_str(), OnSourcesRequestResponseSignal, this, + connection_); + + RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session."; + g_dbus_proxy_call( + proxy_, "SelectSources", + g_variant_new("(oa{sv})", session_handle_.c_str(), &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, + reinterpret_cast(OnSourcesRequested), this); +} + +// static +void ScreenCastPortal::OnSourcesRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + + Scoped error; + Scoped variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message; + that->PortalFailed(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Sources requested from the screen cast session."; + + Scoped handle; + g_variant_get_child(variant.get(), 0, "o", handle.receive()); + if (!handle) { + RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; + if (that->sources_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(that->connection_, + that->sources_request_signal_id_); + that->sources_request_signal_id_ = 0; + } + that->PortalFailed(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Subscribed to sources signal."; +} + +// static +void ScreenCastPortal::OnSourcesRequestResponseSignal( + GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + + RTC_LOG(LS_INFO) << "Received sources signal from session."; + + uint32_t portal_response; + g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); + if (portal_response) { + RTC_LOG(LS_ERROR) + << "Failed to select sources for the screen cast session."; + that->PortalFailed(RequestResponse::kError); + return; + } + + that->StartRequest(); +} + +void ScreenCastPortal::StartRequest() { + StartSessionRequest("webrtc", session_handle_, OnStartRequestResponseSignal, + OnStartRequested, proxy_, connection_, cancellable_, + start_request_signal_id_, start_handle_, this); +} + +// static +void ScreenCastPortal::OnStartRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + StartRequestedHandler(static_cast(user_data), proxy, + result); +} + +// static +void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + + RTC_LOG(LS_INFO) << "Start signal received."; + uint32_t portal_response; + Scoped response_data; + Scoped iter; + g_variant_get(parameters, "(u@a{sv})", &portal_response, + response_data.receive()); + if (portal_response || !response_data) { + RTC_LOG(LS_ERROR) << "Failed to start the screen cast session."; + that->PortalFailed(static_cast(portal_response)); + return; + } + + // Array of PipeWire streams. See + // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml + // documentation for . + if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})", + iter.receive())) { + Scoped variant; + + while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) { + uint32_t stream_id; + uint32_t type; + Scoped options; + + g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive()); + RTC_DCHECK(options.get()); + + if (g_variant_lookup(options.get(), "source_type", "u", &type)) { + that->capture_source_type_ = + static_cast(type); + } + + that->pw_stream_node_id_ = stream_id; + + break; + } + } + + that->OpenPipeWireRemote(); +} + +void ScreenCastPortal::OpenPipeWireRemote() { + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + + RTC_LOG(LS_INFO) << "Opening the PipeWire remote."; + + g_dbus_proxy_call_with_unix_fd_list( + proxy_, "OpenPipeWireRemote", + g_variant_new("(oa{sv})", session_handle_.c_str(), &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, cancellable_, + reinterpret_cast(OnOpenPipeWireRemoteRequested), + this); +} + +// static +void ScreenCastPortal::OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + ScreenCastPortal* that = static_cast(user_data); + RTC_DCHECK(that); + + Scoped error; + Scoped outlist; + Scoped variant(g_dbus_proxy_call_with_unix_fd_list_finish( + proxy, outlist.receive(), result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: " + << error->message; + that->PortalFailed(RequestResponse::kError); + return; + } + + int32_t index; + g_variant_get(variant.get(), "(h)", &index); + + that->pw_fd_ = g_unix_fd_list_get(outlist.get(), index, error.receive()); + + if (that->pw_fd_ == -1) { + RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " + << error->message; + that->PortalFailed(RequestResponse::kError); + return; + } + + that->notifier_->OnScreenCastRequestResult( + RequestResponse::kSuccess, that->pw_stream_node_id_, that->pw_fd_); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.h b/modules/desktop_capture/linux/wayland/screencast_portal.h new file mode 100644 index 0000000000..75884450db --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_portal.h @@ -0,0 +1,156 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_ + +#include + +#include + +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" + +namespace webrtc { + +class ScreenCastPortal { + public: + // Values are set based on source type property in + // xdg-desktop-portal/screencast + // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml + enum class CaptureSourceType : uint32_t { + kScreen = 0b01, + kWindow = 0b10, + kAnyScreenContent = kScreen | kWindow + }; + + // Values are set based on cursor mode property in + // xdg-desktop-portal/screencast + // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml + enum class CursorMode : uint32_t { + // Mouse cursor will not be included in any form + kHidden = 0b01, + // Mouse cursor will be part of the screen content + kEmbedded = 0b10, + // Mouse cursor information will be send separately in form of metadata + kMetadata = 0b100 + }; + + // Interface that must be implemented by the ScreenCastPortal consumers. + class PortalNotifier { + public: + virtual void OnScreenCastRequestResult(xdg_portal::RequestResponse result, + uint32_t stream_node_id, + int fd) = 0; + virtual void OnScreenCastSessionClosed() = 0; + + protected: + PortalNotifier() = default; + virtual ~PortalNotifier() = default; + }; + + explicit ScreenCastPortal(ScreenCastPortal::CaptureSourceType source_type, + PortalNotifier* notifier); + ~ScreenCastPortal(); + + // Initialize ScreenCastPortal with series of DBus calls where we try to + // obtain all the required information, like PipeWire file descriptor and + // PipeWire stream node ID. + // + // The observer will return whether the communication with xdg-desktop-portal + // was successful and only then you will be able to get all the required + // information in order to continue working with PipeWire. + void Start(); + + // Method to notify the reason for failure of a portal request. + void PortalFailed(xdg_portal::RequestResponse result); + + // Sends a create session request to the portal. + void SessionRequest(GDBusProxy* proxy); + + void UnsubscribeSignalHandlers(); + + private: + PortalNotifier* notifier_; + + // A PipeWire stream ID of stream we will be connecting to + uint32_t pw_stream_node_id_ = 0; + // A file descriptor of PipeWire socket + int pw_fd_ = -1; + + CaptureSourceType capture_source_type_ = + ScreenCastPortal::CaptureSourceType::kScreen; + + CursorMode cursor_mode_ = ScreenCastPortal::CursorMode::kMetadata; + + GDBusConnection* connection_ = nullptr; + GDBusProxy* proxy_ = nullptr; + GCancellable* cancellable_ = nullptr; + std::string portal_handle_; + std::string session_handle_; + std::string sources_handle_; + std::string start_handle_; + guint session_request_signal_id_ = 0; + guint sources_request_signal_id_ = 0; + guint start_request_signal_id_ = 0; + guint session_closed_signal_id_ = 0; + + static void OnProxyRequested(GObject* object, + GAsyncResult* result, + gpointer user_data); + static void OnSessionRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); + static void OnSessionRequestResponseSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + static void OnSessionClosedSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + void SourcesRequest(); + static void OnSourcesRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); + static void OnSourcesRequestResponseSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + + void StartRequest(); + static void OnStartRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); + static void OnStartRequestResponseSignal(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + + void OpenPipeWireRemote(); + static void OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_ diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc new file mode 100644 index 0000000000..720234fba8 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc @@ -0,0 +1,876 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" +#include "modules/desktop_capture/screen_capture_frame_queue.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/sanitizer.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/string_to_number.h" +#include "rtc_base/synchronization/mutex.h" + +#if defined(WEBRTC_DLOPEN_PIPEWIRE) +#include "modules/desktop_capture/linux/wayland/pipewire_stubs.h" +using modules_desktop_capture_linux_wayland::InitializeStubs; +using modules_desktop_capture_linux_wayland::kModuleDrm; +using modules_desktop_capture_linux_wayland::kModulePipewire; +using modules_desktop_capture_linux_wayland::StubPathMap; +#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) + +namespace webrtc { + +const int kBytesPerPixel = 4; + +#if defined(WEBRTC_DLOPEN_PIPEWIRE) +const char kPipeWireLib[] = "libpipewire-0.3.so.0"; +const char kDrmLib[] = "libdrm.so.2"; +#endif + +#if !PW_CHECK_VERSION(0, 3, 29) +#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) +#endif +#if !PW_CHECK_VERSION(0, 3, 33) +#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) +#endif + +constexpr int kCursorBpp = 4; +constexpr int CursorMetaSize(int w, int h) { + return (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + + w * h * kCursorBpp); +} + +struct PipeWireVersion { + int major = 0; + int minor = 0; + int micro = 0; +}; + +constexpr PipeWireVersion kDmaBufMinVersion = {0, 3, 24}; +constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33}; +constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40}; + +PipeWireVersion ParsePipeWireVersion(const char* version) { + std::vector parsed_version; + rtc::split(version, '.', &parsed_version); + + if (parsed_version.size() != 3) { + return {}; + } + + absl::optional major = rtc::StringToNumber(parsed_version.at(0)); + absl::optional minor = rtc::StringToNumber(parsed_version.at(1)); + absl::optional micro = rtc::StringToNumber(parsed_version.at(2)); + + // Return invalid version if we failed to parse it + if (!major || !minor || !micro) { + return {0, 0, 0}; + } + + return {major.value(), micro.value(), micro.value()}; +} + +spa_pod* BuildFormat(spa_pod_builder* builder, + uint32_t format, + const std::vector& modifiers) { + bool first = true; + spa_pod_frame frames[2]; + spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; + spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; + + spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + spa_pod_builder_add(builder, SPA_FORMAT_mediaType, + SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); + spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, + SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); + + if (modifiers.size()) { + if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, modifiers[0]); + } else { + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); + + // modifiers from the array + for (int64_t val : modifiers) { + spa_pod_builder_long(builder, val); + // Add the first modifier twice as the very first value is the default + // option + if (first) { + spa_pod_builder_long(builder, val); + first = false; + } + } + spa_pod_builder_pop(builder, &frames[1]); + } + } + + spa_pod_builder_add( + builder, SPA_FORMAT_VIDEO_size, + SPA_POD_CHOICE_RANGE_Rectangle( + &pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), + 0); + + return static_cast(spa_pod_builder_pop(builder, &frames[0])); +} + +class PipeWireThreadLoopLock { + public: + explicit PipeWireThreadLoopLock(pw_thread_loop* loop) : loop_(loop) { + pw_thread_loop_lock(loop_); + } + ~PipeWireThreadLoopLock() { pw_thread_loop_unlock(loop_); } + + private: + pw_thread_loop* const loop_; +}; + +class ScopedBuf { + public: + ScopedBuf() {} + ScopedBuf(uint8_t* map, int map_size, int fd) + : map_(map), map_size_(map_size), fd_(fd) {} + ~ScopedBuf() { + if (map_ != MAP_FAILED) { + munmap(map_, map_size_); + } + } + + explicit operator bool() { return map_ != MAP_FAILED; } + + void initialize(uint8_t* map, int map_size, int fd) { + map_ = map; + map_size_ = map_size; + fd_ = fd; + } + + uint8_t* get() { return map_; } + + protected: + uint8_t* map_ = static_cast(MAP_FAILED); + int map_size_; + int fd_; +}; + +class SharedScreenCastStreamPrivate { + public: + SharedScreenCastStreamPrivate(); + ~SharedScreenCastStreamPrivate(); + + bool StartScreenCastStream(uint32_t stream_node_id, int fd); + void StopScreenCastStream(); + std::unique_ptr CaptureFrame(); + std::unique_ptr CaptureCursor(); + DesktopVector CaptureCursorPosition(); + + private: + uint32_t pw_stream_node_id_ = 0; + int pw_fd_ = -1; + + DesktopSize desktop_size_ = {}; + DesktopSize video_size_; + + webrtc::Mutex queue_lock_; + ScreenCaptureFrameQueue queue_ + RTC_GUARDED_BY(&queue_lock_); + std::unique_ptr mouse_cursor_; + DesktopVector mouse_cursor_position_ = DesktopVector(-1, -1); + + int64_t modifier_; + std::unique_ptr egl_dmabuf_; + // List of modifiers we query as supported by the graphics card/driver + std::vector modifiers_; + + // PipeWire types + struct pw_context* pw_context_ = nullptr; + struct pw_core* pw_core_ = nullptr; + struct pw_stream* pw_stream_ = nullptr; + struct pw_thread_loop* pw_main_loop_ = nullptr; + struct spa_source* renegotiate_ = nullptr; + + spa_hook spa_core_listener_; + spa_hook spa_stream_listener_; + + // A number used to verify all previous methods and the resulting + // events have been handled. + int server_version_sync_ = 0; + // Version of the running PipeWire server we communicate with + PipeWireVersion pw_server_version_; + // Version of the library used to run our code + PipeWireVersion pw_client_version_; + + // event handlers + pw_core_events pw_core_events_ = {}; + pw_stream_events pw_stream_events_ = {}; + + struct spa_video_info_raw spa_video_format_; + + void ProcessBuffer(pw_buffer* buffer); + void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); + + // PipeWire callbacks + static void OnCoreError(void* data, + uint32_t id, + int seq, + int res, + const char* message); + static void OnCoreDone(void* user_data, uint32_t id, int seq); + static void OnCoreInfo(void* user_data, const pw_core_info* info); + static void OnStreamParamChanged(void* data, + uint32_t id, + const struct spa_pod* format); + static void OnStreamStateChanged(void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message); + static void OnStreamProcess(void* data); + // This will be invoked in case we fail to process DMA-BUF PW buffer using + // negotiated stream parameters (modifier). We will drop the modifier we + // failed to use and try to use a different one or fallback to shared memory + // buffers. + static void OnRenegotiateFormat(void* data, uint64_t); +}; + +bool operator>=(const PipeWireVersion& current_pw_version, + const PipeWireVersion& required_pw_version) { + if (!current_pw_version.major && !current_pw_version.minor && + !current_pw_version.micro) { + return false; + } + + return std::tie(current_pw_version.major, current_pw_version.minor, + current_pw_version.micro) >= + std::tie(required_pw_version.major, required_pw_version.minor, + required_pw_version.micro); +} + +bool operator<=(const PipeWireVersion& current_pw_version, + const PipeWireVersion& required_pw_version) { + if (!current_pw_version.major && !current_pw_version.minor && + !current_pw_version.micro) { + return false; + } + + return std::tie(current_pw_version.major, current_pw_version.minor, + current_pw_version.micro) <= + std::tie(required_pw_version.major, required_pw_version.minor, + required_pw_version.micro); +} + +void SharedScreenCastStreamPrivate::OnCoreError(void* data, + uint32_t id, + int seq, + int res, + const char* message) { + SharedScreenCastStreamPrivate* that = + static_cast(data); + RTC_DCHECK(that); + + RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message; +} + +void SharedScreenCastStreamPrivate::OnCoreInfo(void* data, + const pw_core_info* info) { + SharedScreenCastStreamPrivate* stream = + static_cast(data); + RTC_DCHECK(stream); + + stream->pw_server_version_ = ParsePipeWireVersion(info->version); +} + +void SharedScreenCastStreamPrivate::OnCoreDone(void* data, + uint32_t id, + int seq) { + const SharedScreenCastStreamPrivate* stream = + static_cast(data); + RTC_DCHECK(stream); + + if (id == PW_ID_CORE && stream->server_version_sync_ == seq) { + pw_thread_loop_signal(stream->pw_main_loop_, false); + } +} + +// static +void SharedScreenCastStreamPrivate::OnStreamStateChanged( + void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message) { + SharedScreenCastStreamPrivate* that = + static_cast(data); + RTC_DCHECK(that); + + switch (state) { + case PW_STREAM_STATE_ERROR: + RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; + break; + case PW_STREAM_STATE_PAUSED: + case PW_STREAM_STATE_STREAMING: + case PW_STREAM_STATE_UNCONNECTED: + case PW_STREAM_STATE_CONNECTING: + break; + } +} + +// static +void SharedScreenCastStreamPrivate::OnStreamParamChanged( + void* data, + uint32_t id, + const struct spa_pod* format) { + SharedScreenCastStreamPrivate* that = + static_cast(data); + RTC_DCHECK(that); + + RTC_LOG(LS_INFO) << "PipeWire stream format changed."; + if (!format || id != SPA_PARAM_Format) { + return; + } + + spa_format_video_raw_parse(format, &that->spa_video_format_); + + auto width = that->spa_video_format_.size.width; + auto height = that->spa_video_format_.size.height; + auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4); + auto size = height * stride; + + that->desktop_size_ = DesktopSize(width, height); + + uint8_t buffer[1024] = {}; + auto builder = spa_pod_builder{buffer, sizeof(buffer)}; + + // Setup buffers and meta header for new format. + + // When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as + // the server announces support for it. + // See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox + const bool has_modifier = + spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); + that->modifier_ = + has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID; + std::vector params; + const int buffer_types = + has_modifier || (that->pw_server_version_ >= kDmaBufMinVersion) + ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | + (1 << SPA_DATA_MemPtr) + : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); + + params.push_back(reinterpret_cast(spa_pod_builder_add_object( + &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, + SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers, + SPA_POD_CHOICE_RANGE_Int(8, 1, 32), SPA_PARAM_BUFFERS_dataType, + SPA_POD_CHOICE_FLAGS_Int(buffer_types)))); + params.push_back(reinterpret_cast(spa_pod_builder_add_object( + &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, + SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size, + SPA_POD_Int(sizeof(struct spa_meta_header))))); + params.push_back(reinterpret_cast(spa_pod_builder_add_object( + &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, + SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size, + SPA_POD_Int(sizeof(struct spa_meta_region))))); + params.push_back(reinterpret_cast(spa_pod_builder_add_object( + &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, + SPA_POD_Id(SPA_META_Cursor), SPA_PARAM_META_size, + SPA_POD_CHOICE_RANGE_Int(CursorMetaSize(64, 64), CursorMetaSize(1, 1), + CursorMetaSize(384, 384))))); + params.push_back(reinterpret_cast(spa_pod_builder_add_object( + &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, + SPA_POD_Id(SPA_META_VideoDamage), SPA_PARAM_META_size, + SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * 16, + sizeof(struct spa_meta_region) * 1, + sizeof(struct spa_meta_region) * 16)))); + + pw_stream_update_params(that->pw_stream_, params.data(), params.size()); +} + +// static +void SharedScreenCastStreamPrivate::OnStreamProcess(void* data) { + SharedScreenCastStreamPrivate* that = + static_cast(data); + RTC_DCHECK(that); + + struct pw_buffer* next_buffer; + struct pw_buffer* buffer = nullptr; + + next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); + while (next_buffer) { + buffer = next_buffer; + next_buffer = pw_stream_dequeue_buffer(that->pw_stream_); + + if (next_buffer) { + pw_stream_queue_buffer(that->pw_stream_, buffer); + } + } + + if (!buffer) { + return; + } + + that->ProcessBuffer(buffer); + + pw_stream_queue_buffer(that->pw_stream_, buffer); +} + +void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { + SharedScreenCastStreamPrivate* that = + static_cast(data); + RTC_DCHECK(that); + + { + PipeWireThreadLoopLock thread_loop_lock(that->pw_main_loop_); + + uint8_t buffer[2048] = {}; + + spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; + + std::vector params; + + for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { + if (!that->modifiers_.empty()) { + params.push_back(BuildFormat(&builder, format, that->modifiers_)); + } + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + } + + pw_stream_update_params(that->pw_stream_, params.data(), params.size()); + } +} + +SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {} + +SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() { + if (pw_main_loop_) { + pw_thread_loop_stop(pw_main_loop_); + } + + if (pw_stream_) { + pw_stream_destroy(pw_stream_); + } + + if (pw_core_) { + pw_core_disconnect(pw_core_); + } + + if (pw_context_) { + pw_context_destroy(pw_context_); + } + + if (pw_main_loop_) { + pw_thread_loop_destroy(pw_main_loop_); + } +} + +RTC_NO_SANITIZE("cfi-icall") +bool SharedScreenCastStreamPrivate::StartScreenCastStream( + uint32_t stream_node_id, + int fd) { +#if defined(WEBRTC_DLOPEN_PIPEWIRE) + StubPathMap paths; + + // Check if the PipeWire and DRM libraries are available. + paths[kModulePipewire].push_back(kPipeWireLib); + paths[kModuleDrm].push_back(kDrmLib); + + if (!InitializeStubs(paths)) { + RTC_LOG(LS_ERROR) + << "One of following libraries is missing on your system:\n" + << " - PipeWire (" << kPipeWireLib << ")\n" + << " - drm (" << kDrmLib << ")"; + return false; + } +#endif // defined(WEBRTC_DLOPEN_PIPEWIRE) + egl_dmabuf_ = std::make_unique(); + + pw_stream_node_id_ = stream_node_id; + pw_fd_ = fd; + + pw_init(/*argc=*/nullptr, /*argc=*/nullptr); + + pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr); + + pw_context_ = + pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); + if (!pw_context_) { + RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; + return false; + } + + if (pw_thread_loop_start(pw_main_loop_) < 0) { + RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; + return false; + } + + pw_client_version_ = ParsePipeWireVersion(pw_get_library_version()); + + // Initialize event handlers, remote end and stream-related. + pw_core_events_.version = PW_VERSION_CORE_EVENTS; + pw_core_events_.info = &OnCoreInfo; + pw_core_events_.done = &OnCoreDone; + pw_core_events_.error = &OnCoreError; + + pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; + pw_stream_events_.state_changed = &OnStreamStateChanged; + pw_stream_events_.param_changed = &OnStreamParamChanged; + pw_stream_events_.process = &OnStreamProcess; + + { + PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); + + pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); + if (!pw_core_) { + RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; + return false; + } + + pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); + + // Add an event that can be later invoked by pw_loop_signal_event() + renegotiate_ = pw_loop_add_event(pw_thread_loop_get_loop(pw_main_loop_), + OnRenegotiateFormat, this); + + server_version_sync_ = + pw_core_sync(pw_core_, PW_ID_CORE, server_version_sync_); + + pw_thread_loop_wait(pw_main_loop_); + + pw_properties* reuseProps = + pw_properties_new_string("pipewire.client.reuse=1"); + pw_stream_ = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps); + + if (!pw_stream_) { + RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; + return false; + } + + pw_stream_add_listener(pw_stream_, &spa_stream_listener_, + &pw_stream_events_, this); + uint8_t buffer[2048] = {}; + + spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; + + std::vector params; + const bool has_required_pw_client_version = + pw_client_version_ >= kDmaBufModifierMinVersion; + const bool has_required_pw_server_version = + pw_server_version_ >= kDmaBufModifierMinVersion; + for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { + // Modifiers can be used with PipeWire >= 0.3.33 + if (has_required_pw_client_version && has_required_pw_server_version) { + modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format); + + if (!modifiers_.empty()) { + params.push_back(BuildFormat(&builder, format, modifiers_)); + } + } + + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + } + + if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_, + PW_STREAM_FLAG_AUTOCONNECT, params.data(), + params.size()) != 0) { + RTC_LOG(LS_ERROR) << "Could not connect receiving stream."; + return false; + } + + RTC_LOG(LS_INFO) << "PipeWire remote opened."; + } + return true; +} + +void SharedScreenCastStreamPrivate::StopScreenCastStream() { + if (pw_stream_) { + pw_stream_disconnect(pw_stream_); + } +} + +std::unique_ptr SharedScreenCastStreamPrivate::CaptureFrame() { + webrtc::MutexLock lock(&queue_lock_); + + if (!queue_.current_frame()) { + return std::unique_ptr{}; + } + + std::unique_ptr frame = queue_.current_frame()->Share(); + return std::move(frame); +} + +std::unique_ptr SharedScreenCastStreamPrivate::CaptureCursor() { + if (!mouse_cursor_) { + return nullptr; + } + + return std::move(mouse_cursor_); +} + +DesktopVector SharedScreenCastStreamPrivate::CaptureCursorPosition() { + return mouse_cursor_position_; +} + +void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { + spa_buffer* spa_buffer = buffer->buffer; + ScopedBuf map; + std::unique_ptr src_unique_ptr; + uint8_t* src = nullptr; + + // Try to update the mouse cursor first, because it can be the only + // information carried by the buffer + { + const struct spa_meta_cursor* cursor = + static_cast(spa_buffer_find_meta_data( + spa_buffer, SPA_META_Cursor, sizeof(*cursor))); + if (cursor && spa_meta_cursor_is_valid(cursor)) { + struct spa_meta_bitmap* bitmap = nullptr; + + if (cursor->bitmap_offset) + bitmap = + SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap); + + if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) { + const uint8_t* bitmap_data = + SPA_MEMBER(bitmap, bitmap->offset, uint8_t); + BasicDesktopFrame* mouse_frame = new BasicDesktopFrame( + DesktopSize(bitmap->size.width, bitmap->size.height)); + mouse_frame->CopyPixelsFrom( + bitmap_data, bitmap->stride, + DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height)); + mouse_cursor_ = std::make_unique( + mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y)); + } + mouse_cursor_position_.set(cursor->position.x, cursor->position.y); + } + } + + if (spa_buffer->datas[0].chunk->size == 0) { + return; + } + + if (spa_buffer->datas[0].type == SPA_DATA_MemFd) { + map.initialize( + static_cast( + mmap(nullptr, + spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, + PROT_READ, MAP_PRIVATE, spa_buffer->datas[0].fd, 0)), + spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, + spa_buffer->datas[0].fd); + + if (!map) { + RTC_LOG(LS_ERROR) << "Failed to mmap the memory: " + << std::strerror(errno); + return; + } + + src = SPA_MEMBER(map.get(), spa_buffer->datas[0].mapoffset, uint8_t); + } else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) { + const uint n_planes = spa_buffer->n_datas; + + if (!n_planes) { + return; + } + + std::vector plane_datas; + for (uint32_t i = 0; i < n_planes; ++i) { + EglDmaBuf::PlaneData data = { + static_cast(spa_buffer->datas[i].fd), + static_cast(spa_buffer->datas[i].chunk->stride), + static_cast(spa_buffer->datas[i].chunk->offset)}; + plane_datas.push_back(data); + } + + src_unique_ptr = egl_dmabuf_->ImageFromDmaBuf( + desktop_size_, spa_video_format_.format, plane_datas, modifier_); + if (src_unique_ptr) { + src = src_unique_ptr.get(); + } else { + RTC_LOG(LS_ERROR) << "Dropping DMA-BUF modifier: " << modifier_ + << " and trying to renegotiate stream parameters"; + + if (pw_client_version_ >= kDropSingleModifierMinVersion) { + modifiers_.erase( + std::remove(modifiers_.begin(), modifiers_.end(), modifier_), + modifiers_.end()); + } else { + modifiers_.clear(); + } + + pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_), + renegotiate_); + return; + } + } else if (spa_buffer->datas[0].type == SPA_DATA_MemPtr) { + src = static_cast(spa_buffer->datas[0].data); + } + + if (!src) { + return; + } + struct spa_meta_region* video_metadata = + static_cast(spa_buffer_find_meta_data( + spa_buffer, SPA_META_VideoCrop, sizeof(*video_metadata))); + + // Video size from metadata is bigger than an actual video stream size. + // The metadata are wrong or we should up-scale the video...in both cases + // just quit now. + if (video_metadata && (video_metadata->region.size.width > + static_cast(desktop_size_.width()) || + video_metadata->region.size.height > + static_cast(desktop_size_.height()))) { + RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!"; + return; + } + + // Use video metadata when video size from metadata is set and smaller than + // video stream size, so we need to adjust it. + bool video_metadata_use = false; + const struct spa_rectangle* video_metadata_size = + video_metadata ? &video_metadata->region.size : nullptr; + + if (video_metadata_size && video_metadata_size->width != 0 && + video_metadata_size->height != 0 && + (static_cast(video_metadata_size->width) < desktop_size_.width() || + static_cast(video_metadata_size->height) < + desktop_size_.height())) { + video_metadata_use = true; + } + + if (video_metadata_use) { + video_size_ = + DesktopSize(video_metadata_size->width, video_metadata_size->height); + } else { + video_size_ = desktop_size_; + } + + uint32_t y_offset = video_metadata_use && (video_metadata->region.position.y + + video_size_.height() <= + desktop_size_.height()) + ? video_metadata->region.position.y + : 0; + uint32_t x_offset = video_metadata_use && (video_metadata->region.position.x + + video_size_.width() <= + desktop_size_.width()) + ? video_metadata->region.position.x + : 0; + + uint8_t* updated_src = src + (spa_buffer->datas[0].chunk->stride * y_offset) + + (kBytesPerPixel * x_offset); + + webrtc::MutexLock lock(&queue_lock_); + + // Move to the next frame if the current one is being used and shared + if (queue_.current_frame() && queue_.current_frame()->IsShared()) { + queue_.MoveToNextFrame(); + if (queue_.current_frame() && queue_.current_frame()->IsShared()) { + RTC_LOG(LS_WARNING) + << "Failed to process PipeWire buffer: no available frame"; + return; + } + } + + if (!queue_.current_frame() || + !queue_.current_frame()->size().equals(video_size_)) { + std::unique_ptr frame(new BasicDesktopFrame( + DesktopSize(video_size_.width(), video_size_.height()))); + queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame))); + } + + queue_.current_frame()->CopyPixelsFrom( + updated_src, + (spa_buffer->datas[0].chunk->stride - (kBytesPerPixel * x_offset)), + DesktopRect::MakeWH(video_size_.width(), video_size_.height())); + + if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx || + spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { + uint8_t* tmp_src = queue_.current_frame()->data(); + for (int i = 0; i < video_size_.height(); ++i) { + // If both sides decided to go with the RGBx format we need to convert + // it to BGRx to match color format expected by WebRTC. + ConvertRGBxToBGRx(tmp_src, queue_.current_frame()->stride()); + tmp_src += queue_.current_frame()->stride(); + } + } + + queue_.current_frame()->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(queue_.current_frame()->size())); +} + +void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame, + uint32_t size) { + for (uint32_t i = 0; i < size; i += 4) { + uint8_t tempR = frame[i]; + uint8_t tempB = frame[i + 2]; + frame[i] = tempB; + frame[i + 2] = tempR; + } +} + +SharedScreenCastStream::SharedScreenCastStream() + : private_(std::make_unique()) {} + +SharedScreenCastStream::~SharedScreenCastStream() {} + +rtc::scoped_refptr +SharedScreenCastStream::CreateDefault() { + // Explicit new, to access non-public constructor. + return rtc::scoped_refptr(new SharedScreenCastStream()); +} + +bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id, + int fd) { + return private_->StartScreenCastStream(stream_node_id, fd); +} + +void SharedScreenCastStream::StopScreenCastStream() { + private_->StopScreenCastStream(); +} + +std::unique_ptr SharedScreenCastStream::CaptureFrame() { + return private_->CaptureFrame(); +} + +std::unique_ptr SharedScreenCastStream::CaptureCursor() { + return private_->CaptureCursor(); +} + +absl::optional SharedScreenCastStream::CaptureCursorPosition() { + DesktopVector position = private_->CaptureCursorPosition(); + + // Consider only (x >= 0 and y >= 0) a valid position + if (position.x() < 0 || position.y() < 0) { + return absl::nullopt; + } + + return position; +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h new file mode 100644 index 0000000000..443ec745d5 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h @@ -0,0 +1,71 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_ + +#include + +#include "absl/types/optional.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "modules/desktop_capture/desktop_frame.h" +#include "modules/desktop_capture/mouse_cursor.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class SharedScreenCastStreamPrivate; + +class RTC_EXPORT SharedScreenCastStream + : public rtc::RefCountedNonVirtual { + public: + static rtc::scoped_refptr CreateDefault(); + + bool StartScreenCastStream(uint32_t stream_node_id, int fd); + void StopScreenCastStream(); + + // Below functions return the most recent information we get from a + // PipeWire buffer on each Process() callback. This assumes that we + // managed to successfuly connect to a PipeWire stream provided by the + // compositor (based on stream parameters). The cursor data are obtained + // from spa_meta_cursor stream metadata and therefore the cursor is not + // part of actual screen/window frame. + + // Returns the most recent screen/window frame we obtained from PipeWire + // buffer. Will return an empty frame in case we didn't manage to get a frame + // from PipeWire buffer. + std::unique_ptr CaptureFrame(); + + // Returns the most recent mouse cursor image. Will return an nullptr cursor + // in case we didn't manage to get a cursor from PipeWire buffer. NOTE: the + // cursor image might not be updated on every cursor location change, but + // actually only when its shape changes. + std::unique_ptr CaptureCursor(); + + // Returns the most recent mouse cursor position. Will not return a value in + // case we didn't manage to get it from PipeWire buffer. + absl::optional CaptureCursorPosition(); + + ~SharedScreenCastStream(); + + protected: + SharedScreenCastStream(); + + private: + SharedScreenCastStream(const SharedScreenCastStream&) = delete; + SharedScreenCastStream& operator=(const SharedScreenCastStream&) = delete; + + std::unique_ptr private_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_ diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc new file mode 100644 index 0000000000..ca98044c97 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc @@ -0,0 +1,169 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" + +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +std::string RequestResponseToString(RequestResponse request) { + switch (request) { + case RequestResponse::kSuccess: + return "kSuccess"; + case RequestResponse::kUserCancelled: + return "kUserCancelled"; + case RequestResponse::kError: + return "kError"; + default: + return "Uknown"; + } +} + +std::string PrepareSignalHandle(const char* token, + GDBusConnection* connection) { + Scoped sender( + g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); + for (int i = 0; sender.get()[i]; ++i) { + if (sender.get()[i] == '.') { + sender.get()[i] = '_'; + } + } + const char* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), + "/", token, /*end of varargs*/ nullptr); + return handle; +} + +uint32_t SetupRequestResponseSignal(const char* object_path, + const GDBusSignalCallback callback, + gpointer user_data, + GDBusConnection* connection) { + return g_dbus_connection_signal_subscribe( + connection, kDesktopBusName, kRequestInterfaceName, "Response", + object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + callback, user_data, /*user_data_free_func=*/nullptr); +} + +void RequestSessionProxy(const char* interface_name, + const ProxyRequestCallback proxy_request_callback, + GCancellable* cancellable, + gpointer user_data) { + g_dbus_proxy_new_for_bus( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, + kDesktopBusName, kDesktopObjectPath, interface_name, cancellable, + reinterpret_cast(proxy_request_callback), user_data); +} + +void SetupSessionRequestHandlers( + const std::string& portal_prefix, + const SessionRequestCallback session_request_callback, + const SessionRequestResponseSignalHandler request_response_signale_handler, + GDBusConnection* connection, + GDBusProxy* proxy, + GCancellable* cancellable, + std::string& portal_handle, + guint& session_request_signal_id, + gpointer user_data) { + GVariantBuilder builder; + Scoped variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = g_strdup_printf("%s_session%d", portal_prefix.c_str(), + g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "session_handle_token", + g_variant_new_string(variant_string.get())); + + variant_string = g_strdup_printf("%s_%d", portal_prefix.c_str(), + g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + portal_handle = PrepareSignalHandle(variant_string.get(), connection); + session_request_signal_id = SetupRequestResponseSignal( + portal_handle.c_str(), request_response_signale_handler, user_data, + connection); + + RTC_LOG(LS_INFO) << "Desktop session requested."; + g_dbus_proxy_call( + proxy, "CreateSession", g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, + reinterpret_cast(session_request_callback), + user_data); +} + +void StartSessionRequest( + const std::string& prefix, + const std::string session_handle, + const StartRequestResponseSignalHandler signal_handler, + const SessionStartRequestedHandler session_started_handler, + GDBusProxy* proxy, + GDBusConnection* connection, + GCancellable* cancellable, + guint& start_request_signal_id, + std::string& start_handle, + gpointer user_data) { + GVariantBuilder builder; + Scoped variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = + g_strdup_printf("%s%d", prefix.c_str(), g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + start_handle = PrepareSignalHandle(variant_string.get(), connection); + start_request_signal_id = SetupRequestResponseSignal( + start_handle.c_str(), signal_handler, user_data, connection); + + // "Identifier for the application window", this is Wayland, so not "x11:...". + const char parent_window[] = ""; + + RTC_LOG(LS_INFO) << "Starting the portal session."; + g_dbus_proxy_call( + proxy, "Start", + g_variant_new("(osa{sv})", session_handle.c_str(), parent_window, + &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, + reinterpret_cast(session_started_handler), + user_data); +} + +void TearDownSession(std::string session_handle, + GDBusProxy* proxy, + GCancellable* cancellable, + GDBusConnection* connection) { + if (!session_handle.empty()) { + Scoped message( + g_dbus_message_new_method_call(kDesktopBusName, session_handle.c_str(), + kSessionInterfaceName, "Close")); + if (message.get()) { + Scoped error; + g_dbus_connection_send_message(connection, message.get(), + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + /*out_serial=*/nullptr, error.receive()); + if (error.get()) { + RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; + } + } + } + + if (cancellable) { + g_cancellable_cancel(cancellable); + g_object_unref(cancellable); + } + + if (proxy) { + g_object_unref(proxy); + } +} + +} // namespace xdg_portal +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h new file mode 100644 index 0000000000..9b7953424d --- /dev/null +++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h @@ -0,0 +1,234 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ + +#include +#include + +#include +#include + +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +constexpr char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; +constexpr char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; +constexpr char kDesktopRequestObjectPath[] = + "/org/freedesktop/portal/desktop/request"; +constexpr char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; +constexpr char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; +constexpr char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; + +using ProxyRequestCallback = void (*)(GObject*, GAsyncResult*, gpointer); +using SessionRequestCallback = void (*)(GDBusProxy*, GAsyncResult*, gpointer); +using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); +using SessionRequestResponseSignalCallback = void (*)(std::string); +using SessionClosedSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); +using StartRequestResponseSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); +using SessionStartRequestedHandler = void (*)(GDBusProxy*, + GAsyncResult*, + gpointer); + +// Contains type of responses that can be observed when making a request to +// a desktop portal interface. +enum class RequestResponse { + // Success, the request is carried out. + kSuccess, + // The user cancelled the interaction. + kUserCancelled, + // The user interaction was ended in some other way. + kError, + + kMaxValue = kError, +}; + +std::string RequestResponseToString(RequestResponse request); + +// Returns a string path for signal handle based on the provided connection and +// token. +std::string PrepareSignalHandle(const char* token, GDBusConnection* connection); + +// Sets up the callback to execute when a response signal is received for the +// given object. +uint32_t SetupRequestResponseSignal(const char* object_path, + const GDBusSignalCallback callback, + gpointer user_data, + GDBusConnection* connection); + +void RequestSessionProxy(const char* interface_name, + const ProxyRequestCallback proxy_request_callback, + GCancellable* cancellable, + gpointer user_data); + +void SetupSessionRequestHandlers( + const std::string& portal_prefix, + const SessionRequestCallback session_request_callback, + const SessionRequestResponseSignalHandler request_response_signale_handler, + GDBusConnection* connection, + GDBusProxy* proxy, + GCancellable* cancellable, + std::string& portal_handle, + guint& session_request_signal_id, + gpointer user_data); + +void StartSessionRequest( + const std::string& prefix, + const std::string session_handle, + const StartRequestResponseSignalHandler signal_handler, + const SessionStartRequestedHandler session_started_handler, + GDBusProxy* proxy, + GDBusConnection* connection, + GCancellable* cancellable, + guint& start_request_signal_id, + std::string& start_handle, + gpointer user_data); + +// Tears down the portal session and cleans up related objects. +void TearDownSession(std::string session_handle, + GDBusProxy* proxy, + GCancellable* cancellable, + GDBusConnection* connection); + +template +void RequestSessionUsingProxy(T* portal, + GObject* gobject, + GAsyncResult* result) { + RTC_DCHECK(portal); + Scoped error; + GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); + if (!proxy) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: " + << error->message; + portal->PortalFailed(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Successfully created proxy for the portal."; + portal->SessionRequest(proxy); +} + +template +void SessionRequestHandler(T* portal, + GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + RTC_DCHECK(portal); + + Scoped error; + Scoped variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to session: " << error->message; + portal->PortalFailed(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Initializing the session."; + + Scoped handle; + g_variant_get_child(variant.get(), /*index=*/0, /*format_string=*/"o", + &handle); + if (!handle) { + RTC_LOG(LS_ERROR) << "Failed to initialize the session."; + portal->UnsubscribeSignalHandlers(); + portal->PortalFailed(RequestResponse::kError); + return; + } +} + +template +void SessionRequestResponseSignalHelper( + const SessionClosedSignalHandler session_close_signal_handler, + T* portal, + GDBusConnection* connection, + std::string& session_handle, + GVariant* parameters, + guint& session_closed_signal_id) { + uint32_t portal_response; + Scoped response_data; + g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response, + response_data.receive()); + Scoped g_session_handle( + g_variant_lookup_value(response_data.get(), /*key=*/"session_handle", + /*expected_type=*/nullptr)); + session_handle = g_variant_dup_string( + /*value=*/g_session_handle.get(), /*length=*/nullptr); + + if (session_handle.empty() || portal_response) { + RTC_LOG(LS_ERROR) << "Failed to request the session subscription."; + portal->PortalFailed(RequestResponse::kError); + return; + } + + session_closed_signal_id = g_dbus_connection_signal_subscribe( + connection, kDesktopBusName, kSessionInterfaceName, /*member=*/"Closed", + session_handle.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE, + session_close_signal_handler, portal, /*user_data_free_func=*/nullptr); +} + +template +void StartRequestedHandler(T* portal, GDBusProxy* proxy, GAsyncResult* result) { + RTC_DCHECK(portal); + Scoped error; + Scoped variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to start the portal session: " + << error->message; + portal->PortalFailed(RequestResponse::kError); + return; + } + + Scoped handle; + g_variant_get_child(variant.get(), 0, "o", handle.receive()); + if (!handle) { + RTC_LOG(LS_ERROR) << "Failed to initialize the start portal session."; + portal->UnsubscribeSignalHandlers(); + portal->PortalFailed(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Subscribed to the start signal."; +} + +} // namespace xdg_portal +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ diff --git a/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc similarity index 97% rename from modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc rename to modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc index 761d428f42..0f4be547e9 100644 --- a/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc +++ b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/mouse_cursor_monitor_x11.h" +#include "modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h" #include #include @@ -23,7 +23,7 @@ #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/linux/x_error_trap.h" +#include "modules/desktop_capture/linux/x11/x_error_trap.h" #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/mouse_cursor_monitor.h" #include "rtc_base/checks.h" @@ -208,7 +208,7 @@ void MouseCursorMonitorX11::CaptureCursor() { new BasicDesktopFrame(DesktopSize(img->width, img->height))); // Xlib stores 32-bit data in longs, even if longs are 64-bits long. - unsigned long* src = img->pixels; + unsigned long* src = img->pixels; // NOLINT(runtime/int) uint32_t* dst = reinterpret_cast(image->data()); uint32_t* dst_end = dst + (img->width * img->height); while (dst < dst_end) { diff --git a/modules/desktop_capture/linux/mouse_cursor_monitor_x11.h b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h similarity index 87% rename from modules/desktop_capture/linux/mouse_cursor_monitor_x11.h rename to modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h index c34eaf468e..980d254a0a 100644 --- a/modules/desktop_capture/linux/mouse_cursor_monitor_x11.h +++ b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_X11_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_X11_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_ #include @@ -18,7 +18,7 @@ #include "api/scoped_refptr.h" #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capture_types.h" -#include "modules/desktop_capture/linux/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/mouse_cursor_monitor.h" @@ -65,4 +65,4 @@ class MouseCursorMonitorX11 : public MouseCursorMonitor, } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_X11_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_ diff --git a/modules/desktop_capture/linux/screen_capturer_x11.cc b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc similarity index 90% rename from modules/desktop_capture/linux/screen_capturer_x11.cc rename to modules/desktop_capture/linux/x11/screen_capturer_x11.cc index ab1bd7fb7c..2de387578e 100644 --- a/modules/desktop_capture/linux/screen_capturer_x11.cc +++ b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/screen_capturer_x11.h" +#include "modules/desktop_capture/linux/x11/screen_capturer_x11.h" #include #include @@ -25,7 +25,7 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/linux/x_server_pixel_buffer.h" +#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h" #include "modules/desktop_capture/screen_capture_frame_queue.h" #include "modules/desktop_capture/screen_capturer_helper.h" #include "modules/desktop_capture/shared_desktop_frame.h" @@ -325,6 +325,12 @@ bool ScreenCapturerX11::SelectSource(SourceId id) { selected_monitor_name_ = m.name; selected_monitor_rect_ = DesktopRect::MakeXYWH(m.x, m.y, m.width, m.height); + const auto& pixel_buffer_rect = x_server_pixel_buffer_.window_rect(); + if (!pixel_buffer_rect.ContainsRect(selected_monitor_rect_)) { + RTC_LOG(LS_WARNING) + << "Cropping selected monitor rect to fit the pixel-buffer."; + selected_monitor_rect_.IntersectWith(pixel_buffer_rect); + } return true; } } @@ -355,10 +361,13 @@ bool ScreenCapturerX11::HandleXEvent(const XEvent& event) { std::unique_ptr ScreenCapturerX11::CaptureScreen() { std::unique_ptr frame = queue_.current_frame()->Share(); RTC_DCHECK(selected_monitor_rect_.size().equals(frame->size())); + RTC_DCHECK(selected_monitor_rect_.top_left().equals(frame->top_left())); // Pass the screen size to the helper, so it can clip the invalid region if it - // expands that region to a grid. - helper_.set_size_most_recent(x_server_pixel_buffer_.window_size()); + // expands that region to a grid. Note that the helper operates in the + // DesktopFrame coordinate system where the top-left pixel is (0, 0), even for + // a monitor with non-zero offset relative to `x_server_pixel_buffer_`. + helper_.set_size_most_recent(frame->size()); // In the DAMAGE case, ensure the frame is up-to-date with the previous frame // if any. If there isn't a previous frame, that means a screen-resolution @@ -378,19 +387,28 @@ std::unique_ptr ScreenCapturerX11::CaptureScreen() { XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_, &rects_num, &bounds); for (int i = 0; i < rects_num; ++i) { - updated_region->AddRect(DesktopRect::MakeXYWH( - rects[i].x, rects[i].y, rects[i].width, rects[i].height)); + auto damage_rect = DesktopRect::MakeXYWH(rects[i].x, rects[i].y, + rects[i].width, rects[i].height); + + // Damage-regions are relative to `x_server_pixel_buffer`, so convert the + // region to DesktopFrame coordinates where the top-left is always (0, 0), + // before adding to the frame's updated_region. `helper_` also operates in + // DesktopFrame coordinates, and it will take care of cropping away any + // damage-regions that lie outside the selected monitor. + damage_rect.Translate(-frame->top_left()); + updated_region->AddRect(damage_rect); } XFree(rects); helper_.InvalidateRegion(*updated_region); // Capture the damaged portions of the desktop. helper_.TakeInvalidRegion(updated_region); - updated_region->IntersectWith(selected_monitor_rect_); for (DesktopRegion::Iterator it(*updated_region); !it.IsAtEnd(); it.Advance()) { - if (!x_server_pixel_buffer_.CaptureRect(it.rect(), frame.get())) + auto rect = it.rect(); + rect.Translate(frame->top_left()); + if (!x_server_pixel_buffer_.CaptureRect(rect, frame.get())) return nullptr; } } else { @@ -400,7 +418,7 @@ std::unique_ptr ScreenCapturerX11::CaptureScreen() { frame.get())) { return nullptr; } - updated_region->SetRect(selected_monitor_rect_); + updated_region->SetRect(DesktopRect::MakeSize(frame->size())); } return std::move(frame); @@ -445,11 +463,8 @@ void ScreenCapturerX11::SynchronizeFrame() { RTC_DCHECK(current != last); for (DesktopRegion::Iterator it(last_invalid_region_); !it.IsAtEnd(); it.Advance()) { - if (selected_monitor_rect_.ContainsRect(it.rect())) { - DesktopRect r = it.rect(); - r.Translate(-selected_monitor_rect_.top_left()); - current->CopyPixelsFrom(*last, r.top_left(), r); - } + const DesktopRect& r = it.rect(); + current->CopyPixelsFrom(*last, r.top_left(), r); } } diff --git a/modules/desktop_capture/linux/screen_capturer_x11.h b/modules/desktop_capture/linux/x11/screen_capturer_x11.h similarity index 90% rename from modules/desktop_capture/linux/screen_capturer_x11.h rename to modules/desktop_capture/linux/x11/screen_capturer_x11.h index c98b9fbf97..d2a437aaa2 100644 --- a/modules/desktop_capture/linux/screen_capturer_x11.h +++ b/modules/desktop_capture/linux/x11/screen_capturer_x11.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_X11_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_X11_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_ #include #include @@ -23,13 +23,12 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_region.h" -#include "modules/desktop_capture/linux/shared_x_display.h" -#include "modules/desktop_capture/linux/x_atom_cache.h" -#include "modules/desktop_capture/linux/x_server_pixel_buffer.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/x_atom_cache.h" +#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h" #include "modules/desktop_capture/screen_capture_frame_queue.h" #include "modules/desktop_capture/screen_capturer_helper.h" #include "modules/desktop_capture/shared_desktop_frame.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -46,6 +45,9 @@ class ScreenCapturerX11 : public DesktopCapturer, ScreenCapturerX11(); ~ScreenCapturerX11() override; + ScreenCapturerX11(const ScreenCapturerX11&) = delete; + ScreenCapturerX11& operator=(const ScreenCapturerX11&) = delete; + static std::unique_ptr CreateRawScreenCapturer( const DesktopCaptureOptions& options); @@ -138,10 +140,8 @@ class ScreenCapturerX11 : public DesktopCapturer, DesktopRegion last_invalid_region_; std::unique_ptr atom_cache_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerX11); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_X11_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_ diff --git a/modules/desktop_capture/linux/shared_x_display.cc b/modules/desktop_capture/linux/x11/shared_x_display.cc similarity index 94% rename from modules/desktop_capture/linux/shared_x_display.cc rename to modules/desktop_capture/linux/x11/shared_x_display.cc index 3133134a70..ad2e04374d 100644 --- a/modules/desktop_capture/linux/shared_x_display.cc +++ b/modules/desktop_capture/linux/x11/shared_x_display.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" #include #include @@ -36,9 +36,9 @@ rtc::scoped_refptr SharedXDisplay::Create( XOpenDisplay(display_name.empty() ? NULL : display_name.c_str()); if (!display) { RTC_LOG(LS_ERROR) << "Unable to open display"; - return NULL; + return nullptr; } - return new SharedXDisplay(display); + return rtc::scoped_refptr(new SharedXDisplay(display)); } // static diff --git a/modules/desktop_capture/linux/shared_x_display.h b/modules/desktop_capture/linux/x11/shared_x_display.h similarity index 88% rename from modules/desktop_capture/linux/shared_x_display.h rename to modules/desktop_capture/linux/x11/shared_x_display.h index da130b0072..6405c36a2d 100644 --- a/modules/desktop_capture/linux/shared_x_display.h +++ b/modules/desktop_capture/linux/x11/shared_x_display.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_ #include #include @@ -17,7 +17,6 @@ #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" // Including Xlib.h will involve evil defines (Bool, Status, True, False), which @@ -65,6 +64,9 @@ class RTC_EXPORT SharedXDisplay ~SharedXDisplay(); + SharedXDisplay(const SharedXDisplay&) = delete; + SharedXDisplay& operator=(const SharedXDisplay&) = delete; + protected: // Takes ownership of `display`. explicit SharedXDisplay(Display* display); @@ -75,10 +77,8 @@ class RTC_EXPORT SharedXDisplay Display* display_; EventHandlersMap event_handlers_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SharedXDisplay); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_ diff --git a/modules/desktop_capture/linux/window_capturer_x11.cc b/modules/desktop_capture/linux/x11/window_capturer_x11.cc similarity index 96% rename from modules/desktop_capture/linux/window_capturer_x11.cc rename to modules/desktop_capture/linux/x11/window_capturer_x11.cc index 94f9f92c81..5b87e8cbd3 100644 --- a/modules/desktop_capture/linux/window_capturer_x11.cc +++ b/modules/desktop_capture/linux/x11/window_capturer_x11.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/window_capturer_x11.h" +#include "modules/desktop_capture/linux/x11/window_capturer_x11.h" #include #include @@ -23,9 +23,9 @@ #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_region.h" -#include "modules/desktop_capture/linux/shared_x_display.h" -#include "modules/desktop_capture/linux/window_finder_x11.h" -#include "modules/desktop_capture/linux/window_list_utils.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/window_finder_x11.h" +#include "modules/desktop_capture/linux/x11/window_list_utils.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/trace_event.h" diff --git a/modules/desktop_capture/linux/window_capturer_x11.h b/modules/desktop_capture/linux/x11/window_capturer_x11.h similarity index 76% rename from modules/desktop_capture/linux/window_capturer_x11.h rename to modules/desktop_capture/linux/x11/window_capturer_x11.h index d8a085f366..ac591c272e 100644 --- a/modules/desktop_capture/linux/window_capturer_x11.h +++ b/modules/desktop_capture/linux/x11/window_capturer_x11.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_X11_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_X11_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_ #include #include @@ -21,11 +21,10 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/linux/shared_x_display.h" -#include "modules/desktop_capture/linux/window_finder_x11.h" -#include "modules/desktop_capture/linux/x_atom_cache.h" -#include "modules/desktop_capture/linux/x_server_pixel_buffer.h" -#include "rtc_base/constructor_magic.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/window_finder_x11.h" +#include "modules/desktop_capture/linux/x11/x_atom_cache.h" +#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h" namespace webrtc { @@ -35,6 +34,9 @@ class WindowCapturerX11 : public DesktopCapturer, explicit WindowCapturerX11(const DesktopCaptureOptions& options); ~WindowCapturerX11() override; + WindowCapturerX11(const WindowCapturerX11&) = delete; + WindowCapturerX11& operator=(const WindowCapturerX11&) = delete; + static std::unique_ptr CreateRawWindowCapturer( const DesktopCaptureOptions& options); @@ -65,10 +67,8 @@ class WindowCapturerX11 : public DesktopCapturer, XServerPixelBuffer x_server_pixel_buffer_; XAtomCache atom_cache_; WindowFinderX11 window_finder_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerX11); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_X11_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_ diff --git a/modules/desktop_capture/linux/window_finder_x11.cc b/modules/desktop_capture/linux/x11/window_finder_x11.cc similarity index 90% rename from modules/desktop_capture/linux/window_finder_x11.cc rename to modules/desktop_capture/linux/x11/window_finder_x11.cc index 16fb3252ad..dec17ab51f 100644 --- a/modules/desktop_capture/linux/window_finder_x11.cc +++ b/modules/desktop_capture/linux/x11/window_finder_x11.cc @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/window_finder_x11.h" +#include "modules/desktop_capture/linux/x11/window_finder_x11.h" #include #include -#include "modules/desktop_capture/linux/window_list_utils.h" +#include "modules/desktop_capture/linux/x11/window_list_utils.h" #include "rtc_base/checks.h" namespace webrtc { diff --git a/modules/desktop_capture/linux/window_finder_x11.h b/modules/desktop_capture/linux/x11/window_finder_x11.h similarity index 81% rename from modules/desktop_capture/linux/window_finder_x11.h rename to modules/desktop_capture/linux/x11/window_finder_x11.h index d0bba8697b..91de876417 100644 --- a/modules/desktop_capture/linux/window_finder_x11.h +++ b/modules/desktop_capture/linux/x11/window_finder_x11.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_ #include "modules/desktop_capture/window_finder.h" @@ -32,4 +32,4 @@ class WindowFinderX11 final : public WindowFinder { } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_ diff --git a/modules/desktop_capture/linux/window_list_utils.cc b/modules/desktop_capture/linux/x11/window_list_utils.cc similarity index 96% rename from modules/desktop_capture/linux/window_list_utils.cc rename to modules/desktop_capture/linux/x11/window_list_utils.cc index ade92d2be8..ff2d467e29 100644 --- a/modules/desktop_capture/linux/window_list_utils.cc +++ b/modules/desktop_capture/linux/x11/window_list_utils.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/window_list_utils.h" +#include "modules/desktop_capture/linux/x11/window_list_utils.h" #include #include @@ -16,10 +16,9 @@ #include -#include "modules/desktop_capture/linux/x_error_trap.h" -#include "modules/desktop_capture/linux/x_window_property.h" +#include "modules/desktop_capture/linux/x11/x_error_trap.h" +#include "modules/desktop_capture/linux/x11/x_window_property.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" namespace webrtc { diff --git a/modules/desktop_capture/linux/window_list_utils.h b/modules/desktop_capture/linux/x11/window_list_utils.h similarity index 89% rename from modules/desktop_capture/linux/window_list_utils.h rename to modules/desktop_capture/linux/x11/window_list_utils.h index 3ffd336705..923842df14 100644 --- a/modules/desktop_capture/linux/window_list_utils.h +++ b/modules/desktop_capture/linux/x11/window_list_utils.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_ #include #include @@ -17,7 +17,7 @@ #include "api/function_view.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/linux/x_atom_cache.h" +#include "modules/desktop_capture/linux/x11/x_atom_cache.h" namespace webrtc { @@ -53,4 +53,4 @@ DesktopRect DesktopRectFromXAttributes(const T& attributes) { } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_ diff --git a/modules/desktop_capture/linux/x_atom_cache.cc b/modules/desktop_capture/linux/x11/x_atom_cache.cc similarity index 95% rename from modules/desktop_capture/linux/x_atom_cache.cc rename to modules/desktop_capture/linux/x11/x_atom_cache.cc index 4ea024938e..157ba8b8fd 100644 --- a/modules/desktop_capture/linux/x_atom_cache.cc +++ b/modules/desktop_capture/linux/x11/x_atom_cache.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/x_atom_cache.h" +#include "modules/desktop_capture/linux/x11/x_atom_cache.h" #include "rtc_base/checks.h" diff --git a/modules/desktop_capture/linux/x_atom_cache.h b/modules/desktop_capture/linux/x11/x_atom_cache.h similarity index 85% rename from modules/desktop_capture/linux/x_atom_cache.h rename to modules/desktop_capture/linux/x11/x_atom_cache.h index 0249c15978..39d957e98b 100644 --- a/modules/desktop_capture/linux/x_atom_cache.h +++ b/modules/desktop_capture/linux/x11/x_atom_cache.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_ #include #include @@ -42,4 +42,4 @@ class XAtomCache final { } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_ diff --git a/modules/desktop_capture/linux/x_error_trap.cc b/modules/desktop_capture/linux/x11/x_error_trap.cc similarity index 96% rename from modules/desktop_capture/linux/x_error_trap.cc rename to modules/desktop_capture/linux/x11/x_error_trap.cc index 13233d8274..f31565e24b 100644 --- a/modules/desktop_capture/linux/x_error_trap.cc +++ b/modules/desktop_capture/linux/x11/x_error_trap.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/x_error_trap.h" +#include "modules/desktop_capture/linux/x11/x_error_trap.h" #include diff --git a/modules/desktop_capture/linux/x_error_trap.h b/modules/desktop_capture/linux/x11/x_error_trap.h similarity index 76% rename from modules/desktop_capture/linux/x_error_trap.h rename to modules/desktop_capture/linux/x11/x_error_trap.h index 1fb0fdcc92..882d690922 100644 --- a/modules/desktop_capture/linux/x_error_trap.h +++ b/modules/desktop_capture/linux/x11/x_error_trap.h @@ -8,13 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { // Helper class that registers X Window error handler. Caller can use @@ -24,16 +22,17 @@ class XErrorTrap { explicit XErrorTrap(Display* display); ~XErrorTrap(); + XErrorTrap(const XErrorTrap&) = delete; + XErrorTrap& operator=(const XErrorTrap&) = delete; + // Returns last error and removes unregisters the error handler. int GetLastErrorAndDisable(); private: XErrorHandler original_error_handler_; bool enabled_; - - RTC_DISALLOW_COPY_AND_ASSIGN(XErrorTrap); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_ diff --git a/modules/desktop_capture/linux/x_server_pixel_buffer.cc b/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc similarity index 97% rename from modules/desktop_capture/linux/x_server_pixel_buffer.cc rename to modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc index 84b57f1eb7..fd6fc7daf4 100644 --- a/modules/desktop_capture/linux/x_server_pixel_buffer.cc +++ b/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/x_server_pixel_buffer.h" +#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h" #include #include @@ -17,9 +17,9 @@ #include #include "modules/desktop_capture/desktop_frame.h" -#include "modules/desktop_capture/linux/window_list_utils.h" -#include "modules/desktop_capture/linux/x_error_trap.h" -#include "modules/desktop_capture/linux/x_window_property.h" +#include "modules/desktop_capture/linux/x11/window_list_utils.h" +#include "modules/desktop_capture/linux/x11/x_error_trap.h" +#include "modules/desktop_capture/linux/x11/x_window_property.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" diff --git a/modules/desktop_capture/linux/x_server_pixel_buffer.h b/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h similarity index 88% rename from modules/desktop_capture/linux/x_server_pixel_buffer.h rename to modules/desktop_capture/linux/x11/x_server_pixel_buffer.h index aa3f903e0a..38af3a3e76 100644 --- a/modules/desktop_capture/linux/x_server_pixel_buffer.h +++ b/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h @@ -10,8 +10,8 @@ // Don't include this file in any .h files because it pulls in some X headers. -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_ #include #include @@ -20,7 +20,6 @@ #include #include "modules/desktop_capture/desktop_geometry.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -34,6 +33,9 @@ class XServerPixelBuffer { XServerPixelBuffer(); ~XServerPixelBuffer(); + XServerPixelBuffer(const XServerPixelBuffer&) = delete; + XServerPixelBuffer& operator=(const XServerPixelBuffer&) = delete; + void Release(); // Allocate (or reallocate) the pixel buffer for `window`. Returns false in @@ -80,10 +82,8 @@ class XServerPixelBuffer { GC shm_gc_ = nullptr; bool xshm_get_image_succeeded_ = false; std::vector icc_profile_; - - RTC_DISALLOW_COPY_AND_ASSIGN(XServerPixelBuffer); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_ diff --git a/modules/desktop_capture/linux/x_window_property.cc b/modules/desktop_capture/linux/x11/x_window_property.cc similarity index 95% rename from modules/desktop_capture/linux/x_window_property.cc rename to modules/desktop_capture/linux/x11/x_window_property.cc index ba25deec3d..5e16dac404 100644 --- a/modules/desktop_capture/linux/x_window_property.cc +++ b/modules/desktop_capture/linux/x11/x_window_property.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/desktop_capture/linux/x_window_property.h" +#include "modules/desktop_capture/linux/x11/x_window_property.h" namespace webrtc { diff --git a/modules/desktop_capture/linux/x_window_property.h b/modules/desktop_capture/linux/x11/x_window_property.h similarity index 78% rename from modules/desktop_capture/linux/x_window_property.h rename to modules/desktop_capture/linux/x11/x_window_property.h index ef643b6a8f..28dfb97311 100644 --- a/modules/desktop_capture/linux/x_window_property.h +++ b/modules/desktop_capture/linux/x11/x_window_property.h @@ -8,14 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_WINDOW_PROPERTY_H_ -#define MODULES_DESKTOP_CAPTURE_LINUX_X_WINDOW_PROPERTY_H_ +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_ #include #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { class XWindowPropertyBase { @@ -26,6 +24,9 @@ class XWindowPropertyBase { int expected_size); virtual ~XWindowPropertyBase(); + XWindowPropertyBase(const XWindowPropertyBase&) = delete; + XWindowPropertyBase& operator=(const XWindowPropertyBase&) = delete; + // True if we got properly value successfully. bool is_valid() const { return is_valid_; } @@ -38,8 +39,6 @@ class XWindowPropertyBase { private: bool is_valid_ = false; unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty - - RTC_DISALLOW_COPY_AND_ASSIGN(XWindowPropertyBase); }; // Convenience wrapper for XGetWindowProperty() results. @@ -50,14 +49,15 @@ class XWindowProperty : public XWindowPropertyBase { : XWindowPropertyBase(display, window, property, sizeof(PropertyType)) {} ~XWindowProperty() override = default; + XWindowProperty(const XWindowProperty&) = delete; + XWindowProperty& operator=(const XWindowProperty&) = delete; + const PropertyType* data() const { return reinterpret_cast(data_); } PropertyType* data() { return reinterpret_cast(data_); } - - RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty); }; } // namespace webrtc -#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_WINDOW_PROPERTY_H_ +#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_ diff --git a/modules/desktop_capture/mac/desktop_configuration_monitor.h b/modules/desktop_capture/mac/desktop_configuration_monitor.h index aa0ebfbacc..747295a538 100644 --- a/modules/desktop_capture/mac/desktop_configuration_monitor.h +++ b/modules/desktop_capture/mac/desktop_configuration_monitor.h @@ -18,7 +18,6 @@ #include "api/ref_counted_base.h" #include "modules/desktop_capture/mac/desktop_configuration.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" namespace webrtc { @@ -31,6 +30,10 @@ class DesktopConfigurationMonitor final DesktopConfigurationMonitor(); ~DesktopConfigurationMonitor(); + DesktopConfigurationMonitor(const DesktopConfigurationMonitor&) = delete; + DesktopConfigurationMonitor& operator=(const DesktopConfigurationMonitor&) = + delete; + // Returns the current desktop configuration. MacDesktopConfiguration desktop_configuration(); @@ -45,8 +48,6 @@ class DesktopConfigurationMonitor final MacDesktopConfiguration desktop_configuration_ RTC_GUARDED_BY(&desktop_configuration_lock_); std::set reconfiguring_displays_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopConfigurationMonitor); }; } // namespace webrtc diff --git a/modules/desktop_capture/mac/desktop_frame_cgimage.h b/modules/desktop_capture/mac/desktop_frame_cgimage.h index 87804382f2..d6279f9b36 100644 --- a/modules/desktop_capture/mac/desktop_frame_cgimage.h +++ b/modules/desktop_capture/mac/desktop_frame_cgimage.h @@ -35,6 +35,9 @@ class DesktopFrameCGImage final : public DesktopFrame { ~DesktopFrameCGImage() override; + DesktopFrameCGImage(const DesktopFrameCGImage&) = delete; + DesktopFrameCGImage& operator=(const DesktopFrameCGImage&) = delete; + private: static std::unique_ptr CreateFromCGImage( rtc::ScopedCFTypeRef cg_image); @@ -48,8 +51,6 @@ class DesktopFrameCGImage final : public DesktopFrame { const rtc::ScopedCFTypeRef cg_image_; const rtc::ScopedCFTypeRef cg_data_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameCGImage); }; } // namespace webrtc diff --git a/modules/desktop_capture/mac/desktop_frame_iosurface.h b/modules/desktop_capture/mac/desktop_frame_iosurface.h index f79197aa90..73da0f693c 100644 --- a/modules/desktop_capture/mac/desktop_frame_iosurface.h +++ b/modules/desktop_capture/mac/desktop_frame_iosurface.h @@ -30,13 +30,14 @@ class DesktopFrameIOSurface final : public DesktopFrame { ~DesktopFrameIOSurface() override; + DesktopFrameIOSurface(const DesktopFrameIOSurface&) = delete; + DesktopFrameIOSurface& operator=(const DesktopFrameIOSurface&) = delete; + private: // This constructor expects `io_surface` to hold a non-null IOSurfaceRef. explicit DesktopFrameIOSurface(rtc::ScopedCFTypeRef io_surface); const rtc::ScopedCFTypeRef io_surface_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameIOSurface); }; } // namespace webrtc diff --git a/modules/desktop_capture/mac/desktop_frame_provider.h b/modules/desktop_capture/mac/desktop_frame_provider.h index bb45d1ed54..aad28d2f30 100644 --- a/modules/desktop_capture/mac/desktop_frame_provider.h +++ b/modules/desktop_capture/mac/desktop_frame_provider.h @@ -28,6 +28,9 @@ class DesktopFrameProvider { explicit DesktopFrameProvider(bool allow_iosurface); ~DesktopFrameProvider(); + DesktopFrameProvider(const DesktopFrameProvider&) = delete; + DesktopFrameProvider& operator=(const DesktopFrameProvider&) = delete; + // The caller takes ownership of the returned desktop frame. Otherwise // returns null if `display_id` is invalid or not ready. Note that this // function does not remove the frame from the internal container. Caller @@ -49,8 +52,6 @@ class DesktopFrameProvider { // Most recent IOSurface that contains a capture of matching display. std::map> io_surfaces_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameProvider); }; } // namespace webrtc diff --git a/modules/desktop_capture/mac/screen_capturer_mac.h b/modules/desktop_capture/mac/screen_capturer_mac.h index 68b8655b1c..d9a5966efa 100644 --- a/modules/desktop_capture/mac/screen_capturer_mac.h +++ b/modules/desktop_capture/mac/screen_capturer_mac.h @@ -42,6 +42,9 @@ class ScreenCapturerMac final : public DesktopCapturer { bool allow_iosurface); ~ScreenCapturerMac() override; + ScreenCapturerMac(const ScreenCapturerMac&) = delete; + ScreenCapturerMac& operator=(const ScreenCapturerMac&) = delete; + // TODO(julien.isorce): Remove Init() or make it private. bool Init(); @@ -111,8 +114,6 @@ class ScreenCapturerMac final : public DesktopCapturer { // Start, CaptureFrame and destructor have to called in the same thread. SequenceChecker thread_checker_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac); }; } // namespace webrtc diff --git a/modules/desktop_capture/mac/screen_capturer_mac.mm b/modules/desktop_capture/mac/screen_capturer_mac.mm index 701d44476e..634849122e 100644 --- a/modules/desktop_capture/mac/screen_capturer_mac.mm +++ b/modules/desktop_capture/mac/screen_capturer_mac.mm @@ -15,7 +15,6 @@ #include "modules/desktop_capture/mac/desktop_frame_provider.h" #include "modules/desktop_capture/mac/window_list_utils.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" @@ -107,10 +106,10 @@ DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_s CFArrayRef window_array = CGWindowListCreateDescriptionFromArray(window_id_array); if (CFArrayGetCount(window_array) > 0) { - CFDictionaryRef window = + CFDictionaryRef win = reinterpret_cast(CFArrayGetValueAtIndex(window_array, 0)); CFDictionaryRef bounds_ref = - reinterpret_cast(CFDictionaryGetValue(window, kCGWindowBounds)); + reinterpret_cast(CFDictionaryGetValue(win, kCGWindowBounds)); CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect); } @@ -374,13 +373,13 @@ bool ScreenCapturerMac::CgBlit(const DesktopFrame& frame, const DesktopRegion& r // Copy the dirty region from the display buffer into our desktop buffer. uint8_t* out_ptr = frame.GetFrameDataAtPos(display_bounds.top_left()); - for (DesktopRegion::Iterator i(copy_region); !i.IsAtEnd(); i.Advance()) { + for (DesktopRegion::Iterator it(copy_region); !it.IsAtEnd(); it.Advance()) { CopyRect(display_base_address, src_bytes_per_row, out_ptr, frame.stride(), DesktopFrame::kBytesPerPixel, - i.rect()); + it.rect()); } if (excluded_image) { diff --git a/modules/desktop_capture/mock_desktop_capturer_callback.h b/modules/desktop_capture/mock_desktop_capturer_callback.h index 6530dc5542..bb15ceaf4a 100644 --- a/modules/desktop_capture/mock_desktop_capturer_callback.h +++ b/modules/desktop_capture/mock_desktop_capturer_callback.h @@ -22,15 +22,16 @@ class MockDesktopCapturerCallback : public DesktopCapturer::Callback { MockDesktopCapturerCallback(); ~MockDesktopCapturerCallback() override; + MockDesktopCapturerCallback(const MockDesktopCapturerCallback&) = delete; + MockDesktopCapturerCallback& operator=(const MockDesktopCapturerCallback&) = + delete; + MOCK_METHOD(void, OnCaptureResultPtr, (DesktopCapturer::Result result, std::unique_ptr* frame)); void OnCaptureResult(DesktopCapturer::Result result, std::unique_ptr frame) final; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(MockDesktopCapturerCallback); }; } // namespace webrtc diff --git a/modules/desktop_capture/mouse_cursor.h b/modules/desktop_capture/mouse_cursor.h index 0a74140b4b..2dd793179b 100644 --- a/modules/desktop_capture/mouse_cursor.h +++ b/modules/desktop_capture/mouse_cursor.h @@ -15,7 +15,6 @@ #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -29,6 +28,9 @@ class RTC_EXPORT MouseCursor { ~MouseCursor(); + MouseCursor(const MouseCursor&) = delete; + MouseCursor& operator=(const MouseCursor&) = delete; + static MouseCursor* CopyOf(const MouseCursor& cursor); void set_image(DesktopFrame* image) { image_.reset(image); } @@ -40,8 +42,6 @@ class RTC_EXPORT MouseCursor { private: std::unique_ptr image_; DesktopVector hotspot_; - - RTC_DISALLOW_COPY_AND_ASSIGN(MouseCursor); }; } // namespace webrtc diff --git a/modules/desktop_capture/mouse_cursor_monitor_linux.cc b/modules/desktop_capture/mouse_cursor_monitor_linux.cc index e569f6ef35..b44500d4e8 100644 --- a/modules/desktop_capture/mouse_cursor_monitor_linux.cc +++ b/modules/desktop_capture/mouse_cursor_monitor_linux.cc @@ -14,9 +14,13 @@ #include "modules/desktop_capture/mouse_cursor_monitor.h" #if defined(WEBRTC_USE_X11) -#include "modules/desktop_capture/linux/mouse_cursor_monitor_x11.h" +#include "modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h" #endif // defined(WEBRTC_USE_X11) +#if defined(WEBRTC_USE_PIPEWIRE) +#include "modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h" +#endif // defined(WEBRTC_USE_PIPEWIRE) + namespace webrtc { // static @@ -44,6 +48,13 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( // static std::unique_ptr MouseCursorMonitor::Create( const DesktopCaptureOptions& options) { +#if defined(WEBRTC_USE_PIPEWIRE) + if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland() && + options.screencast_stream()) { + return std::make_unique(options); + } +#endif // defined(WEBRTC_USE_PIPEWIRE) + #if defined(WEBRTC_USE_X11) return MouseCursorMonitorX11::Create(options); #else diff --git a/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/modules/desktop_capture/mouse_cursor_monitor_mac.mm index 995aeb9176..3db4332cd1 100644 --- a/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -75,9 +75,9 @@ class MouseCursorMonitorMac : public MouseCursorMonitor { rtc::scoped_refptr configuration_monitor_; CGWindowID window_id_; ScreenId screen_id_; - Callback* callback_; + Callback* callback_ = NULL; Mode mode_; - __strong NSImage* last_cursor_; + __strong NSImage* last_cursor_ = NULL; }; MouseCursorMonitorMac::MouseCursorMonitorMac(const DesktopCaptureOptions& options, @@ -86,7 +86,6 @@ MouseCursorMonitorMac::MouseCursorMonitorMac(const DesktopCaptureOptions& option : configuration_monitor_(options.configuration_monitor()), window_id_(window_id), screen_id_(screen_id), - callback_(NULL), mode_(SHAPE_AND_POSITION) { RTC_DCHECK(window_id == kCGNullWindowID || screen_id == kInvalidScreenId); } diff --git a/modules/desktop_capture/screen_capture_frame_queue.h b/modules/desktop_capture/screen_capture_frame_queue.h index a92bac68d8..f54b8b606a 100644 --- a/modules/desktop_capture/screen_capture_frame_queue.h +++ b/modules/desktop_capture/screen_capture_frame_queue.h @@ -13,7 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" // TODO(zijiehe): These headers are not used in this file, but to avoid build // break in remoting/host. We should add headers in each individual files. #include "modules/desktop_capture/desktop_frame.h" // Remove @@ -40,6 +39,9 @@ class ScreenCaptureFrameQueue { ScreenCaptureFrameQueue() : current_(0) {} ~ScreenCaptureFrameQueue() = default; + ScreenCaptureFrameQueue(const ScreenCaptureFrameQueue&) = delete; + ScreenCaptureFrameQueue& operator=(const ScreenCaptureFrameQueue&) = delete; + // Moves to the next frame in the queue, moving the 'current' frame to become // the 'previous' one. void MoveToNextFrame() { current_ = (current_ + 1) % kQueueLength; } @@ -71,8 +73,6 @@ class ScreenCaptureFrameQueue { static const int kQueueLength = 2; std::unique_ptr frames_[kQueueLength]; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCaptureFrameQueue); }; } // namespace webrtc diff --git a/modules/desktop_capture/screen_capturer_helper.h b/modules/desktop_capture/screen_capturer_helper.h index f7c447aac7..cd7fa689c0 100644 --- a/modules/desktop_capture/screen_capturer_helper.h +++ b/modules/desktop_capture/screen_capturer_helper.h @@ -15,7 +15,6 @@ #include "modules/desktop_capture/desktop_geometry.h" #include "modules/desktop_capture/desktop_region.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -30,6 +29,9 @@ class ScreenCapturerHelper { ScreenCapturerHelper() = default; ~ScreenCapturerHelper() = default; + ScreenCapturerHelper(const ScreenCapturerHelper&) = delete; + ScreenCapturerHelper& operator=(const ScreenCapturerHelper&) = delete; + // Clear out the invalid region. void ClearInvalidRegion(); @@ -82,8 +84,6 @@ class ScreenCapturerHelper { // expanded. // If the value is <= 0, then the invalid region is not expanded to a grid. int log_grid_size_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerHelper); }; } // namespace webrtc diff --git a/modules/desktop_capture/screen_capturer_integration_test.cc b/modules/desktop_capture/screen_capturer_integration_test.cc index 4d3f83a0f9..b33427ad42 100644 --- a/modules/desktop_capture/screen_capturer_integration_test.cc +++ b/modules/desktop_capture/screen_capturer_integration_test.cc @@ -25,7 +25,6 @@ #include "modules/desktop_capture/rgba_color.h" #include "modules/desktop_capture/screen_drawer.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/third_party/base64/base64.h" #include "test/gmock.h" @@ -33,7 +32,7 @@ #if defined(WEBRTC_WIN) #include "modules/desktop_capture/win/screen_capturer_win_directx.h" -#include "rtc_base/win32.h" +#include "rtc_base/win/windows_version.h" #endif // defined(WEBRTC_WIN) using ::testing::_; @@ -338,7 +337,7 @@ TEST_F(ScreenCapturerIntegrationTest, // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844 // TODO(zijiehe): Find the root cause of the border and failure, which cannot // reproduce on my dev machine. - if (rtc::IsWindows8OrLater()) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) { return; } CreateMagnifierCapturer(); @@ -351,7 +350,7 @@ TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoMagnifierCapturers) { // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844 // TODO(zijiehe): Find the root cause of the border and failure, which cannot // reproduce on my dev machine. - if (rtc::IsWindows8OrLater()) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) { return; } CreateMagnifierCapturer(); @@ -362,7 +361,7 @@ TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoMagnifierCapturers) { TEST_F(ScreenCapturerIntegrationTest, DISABLED_MaybeCaptureUpdatedRegionWithDirectxCapturer) { - if (!rtc::IsWindows8OrLater()) { + if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN8) { // ScreenCapturerWinGdi randomly returns blank screen, the root cause is // still unknown. Bug, // https://bugs.chromium.org/p/webrtc/issues/detail?id=6843. diff --git a/modules/desktop_capture/screen_capturer_linux.cc b/modules/desktop_capture/screen_capturer_linux.cc index 6cad2acd64..18840cc6d7 100644 --- a/modules/desktop_capture/screen_capturer_linux.cc +++ b/modules/desktop_capture/screen_capturer_linux.cc @@ -14,11 +14,11 @@ #include "modules/desktop_capture/desktop_capturer.h" #if defined(WEBRTC_USE_PIPEWIRE) -#include "modules/desktop_capture/linux/base_capturer_pipewire.h" +#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h" #endif // defined(WEBRTC_USE_PIPEWIRE) #if defined(WEBRTC_USE_X11) -#include "modules/desktop_capture/linux/screen_capturer_x11.h" +#include "modules/desktop_capture/linux/x11/screen_capturer_x11.h" #endif // defined(WEBRTC_USE_X11) namespace webrtc { @@ -28,7 +28,7 @@ std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( const DesktopCaptureOptions& options) { #if defined(WEBRTC_USE_PIPEWIRE) if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { - return BaseCapturerPipeWire::CreateRawCapturer(options); + return std::make_unique(options); } #endif // defined(WEBRTC_USE_PIPEWIRE) diff --git a/modules/desktop_capture/screen_capturer_unittest.cc b/modules/desktop_capture/screen_capturer_unittest.cc index ba6b8bfe3d..8f5fe631f1 100644 --- a/modules/desktop_capture/screen_capturer_unittest.cc +++ b/modules/desktop_capture/screen_capturer_unittest.cc @@ -15,7 +15,6 @@ #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_region.h" #include "modules/desktop_capture/mock_desktop_capturer_callback.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "test/gmock.h" #include "test/gtest.h" @@ -76,9 +75,11 @@ class FakeSharedMemory : public SharedMemory { : SharedMemory(buffer, size, 0, kTestSharedMemoryId), buffer_(buffer) {} ~FakeSharedMemory() override { delete[] buffer_; } + FakeSharedMemory(const FakeSharedMemory&) = delete; + FakeSharedMemory& operator=(const FakeSharedMemory&) = delete; + private: char* buffer_; - RTC_DISALLOW_COPY_AND_ASSIGN(FakeSharedMemory); }; class FakeSharedMemoryFactory : public SharedMemoryFactory { @@ -86,13 +87,13 @@ class FakeSharedMemoryFactory : public SharedMemoryFactory { FakeSharedMemoryFactory() {} ~FakeSharedMemoryFactory() override {} + FakeSharedMemoryFactory(const FakeSharedMemoryFactory&) = delete; + FakeSharedMemoryFactory& operator=(const FakeSharedMemoryFactory&) = delete; + std::unique_ptr CreateSharedMemory(size_t size) override { return std::unique_ptr( new FakeSharedMemory(new char[size], size)); } - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(FakeSharedMemoryFactory); }; ACTION_P(SaveUniquePtrArg, dest) { diff --git a/modules/desktop_capture/screen_drawer_linux.cc b/modules/desktop_capture/screen_drawer_linux.cc index afd29ced01..fce036b4aa 100644 --- a/modules/desktop_capture/screen_drawer_linux.cc +++ b/modules/desktop_capture/screen_drawer_linux.cc @@ -17,7 +17,7 @@ #include "api/scoped_refptr.h" #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "modules/desktop_capture/linux/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" #include "modules/desktop_capture/rgba_color.h" #include "modules/desktop_capture/screen_drawer.h" #include "modules/desktop_capture/screen_drawer_lock_posix.h" @@ -63,7 +63,7 @@ ScreenDrawerLinux::ScreenDrawerLinux() { if (!XGetWindowAttributes(display_->display(), RootWindow(display_->display(), screen_num_), &root_attributes)) { - RTC_NOTREACHED() << "Failed to get root window size."; + RTC_DCHECK_NOTREACHED() << "Failed to get root window size."; } window_ = XCreateSimpleWindow( display_->display(), RootWindow(display_->display(), screen_num_), 0, 0, @@ -85,7 +85,7 @@ ScreenDrawerLinux::ScreenDrawerLinux() { if (!XTranslateCoordinates(display_->display(), window_, RootWindow(display_->display(), screen_num_), 0, 0, &x, &y, &child)) { - RTC_NOTREACHED() << "Failed to get window position."; + RTC_DCHECK_NOTREACHED() << "Failed to get window position."; } // Some window manager does not allow a window to cover two or more monitors. // So if the window is on the first monitor of a two-monitor system, the diff --git a/modules/desktop_capture/screen_drawer_lock_posix.cc b/modules/desktop_capture/screen_drawer_lock_posix.cc index 095189b05f..bfee619fdb 100644 --- a/modules/desktop_capture/screen_drawer_lock_posix.cc +++ b/modules/desktop_capture/screen_drawer_lock_posix.cc @@ -32,7 +32,7 @@ ScreenDrawerLockPosix::ScreenDrawerLockPosix(const char* name) { semaphore_ = sem_open(name, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1); if (semaphore_ == SEM_FAILED) { RTC_LOG_ERRNO(LS_ERROR) << "Failed to create named semaphore with " << name; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } sem_wait(semaphore_); diff --git a/modules/desktop_capture/shared_desktop_frame.cc b/modules/desktop_capture/shared_desktop_frame.cc index 2ded145478..e374038cbc 100644 --- a/modules/desktop_capture/shared_desktop_frame.cc +++ b/modules/desktop_capture/shared_desktop_frame.cc @@ -21,8 +21,8 @@ SharedDesktopFrame::~SharedDesktopFrame() {} // static std::unique_ptr SharedDesktopFrame::Wrap( std::unique_ptr desktop_frame) { - return std::unique_ptr( - new SharedDesktopFrame(new Core(std::move(desktop_frame)))); + return std::unique_ptr(new SharedDesktopFrame( + rtc::scoped_refptr(new Core(std::move(desktop_frame))))); } SharedDesktopFrame* SharedDesktopFrame::Wrap(DesktopFrame* desktop_frame) { diff --git a/modules/desktop_capture/shared_desktop_frame.h b/modules/desktop_capture/shared_desktop_frame.h index 29f9306b39..c6f52247f4 100644 --- a/modules/desktop_capture/shared_desktop_frame.h +++ b/modules/desktop_capture/shared_desktop_frame.h @@ -15,7 +15,6 @@ #include "api/scoped_refptr.h" #include "modules/desktop_capture/desktop_frame.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/system/rtc_export.h" @@ -27,6 +26,9 @@ class RTC_EXPORT SharedDesktopFrame final : public DesktopFrame { public: ~SharedDesktopFrame() override; + SharedDesktopFrame(const SharedDesktopFrame&) = delete; + SharedDesktopFrame& operator=(const SharedDesktopFrame&) = delete; + static std::unique_ptr Wrap( std::unique_ptr desktop_frame); @@ -56,8 +58,6 @@ class RTC_EXPORT SharedDesktopFrame final : public DesktopFrame { SharedDesktopFrame(rtc::scoped_refptr core); const rtc::scoped_refptr core_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SharedDesktopFrame); }; } // namespace webrtc diff --git a/modules/desktop_capture/shared_memory.h b/modules/desktop_capture/shared_memory.h index 49a9252c1b..a7add4447b 100644 --- a/modules/desktop_capture/shared_memory.h +++ b/modules/desktop_capture/shared_memory.h @@ -21,7 +21,6 @@ typedef void* HANDLE; #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -54,6 +53,9 @@ class RTC_EXPORT SharedMemory { virtual ~SharedMemory() {} + SharedMemory(const SharedMemory&) = delete; + SharedMemory& operator=(const SharedMemory&) = delete; + protected: SharedMemory(void* data, size_t size, Handle handle, int id); @@ -61,9 +63,6 @@ class RTC_EXPORT SharedMemory { const size_t size_; const Handle handle_; const int id_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(SharedMemory); }; // Interface used to create SharedMemory instances. @@ -72,10 +71,10 @@ class SharedMemoryFactory { SharedMemoryFactory() {} virtual ~SharedMemoryFactory() {} - virtual std::unique_ptr CreateSharedMemory(size_t size) = 0; + SharedMemoryFactory(const SharedMemoryFactory&) = delete; + SharedMemoryFactory& operator=(const SharedMemoryFactory&) = delete; - private: - RTC_DISALLOW_COPY_AND_ASSIGN(SharedMemoryFactory); + virtual std::unique_ptr CreateSharedMemory(size_t size) = 0; }; } // namespace webrtc diff --git a/modules/desktop_capture/win/desktop.h b/modules/desktop_capture/win/desktop.h index 382df608c8..01bed8592d 100644 --- a/modules/desktop_capture/win/desktop.h +++ b/modules/desktop_capture/win/desktop.h @@ -15,7 +15,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -24,6 +23,9 @@ class RTC_EXPORT Desktop { public: ~Desktop(); + Desktop(const Desktop&) = delete; + Desktop& operator=(const Desktop&) = delete; + // Returns the name of the desktop represented by the object. Return false if // quering the name failed for any reason. bool GetName(std::wstring* desktop_name_out) const; @@ -56,8 +58,6 @@ class RTC_EXPORT Desktop { // True if `desktop_` must be closed on teardown. bool own_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Desktop); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.cc b/modules/desktop_capture/win/dxgi_output_duplicator.cc index 177e12632c..caad6f43d1 100644 --- a/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -55,7 +55,7 @@ Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) { return Rotation::CLOCK_WISE_270; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return Rotation::CLOCK_WISE_0; } diff --git a/modules/desktop_capture/win/full_screen_win_application_handler.cc b/modules/desktop_capture/win/full_screen_win_application_handler.cc index ad45047cfc..4222dfc01e 100644 --- a/modules/desktop_capture/win/full_screen_win_application_handler.cc +++ b/modules/desktop_capture/win/full_screen_win_application_handler.cc @@ -9,11 +9,14 @@ */ #include "modules/desktop_capture/win/full_screen_win_application_handler.h" + #include #include #include #include #include + +#include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "modules/desktop_capture/win/screen_capture_utils.h" #include "modules/desktop_capture/win/window_capture_utils.h" @@ -145,7 +148,8 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler { std::string GetDocumentFromEditorTitle(HWND window) const { std::string title = WindowText(window); auto position = title.find(kDocumentTitleSeparator); - return rtc::string_trim(title.substr(0, position)); + return std::string(absl::StripAsciiWhitespace( + absl::string_view(title).substr(0, position))); } std::string GetDocumentFromSlideShowTitle(HWND window) const { @@ -158,12 +162,13 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler { if (right_pos > left_pos + kSeparatorLength) { auto result_len = right_pos - left_pos - kSeparatorLength; - auto document = title.substr(left_pos + kSeparatorLength, result_len); - return rtc::string_trim(document); + auto document = absl::string_view(title).substr( + left_pos + kSeparatorLength, result_len); + return std::string(absl::StripAsciiWhitespace(document)); } else { - auto document = - title.substr(left_pos + kSeparatorLength, std::wstring::npos); - return rtc::string_trim(document); + auto document = absl::string_view(title).substr( + left_pos + kSeparatorLength, std::wstring::npos); + return std::string(absl::StripAsciiWhitespace(document)); } } diff --git a/modules/desktop_capture/win/scoped_gdi_object.h b/modules/desktop_capture/win/scoped_gdi_object.h index d3ac9b9443..2b01941e20 100644 --- a/modules/desktop_capture/win/scoped_gdi_object.h +++ b/modules/desktop_capture/win/scoped_gdi_object.h @@ -13,8 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { namespace win { @@ -27,6 +25,9 @@ class ScopedGDIObject { ~ScopedGDIObject() { Traits::Close(handle_); } + ScopedGDIObject(const ScopedGDIObject&) = delete; + ScopedGDIObject& operator=(const ScopedGDIObject&) = delete; + T Get() { return handle_; } void Set(T object) { @@ -50,8 +51,6 @@ class ScopedGDIObject { private: T handle_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); }; // The traits class that uses DeleteObject() to close a handle. diff --git a/modules/desktop_capture/win/scoped_thread_desktop.h b/modules/desktop_capture/win/scoped_thread_desktop.h index 04abaafeeb..98f151a46c 100644 --- a/modules/desktop_capture/win/scoped_thread_desktop.h +++ b/modules/desktop_capture/win/scoped_thread_desktop.h @@ -15,7 +15,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -27,6 +26,9 @@ class RTC_EXPORT ScopedThreadDesktop { ScopedThreadDesktop(); ~ScopedThreadDesktop(); + ScopedThreadDesktop(const ScopedThreadDesktop&) = delete; + ScopedThreadDesktop& operator=(const ScopedThreadDesktop&) = delete; + // Returns true if `desktop` has the same desktop name as the currently // assigned desktop (if assigned) or as the initial desktop (if not assigned). // Returns false in any other case including failing Win32 APIs and @@ -46,8 +48,6 @@ class RTC_EXPORT ScopedThreadDesktop { // The desktop handle assigned to the calling thread at creation. std::unique_ptr initial_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScopedThreadDesktop); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/screen_capture_utils.h b/modules/desktop_capture/win/screen_capture_utils.h index ac60c5abc5..bcb183b9d2 100644 --- a/modules/desktop_capture/win/screen_capture_utils.h +++ b/modules/desktop_capture/win/screen_capture_utils.h @@ -39,24 +39,23 @@ bool GetScreenList(DesktopCapturer::SourceList* screens, // Converts a device index (which are returned by `GetScreenList`) into an // HMONITOR. -bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index, +bool GetHmonitorFromDeviceIndex(DesktopCapturer::SourceId device_index, HMONITOR* hmonitor); // Returns true if `monitor` represents a valid display // monitor. Consumers should recheck the validity of HMONITORs before use if a // WM_DISPLAYCHANGE message has been received. -bool IsMonitorValid(const HMONITOR monitor); +bool IsMonitorValid(HMONITOR monitor); // Returns the rect of the monitor identified by `monitor`, relative to the // primary display's top-left. On failure, returns an empty rect. -DesktopRect GetMonitorRect(const HMONITOR monitor); +DesktopRect GetMonitorRect(HMONITOR monitor); // Returns true if `screen` is a valid screen. The screen device key is // returned through `device_key` if the screen is valid. The device key can be // used in GetScreenRect to verify the screen matches the previously obtained // id. -bool IsScreenValid(const DesktopCapturer::SourceId screen, - std::wstring* device_key); +bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key); // Get the rect of the entire system in system coordinate system. I.e. the // primary monitor always starts from (0, 0). @@ -65,7 +64,7 @@ DesktopRect GetFullscreenRect(); // Get the rect of the screen identified by `screen`, relative to the primary // display's top-left. If the screen device key does not match `device_key`, or // the screen does not exist, or any error happens, an empty rect is returned. -RTC_EXPORT DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen, +RTC_EXPORT DesktopRect GetScreenRect(DesktopCapturer::SourceId screen, const std::wstring& device_key); } // namespace webrtc diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.h b/modules/desktop_capture/win/screen_capturer_win_directx.h index 378f9a7ea8..d64913ed10 100644 --- a/modules/desktop_capture/win/screen_capturer_win_directx.h +++ b/modules/desktop_capture/win/screen_capturer_win_directx.h @@ -73,6 +73,9 @@ class RTC_EXPORT ScreenCapturerWinDirectx : public DesktopCapturer { ~ScreenCapturerWinDirectx() override; + ScreenCapturerWinDirectx(const ScreenCapturerWinDirectx&) = delete; + ScreenCapturerWinDirectx& operator=(const ScreenCapturerWinDirectx&) = delete; + // DesktopCapturer implementation. void Start(Callback* callback) override; void SetSharedMemoryFactory( @@ -87,8 +90,6 @@ class RTC_EXPORT ScreenCapturerWinDirectx : public DesktopCapturer { std::unique_ptr shared_memory_factory_; Callback* callback_ = nullptr; SourceId current_screen_id_ = kFullDesktopScreenId; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinDirectx); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/modules/desktop_capture/win/screen_capturer_win_gdi.cc index 231754c952..57b1f71b0d 100644 --- a/modules/desktop_capture/win/screen_capturer_win_gdi.cc +++ b/modules/desktop_capture/win/screen_capturer_win_gdi.cc @@ -86,7 +86,7 @@ void ScreenCapturerWinGdi::CaptureFrame() { PrepareCaptureResources(); if (!CaptureImage()) { - RTC_LOG(WARNING) << "Failed to capture screen by GDI."; + RTC_LOG(LS_WARNING) << "Failed to capture screen by GDI."; callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); return; } diff --git a/modules/desktop_capture/win/screen_capturer_win_gdi.h b/modules/desktop_capture/win/screen_capturer_win_gdi.h index e006c79021..7c3977ed42 100644 --- a/modules/desktop_capture/win/screen_capturer_win_gdi.h +++ b/modules/desktop_capture/win/screen_capturer_win_gdi.h @@ -20,7 +20,6 @@ #include "modules/desktop_capture/shared_desktop_frame.h" #include "modules/desktop_capture/win/display_configuration_monitor.h" #include "modules/desktop_capture/win/scoped_thread_desktop.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -35,6 +34,9 @@ class ScreenCapturerWinGdi : public DesktopCapturer { explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options); ~ScreenCapturerWinGdi() override; + ScreenCapturerWinGdi(const ScreenCapturerWinGdi&) = delete; + ScreenCapturerWinGdi& operator=(const ScreenCapturerWinGdi&) = delete; + // Overridden from ScreenCapturer: void Start(Callback* callback) override; void SetSharedMemoryFactory( @@ -74,8 +76,6 @@ class ScreenCapturerWinGdi : public DesktopCapturer { HMODULE dwmapi_library_ = NULL; DwmEnableCompositionFunc composition_func_ = nullptr; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinGdi); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/modules/desktop_capture/win/screen_capturer_win_magnifier.h index 29966e9535..07c5b1e9e6 100644 --- a/modules/desktop_capture/win/screen_capturer_win_magnifier.h +++ b/modules/desktop_capture/win/screen_capturer_win_magnifier.h @@ -22,7 +22,6 @@ #include "modules/desktop_capture/screen_capturer_helper.h" #include "modules/desktop_capture/shared_desktop_frame.h" #include "modules/desktop_capture/win/scoped_thread_desktop.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -42,6 +41,10 @@ class ScreenCapturerWinMagnifier : public DesktopCapturer { ScreenCapturerWinMagnifier(); ~ScreenCapturerWinMagnifier() override; + ScreenCapturerWinMagnifier(const ScreenCapturerWinMagnifier&) = delete; + ScreenCapturerWinMagnifier& operator=(const ScreenCapturerWinMagnifier&) = + delete; + // Overridden from ScreenCapturer: void Start(Callback* callback) override; void SetSharedMemoryFactory( @@ -130,8 +133,6 @@ class ScreenCapturerWinMagnifier : public DesktopCapturer { // True if the last OnMagImageScalingCallback was called and handled // successfully. Reset at the beginning of each CaptureImage call. bool magnifier_capture_succeeded_ = true; - - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinMagnifier); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/test_support/test_window.h b/modules/desktop_capture/win/test_support/test_window.h index 8701dc990b..b055da7ccd 100644 --- a/modules/desktop_capture/win/test_support/test_window.h +++ b/modules/desktop_capture/win/test_support/test_window.h @@ -32,17 +32,17 @@ struct WindowInfo { }; WindowInfo CreateTestWindow(const WCHAR* window_title, - const int height = 0, - const int width = 0, - const LONG extended_styles = 0); + int height = 0, + int width = 0, + LONG extended_styles = 0); -void ResizeTestWindow(const HWND hwnd, const int width, const int height); +void ResizeTestWindow(HWND hwnd, int width, int height); -void MoveTestWindow(const HWND hwnd, const int x, const int y); +void MoveTestWindow(HWND hwnd, int x, int y); -void MinimizeTestWindow(const HWND hwnd); +void MinimizeTestWindow(HWND hwnd); -void UnminimizeTestWindow(const HWND hwnd); +void UnminimizeTestWindow(HWND hwnd); void DestroyTestWindow(WindowInfo info); diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc index 22dbf90204..b6b566b3ee 100644 --- a/modules/desktop_capture/win/wgc_capture_session.cc +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -223,8 +223,8 @@ HRESULT WgcCaptureSession::GetFrame( return hr; } - // We need to get this CaptureFrame as an ID3D11Texture2D so that we can get - // the raw image data in the format required by the DesktopFrame interface. + // We need to get `capture_frame` as an `ID3D11Texture2D` so that we can get + // the raw image data in the format required by the `DesktopFrame` interface. ComPtr d3d_surface; hr = capture_frame->get_Surface(&d3d_surface); @@ -261,16 +261,6 @@ HRESULT WgcCaptureSession::GetFrame( // Otherwise it would only be readable by the GPU. ComPtr d3d_context; d3d11_device_->GetImmediateContext(&d3d_context); - d3d_context->CopyResource(mapped_texture_.Get(), texture_2D.Get()); - - D3D11_MAPPED_SUBRESOURCE map_info; - hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0, - D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0, - &map_info); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kMapFrameFailed); - return hr; - } ABI::Windows::Graphics::SizeInt32 new_size; hr = capture_frame->get_ContentSize(&new_size); @@ -279,34 +269,9 @@ HRESULT WgcCaptureSession::GetFrame( return hr; } - // If the size has changed since the last capture, we must be sure to use - // the smaller dimensions. Otherwise we might overrun our buffer, or - // read stale data from the last frame. - int image_height = std::min(previous_size_.Height, new_size.Height); - int image_width = std::min(previous_size_.Width, new_size.Width); - int row_data_length = image_width * DesktopFrame::kBytesPerPixel; - - // Make a copy of the data pointed to by `map_info.pData` so we are free to - // unmap our texture. - uint8_t* src_data = static_cast(map_info.pData); - std::vector image_data; - image_data.reserve(image_height * row_data_length); - uint8_t* image_data_ptr = image_data.data(); - for (int i = 0; i < image_height; i++) { - memcpy(image_data_ptr, src_data, row_data_length); - image_data_ptr += row_data_length; - src_data += map_info.RowPitch; - } - - // Transfer ownership of `image_data` to the output_frame. - DesktopSize size(image_width, image_height); - *output_frame = std::make_unique(size, row_data_length, - std::move(image_data)); - - d3d_context->Unmap(mapped_texture_.Get(), 0); - - // If the size changed, we must resize the texture and frame pool to fit the - // new size. + // If the size changed, we must resize `mapped_texture_` and `frame_pool_` to + // fit the new size. This must be done before `CopySubresourceRegion` so that + // the textures are the same size. if (previous_size_.Height != new_size.Height || previous_size_.Width != new_size.Width) { hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); @@ -323,9 +288,57 @@ HRESULT WgcCaptureSession::GetFrame( } } - RecordGetFrameResult(GetFrameResult::kSuccess); + // If the size has changed since the last capture, we must be sure to use + // the smaller dimensions. Otherwise we might overrun our buffer, or + // read stale data from the last frame. + int image_height = std::min(previous_size_.Height, new_size.Height); + int image_width = std::min(previous_size_.Width, new_size.Width); + + D3D11_BOX copy_region; + copy_region.left = 0; + copy_region.top = 0; + copy_region.right = image_width; + copy_region.bottom = image_height; + // Our textures are 2D so we just want one "slice" of the box. + copy_region.front = 0; + copy_region.back = 1; + d3d_context->CopySubresourceRegion(mapped_texture_.Get(), + /*dst_subresource_index=*/0, /*dst_x=*/0, + /*dst_y=*/0, /*dst_z=*/0, texture_2D.Get(), + /*src_subresource_index=*/0, ©_region); + + D3D11_MAPPED_SUBRESOURCE map_info; + hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0, + D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0, + &map_info); + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kMapFrameFailed); + return hr; + } + + int row_data_length = image_width * DesktopFrame::kBytesPerPixel; + + // Make a copy of the data pointed to by `map_info.pData` so we are free to + // unmap our texture. + uint8_t* src_data = static_cast(map_info.pData); + std::vector image_data; + image_data.resize(image_height * row_data_length); + uint8_t* image_data_ptr = image_data.data(); + for (int i = 0; i < image_height; i++) { + memcpy(image_data_ptr, src_data, row_data_length); + image_data_ptr += row_data_length; + src_data += map_info.RowPitch; + } + + d3d_context->Unmap(mapped_texture_.Get(), 0); + + // Transfer ownership of `image_data` to the output_frame. + DesktopSize size(image_width, image_height); + *output_frame = std::make_unique(size, row_data_length, + std::move(image_data)); previous_size_ = new_size; + RecordGetFrameResult(GetFrameResult::kSuccess); return hr; } diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc index 9b2cce4a8e..35d7bd1cec 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.cc +++ b/modules/desktop_capture/win/wgc_capturer_win.cc @@ -47,18 +47,22 @@ void RecordWgcCapturerResult(WgcCapturerResult error) { WgcCapturerWin::WgcCapturerWin( std::unique_ptr source_factory, - std::unique_ptr source_enumerator) + std::unique_ptr source_enumerator, + bool allow_delayed_capturable_check) : source_factory_(std::move(source_factory)), - source_enumerator_(std::move(source_enumerator)) {} + source_enumerator_(std::move(source_enumerator)), + allow_delayed_capturable_check_(allow_delayed_capturable_check) {} WgcCapturerWin::~WgcCapturerWin() = default; // static std::unique_ptr WgcCapturerWin::CreateRawWindowCapturer( - const DesktopCaptureOptions& options) { + const DesktopCaptureOptions& options, + bool allow_delayed_capturable_check) { return std::make_unique( std::make_unique(), std::make_unique( - options.enumerate_current_process_windows())); + options.enumerate_current_process_windows()), + allow_delayed_capturable_check); } // static @@ -66,7 +70,7 @@ std::unique_ptr WgcCapturerWin::CreateRawScreenCapturer( const DesktopCaptureOptions& options) { return std::make_unique( std::make_unique(), - std::make_unique()); + std::make_unique(), false); } bool WgcCapturerWin::GetSourceList(SourceList* sources) { @@ -75,6 +79,9 @@ bool WgcCapturerWin::GetSourceList(SourceList* sources) { bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { capture_source_ = source_factory_->CreateCaptureSource(id); + if (allow_delayed_capturable_check_) + return true; + return capture_source_->IsCapturable(); } @@ -135,6 +142,13 @@ void WgcCapturerWin::CaptureFrame() { return; } + if (allow_delayed_capturable_check_ && !capture_source_->IsCapturable()) { + RTC_LOG(LS_ERROR) << "Source is not capturable."; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + int64_t capture_start_time_nanos = rtc::TimeNanos(); HRESULT hr; diff --git a/modules/desktop_capture/win/wgc_capturer_win.h b/modules/desktop_capture/win/wgc_capturer_win.h index 34e6874dc0..0eef5283c7 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.h +++ b/modules/desktop_capture/win/wgc_capturer_win.h @@ -80,7 +80,8 @@ class ScreenEnumerator final : public SourceEnumerator { class WgcCapturerWin : public DesktopCapturer { public: WgcCapturerWin(std::unique_ptr source_factory, - std::unique_ptr source_enumerator); + std::unique_ptr source_enumerator, + bool allow_delayed_capturable_check); WgcCapturerWin(const WgcCapturerWin&) = delete; WgcCapturerWin& operator=(const WgcCapturerWin&) = delete; @@ -88,7 +89,8 @@ class WgcCapturerWin : public DesktopCapturer { ~WgcCapturerWin() override; static std::unique_ptr CreateRawWindowCapturer( - const DesktopCaptureOptions& options); + const DesktopCaptureOptions& options, + bool allow_delayed_capturable_check = false); static std::unique_ptr CreateRawScreenCapturer( const DesktopCaptureOptions& options); @@ -128,6 +130,11 @@ class WgcCapturerWin : public DesktopCapturer { // returns. Callback* callback_ = nullptr; + // WgcCaptureSource::IsCapturable is expensive to run. So, caller can + // delay capturable check till capture frame is called if the WgcCapturerWin + // is used as a fallback capturer. + bool allow_delayed_capturable_check_ = false; + // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who // require one to perform the capture. Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index 441ff3ba56..fe18577ae9 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -138,7 +138,7 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, } void StartWindowThreadMessageLoop() { - window_thread_->PostTask(RTC_FROM_HERE, [this]() { + window_thread_->PostTask([this]() { MSG msg; BOOL gm; while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) { diff --git a/modules/desktop_capture/win/window_capture_utils.cc b/modules/desktop_capture/win/window_capture_utils.cc index 032465fa88..ccfef49bc5 100644 --- a/modules/desktop_capture/win/window_capture_utils.cc +++ b/modules/desktop_capture/win/window_capture_utils.cc @@ -20,7 +20,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/string_utils.h" -#include "rtc_base/win32.h" +#include "rtc_base/win/windows_version.h" namespace webrtc { @@ -179,7 +179,8 @@ bool GetCroppedWindowRect(HWND window, // As of Windows8, transparent resize borders are added by the OS at // left/bottom/right sides of a resizeable window. If the cropped window // doesn't remove these borders, the background will be exposed a bit. - if (rtc::IsWindows8OrLater() || is_maximized) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8 || + is_maximized) { // Only apply this cropping to windows with a resize border (otherwise, // it'd clip the edges of captured pop-up windows without this border). LONG style = GetWindowLong(window, GWL_STYLE); @@ -311,7 +312,7 @@ WindowCaptureHelperWin::WindowCaptureHelperWin() { GetProcAddress(dwmapi_library_, "DwmGetWindowAttribute")); } - if (rtc::IsWindows10OrLater()) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10) { if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&virtual_desktop_manager_)))) { diff --git a/modules/desktop_capture/win/window_capture_utils.h b/modules/desktop_capture/win/window_capture_utils.h index 335d873742..caea07958d 100644 --- a/modules/desktop_capture/win/window_capture_utils.h +++ b/modules/desktop_capture/win/window_capture_utils.h @@ -17,7 +17,6 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_geometry.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -104,6 +103,9 @@ class WindowCaptureHelperWin { WindowCaptureHelperWin(); ~WindowCaptureHelperWin(); + WindowCaptureHelperWin(const WindowCaptureHelperWin&) = delete; + WindowCaptureHelperWin& operator=(const WindowCaptureHelperWin&) = delete; + bool IsAeroEnabled(); bool IsWindowChromeNotification(HWND hwnd); bool AreWindowsOverlapping(HWND hwnd, @@ -127,8 +129,6 @@ class WindowCaptureHelperWin { // Only used on Win10+. Microsoft::WRL::ComPtr virtual_desktop_manager_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WindowCaptureHelperWin); }; } // namespace webrtc diff --git a/modules/desktop_capture/win/window_capture_utils_unittest.cc b/modules/desktop_capture/win/window_capture_utils_unittest.cc index f3da4b52cb..913b9205c0 100644 --- a/modules/desktop_capture/win/window_capture_utils_unittest.cc +++ b/modules/desktop_capture/win/window_capture_utils_unittest.cc @@ -39,7 +39,7 @@ std::unique_ptr SetUpUnresponsiveWindow(std::mutex& mtx, // Intentionally create a deadlock to cause the window to become unresponsive. mtx.lock(); - window_thread->PostTask(RTC_FROM_HERE, [&mtx]() { + window_thread->PostTask([&mtx]() { mtx.lock(); mtx.unlock(); }); diff --git a/modules/desktop_capture/win/window_capturer_win_gdi.cc b/modules/desktop_capture/win/window_capturer_win_gdi.cc index 4a73a9ac14..22b0c44aeb 100644 --- a/modules/desktop_capture/win/window_capturer_win_gdi.cc +++ b/modules/desktop_capture/win/window_capturer_win_gdi.cc @@ -29,7 +29,7 @@ #include "rtc_base/string_utils.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" -#include "rtc_base/win32.h" +#include "rtc_base/win/windows_version.h" #include "system_wrappers/include/metrics.h" namespace webrtc { @@ -295,7 +295,7 @@ WindowCapturerWinGdi::CaptureResults WindowCapturerWinGdi::CaptureFrame( // on Windows 8.1 and later, PrintWindow is only used when the window is // occluded. When the window is not occluded, it is much faster to capture // the screen and to crop it to the window position and size. - if (rtc::IsWindows8OrLater()) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) { // Special flag that makes PrintWindow to work on Windows 8.1 and later. // Indeed certain apps (e.g. those using DirectComposition rendering) can't // be captured using BitBlt or PrintWindow without this flag. Note that on diff --git a/modules/desktop_capture/window_capturer_linux.cc b/modules/desktop_capture/window_capturer_linux.cc index 89ba90d80e..638c42ae39 100644 --- a/modules/desktop_capture/window_capturer_linux.cc +++ b/modules/desktop_capture/window_capturer_linux.cc @@ -14,11 +14,11 @@ #include "modules/desktop_capture/desktop_capturer.h" #if defined(WEBRTC_USE_PIPEWIRE) -#include "modules/desktop_capture/linux/base_capturer_pipewire.h" +#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h" #endif // defined(WEBRTC_USE_PIPEWIRE) #if defined(WEBRTC_USE_X11) -#include "modules/desktop_capture/linux/window_capturer_x11.h" +#include "modules/desktop_capture/linux/x11/window_capturer_x11.h" #endif // defined(WEBRTC_USE_X11) namespace webrtc { @@ -28,7 +28,7 @@ std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { #if defined(WEBRTC_USE_PIPEWIRE) if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { - return BaseCapturerPipeWire::CreateRawCapturer(options); + return std::make_unique(options); } #endif // defined(WEBRTC_USE_PIPEWIRE) diff --git a/modules/desktop_capture/window_capturer_mac.mm b/modules/desktop_capture/window_capturer_mac.mm index de15d65ac5..f0b413b0a6 100644 --- a/modules/desktop_capture/window_capturer_mac.mm +++ b/modules/desktop_capture/window_capturer_mac.mm @@ -24,7 +24,6 @@ #include "modules/desktop_capture/mac/window_list_utils.h" #include "modules/desktop_capture/window_finder_mac.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" #include "rtc_base/trace_event.h" @@ -52,6 +51,9 @@ class WindowCapturerMac : public DesktopCapturer { rtc::scoped_refptr configuration_monitor); ~WindowCapturerMac() override; + WindowCapturerMac(const WindowCapturerMac&) = delete; + WindowCapturerMac& operator=(const WindowCapturerMac&) = delete; + // DesktopCapturer interface. void Start(Callback* callback) override; void CaptureFrame() override; @@ -71,8 +73,6 @@ class WindowCapturerMac : public DesktopCapturer { const rtc::scoped_refptr configuration_monitor_; WindowFinderMac window_finder_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac); }; WindowCapturerMac::WindowCapturerMac( diff --git a/modules/desktop_capture/window_capturer_null.cc b/modules/desktop_capture/window_capturer_null.cc index e7c7b0a134..6da2a76691 100644 --- a/modules/desktop_capture/window_capturer_null.cc +++ b/modules/desktop_capture/window_capturer_null.cc @@ -11,7 +11,6 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -22,6 +21,9 @@ class WindowCapturerNull : public DesktopCapturer { WindowCapturerNull(); ~WindowCapturerNull() override; + WindowCapturerNull(const WindowCapturerNull&) = delete; + WindowCapturerNull& operator=(const WindowCapturerNull&) = delete; + // DesktopCapturer interface. void Start(Callback* callback) override; void CaptureFrame() override; @@ -30,8 +32,6 @@ class WindowCapturerNull : public DesktopCapturer { private: Callback* callback_ = nullptr; - - RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull); }; WindowCapturerNull::WindowCapturerNull() {} diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc index 4bfa09f4d6..7f7bea6eff 100644 --- a/modules/desktop_capture/window_capturer_win.cc +++ b/modules/desktop_capture/window_capturer_win.cc @@ -12,12 +12,37 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/win/window_capturer_win_gdi.h" +#if defined(RTC_ENABLE_WIN_WGC) +#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h" +#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h" +#include "modules/desktop_capture/win/wgc_capturer_win.h" +#include "rtc_base/win/windows_version.h" +#endif // defined(RTC_ENABLE_WIN_WGC) + namespace webrtc { // static std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { - return WindowCapturerWinGdi::CreateRawWindowCapturer(options); + std::unique_ptr capturer( + WindowCapturerWinGdi::CreateRawWindowCapturer(options)); +#if defined(RTC_ENABLE_WIN_WGC) + if (options.allow_wgc_capturer_fallback() && + rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_RS5) { + // BlankDectector capturer will send an error when it detects a failed + // GDI rendering, then Fallback capturer will try to capture it again with + // WGC. + capturer = std::make_unique( + std::move(capturer), RgbaColor(0, 0, 0, 0), + /*check_per_capture*/ true); + + capturer = std::make_unique( + std::move(capturer), + WgcCapturerWin::CreateRawWindowCapturer( + options, /*allow_delayed_capturable_check*/ true)); + } +#endif // defined(RTC_ENABLE_WIN_WGC) + return capturer; } } // namespace webrtc diff --git a/modules/desktop_capture/window_finder_unittest.cc b/modules/desktop_capture/window_finder_unittest.cc index 7a586c7361..ac13f124d3 100644 --- a/modules/desktop_capture/window_finder_unittest.cc +++ b/modules/desktop_capture/window_finder_unittest.cc @@ -21,8 +21,8 @@ #include "test/gtest.h" #if defined(WEBRTC_USE_X11) -#include "modules/desktop_capture/linux/shared_x_display.h" -#include "modules/desktop_capture/linux/x_atom_cache.h" +#include "modules/desktop_capture/linux/x11/shared_x_display.h" +#include "modules/desktop_capture/linux/x11/x_atom_cache.h" #endif #if defined(WEBRTC_WIN) diff --git a/modules/pacing/BUILD.gn b/modules/pacing/BUILD.gn index 0787105f14..90e0eb7134 100644 --- a/modules/pacing/BUILD.gn +++ b/modules/pacing/BUILD.gn @@ -35,6 +35,7 @@ rtc_library("pacing") { "..:module_api", "../../api:function_view", "../../api:sequence_checker", + "../../api:webrtc_key_value_config", "../../api/rtc_event_log", "../../api/task_queue:task_queue", "../../api/transport:field_trial_based_config", @@ -48,13 +49,13 @@ rtc_library("pacing") { "../../logging:rtc_event_pacing", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/synchronization:mutex", "../../rtc_base/task_utils:to_queued_task", "../../system_wrappers", "../../system_wrappers:metrics", - "../remote_bitrate_estimator", "../rtp_rtcp", "../rtp_rtcp:rtp_rtcp_format", "../utility", @@ -93,6 +94,7 @@ if (rtc_include_tests) { deps = [ ":interval_budget", ":pacing", + "../../api/task_queue:task_queue", "../../api/transport:network_control", "../../api/units:data_rate", "../../api/units:time_delta", @@ -101,10 +103,10 @@ if (rtc_include_tests) { "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_base_tests_utils", "../../rtc_base/experiments:alr_experiment", + "../../rtc_base/task_utils:to_queued_task", "../../system_wrappers", - "../../system_wrappers:field_trial", "../../test:explicit_key_value_config", - "../../test:field_trial", + "../../test:scoped_key_value_config", "../../test:test_support", "../../test/time_controller:time_controller", "../rtp_rtcp", diff --git a/modules/pacing/paced_sender.cc b/modules/pacing/paced_sender.cc index 51d3edc301..56a14105f7 100644 --- a/modules/pacing/paced_sender.cc +++ b/modules/pacing/paced_sender.cc @@ -16,7 +16,6 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" -#include "api/rtc_event_log/rtc_event_log.h" #include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" @@ -31,20 +30,14 @@ const float PacedSender::kDefaultPaceMultiplier = 2.5f; PacedSender::PacedSender(Clock* clock, PacketRouter* packet_router, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const WebRtcKeyValueConfig& field_trials, ProcessThread* process_thread) : process_mode_( - (field_trials != nullptr && - absl::StartsWith(field_trials->Lookup("WebRTC-Pacer-DynamicProcess"), - "Enabled")) + absl::StartsWith(field_trials.Lookup("WebRTC-Pacer-DynamicProcess"), + "Enabled") ? PacingController::ProcessMode::kDynamic : PacingController::ProcessMode::kPeriodic), - pacing_controller_(clock, - packet_router, - event_log, - field_trials, - process_mode_), + pacing_controller_(clock, packet_router, field_trials, process_mode_), clock_(clock), process_thread_(process_thread) { if (process_thread_) @@ -88,18 +81,10 @@ void PacedSender::Resume() { } } -void PacedSender::SetCongestionWindow(DataSize congestion_window_size) { +void PacedSender::SetCongested(bool congested) { { MutexLock lock(&mutex_); - pacing_controller_.SetCongestionWindow(congestion_window_size); - } - MaybeWakupProcessThread(); -} - -void PacedSender::UpdateOutstandingData(DataSize outstanding_data) { - { - MutexLock lock(&mutex_); - pacing_controller_.UpdateOutstandingData(outstanding_data); + pacing_controller_.SetCongested(congested); } MaybeWakupProcessThread(); } @@ -124,7 +109,7 @@ void PacedSender::EnqueuePackets( packet->SequenceNumber(), "rtp_timestamp", packet->Timestamp()); - RTC_DCHECK_GE(packet->capture_time_ms(), 0); + RTC_DCHECK_GE(packet->capture_time(), Timestamp::Zero()); pacing_controller_.EnqueuePacket(std::move(packet)); } } @@ -163,7 +148,15 @@ absl::optional PacedSender::FirstSentPacketTime() const { TimeDelta PacedSender::OldestPacketWaitTime() const { MutexLock lock(&mutex_); - return pacing_controller_.OldestPacketWaitTime(); + Timestamp oldest_packet = pacing_controller_.OldestPacketEnqueueTime(); + if (oldest_packet.IsInfinite()) + return TimeDelta::Zero(); + + // (webrtc:9716): The clock is not always monotonic. + Timestamp current = clock_->CurrentTime(); + if (current < oldest_packet) + return TimeDelta::Zero(); + return current - oldest_packet; } int64_t PacedSender::TimeUntilNextProcess() { diff --git a/modules/pacing/paced_sender.h b/modules/pacing/paced_sender.h index fe29bc567b..e938a1e9e3 100644 --- a/modules/pacing/paced_sender.h +++ b/modules/pacing/paced_sender.h @@ -37,13 +37,8 @@ namespace webrtc { class Clock; -class RtcEventLog; -// TODO(bugs.webrtc.org/10937): Remove the inheritance from Module after -// updating dependencies. -class PacedSender : public Module, - public RtpPacketPacer, - public RtpPacketSender { +class PacedSender : public RtpPacketPacer, public RtpPacketSender { public: // Expected max pacer delay in ms. If ExpectedQueueTime() is higher than // this value, the packet producers should wait (eg drop frames rather than @@ -61,8 +56,7 @@ class PacedSender : public Module, // optional once all callers have been updated. PacedSender(Clock* clock, PacketRouter* packet_router, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials = nullptr, + const WebRtcKeyValueConfig& field_trials, ProcessThread* process_thread = nullptr); ~PacedSender() override; @@ -84,8 +78,7 @@ class PacedSender : public Module, // Resume sending packets. void Resume() override; - void SetCongestionWindow(DataSize congestion_window_size) override; - void UpdateOutstandingData(DataSize outstanding_data) override; + void SetCongested(bool congested) override; // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) override; @@ -117,24 +110,13 @@ class PacedSender : public Module, // to module processing thread specifics or methods exposed for test. private: - // Methods implementing Module. - // TODO(bugs.webrtc.org/10937): Remove the inheritance from Module once all - // use of it has been cleared up. - // Returns the number of milliseconds until the module want a worker thread // to call Process. - int64_t TimeUntilNextProcess() override; - - // TODO(bugs.webrtc.org/10937): Make this private (and non virtual) once - // dependencies have been updated to not call this via the PacedSender - // interface. - public: - // Process any pending packets in the queue(s). - void Process() override; - - private: + int64_t TimeUntilNextProcess(); // Called when the prober is associated with a process thread. - void ProcessThreadAttached(ProcessThread* process_thread) override; + void ProcessThreadAttached(ProcessThread* process_thread); + // Process any pending packets in the queue(s). + void Process(); // In dynamic process mode, refreshes the next process time. void MaybeWakupProcessThread(); diff --git a/modules/pacing/paced_sender_unittest.cc b/modules/pacing/paced_sender_unittest.cc index 53cc1c42ed..6f2728b72b 100644 --- a/modules/pacing/paced_sender_unittest.cc +++ b/modules/pacing/paced_sender_unittest.cc @@ -19,8 +19,6 @@ #include "modules/pacing/packet_router.h" #include "modules/utility/include/mock/mock_process_thread.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -80,8 +78,8 @@ class PacedSenderTest EXPECT_CALL(process_thread_, RegisterModule) .WillOnce(SaveArg<0>(&paced_module_)); - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - &trials_, &process_thread_); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + &process_thread_); EXPECT_CALL(process_thread_, WakeUp).WillRepeatedly([&](Module* module) { clock_.AdvanceTimeMilliseconds(module->TimeUntilNextProcess()); }); diff --git a/modules/pacing/pacing_controller.cc b/modules/pacing/pacing_controller.cc index 4ebc36b08c..dbf1c29d86 100644 --- a/modules/pacing/pacing_controller.cc +++ b/modules/pacing/pacing_controller.cc @@ -18,7 +18,6 @@ #include "absl/strings/match.h" #include "modules/pacing/bitrate_prober.h" #include "modules/pacing/interval_budget.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" @@ -98,23 +97,20 @@ const TimeDelta PacingController::kMinSleepTime = TimeDelta::Millis(1); PacingController::PacingController(Clock* clock, PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const WebRtcKeyValueConfig& field_trials, ProcessMode mode) : mode_(mode), clock_(clock), packet_sender_(packet_sender), - fallback_field_trials_( - !field_trials ? std::make_unique() : nullptr), - field_trials_(field_trials ? field_trials : fallback_field_trials_.get()), + field_trials_(field_trials), drain_large_queues_( - !IsDisabled(*field_trials_, "WebRTC-Pacer-DrainQueue")), + !IsDisabled(field_trials_, "WebRTC-Pacer-DrainQueue")), send_padding_if_silent_( - IsEnabled(*field_trials_, "WebRTC-Pacer-PadInSilence")), - pace_audio_(IsEnabled(*field_trials_, "WebRTC-Pacer-BlockAudio")), + IsEnabled(field_trials_, "WebRTC-Pacer-PadInSilence")), + pace_audio_(IsEnabled(field_trials_, "WebRTC-Pacer-BlockAudio")), ignore_transport_overhead_( - IsEnabled(*field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")), - padding_target_duration_(GetDynamicPaddingTarget(*field_trials_)), + IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")), + padding_target_duration_(GetDynamicPaddingTarget(field_trials_)), min_packet_limit_(kDefaultMinPacketLimit), transport_overhead_per_packet_(DataSize::Zero()), last_timestamp_(clock_->CurrentTime()), @@ -125,15 +121,14 @@ PacingController::PacingController(Clock* clock, padding_debt_(DataSize::Zero()), media_rate_(DataRate::Zero()), padding_rate_(DataRate::Zero()), - prober_(*field_trials_), + prober_(field_trials_), probing_send_failure_(false), pacing_bitrate_(DataRate::Zero()), last_process_time_(clock->CurrentTime()), last_send_time_(last_process_time_), - packet_queue_(last_process_time_, field_trials_), + packet_queue_(last_process_time_), packet_counter_(0), - congestion_window_size_(DataSize::PlusInfinity()), - outstanding_data_(DataSize::Zero()), + congested_(false), queue_time_limit(kMaxExpectedQueueLength), account_for_audio_(false), include_overhead_(false) { @@ -143,7 +138,7 @@ PacingController::PacingController(Clock* clock, } FieldTrialParameter min_packet_limit_ms("", min_packet_limit_.ms()); ParseFieldTrial({&min_packet_limit_ms}, - field_trials_->Lookup("WebRTC-Pacer-MinPacketLimitMs")); + field_trials_.Lookup("WebRTC-Pacer-MinPacketLimitMs")); min_packet_limit_ = TimeDelta::Millis(min_packet_limit_ms.Get()); UpdateBudgetWithElapsedTime(min_packet_limit_); } @@ -172,29 +167,11 @@ bool PacingController::IsPaused() const { return paused_; } -void PacingController::SetCongestionWindow(DataSize congestion_window_size) { - const bool was_congested = Congested(); - congestion_window_size_ = congestion_window_size; - if (was_congested && !Congested()) { - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(CurrentTime()); - UpdateBudgetWithElapsedTime(elapsed_time); +void PacingController::SetCongested(bool congested) { + if (congested_ && !congested) { + UpdateBudgetWithElapsedTime(UpdateTimeAndGetElapsed(CurrentTime())); } -} - -void PacingController::UpdateOutstandingData(DataSize outstanding_data) { - const bool was_congested = Congested(); - outstanding_data_ = outstanding_data; - if (was_congested && !Congested()) { - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(CurrentTime()); - UpdateBudgetWithElapsedTime(elapsed_time); - } -} - -bool PacingController::Congested() const { - if (congestion_window_size_.IsFinite()) { - return outstanding_data_ >= congestion_window_size_; - } - return false; + congested_ = congested; } bool PacingController::IsProbing() const { @@ -281,13 +258,8 @@ absl::optional PacingController::FirstSentPacketTime() const { return first_sent_packet_time_; } -TimeDelta PacingController::OldestPacketWaitTime() const { - Timestamp oldest_packet = packet_queue_.OldestEnqueueTime(); - if (oldest_packet.IsInfinite()) { - return TimeDelta::Zero(); - } - - return CurrentTime() - oldest_packet; +Timestamp PacingController::OldestPacketEnqueueTime() const { + return packet_queue_.OldestEnqueueTime(); } void PacingController::EnqueuePacketInternal( @@ -297,10 +269,21 @@ void PacingController::EnqueuePacketInternal( Timestamp now = CurrentTime(); - if (mode_ == ProcessMode::kDynamic && packet_queue_.Empty() && - NextSendTime() <= now) { - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(now); + if (mode_ == ProcessMode::kDynamic && packet_queue_.Empty()) { + // If queue is empty, we need to "fast-forward" the last process time, + // so that we don't use passed time as budget for sending the first new + // packet. + Timestamp target_process_time = now; + Timestamp next_send_time = NextSendTime(); + if (next_send_time.IsFinite()) { + // There was already a valid planned send time, such as a keep-alive. + // Use that as last process time only if it's prior to now. + target_process_time = std::min(now, next_send_time); + } + + TimeDelta elapsed_time = UpdateTimeAndGetElapsed(target_process_time); UpdateBudgetWithElapsedTime(elapsed_time); + last_process_time_ = target_process_time; } packet_queue_.Push(priority, now, packet_counter_++, std::move(packet)); } @@ -324,7 +307,7 @@ TimeDelta PacingController::UpdateTimeAndGetElapsed(Timestamp now) { } bool PacingController::ShouldSendKeepalive(Timestamp now) const { - if (send_padding_if_silent_ || paused_ || Congested() || + if (send_padding_if_silent_ || paused_ || congested_ || packet_counter_ == 0) { // We send a padding packet every 500 ms to ensure we won't get stuck in // congested state due to no feedback being received. @@ -370,7 +353,7 @@ Timestamp PacingController::NextSendTime() const { } } - if (Congested() || packet_counter_ == 0) { + if (congested_ || packet_counter_ == 0) { // We need to at least send keep-alive packets with some interval. return last_send_time_ + kCongestedPacketInterval; } @@ -620,7 +603,7 @@ DataSize PacingController::PaddingToAdd(DataSize recommended_probe_size, return DataSize::Zero(); } - if (Congested()) { + if (congested_) { // Don't add padding if congested, even if requested for probing. return DataSize::Zero(); } @@ -662,7 +645,7 @@ std::unique_ptr PacingController::GetPendingPacket( !pace_audio_ && packet_queue_.LeadingAudioPacketEnqueueTime().has_value(); bool is_probe = pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe; if (!unpaced_audio_packet && !is_probe) { - if (Congested()) { + if (congested_) { // Don't send anything if congested. return nullptr; } @@ -725,7 +708,6 @@ void PacingController::UpdateBudgetWithElapsedTime(TimeDelta delta) { } void PacingController::UpdateBudgetWithSentData(DataSize size) { - outstanding_data_ += size; if (mode_ == ProcessMode::kPeriodic) { media_budget_.UseBudget(size.bytes()); padding_budget_.UseBudget(size.bytes()); diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h index 38bb9e543e..4ec4efaa80 100644 --- a/modules/pacing/pacing_controller.h +++ b/modules/pacing/pacing_controller.h @@ -20,7 +20,6 @@ #include "absl/types/optional.h" #include "api/function_view.h" -#include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/field_trial_based_config.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" @@ -38,10 +37,9 @@ namespace webrtc { // This class implements a leaky-bucket packet pacing algorithm. It handles the // logic of determining which packets to send when, but the actual timing of -// the processing is done externally (e.g. PacedSender). Furthermore, the +// the processing is done externally (e.g. RtpPacketPacer). Furthermore, the // forwarding of packets when they are ready to be sent is also handled -// externally, via the PacedSendingController::PacketSender interface. -// +// externally, via the PacingController::PacketSender interface. class PacingController { public: // Periodic mode uses the IntervalBudget class for tracking bitrate @@ -82,8 +80,7 @@ class PacingController { PacingController(Clock* clock, PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const WebRtcKeyValueConfig& field_trials, ProcessMode mode); ~PacingController(); @@ -98,11 +95,11 @@ class PacingController { void Resume(); // Resume sending packets. bool IsPaused() const; - void SetCongestionWindow(DataSize congestion_window_size); - void UpdateOutstandingData(DataSize outstanding_data); + void SetCongested(bool congested); // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate); + DataRate pacing_rate() const { return pacing_bitrate_; } // Currently audio traffic is not accounted by pacer and passed through. // With the introduction of audio BWE audio traffic will be accounted for @@ -113,8 +110,8 @@ class PacingController { void SetTransportOverhead(DataSize overhead_per_packet); - // Returns the time since the oldest queued packet was enqueued. - TimeDelta OldestPacketWaitTime() const; + // Returns the time when the oldest packet was queued. + Timestamp OldestPacketEnqueueTime() const; // Number of packets in the pacer queue. size_t QueueSizePackets() const; @@ -124,7 +121,7 @@ class PacingController { // Current buffer level, i.e. max of media and padding debt. DataSize CurrentBufferLevel() const; - // Returns the time when the first packet was sent; + // Returns the time when the first packet was sent. absl::optional FirstSentPacketTime() const; // Returns the number of milliseconds it will take to send the current @@ -145,8 +142,6 @@ class PacingController { // is available. void ProcessPackets(); - bool Congested() const; - bool IsProbing() const; private: @@ -176,8 +171,7 @@ class PacingController { const ProcessMode mode_; Clock* const clock_; PacketSender* const packet_sender_; - const std::unique_ptr fallback_field_trials_; - const WebRtcKeyValueConfig* field_trials_; + const WebRtcKeyValueConfig& field_trials_; const bool drain_large_queues_; const bool send_padding_if_silent_; @@ -196,9 +190,10 @@ class PacingController { mutable Timestamp last_timestamp_; bool paused_; - // If `use_interval_budget_` is true, `media_budget_` and `padding_budget_` - // will be used to track when packets can be sent. Otherwise the media and - // padding debt counters will be used together with the target rates. + // In dynamic mode, `media_budget_` and `padding_budget_` will be used to + // track when packets can be sent. + // In periodic mode, `media_debt_` and `padding_debt_` will be used together + // with the target rates. // This is the media budget, keeping track of how many bits of media // we can pace out during the current interval. @@ -225,8 +220,7 @@ class PacingController { RoundRobinPacketQueue packet_queue_; uint64_t packet_counter_; - DataSize congestion_window_size_; - DataSize outstanding_data_; + bool congested_; TimeDelta queue_time_limit; bool account_for_audio_; diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc index a953d5b439..33592e3194 100644 --- a/modules/pacing/pacing_controller_unittest.cc +++ b/modules/pacing/pacing_controller_unittest.cc @@ -21,7 +21,6 @@ #include "modules/pacing/packet_router.h" #include "system_wrappers/include/clock.h" #include "test/explicit_key_value_config.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -30,6 +29,7 @@ using ::testing::Field; using ::testing::Pointee; using ::testing::Property; using ::testing::Return; +using ::testing::WithoutArgs; namespace webrtc { namespace test { @@ -60,7 +60,7 @@ std::unique_ptr BuildPacket(RtpPacketMediaType type, packet->set_packet_type(type); packet->SetSsrc(ssrc); packet->SetSequenceNumber(sequence_number); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); packet->SetPayloadSize(size); return packet; } @@ -73,7 +73,7 @@ class MockPacingControllerCallback : public PacingController::PacketSender { void SendPacket(std::unique_ptr packet, const PacedPacketInfo& cluster_info) override { SendPacket(packet->Ssrc(), packet->SequenceNumber(), - packet->capture_time_ms(), + packet->capture_time().ms(), packet->packet_type() == RtpPacketMediaType::kRetransmission, packet->packet_type() == RtpPacketMediaType::kPadding); } @@ -209,13 +209,13 @@ class PacingControllerProbing : public PacingController::PacketSender { class PacingControllerTest : public ::testing::TestWithParam { protected: - PacingControllerTest() : clock_(123456) {} + PacingControllerTest() : clock_(123456), trials_("") {} void SetUp() override { srand(0); // Need to initialize PacingController after we initialize clock. - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + GetParam()); Init(); } @@ -252,8 +252,7 @@ class PacingControllerTest Send(type, ssrc, sequence_number, capture_time_ms, size); EXPECT_CALL(callback_, SendPacket(ssrc, sequence_number, capture_time_ms, - type == RtpPacketMediaType::kRetransmission, false)) - .Times(1); + type == RtpPacketMediaType::kRetransmission, false)); } std::unique_ptr BuildRtpPacket(RtpPacketMediaType type) { @@ -297,7 +296,7 @@ class PacingControllerTest int64_t capture_time_ms = clock_.TimeInMilliseconds(); const size_t kPacketSize = 250; - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); // Due to the multiplicative factor we can send 5 packets during a send // interval. (network capacity * multiplier / (8 bits per byte * @@ -321,6 +320,7 @@ class PacingControllerTest SimulatedClock clock_; ::testing::NiceMock callback_; + ExplicitKeyValueConfig trials_; std::unique_ptr pacer_; }; @@ -365,7 +365,8 @@ class PacingControllerFieldTrialTest }; TEST_P(PacingControllerFieldTrialTest, DefaultNoPaddingInSilence) { - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); @@ -379,8 +380,9 @@ TEST_P(PacingControllerFieldTrialTest, DefaultNoPaddingInSilence) { } TEST_P(PacingControllerFieldTrialTest, PaddingInSilenceWithTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-PadInSilence/Enabled/"); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials( + "WebRTC-Pacer-PadInSilence/Enabled/"); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); @@ -394,16 +396,15 @@ TEST_P(PacingControllerFieldTrialTest, PaddingInSilenceWithTrial) { } TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); + const test::ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/"); EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::KilobitsPerSec(10000), DataRate::Zero()); - pacer.SetCongestionWindow(DataSize::Bytes(video.packet_size - 100)); - pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); + pacer.SetCongested(true); // Audio packet blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(0); @@ -415,7 +416,7 @@ TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { ProcessNext(&pacer); // Audio packet unblocked when congestion window clear. ::testing::Mock::VerifyAndClearExpectations(&callback_); - pacer.UpdateOutstandingData(DataSize::Zero()); + pacer.SetCongested(false); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); } @@ -423,14 +424,14 @@ TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { TEST_P(PacingControllerFieldTrialTest, DefaultCongestionWindowDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(10000000), DataRate::Zero()); - pacer.SetCongestionWindow(DataSize::Bytes(800)); - pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); + pacer.SetCongested(true); // Audio not blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(1); @@ -438,8 +439,8 @@ TEST_P(PacingControllerFieldTrialTest, } TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/"); + PacingController pacer(&clock_, &callback_, trials, GetParam()); DataRate pacing_rate = DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond); pacer.SetPacingRates(pacing_rate, DataRate::Zero()); @@ -451,10 +452,9 @@ TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { InsertPacket(&pacer, &audio); Timestamp wait_start_time = clock_.CurrentTime(); Timestamp wait_end_time = Timestamp::MinusInfinity(); - EXPECT_CALL(callback_, SendPacket) - .WillOnce([&](uint32_t ssrc, uint16_t sequence_number, - int64_t capture_timestamp, bool retransmission, - bool padding) { wait_end_time = clock_.CurrentTime(); }); + EXPECT_CALL(callback_, SendPacket).WillOnce(WithoutArgs([&]() { + wait_end_time = clock_.CurrentTime(); + })); while (!wait_end_time.IsFinite()) { ProcessNext(&pacer); } @@ -469,7 +469,8 @@ TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { TEST_P(PacingControllerFieldTrialTest, DefaultBudgetDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond), DataRate::Zero()); @@ -867,8 +868,8 @@ TEST_P(PacingControllerTest, VerifyAverageBitrateVaryingMediaPayload) { const int kTimeStep = 5; const TimeDelta kAveragingWindowLength = TimeDelta::Seconds(10); PacingControllerPadding callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); @@ -1059,21 +1060,18 @@ TEST_P(PacingControllerTest, SendsOnlyPaddingWhenCongested) { uint32_t ssrc = 202020; uint16_t sequence_number = 1000; int kPacketSize = 250; - int kCongestionWindow = kPacketSize * 10; - pacer_->UpdateOutstandingData(DataSize::Zero()); - pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); - int sent_data = 0; - while (sent_data < kCongestionWindow) { - sent_data += kPacketSize; - SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - AdvanceTimeAndProcess(); - } + // Send an initial packet so we have a last send time. + SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, + clock_.TimeInMilliseconds(), kPacketSize); + AdvanceTimeAndProcess(); ::testing::Mock::VerifyAndClearExpectations(&callback_); + + // Set congested state, we should not send anything until the 500ms since + // last send time limit for keep-alives is triggered. EXPECT_CALL(callback_, SendPacket).Times(0); EXPECT_CALL(callback_, SendPadding).Times(0); - + pacer_->SetCongested(true); size_t blocked_packets = 0; int64_t expected_time_until_padding = 500; while (expected_time_until_padding > 5) { @@ -1084,6 +1082,7 @@ TEST_P(PacingControllerTest, SendsOnlyPaddingWhenCongested) { pacer_->ProcessPackets(); expected_time_until_padding -= 5; } + ::testing::Mock::VerifyAndClearExpectations(&callback_); EXPECT_CALL(callback_, SendPadding(1)).WillOnce(Return(1)); EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1); @@ -1102,15 +1101,13 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { // to be sent in a row. pacer_->SetPacingRates(DataRate::BitsPerSec(400 * 8 * 1000 / 5), DataRate::Zero()); - // The congestion window is small enough to only let one packet through. - pacer_->SetCongestionWindow(DataSize::Bytes(800)); - pacer_->UpdateOutstandingData(DataSize::Zero()); // Not yet budget limited or congested, packet is sent. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); // Packet blocked due to congestion. + pacer_->SetCongested(true); Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(0); clock_.AdvanceTimeMilliseconds(5); @@ -1124,7 +1121,7 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); - pacer_->UpdateOutstandingData(DataSize::Zero()); + pacer_->SetCongested(false); pacer_->ProcessPackets(); // Should be blocked due to budget limitation as congestion has be removed. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); @@ -1133,68 +1130,13 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { pacer_->ProcessPackets(); } -TEST_P(PacingControllerTest, ResumesSendingWhenCongestionEnds) { - uint32_t ssrc = 202020; - uint16_t sequence_number = 1000; - int64_t kPacketSize = 250; - int64_t kCongestionCount = 10; - int64_t kCongestionWindow = kPacketSize * kCongestionCount; - int64_t kCongestionTimeMs = 1000; - - pacer_->UpdateOutstandingData(DataSize::Zero()); - pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); - int sent_data = 0; - while (sent_data < kCongestionWindow) { - sent_data += kPacketSize; - SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - ::testing::Mock::VerifyAndClearExpectations(&callback_); - EXPECT_CALL(callback_, SendPacket).Times(0); - int unacked_packets = 0; - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - unacked_packets++; - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - ::testing::Mock::VerifyAndClearExpectations(&callback_); - - // First mark half of the congested packets as cleared and make sure that just - // as many are sent - int ack_count = kCongestionCount / 2; - EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)).Times(ack_count); - pacer_->UpdateOutstandingData( - DataSize::Bytes(kCongestionWindow - kPacketSize * ack_count)); - - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - unacked_packets -= ack_count; - ::testing::Mock::VerifyAndClearExpectations(&callback_); - - // Second make sure all packets are sent if sent packets are continuously - // marked as acked. - EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)) - .Times(unacked_packets); - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - pacer_->UpdateOutstandingData(DataSize::Zero()); - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } -} - TEST_P(PacingControllerTest, Pause) { uint32_t ssrc_low_priority = 12345; uint32_t ssrc = 12346; uint32_t ssrc_high_priority = 12347; uint16_t sequence_number = 1234; - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); ConsumeInitialBudget(); @@ -1223,8 +1165,7 @@ TEST_P(PacingControllerTest, Pause) { } // Expect everything to be queued. - EXPECT_EQ(TimeDelta::Millis(second_capture_time_ms - capture_time_ms), - pacer_->OldestPacketWaitTime()); + EXPECT_EQ(capture_time_ms, pacer_->OldestPacketEnqueueTime().ms()); // Process triggers keep-alive packet. EXPECT_CALL(callback_, SendPadding).WillOnce([](size_t padding) { @@ -1300,13 +1241,13 @@ TEST_P(PacingControllerTest, Pause) { } } - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); } TEST_P(PacingControllerTest, InactiveFromStart) { // Recreate the pacer without the inital time forwarding. - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + GetParam()); pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); @@ -1350,7 +1291,7 @@ TEST_P(PacingControllerTest, ExpectedQueueTimeMs) { const size_t kNumPackets = 60; const size_t kPacketSize = 1200; const int32_t kMaxBitrate = kPaceMultiplier * 30000; - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); pacer_->SetPacingRates(DataRate::BitsPerSec(30000 * kPaceMultiplier), DataRate::Zero()); @@ -1383,7 +1324,7 @@ TEST_P(PacingControllerTest, ExpectedQueueTimeMs) { TEST_P(PacingControllerTest, QueueTimeGrowsOverTime) { uint32_t ssrc = 12346; uint16_t sequence_number = 1234; - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); pacer_->SetPacingRates(DataRate::BitsPerSec(30000 * kPaceMultiplier), DataRate::Zero()); @@ -1391,9 +1332,10 @@ TEST_P(PacingControllerTest, QueueTimeGrowsOverTime) { clock_.TimeInMilliseconds(), 1200); clock_.AdvanceTimeMilliseconds(500); - EXPECT_EQ(TimeDelta::Millis(500), pacer_->OldestPacketWaitTime()); + EXPECT_EQ(clock_.TimeInMilliseconds() - 500, + pacer_->OldestPacketEnqueueTime().ms()); pacer_->ProcessPackets(); - EXPECT_EQ(TimeDelta::Zero(), pacer_->OldestPacketWaitTime()); + EXPECT_TRUE(pacer_->OldestPacketEnqueueTime().IsInfinite()); } TEST_P(PacingControllerTest, ProbingWithInsertedPackets) { @@ -1403,8 +1345,8 @@ TEST_P(PacingControllerTest, ProbingWithInsertedPackets) { uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; - pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->CreateProbeCluster(kSecondClusterRate, @@ -1463,8 +1405,8 @@ TEST_P(PacingControllerTest, SkipsProbesWhenProcessIntervalTooLarge) { "abort_delayed_probes:1,max_probe_delay:2ms/" : "WebRTC-Bwe-ProbingBehavior/" "abort_delayed_probes:0,max_probe_delay:2ms/"); - pacer_ = std::make_unique(&clock_, &packet_sender, - nullptr, &trials, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials, + GetParam()); pacer_->SetPacingRates( DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier), DataRate::BitsPerSec(kInitialBitrateBps)); @@ -1570,8 +1512,8 @@ TEST_P(PacingControllerTest, ProbingWithPaddingSupport) { uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; - pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates( @@ -1636,8 +1578,8 @@ TEST_P(PacingControllerTest, PaddingOveruse) { TEST_P(PacingControllerTest, ProbeClusterId) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); Init(); uint32_t ssrc = 12346; @@ -1693,8 +1635,8 @@ TEST_P(PacingControllerTest, ProbeClusterId) { TEST_P(PacingControllerTest, OwnedPacketPrioritizedOnType) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); Init(); // Insert a packet of each type, from low to high priority. Since priority @@ -1740,8 +1682,8 @@ TEST_P(PacingControllerTest, OwnedPacketPrioritizedOnType) { TEST_P(PacingControllerTest, SmallFirstProbePacket) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero()); @@ -1899,8 +1841,8 @@ TEST_P(PacingControllerTest, uint16_t sequence_number = 1234; MockPacketSender callback; EXPECT_CALL(callback, SendPacket).Times(::testing::AnyNumber()); - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->SetAccountForAudioPackets(account_for_audio); // First, saturate the padding budget. @@ -2074,9 +2016,13 @@ TEST_P(PacingControllerTest, PaddingTargetAccountsForPaddingRate) { // Re-init pacer with an explicitly set padding target of 10ms; const TimeDelta kPaddingTarget = TimeDelta::Millis(10); - ScopedFieldTrials field_trials( + ExplicitKeyValueConfig field_trials( "WebRTC-Pacer-DynamicPaddingTarget/timedelta:10ms/"); - SetUp(); + srand(0); + // Need to initialize PacingController after we initialize clock. + pacer_ = std::make_unique(&clock_, &callback_, field_trials, + GetParam()); + Init(); const uint32_t kSsrc = 12345; const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125); @@ -2135,6 +2081,39 @@ TEST_P(PacingControllerTest, SendsFecPackets) { AdvanceTimeAndProcess(); } +TEST_P(PacingControllerTest, GapInPacingDoesntAccumulateBudget) { + if (PeriodicProcess()) { + // This test checks behavior when not using interval budget. + return; + } + + const uint32_t kSsrc = 12345; + uint16_t sequence_number = 1234; + const DataSize kPackeSize = DataSize::Bytes(250); + const TimeDelta kPacketSendTime = TimeDelta::Millis(15); + + pacer_->SetPacingRates(kPackeSize / kPacketSendTime, + /*padding_rate=*/DataRate::Zero()); + + // Send an initial packet. + SendAndExpectPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number++, + clock_.TimeInMilliseconds(), kPackeSize.bytes()); + pacer_->ProcessPackets(); + ::testing::Mock::VerifyAndClearExpectations(&callback_); + + // Advance time kPacketSendTime past where the media debt should be 0. + clock_.AdvanceTime(2 * kPacketSendTime); + + // Enqueue two new packets. Expect only one to be sent one ProcessPackets(). + Send(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 1, + clock_.TimeInMilliseconds(), kPackeSize.bytes()); + Send(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 2, + clock_.TimeInMilliseconds(), kPackeSize.bytes()); + EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number + 1, + clock_.TimeInMilliseconds(), false, false)); + pacer_->ProcessPackets(); +} + INSTANTIATE_TEST_SUITE_P( WithAndWithoutIntervalBudget, PacingControllerTest, diff --git a/modules/pacing/packet_router.h b/modules/pacing/packet_router.h index 3e8a0f3312..11d8979052 100644 --- a/modules/pacing/packet_router.h +++ b/modules/pacing/packet_router.h @@ -22,11 +22,9 @@ #include "api/transport/network_types.h" #include "modules/pacing/pacing_controller.h" -#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -45,6 +43,9 @@ class PacketRouter : public PacingController::PacketSender { explicit PacketRouter(uint16_t start_transport_seq); ~PacketRouter() override; + PacketRouter(const PacketRouter&) = delete; + PacketRouter& operator=(const PacketRouter&) = delete; + void AddSendRtpModule(RtpRtcpInterface* rtp_module, bool remb_candidate); void RemoveSendRtpModule(RtpRtcpInterface* rtp_module); @@ -108,8 +109,6 @@ class PacketRouter : public PacingController::PacketSender { // process thread is gone. std::vector> pending_fec_packets_ RTC_GUARDED_BY(modules_mutex_); - - RTC_DISALLOW_COPY_AND_ASSIGN(PacketRouter); }; } // namespace webrtc #endif // MODULES_PACING_PACKET_ROUTER_H_ diff --git a/modules/pacing/round_robin_packet_queue.cc b/modules/pacing/round_robin_packet_queue.cc index ef37e5256b..9014c30cb6 100644 --- a/modules/pacing/round_robin_packet_queue.cc +++ b/modules/pacing/round_robin_packet_queue.cc @@ -107,16 +107,7 @@ RoundRobinPacketQueue::Stream::Stream() : size(DataSize::Zero()), ssrc(0) {} RoundRobinPacketQueue::Stream::Stream(const Stream& stream) = default; RoundRobinPacketQueue::Stream::~Stream() = default; -bool IsEnabled(const WebRtcKeyValueConfig* field_trials, const char* name) { - if (!field_trials) { - return false; - } - return absl::StartsWith(field_trials->Lookup(name), "Enabled"); -} - -RoundRobinPacketQueue::RoundRobinPacketQueue( - Timestamp start_time, - const WebRtcKeyValueConfig* field_trials) +RoundRobinPacketQueue::RoundRobinPacketQueue(Timestamp start_time) : transport_overhead_per_packet_(DataSize::Zero()), time_last_updated_(start_time), paused_(false), diff --git a/modules/pacing/round_robin_packet_queue.h b/modules/pacing/round_robin_packet_queue.h index dd35b90d93..f8255834b3 100644 --- a/modules/pacing/round_robin_packet_queue.h +++ b/modules/pacing/round_robin_packet_queue.h @@ -22,7 +22,6 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -34,8 +33,7 @@ namespace webrtc { class RoundRobinPacketQueue { public: - RoundRobinPacketQueue(Timestamp start_time, - const WebRtcKeyValueConfig* field_trials); + explicit RoundRobinPacketQueue(Timestamp start_time); ~RoundRobinPacketQueue(); void Push(int priority, diff --git a/modules/pacing/rtp_packet_pacer.h b/modules/pacing/rtp_packet_pacer.h index 3dc2b27612..a201838858 100644 --- a/modules/pacing/rtp_packet_pacer.h +++ b/modules/pacing/rtp_packet_pacer.h @@ -34,8 +34,7 @@ class RtpPacketPacer { // Resume sending packets. virtual void Resume() = 0; - virtual void SetCongestionWindow(DataSize congestion_window_size) = 0; - virtual void UpdateOutstandingData(DataSize outstanding_data) = 0; + virtual void SetCongested(bool congested) = 0; // Sets the pacing rates. Must be called once before packets can be sent. virtual void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) = 0; diff --git a/modules/pacing/task_queue_paced_sender.cc b/modules/pacing/task_queue_paced_sender.cc index 515cba31dd..f6591235b3 100644 --- a/modules/pacing/task_queue_paced_sender.cc +++ b/modules/pacing/task_queue_paced_sender.cc @@ -12,6 +12,7 @@ #include #include + #include "absl/memory/memory.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" @@ -20,37 +21,42 @@ #include "rtc_base/trace_event.h" namespace webrtc { + namespace { -// If no calls to MaybeProcessPackets() happen, make sure we update stats -// at least every `kMaxTimeBetweenStatsUpdates` as long as the pacer isn't -// completely drained. -constexpr TimeDelta kMaxTimeBetweenStatsUpdates = TimeDelta::Millis(33); -// Don't call UpdateStats() more than `kMinTimeBetweenStatsUpdates` apart, -// for performance reasons. -constexpr TimeDelta kMinTimeBetweenStatsUpdates = TimeDelta::Millis(1); + +constexpr const char* kSlackedTaskQueuePacedSenderFieldTrial = + "WebRTC-SlackedTaskQueuePacedSender"; + } // namespace TaskQueuePacedSender::TaskQueuePacedSender( Clock* clock, PacingController::PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const WebRtcKeyValueConfig& field_trials, TaskQueueFactory* task_queue_factory, - TimeDelta hold_back_window) + TimeDelta max_hold_back_window, + int max_hold_back_window_in_packets) : clock_(clock), - hold_back_window_(hold_back_window), + allow_low_precision_( + field_trials.IsEnabled(kSlackedTaskQueuePacedSenderFieldTrial)), + max_hold_back_window_(allow_low_precision_ + ? PacingController::kMinSleepTime + : max_hold_back_window), + max_hold_back_window_in_packets_( + allow_low_precision_ ? 0 : max_hold_back_window_in_packets), pacing_controller_(clock, packet_sender, - event_log, field_trials, PacingController::ProcessMode::kDynamic), next_process_time_(Timestamp::MinusInfinity()), - stats_update_scheduled_(false), - last_stats_time_(Timestamp::MinusInfinity()), + is_started_(false), is_shutdown_(false), + packet_size_(/*alpha=*/0.95), task_queue_(task_queue_factory->CreateTaskQueue( "TaskQueuePacedSender", - TaskQueueFactory::Priority::NORMAL)) {} + TaskQueueFactory::Priority::NORMAL)) { + packet_size_.Apply(1, 0); +} TaskQueuePacedSender::~TaskQueuePacedSender() { // Post an immediate task to mark the queue as shutting down. @@ -94,27 +100,10 @@ void TaskQueuePacedSender::Resume() { }); } -void TaskQueuePacedSender::SetCongestionWindow( - DataSize congestion_window_size) { - task_queue_.PostTask([this, congestion_window_size]() { +void TaskQueuePacedSender::SetCongested(bool congested) { + task_queue_.PostTask([this, congested]() { RTC_DCHECK_RUN_ON(&task_queue_); - pacing_controller_.SetCongestionWindow(congestion_window_size); - MaybeProcessPackets(Timestamp::MinusInfinity()); - }); -} - -void TaskQueuePacedSender::UpdateOutstandingData(DataSize outstanding_data) { - if (task_queue_.IsCurrent()) { - RTC_DCHECK_RUN_ON(&task_queue_); - // Fast path since this can be called once per sent packet while on the - // task queue. - pacing_controller_.UpdateOutstandingData(outstanding_data); - return; - } - - task_queue_.PostTask([this, outstanding_data]() { - RTC_DCHECK_RUN_ON(&task_queue_); - pacing_controller_.UpdateOutstandingData(outstanding_data); + pacing_controller_.SetCongested(congested); MaybeProcessPackets(Timestamp::MinusInfinity()); }); } @@ -144,7 +133,8 @@ void TaskQueuePacedSender::EnqueuePackets( task_queue_.PostTask([this, packets_ = std::move(packets)]() mutable { RTC_DCHECK_RUN_ON(&task_queue_); for (auto& packet : packets_) { - RTC_DCHECK_GE(packet->capture_time_ms(), 0); + packet_size_.Apply(1, packet->size()); + RTC_DCHECK_GE(packet->capture_time(), Timestamp::Zero()); pacing_controller_.EnqueuePacket(std::move(packet)); } MaybeProcessPackets(Timestamp::MinusInfinity()); @@ -155,6 +145,7 @@ void TaskQueuePacedSender::SetAccountForAudioPackets(bool account_for_audio) { task_queue_.PostTask([this, account_for_audio]() { RTC_DCHECK_RUN_ON(&task_queue_); pacing_controller_.SetAccountForAudioPackets(account_for_audio); + MaybeProcessPackets(Timestamp::MinusInfinity()); }); } @@ -162,6 +153,7 @@ void TaskQueuePacedSender::SetIncludeOverhead() { task_queue_.PostTask([this]() { RTC_DCHECK_RUN_ON(&task_queue_); pacing_controller_.SetIncludeOverhead(); + MaybeProcessPackets(Timestamp::MinusInfinity()); }); } @@ -169,6 +161,7 @@ void TaskQueuePacedSender::SetTransportOverhead(DataSize overhead_per_packet) { task_queue_.PostTask([this, overhead_per_packet]() { RTC_DCHECK_RUN_ON(&task_queue_); pacing_controller_.SetTransportOverhead(overhead_per_packet); + MaybeProcessPackets(Timestamp::MinusInfinity()); }); } @@ -193,7 +186,15 @@ absl::optional TaskQueuePacedSender::FirstSentPacketTime() const { } TimeDelta TaskQueuePacedSender::OldestPacketWaitTime() const { - return GetStats().oldest_packet_wait_time; + Timestamp oldest_packet = GetStats().oldest_packet_enqueue_time; + if (oldest_packet.IsInfinite()) + return TimeDelta::Zero(); + + // (webrtc:9716): The clock is not always monotonic. + Timestamp current = clock_->CurrentTime(); + if (current < oldest_packet) + return TimeDelta::Zero(); + return current - oldest_packet; } void TaskQueuePacedSender::OnStatsUpdated(const Stats& stats) { @@ -227,6 +228,17 @@ void TaskQueuePacedSender::MaybeProcessPackets( next_process_time = pacing_controller_.NextSendTime(); } + TimeDelta hold_back_window = max_hold_back_window_; + DataRate pacing_rate = pacing_controller_.pacing_rate(); + DataSize avg_packet_size = DataSize::Bytes(packet_size_.filtered()); + if (max_hold_back_window_in_packets_ > 0 && !pacing_rate.IsZero() && + !avg_packet_size.IsZero()) { + TimeDelta avg_packet_send_time = avg_packet_size / pacing_rate; + hold_back_window = + std::min(hold_back_window, + avg_packet_send_time * max_hold_back_window_in_packets_); + } + absl::optional time_to_next_process; if (pacing_controller_.IsProbing() && next_process_time != next_process_time_) { @@ -241,84 +253,40 @@ void TaskQueuePacedSender::MaybeProcessPackets( (next_process_time - now).RoundDownTo(TimeDelta::Millis(1))); } } else if (next_process_time_.IsMinusInfinity() || - next_process_time <= next_process_time_ - hold_back_window_) { + next_process_time <= next_process_time_ - hold_back_window) { // Schedule a new task since there is none currently scheduled // (`next_process_time_` is infinite), or the new process time is at least // one holdback window earlier than whatever is currently scheduled. - time_to_next_process = std::max(next_process_time - now, hold_back_window_); + time_to_next_process = std::max(next_process_time - now, hold_back_window); } if (time_to_next_process) { // Set a new scheduled process time and post a delayed task. next_process_time_ = next_process_time; - task_queue_.PostDelayedTask( + // Prefer low precision if allowed and not probing. + TaskQueueBase::DelayPrecision precision = + allow_low_precision_ && !pacing_controller_.IsProbing() + ? TaskQueueBase::DelayPrecision::kLow + : TaskQueueBase::DelayPrecision::kHigh; + + task_queue_.PostDelayedTaskWithPrecision( + precision, [this, next_process_time]() { MaybeProcessPackets(next_process_time); }, time_to_next_process->ms()); } - MaybeUpdateStats(false); + UpdateStats(); } -void TaskQueuePacedSender::MaybeUpdateStats(bool is_scheduled_call) { - if (is_shutdown_) { - if (is_scheduled_call) { - stats_update_scheduled_ = false; - } - return; - } - - Timestamp now = clock_->CurrentTime(); - if (is_scheduled_call) { - // Allow scheduled task to process packets to clear up an remaining debt - // level in an otherwise empty queue. - pacing_controller_.ProcessPackets(); - } else { - if (now - last_stats_time_ < kMinTimeBetweenStatsUpdates) { - // Too frequent unscheduled stats update, return early. - return; - } - } - +void TaskQueuePacedSender::UpdateStats() { Stats new_stats; new_stats.expected_queue_time = pacing_controller_.ExpectedQueueTime(); new_stats.first_sent_packet_time = pacing_controller_.FirstSentPacketTime(); - new_stats.oldest_packet_wait_time = pacing_controller_.OldestPacketWaitTime(); + new_stats.oldest_packet_enqueue_time = + pacing_controller_.OldestPacketEnqueueTime(); new_stats.queue_size = pacing_controller_.QueueSizeData(); OnStatsUpdated(new_stats); - - last_stats_time_ = now; - - bool pacer_drained = pacing_controller_.QueueSizePackets() == 0 && - pacing_controller_.CurrentBufferLevel().IsZero(); - - // If there's anything interesting to get from the pacer and this is a - // scheduled call (or no scheduled call in flight), post a new scheduled stats - // update. - if (!pacer_drained) { - if (!stats_update_scheduled_) { - // There is no pending delayed task to update stats, add one. - // Treat this call as being scheduled in order to bootstrap scheduling - // loop. - stats_update_scheduled_ = true; - is_scheduled_call = true; - } - - // Only if on the scheduled call loop do we want to schedule a new delayed - // task. - if (is_scheduled_call) { - task_queue_.PostDelayedTask( - [this]() { - RTC_DCHECK_RUN_ON(&task_queue_); - MaybeUpdateStats(true); - }, - kMaxTimeBetweenStatsUpdates.ms()); - } - } else if (is_scheduled_call) { - // This is a scheduled call, signing out since there's nothing interesting - // left to check. - stats_update_scheduled_ = false; - } } TaskQueuePacedSender::Stats TaskQueuePacedSender::GetStats() const { diff --git a/modules/pacing/task_queue_paced_sender.h b/modules/pacing/task_queue_paced_sender.h index d39417bf45..4df8aafbdb 100644 --- a/modules/pacing/task_queue_paced_sender.h +++ b/modules/pacing/task_queue_paced_sender.h @@ -19,23 +19,24 @@ #include #include +#include "absl/base/attributes.h" #include "absl/types/optional.h" #include "api/sequence_checker.h" #include "api/task_queue/task_queue_factory.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" -#include "modules/include/module.h" +#include "api/webrtc_key_value_config.h" #include "modules/pacing/pacing_controller.h" #include "modules/pacing/rtp_packet_pacer.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/numerics/exp_filter.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_queue.h" #include "rtc_base/thread_annotations.h" namespace webrtc { class Clock; -class RtcEventLog; class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { public: @@ -43,14 +44,12 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // there is currently a pacer queue and packets can't immediately be // processed. Increasing this reduces thread wakeups at the expense of higher // latency. - // TODO(bugs.webrtc.org/10809): Remove default value for hold_back_window. - TaskQueuePacedSender( - Clock* clock, - PacingController::PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, - TaskQueueFactory* task_queue_factory, - TimeDelta hold_back_window = PacingController::kMinSleepTime); + TaskQueuePacedSender(Clock* clock, + PacingController::PacketSender* packet_sender, + const WebRtcKeyValueConfig& field_trials, + TaskQueueFactory* task_queue_factory, + TimeDelta max_hold_back_window, + int max_hold_back_window_in_packets); ~TaskQueuePacedSender() override; @@ -64,7 +63,7 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { void EnqueuePackets( std::vector> packets) override; - // Methods implementing RtpPacketPacer: + // Methods implementing RtpPacketPacer. void CreateProbeCluster(DataRate bitrate, int cluster_id) override; @@ -74,8 +73,7 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // Resume sending packets. void Resume() override; - void SetCongestionWindow(DataSize congestion_window_size) override; - void UpdateOutstandingData(DataSize outstanding_data) override; + void SetCongested(bool congested) override; // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) override; @@ -110,15 +108,15 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // Exposed as protected for test. struct Stats { Stats() - : oldest_packet_wait_time(TimeDelta::Zero()), + : oldest_packet_enqueue_time(Timestamp::MinusInfinity()), queue_size(DataSize::Zero()), expected_queue_time(TimeDelta::Zero()) {} - TimeDelta oldest_packet_wait_time; + Timestamp oldest_packet_enqueue_time; DataSize queue_size; TimeDelta expected_queue_time; absl::optional first_sent_packet_time; }; - virtual void OnStatsUpdated(const Stats& stats); + void OnStatsUpdated(const Stats& stats); private: // Check if it is time to send packets, or schedule a delayed task if not. @@ -128,11 +126,23 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // method again with desired (finite) scheduled process time. void MaybeProcessPackets(Timestamp scheduled_process_time); - void MaybeUpdateStats(bool is_scheduled_call) RTC_RUN_ON(task_queue_); + void UpdateStats() RTC_RUN_ON(task_queue_); Stats GetStats() const; Clock* const clock_; - const TimeDelta hold_back_window_; + // If `kSlackedTaskQueuePacedSenderFieldTrial` is enabled, delayed tasks + // invoking MaybeProcessPackets() are scheduled using low precision instead of + // high precision, resulting in less idle wake ups and packets being sent in + // bursts if the `task_queue_` implementation supports slack. + // + // When probing, high precision is used regardless of `allow_low_precision_` + // to ensure good bandwidth estimation. + const bool allow_low_precision_; + // The holdback window prevents too frequent delayed MaybeProcessPackets() + // calls. These are only applicable if `allow_low_precision_` is false. + const TimeDelta max_hold_back_window_; + const int max_hold_back_window_in_packets_; + PacingController pacing_controller_ RTC_GUARDED_BY(task_queue_); // We want only one (valid) delayed process task in flight at a time. @@ -142,25 +152,18 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // Timestamp::MinusInfinity() indicates no valid pending task. Timestamp next_process_time_ RTC_GUARDED_BY(task_queue_); - // Since we don't want to support synchronous calls that wait for a - // task execution, we poll the stats at some interval and update - // `current_stats_`, which can in turn be polled at any time. - - // True iff there is delayed task in flight that that will call - // UdpateStats(). - bool stats_update_scheduled_ RTC_GUARDED_BY(task_queue_); - // Last time stats were updated. - Timestamp last_stats_time_ RTC_GUARDED_BY(task_queue_); - // Indicates if this task queue is started. If not, don't allow // posting delayed tasks yet. - bool is_started_ RTC_GUARDED_BY(task_queue_) = false; + bool is_started_ RTC_GUARDED_BY(task_queue_); // Indicates if this task queue is shutting down. If so, don't allow // posting any more delayed tasks as that can cause the task queue to // never drain. bool is_shutdown_ RTC_GUARDED_BY(task_queue_); + // Filtered size of enqueued packets, in bytes. + rtc::ExpFilter packet_size_ RTC_GUARDED_BY(task_queue_); + mutable Mutex stats_mutex_; Stats current_stats_ RTC_GUARDED_BY(stats_mutex_); diff --git a/modules/pacing/task_queue_paced_sender_unittest.cc b/modules/pacing/task_queue_paced_sender_unittest.cc index aca1ba0b4d..420d4f47e5 100644 --- a/modules/pacing/task_queue_paced_sender_unittest.cc +++ b/modules/pacing/task_queue_paced_sender_unittest.cc @@ -11,18 +11,22 @@ #include "modules/pacing/task_queue_paced_sender.h" #include +#include #include #include #include #include #include +#include "api/task_queue/task_queue_base.h" #include "api/transport/network_types.h" +#include "api/units/data_rate.h" #include "modules/pacing/packet_router.h" #include "modules/utility/include/mock/mock_process_thread.h" -#include "test/field_trial.h" +#include "rtc_base/task_utils/to_queued_task.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" using ::testing::_; @@ -37,6 +41,7 @@ constexpr uint32_t kVideoSsrc = 234565; constexpr uint32_t kVideoRtxSsrc = 34567; constexpr uint32_t kFlexFecSsrc = 45678; constexpr size_t kDefaultPacketSize = 1234; +constexpr int kNoPacketHoldback = -1; class MockPacketRouter : public PacketRouter { public: @@ -55,37 +60,6 @@ class MockPacketRouter : public PacketRouter { (override)); }; -class StatsUpdateObserver { - public: - StatsUpdateObserver() = default; - virtual ~StatsUpdateObserver() = default; - - virtual void OnStatsUpdated() = 0; -}; - -class TaskQueuePacedSenderForTest : public TaskQueuePacedSender { - public: - TaskQueuePacedSenderForTest(Clock* clock, - PacketRouter* packet_router, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, - TaskQueueFactory* task_queue_factory, - TimeDelta hold_back_window) - : TaskQueuePacedSender(clock, - packet_router, - event_log, - field_trials, - task_queue_factory, - hold_back_window) {} - - void OnStatsUpdated(const Stats& stats) override { - ++num_stats_updates_; - TaskQueuePacedSender::OnStatsUpdated(stats); - } - - size_t num_stats_updates_ = 0; -}; - std::vector> GeneratePadding( DataSize target_size) { // 224 bytes is the max padding size for plain padding packets generated by @@ -106,488 +80,690 @@ std::vector> GeneratePadding( return padding_packets; } +class TaskQueueWithFakePrecisionFactory : public TaskQueueFactory { + public: + explicit TaskQueueWithFakePrecisionFactory( + TaskQueueFactory* task_queue_factory) + : task_queue_factory_(task_queue_factory) {} + + std::unique_ptr CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + return std::unique_ptr( + new TaskQueueWithFakePrecision( + const_cast(this), + task_queue_factory_)); + } + + int delayed_low_precision_count() const { + return delayed_low_precision_count_; + } + int delayed_high_precision_count() const { + return delayed_high_precision_count_; + } + + private: + friend class TaskQueueWithFakePrecision; + + class TaskQueueWithFakePrecision : public TaskQueueBase { + public: + TaskQueueWithFakePrecision( + TaskQueueWithFakePrecisionFactory* parent_factory, + TaskQueueFactory* task_queue_factory) + : parent_factory_(parent_factory), + task_queue_(task_queue_factory->CreateTaskQueue( + "TaskQueueWithFakePrecision", + TaskQueueFactory::Priority::NORMAL)) {} + ~TaskQueueWithFakePrecision() override {} + + void Delete() override { + // `task_queue_->Delete()` is implicitly called in the destructor due to + // TaskQueueDeleter. + delete this; + } + void PostTask(std::unique_ptr task) override { + task_queue_->PostTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + })); + } + void PostDelayedTask(std::unique_ptr task, + uint32_t milliseconds) override { + ++parent_factory_->delayed_low_precision_count_; + task_queue_->PostDelayedTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + }), + milliseconds); + } + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) override { + ++parent_factory_->delayed_high_precision_count_; + task_queue_->PostDelayedHighPrecisionTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + }), + milliseconds); + } + + private: + void RunTask(std::unique_ptr task) { + CurrentTaskQueueSetter set_current(this); + if (!task->Run()) + task.release(); + } + + TaskQueueWithFakePrecisionFactory* parent_factory_; + std::unique_ptr task_queue_; + }; + + TaskQueueFactory* task_queue_factory_; + std::atomic delayed_low_precision_count_ = 0u; + std::atomic delayed_high_precision_count_ = 0u; +}; + } // namespace namespace test { - std::unique_ptr BuildRtpPacket(RtpPacketMediaType type) { - auto packet = std::make_unique(nullptr); - packet->set_packet_type(type); - switch (type) { - case RtpPacketMediaType::kAudio: - packet->SetSsrc(kAudioSsrc); - break; - case RtpPacketMediaType::kVideo: - packet->SetSsrc(kVideoSsrc); - break; - case RtpPacketMediaType::kRetransmission: - case RtpPacketMediaType::kPadding: - packet->SetSsrc(kVideoRtxSsrc); - break; - case RtpPacketMediaType::kForwardErrorCorrection: - packet->SetSsrc(kFlexFecSsrc); - break; - } - - packet->SetPayloadSize(kDefaultPacketSize); - return packet; +std::unique_ptr BuildRtpPacket(RtpPacketMediaType type) { + auto packet = std::make_unique(nullptr); + packet->set_packet_type(type); + switch (type) { + case RtpPacketMediaType::kAudio: + packet->SetSsrc(kAudioSsrc); + break; + case RtpPacketMediaType::kVideo: + packet->SetSsrc(kVideoSsrc); + break; + case RtpPacketMediaType::kRetransmission: + case RtpPacketMediaType::kPadding: + packet->SetSsrc(kVideoRtxSsrc); + break; + case RtpPacketMediaType::kForwardErrorCorrection: + packet->SetSsrc(kFlexFecSsrc); + break; } - std::vector> GeneratePackets( - RtpPacketMediaType type, - size_t num_packets) { - std::vector> packets; - for (size_t i = 0; i < num_packets; ++i) { - packets.push_back(BuildRtpPacket(type)); - } - return packets; + packet->SetPayloadSize(kDefaultPacketSize); + return packet; +} + +std::vector> GeneratePackets( + RtpPacketMediaType type, + size_t num_packets) { + std::vector> packets; + for (size_t i = 0; i < num_packets; ++i) { + packets.push_back(BuildRtpPacket(type)); } + return packets; +} - TEST(TaskQueuePacedSenderTest, PacesPackets) { - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime); +TEST(TaskQueuePacedSenderTest, PacesPackets) { + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - // Insert a number of packets, covering one second. - static constexpr size_t kPacketsToSend = 42; - pacer.SetPacingRates( - DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend), - DataRate::Zero()); - pacer.EnsureStarted(); - pacer.EnqueuePackets( - GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + // Insert a number of packets, covering one second. + static constexpr size_t kPacketsToSend = 42; + pacer.SetPacingRates( + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend), + DataRate::Zero()); + pacer.EnsureStarted(); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); - // Expect all of them to be sent. - size_t packets_sent = 0; - Timestamp end_time = Timestamp::PlusInfinity(); - EXPECT_CALL(packet_router, SendPacket) - .WillRepeatedly([&](std::unique_ptr packet, - const PacedPacketInfo& cluster_info) { - ++packets_sent; - if (packets_sent == kPacketsToSend) { - end_time = time_controller.GetClock()->CurrentTime(); - } - }); + // Expect all of them to be sent. + size_t packets_sent = 0; + Timestamp end_time = Timestamp::PlusInfinity(); + EXPECT_CALL(packet_router, SendPacket) + .WillRepeatedly([&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { + ++packets_sent; + if (packets_sent == kPacketsToSend) { + end_time = time_controller.GetClock()->CurrentTime(); + } + }); - const Timestamp start_time = time_controller.GetClock()->CurrentTime(); + const Timestamp start_time = time_controller.GetClock()->CurrentTime(); - // Packets should be sent over a period of close to 1s. Expect a little - // lower than this since initial probing is a bit quicker. - time_controller.AdvanceTime(TimeDelta::Seconds(1)); - EXPECT_EQ(packets_sent, kPacketsToSend); - ASSERT_TRUE(end_time.IsFinite()); - EXPECT_NEAR((end_time - start_time).ms(), 1000.0, 50.0); - } + // Packets should be sent over a period of close to 1s. Expect a little + // lower than this since initial probing is a bit quicker. + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(packets_sent, kPacketsToSend); + ASSERT_TRUE(end_time.IsFinite()); + EXPECT_NEAR((end_time - start_time).ms(), 1000.0, 50.0); +} - TEST(TaskQueuePacedSenderTest, ReschedulesProcessOnRateChange) { - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime); +TEST(TaskQueuePacedSenderTest, ReschedulesProcessOnRateChange) { + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - // Insert a number of packets to be sent 200ms apart. - const size_t kPacketsPerSecond = 5; - const DataRate kPacingRate = - DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsPerSecond); - pacer.SetPacingRates(kPacingRate, DataRate::Zero()); - pacer.EnsureStarted(); + // Insert a number of packets to be sent 200ms apart. + const size_t kPacketsPerSecond = 5; + const DataRate kPacingRate = + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsPerSecond); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); - // Send some initial packets to be rid of any probes. - EXPECT_CALL(packet_router, SendPacket).Times(kPacketsPerSecond); - pacer.EnqueuePackets( - GeneratePackets(RtpPacketMediaType::kVideo, kPacketsPerSecond)); - time_controller.AdvanceTime(TimeDelta::Seconds(1)); + // Send some initial packets to be rid of any probes. + EXPECT_CALL(packet_router, SendPacket).Times(kPacketsPerSecond); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsPerSecond)); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); - // Insert three packets, and record send time of each of them. - // After the second packet is sent, double the send rate so we can - // check the third packets is sent after half the wait time. - Timestamp first_packet_time = Timestamp::MinusInfinity(); - Timestamp second_packet_time = Timestamp::MinusInfinity(); - Timestamp third_packet_time = Timestamp::MinusInfinity(); + // Insert three packets, and record send time of each of them. + // After the second packet is sent, double the send rate so we can + // check the third packets is sent after half the wait time. + Timestamp first_packet_time = Timestamp::MinusInfinity(); + Timestamp second_packet_time = Timestamp::MinusInfinity(); + Timestamp third_packet_time = Timestamp::MinusInfinity(); - EXPECT_CALL(packet_router, SendPacket) - .Times(3) - .WillRepeatedly([&](std::unique_ptr packet, - const PacedPacketInfo& cluster_info) { - if (first_packet_time.IsInfinite()) { - first_packet_time = time_controller.GetClock()->CurrentTime(); - } else if (second_packet_time.IsInfinite()) { - second_packet_time = time_controller.GetClock()->CurrentTime(); - pacer.SetPacingRates(2 * kPacingRate, DataRate::Zero()); - } else { - third_packet_time = time_controller.GetClock()->CurrentTime(); - } - }); + EXPECT_CALL(packet_router, SendPacket) + .Times(3) + .WillRepeatedly([&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { + if (first_packet_time.IsInfinite()) { + first_packet_time = time_controller.GetClock()->CurrentTime(); + } else if (second_packet_time.IsInfinite()) { + second_packet_time = time_controller.GetClock()->CurrentTime(); + pacer.SetPacingRates(2 * kPacingRate, DataRate::Zero()); + } else { + third_packet_time = time_controller.GetClock()->CurrentTime(); + } + }); - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 3)); - time_controller.AdvanceTime(TimeDelta::Millis(500)); - ASSERT_TRUE(third_packet_time.IsFinite()); - EXPECT_NEAR((second_packet_time - first_packet_time).ms(), 200.0, - 1.0); - EXPECT_NEAR((third_packet_time - second_packet_time).ms(), 100.0, - 1.0); - } + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 3)); + time_controller.AdvanceTime(TimeDelta::Millis(500)); + ASSERT_TRUE(third_packet_time.IsFinite()); + EXPECT_NEAR((second_packet_time - first_packet_time).ms(), 200.0, + 1.0); + EXPECT_NEAR((third_packet_time - second_packet_time).ms(), 100.0, + 1.0); +} - TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) { - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime); +TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) { + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125); - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = kPacketSize / kPacingDataRate; + const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125); + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = kPacketSize / kPacingDataRate; - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); - pacer.EnsureStarted(); + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + pacer.EnsureStarted(); - // Add some initial video packets, only one should be sent. - EXPECT_CALL(packet_router, SendPacket); - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); - time_controller.AdvanceTime(TimeDelta::Zero()); - ::testing::Mock::VerifyAndClearExpectations(&packet_router); + // Add some initial video packets, only one should be sent. + EXPECT_CALL(packet_router, SendPacket); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); + time_controller.AdvanceTime(TimeDelta::Zero()); + ::testing::Mock::VerifyAndClearExpectations(&packet_router); - // Advance time, but still before next packet should be sent. - time_controller.AdvanceTime(kPacketPacingTime / 2); + // Advance time, but still before next packet should be sent. + time_controller.AdvanceTime(kPacketPacingTime / 2); - // Insert an audio packet, it should be sent immediately. - EXPECT_CALL(packet_router, SendPacket); - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kAudio, 1)); - time_controller.AdvanceTime(TimeDelta::Zero()); - ::testing::Mock::VerifyAndClearExpectations(&packet_router); - } + // Insert an audio packet, it should be sent immediately. + EXPECT_CALL(packet_router, SendPacket); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kAudio, 1)); + time_controller.AdvanceTime(TimeDelta::Zero()); + ::testing::Mock::VerifyAndClearExpectations(&packet_router); +} - TEST(TaskQueuePacedSenderTest, SleepsDuringCoalscingWindow) { - const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - kCoalescingWindow); +TEST(TaskQueuePacedSenderTest, SleepsDuringCoalscingWindow) { + const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + kCoalescingWindow, kNoPacketHoldback); - // Set rates so one packet adds one ms of buffer level. - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); - const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + // Set rates so one packet adds one ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); - pacer.EnsureStarted(); + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + pacer.EnsureStarted(); - // Add 10 packets. The first should be sent immediately since the buffers - // are clear. - EXPECT_CALL(packet_router, SendPacket); - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); - time_controller.AdvanceTime(TimeDelta::Zero()); - ::testing::Mock::VerifyAndClearExpectations(&packet_router); + // Add 10 packets. The first should be sent immediately since the buffers + // are clear. + EXPECT_CALL(packet_router, SendPacket); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); + time_controller.AdvanceTime(TimeDelta::Zero()); + ::testing::Mock::VerifyAndClearExpectations(&packet_router); - // Advance time to 1ms before the coalescing window ends. No packets should - // be sent. - EXPECT_CALL(packet_router, SendPacket).Times(0); - time_controller.AdvanceTime(kCoalescingWindow - TimeDelta::Millis(1)); + // Advance time to 1ms before the coalescing window ends. No packets should + // be sent. + EXPECT_CALL(packet_router, SendPacket).Times(0); + time_controller.AdvanceTime(kCoalescingWindow - TimeDelta::Millis(1)); - // Advance time to where coalescing window ends. All packets that should - // have been sent up til now will be sent. - EXPECT_CALL(packet_router, SendPacket).Times(5); - time_controller.AdvanceTime(TimeDelta::Millis(1)); - ::testing::Mock::VerifyAndClearExpectations(&packet_router); - } + // Advance time to where coalescing window ends. All packets that should + // have been sent up til now will be sent. + EXPECT_CALL(packet_router, SendPacket).Times(5); + time_controller.AdvanceTime(TimeDelta::Millis(1)); + ::testing::Mock::VerifyAndClearExpectations(&packet_router); +} - TEST(TaskQueuePacedSenderTest, ProbingOverridesCoalescingWindow) { - const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - kCoalescingWindow); +TEST(TaskQueuePacedSenderTest, ProbingOverridesCoalescingWindow) { + const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + kCoalescingWindow, kNoPacketHoldback); - // Set rates so one packet adds one ms of buffer level. - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); - const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + // Set rates so one packet adds one ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); - pacer.EnsureStarted(); + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + pacer.EnsureStarted(); - // Add 10 packets. The first should be sent immediately since the buffers - // are clear. This will also trigger the probe to start. - EXPECT_CALL(packet_router, SendPacket).Times(AtLeast(1)); - pacer.CreateProbeCluster(kPacingDataRate * 2, 17); - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); - time_controller.AdvanceTime(TimeDelta::Zero()); - ::testing::Mock::VerifyAndClearExpectations(&packet_router); + // Add 10 packets. The first should be sent immediately since the buffers + // are clear. This will also trigger the probe to start. + EXPECT_CALL(packet_router, SendPacket).Times(AtLeast(1)); + pacer.CreateProbeCluster(kPacingDataRate * 2, 17); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10)); + time_controller.AdvanceTime(TimeDelta::Zero()); + ::testing::Mock::VerifyAndClearExpectations(&packet_router); - // Advance time to 1ms before the coalescing window ends. Packets should be - // flying. - EXPECT_CALL(packet_router, SendPacket).Times(AtLeast(1)); - time_controller.AdvanceTime(kCoalescingWindow - TimeDelta::Millis(1)); - } + // Advance time to 1ms before the coalescing window ends. Packets should be + // flying. + EXPECT_CALL(packet_router, SendPacket).Times(AtLeast(1)); + time_controller.AdvanceTime(kCoalescingWindow - TimeDelta::Millis(1)); +} - TEST(TaskQueuePacedSenderTest, RespectedMinTimeBetweenStatsUpdates) { - const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - kCoalescingWindow); - const DataRate kPacingDataRate = DataRate::KilobitsPerSec(300); - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); - pacer.EnsureStarted(); +TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSetTime) { + ScopedKeyValueConfig trials( + "WebRTC-Bwe-ProbingBehavior/min_probe_delta:1ms/"); + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - const TimeDelta kMinTimeBetweenStatsUpdates = TimeDelta::Millis(1); + // Set rates so one packet adds 4ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(4); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + pacer.SetPacingRates(kPacingDataRate, /*padding_rate=*/DataRate::Zero()); + pacer.EnsureStarted(); + EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { + return std::vector>(); + }); + EXPECT_CALL(packet_router, GeneratePadding(_)) + .WillRepeatedly( + [](DataSize target_size) { return GeneratePadding(target_size); }); - // Nothing inserted, no stats updates yet. - EXPECT_EQ(pacer.num_stats_updates_, 0u); + // Enqueue two packets, only the first is sent immediately and the next + // will be scheduled for sending in 4ms. + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 2)); + const int kNotAProbe = PacedPacketInfo::kNotAProbe; + EXPECT_CALL(packet_router, + SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, + kNotAProbe))); + // Advance to less than 3ms before next packet send time. + time_controller.AdvanceTime(TimeDelta::Micros(1001)); - // Insert one packet, stats should be updated. - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, 1u); + // Trigger a probe at 4x the current pacing rate and insert the number of + // packets the probe needs. + const DataRate kProbeRate = 2 * kPacingDataRate; + const int kProbeClusterId = 1; + pacer.CreateProbeCluster(kProbeRate, kProbeClusterId); - // Advance time half of the min stats update interval, and trigger a - // refresh - stats should not be updated yet. - time_controller.AdvanceTime(kMinTimeBetweenStatsUpdates / 2); - pacer.EnqueuePackets({}); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, 1u); + // Expected size for each probe in a cluster is twice the expected bits + // sent during min_probe_delta. + // Expect one additional call since probe always starts with a small + const TimeDelta kProbeTimeDelta = TimeDelta::Millis(2); + const DataSize kProbeSize = kProbeRate * kProbeTimeDelta; + const size_t kNumPacketsInProbe = + (kProbeSize + kPacketSize - DataSize::Bytes(1)) / kPacketSize; + EXPECT_CALL(packet_router, + SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, + kProbeClusterId))) + .Times(kNumPacketsInProbe + 1); - // Advance time the next half, now stats update is triggered. - time_controller.AdvanceTime(kMinTimeBetweenStatsUpdates / 2); - pacer.EnqueuePackets({}); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, 2u); - } + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kNumPacketsInProbe)); + time_controller.AdvanceTime(TimeDelta::Zero()); - TEST(TaskQueuePacedSenderTest, ThrottlesStatsUpdates) { - const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - kCoalescingWindow); + // The pacer should have scheduled the next probe to be sent in + // kProbeTimeDelta. That there was existing scheduled call less than + // PacingController::kMinSleepTime before this should not matter. + EXPECT_CALL(packet_router, + SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, + kProbeClusterId))) + .Times(AtLeast(1)); + time_controller.AdvanceTime(TimeDelta::Millis(2)); +} - // Set rates so one packet adds 10ms of buffer level. - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = TimeDelta::Millis(10); - const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; - const TimeDelta kMinTimeBetweenStatsUpdates = TimeDelta::Millis(1); - const TimeDelta kMaxTimeBetweenStatsUpdates = TimeDelta::Millis(33); +TEST(TaskQueuePacedSenderTest, NoMinSleepTimeWhenProbing) { + // Set min_probe_delta to be less than kMinSleepTime (1ms). + const TimeDelta kMinProbeDelta = TimeDelta::Micros(100); + ScopedKeyValueConfig trials( + "WebRTC-Bwe-ProbingBehavior/min_probe_delta:100us/"); + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - // Nothing inserted, no stats updates yet. - size_t num_expected_stats_updates = 0; - EXPECT_EQ(pacer.num_stats_updates_, num_expected_stats_updates); - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); - pacer.EnsureStarted(); - time_controller.AdvanceTime(kMinTimeBetweenStatsUpdates); - // Updating pacing rates refreshes stats. - EXPECT_EQ(pacer.num_stats_updates_, ++num_expected_stats_updates); + // Set rates so one packet adds 4ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(4); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + pacer.SetPacingRates(kPacingDataRate, /*padding_rate=*/DataRate::Zero()); + pacer.EnsureStarted(); + EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { + return std::vector>(); + }); + EXPECT_CALL(packet_router, GeneratePadding) + .WillRepeatedly( + [](DataSize target_size) { return GeneratePadding(target_size); }); - // Record time when we insert first packet, this triggers the scheduled - // stats updating. - Clock* const clock = time_controller.GetClock(); - const Timestamp start_time = clock->CurrentTime(); + // Set a high probe rate. + const int kProbeClusterId = 1; + DataRate kProbingRate = kPacingDataRate * 10; + pacer.CreateProbeCluster(kProbingRate, kProbeClusterId); - while (clock->CurrentTime() - start_time <= - kMaxTimeBetweenStatsUpdates - kPacketPacingTime) { - // Enqueue packet, expect stats update. - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, ++num_expected_stats_updates); + // Advance time less than PacingController::kMinSleepTime, probing packets + // for the first millisecond should be sent immediately. Min delta between + // probes is 2x 100us, meaning 4 times per ms we will get least one call to + // SendPacket(). + DataSize data_sent = DataSize::Zero(); + EXPECT_CALL(packet_router, + SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, + kProbeClusterId))) + .Times(AtLeast(4)) + .WillRepeatedly([&](std::unique_ptr packet, + const PacedPacketInfo&) { + data_sent += + DataSize::Bytes(packet->payload_size() + packet->padding_size()); + }); - // Advance time to halfway through pacing time, expect another stats - // update. - time_controller.AdvanceTime(kPacketPacingTime / 2); - pacer.EnqueuePackets({}); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, ++num_expected_stats_updates); + // Add one packet to kickstart probing, the rest will be padding packets. + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); + time_controller.AdvanceTime(kMinProbeDelta); - // Advance time the rest of the way. - time_controller.AdvanceTime(kPacketPacingTime / 2); - } + // Verify the amount of probing data sent. + // Probe always starts with a small (1 byte) padding packet that's not + // counted into the probe rate here. + EXPECT_EQ(data_sent, + kProbingRate * TimeDelta::Millis(1) + DataSize::Bytes(1)); +} - // At this point, the pace queue is drained so there is no more intersting - // update to be made - but there is still as schduled task that should run - // `kMaxTimeBetweenStatsUpdates` after the first update. - time_controller.AdvanceTime(start_time + kMaxTimeBetweenStatsUpdates - - clock->CurrentTime()); - EXPECT_EQ(pacer.num_stats_updates_, ++num_expected_stats_updates); +TEST(TaskQueuePacedSenderTest, PacketBasedCoalescing) { + const TimeDelta kFixedCoalescingWindow = TimeDelta::Millis(10); + const int kPacketBasedHoldback = 5; - // Advance time a significant time - don't expect any more calls as stats - // updating does not happen when queue is drained. - time_controller.AdvanceTime(TimeDelta::Millis(400)); - EXPECT_EQ(pacer.num_stats_updates_, num_expected_stats_updates); - } + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + kFixedCoalescingWindow, kPacketBasedHoldback); - TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSetTime) { - ScopedFieldTrials trials("WebRTC-Bwe-ProbingBehavior/min_probe_delta:1ms/"); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime); + // Set rates so one packet adds one ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + const TimeDelta kExpectedHoldbackWindow = + kPacketPacingTime * kPacketBasedHoldback; + // `kFixedCoalescingWindow` sets the upper bound for the window. + ASSERT_GE(kFixedCoalescingWindow, kExpectedHoldbackWindow); - // Set rates so one packet adds 4ms of buffer level. - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = TimeDelta::Millis(4); - const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; - pacer.SetPacingRates(kPacingDataRate, /*padding_rate=*/DataRate::Zero()); - pacer.EnsureStarted(); - EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { - return std::vector>(); - }); - EXPECT_CALL(packet_router, GeneratePadding(_)) - .WillRepeatedly( - [](DataSize target_size) { return GeneratePadding(target_size); }); + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { + return std::vector>(); + }); + pacer.EnsureStarted(); - // Enqueue two packets, only the first is sent immediately and the next - // will be scheduled for sending in 4ms. - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 2)); - const int kNotAProbe = PacedPacketInfo::kNotAProbe; - EXPECT_CALL( - packet_router, - SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, - kNotAProbe))); - // Advance to less than 3ms before next packet send time. - time_controller.AdvanceTime(TimeDelta::Micros(1001)); + // Add some packets and wait till all have been sent, so that the pacer + // has a valid estimate of packet size. + const int kNumWarmupPackets = 40; + EXPECT_CALL(packet_router, SendPacket).Times(kNumWarmupPackets); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kNumWarmupPackets)); + // Wait until all packes have been sent, with a 2x margin. + time_controller.AdvanceTime(kPacketPacingTime * (kNumWarmupPackets * 2)); - // Trigger a probe at 4x the current pacing rate and insert the number of - // packets the probe needs. - const DataRate kProbeRate = 2 * kPacingDataRate; - const int kProbeClusterId = 1; - pacer.CreateProbeCluster(kProbeRate, kProbeClusterId); + // Enqueue packets. Expect only the first one to be sent immediately. + EXPECT_CALL(packet_router, SendPacket).Times(1); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketBasedHoldback)); + time_controller.AdvanceTime(TimeDelta::Zero()); - // Expected size for each probe in a cluster is twice the expected bits - // sent during min_probe_delta. - // Expect one additional call since probe always starts with a small - const TimeDelta kProbeTimeDelta = TimeDelta::Millis(2); - const DataSize kProbeSize = kProbeRate * kProbeTimeDelta; - const size_t kNumPacketsInProbe = - (kProbeSize + kPacketSize - DataSize::Bytes(1)) / kPacketSize; - EXPECT_CALL( - packet_router, - SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, - kProbeClusterId))) - .Times(kNumPacketsInProbe + 1); + // Advance time to 1ms before the coalescing window ends. + EXPECT_CALL(packet_router, SendPacket).Times(0); + time_controller.AdvanceTime(kExpectedHoldbackWindow - TimeDelta::Millis(1)); - pacer.EnqueuePackets( - GeneratePackets(RtpPacketMediaType::kVideo, kNumPacketsInProbe)); - time_controller.AdvanceTime(TimeDelta::Zero()); + // Advance past where the coalescing window should end. + EXPECT_CALL(packet_router, SendPacket).Times(kPacketBasedHoldback - 1); + time_controller.AdvanceTime(TimeDelta::Millis(1)); +} - // The pacer should have scheduled the next probe to be sent in - // kProbeTimeDelta. That there was existing scheduled call less than - // PacingController::kMinSleepTime before this should not matter. +TEST(TaskQueuePacedSenderTest, FixedHoldBackHasPriorityOverPackets) { + const TimeDelta kFixedCoalescingWindow = TimeDelta::Millis(2); + const int kPacketBasedHoldback = 5; - EXPECT_CALL( - packet_router, - SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, - kProbeClusterId))) - .Times(AtLeast(1)); - time_controller.AdvanceTime(TimeDelta::Millis(2)); - } + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + kFixedCoalescingWindow, kPacketBasedHoldback); - TEST(TaskQueuePacedSenderTest, NoMinSleepTimeWhenProbing) { - // Set min_probe_delta to be less than kMinSleepTime (1ms). - const TimeDelta kMinProbeDelta = TimeDelta::Micros(100); - ScopedFieldTrials trials( - "WebRTC-Bwe-ProbingBehavior/min_probe_delta:100us/"); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime); + // Set rates so one packet adds one ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); + const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; + const TimeDelta kExpectedPacketHoldbackWindow = + kPacketPacingTime * kPacketBasedHoldback; + // |kFixedCoalescingWindow| sets the upper bound for the window. + ASSERT_LT(kFixedCoalescingWindow, kExpectedPacketHoldbackWindow); - // Set rates so one packet adds 4ms of buffer level. - const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); - const TimeDelta kPacketPacingTime = TimeDelta::Millis(4); - const DataRate kPacingDataRate = kPacketSize / kPacketPacingTime; - pacer.SetPacingRates(kPacingDataRate, /*padding_rate=*/DataRate::Zero()); - pacer.EnsureStarted(); - EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { - return std::vector>(); - }); - EXPECT_CALL(packet_router, GeneratePadding) - .WillRepeatedly( - [](DataSize target_size) { return GeneratePadding(target_size); }); + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { + return std::vector>(); + }); + pacer.EnsureStarted(); - // Set a high probe rate. - const int kProbeClusterId = 1; - DataRate kProbingRate = kPacingDataRate * 10; - pacer.CreateProbeCluster(kProbingRate, kProbeClusterId); + // Add some packets and wait till all have been sent, so that the pacer + // has a valid estimate of packet size. + const int kNumWarmupPackets = 40; + EXPECT_CALL(packet_router, SendPacket).Times(kNumWarmupPackets); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kNumWarmupPackets)); + // Wait until all packes have been sent, with a 2x margin. + time_controller.AdvanceTime(kPacketPacingTime * (kNumWarmupPackets * 2)); - // Advance time less than PacingController::kMinSleepTime, probing packets - // for the first millisecond should be sent immediately. Min delta between - // probes is 2x 100us, meaning 4 times per ms we will get least one call to - // SendPacket(). - DataSize data_sent = DataSize::Zero(); - EXPECT_CALL( - packet_router, - SendPacket(_, ::testing::Field(&PacedPacketInfo::probe_cluster_id, - kProbeClusterId))) - .Times(AtLeast(4)) - .WillRepeatedly([&](std::unique_ptr packet, - const PacedPacketInfo&) { - data_sent += - DataSize::Bytes(packet->payload_size() + packet->padding_size()); - }); + // Enqueue packets. Expect onlt the first one to be sent immediately. + EXPECT_CALL(packet_router, SendPacket).Times(1); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketBasedHoldback)); + time_controller.AdvanceTime(TimeDelta::Zero()); - // Add one packet to kickstart probing, the rest will be padding packets. - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); - time_controller.AdvanceTime(kMinProbeDelta); + // Advance time to the fixed coalescing window, that should take presedence so + // at least some of the packets should be sent. + EXPECT_CALL(packet_router, SendPacket).Times(AtLeast(1)); + time_controller.AdvanceTime(kFixedCoalescingWindow); +} - // Verify the amount of probing data sent. - // Probe always starts with a small (1 byte) padding packet that's not - // counted into the probe rate here. - EXPECT_EQ(data_sent, - kProbingRate * TimeDelta::Millis(1) + DataSize::Bytes(1)); - } +TEST(TaskQueuePacedSenderTest, Stats) { + static constexpr Timestamp kStartTime = Timestamp::Millis(1234); + GlobalSimulatedTimeController time_controller(kStartTime); + MockPacketRouter packet_router; + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + kNoPacketHoldback); - TEST(TaskQueuePacedSenderTest, NoStatsUpdatesBeforeStart) { - const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); - GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); - MockPacketRouter packet_router; - TaskQueuePacedSenderForTest pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - kCoalescingWindow); - const DataRate kPacingDataRate = DataRate::KilobitsPerSec(300); - pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + // Simulate ~2mbps video stream, covering one second. + static constexpr size_t kPacketsToSend = 200; + static constexpr DataRate kPacingRate = + DataRate::BytesPerSec(kDefaultPacketSize * kPacketsToSend); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); - const TimeDelta kMinTimeBetweenStatsUpdates = TimeDelta::Millis(1); + // Allowed `QueueSizeData` and `ExpectedQueueTime` deviation. + static constexpr size_t kAllowedPacketsDeviation = 1; + static constexpr DataSize kAllowedQueueSizeDeviation = + DataSize::Bytes(kDefaultPacketSize * kAllowedPacketsDeviation); + static constexpr TimeDelta kAllowedQueueTimeDeviation = + kAllowedQueueSizeDeviation / kPacingRate; - // Nothing inserted, no stats updates yet. - EXPECT_EQ(pacer.num_stats_updates_, 0u); + DataSize expected_queue_size = DataSize::MinusInfinity(); + TimeDelta expected_queue_time = TimeDelta::MinusInfinity(); - // Insert one packet, stats should not be updated. - pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); - time_controller.AdvanceTime(TimeDelta::Zero()); - EXPECT_EQ(pacer.num_stats_updates_, 0u); + EXPECT_CALL(packet_router, SendPacket).Times(kPacketsToSend); + + // Stats before insert any packets. + EXPECT_TRUE(pacer.OldestPacketWaitTime().IsZero()); + EXPECT_FALSE(pacer.FirstSentPacketTime().has_value()); + EXPECT_TRUE(pacer.QueueSizeData().IsZero()); + EXPECT_TRUE(pacer.ExpectedQueueTime().IsZero()); + + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + + // Advance to 200ms. + time_controller.AdvanceTime(TimeDelta::Millis(200)); + EXPECT_EQ(pacer.OldestPacketWaitTime(), TimeDelta::Millis(200)); + EXPECT_EQ(pacer.FirstSentPacketTime(), kStartTime); + + expected_queue_size = kPacingRate * TimeDelta::Millis(800); + expected_queue_time = expected_queue_size / kPacingRate; + EXPECT_NEAR(pacer.QueueSizeData().bytes(), expected_queue_size.bytes(), + kAllowedQueueSizeDeviation.bytes()); + EXPECT_NEAR(pacer.ExpectedQueueTime().ms(), expected_queue_time.ms(), + kAllowedQueueTimeDeviation.ms()); + + // Advance to 500ms. + time_controller.AdvanceTime(TimeDelta::Millis(300)); + EXPECT_EQ(pacer.OldestPacketWaitTime(), TimeDelta::Millis(500)); + EXPECT_EQ(pacer.FirstSentPacketTime(), kStartTime); + + expected_queue_size = kPacingRate * TimeDelta::Millis(500); + expected_queue_time = expected_queue_size / kPacingRate; + EXPECT_NEAR(pacer.QueueSizeData().bytes(), expected_queue_size.bytes(), + kAllowedQueueSizeDeviation.bytes()); + EXPECT_NEAR(pacer.ExpectedQueueTime().ms(), expected_queue_time.ms(), + kAllowedQueueTimeDeviation.ms()); + + // Advance to 1000ms+, expect all packets to be sent. + time_controller.AdvanceTime(TimeDelta::Millis(500) + + kAllowedQueueTimeDeviation); + EXPECT_TRUE(pacer.OldestPacketWaitTime().IsZero()); + EXPECT_EQ(pacer.FirstSentPacketTime(), kStartTime); + EXPECT_TRUE(pacer.QueueSizeData().IsZero()); + EXPECT_TRUE(pacer.ExpectedQueueTime().IsZero()); +} + +TEST(TaskQueuePacedSenderTest, HighPrecisionPacingWhenSlackIsDisabled) { + test::ScopedKeyValueConfig experiments( + "WebRTC-SlackedTaskQueuePacedSender/Disabled/"); + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + TaskQueueWithFakePrecisionFactory task_queue_factory( + time_controller.GetTaskQueueFactory()); + + MockPacketRouter packet_router; + TaskQueuePacedSender pacer( + time_controller.GetClock(), &packet_router, experiments, + &task_queue_factory, PacingController::kMinSleepTime, kNoPacketHoldback); + + // Send enough packets (covering one second) that pacing is triggered, i.e. + // delayed tasks being scheduled. + static constexpr size_t kPacketsToSend = 42; + static constexpr DataRate kPacingRate = + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + // Expect all of them to be sent. + size_t packets_sent = 0; + EXPECT_CALL(packet_router, SendPacket) + .WillRepeatedly( + [&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { ++packets_sent; }); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(packets_sent, kPacketsToSend); + + // Expect pacing to make use of high precision. + EXPECT_EQ(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); + + // Create probe cluster which is also high precision. + pacer.CreateProbeCluster(kPacingRate, 123); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); +} + +TEST(TaskQueuePacedSenderTest, LowPrecisionPacingWhenSlackIsEnabled) { + test::ScopedKeyValueConfig experiments( + "WebRTC-SlackedTaskQueuePacedSender/Enabled/"); + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + TaskQueueWithFakePrecisionFactory task_queue_factory( + time_controller.GetTaskQueueFactory()); + + MockPacketRouter packet_router; + TaskQueuePacedSender pacer( + time_controller.GetClock(), &packet_router, experiments, + &task_queue_factory, PacingController::kMinSleepTime, kNoPacketHoldback); + + // Send enough packets (covering one second) that pacing is triggered, i.e. + // delayed tasks being scheduled. + static constexpr size_t kPacketsToSend = 42; + static constexpr DataRate kPacingRate = + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + // Expect all of them to be sent. + size_t packets_sent = 0; + EXPECT_CALL(packet_router, SendPacket) + .WillRepeatedly( + [&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { ++packets_sent; }); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(packets_sent, kPacketsToSend); + + // Expect pacing to make use of low precision. + EXPECT_GT(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_EQ(task_queue_factory.delayed_high_precision_count(), 0); + + // Create probe cluster, which uses high precision despite regular pacing + // being low precision. + pacer.CreateProbeCluster(kPacingRate, 123); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); +} - // Advance time of the min stats update interval, and trigger a - // refresh - stats should not be updated still. - time_controller.AdvanceTime(kMinTimeBetweenStatsUpdates); - EXPECT_EQ(pacer.num_stats_updates_, 0u); - } } // namespace test } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/BUILD.gn b/modules/remote_bitrate_estimator/BUILD.gn index 923f00a74c..fd52b2dcb9 100644 --- a/modules/remote_bitrate_estimator/BUILD.gn +++ b/modules/remote_bitrate_estimator/BUILD.gn @@ -122,6 +122,7 @@ if (rtc_include_tests) { "../../api/transport:field_trial_based_config", "../../api/transport:mock_network_control", "../../api/transport:network_control", + "../../api/units:data_rate", "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", diff --git a/modules/remote_bitrate_estimator/OWNERS b/modules/remote_bitrate_estimator/OWNERS index 9b97144ac8..97e5d51dac 100644 --- a/modules/remote_bitrate_estimator/OWNERS +++ b/modules/remote_bitrate_estimator/OWNERS @@ -3,4 +3,4 @@ terelius@webrtc.org asapersson@webrtc.org mflodman@webrtc.org philipel@webrtc.org -srte@webrtc.org +perkj@webrtc.org diff --git a/modules/remote_bitrate_estimator/aimd_rate_control.cc b/modules/remote_bitrate_estimator/aimd_rate_control.cc index 0a754e1f41..1714dd115a 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control.cc +++ b/modules/remote_bitrate_estimator/aimd_rate_control.cc @@ -53,9 +53,9 @@ double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) { sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor); if (parsed_values == 1) { if (backoff_factor >= 1.0) { - RTC_LOG(WARNING) << "Back-off factor must be less than 1."; + RTC_LOG(LS_WARNING) << "Back-off factor must be less than 1."; } else if (backoff_factor <= 0.0) { - RTC_LOG(WARNING) << "Back-off factor must be greater than 0."; + RTC_LOG(LS_WARNING) << "Back-off factor must be greater than 0."; } else { return backoff_factor; } @@ -95,11 +95,13 @@ AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config, estimate_bounded_backoff_( IsNotDisabled(*key_value_config, "WebRTC-Bwe-EstimateBoundedBackoff")), - estimate_bounded_increase_( - IsNotDisabled(*key_value_config, - "WebRTC-Bwe-EstimateBoundedIncrease")), initial_backoff_interval_("initial_backoff_interval"), link_capacity_fix_("link_capacity_fix") { + ParseFieldTrial( + {&disable_estimate_bounded_increase_, &estimate_bounded_increase_ratio_, + &ignore_throughput_limit_if_network_estimate_, + &ignore_network_estimate_decrease_}, + key_value_config->Lookup("WebRTC-Bwe-EstimateBoundedIncrease")); // E.g // WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/ ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_}, @@ -272,27 +274,34 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, ChangeState(input, at_time); - // We limit the new bitrate based on the troughput to avoid unlimited bitrate - // increases. We allow a bit more lag at very low rates to not too easily get - // stuck if the encoder produces uneven outputs. - const DataRate troughput_based_limit = - 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); - switch (rate_control_state_) { case RateControlState::kRcHold: break; - case RateControlState::kRcIncrease: + case RateControlState::kRcIncrease: { if (estimated_throughput > link_capacity_.UpperBound()) link_capacity_.Reset(); - // Do not increase the delay based estimate in alr since the estimator - // will not be able to get transport feedback necessary to detect if - // the new estimate is correct. - // If we have previously increased above the limit (for instance due to - // probing), we don't allow further changes. - if (current_bitrate_ < troughput_based_limit && - !(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) { + // We limit the new bitrate based on the troughput to avoid unlimited + // bitrate increases. We allow a bit more lag at very low rates to not too + // easily get stuck if the encoder produces uneven outputs. + DataRate increase_limit = + 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); + if (ignore_throughput_limit_if_network_estimate_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + // If we have a Network estimate, we do allow the estimate to increase. + increase_limit = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + } else if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) { + // Do not increase the delay based estimate in alr since the estimator + // will not be able to get transport feedback necessary to detect if + // the new estimate is correct. + // If we have previously increased above the limit (for instance due to + // probing), we don't allow further changes. + increase_limit = current_bitrate_; + } + + if (current_bitrate_ < increase_limit) { DataRate increased_bitrate = DataRate::MinusInfinity(); if (link_capacity_.has_estimate()) { // The link_capacity estimate is reset if the measured throughput @@ -309,11 +318,11 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, at_time, time_last_bitrate_change_, current_bitrate_); increased_bitrate = current_bitrate_ + multiplicative_increase; } - new_bitrate = std::min(increased_bitrate, troughput_based_limit); + new_bitrate = std::min(increased_bitrate, increase_limit); } - time_last_bitrate_change_ = at_time; break; + } case RateControlState::kRcDecrease: { DataRate decreased_bitrate = DataRate::PlusInfinity(); @@ -361,16 +370,21 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_)); } DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const { - if (estimate_bounded_increase_ && network_estimate_) { - DataRate upper_bound = network_estimate_->link_capacity_upper; - new_bitrate = std::min(new_bitrate, upper_bound); + if (!disable_estimate_bounded_increase_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + DataRate upper_bound = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + if (ignore_network_estimate_decrease_) { + upper_bound = std::max(upper_bound, current_bitrate_); + } + new_bitrate = std::min(upper_bound, new_bitrate); } new_bitrate = std::max(new_bitrate, min_configured_bitrate_); return new_bitrate; @@ -416,7 +430,7 @@ void AimdRateControl::ChangeState(const RateControlInput& input, rate_control_state_ = RateControlState::kRcHold; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/modules/remote_bitrate_estimator/aimd_rate_control.h b/modules/remote_bitrate_estimator/aimd_rate_control.h index 3e0d541b60..699b185c2d 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control.h +++ b/modules/remote_bitrate_estimator/aimd_rate_control.h @@ -107,9 +107,14 @@ class AimdRateControl { // Use estimated link capacity lower bound if it is higher than the // acknowledged rate when backing off due to overuse. const bool estimate_bounded_backoff_; - // Use estimated link capacity upper bound as upper limit for increasing - // bitrate over the acknowledged rate. - const bool estimate_bounded_increase_; + // If false, uses estimated link capacity upper bound * + // `estimate_bounded_increase_ratio_` as upper limit for the estimate. + FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"}; + FieldTrialParameter estimate_bounded_increase_ratio_{"ratio", 1.0}; + FieldTrialParameter ignore_throughput_limit_if_network_estimate_{ + "ignore_acked", false}; + FieldTrialParameter ignore_network_estimate_decrease_{"ignore_decr", + false}; absl::optional last_decrease_; FieldTrialOptional initial_backoff_interval_; FieldTrialFlag link_capacity_fix_; diff --git a/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc b/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc index 6cbccf6b7b..5d9c328e06 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc +++ b/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc @@ -12,6 +12,7 @@ #include #include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" @@ -254,6 +255,37 @@ TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) { 2 * kInitialBitrateBps); } +TEST(AimdRateControlTest, SetEstimateClampedByNetworkEstimate) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, SetEstimateIgnoresNetworkEstimatesLowerThanCurrent) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(30)); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); + + NetworkStateEstimate lower_network_estimate; + lower_network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(300); + states.aimd_rate_control->SetNetworkStateEstimate(lower_network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { // Allow the estimate to increase as long as alr is not detected to ensure // tha BWE can not get stuck at a certain bitrate. @@ -274,4 +306,123 @@ TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { kInitialBitrateBps); } +TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(false); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_GT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, EstimateLimitedByNetworkEstimateInAlrIfSet) { + // Even if alr is detected, the delay based estimator is allowed to increase + // up to a percentage of upper link capacity. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, EstimateNotLoweredByNetworkEstimate) { + // The delay based estimator is allowed to increase up to a percentage of + // upper link capacity but does not decrease unless the delay detector + // discover an overuse. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + constexpr int kEstimatedThroughputBps = 30'000; + SetEstimate(states, kInitialBitrateBps); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + DataRate estimate_after_increase = states.aimd_rate_control->LatestEstimate(); + ASSERT_EQ(estimate_after_increase, + network_estimate.link_capacity_upper * 0.85); + + // A lower network estimate does not decrease the estimate immediately, + // but the estimate is not allowed to increase. + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(100); + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(80); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 10; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + estimate_after_increase); + } + + // If the detector detects and overuse, BWE drops to a value relative the + // network estimate. + UpdateRateControl(states, BandwidthUsage::kBwOverusing, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_LT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_lower); + EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), + kEstimatedThroughputBps); +} + +TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlrIfNetworkEstimateNotSet) { + // When alr is detected, the delay based estimator is not allowed to increase + // bwe since there will be no feedback from the network if the new estimate + // is correct. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); +} + } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/bwe_defines.cc b/modules/remote_bitrate_estimator/bwe_defines.cc index 6afbe133e2..22c605e82d 100644 --- a/modules/remote_bitrate_estimator/bwe_defines.cc +++ b/modules/remote_bitrate_estimator/bwe_defines.cc @@ -10,8 +10,6 @@ #include "modules/remote_bitrate_estimator/include/bwe_defines.h" -#include "system_wrappers/include/field_trial.h" - namespace webrtc { const char kBweTypeHistogram[] = "WebRTC.BWE.Types"; diff --git a/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h b/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h index a46a6d47c8..dcc08f49cc 100644 --- a/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h +++ b/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h @@ -38,9 +38,6 @@ class RemoteBitrateObserver { virtual ~RemoteBitrateObserver() {} }; -// TODO(holmer): Remove when all implementations have been updated. -struct ReceiveBandwidthEstimatorStats {}; - class RemoteBitrateEstimator : public CallStatsObserver, public Module { public: ~RemoteBitrateEstimator() override {} @@ -63,9 +60,6 @@ class RemoteBitrateEstimator : public CallStatsObserver, public Module { virtual bool LatestEstimate(std::vector* ssrcs, uint32_t* bitrate_bps) const = 0; - // TODO(holmer): Remove when all implementations have been updated. - virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const; - virtual void SetMinBitrate(int min_bitrate_bps) = 0; protected: @@ -73,11 +67,6 @@ class RemoteBitrateEstimator : public CallStatsObserver, public Module { static const int64_t kStreamTimeOutMs = 2000; }; -inline bool RemoteBitrateEstimator::GetStats( - ReceiveBandwidthEstimatorStats* output) const { - return false; -} - } // namespace webrtc #endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_ diff --git a/modules/remote_bitrate_estimator/overuse_detector.h b/modules/remote_bitrate_estimator/overuse_detector.h index d1c6aa8d30..179e290c21 100644 --- a/modules/remote_bitrate_estimator/overuse_detector.h +++ b/modules/remote_bitrate_estimator/overuse_detector.h @@ -14,7 +14,6 @@ #include "api/network_state_predictor.h" #include "api/transport/webrtc_key_value_config.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -26,6 +25,9 @@ class OveruseDetector { explicit OveruseDetector(const WebRtcKeyValueConfig* key_value_config); virtual ~OveruseDetector(); + OveruseDetector(const OveruseDetector&) = delete; + OveruseDetector& operator=(const OveruseDetector&) = delete; + // Update the detection state based on the estimated inter-arrival time delta // offset. `timestamp_delta` is the delta between the last timestamp which the // estimated offset is based on and the last timestamp on which the last @@ -54,8 +56,6 @@ class OveruseDetector { double time_over_using_; int overuse_counter_; BandwidthUsage hypothesis_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OveruseDetector); }; } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/overuse_estimator.h b/modules/remote_bitrate_estimator/overuse_estimator.h index d023b36d89..c021f00da7 100644 --- a/modules/remote_bitrate_estimator/overuse_estimator.h +++ b/modules/remote_bitrate_estimator/overuse_estimator.h @@ -15,7 +15,6 @@ #include #include "api/network_state_predictor.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -39,6 +38,9 @@ class OveruseEstimator { explicit OveruseEstimator(const OverUseDetectorOptions& options); ~OveruseEstimator(); + OveruseEstimator(const OveruseEstimator&) = delete; + OveruseEstimator& operator=(const OveruseEstimator&) = delete; + // Update the estimator with a new sample. The deltas should represent deltas // between timestamp groups as defined by the InterArrival class. // `current_hypothesis` should be the hypothesis of the over-use detector at @@ -75,8 +77,6 @@ class OveruseEstimator { double avg_noise_; double var_noise_; std::deque ts_delta_hist_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OveruseEstimator); }; } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc index ae960ab960..0bc4f6dd12 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc @@ -278,13 +278,7 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo( TimeoutStreams(now); RTC_DCHECK(inter_arrival_); RTC_DCHECK(estimator_); - // TODO(danilchap): Replace 5 lines below with insert_or_assign when that - // c++17 function is available. - auto inserted = ssrcs_.insert(std::make_pair(ssrc, now)); - if (!inserted.second) { - // Already inserted, update. - inserted.first->second = now; - } + ssrcs_.insert_or_assign(ssrc, now); // For now only try to detect probes while we don't have a valid estimate. // We currently assume that only packets larger than 200 bytes are paced by diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc index 10dd99cfd9..d8ef23cc92 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc @@ -11,7 +11,6 @@ #include "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h" #include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" -#include "rtc_base/constructor_magic.h" #include "test/gtest.h" namespace webrtc { @@ -20,13 +19,18 @@ class RemoteBitrateEstimatorAbsSendTimeTest : public RemoteBitrateEstimatorTest { public: RemoteBitrateEstimatorAbsSendTimeTest() {} + + RemoteBitrateEstimatorAbsSendTimeTest( + const RemoteBitrateEstimatorAbsSendTimeTest&) = delete; + RemoteBitrateEstimatorAbsSendTimeTest& operator=( + const RemoteBitrateEstimatorAbsSendTimeTest&) = delete; + virtual void SetUp() { bitrate_estimator_.reset(new RemoteBitrateEstimatorAbsSendTime( bitrate_observer_.get(), &clock_)); } protected: - RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorAbsSendTimeTest); }; TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, InitialBehavior) { diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc index d0a7b22505..64ef39d935 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc @@ -11,7 +11,6 @@ #include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h" #include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" -#include "rtc_base/constructor_magic.h" #include "test/gtest.h" namespace webrtc { @@ -19,13 +18,18 @@ namespace webrtc { class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest { public: RemoteBitrateEstimatorSingleTest() {} + + RemoteBitrateEstimatorSingleTest(const RemoteBitrateEstimatorSingleTest&) = + delete; + RemoteBitrateEstimatorSingleTest& operator=( + const RemoteBitrateEstimatorSingleTest&) = delete; + virtual void SetUp() { bitrate_estimator_.reset(new RemoteBitrateEstimatorSingleStream( bitrate_observer_.get(), &clock_)); } protected: - RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorSingleTest); }; TEST_F(RemoteBitrateEstimatorSingleTest, InitialBehavior) { diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h b/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h index db4e899fbe..a3b1cfdb34 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h @@ -18,7 +18,6 @@ #include #include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/clock.h" #include "test/gtest.h" @@ -71,6 +70,10 @@ class RtpStream { uint32_t frequency, uint32_t timestamp_offset, int64_t rtcp_receive_time); + + RtpStream(const RtpStream&) = delete; + RtpStream& operator=(const RtpStream&) = delete; + void set_rtp_timestamp_offset(uint32_t offset); // Generates a new frame for this stream. If called too soon after the @@ -104,8 +107,6 @@ class RtpStream { int64_t next_rtcp_time_; uint32_t rtp_timestamp_offset_; const double kNtpFracPerMs; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpStream); }; class StreamGenerator { @@ -116,6 +117,9 @@ class StreamGenerator { ~StreamGenerator(); + StreamGenerator(const StreamGenerator&) = delete; + StreamGenerator& operator=(const StreamGenerator&) = delete; + // Add a new stream. void AddStream(RtpStream* stream); @@ -142,8 +146,6 @@ class StreamGenerator { int64_t prev_arrival_time_us_; // All streams being transmitted on this simulated channel. StreamMap streams_; - - RTC_DISALLOW_COPY_AND_ASSIGN(StreamGenerator); }; } // namespace testing @@ -152,6 +154,10 @@ class RemoteBitrateEstimatorTest : public ::testing::Test { RemoteBitrateEstimatorTest(); virtual ~RemoteBitrateEstimatorTest(); + RemoteBitrateEstimatorTest(const RemoteBitrateEstimatorTest&) = delete; + RemoteBitrateEstimatorTest& operator=(const RemoteBitrateEstimatorTest&) = + delete; + protected: virtual void SetUp() = 0; @@ -213,8 +219,6 @@ class RemoteBitrateEstimatorTest : public ::testing::Test { std::unique_ptr bitrate_estimator_; std::unique_ptr stream_generator_; int64_t arrival_time_offset_ms_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorTest); }; } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.h b/modules/remote_bitrate_estimator/test/bwe_test_logging.h index e85565596d..49e1e716b2 100644 --- a/modules/remote_bitrate_estimator/test/bwe_test_logging.h +++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.h @@ -128,7 +128,6 @@ #include #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \ @@ -341,6 +340,10 @@ class Logging { Logging(); ~Logging(); + + Logging(const Logging&) = delete; + Logging& operator=(const Logging&) = delete; + void PushState(const std::string& append_to_tag, int64_t timestamp_ms, bool enabled); @@ -348,8 +351,6 @@ class Logging { Mutex mutex_; ThreadMap thread_map_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Logging); }; } // namespace bwe } // namespace testing diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 950e832616..9922484647 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -377,7 +377,9 @@ rtc_library("rtcp_transceiver") { "../../api/units:timestamp", "../../api/video:video_bitrate_allocation", "../../rtc_base:checks", + "../../rtc_base:divide_round", "../../rtc_base:rtc_base_approved", + "../../rtc_base/containers:flat_map", "../../rtc_base/task_utils:repeating_task", "../../rtc_base/task_utils:to_queued_task", "../../system_wrappers", @@ -397,6 +399,7 @@ rtc_library("rtp_video_header") { "source/rtp_video_header.h", ] deps = [ + "../../api:rtp_headers", "../../api/transport/rtp:dependency_descriptor", "../../api/video:video_frame", "../../api/video:video_frame_type", @@ -435,7 +438,6 @@ rtc_library("mock_rtp_rtcp") { deps = [ ":rtp_rtcp", ":rtp_rtcp_format", - "..:module_api", "../../api/video:video_bitrate_allocation", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", diff --git a/modules/rtp_rtcp/include/remote_ntp_time_estimator.h b/modules/rtp_rtcp/include/remote_ntp_time_estimator.h index 5734a50e14..f31503dc4e 100644 --- a/modules/rtp_rtcp/include/remote_ntp_time_estimator.h +++ b/modules/rtp_rtcp/include/remote_ntp_time_estimator.h @@ -14,7 +14,6 @@ #include #include "absl/types/optional.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/numerics/moving_median_filter.h" #include "system_wrappers/include/rtp_to_ntp_estimator.h" @@ -32,6 +31,9 @@ class RemoteNtpTimeEstimator { ~RemoteNtpTimeEstimator(); + RemoteNtpTimeEstimator(const RemoteNtpTimeEstimator&) = delete; + RemoteNtpTimeEstimator& operator=(const RemoteNtpTimeEstimator&) = delete; + // Updates the estimator with round trip time `rtt`, NTP seconds `ntp_secs`, // NTP fraction `ntp_frac` and RTP timestamp `rtp_timestamp`. bool UpdateRtcpTimestamp(int64_t rtt, @@ -52,7 +54,6 @@ class RemoteNtpTimeEstimator { MovingMedianFilter ntp_clocks_offset_estimator_; RtpToNtpEstimator rtp_to_ntp_; int64_t last_timing_log_ms_; - RTC_DISALLOW_COPY_AND_ASSIGN(RemoteNtpTimeEstimator); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/include/rtp_cvo.h b/modules/rtp_rtcp/include/rtp_cvo.h index bc0b4ee95c..497946d6a7 100644 --- a/modules/rtp_rtcp/include/rtp_cvo.h +++ b/modules/rtp_rtcp/include/rtp_cvo.h @@ -30,7 +30,7 @@ inline uint8_t ConvertVideoRotationToCVOByte(VideoRotation rotation) { case kVideoRotation_270: return 3; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -47,7 +47,7 @@ inline VideoRotation ConvertCVOByteToVideoRotation(uint8_t cvo_byte) { case 3: return kVideoRotation_270; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kVideoRotation_0; } } diff --git a/modules/rtp_rtcp/include/rtp_header_extension_map.h b/modules/rtp_rtcp/include/rtp_header_extension_map.h index a746d8a076..ff1ea61f52 100644 --- a/modules/rtp_rtcp/include/rtp_header_extension_map.h +++ b/modules/rtp_rtcp/include/rtp_header_extension_map.h @@ -15,6 +15,7 @@ #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -35,7 +36,7 @@ class RtpHeaderExtensionMap { template bool Register(int id) { - return Register(id, Extension::kId, Extension::kUri); + return Register(id, Extension::kId, Extension::Uri()); } bool RegisterByType(int id, RTPExtensionType type); bool RegisterByUri(int id, absl::string_view uri); @@ -52,7 +53,6 @@ class RtpHeaderExtensionMap { return ids_[type]; } - int32_t Deregister(RTPExtensionType type); void Deregister(absl::string_view uri); // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285. @@ -64,7 +64,7 @@ class RtpHeaderExtensionMap { } private: - bool Register(int id, RTPExtensionType type, const char* uri); + bool Register(int id, RTPExtensionType type, absl::string_view uri); uint8_t ids_[kRtpExtensionNumberOfExtensions]; bool extmap_allow_mixed_; diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.cc b/modules/rtp_rtcp/include/rtp_rtcp_defines.cc index 5aa41fccb3..78e730f3c9 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp_defines.cc +++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.cc @@ -25,6 +25,9 @@ namespace { constexpr size_t kMidRsidMaxSize = 16; // Check if passed character is a "token-char" from RFC 4566. +// https://datatracker.ietf.org/doc/html/rfc4566#section-9 +// token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 +// / %x41-5A / %x5E-7E bool IsTokenChar(char ch) { return ch == 0x21 || (ch >= 0x23 && ch <= 0x27) || ch == 0x2a || ch == 0x2b || ch == 0x2d || ch == 0x2e || (ch >= 0x30 && ch <= 0x39) || diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h index dfdf641ed2..79626255ed 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h +++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h @@ -103,6 +103,12 @@ enum RTCPPacketType : uint32_t { kRtcpXrTargetBitrate = 0x200000 }; +enum class KeyFrameReqMethod : uint8_t { + kNone, // Don't request keyframes. + kPliRtcp, // Request keyframes through Picture Loss Indication. + kFirRtcp // Request keyframes through Full Intra-frame Request. +}; + enum RtxMode { kRtxOff = 0x0, kRtxRetransmitted = 0x1, // Only send retransmissions over RTX. @@ -228,8 +234,6 @@ struct RtpPacketSendInfo { RtpPacketSendInfo() = default; uint16_t transport_sequence_number = 0; - // TODO(bugs.webrtc.org/12713): Remove once downstream usage is gone. - uint32_t ssrc = 0; absl::optional media_ssrc; uint16_t rtp_sequence_number = 0; // Only valid if `media_ssrc` is set. uint32_t rtp_timestamp = 0; diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 05e49fb861..2f1c894942 100644 --- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -19,7 +19,6 @@ #include "absl/types/optional.h" #include "api/video/video_bitrate_allocation.h" -#include "modules/include/module.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/rtp_rtcp/source/rtp_rtcp_interface.h" @@ -50,10 +49,6 @@ class MockRtpRtcpInterface : public RtpRtcpInterface { RegisterRtpHeaderExtension, (absl::string_view uri, int id), (override)); - MOCK_METHOD(int32_t, - DeregisterSendRtpHeaderExtension, - (RTPExtensionType type), - (override)); MOCK_METHOD(void, DeregisterSendRtpHeaderExtension, (absl::string_view uri), diff --git a/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc b/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc index 201a1ce05c..db6b50a449 100644 --- a/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc +++ b/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc @@ -156,7 +156,7 @@ void DEPRECATED_RtpSenderEgress::SendPacket( // In case of VideoTimingExtension, since it's present not in every packet, // data after rtp header may be corrupted if these packets are protected by // the FEC. - int64_t diff_ms = now_ms - packet->capture_time_ms(); + int64_t diff_ms = now_ms - packet->capture_time().ms(); if (packet->HasExtension()) { packet->SetExtension(kTimestampTicksPerMs * diff_ms); } @@ -167,9 +167,9 @@ void DEPRECATED_RtpSenderEgress::SendPacket( if (packet->HasExtension()) { if (populate_network2_timestamp_) { - packet->set_network2_time_ms(now_ms); + packet->set_network2_time(Timestamp::Millis(now_ms)); } else { - packet->set_pacer_exit_time_ms(now_ms); + packet->set_pacer_exit_time(Timestamp::Millis(now_ms)); } } @@ -190,8 +190,8 @@ void DEPRECATED_RtpSenderEgress::SendPacket( if (packet->packet_type() != RtpPacketMediaType::kPadding && packet->packet_type() != RtpPacketMediaType::kRetransmission) { - UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc); - UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), + UpdateDelayStatistics(packet->capture_time().ms(), now_ms, packet_ssrc); + UpdateOnSendPacket(options.packet_id, packet->capture_time().ms(), packet_ssrc); } @@ -201,7 +201,7 @@ void DEPRECATED_RtpSenderEgress::SendPacket( // actual sending fails. if (is_media && packet->allow_retransmission()) { packet_history_->PutRtpPacket(std::make_unique(*packet), - now_ms); + Timestamp::Millis(now_ms)); } else if (packet->retransmitted_sequence_number()) { packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number()); } @@ -323,8 +323,6 @@ void DEPRECATED_RtpSenderEgress::AddPacketToTransportFeedback( } RtpPacketSendInfo packet_info; - // TODO(bugs.webrtc.org/12713): Remove once downstream usage is gone. - packet_info.ssrc = ssrc_; packet_info.media_ssrc = ssrc_; packet_info.transport_sequence_number = packet_id; packet_info.rtp_sequence_number = packet.SequenceNumber(); diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc b/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc index 59541c45d0..2fbb3caae8 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc @@ -233,7 +233,8 @@ size_t FlexfecHeaderWriter::MinPacketMaskSize(const uint8_t* packet_mask, // We must expand it with zeros. return kFlexfecPacketMaskSizes[2]; } - RTC_NOTREACHED() << "Incorrect packet mask size: " << packet_mask_size << "."; + RTC_DCHECK_NOTREACHED() << "Incorrect packet mask size: " << packet_mask_size + << "."; return kFlexfecPacketMaskSizes[2]; } @@ -311,8 +312,8 @@ void FlexfecHeaderWriter::FinalizeFecHeader( written_packet_mask[2] |= 0x40; // Set bit 15. } } else { - RTC_NOTREACHED() << "Incorrect packet mask size: " << packet_mask_size - << "."; + RTC_DCHECK_NOTREACHED() + << "Incorrect packet mask size: " << packet_mask_size << "."; } } diff --git a/modules/rtp_rtcp/source/flexfec_receiver.cc b/modules/rtp_rtcp/source/flexfec_receiver.cc index e01b9205cf..b5198b53a6 100644 --- a/modules/rtp_rtcp/source/flexfec_receiver.cc +++ b/modules/rtp_rtcp/source/flexfec_receiver.cc @@ -159,18 +159,28 @@ void FlexfecReceiver::ProcessReceivedPacket( // Set this flag first, since OnRecoveredPacket may end up here // again, with the same packet. recovered_packet->returned = true; - RTC_CHECK_GT(recovered_packet->pkt->data.size(), 0); + RTC_CHECK_GE(recovered_packet->pkt->data.size(), kRtpHeaderSize); recovered_packet_receiver_->OnRecoveredPacket( recovered_packet->pkt->data.cdata(), recovered_packet->pkt->data.size()); - // Periodically log the incoming packets. + uint32_t media_ssrc = + ForwardErrorCorrection::ParseSsrc(recovered_packet->pkt->data.data()); + uint16_t media_seq_num = ForwardErrorCorrection::ParseSequenceNumber( + recovered_packet->pkt->data.data()); + // Periodically log the incoming packets at LS_INFO. int64_t now_ms = clock_->TimeInMilliseconds(); - if (now_ms - last_recovered_packet_ms_ > kPacketLogIntervalMs) { - uint32_t media_ssrc = - ForwardErrorCorrection::ParseSsrc(recovered_packet->pkt->data.data()); - RTC_LOG(LS_VERBOSE) << "Recovered media packet with SSRC: " << media_ssrc - << " from FlexFEC stream with SSRC: " << ssrc_ << "."; - last_recovered_packet_ms_ = now_ms; + bool should_log_periodically = + now_ms - last_recovered_packet_ms_ > kPacketLogIntervalMs; + if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) || should_log_periodically) { + rtc::LoggingSeverity level = + should_log_periodically ? rtc::LS_INFO : rtc::LS_VERBOSE; + RTC_LOG_V(level) << "Recovered media packet with SSRC: " << media_ssrc + << " seq " << media_seq_num << " recovered length " + << recovered_packet->pkt->data.size() + << " from FlexFEC stream with SSRC: " << ssrc_; + if (should_log_periodically) { + last_recovered_packet_ms_ = now_ms; + } } } } diff --git a/modules/rtp_rtcp/source/flexfec_sender.cc b/modules/rtp_rtcp/source/flexfec_sender.cc index f1fe71d198..f6fe06e0e4 100644 --- a/modules/rtp_rtcp/source/flexfec_sender.cc +++ b/modules/rtp_rtcp/source/flexfec_sender.cc @@ -47,13 +47,13 @@ RtpHeaderExtensionMap RegisterSupportedExtensions( const std::vector& rtp_header_extensions) { RtpHeaderExtensionMap map; for (const auto& extension : rtp_header_extensions) { - if (extension.uri == TransportSequenceNumber::kUri) { + if (extension.uri == TransportSequenceNumber::Uri()) { map.Register(extension.id); - } else if (extension.uri == AbsoluteSendTime::kUri) { + } else if (extension.uri == AbsoluteSendTime::Uri()) { map.Register(extension.id); - } else if (extension.uri == TransmissionOffset::kUri) { + } else if (extension.uri == TransmissionOffset::Uri()) { map.Register(extension.id); - } else if (extension.uri == RtpMid::kUri) { + } else if (extension.uri == RtpMid::Uri()) { map.Register(extension.id); } else { RTC_LOG(LS_INFO) @@ -142,7 +142,7 @@ std::vector> FlexfecSender::GetFecPackets() { clock_->TimeInMilliseconds())); // Set "capture time" so that the TransmissionOffset header extension // can be set by the RTPSender. - fec_packet_to_send->set_capture_time_ms(clock_->TimeInMilliseconds()); + fec_packet_to_send->set_capture_time(clock_->CurrentTime()); fec_packet_to_send->SetSsrc(ssrc_); // Reserve extensions, if registered. These will be set by the RTPSender. fec_packet_to_send->ReserveExtension(); diff --git a/modules/rtp_rtcp/source/forward_error_correction_internal.cc b/modules/rtp_rtcp/source/forward_error_correction_internal.cc index 400b640d99..ac68162d26 100644 --- a/modules/rtp_rtcp/source/forward_error_correction_internal.cc +++ b/modules/rtp_rtcp/source/forward_error_correction_internal.cc @@ -266,7 +266,7 @@ void RemainingPacketProtection(int num_media_packets, } } } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/modules/rtp_rtcp/source/packet_sequencer.cc b/modules/rtp_rtcp/source/packet_sequencer.cc index a0c27dee67..55edd768a8 100644 --- a/modules/rtp_rtcp/source/packet_sequencer.cc +++ b/modules/rtp_rtcp/source/packet_sequencer.cc @@ -66,7 +66,7 @@ void PacketSequencer::Sequence(RtpPacketToSend& packet) { } packet.SetSequenceNumber(rtx_sequence_number_++); } else { - RTC_NOTREACHED() << "Unexpected ssrc " << packet.Ssrc(); + RTC_DCHECK_NOTREACHED() << "Unexpected ssrc " << packet.Ssrc(); } } @@ -99,7 +99,7 @@ void PacketSequencer::UpdateLastPacketState(const RtpPacketToSend& packet) { // Save timestamps to generate timestamp field and extensions for the padding. last_rtp_timestamp_ = packet.Timestamp(); last_timestamp_time_ms_ = clock_->TimeInMilliseconds(); - last_capture_time_ms_ = packet.capture_time_ms(); + last_capture_time_ms_ = packet.capture_time().ms(); } void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { @@ -107,7 +107,7 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { RTC_DCHECK(CanSendPaddingOnMediaSsrc()); packet.SetTimestamp(last_rtp_timestamp_); - packet.set_capture_time_ms(last_capture_time_ms_); + packet.set_capture_time(Timestamp::Millis(last_capture_time_ms_)); packet.SetPayloadType(last_payload_type_); return; } @@ -119,7 +119,7 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { } packet.SetTimestamp(last_rtp_timestamp_); - packet.set_capture_time_ms(last_capture_time_ms_); + packet.set_capture_time(Timestamp::Millis(last_capture_time_ms_)); // Only change the timestamp of padding packets sent over RTX. // Padding only packets over RTP has to be sent as part of a media @@ -129,9 +129,10 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { packet.SetTimestamp(packet.Timestamp() + (now_ms - last_timestamp_time_ms_) * kTimestampTicksPerMs); - if (packet.capture_time_ms() > 0) { - packet.set_capture_time_ms(packet.capture_time_ms() + - (now_ms - last_timestamp_time_ms_)); + if (packet.capture_time() > Timestamp::Zero()) { + packet.set_capture_time( + packet.capture_time() + + TimeDelta::Millis(now_ms - last_timestamp_time_ms_)); } } } diff --git a/modules/rtp_rtcp/source/packet_sequencer_unittest.cc b/modules/rtp_rtcp/source/packet_sequencer_unittest.cc index b82e76541c..d892863768 100644 --- a/modules/rtp_rtcp/source/packet_sequencer_unittest.cc +++ b/modules/rtp_rtcp/source/packet_sequencer_unittest.cc @@ -41,10 +41,10 @@ class PacketSequencerTest : public ::testing::Test { packet.set_packet_type(type); packet.SetSsrc(ssrc); packet.SetSequenceNumber(kDefaultSequenceNumber); - packet.set_capture_time_ms(clock_.TimeInMilliseconds()); + packet.set_capture_time(clock_.CurrentTime()); packet.SetTimestamp( kStartRtpTimestamp + - static_cast(packet.capture_time_ms() - kStartTime.ms())); + static_cast(packet.capture_time().ms() - kStartTime.ms())); return packet; } @@ -152,7 +152,7 @@ TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastMediaPacket) { EXPECT_EQ(padding_packet.SequenceNumber(), kMediaStartSequenceNumber + 1); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType); EXPECT_EQ(padding_packet.Timestamp(), media_packet.Timestamp()); - EXPECT_EQ(padding_packet.capture_time_ms(), media_packet.capture_time_ms()); + EXPECT_EQ(padding_packet.capture_time(), media_packet.capture_time()); } TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastRedPacket) { @@ -181,7 +181,7 @@ TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastRedPacket) { EXPECT_EQ(padding_packet.SequenceNumber(), kMediaStartSequenceNumber + 1); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType + 1); EXPECT_EQ(padding_packet.Timestamp(), media_packet.Timestamp()); - EXPECT_EQ(padding_packet.capture_time_ms(), media_packet.capture_time_ms()); + EXPECT_EQ(padding_packet.capture_time(), media_packet.capture_time()); } TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { @@ -201,7 +201,7 @@ TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { padding_packet.SetPayloadSize(100); padding_packet.SetPayloadType(kMediaPayloadType + 1); padding_packet.SetTimestamp(kStartRtpTimestamp + 1); - padding_packet.set_capture_time_ms(kStartTime.ms() + 1); + padding_packet.set_capture_time(kStartTime + TimeDelta::Millis(1)); sequencer_.set_rtx_sequence_number(kRtxStartSequenceNumber); sequencer_.Sequence(padding_packet); @@ -209,7 +209,7 @@ TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { EXPECT_EQ(padding_packet.SequenceNumber(), kRtxStartSequenceNumber); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType + 1); EXPECT_EQ(padding_packet.Timestamp(), kStartRtpTimestamp + 1); - EXPECT_EQ(padding_packet.capture_time_ms(), kStartTime.ms() + 1); + EXPECT_EQ(padding_packet.capture_time(), kStartTime + TimeDelta::Millis(1)); } TEST_F(PacketSequencerTest, UpdatesRtxPaddingBasedOnLastMediaPacket) { @@ -242,8 +242,8 @@ TEST_F(PacketSequencerTest, UpdatesRtxPaddingBasedOnLastMediaPacket) { EXPECT_EQ( padding_packet.Timestamp(), media_packet.Timestamp() + (kTimeDelta.ms() * kTimestampTicksPerMs)); - EXPECT_EQ(padding_packet.capture_time_ms(), - media_packet.capture_time_ms() + kTimeDelta.ms()); + EXPECT_EQ(padding_packet.capture_time(), + media_packet.capture_time() + kTimeDelta); } } // namespace diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.cc b/modules/rtp_rtcp/source/receive_statistics_impl.cc index b16f122ee1..d2dba66ec5 100644 --- a/modules/rtp_rtcp/source/receive_statistics_impl.cc +++ b/modules/rtp_rtcp/source/receive_statistics_impl.cc @@ -22,16 +22,13 @@ #include "modules/rtp_rtcp/source/rtp_rtcp_config.h" #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" #include "system_wrappers/include/clock.h" namespace webrtc { namespace { constexpr int64_t kStatisticsTimeoutMs = 8000; constexpr int64_t kStatisticsProcessIntervalMs = 1000; - -// Number of seconds since 1900 January 1 00:00 GMT (see -// https://tools.ietf.org/html/rfc868). -constexpr int64_t kNtpJan1970Millisecs = 2'208'988'800'000; } // namespace StreamStatistician::~StreamStatistician() {} @@ -43,7 +40,7 @@ StreamStatisticianImpl::StreamStatisticianImpl(uint32_t ssrc, clock_(clock), delta_internal_unix_epoch_ms_(clock_->CurrentNtpInMilliseconds() - clock_->TimeInMilliseconds() - - kNtpJan1970Millisecs), + rtc::kNtpJan1970Millisecs), incoming_bitrate_(kStatisticsProcessIntervalMs, RateStatistics::kBpsScale), max_reordering_threshold_(max_reordering_threshold), diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc index 723064eeba..b1602e5dcd 100644 --- a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc +++ b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc @@ -40,21 +40,21 @@ bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt, uint32_t ntp_secs, uint32_t ntp_frac, uint32_t rtp_timestamp) { - bool new_rtcp_sr = false; - if (!rtp_to_ntp_.UpdateMeasurements(ntp_secs, ntp_frac, rtp_timestamp, - &new_rtcp_sr)) { - return false; - } - if (!new_rtcp_sr) { - // No new RTCP SR since last time this function was called. - return true; + NtpTime sender_send_time(ntp_secs, ntp_frac); + switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) { + case RtpToNtpEstimator::kInvalidMeasurement: + return false; + case RtpToNtpEstimator::kSameMeasurement: + // No new RTCP SR since last time this function was called. + return true; + case RtpToNtpEstimator::kNewMeasurement: + break; } // Update extrapolator with the new arrival time. // The extrapolator assumes the ntp time. int64_t receiver_arrival_time_ms = clock_->CurrentNtpInMilliseconds(); - int64_t sender_send_time_ms = NtpTime(ntp_secs, ntp_frac).ToMs(); - int64_t sender_arrival_time_ms = sender_send_time_ms + rtt / 2; + int64_t sender_arrival_time_ms = sender_send_time.ToMs() + rtt / 2; int64_t remote_to_local_clocks_offset = receiver_arrival_time_ms - sender_arrival_time_ms; ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset); @@ -62,10 +62,11 @@ bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt, } int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) { - int64_t sender_capture_ntp_ms = 0; - if (!rtp_to_ntp_.Estimate(rtp_timestamp, &sender_capture_ntp_ms)) { + NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp); + if (!sender_capture.Valid()) { return -1; } + int64_t sender_capture_ntp_ms = sender_capture.ToMs(); int64_t remote_to_local_clocks_offset = ntp_clocks_offset_estimator_.GetFilteredValue(); diff --git a/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h b/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h index 8bee600692..d98dbd088d 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h +++ b/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h @@ -16,7 +16,6 @@ #include #include "modules/rtp_rtcp/source/rtcp_packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { namespace rtcp { @@ -26,6 +25,9 @@ class CompoundPacket : public RtcpPacket { CompoundPacket(); ~CompoundPacket() override; + CompoundPacket(const CompoundPacket&) = delete; + CompoundPacket& operator=(const CompoundPacket&) = delete; + void Append(std::unique_ptr packet); // Size of this packet in bytes (i.e. total size of nested packets). @@ -38,9 +40,6 @@ class CompoundPacket : public RtcpPacket { protected: std::vector> appended_packets_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(CompoundPacket); }; } // namespace rtcp diff --git a/modules/rtp_rtcp/source/rtcp_packet/dlrr.h b/modules/rtp_rtcp/source/rtcp_packet/dlrr.h index 6fe2099fd9..ad91dfdcc6 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/dlrr.h +++ b/modules/rtp_rtcp/source/rtcp_packet/dlrr.h @@ -24,11 +24,21 @@ struct ReceiveTimeInfo { ReceiveTimeInfo() : ssrc(0), last_rr(0), delay_since_last_rr(0) {} ReceiveTimeInfo(uint32_t ssrc, uint32_t last_rr, uint32_t delay) : ssrc(ssrc), last_rr(last_rr), delay_since_last_rr(delay) {} + uint32_t ssrc; uint32_t last_rr; uint32_t delay_since_last_rr; }; +inline bool operator==(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) { + return lhs.ssrc == rhs.ssrc && lhs.last_rr == rhs.last_rr && + lhs.delay_since_last_rr == rhs.delay_since_last_rr; +} + +inline bool operator!=(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) { + return !(lhs == rhs); +} + // DLRR Report Block: Delay since the Last Receiver Report (RFC 3611). class Dlrr { public: diff --git a/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc index 7c50c01c43..3d9a2a3408 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc @@ -24,19 +24,6 @@ using webrtc::rtcp::ReceiveTimeInfo; using webrtc::rtcp::Rrtr; namespace webrtc { -// Define comparision operators that shouldn't be needed in production, -// but make testing matches more clear. -namespace rtcp { -bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) { - return rrtr1.ntp() == rrtr2.ntp(); -} - -bool operator==(const ReceiveTimeInfo& time1, const ReceiveTimeInfo& time2) { - return time1.ssrc == time2.ssrc && time1.last_rr == time2.last_rr && - time1.delay_since_last_rr == time2.delay_since_last_rr; -} -} // namespace rtcp - namespace { constexpr uint32_t kSenderSsrc = 0x12345678; constexpr uint8_t kEmptyPacket[] = {0x80, 207, 0x00, 0x01, diff --git a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h index b23008c528..0f70cf75c3 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h +++ b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h @@ -42,8 +42,8 @@ class LossNotification : public Psfb { // Set all of the values transmitted by the loss notification message. // If the values may not be represented by a loss notification message, // false is returned, and no change is made to the object; this happens - // when `last_recieved` is ahead of `last_decoded` by more than 0x7fff. - // This is because `last_recieved` is represented on the wire as a delta, + // when `last_received` is ahead of `last_decoded` by more than 0x7fff. + // This is because `last_received` is represented on the wire as a delta, // and only 15 bits are available for that delta. ABSL_MUST_USE_RESULT bool Set(uint16_t last_decoded, diff --git a/modules/rtp_rtcp/source/rtcp_packet/rrtr.h b/modules/rtp_rtcp/source/rtcp_packet/rrtr.h index 8eb4ce62ad..827bd74399 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/rrtr.h +++ b/modules/rtp_rtcp/source/rtcp_packet/rrtr.h @@ -46,6 +46,14 @@ class Rrtr { NtpTime ntp_; }; +inline bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) { + return rrtr1.ntp() == rrtr2.ntp(); +} + +inline bool operator!=(const Rrtr& rrtr1, const Rrtr& rrtr2) { + return !(rrtr1 == rrtr2); +} + } // namespace rtcp } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_ diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc index c309e7ce96..50102c21a3 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc @@ -12,8 +12,10 @@ #include #include +#include #include +#include "absl/algorithm/container.h" #include "modules/include/module_common_types_public.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" @@ -447,19 +449,12 @@ bool TransportFeedback::Parse(const CommonHeader& packet) { num_seq_no_ = status_count; uint16_t seq_no = base_seq_no_; - size_t recv_delta_size = 0; - for (size_t delta_size : delta_sizes) { - recv_delta_size += delta_size; - } + size_t recv_delta_size = absl::c_accumulate(delta_sizes, 0); // Determine if timestamps, that is, recv_delta are included in the packet. if (end_index >= index + recv_delta_size) { for (size_t delta_size : delta_sizes) { - if (index + delta_size > end_index) { - RTC_LOG(LS_WARNING) << "Buffer overflow while parsing packet."; - Clear(); - return false; - } + RTC_DCHECK_LE(index + delta_size, end_index); switch (delta_size) { case 0: if (include_lost_) @@ -489,7 +484,7 @@ bool TransportFeedback::Parse(const CommonHeader& packet) { return false; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } ++seq_no; @@ -549,7 +544,7 @@ bool TransportFeedback::IsConsistent() const { << num_seq_no_; return false; } - int64_t timestamp_us = base_time_ticks_ * kBaseScaleFactor; + int64_t timestamp_us = GetBaseTimeUs(); auto packet_it = received_packets_.begin(); uint16_t seq_no = base_seq_no_; for (DeltaSize delta_size : delta_sizes) { diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc index abaa0786a3..6003df45d0 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc @@ -232,6 +232,15 @@ TEST(RtcpPacketTest, TransportFeedbackTwoBitVectorFull) { test.VerifyPacket(); } +TEST(RtcpPacketTest, TransportFeedbackWithLargeBaseTimeIsConsistent) { + TransportFeedback tb; + constexpr int64_t kTimestampUs = + int64_t{0x7fff'ffff} * TransportFeedback::kDeltaScaleFactor; + tb.SetBase(/*base_sequence=*/0, /*ref_timestamp_us=*/kTimestampUs); + tb.AddReceivedPacket(/*base_sequence=*/0, /*ref_timestamp_us=*/kTimestampUs); + EXPECT_TRUE(tb.IsConsistent()); +} + TEST(RtcpPacketTest, TransportFeedbackLargeAndNegativeDeltas) { const uint16_t kReceived[] = {1, 2, 6, 7, 8}; const int64_t kReceiveTimes[] = { diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc index c57f6d3b17..da705f79aa 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -662,7 +662,12 @@ void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, rtcp_report_block.delay_since_last_sender_report = report_block.delay_since_last_sr(); rtcp_report_block.last_sender_report_timestamp = report_block.last_sr(); - report_block_data->SetReportBlock(rtcp_report_block, rtc::TimeUTCMicros()); + // Number of seconds since 1900 January 1 00:00 GMT (see + // https://tools.ietf.org/html/rfc868). + report_block_data->SetReportBlock( + rtcp_report_block, + (clock_->CurrentNtpInMilliseconds() - rtc::kNtpJan1970Millisecs) * + rtc::kNumMicrosecsPerMillisec); int64_t rtt_ms = 0; uint32_t send_time_ntp = report_block.last_sr(); diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index fa7d569012..eb5d265c6a 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -1573,12 +1573,8 @@ TEST(RtcpReceiverTest, const uint32_t kCumulativeLoss = 7; const uint32_t kJitter = 9; const uint16_t kSequenceNumber = 1234; - const int64_t kUtcNowUs = 42; - - // The "report_block_timestamp_utc_us" is obtained from the global UTC clock - // (not the simulcated `mocks.clock`) and requires a scoped fake clock. - rtc::ScopedFakeClock fake_clock; - fake_clock.SetTime(Timestamp::Micros(kUtcNowUs)); + const int64_t kNtpNowMs = + mocks.clock.CurrentNtpInMilliseconds() - rtc::kNtpJan1970Millisecs; rtcp::ReportBlock rtcp_block; rtcp_block.SetMediaSsrc(kReceiverMainSsrc); @@ -1601,7 +1597,8 @@ TEST(RtcpReceiverTest, EXPECT_EQ(rtcp_block.extended_high_seq_num(), report_block.extended_highest_sequence_number); EXPECT_EQ(rtcp_block.jitter(), report_block.jitter); - EXPECT_EQ(kUtcNowUs, report_block_data.report_block_timestamp_utc_us()); + EXPECT_EQ(kNtpNowMs * rtc::kNumMicrosecsPerMillisec, + report_block_data.report_block_timestamp_utc_us()); // No RTT is calculated in this test. EXPECT_EQ(0u, report_block_data.num_rtts()); }); diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc index 597bb3c795..04f8041bd4 100644 --- a/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/modules/rtp_rtcp/source/rtcp_sender.cc @@ -484,7 +484,9 @@ void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) { rtcp::ReceiverReport report; report.SetSenderSsrc(ssrc_); report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_)); - sender.AppendPacket(report); + if (method_ == RtcpMode::kCompound || !report.report_blocks().empty()) { + sender.AppendPacket(report); + } } void RTCPSender::BuildPLI(const RtcpContext& ctx, PacketSender& sender) { @@ -742,8 +744,8 @@ absl::optional RTCPSender::ComputeCompoundRTCPPacket( } auto builder_it = builders_.find(rtcp_packet_type); if (builder_it == builders_.end()) { - RTC_NOTREACHED() << "Could not find builder for packet type " - << rtcp_packet_type; + RTC_DCHECK_NOTREACHED() + << "Could not find builder for packet type " << rtcp_packet_type; } else { BuilderFunc func = builder_it->second; (this->*func)(context, sender); diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc index d05d8d6dd5..38a9302b67 100644 --- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc @@ -252,13 +252,20 @@ TEST_F(RtcpSenderTest, DoNotSendCompundBeforeRtp) { TEST_F(RtcpSenderTest, SendRr) { auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpRr)); EXPECT_EQ(1, parser()->receiver_report()->num_packets()); EXPECT_EQ(kSenderSsrc, parser()->receiver_report()->sender_ssrc()); EXPECT_EQ(0U, parser()->receiver_report()->report_blocks().size()); } +TEST_F(RtcpSenderTest, DoesntSendEmptyRrInReducedSizeMode) { + auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); + rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); + EXPECT_EQ(parser()->receiver_report()->num_packets(), 0); +} + TEST_F(RtcpSenderTest, SendRrWithOneReportBlock) { const uint16_t kSeqNum = 11111; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); @@ -415,7 +422,7 @@ TEST_F(RtcpSenderTest, SendLossNotificationBufferingAllowed) { TEST_F(RtcpSenderTest, RembNotIncludedBeforeSet) { auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); @@ -427,7 +434,7 @@ TEST_F(RtcpSenderTest, RembNotIncludedAfterUnset) { const int64_t kBitrate = 261011; const std::vector kSsrcs = {kRemoteSsrc, kRemoteSsrc + 1}; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); rtcp_sender->SetRemb(kBitrate, kSsrcs); rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); ASSERT_EQ(1, parser()->receiver_report()->num_packets()); diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc index 214d8fd409..dea6286096 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc @@ -48,13 +48,13 @@ bool RtcpTransceiverConfig::Validate() const { RTC_LOG(LS_ERROR) << debug_id << "outgoing transport must be set"; return false; } - if (initial_report_delay_ms < 0) { - RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay_ms + if (initial_report_delay < TimeDelta::Zero()) { + RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay.ms() << "ms before first report shouldn't be negative."; return false; } - if (report_period_ms <= 0) { - RTC_LOG(LS_ERROR) << debug_id << "period " << report_period_ms + if (report_period <= TimeDelta::Zero()) { + RTC_LOG(LS_ERROR) << debug_id << "period " << report_period.ms() << "ms between reports should be positive."; return false; } @@ -67,7 +67,7 @@ bool RtcpTransceiverConfig::Validate() const { RTC_LOG(LS_ERROR) << debug_id << "unsupported rtcp mode"; return false; } - if (non_sender_rtt_measurement && !rtt_observer) + if (non_sender_rtt_measurement && !network_link_observer) RTC_LOG(LS_WARNING) << debug_id << "Enabled special feature to calculate rtt, but no " "rtt observer is provided."; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index f24a23d75a..8b0a8fad62 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -64,6 +64,44 @@ class MediaReceiverRtcpObserver { const VideoBitrateAllocation& allocation) {} }; +// Handles RTCP related messages for a single RTP stream (i.e. single SSRC) +class RtpStreamRtcpHandler { + public: + virtual ~RtpStreamRtcpHandler() = default; + + // Statistic about sent RTP packets to propagate to RTCP sender report. + class RtpStats { + public: + RtpStats() = default; + RtpStats(const RtpStats&) = default; + RtpStats& operator=(const RtpStats&) = default; + ~RtpStats() = default; + + size_t num_sent_packets() const { return num_sent_packets_; } + size_t num_sent_bytes() const { return num_sent_bytes_; } + Timestamp last_capture_time() const { return last_capture_time_; } + uint32_t last_rtp_timestamp() const { return last_rtp_timestamp_; } + int last_clock_rate() const { return last_clock_rate_; } + + void set_num_sent_packets(size_t v) { num_sent_packets_ = v; } + void set_num_sent_bytes(size_t v) { num_sent_bytes_ = v; } + void set_last_capture_time(Timestamp v) { last_capture_time_ = v; } + void set_last_rtp_timestamp(uint32_t v) { last_rtp_timestamp_ = v; } + void set_last_clock_rate(int v) { last_clock_rate_ = v; } + + private: + size_t num_sent_packets_ = 0; + size_t num_sent_bytes_ = 0; + Timestamp last_capture_time_ = Timestamp::Zero(); + uint32_t last_rtp_timestamp_ = 0; + int last_clock_rate_ = 90'000; + }; + virtual RtpStats SentStats() = 0; + + virtual void OnNack(uint32_t sender_ssrc, + rtc::ArrayView sequence_numbers) {} +}; + struct RtcpTransceiverConfig { RtcpTransceiverConfig(); RtcpTransceiverConfig(const RtcpTransceiverConfig&); @@ -99,12 +137,6 @@ struct RtcpTransceiverConfig { // Rtcp report block generator for outgoing receiver reports. ReceiveStatisticsProvider* receive_statistics = nullptr; - // Callback to pass result of rtt calculation. Should outlive RtcpTransceiver. - // Callbacks will be invoked on the `task_queue`. - // Deprecated, rtt_observer will be deleted in favor of more generic - // `network_link_observer` - RtcpRttStats* rtt_observer = nullptr; - // Should outlive RtcpTransceiver. // Callbacks will be invoked on the `task_queue`. NetworkLinkRtcpObserver* network_link_observer = nullptr; @@ -120,10 +152,10 @@ struct RtcpTransceiverConfig { // Initial state if `outgoing_transport` ready to accept packets. bool initial_ready_to_send = true; // Delay before 1st periodic compound packet. - int initial_report_delay_ms = 500; + TimeDelta initial_report_delay = TimeDelta::Millis(500); // Period between periodic compound packets. - int report_period_ms = 1000; + TimeDelta report_period = TimeDelta::Seconds(1); // // Flags for features and experiments. @@ -133,6 +165,13 @@ struct RtcpTransceiverConfig { // https://tools.ietf.org/html/rfc3611#section-4.4 and #section-4.5 bool non_sender_rtt_measurement = false; + // Reply to incoming RRTR messages so that remote endpoint may estimate RTT as + // non-sender as described in https://tools.ietf.org/html/rfc3611#section-4.4 + // and #section-4.5 + // TODO(danilchap): Make it true by default after users got enough time to + // turn it off if not needed. + bool reply_to_non_sender_rtt_measurement = false; + // Allows a REMB message to be sent immediately when SetRemb is called without // having to wait for the next compount message to be sent. bool send_remb_on_change = false; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index c56515e27e..a43edbde17 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -10,6 +10,7 @@ #include "modules/rtp_rtcp/source/rtcp_transceiver_impl.h" +#include #include #include "absl/algorithm/container.h" @@ -32,6 +33,7 @@ #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/divide_round.h" #include "rtc_base/task_utils/repeating_task.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/time_utils.h" @@ -52,6 +54,12 @@ struct RtcpTransceiverImpl::RemoteSenderState { std::vector observers; }; +struct RtcpTransceiverImpl::LocalSenderState { + uint32_t ssrc; + size_t last_num_sent_bytes = 0; + RtpStreamRtcpHandler* handler = nullptr; +}; + // Helper to put several RTCP packets into lower layer datagram composing // Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2. // TODO(danilchap): When in compound mode and packets are so many that several @@ -92,7 +100,7 @@ RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config) : config_(config), ready_to_send_(config.initial_ready_to_send) { RTC_CHECK(config_.Validate()); if (ready_to_send_ && config_.schedule_periodic_compound_packets) { - SchedulePeriodicCompoundPackets(config_.initial_report_delay_ms); + SchedulePeriodicCompoundPackets(config_.initial_report_delay); } } @@ -119,13 +127,39 @@ void RtcpTransceiverImpl::RemoveMediaReceiverRtcpObserver( stored.erase(it); } +bool RtcpTransceiverImpl::AddMediaSender(uint32_t local_ssrc, + RtpStreamRtcpHandler* handler) { + RTC_DCHECK(handler != nullptr); + LocalSenderState state; + state.ssrc = local_ssrc; + state.handler = handler; + local_senders_.push_back(state); + auto it = std::prev(local_senders_.end()); + auto [unused, inserted] = local_senders_by_ssrc_.emplace(local_ssrc, it); + if (!inserted) { + local_senders_.pop_back(); + return false; + } + return true; +} + +bool RtcpTransceiverImpl::RemoveMediaSender(uint32_t local_ssrc) { + auto index_it = local_senders_by_ssrc_.find(local_ssrc); + if (index_it == local_senders_by_ssrc_.end()) { + return false; + } + local_senders_.erase(index_it->second); + local_senders_by_ssrc_.erase(index_it); + return true; +} + void RtcpTransceiverImpl::SetReadyToSend(bool ready) { if (config_.schedule_periodic_compound_packets) { if (ready_to_send_ && !ready) periodic_task_handle_.Stop(); if (!ready_to_send_ && ready) // Restart periodic sending. - SchedulePeriodicCompoundPackets(config_.report_period_ms / 2); + SchedulePeriodicCompoundPackets(config_.report_period / 2); } ready_to_send_ = ready; } @@ -317,10 +351,34 @@ void RtcpTransceiverImpl::HandlePayloadSpecificFeedback( void RtcpTransceiverImpl::HandleRtpFeedback( const rtcp::CommonHeader& rtcp_packet_header, Timestamp now) { - // Transport feedback is the only message handled right now. - if (rtcp_packet_header.fmt() != - rtcp::TransportFeedback::kFeedbackMessageType || - config_.network_link_observer == nullptr) { + switch (rtcp_packet_header.fmt()) { + case rtcp::Nack::kFeedbackMessageType: + HandleNack(rtcp_packet_header); + break; + case rtcp::TransportFeedback::kFeedbackMessageType: + HandleTransportFeedback(rtcp_packet_header, now); + break; + } +} + +void RtcpTransceiverImpl::HandleNack( + const rtcp::CommonHeader& rtcp_packet_header) { + rtcp::Nack nack; + if (local_senders_.empty() || !nack.Parse(rtcp_packet_header)) { + return; + } + auto it = local_senders_by_ssrc_.find(nack.media_ssrc()); + if (it != local_senders_by_ssrc_.end()) { + it->second->handler->OnNack(nack.sender_ssrc(), nack.packet_ids()); + } +} + +void RtcpTransceiverImpl::HandleTransportFeedback( + const rtcp::CommonHeader& rtcp_packet_header, + Timestamp now) { + RTC_DCHECK_EQ(rtcp_packet_header.fmt(), + rtcp::TransportFeedback::kFeedbackMessageType); + if (config_.network_link_observer == nullptr) { return; } rtcp::TransportFeedback feedback; @@ -336,6 +394,14 @@ void RtcpTransceiverImpl::HandleExtendedReports( if (!extended_reports.Parse(rtcp_packet_header)) return; + if (config_.reply_to_non_sender_rtt_measurement && extended_reports.rrtr()) { + RrtrTimes& rrtr = received_rrtrs_[extended_reports.sender_ssrc()]; + rrtr.received_remote_mid_ntp_time = + CompactNtp(extended_reports.rrtr()->ntp()); + rrtr.local_receive_mid_ntp_time = + CompactNtp(config_.clock->ConvertTimestampToNtpTime(now)); + } + if (extended_reports.dlrr()) HandleDlrr(extended_reports.dlrr(), now); @@ -345,7 +411,8 @@ void RtcpTransceiverImpl::HandleExtendedReports( } void RtcpTransceiverImpl::HandleDlrr(const rtcp::Dlrr& dlrr, Timestamp now) { - if (!config_.non_sender_rtt_measurement) { + if (!config_.non_sender_rtt_measurement || + config_.network_link_observer == nullptr) { return; } @@ -358,13 +425,7 @@ void RtcpTransceiverImpl::HandleDlrr(const rtcp::Dlrr& dlrr, Timestamp now) { continue; uint32_t rtt_ntp = receive_time_ntp - rti.delay_since_last_rr - rti.last_rr; int64_t rtt_ms = CompactNtpRttToMs(rtt_ntp); - if (config_.rtt_observer != nullptr) { - config_.rtt_observer->OnRttUpdate(rtt_ms); - } - if (config_.network_link_observer != nullptr) { - config_.network_link_observer->OnRttUpdate(now, - TimeDelta::Millis(rtt_ms)); - } + config_.network_link_observer->OnRttUpdate(now, TimeDelta::Millis(rtt_ms)); } } @@ -433,54 +494,211 @@ void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets() { return; periodic_task_handle_.Stop(); RTC_DCHECK(ready_to_send_); - SchedulePeriodicCompoundPackets(config_.report_period_ms); + SchedulePeriodicCompoundPackets(config_.report_period); } -void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { - periodic_task_handle_ = RepeatingTaskHandle::DelayedStart( - config_.task_queue, TimeDelta::Millis(delay_ms), [this] { +void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(TimeDelta delay) { + periodic_task_handle_ = + RepeatingTaskHandle::DelayedStart(config_.task_queue, delay, [this] { RTC_DCHECK(config_.schedule_periodic_compound_packets); RTC_DCHECK(ready_to_send_); SendPeriodicCompoundPacket(); - return TimeDelta::Millis(config_.report_period_ms); + return config_.report_period; }); } -void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) { - RTC_DCHECK(sender->IsEmpty()); - const uint32_t sender_ssrc = config_.feedback_ssrc; - Timestamp now = config_.clock->CurrentTime(); - rtcp::ReceiverReport receiver_report; - receiver_report.SetSenderSsrc(sender_ssrc); - receiver_report.SetReportBlocks(CreateReportBlocks(now)); - if (config_.rtcp_mode == RtcpMode::kCompound || - !receiver_report.report_blocks().empty()) { - sender->AppendPacket(receiver_report); +RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( + Timestamp now, + size_t reserved_bytes, + PacketSender& rtcp_sender) { + // Sender/receiver reports should be first in the RTCP packet. + RTC_DCHECK(rtcp_sender.IsEmpty()); + + size_t available_bytes = config_.max_packet_size; + if (reserved_bytes > available_bytes) { + // Because reserved_bytes is unsigned, substracting would underflow and will + // not produce desired result. + available_bytes = 0; + } else { + available_bytes -= reserved_bytes; } - if (!config_.cname.empty() && !sender->IsEmpty()) { - rtcp::Sdes sdes; - bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname); - RTC_DCHECK(added) << "Failed to add cname " << config_.cname - << " to rtcp sdes packet."; - sender->AppendPacket(sdes); + CompoundPacketInfo result; + result.sender_ssrc = config_.feedback_ssrc; + result.has_sender_report = false; + + static constexpr size_t kSenderReportSizeBytes = 28; + static constexpr size_t kFullSenderReportSizeBytes = + kSenderReportSizeBytes + + rtcp::SenderReport::kMaxNumberOfReportBlocks * rtcp::ReportBlock::kLength; + size_t max_full_sender_reports = available_bytes / kFullSenderReportSizeBytes; + size_t max_report_blocks = + max_full_sender_reports * rtcp::SenderReport::kMaxNumberOfReportBlocks; + size_t available_bytes_for_last_sender_report = + available_bytes - max_full_sender_reports * kFullSenderReportSizeBytes; + if (available_bytes_for_last_sender_report >= kSenderReportSizeBytes) { + max_report_blocks += + (available_bytes_for_last_sender_report - kSenderReportSizeBytes) / + rtcp::ReportBlock::kLength; } - if (remb_) { - remb_->SetSenderSsrc(sender_ssrc); - sender->AppendPacket(*remb_); + + std::vector report_blocks = + CreateReportBlocks(now, max_report_blocks); + // Previous calculation of max number of sender report made space for max + // number of report blocks per sender report, but if number of report blocks + // is low, more sender reports may fit in. + size_t max_sender_reports = + (available_bytes - report_blocks.size() * rtcp::ReportBlock::kLength) / + kSenderReportSizeBytes; + + auto last_handled_sender_it = local_senders_.end(); + auto report_block_it = report_blocks.begin(); + size_t num_sender_reports = 0; + for (auto it = local_senders_.begin(); + it != local_senders_.end() && num_sender_reports < max_sender_reports; + ++it) { + LocalSenderState& rtp_sender = *it; + RtpStreamRtcpHandler::RtpStats stats = rtp_sender.handler->SentStats(); + + if (stats.num_sent_bytes() < rtp_sender.last_num_sent_bytes) { + RTC_LOG(LS_ERROR) << "Inconsistent SR for SSRC " << rtp_sender.ssrc + << ". Number of total sent bytes decreased."; + rtp_sender.last_num_sent_bytes = 0; + } + if (stats.num_sent_bytes() == rtp_sender.last_num_sent_bytes) { + // Skip because no RTP packet was send for this SSRC since last report. + continue; + } + rtp_sender.last_num_sent_bytes = stats.num_sent_bytes(); + + last_handled_sender_it = it; + rtcp::SenderReport sender_report; + sender_report.SetSenderSsrc(rtp_sender.ssrc); + sender_report.SetPacketCount(stats.num_sent_packets()); + sender_report.SetOctetCount(stats.num_sent_bytes()); + sender_report.SetNtp(config_.clock->ConvertTimestampToNtpTime(now)); + RTC_DCHECK_GE(now, stats.last_capture_time()); + sender_report.SetRtpTimestamp( + stats.last_rtp_timestamp() + + ((now - stats.last_capture_time()) * stats.last_clock_rate()) + .seconds()); + if (report_block_it != report_blocks.end()) { + size_t num_blocks = + std::min(rtcp::SenderReport::kMaxNumberOfReportBlocks, + report_blocks.end() - report_block_it); + std::vector sub_blocks(report_block_it, + report_block_it + num_blocks); + sender_report.SetReportBlocks(std::move(sub_blocks)); + report_block_it += num_blocks; + } + rtcp_sender.AppendPacket(sender_report); + ++num_sender_reports; + + if (!result.has_sender_report) { + result.has_sender_report = true; + result.sender_ssrc = rtp_sender.ssrc; + } + } + if (last_handled_sender_it != local_senders_.end()) { + // Rotate `local_senders_` so that the 1st unhandled sender become first in + // the list, and thus will be first to generate rtcp sender report for on + // the next call to `FillReports`. + local_senders_.splice(local_senders_.end(), local_senders_, + local_senders_.begin(), + std::next(last_handled_sender_it)); + } + + // Calculcate number of receiver reports to attach remaining report blocks to. + size_t num_receiver_reports = + DivideRoundUp(report_blocks.end() - report_block_it, + rtcp::ReceiverReport::kMaxNumberOfReportBlocks); + + // In compound mode each RTCP packet has to start with a sender or receiver + // report. + if (config_.rtcp_mode == RtcpMode::kCompound && num_sender_reports == 0 && + num_receiver_reports == 0) { + num_receiver_reports = 1; + } + + for (size_t i = 0; i < num_receiver_reports; ++i) { + rtcp::ReceiverReport receiver_report; + receiver_report.SetSenderSsrc(result.sender_ssrc); + size_t num_blocks = + std::min(rtcp::ReceiverReport::kMaxNumberOfReportBlocks, + report_blocks.end() - report_block_it); + std::vector sub_blocks(report_block_it, + report_block_it + num_blocks); + receiver_report.SetReportBlocks(std::move(sub_blocks)); + report_block_it += num_blocks; + rtcp_sender.AppendPacket(receiver_report); + } + // All report blocks should be attached at this point. + RTC_DCHECK_EQ(report_blocks.end() - report_block_it, 0); + return result; +} + +void RtcpTransceiverImpl::CreateCompoundPacket(Timestamp now, + size_t reserved_bytes, + PacketSender& sender) { + RTC_DCHECK(sender.IsEmpty()); + absl::optional sdes; + if (!config_.cname.empty()) { + sdes.emplace(); + bool added = sdes->AddCName(config_.feedback_ssrc, config_.cname); + RTC_DCHECK(added) << "Failed to add CNAME " << config_.cname + << " to RTCP SDES packet."; + reserved_bytes += sdes->BlockLength(); + } + if (remb_.has_value()) { + reserved_bytes += remb_->BlockLength(); + } + absl::optional xr; + if (!received_rrtrs_.empty()) { + RTC_DCHECK(config_.reply_to_non_sender_rtt_measurement); + xr.emplace(); + uint32_t now_ntp = + CompactNtp(config_.clock->ConvertTimestampToNtpTime(now)); + for (const auto& [ssrc, rrtr_info] : received_rrtrs_) { + rtcp::ReceiveTimeInfo reply; + reply.ssrc = ssrc; + reply.last_rr = rrtr_info.received_remote_mid_ntp_time; + reply.delay_since_last_rr = + now_ntp - rrtr_info.local_receive_mid_ntp_time; + xr->AddDlrrItem(reply); + } + reserved_bytes += xr->BlockLength(); } - // TODO(bugs.webrtc.org/8239): Do not send rrtr if this packet starts with - // SenderReport instead of ReceiverReport - // when RtcpTransceiver supports rtp senders. if (config_.non_sender_rtt_measurement) { - rtcp::ExtendedReports xr; + // It looks like bytes for ExtendedReport header are reserved twice, but in + // practice the same RtcpTransceiver won't both produce RRTR (i.e. it is a + // receiver-only) and reply to RRTR (i.e. remote participant is a receiver + // only). If that happen, then `reserved_bytes` would be slightly larger + // than it should, which is not an issue. + // 4 bytes for common RTCP header + 4 bytes for the ExtenedReports header. + reserved_bytes += (4 + 4 + rtcp::Rrtr::kLength); + } + + CompoundPacketInfo result = FillReports(now, reserved_bytes, sender); + + if (sdes.has_value() && !sender.IsEmpty()) { + sender.AppendPacket(*sdes); + } + if (remb_.has_value()) { + remb_->SetSenderSsrc(result.sender_ssrc); + sender.AppendPacket(*remb_); + } + if (!result.has_sender_report && config_.non_sender_rtt_measurement) { + if (!xr.has_value()) { + xr.emplace(); + } rtcp::Rrtr rrtr; rrtr.SetNtp(config_.clock->ConvertTimestampToNtpTime(now)); - xr.SetRrtr(rrtr); - - xr.SetSenderSsrc(sender_ssrc); - sender->AppendPacket(xr); + xr->SetRrtr(rrtr); + } + if (xr.has_value()) { + xr->SetSenderSsrc(result.sender_ssrc); + sender.AppendPacket(*xr); } } @@ -488,8 +706,9 @@ void RtcpTransceiverImpl::SendPeriodicCompoundPacket() { auto send_packet = [this](rtc::ArrayView packet) { config_.outgoing_transport->SendRtcp(packet.data(), packet.size()); }; + Timestamp now = config_.clock->CurrentTime(); PacketSender sender(send_packet, config_.max_packet_size); - CreateCompoundPacket(&sender); + CreateCompoundPacket(now, /*reserved_bytes=*/0, sender); sender.Send(); } @@ -515,8 +734,11 @@ void RtcpTransceiverImpl::SendImmediateFeedback( PacketSender sender(send_packet, config_.max_packet_size); // Compound mode requires every sent rtcp packet to be compound, i.e. start // with a sender or receiver report. - if (config_.rtcp_mode == RtcpMode::kCompound) - CreateCompoundPacket(&sender); + if (config_.rtcp_mode == RtcpMode::kCompound) { + Timestamp now = config_.clock->CurrentTime(); + CreateCompoundPacket(now, /*reserved_bytes=*/rtcp_packet.BlockLength(), + sender); + } sender.AppendPacket(rtcp_packet); sender.Send(); @@ -527,14 +749,12 @@ void RtcpTransceiverImpl::SendImmediateFeedback( } std::vector RtcpTransceiverImpl::CreateReportBlocks( - Timestamp now) { + Timestamp now, + size_t num_max_blocks) { if (!config_.receive_statistics) return {}; - // TODO(danilchap): Support sending more than - // `ReceiverReport::kMaxNumberOfReportBlocks` per compound rtcp packet. std::vector report_blocks = - config_.receive_statistics->RtcpReportBlocks( - rtcp::ReceiverReport::kMaxNumberOfReportBlocks); + config_.receive_statistics->RtcpReportBlocks(num_max_blocks); uint32_t last_sr = 0; uint32_t last_delay = 0; for (rtcp::ReportBlock& report_block : report_blocks) { diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index 91dc496faf..9e0da6a42d 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -11,7 +11,7 @@ #ifndef MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_IMPL_H_ #define MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_IMPL_H_ -#include +#include #include #include #include @@ -25,6 +25,7 @@ #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" #include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h" #include "modules/rtp_rtcp/source/rtcp_transceiver_config.h" +#include "rtc_base/containers/flat_map.h" #include "rtc_base/task_utils/repeating_task.h" #include "system_wrappers/include/ntp_time.h" @@ -47,6 +48,11 @@ class RtcpTransceiverImpl { void RemoveMediaReceiverRtcpObserver(uint32_t remote_ssrc, MediaReceiverRtcpObserver* observer); + // Returns false on failure, e.g. when there is already an handler for the + // `local_ssrc`. + bool AddMediaSender(uint32_t local_ssrc, RtpStreamRtcpHandler* handler); + bool RemoveMediaSender(uint32_t local_ssrc); + void SetReadyToSend(bool ready); void ReceivePacket(rtc::ArrayView packet, Timestamp now); @@ -75,6 +81,14 @@ class RtcpTransceiverImpl { private: class PacketSender; struct RemoteSenderState; + struct LocalSenderState; + struct RrtrTimes { + // Received remote NTP timestamp in compact representation. + uint32_t received_remote_mid_ntp_time; + + // Local NTP time when the report was received in compact representation. + uint32_t local_receive_mid_ntp_time; + }; void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now, @@ -91,6 +105,9 @@ class RtcpTransceiverImpl { Timestamp now); void HandleRtpFeedback(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); + void HandleNack(const rtcp::CommonHeader& rtcp_packet_header); + void HandleTransportFeedback(const rtcp::CommonHeader& rtcp_packet_header, + Timestamp now); void HandleExtendedReports(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); // Extended Reports blocks handlers. @@ -102,15 +119,30 @@ class RtcpTransceiverImpl { rtc::ArrayView report_blocks); void ReschedulePeriodicCompoundPackets(); - void SchedulePeriodicCompoundPackets(int64_t delay_ms); + void SchedulePeriodicCompoundPackets(TimeDelta delay); + // Appends RTCP sender and receiver reports to the `sender`. + // Both sender and receiver reports may have attached report blocks. + // Uses up to `config_.max_packet_size - reserved_bytes` + struct CompoundPacketInfo { + uint32_t sender_ssrc; + bool has_sender_report; + }; + CompoundPacketInfo FillReports(Timestamp now, + size_t reserved_bytes, + PacketSender& rtcp_sender); + // Creates compound RTCP packet, as defined in // https://tools.ietf.org/html/rfc5506#section-2 - void CreateCompoundPacket(PacketSender* sender); + void CreateCompoundPacket(Timestamp now, + size_t reserved_bytes, + PacketSender& rtcp_sender); + // Sends RTCP packets. void SendPeriodicCompoundPacket(); void SendImmediateFeedback(const rtcp::RtcpPacket& rtcp_packet); - // Generate Report Blocks to be send in Sender or Receiver Report. - std::vector CreateReportBlocks(Timestamp now); + // Generate Report Blocks to be send in Sender or Receiver Reports. + std::vector CreateReportBlocks(Timestamp now, + size_t num_max_blocks); const RtcpTransceiverConfig config_; @@ -118,7 +150,11 @@ class RtcpTransceiverImpl { absl::optional remb_; // TODO(danilchap): Remove entries from remote_senders_ that are no longer // needed. - std::map remote_senders_; + flat_map remote_senders_; + std::list local_senders_; + flat_map::iterator> + local_senders_by_ssrc_; + flat_map received_rrtrs_; RepeatingTaskHandle periodic_task_handle_; }; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index a76f36dd2f..ea6b49525a 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -40,10 +40,13 @@ namespace { using ::testing::_; using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Ge; using ::testing::NiceMock; using ::testing::Return; using ::testing::SizeIs; using ::testing::StrictMock; +using ::testing::UnorderedElementsAre; using ::testing::WithArg; using ::webrtc::rtcp::Bye; using ::webrtc::rtcp::CompoundPacket; @@ -66,6 +69,29 @@ class MockMediaReceiverRtcpObserver : public MediaReceiverRtcpObserver { (override)); }; +class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler { + public: + MockRtpStreamRtcpHandler() { + // With each next call increase number of sent packets and bytes to simulate + // active RTP sender. + ON_CALL(*this, SentStats).WillByDefault([this] { + RtpStats stats; + stats.set_num_sent_packets(++num_calls_); + stats.set_num_sent_bytes(1'000 * num_calls_); + return stats; + }); + } + + MOCK_METHOD(RtpStats, SentStats, (), (override)); + MOCK_METHOD(void, + OnNack, + (uint32_t, rtc::ArrayView), + (override)); + + private: + int num_calls_ = 0; +}; + class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { public: MOCK_METHOD(void, @@ -90,7 +116,7 @@ class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { // Since some tests will need to wait for this period, make it small to avoid // slowing tests too much. As long as there are test bots with high scheduler // granularity, small period should be ok. -constexpr int kReportPeriodMs = 10; +constexpr TimeDelta kReportPeriod = TimeDelta::Millis(10); // On some systems task queue might be slow, instead of guessing right // grace period, use very large timeout, 100x larger expected wait time. // Use finite timeout to fail tests rather than hang them. @@ -155,8 +181,8 @@ RtcpTransceiverConfig DefaultTestConfig() { config.clock = &null_clock; config.outgoing_transport = &null_transport; config.schedule_periodic_compound_packets = false; - config.initial_report_delay_ms = 10; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Millis(10); + config.report_period = kReportPeriod; return config; } @@ -225,7 +251,7 @@ TEST(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 10; + config.initial_report_delay = TimeDelta::Millis(10); config.task_queue = queue.Get(); absl::optional rtcp_transceiver; @@ -233,7 +259,7 @@ TEST(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) { queue.PostTask([&] { rtcp_transceiver.emplace(config); }); EXPECT_TRUE(transport.WaitPacket()); - EXPECT_GE(rtc::TimeMillis() - started_ms, config.initial_report_delay_ms); + EXPECT_GE(rtc::TimeMillis() - started_ms, config.initial_report_delay.ms()); // Cleanup. rtc::Event done; @@ -252,8 +278,8 @@ TEST(RtcpTransceiverImplTest, PeriodicallySendsPackets) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 0; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; config.task_queue = queue.Get(); absl::optional rtcp_transceiver; int64_t time_just_before_1st_packet_ms = 0; @@ -269,7 +295,7 @@ TEST(RtcpTransceiverImplTest, PeriodicallySendsPackets) { int64_t time_just_after_2nd_packet_ms = rtc::TimeMillis(); EXPECT_GE(time_just_after_2nd_packet_ms - time_just_before_1st_packet_ms, - config.report_period_ms - 1); + config.report_period.ms() - 1); // Cleanup. rtc::Event done; @@ -288,8 +314,8 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 0; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; config.task_queue = queue.Get(); absl::optional rtcp_transceiver; queue.PostTask([&] { rtcp_transceiver.emplace(config); }); @@ -305,7 +331,7 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { rtcp_transceiver->SendCompoundPacket(); non_periodic.Set(); }, - config.report_period_ms / 2); + (config.report_period / 2).ms()); // Though non-periodic packet is scheduled just in between periodic, due to // small period and task queue flakiness it migth end-up 1ms after next // periodic packet. To be sure duration after non-periodic packet is tested @@ -317,7 +343,7 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { int64_t time_of_last_periodic_packet_ms = rtc::TimeMillis(); EXPECT_GE(time_of_last_periodic_packet_ms - time_of_non_periodic_packet_ms, - config.report_period_ms - 1); + config.report_period.ms() - 1); // Cleanup. rtc::Event done; @@ -946,6 +972,61 @@ TEST(RtcpTransceiverImplTest, EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100); } +TEST(RtcpTransceiverImplTest, MaySendMultipleReceiverReportInSinglePacket) { + std::vector statistics_report_blocks(40); + MockReceiveStatisticsProvider receive_statistics; + EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u))) + .WillOnce(Return(statistics_report_blocks)); + + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.receive_statistics = &receive_statistics; + RtcpTransceiverImpl rtcp_transceiver(config); + + // Trigger ReceiverReports. + rtcp_transceiver.SendCompoundPacket(); + + // Expect a single RTCP packet with multiple receiver reports in it. + EXPECT_EQ(transport.num_packets(), 1); + // Receiver report may contain up to 31 report blocks, thus 2 reports are + // needed to carry 40 blocks: 31 in the first, 9 in the last. + EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 2); + // RtcpParser remembers just the last receiver report, thus can't check number + // of blocks in the first receiver report. + EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9)); +} + +TEST(RtcpTransceiverImplTest, AttachMaxNumberOfReportBlocksToCompoundPacket) { + MockReceiveStatisticsProvider receive_statistics; + EXPECT_CALL(receive_statistics, RtcpReportBlocks) + .WillOnce([](size_t max_blocks) { + return std::vector(max_blocks); + }); + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.rtcp_mode = RtcpMode::kCompound; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.receive_statistics = &receive_statistics; + RtcpTransceiverImpl rtcp_transceiver(config); + + EXPECT_EQ(transport.num_packets(), 0); + // Send some fast feedback message. Because of compound mode, report blocks + // should be attached. + rtcp_transceiver.SendPictureLossIndication(/*ssrc=*/123); + + // Expect single RTCP packet with multiple receiver reports and a PLI. + EXPECT_EQ(transport.num_packets(), 1); + EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 1); + EXPECT_EQ(rtcp_parser.pli()->num_packets(), 1); +} + TEST(RtcpTransceiverImplTest, SendsNack) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrc = 4321; @@ -968,6 +1049,30 @@ TEST(RtcpTransceiverImplTest, SendsNack) { EXPECT_EQ(rtcp_parser.nack()->packet_ids(), kMissingSequenceNumbers); } +TEST(RtcpTransceiverImplTest, ReceivesNack) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + std::vector kMissingSequenceNumbers = {34, 37, 38}; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, + OnNack(kRemoteSsrc, ElementsAreArray(kMissingSequenceNumbers))); + EXPECT_CALL(local_stream2, OnNack).Times(0); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Nack nack; + nack.SetSenderSsrc(kRemoteSsrc); + nack.SetMediaSsrc(kMediaSsrc1); + nack.SetPacketIds(kMissingSequenceNumbers); + rtcp_transceiver.ReceivePacket(nack.Build(), config.clock->CurrentTime()); +} + TEST(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrc = 4321; @@ -1133,6 +1238,43 @@ TEST(RtcpTransceiverImplTest, SendsXrRrtrWhenEnabled) { EXPECT_EQ(rtcp_parser.xr()->rrtr()->ntp(), ntp_time_now); } +TEST(RtcpTransceiverImplTest, RepliesToRrtrWhenEnabled) { + static constexpr uint32_t kSenderSsrc[] = {4321, 9876}; + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.reply_to_non_sender_rtt_measurement = true; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp::ExtendedReports xr; + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kSenderSsrc[0]); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(1'500)); + + rrtr.SetNtp(NtpTime(uint64_t{0x4444'5555'6666'7777})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kSenderSsrc[1]); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(500)); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); + static constexpr uint32_t kComactNtpOneSecond = 0x0001'0000; + EXPECT_THAT(rtcp_parser.xr()->dlrr().sub_blocks(), + UnorderedElementsAre( + rtcp::ReceiveTimeInfo(kSenderSsrc[0], 0x2222'3333, + /*delay=*/2 * kComactNtpOneSecond), + rtcp::ReceiveTimeInfo(kSenderSsrc[1], 0x5555'6666, + /*delay=*/kComactNtpOneSecond / 2))); +} + TEST(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) { SimulatedClock clock(0); RtcpTransceiverConfig config; @@ -1152,33 +1294,6 @@ TEST(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) { EXPECT_FALSE(rtcp_parser.xr()->rrtr()); } -TEST(RtcpTransceiverImplTest, CalculatesRoundTripTimeOnDlrr) { - const uint32_t kSenderSsrc = 4321; - SimulatedClock clock(0); - MockRtcpRttStats rtt_observer; - MockTransport null_transport; - RtcpTransceiverConfig config; - config.clock = &clock; - config.feedback_ssrc = kSenderSsrc; - config.schedule_periodic_compound_packets = false; - config.outgoing_transport = &null_transport; - config.non_sender_rtt_measurement = true; - config.rtt_observer = &rtt_observer; - RtcpTransceiverImpl rtcp_transceiver(config); - - Timestamp time = Timestamp::Micros(12345678); - webrtc::rtcp::ReceiveTimeInfo rti; - rti.ssrc = kSenderSsrc; - rti.last_rr = CompactNtp(clock.ConvertTimestampToNtpTime(time)); - rti.delay_since_last_rr = SaturatedUsToCompactNtp(10 * 1000); - webrtc::rtcp::ExtendedReports xr; - xr.AddDlrrItem(rti); - auto raw_packet = xr.Build(); - - EXPECT_CALL(rtt_observer, OnRttUpdate(100 /* rtt_ms */)); - rtcp_transceiver.ReceivePacket(raw_packet, time + TimeDelta::Millis(110)); -} - TEST(RtcpTransceiverImplTest, PassRttFromDlrrToLinkObserver) { const uint32_t kSenderSsrc = 4321; MockNetworkLinkRtcpObserver link_observer; @@ -1230,7 +1345,7 @@ TEST(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) { const uint32_t kSenderSsrc = 4321; const uint32_t kUnknownSsrc = 4322; SimulatedClock clock(0); - MockRtcpRttStats rtt_observer; + MockNetworkLinkRtcpObserver link_observer; MockTransport null_transport; RtcpTransceiverConfig config; config.clock = &clock; @@ -1238,7 +1353,7 @@ TEST(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) { config.schedule_periodic_compound_packets = false; config.outgoing_transport = &null_transport; config.non_sender_rtt_measurement = true; - config.rtt_observer = &rtt_observer; + config.network_link_observer = &link_observer; RtcpTransceiverImpl rtcp_transceiver(config); Timestamp time = Timestamp::Micros(12345678); @@ -1249,7 +1364,7 @@ TEST(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) { xr.AddDlrrItem(rti); auto raw_packet = xr.Build(); - EXPECT_CALL(rtt_observer, OnRttUpdate(_)).Times(0); + EXPECT_CALL(link_observer, OnRttUpdate).Times(0); rtcp_transceiver.ReceivePacket(raw_packet, time + TimeDelta::Millis(100)); } @@ -1290,7 +1405,7 @@ TEST(RtcpTransceiverImplTest, ParsesRemb) { } TEST(RtcpTransceiverImplTest, - CombinesReportBlocksFromSenderAndRecieverReports) { + CombinesReportBlocksFromSenderAndReceiverReports) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; @@ -1315,5 +1430,179 @@ TEST(RtcpTransceiverImplTest, rtcp_transceiver.ReceivePacket(packet.Build(), receive_time); } +TEST(RtcpTransceiverImplTest, FailsToRegisterTwoSendersWithTheSameSsrc) { + RtcpTransceiverImpl rtcp_transceiver(DefaultTestConfig()); + MockRtpStreamRtcpHandler sender1; + MockRtpStreamRtcpHandler sender2; + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender1)); + EXPECT_FALSE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender2)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10002, &sender2)); + + EXPECT_TRUE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001)); + EXPECT_FALSE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001)); +} + +TEST(RtcpTransceiverImplTest, SendsSenderReport) { + static constexpr uint32_t kFeedbackSsrc = 123; + static constexpr uint32_t kSenderSsrc = 12345; + SimulatedClock clock(100'000'000); + RtcpTransceiverConfig config; + config.clock = &clock; + config.feedback_ssrc = kFeedbackSsrc; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + + RtpStreamRtcpHandler::RtpStats sender_stats; + sender_stats.set_num_sent_packets(10); + sender_stats.set_num_sent_bytes(1000); + sender_stats.set_last_rtp_timestamp(0x3333); + sender_stats.set_last_capture_time(clock.CurrentTime() - + TimeDelta::Seconds(2)); + sender_stats.set_last_clock_rate(0x1000); + MockRtpStreamRtcpHandler sender; + ON_CALL(sender, SentStats).WillByDefault(Return(sender_stats)); + rtcp_transceiver.AddMediaSender(kSenderSsrc, &sender); + + rtcp_transceiver.SendCompoundPacket(); + + ASSERT_GT(rtcp_parser.sender_report()->num_packets(), 0); + EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc); + EXPECT_EQ(rtcp_parser.sender_report()->ntp(), clock.CurrentNtpTime()); + EXPECT_EQ(rtcp_parser.sender_report()->rtp_timestamp(), 0x3333u + 0x2000u); + EXPECT_EQ(rtcp_parser.sender_report()->sender_packet_count(), 10u); + EXPECT_EQ(rtcp_parser.sender_report()->sender_octet_count(), 1000u); +} + +TEST(RtcpTransceiverImplTest, + MaySendBothSenderReportAndReceiverReportInTheSamePacket) { + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + std::vector statistics_report_blocks(40); + MockReceiveStatisticsProvider receive_statistics; + EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u))) + .WillOnce(Return(statistics_report_blocks)); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.outgoing_transport = &transport; + config.receive_statistics = &receive_statistics; + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler sender; + rtcp_transceiver.AddMediaSender(/*ssrc=*/12345, &sender); + + rtcp_transceiver.SendCompoundPacket(); + + // Expect a single RTCP packet with a sender and a receiver reports in it. + EXPECT_EQ(transport.num_packets(), 1); + ASSERT_EQ(rtcp_parser.sender_report()->num_packets(), 1); + ASSERT_EQ(rtcp_parser.receiver_report()->num_packets(), 1); + // Sender report may contain up to 31 report blocks, thus remaining 9 report + // block should be attached to the receiver report. + EXPECT_THAT(rtcp_parser.sender_report()->report_blocks(), SizeIs(31)); + EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9)); +} + +TEST(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) { + // Send 6 compound packet, each should contain 5 sender reports, + // each of 6 senders should be mentioned 5 times. + static constexpr int kNumSenders = 6; + static constexpr uint32_t kSenderSsrc[kNumSenders] = {10, 20, 30, 40, 50, 60}; + static constexpr int kSendersPerPacket = 5; + SimulatedClock clock(100'000'000); + // RtcpPacketParser remembers only latest block for each type, but this test + // is about sending multiple sender reports in the same packet, thus need + // a more advance parser: RtcpTranceiver + RtcpTransceiverConfig receiver_config = DefaultTestConfig(); + receiver_config.clock = &clock; + RtcpTransceiverImpl rtcp_receiver(receiver_config); + // Main expectatation: all senders are spread equally across multiple packets. + NiceMock receiver[kNumSenders]; + for (int i = 0; i < kNumSenders; ++i) { + SCOPED_TRACE(i); + EXPECT_CALL(receiver[i], OnSenderReport(kSenderSsrc[i], _, _)) + .Times(kSendersPerPacket); + rtcp_receiver.AddMediaReceiverRtcpObserver(kSenderSsrc[i], &receiver[i]); + } + + MockTransport transport; + EXPECT_CALL(transport, SendRtcp) + .Times(kNumSenders) + .WillRepeatedly([&](const uint8_t* data, size_t size) { + rtcp_receiver.ReceivePacket(rtc::MakeArrayView(data, size), + clock.CurrentTime()); + return true; + }); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.outgoing_transport = &transport; + // Limit packet to have space just for kSendersPerPacket sender reports. + // Sender report without report blocks require 28 bytes. + config.max_packet_size = kSendersPerPacket * 28; + RtcpTransceiverImpl rtcp_transceiver(config); + NiceMock sender[kNumSenders]; + for (int i = 0; i < kNumSenders; ++i) { + rtcp_transceiver.AddMediaSender(kSenderSsrc[i], &sender[i]); + } + + for (int i = 1; i <= kNumSenders; ++i) { + SCOPED_TRACE(i); + rtcp_transceiver.SendCompoundPacket(); + } +} + +TEST(RtcpTransceiverImplTest, SkipsSenderReportForInactiveSender) { + static constexpr uint32_t kSenderSsrc[] = {12345, 23456}; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + RtpStreamRtcpHandler::RtpStats sender_stats[2]; + NiceMock sender[2]; + ON_CALL(sender[0], SentStats).WillByDefault([&] { return sender_stats[0]; }); + ON_CALL(sender[1], SentStats).WillByDefault([&] { return sender_stats[1]; }); + rtcp_transceiver.AddMediaSender(kSenderSsrc[0], &sender[0]); + rtcp_transceiver.AddMediaSender(kSenderSsrc[1], &sender[1]); + + // Start with both senders beeing active. + sender_stats[0].set_num_sent_packets(10); + sender_stats[0].set_num_sent_bytes(1'000); + sender_stats[1].set_num_sent_packets(5); + sender_stats[1].set_num_sent_bytes(2'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 1); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 2); + + // Keep 1st sender active, but make 2nd second look inactive by returning the + // same RtpStats. + sender_stats[0].set_num_sent_packets(15); + sender_stats[0].set_num_sent_bytes(2'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 2); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 3); + EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[0]); + + // Swap active sender. + sender_stats[1].set_num_sent_packets(20); + sender_stats[1].set_num_sent_bytes(3'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 3); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 4); + EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[1]); + + // Activate both senders again. + sender_stats[0].set_num_sent_packets(20); + sender_stats[0].set_num_sent_bytes(3'000); + sender_stats[1].set_num_sent_packets(25); + sender_stats[1].set_num_sent_bytes(3'500); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 4); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6); +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc index 3b09818576..fd42b798d4 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc @@ -23,7 +23,6 @@ namespace webrtc { constexpr RTPExtensionType RtpDependencyDescriptorExtension::kId; -constexpr char RtpDependencyDescriptorExtension::kUri[]; constexpr std::bitset<32> RtpDependencyDescriptorExtension::kAllChainsAreActive; bool RtpDependencyDescriptorExtension::Parse( diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h index de16eeab2a..a2aedb8d4f 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h @@ -13,7 +13,9 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/rtp_parameters.h" #include "api/transport/rtp/dependency_descriptor.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -25,10 +27,9 @@ namespace webrtc { class RtpDependencyDescriptorExtension { public: static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor02; - // TODO(bugs.webrtc.org/10342): Use uri from the spec when there is one. - static constexpr char kUri[] = - "https://aomediacodec.github.io/av1-rtp-spec/" - "#dependency-descriptor-rtp-header-extension"; + static constexpr absl::string_view Uri() { + return RtpExtension::kDependencyDescriptorUri; + } static bool Parse(rtc::ArrayView data, const FrameDependencyStructure* structure, diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h index f658594243..283beacb19 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264.h +++ b/modules/rtp_rtcp/source/rtp_format_h264.h @@ -23,7 +23,6 @@ #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -37,6 +36,9 @@ class RtpPacketizerH264 : public RtpPacketizer { ~RtpPacketizerH264() override; + RtpPacketizerH264(const RtpPacketizerH264&) = delete; + RtpPacketizerH264& operator=(const RtpPacketizerH264&) = delete; + size_t NumPackets() const override; // Get the next payload with H264 payload header. @@ -82,8 +84,6 @@ class RtpPacketizerH264 : public RtpPacketizer { size_t num_packets_left_; std::deque> input_fragments_; std::queue packets_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerH264); }; } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_ diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic.h b/modules/rtp_rtcp/source/rtp_format_video_generic.h index 5acd691163..fd44bd1980 100644 --- a/modules/rtp_rtcp/source/rtp_format_video_generic.h +++ b/modules/rtp_rtcp/source/rtp_format_video_generic.h @@ -16,7 +16,6 @@ #include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -48,6 +47,9 @@ class RtpPacketizerGeneric : public RtpPacketizer { ~RtpPacketizerGeneric() override; + RtpPacketizerGeneric(const RtpPacketizerGeneric&) = delete; + RtpPacketizerGeneric& operator=(const RtpPacketizerGeneric&) = delete; + size_t NumPackets() const override; // Get the next payload. @@ -64,8 +66,6 @@ class RtpPacketizerGeneric : public RtpPacketizer { rtc::ArrayView remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerGeneric); }; } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_ diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h index 21009280e4..d1f569a946 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8.h +++ b/modules/rtp_rtcp/source/rtp_format_vp8.h @@ -35,7 +35,6 @@ #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -50,6 +49,9 @@ class RtpPacketizerVp8 : public RtpPacketizer { ~RtpPacketizerVp8() override; + RtpPacketizerVp8(const RtpPacketizerVp8&) = delete; + RtpPacketizerVp8& operator=(const RtpPacketizerVp8&) = delete; + size_t NumPackets() const override; // Get the next payload with VP8 payload header. @@ -66,8 +68,6 @@ class RtpPacketizerVp8 : public RtpPacketizer { rtc::ArrayView remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp8); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h index 916d6577f1..3ecaa476da 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h +++ b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h @@ -21,7 +21,6 @@ #include "modules/rtp_rtcp/source/rtp_format_vp8.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,10 @@ class RtpFormatVp8TestHelper { public: RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr, size_t payload_len); ~RtpFormatVp8TestHelper(); + + RtpFormatVp8TestHelper(const RtpFormatVp8TestHelper&) = delete; + RtpFormatVp8TestHelper& operator=(const RtpFormatVp8TestHelper&) = delete; + void GetAllPacketsAndCheck(RtpPacketizerVp8* packetizer, rtc::ArrayView expected_sizes); @@ -46,8 +49,6 @@ class RtpFormatVp8TestHelper { const RTPVideoHeaderVP8* const hdr_info_; rtc::Buffer payload_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpFormatVp8TestHelper); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_format_vp9.h b/modules/rtp_rtcp/source/rtp_format_vp9.h index 02458aea6a..3cf4dd56e5 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp9.h +++ b/modules/rtp_rtcp/source/rtp_format_vp9.h @@ -30,7 +30,6 @@ #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -43,6 +42,9 @@ class RtpPacketizerVp9 : public RtpPacketizer { ~RtpPacketizerVp9() override; + RtpPacketizerVp9(const RtpPacketizerVp9&) = delete; + RtpPacketizerVp9& operator=(const RtpPacketizerVp9&) = delete; + size_t NumPackets() const override; // Gets the next payload with VP9 payload header. @@ -64,8 +66,6 @@ class RtpPacketizerVp9 : public RtpPacketizer { rtc::ArrayView remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp9); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc index 49ec4a10a0..8a0810f445 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc @@ -57,7 +57,6 @@ constexpr uint8_t kFlageXtendedOffset = 0x02; // | ... | // +-+-+-+-+-+-+-+-+ constexpr RTPExtensionType RtpGenericFrameDescriptorExtension00::kId; -constexpr char RtpGenericFrameDescriptorExtension00::kUri[]; bool RtpGenericFrameDescriptorExtension00::Parse( rtc::ArrayView data, diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h index ac7afb489b..93ca690b22 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h @@ -13,7 +13,9 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" @@ -23,9 +25,9 @@ class RtpGenericFrameDescriptorExtension00 { public: using value_type = RtpGenericFrameDescriptor; static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor00; - static constexpr char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/" - "generic-frame-descriptor-00"; + static constexpr absl::string_view Uri() { + return RtpExtension::kGenericFrameDescriptorUri00; + } static constexpr int kMaxSizeBytes = 16; static bool Parse(rtc::ArrayView data, diff --git a/modules/rtp_rtcp/source/rtp_header_extension_map.cc b/modules/rtp_rtcp/source/rtp_header_extension_map.cc index ddd579344f..4b8c7b5385 100644 --- a/modules/rtp_rtcp/source/rtp_header_extension_map.cc +++ b/modules/rtp_rtcp/source/rtp_header_extension_map.cc @@ -10,6 +10,7 @@ #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "absl/strings/string_view.h" #include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" @@ -23,12 +24,12 @@ namespace { struct ExtensionInfo { RTPExtensionType type; - const char* uri; + absl::string_view uri; }; template constexpr ExtensionInfo CreateExtensionInfo() { - return {Extension::kId, Extension::kUri}; + return {Extension::kId, Extension::Uri()}; } constexpr ExtensionInfo kExtensions[] = { @@ -92,7 +93,7 @@ bool RtpHeaderExtensionMap::RegisterByType(int id, RTPExtensionType type) { for (const ExtensionInfo& extension : kExtensions) if (type == extension.type) return Register(id, extension.type, extension.uri); - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } @@ -117,13 +118,6 @@ RTPExtensionType RtpHeaderExtensionMap::GetType(int id) const { return kInvalidType; } -int32_t RtpHeaderExtensionMap::Deregister(RTPExtensionType type) { - if (IsRegistered(type)) { - ids_[type] = kInvalidId; - } - return 0; -} - void RtpHeaderExtensionMap::Deregister(absl::string_view uri) { for (const ExtensionInfo& extension : kExtensions) { if (extension.uri == uri) { @@ -135,7 +129,7 @@ void RtpHeaderExtensionMap::Deregister(absl::string_view uri) { bool RtpHeaderExtensionMap::Register(int id, RTPExtensionType type, - const char* uri) { + absl::string_view uri) { RTC_DCHECK_GT(type, kRtpExtensionNone); RTC_DCHECK_LT(type, kRtpExtensionNumberOfExtensions); @@ -160,7 +154,12 @@ bool RtpHeaderExtensionMap::Register(int id, << static_cast(registered_type); return false; } - RTC_DCHECK(!IsRegistered(type)); + if (IsRegistered(type)) { + RTC_LOG(LS_WARNING) << "Illegal reregistration for uri: " << uri + << " is previously registered with id " << GetId(type) + << " and cannot be reregistered with id " << id; + return false; + } // There is a run-time check above id fits into uint8_t. ids_[type] = static_cast(id); diff --git a/modules/rtp_rtcp/source/rtp_header_extension_map_unittest.cc b/modules/rtp_rtcp/source/rtp_header_extension_map_unittest.cc index f5bb1bb324..42842cc876 100644 --- a/modules/rtp_rtcp/source/rtp_header_extension_map_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_header_extension_map_unittest.cc @@ -31,7 +31,7 @@ TEST(RtpHeaderExtensionTest, RegisterByType) { TEST(RtpHeaderExtensionTest, RegisterByUri) { RtpHeaderExtensionMap map; - EXPECT_TRUE(map.RegisterByUri(3, TransmissionOffset::kUri)); + EXPECT_TRUE(map.RegisterByUri(3, TransmissionOffset::Uri())); EXPECT_TRUE(map.IsRegistered(TransmissionOffset::kId)); EXPECT_EQ(3, map.GetId(TransmissionOffset::kId)); @@ -49,8 +49,8 @@ TEST(RtpHeaderExtensionTest, RegisterWithTrait) { } TEST(RtpHeaderExtensionTest, RegisterDuringContruction) { - const std::vector config = {{TransmissionOffset::kUri, 1}, - {AbsoluteSendTime::kUri, 3}}; + const std::vector config = {{TransmissionOffset::Uri(), 1}, + {AbsoluteSendTime::Uri(), 3}}; const RtpHeaderExtensionMap map(config); EXPECT_EQ(1, map.GetId(TransmissionOffset::kId)); @@ -77,8 +77,8 @@ TEST(RtpHeaderExtensionTest, Idempotent) { EXPECT_TRUE(map.Register(3)); EXPECT_TRUE(map.Register(3)); - map.Deregister(TransmissionOffset::kId); - map.Deregister(TransmissionOffset::kId); + map.Deregister(TransmissionOffset::Uri()); + map.Deregister(TransmissionOffset::Uri()); } TEST(RtpHeaderExtensionTest, NonUniqueId) { @@ -106,4 +106,10 @@ TEST(RtpHeaderExtensionTest, GetId) { EXPECT_EQ(3, map.GetId(TransmissionOffset::kId)); } +TEST(RtpHeaderExtensionTest, RemapFails) { + RtpHeaderExtensionMap map; + EXPECT_TRUE(map.Register(3)); + EXPECT_FALSE(map.Register(4)); +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.cc b/modules/rtp_rtcp/source/rtp_header_extensions.cc index 12359a4091..32edd63ef7 100644 --- a/modules/rtp_rtcp/source/rtp_header_extensions.cc +++ b/modules/rtp_rtcp/source/rtp_header_extensions.cc @@ -41,7 +41,6 @@ namespace webrtc { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType AbsoluteSendTime::kId; constexpr uint8_t AbsoluteSendTime::kValueSizeBytes; -constexpr const char AbsoluteSendTime::kUri[]; bool AbsoluteSendTime::Parse(rtc::ArrayView data, uint32_t* time_24bits) { @@ -98,7 +97,6 @@ constexpr RTPExtensionType AbsoluteCaptureTimeExtension::kId; constexpr uint8_t AbsoluteCaptureTimeExtension::kValueSizeBytes; constexpr uint8_t AbsoluteCaptureTimeExtension:: kValueSizeBytesWithoutEstimatedCaptureClockOffset; -constexpr const char AbsoluteCaptureTimeExtension::kUri[]; bool AbsoluteCaptureTimeExtension::Parse(rtc::ArrayView data, AbsoluteCaptureTime* extension) { @@ -164,7 +162,6 @@ bool AbsoluteCaptureTimeExtension::Write(rtc::ArrayView data, constexpr RTPExtensionType AudioLevel::kId; constexpr uint8_t AudioLevel::kValueSizeBytes; -constexpr const char AudioLevel::kUri[]; bool AudioLevel::Parse(rtc::ArrayView data, bool* voice_activity, @@ -210,7 +207,6 @@ bool AudioLevel::Write(rtc::ArrayView data, // Sample Audio Level Encoding Using the Two-Byte Header Format constexpr RTPExtensionType CsrcAudioLevel::kId; constexpr uint8_t CsrcAudioLevel::kMaxValueSizeBytes; -constexpr const char CsrcAudioLevel::kUri[]; bool CsrcAudioLevel::Parse(rtc::ArrayView data, std::vector* csrc_audio_levels) { @@ -259,7 +255,6 @@ bool CsrcAudioLevel::Write(rtc::ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType TransmissionOffset::kId; constexpr uint8_t TransmissionOffset::kValueSizeBytes; -constexpr const char TransmissionOffset::kUri[]; bool TransmissionOffset::Parse(rtc::ArrayView data, int32_t* rtp_time) { @@ -285,7 +280,6 @@ bool TransmissionOffset::Write(rtc::ArrayView data, int32_t rtp_time) { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType TransportSequenceNumber::kId; constexpr uint8_t TransportSequenceNumber::kValueSizeBytes; -constexpr const char TransportSequenceNumber::kUri[]; bool TransportSequenceNumber::Parse(rtc::ArrayView data, uint16_t* transport_sequence_number) { @@ -323,7 +317,6 @@ constexpr RTPExtensionType TransportSequenceNumberV2::kId; constexpr uint8_t TransportSequenceNumberV2::kValueSizeBytes; constexpr uint8_t TransportSequenceNumberV2::kValueSizeBytesWithoutFeedbackRequest; -constexpr const char TransportSequenceNumberV2::kUri[]; constexpr uint16_t TransportSequenceNumberV2::kIncludeTimestampsBit; bool TransportSequenceNumberV2::Parse( @@ -385,7 +378,6 @@ bool TransportSequenceNumberV2::Write( // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType VideoOrientation::kId; constexpr uint8_t VideoOrientation::kValueSizeBytes; -constexpr const char VideoOrientation::kUri[]; bool VideoOrientation::Parse(rtc::ArrayView data, VideoRotation* rotation) { @@ -423,7 +415,6 @@ bool VideoOrientation::Write(rtc::ArrayView data, uint8_t value) { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType PlayoutDelayLimits::kId; constexpr uint8_t PlayoutDelayLimits::kValueSizeBytes; -constexpr const char PlayoutDelayLimits::kUri[]; bool PlayoutDelayLimits::Parse(rtc::ArrayView data, VideoPlayoutDelay* playout_delay) { @@ -465,7 +456,6 @@ bool PlayoutDelayLimits::Write(rtc::ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constexpr RTPExtensionType VideoContentTypeExtension::kId; constexpr uint8_t VideoContentTypeExtension::kValueSizeBytes; -constexpr const char VideoContentTypeExtension::kUri[]; bool VideoContentTypeExtension::Parse(rtc::ArrayView data, VideoContentType* content_type) { @@ -508,7 +498,6 @@ bool VideoContentTypeExtension::Write(rtc::ArrayView data, constexpr RTPExtensionType VideoTimingExtension::kId; constexpr uint8_t VideoTimingExtension::kValueSizeBytes; -constexpr const char VideoTimingExtension::kUri[]; constexpr uint8_t VideoTimingExtension::kFlagsOffset; constexpr uint8_t VideoTimingExtension::kEncodeStartDeltaOffset; constexpr uint8_t VideoTimingExtension::kEncodeFinishDeltaOffset; @@ -618,7 +607,6 @@ bool VideoTimingExtension::Write(rtc::ArrayView data, constexpr RTPExtensionType ColorSpaceExtension::kId; constexpr uint8_t ColorSpaceExtension::kValueSizeBytes; -constexpr const char ColorSpaceExtension::kUri[]; bool ColorSpaceExtension::Parse(rtc::ArrayView data, ColorSpace* color_space) { @@ -822,16 +810,10 @@ bool BaseRtpStringExtension::Write(rtc::ArrayView data, return true; } -// Constant declarations for string RTP header extension types. - +// Constant declarations for RTP header extension types. constexpr RTPExtensionType RtpStreamId::kId; -constexpr const char RtpStreamId::kUri[]; - constexpr RTPExtensionType RepairedRtpStreamId::kId; -constexpr const char RepairedRtpStreamId::kUri[]; - constexpr RTPExtensionType RtpMid::kId; -constexpr const char RtpMid::kUri[]; // An RTP Header Extension for Inband Comfort Noise // @@ -888,7 +870,6 @@ bool InbandComfortNoiseExtension::Write(rtc::ArrayView data, constexpr RTPExtensionType VideoFrameTrackingIdExtension::kId; constexpr uint8_t VideoFrameTrackingIdExtension::kValueSizeBytes; -constexpr const char VideoFrameTrackingIdExtension::kUri[]; bool VideoFrameTrackingIdExtension::Parse(rtc::ArrayView data, uint16_t* video_frame_tracking_id) { diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.h b/modules/rtp_rtcp/source/rtp_header_extensions.h index b47824afdb..ffbdafda4f 100644 --- a/modules/rtp_rtcp/source/rtp_header_extensions.h +++ b/modules/rtp_rtcp/source/rtp_header_extensions.h @@ -16,8 +16,10 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/rtp_headers.h" +#include "api/rtp_parameters.h" #include "api/video/color_space.h" #include "api/video/video_content_type.h" #include "api/video/video_rotation.h" @@ -31,8 +33,9 @@ class AbsoluteSendTime { using value_type = uint32_t; static constexpr RTPExtensionType kId = kRtpExtensionAbsoluteSendTime; static constexpr uint8_t kValueSizeBytes = 3; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; + static constexpr absl::string_view Uri() { + return RtpExtension::kAbsSendTimeUri; + } static bool Parse(rtc::ArrayView data, uint32_t* time_24bits); static size_t ValueSize(uint32_t time_24bits) { return kValueSizeBytes; } @@ -50,8 +53,9 @@ class AbsoluteCaptureTimeExtension { static constexpr uint8_t kValueSizeBytes = 16; static constexpr uint8_t kValueSizeBytesWithoutEstimatedCaptureClockOffset = 8; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"; + static constexpr absl::string_view Uri() { + return RtpExtension::kAbsoluteCaptureTimeUri; + } static bool Parse(rtc::ArrayView data, AbsoluteCaptureTime* extension); @@ -64,8 +68,9 @@ class AudioLevel { public: static constexpr RTPExtensionType kId = kRtpExtensionAudioLevel; static constexpr uint8_t kValueSizeBytes = 1; - static constexpr const char kUri[] = - "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; + static constexpr absl::string_view Uri() { + return RtpExtension::kAudioLevelUri; + } static bool Parse(rtc::ArrayView data, bool* voice_activity, @@ -82,8 +87,9 @@ class CsrcAudioLevel { public: static constexpr RTPExtensionType kId = kRtpExtensionCsrcAudioLevel; static constexpr uint8_t kMaxValueSizeBytes = 15; - static constexpr const char kUri[] = - "urn:ietf:params:rtp-hdrext:csrc-audio-level"; + static constexpr absl::string_view Uri() { + return RtpExtension::kCsrcAudioLevelsUri; + } static bool Parse(rtc::ArrayView data, std::vector* csrc_audio_levels); @@ -97,7 +103,9 @@ class TransmissionOffset { using value_type = int32_t; static constexpr RTPExtensionType kId = kRtpExtensionTransmissionTimeOffset; static constexpr uint8_t kValueSizeBytes = 3; - static constexpr const char kUri[] = "urn:ietf:params:rtp-hdrext:toffset"; + static constexpr absl::string_view Uri() { + return RtpExtension::kTimestampOffsetUri; + } static bool Parse(rtc::ArrayView data, int32_t* rtp_time); static size_t ValueSize(int32_t rtp_time) { return kValueSizeBytes; } @@ -109,9 +117,10 @@ class TransportSequenceNumber { using value_type = uint16_t; static constexpr RTPExtensionType kId = kRtpExtensionTransportSequenceNumber; static constexpr uint8_t kValueSizeBytes = 2; - static constexpr const char kUri[] = - "http://www.ietf.org/id/" - "draft-holmer-rmcat-transport-wide-cc-extensions-01"; + static constexpr absl::string_view Uri() { + return RtpExtension::kTransportSequenceNumberUri; + } + static bool Parse(rtc::ArrayView data, uint16_t* transport_sequence_number); static size_t ValueSize(uint16_t /*transport_sequence_number*/) { @@ -127,8 +136,10 @@ class TransportSequenceNumberV2 { kRtpExtensionTransportSequenceNumber02; static constexpr uint8_t kValueSizeBytes = 4; static constexpr uint8_t kValueSizeBytesWithoutFeedbackRequest = 2; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02"; + static constexpr absl::string_view Uri() { + return RtpExtension::kTransportSequenceNumberV2Uri; + } + static bool Parse(rtc::ArrayView data, uint16_t* transport_sequence_number, absl::optional* feedback_request); @@ -151,7 +162,9 @@ class VideoOrientation { using value_type = VideoRotation; static constexpr RTPExtensionType kId = kRtpExtensionVideoRotation; static constexpr uint8_t kValueSizeBytes = 1; - static constexpr const char kUri[] = "urn:3gpp:video-orientation"; + static constexpr absl::string_view Uri() { + return RtpExtension::kVideoRotationUri; + } static bool Parse(rtc::ArrayView data, VideoRotation* value); static size_t ValueSize(VideoRotation) { return kValueSizeBytes; } @@ -166,8 +179,9 @@ class PlayoutDelayLimits { using value_type = VideoPlayoutDelay; static constexpr RTPExtensionType kId = kRtpExtensionPlayoutDelay; static constexpr uint8_t kValueSizeBytes = 3; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"; + static constexpr absl::string_view Uri() { + return RtpExtension::kPlayoutDelayUri; + } // Playout delay in milliseconds. A playout delay limit (min or max) // has 12 bits allocated. This allows a range of 0-4095 values which @@ -188,8 +202,9 @@ class VideoContentTypeExtension { using value_type = VideoContentType; static constexpr RTPExtensionType kId = kRtpExtensionVideoContentType; static constexpr uint8_t kValueSizeBytes = 1; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"; + static constexpr absl::string_view Uri() { + return RtpExtension::kVideoContentTypeUri; + } static bool Parse(rtc::ArrayView data, VideoContentType* content_type); @@ -203,8 +218,9 @@ class VideoTimingExtension { using value_type = VideoSendTiming; static constexpr RTPExtensionType kId = kRtpExtensionVideoTiming; static constexpr uint8_t kValueSizeBytes = 13; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/video-timing"; + static constexpr absl::string_view Uri() { + return RtpExtension::kVideoTimingUri; + } // Offsets of the fields in the RTP header extension, counting from the first // byte after the one-byte header. @@ -237,8 +253,9 @@ class ColorSpaceExtension { static constexpr RTPExtensionType kId = kRtpExtensionColorSpace; static constexpr uint8_t kValueSizeBytes = 28; static constexpr uint8_t kValueSizeBytesWithoutHdrMetadata = 4; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/color-space"; + static constexpr absl::string_view Uri() { + return RtpExtension::kColorSpaceUri; + } static bool Parse(rtc::ArrayView data, ColorSpace* color_space); @@ -287,21 +304,21 @@ class BaseRtpStringExtension { class RtpStreamId : public BaseRtpStringExtension { public: static constexpr RTPExtensionType kId = kRtpExtensionRtpStreamId; - static constexpr const char kUri[] = - "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"; + static constexpr absl::string_view Uri() { return RtpExtension::kRidUri; } }; class RepairedRtpStreamId : public BaseRtpStringExtension { public: static constexpr RTPExtensionType kId = kRtpExtensionRepairedRtpStreamId; - static constexpr const char kUri[] = - "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"; + static constexpr absl::string_view Uri() { + return RtpExtension::kRepairedRidUri; + } }; class RtpMid : public BaseRtpStringExtension { public: static constexpr RTPExtensionType kId = kRtpExtensionMid; - static constexpr const char kUri[] = "urn:ietf:params:rtp-hdrext:sdes:mid"; + static constexpr absl::string_view Uri() { return RtpExtension::kMidUri; } }; class InbandComfortNoiseExtension { @@ -312,6 +329,7 @@ class InbandComfortNoiseExtension { static constexpr uint8_t kValueSizeBytes = 1; static constexpr const char kUri[] = "http://www.webrtc.org/experiments/rtp-hdrext/inband-cn"; + static constexpr absl::string_view Uri() { return kUri; } static bool Parse(rtc::ArrayView data, absl::optional* level); @@ -327,8 +345,10 @@ class VideoFrameTrackingIdExtension { using value_type = uint16_t; static constexpr RTPExtensionType kId = kRtpExtensionVideoFrameTrackingId; static constexpr uint8_t kValueSizeBytes = 2; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/video-frame-tracking-id"; + static constexpr absl::string_view Uri() { + return RtpExtension::kVideoFrameTrackingIdUri; + } + static bool Parse(rtc::ArrayView data, uint16_t* video_frame_tracking_id); static size_t ValueSize(uint16_t /*video_frame_tracking_id*/) { diff --git a/modules/rtp_rtcp/source/rtp_packet_history.cc b/modules/rtp_rtcp/source/rtp_packet_history.cc index fe5ccc708e..c8d400a985 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history.cc @@ -23,26 +23,13 @@ namespace webrtc { -constexpr size_t RtpPacketHistory::kMaxCapacity; -constexpr size_t RtpPacketHistory::kMaxPaddingHistory; -constexpr int64_t RtpPacketHistory::kMinPacketDurationMs; -constexpr int RtpPacketHistory::kMinPacketDurationRtt; -constexpr int RtpPacketHistory::kPacketCullingDelayFactor; - -RtpPacketHistory::PacketState::PacketState() = default; -RtpPacketHistory::PacketState::PacketState(const PacketState&) = default; -RtpPacketHistory::PacketState::~PacketState() = default; - RtpPacketHistory::StoredPacket::StoredPacket( std::unique_ptr packet, - absl::optional send_time_ms, + Timestamp send_time, uint64_t insert_order) - : send_time_ms_(send_time_ms), - packet_(std::move(packet)), - // No send time indicates packet is not sent immediately, but instead will - // be put in the pacer queue and later retrieved via - // GetPacketAndSetSendTime(). - pending_transmission_(!send_time_ms.has_value()), + : packet_(std::move(packet)), + pending_transmission_(false), + send_time_(send_time), insert_order_(insert_order), times_retransmitted_(0) {} @@ -85,7 +72,7 @@ RtpPacketHistory::RtpPacketHistory(Clock* clock, bool enable_padding_prio) enable_padding_prio_(enable_padding_prio), number_to_store_(0), mode_(StorageMode::kDisabled), - rtt_ms_(-1), + rtt_(TimeDelta::MinusInfinity()), packets_inserted_(0) {} RtpPacketHistory::~RtpPacketHistory() {} @@ -107,29 +94,28 @@ RtpPacketHistory::StorageMode RtpPacketHistory::GetStorageMode() const { return mode_; } -void RtpPacketHistory::SetRtt(int64_t rtt_ms) { +void RtpPacketHistory::SetRtt(TimeDelta rtt) { MutexLock lock(&lock_); - RTC_DCHECK_GE(rtt_ms, 0); - rtt_ms_ = rtt_ms; + RTC_DCHECK_GE(rtt, TimeDelta::Zero()); + rtt_ = rtt; // If storage is not disabled, packets will be removed after a timeout // that depends on the RTT. Changing the RTT may thus cause some packets // become "old" and subject to removal. if (mode_ != StorageMode::kDisabled) { - CullOldPackets(clock_->TimeInMilliseconds()); + CullOldPackets(); } } void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, - absl::optional send_time_ms) { + Timestamp send_time) { RTC_DCHECK(packet); MutexLock lock(&lock_); - int64_t now_ms = clock_->TimeInMilliseconds(); if (mode_ == StorageMode::kDisabled) { return; } RTC_DCHECK(packet->allow_retransmission()); - CullOldPackets(now_ms); + CullOldPackets(); // Store packet. const uint16_t rtp_seq_no = packet->SequenceNumber(); @@ -145,11 +131,11 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, // Packet to be inserted ahead of first packet, expand front. for (; packet_index < 0; ++packet_index) { - packet_history_.emplace_front(nullptr, absl::nullopt, 0); + packet_history_.emplace_front(); } // Packet to be inserted behind last packet, expand back. while (static_cast(packet_history_.size()) <= packet_index) { - packet_history_.emplace_back(nullptr, absl::nullopt, 0); + packet_history_.emplace_back(); } RTC_DCHECK_GE(packet_index, 0); @@ -157,7 +143,7 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, RTC_DCHECK(packet_history_[packet_index].packet_ == nullptr); packet_history_[packet_index] = - StoredPacket(std::move(packet), send_time_ms, packets_inserted_++); + StoredPacket(std::move(packet), send_time, packets_inserted_++); if (enable_padding_prio_) { if (padding_priority_.size() >= kMaxPaddingHistory - 1) { @@ -168,36 +154,6 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, } } -std::unique_ptr RtpPacketHistory::GetPacketAndSetSendTime( - uint16_t sequence_number) { - MutexLock lock(&lock_); - if (mode_ == StorageMode::kDisabled) { - return nullptr; - } - - StoredPacket* packet = GetStoredPacket(sequence_number); - if (packet == nullptr) { - return nullptr; - } - - int64_t now_ms = clock_->TimeInMilliseconds(); - if (!VerifyRtt(*packet, now_ms)) { - return nullptr; - } - - if (packet->send_time_ms_) { - packet->IncrementTimesRetransmitted( - enable_padding_prio_ ? &padding_priority_ : nullptr); - } - - // Update send-time and mark as no long in pacer queue. - packet->send_time_ms_ = now_ms; - packet->pending_transmission_ = false; - - // Return copy of packet instance since it may need to be retransmitted. - return std::make_unique(*packet->packet_); -} - std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( uint16_t sequence_number) { return GetPacketAndMarkAsPending( @@ -225,7 +181,7 @@ std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( return nullptr; } - if (!VerifyRtt(*packet, clock_->TimeInMilliseconds())) { + if (!VerifyRtt(*packet)) { // Packet already resent within too short a time window, ignore. return nullptr; } @@ -251,51 +207,45 @@ void RtpPacketHistory::MarkPacketAsSent(uint16_t sequence_number) { return; } - RTC_DCHECK(packet->send_time_ms_); - // Update send-time, mark as no longer in pacer queue, and increment // transmission count. - packet->send_time_ms_ = clock_->TimeInMilliseconds(); + packet->set_send_time(clock_->CurrentTime()); packet->pending_transmission_ = false; packet->IncrementTimesRetransmitted(enable_padding_prio_ ? &padding_priority_ : nullptr); } -absl::optional RtpPacketHistory::GetPacketState( - uint16_t sequence_number) const { +bool RtpPacketHistory::GetPacketState(uint16_t sequence_number) const { MutexLock lock(&lock_); if (mode_ == StorageMode::kDisabled) { - return absl::nullopt; + return false; } int packet_index = GetPacketIndex(sequence_number); if (packet_index < 0 || static_cast(packet_index) >= packet_history_.size()) { - return absl::nullopt; + return false; } const StoredPacket& packet = packet_history_[packet_index]; if (packet.packet_ == nullptr) { - return absl::nullopt; + return false; } - if (!VerifyRtt(packet, clock_->TimeInMilliseconds())) { - return absl::nullopt; + if (!VerifyRtt(packet)) { + return false; } - return StoredPacketToPacketState(packet); + return true; } -bool RtpPacketHistory::VerifyRtt(const RtpPacketHistory::StoredPacket& packet, - int64_t now_ms) const { - if (packet.send_time_ms_) { - // Send-time already set, this check must be for a retransmission. - if (packet.times_retransmitted() > 0 && - now_ms < *packet.send_time_ms_ + rtt_ms_) { - // This packet has already been retransmitted once, and the time since - // that even is lower than on RTT. Ignore request as this packet is - // likely already in the network pipe. - return false; - } +bool RtpPacketHistory::VerifyRtt( + const RtpPacketHistory::StoredPacket& packet) const { + if (packet.times_retransmitted() > 0 && + clock_->CurrentTime() - packet.send_time() < rtt_) { + // This packet has already been retransmitted once, and the time since + // that even is lower than on RTT. Ignore request as this packet is + // likely already in the network pipe. + return false; } return true; @@ -348,7 +298,7 @@ std::unique_ptr RtpPacketHistory::GetPayloadPaddingPacket( return nullptr; } - best_packet->send_time_ms_ = clock_->TimeInMilliseconds(); + best_packet->set_send_time(clock_->CurrentTime()); best_packet->IncrementTimesRetransmitted( enable_padding_prio_ ? &padding_priority_ : nullptr); @@ -368,21 +318,6 @@ void RtpPacketHistory::CullAcknowledgedPackets( } } -bool RtpPacketHistory::SetPendingTransmission(uint16_t sequence_number) { - MutexLock lock(&lock_); - if (mode_ == StorageMode::kDisabled) { - return false; - } - - StoredPacket* packet = GetStoredPacket(sequence_number); - if (packet == nullptr) { - return false; - } - - packet->pending_transmission_ = true; - return true; -} - void RtpPacketHistory::Clear() { MutexLock lock(&lock_); Reset(); @@ -393,9 +328,12 @@ void RtpPacketHistory::Reset() { padding_priority_.clear(); } -void RtpPacketHistory::CullOldPackets(int64_t now_ms) { - int64_t packet_duration_ms = - std::max(kMinPacketDurationRtt * rtt_ms_, kMinPacketDurationMs); +void RtpPacketHistory::CullOldPackets() { + Timestamp now = clock_->CurrentTime(); + TimeDelta packet_duration = + rtt_.IsFinite() + ? std::max(kMinPacketDurationRtt * rtt_, kMinPacketDuration) + : kMinPacketDuration; while (!packet_history_.empty()) { if (packet_history_.size() >= kMaxCapacity) { // We have reached the absolute max capacity, remove one packet @@ -410,15 +348,15 @@ void RtpPacketHistory::CullOldPackets(int64_t now_ms) { return; } - if (*stored_packet.send_time_ms_ + packet_duration_ms > now_ms) { + if (stored_packet.send_time() + packet_duration > now) { // Don't cull packets too early to avoid failed retransmission requests. return; } if (packet_history_.size() >= number_to_store_ || - *stored_packet.send_time_ms_ + - (packet_duration_ms * kPacketCullingDelayFactor) <= - now_ms) { + stored_packet.send_time() + + (packet_duration * kPacketCullingDelayFactor) <= + now) { // Too many packets in history, or this packet has timed out. Remove it // and continue. RemovePacket(0); @@ -487,17 +425,4 @@ RtpPacketHistory::StoredPacket* RtpPacketHistory::GetStoredPacket( return &packet_history_[index]; } -RtpPacketHistory::PacketState RtpPacketHistory::StoredPacketToPacketState( - const RtpPacketHistory::StoredPacket& stored_packet) { - RtpPacketHistory::PacketState state; - state.rtp_sequence_number = stored_packet.packet_->SequenceNumber(); - state.send_time_ms = stored_packet.send_time_ms_; - state.capture_time_ms = stored_packet.packet_->capture_time_ms(); - state.ssrc = stored_packet.packet_->Ssrc(); - state.packet_size = stored_packet.packet_->size(); - state.times_retransmitted = stored_packet.times_retransmitted(); - state.pending_transmission = stored_packet.pending_transmission_; - return state; -} - } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_history.h b/modules/rtp_rtcp/source/rtp_packet_history.h index f87ad4d550..390fd98e08 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.h +++ b/modules/rtp_rtcp/source/rtp_packet_history.h @@ -15,9 +15,13 @@ #include #include #include +#include #include +#include "absl/base/attributes.h" #include "api/function_view.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -35,28 +39,12 @@ class RtpPacketHistory { // packets as they time out or as signaled as received. }; - // Snapshot indicating the state of a packet in the history. - struct PacketState { - PacketState(); - PacketState(const PacketState&); - ~PacketState(); - - uint16_t rtp_sequence_number = 0; - absl::optional send_time_ms; - int64_t capture_time_ms = 0; - uint32_t ssrc = 0; - size_t packet_size = 0; - // Number of times RE-transmitted, ie not including the first transmission. - size_t times_retransmitted = 0; - bool pending_transmission = false; - }; - // Maximum number of packets we ever allow in the history. static constexpr size_t kMaxCapacity = 9600; // Maximum number of entries in prioritized queue of padding packets. static constexpr size_t kMaxPaddingHistory = 63; - // Don't remove packets within max(1000ms, 3x RTT). - static constexpr int64_t kMinPacketDurationMs = 1000; + // Don't remove packets within max(1 second, 3x RTT). + static constexpr TimeDelta kMinPacketDuration = TimeDelta::Seconds(1); static constexpr int kMinPacketDurationRtt = 3; // With kStoreAndCull, always remove packets after 3x max(1000ms, 3x rtt). static constexpr int kPacketCullingDelayFactor = 3; @@ -76,17 +64,19 @@ class RtpPacketHistory { // Set RTT, used to avoid premature retransmission and to prevent over-writing // a packet in the history before we are reasonably sure it has been received. - void SetRtt(int64_t rtt_ms); + ABSL_DEPRECATED("Use SetRtt below that takes TimeDelta") + void SetRtt(int64_t rtt_ms) { SetRtt(TimeDelta::Millis(rtt_ms)); } - // If `send_time` is set, packet was sent without using pacer, so state will - // be set accordingly. + void SetRtt(TimeDelta rtt); + + ABSL_DEPRECATED("Use PutRtpPacket below that take Timestamp") void PutRtpPacket(std::unique_ptr packet, - absl::optional send_time_ms); + int64_t send_time_ms) { + PutRtpPacket(std::move(packet), Timestamp::Millis(send_time_ms)); + } - // Gets stored RTP packet corresponding to the input |sequence number|. - // Returns nullptr if packet is not found or was (re)sent too recently. - std::unique_ptr GetPacketAndSetSendTime( - uint16_t sequence_number); + void PutRtpPacket(std::unique_ptr packet, + Timestamp send_time); // Gets stored RTP packet corresponding to the input |sequence number|. // Returns nullptr if packet is not found or was (re)sent too recently. @@ -109,9 +99,9 @@ class RtpPacketHistory { // counter. Marks the packet as no longer being in the pacer queue. void MarkPacketAsSent(uint16_t sequence_number); - // Similar to GetPacketAndSetSendTime(), but only returns a snapshot of the - // current state for packet, and never updates internal state. - absl::optional GetPacketState(uint16_t sequence_number) const; + // Returns true if history contains packet with `sequence_number` and it can + // be retransmitted. + bool GetPacketState(uint16_t sequence_number) const; // Get the packet (if any) from the history, that is deemed most likely to // the remote side. This is calculated from heuristics such as packet age @@ -130,11 +120,6 @@ class RtpPacketHistory { // Cull packets that have been acknowledged as received by the remote end. void CullAcknowledgedPackets(rtc::ArrayView sequence_numbers); - // Mark packet as queued for transmission. This will prevent premature - // removal or duplicate retransmissions in the pacer queue. - // Returns true if status was set, false if packet was not found. - bool SetPendingTransmission(uint16_t sequence_number); - // Remove all pending packets from the history, but keep storage mode and // capacity. void Clear(); @@ -146,8 +131,9 @@ class RtpPacketHistory { class StoredPacket { public: + StoredPacket() = default; StoredPacket(std::unique_ptr packet, - absl::optional send_time_ms, + Timestamp send_time, uint64_t insert_order); StoredPacket(StoredPacket&&); StoredPacket& operator=(StoredPacket&&); @@ -158,7 +144,8 @@ class RtpPacketHistory { void IncrementTimesRetransmitted(PacketPrioritySet* priority_set); // The time of last transmission, including retransmissions. - absl::optional send_time_ms_; + Timestamp send_time() const { return send_time_; } + void set_send_time(Timestamp value) { send_time_ = value; } // The actual packet. std::unique_ptr packet_; @@ -167,6 +154,8 @@ class RtpPacketHistory { bool pending_transmission_; private: + Timestamp send_time_ = Timestamp::Zero(); + // Unique number per StoredPacket, incremented by one for each added // packet. Used to sort on insert order. uint64_t insert_order_; @@ -178,12 +167,11 @@ class RtpPacketHistory { bool operator()(StoredPacket* lhs, StoredPacket* rhs) const; }; - // Helper method used by GetPacketAndSetSendTime() and GetPacketState() to - // check if packet has too recently been sent. - bool VerifyRtt(const StoredPacket& packet, int64_t now_ms) const + // Helper method to check if packet has too recently been sent. + bool VerifyRtt(const StoredPacket& packet) const RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); void Reset() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); - void CullOldPackets(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); + void CullOldPackets() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); // Removes the packet from the history, and context/mapping that has been // stored. Returns the RTP packet instance contained within the StoredPacket. std::unique_ptr RemovePacket(int packet_index) @@ -192,15 +180,13 @@ class RtpPacketHistory { RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); StoredPacket* GetStoredPacket(uint16_t sequence_number) RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); - static PacketState StoredPacketToPacketState( - const StoredPacket& stored_packet); Clock* const clock_; const bool enable_padding_prio_; mutable Mutex lock_; size_t number_to_store_ RTC_GUARDED_BY(lock_); StorageMode mode_ RTC_GUARDED_BY(lock_); - int64_t rtt_ms_ RTC_GUARDED_BY(lock_); + TimeDelta rtt_ RTC_GUARDED_BY(lock_); // Queue of stored packets, ordered by sequence number, with older packets in // the front and new packets being added to the back. Note that there may be diff --git a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc index 90e984a78c..f50541849e 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc @@ -45,7 +45,7 @@ class RtpPacketHistoryTest : public ::testing::TestWithParam { // Payload, ssrc, timestamp and extensions are irrelevant for this tests. std::unique_ptr packet(new RtpPacketToSend(nullptr)); packet->SetSequenceNumber(seq_num); - packet->set_capture_time_ms(fake_clock_.TimeInMilliseconds()); + packet->set_capture_time(fake_clock_.CurrentTime()); packet->set_allow_retransmission(true); return packet; } @@ -63,8 +63,8 @@ TEST_P(RtpPacketHistoryTest, SetStoreStatus) { TEST_P(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - // Store a packet, but with send-time. It should then not be removed. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), absl::nullopt); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Changing store status, even to the current one, will clear the history. @@ -74,25 +74,27 @@ TEST_P(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) { TEST_P(RtpPacketHistoryTest, StartSeqResetAfterReset) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - // Store a packet, but with send-time. It should then not be removed. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), absl::nullopt); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), + /*send_time=*/fake_clock_.CurrentTime()); + // Mark packet as pending so it won't be removed. + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Changing store status, to clear the history. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); // Add a new packet. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), absl::nullopt); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), + /*send_time=*/fake_clock_.CurrentTime()); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + 1))); // Advance time past where packet expires. - fake_clock_.AdvanceTimeMilliseconds( - RtpPacketHistory::kPacketCullingDelayFactor * - RtpPacketHistory::kMinPacketDurationMs); + fake_clock_.AdvanceTime(RtpPacketHistory::kPacketCullingDelayFactor * + RtpPacketHistory::kMinPacketDuration); // Add one more packet and verify no state left from packet before reset. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), absl::nullopt); + hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); @@ -101,7 +103,8 @@ TEST_P(RtpPacketHistoryTest, StartSeqResetAfterReset) { TEST_P(RtpPacketHistoryTest, NoStoreStatus) { EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); // Packet should not be stored. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } @@ -116,139 +119,70 @@ TEST_P(RtpPacketHistoryTest, PutRtpPacket) { std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, GetRtpPacket) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - int64_t capture_time_ms = 1; + Timestamp capture_time = Timestamp::Millis(1); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(capture_time); rtc::CopyOnWriteBuffer buffer = packet->Buffer(); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); std::unique_ptr packet_out = - hist_.GetPacketAndSetSendTime(kStartSeqNum); - EXPECT_TRUE(packet_out); + hist_.GetPacketAndMarkAsPending(kStartSeqNum); + ASSERT_TRUE(packet_out); EXPECT_EQ(buffer, packet_out->Buffer()); - EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms()); + EXPECT_EQ(capture_time, packet_out->capture_time()); } -TEST_P(RtpPacketHistoryTest, PacketStateIsCorrect) { - const uint32_t kSsrc = 92384762; - const int64_t kRttMs = 100; - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kRttMs); - std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - packet->SetSsrc(kSsrc); - packet->SetPayloadSize(1234); - const size_t packet_size = packet->size(); - - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - - absl::optional state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(state); - EXPECT_EQ(state->rtp_sequence_number, kStartSeqNum); - EXPECT_EQ(state->send_time_ms, fake_clock_.TimeInMilliseconds()); - EXPECT_EQ(state->capture_time_ms, fake_clock_.TimeInMilliseconds()); - EXPECT_EQ(state->ssrc, kSsrc); - EXPECT_EQ(state->packet_size, packet_size); - EXPECT_EQ(state->times_retransmitted, 0u); - - fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - fake_clock_.AdvanceTimeMilliseconds(kRttMs + 1); - - state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(state); - EXPECT_EQ(state->times_retransmitted, 1u); -} - -TEST_P(RtpPacketHistoryTest, MinResendTimeWithPacer) { - static const int64_t kMinRetransmitIntervalMs = 100; +TEST_P(RtpPacketHistoryTest, MinResendTime) { + static const TimeDelta kMinRetransmitInterval = TimeDelta::Millis(100); hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kMinRetransmitIntervalMs); - int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); + hist_.SetRtt(kMinRetransmitInterval); + Timestamp capture_time = fake_clock_.CurrentTime(); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); size_t len = packet->size(); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); - - // First transmission: TimeToSendPacket() call from pacer. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // First retransmission - allow early retransmission. fake_clock_.AdvanceTimeMilliseconds(1); - - // With pacer there's two calls to history: - // 1) When the NACK request arrived, use GetPacketState() to see if the - // packet is there and verify RTT constraints. Then we use the ssrc - // and sequence number to enqueue the retransmission in the pacer - // 2) When the pacer determines that it is time to send the packet, it calls - // GetPacketAndSetSendTime(). - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - EXPECT_TRUE(packet_state); - EXPECT_EQ(len, packet_state->packet_size); - EXPECT_EQ(capture_time_ms, packet_state->capture_time_ms); - - // Retransmission was allowed, next send it from pacer. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - - // Second retransmission - advance time to just before retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1); - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - - // Advance time to just after retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); -} - -TEST_P(RtpPacketHistoryTest, MinResendTimeWithoutPacer) { - static const int64_t kMinRetransmitIntervalMs = 100; - - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kMinRetransmitIntervalMs); - int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); - std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - size_t len = packet->size(); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - - // First retransmission - allow early retransmission. - fake_clock_.AdvanceTimeMilliseconds(1); - packet = hist_.GetPacketAndSetSendTime(kStartSeqNum); - EXPECT_TRUE(packet); + packet = hist_.GetPacketAndMarkAsPending(kStartSeqNum); + ASSERT_TRUE(packet); EXPECT_EQ(len, packet->size()); - EXPECT_EQ(capture_time_ms, packet->capture_time_ms()); + EXPECT_EQ(packet->capture_time(), capture_time); + hist_.MarkPacketAsSent(kStartSeqNum); // Second retransmission - advance time to just before retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1); - EXPECT_FALSE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + fake_clock_.AdvanceTime(kMinRetransmitInterval - TimeDelta::Millis(1)); + EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Advance time to just after retransmission OK. fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) { const size_t kMaxNumPackets = 10; hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - // History does not allow removing packets within kMinPacketDurationMs, + // History does not allow removing packets within kMinPacketDuration, // so in order to test capacity, make sure insertion spans this time. - const int64_t kPacketIntervalMs = - RtpPacketHistory::kMinPacketDurationMs / kMaxNumPackets; + const TimeDelta kPacketInterval = + RtpPacketHistory::kMinPacketDuration / kMaxNumPackets; // Add packets until the buffer is full. for (size_t i = 0; i < kMaxNumPackets; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); // Immediate mark packet as sent. - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(kPacketIntervalMs); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(kPacketInterval); } // First packet should still be there. @@ -257,7 +191,7 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) { // History is full, oldest one should be overwritten. std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Oldest packet should be gone, but packet after than one still present. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); @@ -275,8 +209,10 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) { for (size_t i = 0; i < kMaxNumPackets; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); - // Don't mark packets as sent, preventing them from being removed. - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); + // Mark packets as pending, preventing it from being removed. + hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + i)); } // First packet should still be there. @@ -285,7 +221,7 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) { // History is full, oldest one should be overwritten. std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Oldest packet should be gone, but packet after than one still present. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); @@ -302,14 +238,14 @@ TEST_P(RtpPacketHistoryTest, RemovesLowestPrioPaddingWhenAtMaxCapacity) { // set of potential padding packets. const size_t kMaxNumPackets = RtpPacketHistory::kMaxPaddingHistory; hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets * 2); - hist_.SetRtt(1); + hist_.SetRtt(TimeDelta::Millis(1)); // Add packets until the max is reached, and then yet another one. for (size_t i = 0; i < kMaxNumPackets + 1; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); // Don't mark packets as sent, preventing them from being removed. - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); } // Advance time to allow retransmission/padding. @@ -329,88 +265,53 @@ TEST_P(RtpPacketHistoryTest, RemovesLowestPrioPaddingWhenAtMaxCapacity) { EXPECT_EQ(packet->SequenceNumber(), To16u(kStartSeqNum + kMaxNumPackets)); } -TEST_P(RtpPacketHistoryTest, DontRemoveUnsentPackets) { - const size_t kMaxNumPackets = 10; - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - - // Add packets until the buffer is full. - for (size_t i = 0; i < kMaxNumPackets; ++i) { - // Mark packets as unsent. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)), absl::nullopt); - } - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs); - - // First packet should still be there. - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - - // History is full, but old packets not sent, so allow expansion. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)), - fake_clock_.TimeInMilliseconds()); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - - // Set all packet as sent and advance time past min packet duration time, - // otherwise packets till still be prevented from being removed. - for (size_t i = 0; i <= kMaxNumPackets; ++i) { - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + i))); - } - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs); - // Add a new packet, this means the two oldest ones will be culled. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets + 1)), - fake_clock_.TimeInMilliseconds()); - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); -} - TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPackets) { // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add a packet, marked as send, and advance time to just before removal time. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs - - 1); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(RtpPacketHistory::kMinPacketDuration - + TimeDelta::Millis(1)); // Add a new packet to trigger culling. hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Advance time to where packet will be eligible for removal and try again. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should no be gone, but next one still there. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); } TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPacketsHighRtt) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - const int64_t kPacketTimeoutMs = - kRttMs * RtpPacketHistory::kMinPacketDurationRtt; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + const TimeDelta kPacketTimeout = + kRtt * RtpPacketHistory::kMinPacketDurationRtt; // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); // Add a packet, marked as send, and advance time to just before removal time. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(kPacketTimeoutMs - 1); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); // Add a new packet to trigger culling. hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Advance time to where packet will be eligible for removal and try again. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should no be gone, but next one still there. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); @@ -421,12 +322,11 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCulling) { // Enable culling. Even without feedback, this can trigger early removal. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); - int64_t kMaxPacketDurationMs = RtpPacketHistory::kMinPacketDurationMs * + TimeDelta kMaxPacketDuration = RtpPacketHistory::kMinPacketDuration * RtpPacketHistory::kPacketCullingDelayFactor; - fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1); + fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); @@ -434,25 +334,24 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCulling) { // Advance to where packet can be culled, even if buffer is not full. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) { const size_t kMaxNumPackets = 10; - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; // Enable culling. Even without feedback, this can trigger early removal. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); - int64_t kMaxPacketDurationMs = kRttMs * + TimeDelta kMaxPacketDuration = kRtt * RtpPacketHistory::kMinPacketDurationRtt * RtpPacketHistory::kPacketCullingDelayFactor; - fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1); + fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); @@ -460,122 +359,80 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) { // Advance to where packet can be culled, even if buffer is not full. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, CullWithAcks) { - const int64_t kPacketLifetime = RtpPacketHistory::kMinPacketDurationMs * - RtpPacketHistory::kPacketCullingDelayFactor; + const TimeDelta kPacketLifetime = RtpPacketHistory::kMinPacketDuration * + RtpPacketHistory::kPacketCullingDelayFactor; - const int64_t start_time = fake_clock_.TimeInMilliseconds(); + const Timestamp start_time = fake_clock_.CurrentTime(); hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); // Insert three packets 33ms apart, immediately mark them as sent. std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(kStartSeqNum); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); packet = CreateRtpPacket(To16u(kStartSeqNum + 1)); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + 1)); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); packet = CreateRtpPacket(To16u(kStartSeqNum + 2)); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + 2)); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Remove middle one using ack, check that only that one is gone. std::vector acked_sequence_numbers = {To16u(kStartSeqNum + 1)}; hist_.CullAcknowledgedPackets(acked_sequence_numbers); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Advance time to where second packet would have expired, verify first packet // is removed. - int64_t second_packet_expiry_time = start_time + kPacketLifetime + 33 + 1; - fake_clock_.AdvanceTimeMilliseconds(second_packet_expiry_time - - fake_clock_.TimeInMilliseconds()); - hist_.SetRtt(1); // Trigger culling of old packets. - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + Timestamp second_packet_expiry_time = + start_time + kPacketLifetime + TimeDelta::Millis(33 + 1); + fake_clock_.AdvanceTime(second_packet_expiry_time - + fake_clock_.CurrentTime()); + hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Advance to where last packet expires, verify all gone. fake_clock_.AdvanceTimeMilliseconds(33); - hist_.SetRtt(1); // Trigger culling of old packets. - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); -} - -TEST_P(RtpPacketHistoryTest, SetsPendingTransmissionState) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); - - // Set size to remove old packets as soon as possible. - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - - // Add a packet, without send time, indicating it's in pacer queue. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - /* send_time_ms = */ absl::nullopt); - - // Packet is pending transmission. - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); - - // Packet sent, state should be back to non-pending. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_FALSE(packet_state->pending_transmission); - - // Time for a retransmission. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); - EXPECT_TRUE(hist_.SetPendingTransmission(kStartSeqNum)); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); - - // Packet sent. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - // Too early for retransmission. - ASSERT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - - // Retransmission allowed again, it's not in a pending state. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_FALSE(packet_state->pending_transmission); + hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); } TEST_P(RtpPacketHistoryTest, GetPacketAndSetSent) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + hist_.SetRtt(kRtt); // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add a sent packet to the history. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Retransmission request, first retransmission is allowed immediately. EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Packet not yet sent, new retransmission not allowed. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); + fake_clock_.AdvanceTime(kRtt); EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Mark as sent, but too early for retransmission. @@ -583,14 +440,14 @@ TEST_P(RtpPacketHistoryTest, GetPacketAndSetSent) { EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Enough time has passed, retransmission is allowed again. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); + fake_clock_.AdvanceTime(kRtt); EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { const uint32_t kSsrc = 92384762; - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + hist_.SetRtt(kRtt); // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); @@ -598,7 +455,7 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { // Add a sent packet to the history, with a set SSRC. std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); packet->SetSsrc(kSsrc); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Retransmission request, simulate an RTX-like encapsulation, were the packet // is sent on a different SSRC. @@ -617,8 +474,7 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulationAbortOnNullptr) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Retransmission request, but the encapsulator determines that this packet is // not suitable for retransmission (bandwidth exhausted?) so the retransmit is @@ -632,36 +488,31 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulationAbortOnNullptr) { } TEST_P(RtpPacketHistoryTest, DontRemovePendingTransmissions) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - const int64_t kPacketTimeoutMs = - kRttMs * RtpPacketHistory::kMinPacketDurationRtt; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + const TimeDelta kPacketTimeout = + kRtt * RtpPacketHistory::kMinPacketDurationRtt; // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); // Add a sent packet. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Advance clock to just before packet timeout. - fake_clock_.AdvanceTimeMilliseconds(kPacketTimeoutMs - 1); + fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); // Mark as enqueued in pacer. - EXPECT_TRUE(hist_.SetPendingTransmission(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Advance clock to where packet would have timed out. It should still // be there and pending. fake_clock_.AdvanceTimeMilliseconds(1); - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Packet sent. Now it can be removed. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - hist_.SetRtt(kRttMs); // Force culling of old packets. - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_FALSE(packet_state.has_value()); + hist_.MarkPacketAsSent(kStartSeqNum); + hist_.SetRtt(kRtt); // Force culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { @@ -673,12 +524,11 @@ TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add two sent packets, one millisecond apart. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); // Latest packet given equal retransmission count. @@ -709,26 +559,24 @@ TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { TEST_P(RtpPacketHistoryTest, NoPendingPacketAsPadding) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); // If packet is pending retransmission, don't try to use it as padding. - hist_.SetPendingTransmission(kStartSeqNum); + hist_.GetPacketAndMarkAsPending(kStartSeqNum); EXPECT_EQ(nullptr, hist_.GetPayloadPaddingPacket()); // Market it as no longer pending, should be usable as padding again. - hist_.GetPacketAndSetSendTime(kStartSeqNum); + hist_.MarkPacketAsSent(kStartSeqNum); EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); } TEST_P(RtpPacketHistoryTest, PayloadPaddingWithEncapsulation) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); // Aborted padding. @@ -749,10 +597,9 @@ TEST_P(RtpPacketHistoryTest, PayloadPaddingWithEncapsulation) { TEST_P(RtpPacketHistoryTest, NackAfterAckIsNoop) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 2); // Add two sent packets. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // Remove newest one. hist_.CullAcknowledgedPackets(std::vector{kStartSeqNum + 1}); // Retransmission request for already acked packet, should be noop. @@ -766,29 +613,22 @@ TEST_P(RtpPacketHistoryTest, OutOfOrderInsertRemoval) { // Insert packets, out of order, including both forwards and backwards // sequence number wraps. const int seq_offsets[] = {0, 1, -1, 2, -2, 3, -3}; - const int64_t start_time_ms = fake_clock_.TimeInMilliseconds(); for (int offset : seq_offsets) { uint16_t seq_no = To16u(kStartSeqNum + offset); std::unique_ptr packet = CreateRtpPacket(seq_no); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(seq_no); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); } // Check packet are there and remove them in the same out-of-order fashion. - int64_t expected_time_offset_ms = 0; for (int offset : seq_offsets) { uint16_t seq_no = To16u(kStartSeqNum + offset); - absl::optional packet_state = - hist_.GetPacketState(seq_no); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_EQ(packet_state->send_time_ms, - start_time_ms + expected_time_offset_ms); + EXPECT_TRUE(hist_.GetPacketState(seq_no)); std::vector acked_sequence_numbers = {seq_no}; hist_.CullAcknowledgedPackets(acked_sequence_numbers); - expected_time_offset_ms += 33; + EXPECT_FALSE(hist_.GetPacketState(seq_no)); } } @@ -805,7 +645,7 @@ TEST_P(RtpPacketHistoryTest, UsesLastPacketAsPaddingWithPrioOff) { for (size_t i = 0; i < kHistorySize; ++i) { hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); hist_.MarkPacketAsSent(To16u(kStartSeqNum + i)); fake_clock_.AdvanceTimeMilliseconds(1); diff --git a/modules/rtp_rtcp/source/rtp_packet_received.h b/modules/rtp_rtcp/source/rtp_packet_received.h index 431d3f52be..f290a643a4 100644 --- a/modules/rtp_rtcp/source/rtp_packet_received.h +++ b/modules/rtp_rtcp/source/rtp_packet_received.h @@ -14,7 +14,6 @@ #include -#include "absl/base/attributes.h" #include "api/array_view.h" #include "api/ref_counted_base.h" #include "api/rtp_headers.h" @@ -49,15 +48,6 @@ class RtpPacketReceived : public RtpPacket { webrtc::Timestamp arrival_time() const { return arrival_time_; } void set_arrival_time(webrtc::Timestamp time) { arrival_time_ = time; } - ABSL_DEPRECATED("Use arrival_time() instead") - int64_t arrival_time_ms() const { - return arrival_time_.IsMinusInfinity() ? -1 : arrival_time_.ms(); - } - ABSL_DEPRECATED("Use set_arrival_time() instead") - void set_arrival_time_ms(int64_t time) { - arrival_time_ = webrtc::Timestamp::Millis(time); - } - // Flag if packet was recovered via RTX or FEC. bool recovered() const { return recovered_; } void set_recovered(bool value) { recovered_ = value; } diff --git a/modules/rtp_rtcp/source/rtp_packet_to_send.h b/modules/rtp_rtcp/source/rtp_packet_to_send.h index 12341ef6cf..8c0fc7bd5c 100644 --- a/modules/rtp_rtcp/source/rtp_packet_to_send.h +++ b/modules/rtp_rtcp/source/rtp_packet_to_send.h @@ -19,6 +19,7 @@ #include "api/array_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" +#include "api/units/timestamp.h" #include "api/video/video_timing.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" @@ -44,9 +45,8 @@ class RtpPacketToSend : public RtpPacket { ~RtpPacketToSend(); // Time in local time base as close as it can to frame capture time. - int64_t capture_time_ms() const { return capture_time_ms_; } - - void set_capture_time_ms(int64_t time) { capture_time_ms_ = time; } + webrtc::Timestamp capture_time() const { return capture_time_; } + void set_capture_time(webrtc::Timestamp time) { capture_time_ = time; } void set_packet_type(RtpPacketMediaType type) { packet_type_ = type; } absl::optional packet_type() const { @@ -77,27 +77,27 @@ class RtpPacketToSend : public RtpPacket { additional_data_ = std::move(data); } - void set_packetization_finish_time_ms(int64_t time) { + void set_packetization_finish_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kPacketizationFinishDeltaOffset); } - void set_pacer_exit_time_ms(int64_t time) { + void set_pacer_exit_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kPacerExitDeltaOffset); } - void set_network_time_ms(int64_t time) { + void set_network_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kNetworkTimestampDeltaOffset); } - void set_network2_time_ms(int64_t time) { + void set_network2_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kNetwork2TimestampDeltaOffset); } @@ -121,7 +121,7 @@ class RtpPacketToSend : public RtpPacket { bool is_red() const { return is_red_; } private: - int64_t capture_time_ms_ = 0; + webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero(); absl::optional packet_type_; bool allow_retransmission_ = false; absl::optional retransmitted_sequence_number_; diff --git a/modules/rtp_rtcp/source/rtp_packet_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_unittest.cc index 8c5df1a0ad..ba5e4e61c6 100644 --- a/modules/rtp_rtcp/source/rtp_packet_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_unittest.cc @@ -856,7 +856,7 @@ struct UncopyableValue { }; struct UncopyableExtension { static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor02; - static constexpr char kUri[] = "uri"; + static constexpr absl::string_view Uri() { return "uri"; } static size_t ValueSize(const UncopyableValue& value) { return 1; } static bool Write(rtc::ArrayView data, @@ -869,7 +869,6 @@ struct UncopyableExtension { } }; constexpr RTPExtensionType UncopyableExtension::kId; -constexpr char UncopyableExtension::kUri[]; TEST(RtpPacketTest, SetUncopyableExtension) { RtpPacket::ExtensionManager extensions; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_config.h b/modules/rtp_rtcp/source/rtp_rtcp_config.h index 66caadd578..3e6aa3baae 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_config.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_config.h @@ -15,7 +15,7 @@ // Configuration file for RTP utilities (RTPSender, RTPReceiver ...) namespace webrtc { -constexpr int kDefaultMaxReorderingThreshold = 5; // In sequence numbers. +constexpr int kDefaultMaxReorderingThreshold = 50; // In sequence numbers. constexpr int kRtcpMaxNackFields = 253; constexpr TimeDelta RTCP_SEND_BEFORE_KEY_FRAME = TimeDelta::Millis(100); diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 3dda8c6710..a98dab3972 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -630,10 +630,6 @@ void ModuleRtpRtcpImpl::RegisterRtpHeaderExtension(absl::string_view uri, RTC_CHECK(registered); } -int32_t ModuleRtpRtcpImpl::DeregisterSendRtpHeaderExtension( - const RTPExtensionType type) { - return rtp_sender_->packet_generator.DeregisterRtpHeaderExtension(type); -} void ModuleRtpRtcpImpl::DeregisterSendRtpHeaderExtension( absl::string_view uri) { rtp_sender_->packet_generator.DeregisterRtpHeaderExtension(uri); @@ -791,7 +787,7 @@ void ModuleRtpRtcpImpl::set_rtt_ms(int64_t rtt_ms) { rtt_ms_ = rtt_ms; } if (rtp_sender_) { - rtp_sender_->packet_history.SetRtt(rtt_ms); + rtp_sender_->packet_history.SetRtt(TimeDelta::Millis(rtt_ms)); } } diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 655691c9e1..499573cab4 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -76,7 +76,6 @@ class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp { // Register RTP header extension. void RegisterRtpHeaderExtension(absl::string_view uri, int id) override; - int32_t DeregisterSendRtpHeaderExtension(RTPExtensionType type) override; void DeregisterSendRtpHeaderExtension(absl::string_view uri) override; bool SupportsPadding() const override; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc index 73ae138085..06fbbe01b5 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc @@ -584,10 +584,6 @@ void ModuleRtpRtcpImpl2::RegisterRtpHeaderExtension(absl::string_view uri, RTC_CHECK(registered); } -int32_t ModuleRtpRtcpImpl2::DeregisterSendRtpHeaderExtension( - const RTPExtensionType type) { - return rtp_sender_->packet_generator.DeregisterRtpHeaderExtension(type); -} void ModuleRtpRtcpImpl2::DeregisterSendRtpHeaderExtension( absl::string_view uri) { rtp_sender_->packet_generator.DeregisterRtpHeaderExtension(uri); @@ -749,7 +745,7 @@ void ModuleRtpRtcpImpl2::set_rtt_ms(int64_t rtt_ms) { rtt_ms_ = rtt_ms; } if (rtp_sender_) { - rtp_sender_->packet_history.SetRtt(rtt_ms); + rtp_sender_->packet_history.SetRtt(TimeDelta::Millis(rtt_ms)); } } diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h index 57a3fd1391..535907f2cb 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h @@ -83,7 +83,6 @@ class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, void SetExtmapAllowMixed(bool extmap_allow_mixed) override; void RegisterRtpHeaderExtension(absl::string_view uri, int id) override; - int32_t DeregisterSendRtpHeaderExtension(RTPExtensionType type) override; void DeregisterSendRtpHeaderExtension(absl::string_view uri) override; bool SupportsPadding() const override; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc index 599264e7c5..8592e42173 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc @@ -957,7 +957,7 @@ TEST_P(RtpRtcpImpl2Test, PaddingTimestampMatchesMedia) { } TEST_P(RtpRtcpImpl2Test, AssignsTransportSequenceNumber) { - sender_.RegisterHeaderExtension(TransportSequenceNumber::kUri, + sender_.RegisterHeaderExtension(TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId); EXPECT_TRUE(SendFrame(&sender_, sender_video_.get(), kBaseLayerTid)); @@ -974,7 +974,7 @@ TEST_P(RtpRtcpImpl2Test, AssignsTransportSequenceNumber) { } TEST_P(RtpRtcpImpl2Test, AssignsAbsoluteSendTime) { - sender_.RegisterHeaderExtension(AbsoluteSendTime::kUri, + sender_.RegisterHeaderExtension(AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId); EXPECT_TRUE(SendFrame(&sender_, sender_video_.get(), kBaseLayerTid)); @@ -982,7 +982,7 @@ TEST_P(RtpRtcpImpl2Test, AssignsAbsoluteSendTime) { } TEST_P(RtpRtcpImpl2Test, AssignsTransmissionTimeOffset) { - sender_.RegisterHeaderExtension(TransmissionOffset::kUri, + sender_.RegisterHeaderExtension(TransmissionOffset::Uri(), kTransmissionOffsetExtensionId); constexpr TimeDelta kOffset = TimeDelta::Millis(100); @@ -998,7 +998,7 @@ TEST_P(RtpRtcpImpl2Test, AssignsTransmissionTimeOffset) { } TEST_P(RtpRtcpImpl2Test, PropagatesSentPacketInfo) { - sender_.RegisterHeaderExtension(TransportSequenceNumber::kUri, + sender_.RegisterHeaderExtension(TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId); int64_t now_ms = time_controller_.GetClock()->TimeInMilliseconds(); EXPECT_TRUE(SendFrame(&sender_, sender_video_.get(), kBaseLayerTid)); diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h index 13ebb0ab63..a411b237a0 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h @@ -27,7 +27,6 @@ #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/rtp_rtcp/source/rtp_sequence_number_map.h" #include "modules/rtp_rtcp/source/video_fec_generator.h" -#include "rtc_base/constructor_magic.h" #include "system_wrappers/include/ntp_time.h" namespace webrtc { @@ -47,6 +46,9 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { Configuration() = default; Configuration(Configuration&& rhs) = default; + Configuration(const Configuration&) = delete; + Configuration& operator=(const Configuration&) = delete; + // True for a audio version of the RTP/RTCP module object false will create // a video version. bool audio = false; @@ -145,9 +147,6 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { // Estimate RTT as non-sender as described in // https://tools.ietf.org/html/rfc3611#section-4.4 and #section-4.5 bool non_sender_rtt_measurement = false; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(Configuration); }; // Stats for RTCP sender reports (SR) for a specific SSRC. @@ -218,7 +217,6 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { // Register extension by uri, triggers CHECK on falure. virtual void RegisterRtpHeaderExtension(absl::string_view uri, int id) = 0; - virtual int32_t DeregisterSendRtpHeaderExtension(RTPExtensionType type) = 0; virtual void DeregisterSendRtpHeaderExtension(absl::string_view uri) = 0; // Returns true if RTP module is send media, and any of the extensions diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index 8a0b613bd4..45838e7036 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -125,7 +125,7 @@ bool IsNonVolatile(RTPExtensionType type) { return false; case kRtpExtensionNone: case kRtpExtensionNumberOfExtensions: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } RTC_CHECK_NOTREACHED(); @@ -238,14 +238,6 @@ bool RTPSender::IsRtpHeaderExtensionRegistered(RTPExtensionType type) const { return rtp_header_extension_map_.IsRegistered(type); } -int32_t RTPSender::DeregisterRtpHeaderExtension(RTPExtensionType type) { - MutexLock lock(&send_mutex_); - rtp_header_extension_map_.Deregister(type); - supports_bwe_extension_ = HasBweExtension(rtp_header_extension_map_); - UpdateHeaderSizes(); - return 0; -} - void RTPSender::DeregisterRtpHeaderExtension(absl::string_view uri) { MutexLock lock(&send_mutex_); rtp_header_extension_map_.Deregister(uri); @@ -266,6 +258,12 @@ size_t RTPSender::MaxRtpPacketSize() const { void RTPSender::SetRtxStatus(int mode) { MutexLock lock(&send_mutex_); + if (mode != kRtxOff && + (!rtx_ssrc_.has_value() || rtx_payload_type_map_.empty())) { + RTC_LOG(LS_ERROR) + << "Failed to enable RTX without RTX SSRC or payload types."; + return; + } rtx_ = mode; } @@ -288,16 +286,7 @@ void RTPSender::SetRtxPayloadType(int payload_type, } int32_t RTPSender::ReSendPacket(uint16_t packet_id) { - // Try to find packet in RTP packet history. Also verify RTT here, so that we - // don't retransmit too often. - absl::optional stored_packet = - packet_history_->GetPacketState(packet_id); - if (!stored_packet || stored_packet->pending_transmission) { - // Packet not found or already queued for retransmission, ignore. - return 0; - } - - const int32_t packet_size = static_cast(stored_packet->packet_size); + int32_t packet_size = 0; const bool rtx = (RtxStatus() & kRtxRetransmitted) > 0; std::unique_ptr packet = @@ -306,6 +295,7 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id) { // Check if we're overusing retransmission bitrate. // TODO(sprang): Add histograms for nack success or failure // reasons. + packet_size = stored_packet.size(); std::unique_ptr retransmit_packet; if (retransmission_rate_limiter_ && !retransmission_rate_limiter_->TryUseRate(packet_size)) { @@ -323,7 +313,14 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id) { } return retransmit_packet; }); + if (packet_size == 0) { + // Packet not found or already queued for retransmission, ignore. + RTC_DCHECK(!packet); + return 0; + } if (!packet) { + // Packet was found, but lambda helper above chose not to create + // `retransmit_packet` out of it. return -1; } packet->set_packet_type(RtpPacketMediaType::kRetransmission); @@ -357,7 +354,7 @@ void RTPSender::OnReceivedAckOnRtxSsrc( void RTPSender::OnReceivedNack( const std::vector& nack_sequence_numbers, int64_t avg_rtt) { - packet_history_->SetRtt(5 + avg_rtt); + packet_history_->SetRtt(TimeDelta::Millis(5 + avg_rtt)); for (uint16_t seq_no : nack_sequence_numbers) { const int32_t bytes_sent = ReSendPacket(seq_no); if (bytes_sent < 0) { @@ -461,6 +458,7 @@ std::vector> RTPSender::GeneratePadding( } RTC_DCHECK(rtx_ssrc_); + RTC_DCHECK(!rtx_payload_type_map_.empty()); padding_packet->SetSsrc(*rtx_ssrc_); padding_packet->SetPayloadType(rtx_payload_type_map_.begin()->second); } @@ -485,13 +483,11 @@ std::vector> RTPSender::GeneratePadding( bool RTPSender::SendToNetwork(std::unique_ptr packet) { RTC_DCHECK(packet); - int64_t now_ms = clock_->TimeInMilliseconds(); - auto packet_type = packet->packet_type(); RTC_CHECK(packet_type) << "Packet type must be set before sending."; - if (packet->capture_time_ms() <= 0) { - packet->set_capture_time_ms(now_ms); + if (packet->capture_time() <= Timestamp::Zero()) { + packet->set_capture_time(clock_->CurrentTime()); } std::vector> packets; @@ -504,13 +500,13 @@ bool RTPSender::SendToNetwork(std::unique_ptr packet) { void RTPSender::EnqueuePackets( std::vector> packets) { RTC_DCHECK(!packets.empty()); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); for (auto& packet : packets) { RTC_DCHECK(packet); RTC_CHECK(packet->packet_type().has_value()) << "Packet type must be set before sending."; - if (packet->capture_time_ms() <= 0) { - packet->set_capture_time_ms(now_ms); + if (packet->capture_time() <= Timestamp::Zero()) { + packet->set_capture_time(now); } } @@ -727,7 +723,7 @@ std::unique_ptr RTPSender::BuildRtxPacket( rtx_packet->set_additional_data(packet.additional_data()); // Copy capture time so e.g. TransmissionOffset is correctly set. - rtx_packet->set_capture_time_ms(packet.capture_time_ms()); + rtx_packet->set_capture_time(packet.capture_time()); return rtx_packet; } diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index 3374f2b993..d892970542 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -88,8 +88,6 @@ class RTPSender { RTC_LOCKS_EXCLUDED(send_mutex_); bool IsRtpHeaderExtensionRegistered(RTPExtensionType type) const RTC_LOCKS_EXCLUDED(send_mutex_); - int32_t DeregisterRtpHeaderExtension(RTPExtensionType type) - RTC_LOCKS_EXCLUDED(send_mutex_); void DeregisterRtpHeaderExtension(absl::string_view uri) RTC_LOCKS_EXCLUDED(send_mutex_); diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.cc b/modules/rtp_rtcp/source/rtp_sender_audio.cc index 9840738f8f..285967e478 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio.cc @@ -60,8 +60,8 @@ RTPSenderAudio::RTPSenderAudio(Clock* clock, RTPSender* rtp_sender) rtp_sender_(rtp_sender), absolute_capture_time_sender_(clock), include_capture_clock_offset_( - absl::StartsWith(field_trials_.Lookup(kIncludeCaptureClockOffset), - "Enabled")) { + !absl::StartsWith(field_trials_.Lookup(kIncludeCaptureClockOffset), + "Disabled")) { RTC_DCHECK(clock_); } @@ -272,7 +272,7 @@ bool RTPSenderAudio::SendAudio(AudioFrameType frame_type, packet->SetMarker(MarkerBit(frame_type, payload_type)); packet->SetPayloadType(payload_type); packet->SetTimestamp(rtp_timestamp); - packet->set_capture_time_ms(clock_->TimeInMilliseconds()); + packet->set_capture_time(clock_->CurrentTime()); // RingRTC change to reduce unneeded information on the wire // Make the audio level less precise (0, 10, 20, 30, ...). audio_level_dbov -= (audio_level_dbov % 10); @@ -374,7 +374,7 @@ bool RTPSenderAudio::SendTelephoneEventPacket(bool ended, packet->SetMarker(marker_bit); packet->SetSsrc(rtp_sender_->SSRC()); packet->SetTimestamp(dtmf_timestamp); - packet->set_capture_time_ms(clock_->TimeInMilliseconds()); + packet->set_capture_time(clock_->CurrentTime()); // Create DTMF data. uint8_t* dtmfbuffer = packet->AllocatePayload(kDtmfSize); diff --git a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc index 0221800ea8..52880b9a59 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc @@ -106,7 +106,7 @@ TEST_F(RtpSenderAudioTest, SendAudio) { TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { EXPECT_EQ(0, rtp_sender_audio_->SetAudioLevel(kAudioLevel)); - rtp_module_->RegisterRtpHeaderExtension(AudioLevel::kUri, + rtp_module_->RegisterRtpHeaderExtension(AudioLevel::Uri(), kAudioLevelExtensionId); const char payload_name[] = "PAYLOAD_NAME"; @@ -148,8 +148,17 @@ TEST_F(RtpSenderAudioTest, SendAudioWithoutAbsoluteCaptureTime) { .HasExtension()); } +// Essentially the same test as +// SendAudioWithAbsoluteCaptureTimeWithCaptureClockOffset but with a field +// trial. We will remove this test eventually. TEST_F(RtpSenderAudioTest, SendAudioWithAbsoluteCaptureTime) { - rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + // Recreate rtp_sender_audio_ with new field trial. + test::ScopedFieldTrials field_trial( + "WebRTC-IncludeCaptureClockOffset/Disabled/"); + rtp_sender_audio_ = + std::make_unique(&fake_clock_, rtp_module_->RtpSender()); + + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), kAbsoluteCaptureTimeExtensionId); constexpr uint32_t kAbsoluteCaptureTimestampMs = 521; const char payload_name[] = "audio"; @@ -174,18 +183,9 @@ TEST_F(RtpSenderAudioTest, SendAudioWithAbsoluteCaptureTime) { absolute_capture_time->estimated_capture_clock_offset.has_value()); } -// Essentially the same test as SendAudioWithAbsoluteCaptureTime but with a -// field trial. After the field trial is experimented, we will remove -// SendAudioWithAbsoluteCaptureTime. TEST_F(RtpSenderAudioTest, SendAudioWithAbsoluteCaptureTimeWithCaptureClockOffset) { - // Recreate rtp_sender_audio_ wieh new field trial. - test::ScopedFieldTrials field_trial( - "WebRTC-IncludeCaptureClockOffset/Enabled/"); - rtp_sender_audio_ = - std::make_unique(&fake_clock_, rtp_module_->RtpSender()); - - rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), kAbsoluteCaptureTimeExtensionId); constexpr uint32_t kAbsoluteCaptureTimestampMs = 521; const char payload_name[] = "audio"; diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.cc b/modules/rtp_rtcp/source/rtp_sender_egress.cc index 89c5597c1b..e5c2e536f6 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress.cc @@ -225,7 +225,7 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, // In case of VideoTimingExtension, since it's present not in every packet, // data after rtp header may be corrupted if these packets are protected by // the FEC. - int64_t diff_ms = now_ms - packet->capture_time_ms(); + int64_t diff_ms = now_ms - packet->capture_time().ms(); if (packet->HasExtension()) { packet->SetExtension(kTimestampTicksPerMs * diff_ms); } @@ -236,9 +236,9 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, if (packet->HasExtension()) { if (populate_network2_timestamp_) { - packet->set_network2_time_ms(now_ms); + packet->set_network2_time(Timestamp::Millis(now_ms)); } else { - packet->set_pacer_exit_time_ms(now_ms); + packet->set_pacer_exit_time(Timestamp::Millis(now_ms)); } } @@ -265,8 +265,8 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, if (packet->packet_type() != RtpPacketMediaType::kPadding && packet->packet_type() != RtpPacketMediaType::kRetransmission) { - UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc); - UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), + UpdateDelayStatistics(packet->capture_time().ms(), now_ms, packet_ssrc); + UpdateOnSendPacket(options.packet_id, packet->capture_time().ms(), packet_ssrc); } @@ -276,7 +276,7 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, // actual sending fails. if (is_media && packet->allow_retransmission()) { packet_history_->PutRtpPacket(std::make_unique(*packet), - now_ms); + Timestamp::Millis(now_ms)); } else if (packet->retransmitted_sequence_number()) { packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number()); } @@ -445,8 +445,6 @@ void RtpSenderEgress::AddPacketToTransportFeedback( // or lost. break; } - // TODO(bugs.webrtc.org/12713): Remove once downstream usage is gone. - packet_info.ssrc = packet_info.media_ssrc.value_or(0); transport_feedback_observer_->OnAddPacket(packet_info); } diff --git a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc index d95a639e21..ee7123b112 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc @@ -184,7 +184,7 @@ class RtpSenderEgressTest : public ::testing::TestWithParam { packet->set_packet_type(RtpPacketMediaType::kVideo); packet->SetMarker(marker_bit); packet->SetTimestamp(capture_time_ms * 90); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); packet->SetSequenceNumber(sequence_number_++); return packet; } @@ -212,7 +212,7 @@ TEST_P(RtpSenderEgressTest, TransportFeedbackObserverGetsCorrectByteCount) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); const size_t expected_bytes = GetParam().with_overhead ? kPayloadSize + kRtpOverheadBytesPerPacket @@ -266,7 +266,7 @@ TEST_P(RtpSenderEgressTest, std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr packet = BuildRtpPacket(); sender->SendPacket(packet.get(), PacedPacketInfo()); EXPECT_TRUE(transport_.last_packet()->options.included_in_feedback); @@ -278,7 +278,7 @@ TEST_P( std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr packet = BuildRtpPacket(); sender->SendPacket(packet.get(), PacedPacketInfo()); EXPECT_TRUE(transport_.last_packet()->options.included_in_allocation); @@ -342,7 +342,7 @@ TEST_P(RtpSenderEgressTest, OnSendSideDelayUpdated) { TEST_P(RtpSenderEgressTest, WritesPacerExitToTimingExtension) { std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kVideoTimingExtensionId, - VideoTimingExtension::kUri); + VideoTimingExtension::Uri()); std::unique_ptr packet = BuildRtpPacket(); packet->SetExtension(VideoSendTiming{}); @@ -364,7 +364,7 @@ TEST_P(RtpSenderEgressTest, WritesNetwork2ToTimingExtension) { rtp_config.populate_network2_timestamp = true; auto sender = std::make_unique(rtp_config, &packet_history_); header_extensions_.RegisterByUri(kVideoTimingExtensionId, - VideoTimingExtension::kUri); + VideoTimingExtension::Uri()); const uint16_t kPacerExitMs = 1234u; std::unique_ptr packet = BuildRtpPacket(); @@ -388,7 +388,7 @@ TEST_P(RtpSenderEgressTest, WritesNetwork2ToTimingExtension) { TEST_P(RtpSenderEgressTest, OnSendPacketUpdated) { std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); const uint16_t kTransportSequenceNumber = 1; EXPECT_CALL(send_packet_observer_, @@ -402,7 +402,7 @@ TEST_P(RtpSenderEgressTest, OnSendPacketUpdated) { TEST_P(RtpSenderEgressTest, OnSendPacketNotUpdatedForRetransmits) { std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); const uint16_t kTransportSequenceNumber = 1; EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(0); @@ -496,8 +496,7 @@ TEST_P(RtpSenderEgressTest, DoesNotPutNotRetransmittablePacketsInHistory) { std::unique_ptr packet = BuildRtpPacket(); packet->set_allow_retransmission(false); sender->SendPacket(packet.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(packet->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, PutsRetransmittablePacketsInHistory) { @@ -508,10 +507,7 @@ TEST_P(RtpSenderEgressTest, PutsRetransmittablePacketsInHistory) { std::unique_ptr packet = BuildRtpPacket(); packet->set_allow_retransmission(true); sender->SendPacket(packet.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, DoesNotPutNonMediaInHistory) { @@ -527,22 +523,20 @@ TEST_P(RtpSenderEgressTest, DoesNotPutNonMediaInHistory) { retransmission->set_retransmitted_sequence_number( retransmission->SequenceNumber()); sender->SendPacket(retransmission.get(), PacedPacketInfo()); - EXPECT_FALSE(packet_history_.GetPacketState(retransmission->SequenceNumber()) - .has_value()); + EXPECT_FALSE( + packet_history_.GetPacketState(retransmission->SequenceNumber())); std::unique_ptr fec = BuildRtpPacket(); fec->set_allow_retransmission(true); fec->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); sender->SendPacket(fec.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(fec->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(fec->SequenceNumber())); std::unique_ptr padding = BuildRtpPacket(); padding->set_allow_retransmission(true); padding->set_packet_type(RtpPacketMediaType::kPadding); sender->SendPacket(padding.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(padding->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(padding->SequenceNumber())); } TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { @@ -554,10 +548,7 @@ TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { std::unique_ptr media_packet = BuildRtpPacket(); media_packet->set_allow_retransmission(true); sender->SendPacket(media_packet.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); // Simulate a retransmission, marking the packet as pending. std::unique_ptr retransmission = @@ -565,16 +556,11 @@ TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { retransmission->set_retransmitted_sequence_number( media_packet->SequenceNumber()); retransmission->set_packet_type(RtpPacketMediaType::kRetransmission); - EXPECT_THAT(packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional(Field( - &RtpPacketHistory::PacketState::pending_transmission, true))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); // Simulate packet leaving pacer, the packet should be marked as non-pending. sender->SendPacket(retransmission.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, StreamDataCountersCallbacks) { @@ -748,15 +734,15 @@ TEST_P(RtpSenderEgressTest, UpdatesDataCounters) { TEST_P(RtpSenderEgressTest, SendPacketUpdatesExtensions) { header_extensions_.RegisterByUri(kVideoTimingExtensionId, - VideoTimingExtension::kUri); + VideoTimingExtension::Uri()); header_extensions_.RegisterByUri(kAbsoluteSendTimeExtensionId, - AbsoluteSendTime::kUri); + AbsoluteSendTime::Uri()); header_extensions_.RegisterByUri(kTransmissionOffsetExtensionId, - TransmissionOffset::kUri); + TransmissionOffset::Uri()); std::unique_ptr sender = CreateRtpSenderEgress(); std::unique_ptr packet = BuildRtpPacket(); - packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds()); + packet->set_packetization_finish_time(clock_->CurrentTime()); const int32_t kDiffMs = 10; time_controller_.AdvanceTime(TimeDelta::Millis(kDiffMs)); @@ -779,7 +765,7 @@ TEST_P(RtpSenderEgressTest, SendPacketSetsPacketOptions) { const uint16_t kPacketId = 42; std::unique_ptr sender = CreateRtpSenderEgress(); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr packet = BuildRtpPacket(); packet->SetExtension(kPacketId); @@ -816,7 +802,7 @@ TEST_P(RtpSenderEgressTest, SendPacketUpdatesStats) { auto sender = std::make_unique(config, &packet_history_); header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); const int64_t capture_time_ms = clock_->TimeInMilliseconds(); @@ -871,7 +857,7 @@ TEST_P(RtpSenderEgressTest, SendPacketUpdatesStats) { TEST_P(RtpSenderEgressTest, TransportFeedbackObserverWithRetransmission) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr retransmission = BuildRtpPacket(); retransmission->set_packet_type(RtpPacketMediaType::kRetransmission); retransmission->SetExtension( @@ -893,7 +879,7 @@ TEST_P(RtpSenderEgressTest, TransportFeedbackObserverWithRetransmission) { TEST_P(RtpSenderEgressTest, TransportFeedbackObserverWithRtxRetransmission) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr rtx_retransmission = BuildRtpPacket(); rtx_retransmission->SetSsrc(kRtxSsrc); @@ -917,7 +903,7 @@ TEST_P(RtpSenderEgressTest, TransportFeedbackObserverWithRtxRetransmission) { TEST_P(RtpSenderEgressTest, TransportFeedbackObserverPadding) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr padding = BuildRtpPacket(); padding->SetPadding(224); padding->set_packet_type(RtpPacketMediaType::kPadding); @@ -935,7 +921,7 @@ TEST_P(RtpSenderEgressTest, TransportFeedbackObserverPadding) { TEST_P(RtpSenderEgressTest, TransportFeedbackObserverRtxPadding) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr rtx_padding = BuildRtpPacket(); rtx_padding->SetPadding(224); @@ -955,7 +941,7 @@ TEST_P(RtpSenderEgressTest, TransportFeedbackObserverRtxPadding) { TEST_P(RtpSenderEgressTest, TransportFeedbackObserverFec) { const uint16_t kTransportSequenceNumber = 17; header_extensions_.RegisterByUri(kTransportSequenceNumberExtensionId, - TransportSequenceNumber::kUri); + TransportSequenceNumber::Uri()); std::unique_ptr fec_packet = BuildRtpPacket(); fec_packet->SetSsrc(kFlexFecSsrc); diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 2724cec8ff..d77d566c17 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -198,7 +198,7 @@ class RtpSenderTest : public ::testing::Test { packet->set_packet_type(RtpPacketMediaType::kVideo); packet->SetMarker(marker_bit); packet->SetTimestamp(timestamp); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); return packet; } @@ -267,15 +267,16 @@ class RtpSenderTest : public ::testing::Test { // Enable sending of the MID header extension for both the primary SSRC and // the RTX SSRC. void EnableMidSending(const std::string& mid) { - rtp_sender_->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId); + rtp_sender_->RegisterRtpHeaderExtension(RtpMid::Uri(), kMidExtensionId); rtp_sender_->SetMid(mid); } // Enable sending of the RSID header extension for the primary SSRC and the // RRSID header extension for the RTX SSRC. void EnableRidSending(const std::string& rid) { - rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::kUri, kRidExtensionId); - rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri, + rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), + kRidExtensionId); + rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), kRepairedRidExtensionId); rtp_sender_->SetRid(rid); } @@ -297,15 +298,15 @@ TEST_F(RtpSenderTest, AllocatePacketSetCsrc) { TEST_F(RtpSenderTest, AllocatePacketReserveExtensions) { // Configure rtp_sender with extensions. ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId)); + TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId)); - ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(AudioLevel::kUri, + AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); + ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(AudioLevel::Uri(), kAudioLevelExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - VideoOrientation::kUri, kVideoRotationExtensionId)); + VideoOrientation::Uri(), kVideoRotationExtensionId)); auto packet = rtp_sender_->AllocatePacket(); @@ -353,13 +354,12 @@ TEST_F(RtpSenderTest, PaddingAlwaysAllowedOnAudio) { TEST_F(RtpSenderTest, SendToNetworkForwardsPacketsToPacer) { auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, 0); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); - EXPECT_CALL( - mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms)))))); + EXPECT_CALL(mock_paced_sender_, + EnqueuePackets(ElementsAre(AllOf( + Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), + Pointee(Property(&RtpPacketToSend::capture_time, now)))))); EXPECT_TRUE( rtp_sender_->SendToNetwork(std::make_unique(*packet))); } @@ -371,13 +371,14 @@ TEST_F(RtpSenderTest, ReSendPacketForwardsPacketsToPacer) { auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, now_ms); packet->SetSequenceNumber(kSeqNum); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), now_ms); + packet_history_->PutRtpPacket(std::move(packet), Timestamp::Millis(now_ms)); EXPECT_CALL(mock_paced_sender_, EnqueuePackets(ElementsAre(AllOf( Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(now_ms))), Pointee(Property(&RtpPacketToSend::packet_type, RtpPacketMediaType::kRetransmission)))))); EXPECT_TRUE(rtp_sender_->ReSendPacket(kSeqNum)); @@ -442,9 +443,42 @@ TEST_F(RtpSenderTest, NoPaddingAsFirstPacketWithoutBweExtensions) { IsEmpty()); } +TEST_F(RtpSenderTest, RequiresRtxSsrcToEnableRtx) { + RtpRtcpInterface::Configuration config = GetDefaultConfig(); + config.rtx_send_ssrc = absl::nullopt; + RTPSender rtp_sender(config, packet_history_.get(), config.paced_sender); + rtp_sender.SetRtxPayloadType(kRtxPayload, kPayload); + + rtp_sender.SetRtxStatus(kRtxRetransmitted); + + EXPECT_EQ(rtp_sender.RtxStatus(), kRtxOff); +} + +TEST_F(RtpSenderTest, RequiresRtxPayloadTypesToEnableRtx) { + RtpRtcpInterface::Configuration config = GetDefaultConfig(); + config.rtx_send_ssrc = kRtxSsrc; + RTPSender rtp_sender(config, packet_history_.get(), config.paced_sender); + + rtp_sender.SetRtxStatus(kRtxRetransmitted); + + EXPECT_EQ(rtp_sender.RtxStatus(), kRtxOff); +} + +TEST_F(RtpSenderTest, CanEnableRtxWhenRtxSsrcAndPayloadTypeAreConfigured) { + RtpRtcpInterface::Configuration config = GetDefaultConfig(); + config.rtx_send_ssrc = kRtxSsrc; + RTPSender rtp_sender(config, packet_history_.get(), config.paced_sender); + rtp_sender.SetRtxPayloadType(kRtxPayload, kPayload); + + ASSERT_EQ(rtp_sender.RtxStatus(), kRtxOff); + rtp_sender.SetRtxStatus(kRtxRetransmitted); + + EXPECT_EQ(rtp_sender.RtxStatus(), kRtxRetransmitted); +} + TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithTransportCc) { ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); // Padding can't be sent as first packet on media SSRC since we don't know // what payload type to assign. @@ -465,7 +499,7 @@ TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithTransportCc) { TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithAbsSendTime) { ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId)); + AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); // Padding can't be sent as first packet on media SSRC since we don't know // what payload type to assign. @@ -493,10 +527,11 @@ TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) { // Start by sending one media packet. EXPECT_CALL( mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time)))))); + EnqueuePackets(ElementsAre( + AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time))))))); std::unique_ptr media_packet = SendPacket(start_time, /*payload_size=*/600); sequencer_->Sequence(*media_packet); @@ -512,13 +547,13 @@ TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) { Property(&RtpPacketToSend::padding_size, kMaxPaddingLength), Property(&RtpPacketToSend::Timestamp, start_timestamp + (kTimestampTicksPerMs * kTimeDiff.ms())), - Property(&RtpPacketToSend::capture_time_ms, - start_time + kTimeDiff.ms()))))); + Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time) + kTimeDiff))))); } TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) { ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); EnableRtx(); // Timestamps as set based on capture time in RtpSenderTest. const int64_t start_time = clock_->TimeInMilliseconds(); @@ -529,27 +564,29 @@ TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) { // Start by sending one media packet and putting in the packet history. EXPECT_CALL( mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time)))))); + EnqueuePackets(ElementsAre( + AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time))))))); std::unique_ptr media_packet = SendPacket(start_time, kPayloadSize); - packet_history_->PutRtpPacket(std::move(media_packet), start_time); + packet_history_->PutRtpPacket(std::move(media_packet), + Timestamp::Millis(start_time)); // Advance time before sending padding. const TimeDelta kTimeDiff = TimeDelta::Millis(17); time_controller_.AdvanceTime(kTimeDiff); // Timestamps on payload padding should be set to original. - EXPECT_THAT( - GeneratePadding(/*target_size_bytes=*/100), - Each(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::payload_size, - kPayloadSize + kRtxHeaderSize)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time))))); + EXPECT_THAT(GeneratePadding(/*target_size_bytes=*/100), + Each(AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::payload_size, + kPayloadSize + kRtxHeaderSize)), + Pointee(Property(&RtpPacketToSend::Timestamp, + start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time)))))); } // Test that the MID header extension is included on sent packets when @@ -590,7 +627,7 @@ TEST_F(RtpSenderTest, RidIncludedOnRtxSentPackets) { .WillOnce([&](std::vector> packets) { sequencer_->Sequence(*packets[0]); packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); SendGenericPacket(); @@ -669,7 +706,7 @@ TEST_F(RtpSenderTest, MidAndRidIncludedOnFirstRtxPacket) { EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) .WillOnce([&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto second_built_packet = SendGenericPacket(); @@ -698,7 +735,7 @@ TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) { sequencer_->Sequence(*first_built_packet); packet_history_->PutRtpPacket( std::make_unique(*first_built_packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); // The second packet will include neither since an ack was received. @@ -706,7 +743,7 @@ TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) { sequencer_->Sequence(*second_built_packet); packet_history_->PutRtpPacket( std::make_unique(*second_built_packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); // The first RTX packet will include MID and RRID. EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) @@ -746,7 +783,7 @@ TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnRtxPacketsWhenConfigured) { .WillRepeatedly( [&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto media_packet1 = SendGenericPacket(); rtp_sender_->OnReceivedAckOnSsrc(media_packet1->SequenceNumber()); @@ -814,7 +851,7 @@ TEST_F(RtpSenderTest, MidAndRridNotIncludedOnRtxPacketsAfterRtpStateRestored) { EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) .WillOnce([&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto built_packet = SendGenericPacket(); @@ -841,7 +878,7 @@ TEST_F(RtpSenderTest, RespectsNackBitrateLimit) { sequencer_->Sequence(*packet); sequence_numbers.push_back(packet->SequenceNumber()); packet_history_->PutRtpPacket(std::move(packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); time_controller_.AdvanceTime(TimeDelta::Millis(1)); } @@ -892,7 +929,7 @@ TEST_F(RtpSenderTest, OnOverheadChanged) { // Base RTP overhead is 12B. EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); - rtp_sender_->RegisterRtpHeaderExtension(TransmissionOffset::kUri, + rtp_sender_->RegisterRtpHeaderExtension(TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId); // TransmissionTimeOffset extension has a size of 3B, but with the addition @@ -908,8 +945,8 @@ TEST_F(RtpSenderTest, CountMidOnlyUntilAcked) { // Base RTP overhead is 12B. EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); - rtp_sender_->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId); - rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::kUri, kRidExtensionId); + rtp_sender_->RegisterRtpHeaderExtension(RtpMid::Uri(), kMidExtensionId); + rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), kRidExtensionId); // Counted only if set. EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); @@ -931,15 +968,16 @@ TEST_F(RtpSenderTest, DontCountVolatileExtensionsIntoOverhead) { // Base RTP overhead is 12B. EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); - rtp_sender_->RegisterRtpHeaderExtension(InbandComfortNoiseExtension::kUri, 1); - rtp_sender_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + rtp_sender_->RegisterRtpHeaderExtension(InbandComfortNoiseExtension::Uri(), + 1); + rtp_sender_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), 2); - rtp_sender_->RegisterRtpHeaderExtension(VideoOrientation::kUri, 3); - rtp_sender_->RegisterRtpHeaderExtension(PlayoutDelayLimits::kUri, 4); - rtp_sender_->RegisterRtpHeaderExtension(VideoContentTypeExtension::kUri, 5); - rtp_sender_->RegisterRtpHeaderExtension(VideoTimingExtension::kUri, 6); - rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri, 7); - rtp_sender_->RegisterRtpHeaderExtension(ColorSpaceExtension::kUri, 8); + rtp_sender_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), 3); + rtp_sender_->RegisterRtpHeaderExtension(PlayoutDelayLimits::Uri(), 4); + rtp_sender_->RegisterRtpHeaderExtension(VideoContentTypeExtension::Uri(), 5); + rtp_sender_->RegisterRtpHeaderExtension(VideoTimingExtension::Uri(), 6); + rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), 7); + rtp_sender_->RegisterRtpHeaderExtension(ColorSpaceExtension::Uri(), 8); // Still only 12B counted since can't count on above being sent. EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); @@ -957,8 +995,7 @@ TEST_F(RtpSenderTest, SendPacketHandlesRetransmissionHistory) { BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds()); const uint16_t media_sequence_number = packet->SequenceNumber(); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Simulate successful retransmission request. time_controller_.AdvanceTime(TimeDelta::Millis(30)); @@ -985,8 +1022,7 @@ TEST_F(RtpSenderTest, MarksRetransmittedPackets) { BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds()); const uint16_t media_sequence_number = packet->SequenceNumber(); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Expect a retransmission packet marked with which packet it is a // retransmit of. @@ -1006,11 +1042,11 @@ TEST_F(RtpSenderTest, GeneratedPaddingHasBweExtensions) { EnableRtx(); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId)); + TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId)); + AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); // Put a packet in the history, in order to facilitate payload padding. std::unique_ptr packet = @@ -1018,8 +1054,7 @@ TEST_F(RtpSenderTest, GeneratedPaddingHasBweExtensions) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kMinPaddingSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Generate a plain padding packet, check that extensions are registered. std::vector> generated_packets = @@ -1047,13 +1082,13 @@ TEST_F(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { // Min requested size in order to use RTX payload. const size_t kMinPaddingSize = 50; - rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); + rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); packet_history_->SetStorePacketsStatus( RtpPacketHistory::StorageMode::kStoreAndCull, 1); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); const size_t kPayloadPacketSize = kMinPaddingSize; std::unique_ptr packet = @@ -1061,8 +1096,7 @@ TEST_F(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Generated padding has large enough budget that the video packet should be // retransmitted as padding. @@ -1097,13 +1131,13 @@ TEST_F(RtpSenderTest, LimitsPayloadPaddingSize) { const double kFactor = 2.0; field_trials_.SetMaxPaddingFactor(kFactor); SetUpRtpSender(false, false, nullptr); - rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); + rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); packet_history_->SetStorePacketsStatus( RtpPacketHistory::StorageMode::kStoreAndCull, 1); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); // Send a dummy video packet so it ends up in the packet history. const size_t kPayloadPacketSize = 1234u; @@ -1112,8 +1146,7 @@ TEST_F(RtpSenderTest, LimitsPayloadPaddingSize) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Smallest target size that will result in the sent packet being returned as // padding. @@ -1139,11 +1172,11 @@ TEST_F(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { packet_history_->SetStorePacketsStatus( RtpPacketHistory::StorageMode::kStoreAndCull, 1); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId)); + TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId)); + AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( - TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId)); + TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); const size_t kPayloadPacketSize = 1234; // Send a dummy video packet so it ends up in the packet history. Since we @@ -1154,8 +1187,7 @@ TEST_F(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Payload padding not available without RTX, only generate plain padding on // the media SSRC. @@ -1188,8 +1220,8 @@ TEST_F(RtpSenderTest, SupportsPadding) { bool kSendingMediaStats[] = {true, false}; bool kEnableRedundantPayloads[] = {true, false}; absl::string_view kBweExtensionUris[] = { - TransportSequenceNumber::kUri, TransportSequenceNumberV2::kUri, - AbsoluteSendTime::kUri, TransmissionOffset::kUri}; + TransportSequenceNumber::Uri(), TransportSequenceNumberV2::Uri(), + AbsoluteSendTime::Uri(), TransmissionOffset::Uri()}; const int kExtensionsId = 7; for (bool sending_media : kSendingMediaStats) { @@ -1199,6 +1231,7 @@ TEST_F(RtpSenderTest, SupportsPadding) { if (redundant_payloads) { rtx_mode |= kRtxRedundantPayloads; } + rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); rtp_sender_->SetRtxStatus(rtx_mode); for (auto extension_uri : kBweExtensionUris) { @@ -1231,20 +1264,22 @@ TEST_F(RtpSenderTest, SetsCaptureTimeOnRtxRetransmissions) { /*capture_time_ms=*/start_time_ms); packet->set_allow_retransmission(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Advance time, request an RTX retransmission. Capture timestamp should be // preserved. time_controller_.AdvanceTime(TimeDelta::Millis(10)); - EXPECT_CALL(mock_paced_sender_, - EnqueuePackets(ElementsAre(Pointee(Property( - &RtpPacketToSend::capture_time_ms, start_time_ms))))); + EXPECT_CALL( + mock_paced_sender_, + EnqueuePackets(ElementsAre(Pointee(Property( + &RtpPacketToSend::capture_time, Timestamp::Millis(start_time_ms)))))); EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); } TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) { - const int64_t kRtt = 10; + const TimeDelta kRtt = TimeDelta::Millis(10); EnableRtx(); packet_history_->SetRtt(kRtt); @@ -1256,18 +1291,19 @@ TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) { /*capture_time_ms=*/start_time_ms); packet->set_allow_retransmission(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Disable media sending and try to retransmit the packet, it should fail. rtp_sender_->SetSendingMediaStatus(false); - time_controller_.AdvanceTime(TimeDelta::Millis(kRtt)); + time_controller_.AdvanceTime(kRtt); EXPECT_LT(rtp_sender_->ReSendPacket(kSeqNum), 0); } TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { // Set up retranmission without RTX, so that a plain copy of the old packet is // re-sent instead. - const int64_t kRtt = 10; + const TimeDelta kRtt = TimeDelta::Millis(10); rtp_sender_->SetSendingMediaStatus(true); rtp_sender_->SetRtxStatus(kRtxOff); packet_history_->SetStorePacketsStatus( @@ -1282,7 +1318,8 @@ TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { packet->set_allow_retransmission(true); packet->set_fec_protect_packet(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Re-send packet, the retransmitted packet should not have the FEC protection // flag set. @@ -1290,7 +1327,7 @@ TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { EnqueuePackets(ElementsAre(Pointee( Property(&RtpPacketToSend::fec_protect_packet, false))))); - time_controller_.AdvanceTime(TimeDelta::Millis(kRtt)); + time_controller_.AdvanceTime(kRtt); EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); } diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index b8d9b65546..6d6ba33600 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -107,7 +107,7 @@ const char* FrameTypeToString(VideoFrameType frame_type) { case VideoFrameType::kVideoFrameDelta: return "video_delta"; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } } @@ -175,9 +175,9 @@ RTPSenderVideo::RTPSenderVideo(const Config& config) rtp_sender_->SSRC(), config.send_transport_queue) : nullptr), - include_capture_clock_offset_(absl::StartsWith( + include_capture_clock_offset_(!absl::StartsWith( config.field_trials->Lookup(kIncludeCaptureClockOffset), - "Enabled")) { + "Disabled")) { if (frame_transformer_delegate_) frame_transformer_delegate_->Init(); } @@ -292,21 +292,33 @@ void RTPSenderVideo::SetVideoLayersAllocationAfterTransformation( void RTPSenderVideo::SetVideoLayersAllocationInternal( VideoLayersAllocation allocation) { RTC_DCHECK_RUNS_SERIALIZED(&send_checker_); - if (!allocation_ || allocation.active_spatial_layers.size() > + if (!allocation_ || allocation.active_spatial_layers.size() != allocation_->active_spatial_layers.size()) { send_allocation_ = SendVideoLayersAllocation::kSendWithResolution; } else if (send_allocation_ == SendVideoLayersAllocation::kDontSend) { send_allocation_ = SendVideoLayersAllocation::kSendWithoutResolution; } + if (send_allocation_ == SendVideoLayersAllocation::kSendWithoutResolution) { + // Check if frame rate changed more than 5fps since the last time the + // extension was sent with frame rate and resolution. + for (size_t i = 0; i < allocation.active_spatial_layers.size(); ++i) { + if (abs(static_cast( + allocation.active_spatial_layers[i].frame_rate_fps) - + static_cast( + last_full_sent_allocation_->active_spatial_layers[i] + .frame_rate_fps)) > 5) { + send_allocation_ = SendVideoLayersAllocation::kSendWithResolution; + break; + } + } + } allocation_ = std::move(allocation); } -void RTPSenderVideo::AddRtpHeaderExtensions( - const RTPVideoHeader& video_header, - const absl::optional& absolute_capture_time, - bool first_packet, - bool last_packet, - RtpPacketToSend* packet) const { +void RTPSenderVideo::AddRtpHeaderExtensions(const RTPVideoHeader& video_header, + bool first_packet, + bool last_packet, + RtpPacketToSend* packet) const { // Send color space when changed or if the frame is a key frame. Keep // sending color space information until the first base layer frame to // guarantee that the information is retrieved by the receiver. @@ -355,8 +367,9 @@ void RTPSenderVideo::AddRtpHeaderExtensions( packet->SetExtension(current_playout_delay_); } - if (first_packet && absolute_capture_time) { - packet->SetExtension(*absolute_capture_time); + if (first_packet && video_header.absolute_capture_time.has_value()) { + packet->SetExtension( + *video_header.absolute_capture_time); } if (video_header.generic) { @@ -463,8 +476,7 @@ bool RTPSenderVideo::SendVideo( int64_t capture_time_ms, rtc::ArrayView payload, RTPVideoHeader video_header, - absl::optional expected_retransmission_time_ms, - absl::optional estimated_capture_clock_offset_ms) { + absl::optional expected_retransmission_time_ms) { #if RTC_TRACE_EVENTS_ENABLED TRACE_EVENT_ASYNC_STEP1("webrtc", "Video", capture_time_ms, "Send", "type", FrameTypeToString(video_header.frame_type)); @@ -522,24 +534,33 @@ bool RTPSenderVideo::SendVideo( RTC_DCHECK_LE(packet_capacity, single_packet->capacity()); single_packet->SetPayloadType(payload_type); single_packet->SetTimestamp(rtp_timestamp); - single_packet->set_capture_time_ms(capture_time_ms); + single_packet->set_capture_time(Timestamp::Millis(capture_time_ms)); - const absl::optional absolute_capture_time = + // Construct the absolute capture time extension if not provided. + if (!video_header.absolute_capture_time.has_value()) { + video_header.absolute_capture_time.emplace(); + video_header.absolute_capture_time->absolute_capture_timestamp = + Int64MsToUQ32x32( + clock_->ConvertTimestampToNtpTimeInMilliseconds(capture_time_ms)); + if (include_capture_clock_offset_) { + video_header.absolute_capture_time->estimated_capture_clock_offset = 0; + } + } + + // Let `absolute_capture_time_sender_` decide if the extension should be sent. + video_header.absolute_capture_time = absolute_capture_time_sender_.OnSendPacket( AbsoluteCaptureTimeSender::GetSource(single_packet->Ssrc(), single_packet->Csrcs()), single_packet->Timestamp(), kVideoPayloadTypeFrequency, - Int64MsToUQ32x32( - clock_->ConvertTimestampToNtpTimeInMilliseconds(capture_time_ms)), - /*estimated_capture_clock_offset=*/ - include_capture_clock_offset_ ? estimated_capture_clock_offset_ms - : absl::nullopt); + video_header.absolute_capture_time->absolute_capture_timestamp, + video_header.absolute_capture_time->estimated_capture_clock_offset); auto first_packet = std::make_unique(*single_packet); auto middle_packet = std::make_unique(*single_packet); auto last_packet = std::make_unique(*single_packet); // Simplest way to estimate how much extensions would occupy is to set them. - AddRtpHeaderExtensions(video_header, absolute_capture_time, + AddRtpHeaderExtensions(video_header, /*first_packet=*/true, /*last_packet=*/true, single_packet.get()); if (video_structure_ != nullptr && @@ -554,13 +575,13 @@ bool RTPSenderVideo::SendVideo( video_structure_ = nullptr; } - AddRtpHeaderExtensions(video_header, absolute_capture_time, + AddRtpHeaderExtensions(video_header, /*first_packet=*/true, /*last_packet=*/false, first_packet.get()); - AddRtpHeaderExtensions(video_header, absolute_capture_time, + AddRtpHeaderExtensions(video_header, /*first_packet=*/false, /*last_packet=*/false, middle_packet.get()); - AddRtpHeaderExtensions(video_header, absolute_capture_time, + AddRtpHeaderExtensions(video_header, /*first_packet=*/false, /*last_packet=*/true, last_packet.get()); @@ -674,7 +695,7 @@ bool RTPSenderVideo::SendVideo( // Put packetization finish timestamp into extension. if (packet->HasExtension()) { - packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds()); + packet->set_packetization_finish_time(clock_->CurrentTime()); } packet->set_fec_protect_packet(use_fec); @@ -726,6 +747,9 @@ bool RTPSenderVideo::SendVideo( // This frame will likely be delivered, no need to populate playout // delay extensions until it changes again. playout_delay_pending_ = false; + if (send_allocation_ == SendVideoLayersAllocation::kSendWithResolution) { + last_full_sent_allocation_ = allocation_; + } send_allocation_ = SendVideoLayersAllocation::kDontSend; } diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index 226c4062a1..5164969489 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -90,20 +90,14 @@ class RTPSenderVideo { // expected_retransmission_time_ms.has_value() -> retransmission allowed. // `capture_time_ms` and `clock::CurrentTime` should be using the same epoch. - // Calls to this method is assumed to be externally serialized. - // `estimated_capture_clock_offset_ms` is an estimated clock offset between - // this sender and the original capturer, for this video packet. See - // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time for more - // details. If the sender and the capture has the same clock, it is supposed - // to be zero valued, which is given as the default. + // Calls to this method are assumed to be externally serialized. bool SendVideo(int payload_type, absl::optional codec_type, uint32_t rtp_timestamp, int64_t capture_time_ms, rtc::ArrayView payload, RTPVideoHeader video_header, - absl::optional expected_retransmission_time_ms, - absl::optional estimated_capture_clock_offset_ms = 0); + absl::optional expected_retransmission_time_ms); bool SendEncodedImage( int payload_type, @@ -170,12 +164,10 @@ class RTPSenderVideo { const FrameDependencyStructure* video_structure); void SetVideoLayersAllocationInternal(VideoLayersAllocation allocation); - void AddRtpHeaderExtensions( - const RTPVideoHeader& video_header, - const absl::optional& absolute_capture_time, - bool first_packet, - bool last_packet, - RtpPacketToSend* packet) const + void AddRtpHeaderExtensions(const RTPVideoHeader& video_header, + bool first_packet, + bool last_packet, + RtpPacketToSend* packet) const RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_); size_t FecPacketOverhead() const RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_); @@ -210,6 +202,8 @@ class RTPSenderVideo { RTC_GUARDED_BY(send_checker_); // Flag indicating if we should send `allocation_`. SendVideoLayersAllocation send_allocation_ RTC_GUARDED_BY(send_checker_); + absl::optional last_full_sent_allocation_ + RTC_GUARDED_BY(send_checker_); // Current target playout delay. VideoPlayoutDelay current_playout_delay_ RTC_GUARDED_BY(send_checker_); diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc index 377f6c4fbf..c3295c68b9 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc @@ -139,7 +139,7 @@ void RTPSenderVideoFrameTransformerDelegate::OnTransformedFrame( // arrives. if (!sender_ || !encoder_queue_) return; - rtc::scoped_refptr delegate = this; + rtc::scoped_refptr delegate(this); encoder_queue_->PostTask(ToQueuedTask( [delegate = std::move(delegate), frame = std::move(frame)]() mutable { delegate->SendVideo(std::move(frame)); diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc index 3e803fff1f..dc845e4d24 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc @@ -16,6 +16,7 @@ #include #include "absl/memory/memory.h" +#include "api/rtp_headers.h" #include "api/test/mock_frame_encryptor.h" #include "api/transport/field_trial_based_config.h" #include "api/transport/rtp/dependency_descriptor.h" @@ -159,7 +160,7 @@ class FieldTrials : public WebRtcKeyValueConfig { if (key == "WebRTC-SendSideBwe-WithOverhead") { return use_send_side_bwe_with_overhead_ ? "Enabled" : ""; } else if (key == "WebRTC-IncludeCaptureClockOffset") { - return include_capture_clock_offset_ ? "Enabled" : ""; + return include_capture_clock_offset_ ? "" : "Disabled"; } return ""; } @@ -207,7 +208,7 @@ class RtpSenderVideoTest : public ::testing::TestWithParam { TEST_P(RtpSenderVideoTest, KeyFrameHasCVO) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::kUri, + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), kVideoRotationExtensionId); RTPVideoHeader hdr; @@ -227,7 +228,7 @@ TEST_P(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) { const int64_t kPacketizationTimeMs = 100; const int64_t kEncodeStartDeltaMs = 10; const int64_t kEncodeFinishDeltaMs = 50; - rtp_module_->RegisterRtpHeaderExtension(VideoTimingExtension::kUri, + rtp_module_->RegisterRtpHeaderExtension(VideoTimingExtension::Uri(), kVideoTimingExtensionId); const int64_t kCaptureTimestamp = fake_clock_.TimeInMilliseconds(); @@ -252,7 +253,7 @@ TEST_P(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) { TEST_P(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::kUri, + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), kVideoRotationExtensionId); RTPVideoHeader hdr; @@ -276,7 +277,7 @@ TEST_P(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) { TEST_P(RtpSenderVideoTest, DeltaFrameHasCVOWhenNonZero) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::kUri, + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), kVideoRotationExtensionId); RTPVideoHeader hdr; @@ -508,7 +509,7 @@ TEST_P(RtpSenderVideoTest, SendsDependencyDescriptorWhenVideoStructureIsSet) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; video_structure.templates = { @@ -577,7 +578,7 @@ TEST_P(RtpSenderVideoTest, const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); rtp_module_->SetExtmapAllowMixed(false); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; @@ -630,7 +631,7 @@ TEST_P(RtpSenderVideoTest, PropagatesChainDiffsIntoDependencyDescriptor) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; video_structure.num_chains = 1; @@ -664,7 +665,7 @@ TEST_P(RtpSenderVideoTest, const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; video_structure.num_chains = 1; @@ -698,7 +699,7 @@ TEST_P(RtpSenderVideoTest, const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure1; video_structure1.num_decode_targets = 2; video_structure1.templates = { @@ -773,9 +774,8 @@ TEST_P(RtpSenderVideoTest, uint8_t kFrame[kFrameSize] = {1, 2, 3, 4}; rtp_module_->RegisterRtpHeaderExtension( - RtpDependencyDescriptorExtension::kUri, kDependencyDescriptorId); - rtc::scoped_refptr encryptor( - new rtc::RefCountedObject>); + RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); + auto encryptor = rtc::make_ref_counted>(); ON_CALL(*encryptor, GetMaxCiphertextByteSize).WillByDefault(ReturnArg<1>()); ON_CALL(*encryptor, Encrypt) .WillByDefault(WithArgs<3, 5>( @@ -815,7 +815,7 @@ TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; rtp_module_->RegisterRtpHeaderExtension( - RtpGenericFrameDescriptorExtension00::kUri, kGenericDescriptorId); + RtpGenericFrameDescriptorExtension00::Uri(), kGenericDescriptorId); RTPVideoHeader hdr; RTPVideoHeader::GenericDescriptorInfo& generic = hdr.generic.emplace(); @@ -847,7 +847,7 @@ void RtpSenderVideoTest:: uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpGenericFrameDescriptorExtension00::kUri, kGenericDescriptorId); + RtpGenericFrameDescriptorExtension00::Uri(), kGenericDescriptorId); RTPVideoHeader hdr; hdr.codec = kVideoCodecVP8; @@ -882,7 +882,7 @@ TEST_P(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnKeyFrames) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpVideoLayersAllocationExtension::kUri, + RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); VideoLayersAllocation allocation; @@ -919,7 +919,7 @@ TEST_P(RtpSenderVideoTest, const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpVideoLayersAllocationExtension::kUri, + RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); VideoLayersAllocation allocation; @@ -967,7 +967,7 @@ TEST_P(RtpSenderVideoTest, const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpVideoLayersAllocationExtension::kUri, + RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); VideoLayersAllocation allocation; @@ -1008,11 +1008,94 @@ TEST_P(RtpSenderVideoTest, EXPECT_TRUE(sent_allocation.resolution_and_frame_rate_is_valid); } +TEST_P(RtpSenderVideoTest, + VideoLayersAllocationWithResolutionSentOnLargeFrameRateChange) { + const size_t kFrameSize = 100; + uint8_t kFrame[kFrameSize]; + rtp_module_->RegisterRtpHeaderExtension( + RtpVideoLayersAllocationExtension::Uri(), + kVideoLayersAllocationExtensionId); + + VideoLayersAllocation allocation; + allocation.resolution_and_frame_rate_is_valid = true; + VideoLayersAllocation::SpatialLayer layer; + layer.width = 360; + layer.height = 180; + layer.spatial_id = 0; + layer.frame_rate_fps = 10; + layer.target_bitrate_per_temporal_layer.push_back( + DataRate::KilobitsPerSec(50)); + allocation.active_spatial_layers.push_back(layer); + rtp_sender_video_->SetVideoLayersAllocation(allocation); + + RTPVideoHeader hdr; + hdr.frame_type = VideoFrameType::kVideoFrameKey; + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + ASSERT_TRUE(transport_.last_sent_packet() + .HasExtension()); + + // Update frame rate only. + allocation.active_spatial_layers[0].frame_rate_fps = 20; + rtp_sender_video_->SetVideoLayersAllocation(allocation); + hdr.frame_type = VideoFrameType::kVideoFrameDelta; + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + + VideoLayersAllocation sent_allocation; + EXPECT_TRUE( + transport_.last_sent_packet() + .GetExtension(&sent_allocation)); + ASSERT_TRUE(sent_allocation.resolution_and_frame_rate_is_valid); + EXPECT_EQ(sent_allocation.active_spatial_layers[0].frame_rate_fps, 20); +} + +TEST_P(RtpSenderVideoTest, + VideoLayersAllocationWithoutResolutionSentOnSmallFrameRateChange) { + const size_t kFrameSize = 100; + uint8_t kFrame[kFrameSize]; + rtp_module_->RegisterRtpHeaderExtension( + RtpVideoLayersAllocationExtension::Uri(), + kVideoLayersAllocationExtensionId); + + VideoLayersAllocation allocation; + allocation.resolution_and_frame_rate_is_valid = true; + VideoLayersAllocation::SpatialLayer layer; + layer.width = 360; + layer.height = 180; + layer.spatial_id = 0; + layer.frame_rate_fps = 10; + layer.target_bitrate_per_temporal_layer.push_back( + DataRate::KilobitsPerSec(50)); + allocation.active_spatial_layers.push_back(layer); + rtp_sender_video_->SetVideoLayersAllocation(allocation); + + RTPVideoHeader hdr; + hdr.frame_type = VideoFrameType::kVideoFrameKey; + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + ASSERT_TRUE(transport_.last_sent_packet() + .HasExtension()); + + // Update frame rate slightly. + allocation.active_spatial_layers[0].frame_rate_fps = 9; + rtp_sender_video_->SetVideoLayersAllocation(allocation); + hdr.frame_type = VideoFrameType::kVideoFrameDelta; + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + + VideoLayersAllocation sent_allocation; + EXPECT_TRUE( + transport_.last_sent_packet() + .GetExtension(&sent_allocation)); + EXPECT_FALSE(sent_allocation.resolution_and_frame_rate_is_valid); +} + TEST_P(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpVideoLayersAllocationExtension::kUri, + RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); VideoLayersAllocation allocation; @@ -1054,7 +1137,7 @@ TEST_P(RtpSenderVideoTest, VideoLayersAllocationNotSentOnHigherTemporalLayers) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; rtp_module_->RegisterRtpHeaderExtension( - RtpVideoLayersAllocationExtension::kUri, + RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); VideoLayersAllocation allocation; @@ -1089,7 +1172,7 @@ TEST_P(RtpSenderVideoTest, VideoLayersAllocationNotSentOnHigherTemporalLayers) { TEST_P(RtpSenderVideoTest, AbsoluteCaptureTime) { constexpr int64_t kAbsoluteCaptureTimestampMs = 12345678; uint8_t kFrame[kMaxPacketLength]; - rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), kAbsoluteCaptureTimeExtensionId); RTPVideoHeader hdr; @@ -1098,24 +1181,27 @@ TEST_P(RtpSenderVideoTest, AbsoluteCaptureTime) { kAbsoluteCaptureTimestampMs, kFrame, hdr, kDefaultExpectedRetransmissionTimeMs); + absl::optional absolute_capture_time; + // It is expected that one and only one of the packets sent on this video - // frame has absolute capture time header extension. And no absolute capture - // time header extensions include capture clock offset. - int packets_with_abs_capture_time = 0; + // frame has absolute capture time header extension. for (const RtpPacketReceived& packet : transport_.sent_packets()) { - auto absolute_capture_time = - packet.GetExtension(); - if (absolute_capture_time) { - ++packets_with_abs_capture_time; - EXPECT_EQ( - absolute_capture_time->absolute_capture_timestamp, - Int64MsToUQ32x32(fake_clock_.ConvertTimestampToNtpTimeInMilliseconds( - kAbsoluteCaptureTimestampMs))); - EXPECT_FALSE( - absolute_capture_time->estimated_capture_clock_offset.has_value()); + if (absolute_capture_time.has_value()) { + EXPECT_FALSE(packet.HasExtension()); + } else { + absolute_capture_time = + packet.GetExtension(); } } - EXPECT_EQ(packets_with_abs_capture_time, 1); + + // Verify the capture timestamp and that the clock offset is not set. + ASSERT_TRUE(absolute_capture_time.has_value()); + EXPECT_EQ( + absolute_capture_time->absolute_capture_timestamp, + Int64MsToUQ32x32(fake_clock_.ConvertTimestampToNtpTimeInMilliseconds( + kAbsoluteCaptureTimestampMs))); + EXPECT_FALSE( + absolute_capture_time->estimated_capture_clock_offset.has_value()); } // Essentially the same test as AbsoluteCaptureTime but with a field trial. @@ -1127,42 +1213,75 @@ TEST_P(RtpSenderVideoTest, AbsoluteCaptureTimeWithCaptureClockOffset) { constexpr int64_t kAbsoluteCaptureTimestampMs = 12345678; uint8_t kFrame[kMaxPacketLength]; - rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), kAbsoluteCaptureTimeExtensionId); RTPVideoHeader hdr; - const absl::optional kExpectedCaptureClockOffset = - absl::make_optional(1234); hdr.frame_type = VideoFrameType::kVideoFrameKey; - rtp_sender_video_->SendVideo( - kPayload, kType, kTimestamp, kAbsoluteCaptureTimestampMs, kFrame, hdr, - kDefaultExpectedRetransmissionTimeMs, kExpectedCaptureClockOffset); + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, + kAbsoluteCaptureTimestampMs, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + + absl::optional absolute_capture_time; // It is expected that one and only one of the packets sent on this video - // frame has absolute capture time header extension. And it includes capture - // clock offset. - int packets_with_abs_capture_time = 0; + // frame has absolute capture time header extension. for (const RtpPacketReceived& packet : transport_.sent_packets()) { - auto absolute_capture_time = - packet.GetExtension(); - if (absolute_capture_time) { - ++packets_with_abs_capture_time; - EXPECT_EQ( - absolute_capture_time->absolute_capture_timestamp, - Int64MsToUQ32x32(fake_clock_.ConvertTimestampToNtpTimeInMilliseconds( - kAbsoluteCaptureTimestampMs))); - EXPECT_EQ(kExpectedCaptureClockOffset, - absolute_capture_time->estimated_capture_clock_offset); + if (absolute_capture_time.has_value()) { + EXPECT_FALSE(packet.HasExtension()); + } else { + absolute_capture_time = + packet.GetExtension(); } } - EXPECT_EQ(packets_with_abs_capture_time, 1); + + // Verify the capture timestamp and that the clock offset is set to zero. + ASSERT_TRUE(absolute_capture_time.has_value()); + EXPECT_EQ( + absolute_capture_time->absolute_capture_timestamp, + Int64MsToUQ32x32(fake_clock_.ConvertTimestampToNtpTimeInMilliseconds( + kAbsoluteCaptureTimestampMs))); + EXPECT_EQ(absolute_capture_time->estimated_capture_clock_offset, 0); +} + +TEST_P(RtpSenderVideoTest, AbsoluteCaptureTimeWithExtensionProvided) { + constexpr AbsoluteCaptureTime kAbsoluteCaptureTime = { + 123, + absl::optional(456), + }; + uint8_t kFrame[kMaxPacketLength]; + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), + kAbsoluteCaptureTimeExtensionId); + + RTPVideoHeader hdr; + hdr.frame_type = VideoFrameType::kVideoFrameKey; + hdr.absolute_capture_time = kAbsoluteCaptureTime; + rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, + /*capture_time_ms=*/789, kFrame, hdr, + kDefaultExpectedRetransmissionTimeMs); + + absl::optional absolute_capture_time; + + // It is expected that one and only one of the packets sent on this video + // frame has absolute capture time header extension. + for (const RtpPacketReceived& packet : transport_.sent_packets()) { + if (absolute_capture_time.has_value()) { + EXPECT_FALSE(packet.HasExtension()); + } else { + absolute_capture_time = + packet.GetExtension(); + } + } + + // Verify the extension. + EXPECT_EQ(absolute_capture_time, kAbsoluteCaptureTime); } TEST_P(RtpSenderVideoTest, PopulatesPlayoutDelay) { // Single packet frames. constexpr size_t kPacketSize = 123; uint8_t kFrame[kPacketSize]; - rtp_module_->RegisterRtpHeaderExtension(PlayoutDelayLimits::kUri, + rtp_module_->RegisterRtpHeaderExtension(PlayoutDelayLimits::Uri(), kPlayoutDelayExtensionId); const VideoPlayoutDelay kExpectedDelay = {10, 20}; @@ -1314,8 +1433,8 @@ std::unique_ptr CreateDefaultEncodedImage() { TEST_F(RtpSenderVideoWithFrameTransformerTest, CreateSenderRegistersFrameTransformer) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameSinkCallback(_, kSsrc)); std::unique_ptr rtp_sender_video = @@ -1324,8 +1443,8 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, TEST_F(RtpSenderVideoWithFrameTransformerTest, DestroySenderUnregistersFrameTransformer) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); std::unique_ptr rtp_sender_video = CreateSenderWithFrameTransformer(mock_frame_transformer); EXPECT_CALL(*mock_frame_transformer, @@ -1335,8 +1454,8 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, TEST_F(RtpSenderVideoWithFrameTransformerTest, SendEncodedImageTransformsFrame) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); std::unique_ptr rtp_sender_video = CreateSenderWithFrameTransformer(mock_frame_transformer); auto encoded_image = CreateDefaultEncodedImage(); @@ -1350,8 +1469,8 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST_F(RtpSenderVideoWithFrameTransformerTest, ValidPayloadTypes) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); std::unique_ptr rtp_sender_video = CreateSenderWithFrameTransformer(mock_frame_transformer); auto encoded_image = CreateDefaultEncodedImage(); @@ -1375,8 +1494,8 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, ValidPayloadTypes) { #endif TEST_F(RtpSenderVideoWithFrameTransformerTest, OnTransformedFrameSendsVideo) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); rtc::scoped_refptr callback; EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameSinkCallback) .WillOnce(SaveArg<0>(&callback)); @@ -1406,8 +1525,8 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, OnTransformedFrameSendsVideo) { TEST_F(RtpSenderVideoWithFrameTransformerTest, TransformableFrameMetadataHasCorrectValue) { - rtc::scoped_refptr mock_frame_transformer = - new rtc::RefCountedObject>(); + auto mock_frame_transformer = + rtc::make_ref_counted>(); std::unique_ptr rtp_sender_video = CreateSenderWithFrameTransformer(mock_frame_transformer); auto encoded_image = CreateDefaultEncodedImage(); diff --git a/modules/rtp_rtcp/source/rtp_video_header.h b/modules/rtp_rtcp/source/rtp_video_header.h index c1be76fa4c..115b17d36d 100644 --- a/modules/rtp_rtcp/source/rtp_video_header.h +++ b/modules/rtp_rtcp/source/rtp_video_header.h @@ -16,6 +16,7 @@ #include "absl/container/inlined_vector.h" #include "absl/types/optional.h" #include "absl/types/variant.h" +#include "api/rtp_headers.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/video/color_space.h" #include "api/video/video_codec_type.h" @@ -81,6 +82,11 @@ struct RTPVideoHeader { // carries the webrtc::VideoFrame id field from the sender to the receiver. absl::optional video_frame_tracking_id; RTPVideoTypeHeader video_type_header; + + // When provided, is sent as is as an RTP header extension according to + // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time. + // Otherwise, it is derived from other relevant information. + absl::optional absolute_capture_time; }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc index 234ac31b8b..6816a6277f 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc @@ -21,7 +21,6 @@ namespace webrtc { constexpr RTPExtensionType RtpVideoLayersAllocationExtension::kId; -constexpr const char RtpVideoLayersAllocationExtension::kUri[]; namespace { @@ -289,7 +288,7 @@ bool RtpVideoLayersAllocationExtension::Parse( if (data.size() == 1 && *read_at == 0) { allocation->rtp_stream_index = 0; allocation->resolution_and_frame_rate_is_valid = true; - return true; + return AllocationIsValid(*allocation); } // Header byte. @@ -366,7 +365,7 @@ bool RtpVideoLayersAllocationExtension::Parse( if (read_at == end) { allocation->resolution_and_frame_rate_is_valid = false; - return true; + return AllocationIsValid(*allocation); } if (read_at + 5 * allocation->active_spatial_layers.size() != end) { @@ -383,7 +382,8 @@ bool RtpVideoLayersAllocationExtension::Parse( layer.frame_rate_fps = *read_at; ++read_at; } - return true; + + return AllocationIsValid(*allocation); } size_t RtpVideoLayersAllocationExtension::ValueSize( diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h index ff8ea2a7a2..3f1603bcd8 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h @@ -11,6 +11,8 @@ #ifndef MODULES_RTP_RTCP_SOURCE_RTP_VIDEO_LAYERS_ALLOCATION_EXTENSION_H_ #define MODULES_RTP_RTCP_SOURCE_RTP_VIDEO_LAYERS_ALLOCATION_EXTENSION_H_ +#include "absl/strings/string_view.h" +#include "api/rtp_parameters.h" #include "api/video/video_layers_allocation.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -22,8 +24,10 @@ class RtpVideoLayersAllocationExtension { public: using value_type = VideoLayersAllocation; static constexpr RTPExtensionType kId = kRtpExtensionVideoLayersAllocation; - static constexpr const char kUri[] = - "http://www.webrtc.org/experiments/rtp-hdrext/video-layers-allocation00"; + static constexpr absl::string_view Uri() { + return RtpExtension::kVideoLayersAllocationUri; + } + static bool Parse(rtc::ArrayView data, VideoLayersAllocation* allocation); static size_t ValueSize(const VideoLayersAllocation& allocation); diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc index 17b4c4cfa6..db077409ee 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc @@ -256,5 +256,32 @@ TEST(RtpVideoLayersAllocationExtension, DiscardsOverLargeDataRate) { EXPECT_FALSE(RtpVideoLayersAllocationExtension::Parse(buffer, &allocation)); } +TEST(RtpVideoLayersAllocationExtension, DiscardsInvalidHeight) { + VideoLayersAllocation written_allocation; + written_allocation.rtp_stream_index = 0; + written_allocation.resolution_and_frame_rate_is_valid = true; + written_allocation.active_spatial_layers = { + { + /*rtp_stream_index*/ 0, + /*spatial_id*/ 0, + /*target_bitrate_per_temporal_layer*/ + {DataRate::KilobitsPerSec(25), DataRate::KilobitsPerSec(50)}, + /*width*/ 320, + /*height*/ 240, + /*frame_rate_fps*/ 8, + }, + }; + rtc::Buffer buffer( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); + ASSERT_TRUE( + RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + + // Modify the height to be invalid. + buffer[buffer.size() - 3] = 0xff; + buffer[buffer.size() - 2] = 0xff; + VideoLayersAllocation allocation; + EXPECT_FALSE(RtpVideoLayersAllocationExtension::Parse(buffer, &allocation)); +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc index af2ed0c183..5c41b48cc4 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc @@ -191,14 +191,15 @@ VectorObuInfo ParseObus( reinterpret_cast(rtp_payload.data()), rtp_payload.size()); uint8_t aggregation_header; if (!payload.ReadUInt8(&aggregation_header)) { - RTC_DLOG(WARNING) << "Failed to find aggregation header in the packet."; + RTC_DLOG(LS_WARNING) + << "Failed to find aggregation header in the packet."; return {}; } // Z-bit: 1 if the first OBU contained in the packet is a continuation of a // previous OBU. bool continues_obu = RtpStartsWithFragment(aggregation_header); if (continues_obu != expect_continues_obu) { - RTC_DLOG(WARNING) << "Unexpected Z-bit " << continues_obu; + RTC_DLOG(LS_WARNING) << "Unexpected Z-bit " << continues_obu; return {}; } int num_expected_obus = RtpNumObus(aggregation_header); @@ -206,7 +207,8 @@ VectorObuInfo ParseObus( // rtp packet has just the aggregation header. That may be valid only when // there is exactly one fragment in the packet of size 0. if (num_expected_obus != 1) { - RTC_DLOG(WARNING) << "Invalid packet with just an aggregation header."; + RTC_DLOG(LS_WARNING) + << "Invalid packet with just an aggregation header."; return {}; } if (!continues_obu) { @@ -228,16 +230,16 @@ VectorObuInfo ParseObus( bool has_fragment_size = (obu_index != num_expected_obus); if (has_fragment_size) { if (!payload.ReadUVarint(&fragment_size)) { - RTC_DLOG(WARNING) << "Failed to read fragment size for obu #" - << obu_index << "/" << num_expected_obus; + RTC_DLOG(LS_WARNING) << "Failed to read fragment size for obu #" + << obu_index << "/" << num_expected_obus; return {}; } if (fragment_size > payload.Length()) { // Malformed input: written size is larger than remaining buffer. - RTC_DLOG(WARNING) << "Malformed fragment size " << fragment_size - << " is larger than remaining size " - << payload.Length() << " while reading obu #" - << obu_index << "/" << num_expected_obus; + RTC_DLOG(LS_WARNING) << "Malformed fragment size " << fragment_size + << " is larger than remaining size " + << payload.Length() << " while reading obu #" + << obu_index << "/" << num_expected_obus; return {}; } } else { @@ -254,7 +256,7 @@ VectorObuInfo ParseObus( expect_continues_obu = RtpEndsWithFragment(aggregation_header); } if (expect_continues_obu) { - RTC_DLOG(WARNING) << "Last packet shouldn't have last obu fragmented."; + RTC_DLOG(LS_WARNING) << "Last packet shouldn't have last obu fragmented."; return {}; } return obu_infos; @@ -278,7 +280,7 @@ int WriteLeb128(uint32_t value, uint8_t* buffer) { // Returns false if obu found to be misformed. bool CalculateObuSizes(ObuInfo* obu_info) { if (obu_info->data.empty()) { - RTC_DLOG(WARNING) << "Invalid bitstream: empty obu provided."; + RTC_DLOG(LS_WARNING) << "Invalid bitstream: empty obu provided."; return false; } auto it = obu_info->data.begin(); @@ -305,7 +307,7 @@ bool CalculateObuSizes(ObuInfo* obu_info) { uint8_t leb128_byte; do { if (it == obu_info->data.end() || size_of_obu_size_bytes >= 8) { - RTC_DLOG(WARNING) + RTC_DLOG(LS_WARNING) << "Failed to read obu_size. obu_size field is too long: " << size_of_obu_size_bytes << " bytes processed."; return false; @@ -321,8 +323,9 @@ bool CalculateObuSizes(ObuInfo* obu_info) { obu_info->data.size() - obu_info->prefix_size - size_of_obu_size_bytes; if (obu_size_bytes != obu_info->payload_size) { // obu_size was present in the bitstream and mismatches calculated size. - RTC_DLOG(WARNING) << "Mismatch in obu_size. signaled: " << obu_size_bytes - << ", actual: " << obu_info->payload_size; + RTC_DLOG(LS_WARNING) << "Mismatch in obu_size. signaled: " + << obu_size_bytes + << ", actual: " << obu_info->payload_size; return false; } } diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc index e87be031a8..ee4d744578 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc @@ -15,7 +15,6 @@ #include #include -#include "absl/base/macros.h" #include "absl/types/optional.h" #include "absl/types/variant.h" #include "common_video/h264/h264_common.h" @@ -197,7 +196,7 @@ absl::optional ProcessStapAOrSingleNalu( case H264::NaluType::kIdr: parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameKey; - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; case H264::NaluType::kSlice: { absl::optional pps_id = PpsParser::ParsePpsIdFromSlice( &payload_data[start_offset], end_offset - start_offset); diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc index cd5ebe7235..41f363d221 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc @@ -211,8 +211,8 @@ int VideoRtpDepacketizerVp9::ParseRtpPayload( video_header->height = vp9_header.height[0]; } } - video_header->is_first_packet_in_frame = - b_bit && (!l_bit || !vp9_header.inter_layer_predicted); + video_header->is_first_packet_in_frame = b_bit; + video_header->is_last_packet_in_frame = e_bit; int num_remaining_bits = parser.RemainingBitCount(); if (num_remaining_bits <= 0) { diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc index 937d15de23..36af59a779 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc @@ -279,6 +279,7 @@ TEST(VideoRtpDepacketizerVp9Test, ParseFirstPacketInKeyFrame) { EXPECT_EQ(video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_TRUE(video_header.is_first_packet_in_frame); + EXPECT_FALSE(video_header.is_last_packet_in_frame); } TEST(VideoRtpDepacketizerVp9Test, ParseLastPacketInDeltaFrame) { @@ -290,6 +291,7 @@ TEST(VideoRtpDepacketizerVp9Test, ParseLastPacketInDeltaFrame) { EXPECT_EQ(video_header.frame_type, VideoFrameType::kVideoFrameDelta); EXPECT_FALSE(video_header.is_first_packet_in_frame); + EXPECT_TRUE(video_header.is_last_packet_in_frame); } TEST(VideoRtpDepacketizerVp9Test, ParseResolution) { diff --git a/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc b/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc index 8941f1ca94..25ceee585a 100644 --- a/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc +++ b/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc @@ -509,7 +509,7 @@ class FecPacketMaskMetricsTest : public ::testing::Test { } else if (code_type == xor_bursty_code) { CopyMetrics(&kMetricsXorBursty[code_index], metrics_code); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/modules/utility/source/jvm_android.cc b/modules/utility/source/jvm_android.cc index 8e24daa0f2..ee9930bcaa 100644 --- a/modules/utility/source/jvm_android.cc +++ b/modules/utility/source/jvm_android.cc @@ -38,10 +38,10 @@ struct { // stack. Consequently, we only look up all classes once in native WebRTC. // http://developer.android.com/training/articles/perf-jni.html#faq_FindClass void LoadClasses(JNIEnv* jni) { - RTC_LOG(INFO) << "LoadClasses:"; + RTC_LOG(LS_INFO) << "LoadClasses:"; for (auto& c : loaded_classes) { jclass localRef = FindClass(jni, c.name); - RTC_LOG(INFO) << "name: " << c.name; + RTC_LOG(LS_INFO) << "name: " << c.name; CHECK_EXCEPTION(jni) << "Error during FindClass: " << c.name; RTC_CHECK(localRef) << c.name; jclass globalRef = reinterpret_cast(jni->NewGlobalRef(localRef)); @@ -69,12 +69,12 @@ jclass LookUpClass(const char* name) { // JvmThreadConnector implementation. JvmThreadConnector::JvmThreadConnector() : attached_(false) { - RTC_LOG(INFO) << "JvmThreadConnector::ctor"; + RTC_LOG(LS_INFO) << "JvmThreadConnector::ctor"; JavaVM* jvm = JVM::GetInstance()->jvm(); RTC_CHECK(jvm); JNIEnv* jni = GetEnv(jvm); if (!jni) { - RTC_LOG(INFO) << "Attaching thread to JVM"; + RTC_LOG(LS_INFO) << "Attaching thread to JVM"; JNIEnv* env = nullptr; jint ret = jvm->AttachCurrentThread(&env, nullptr); attached_ = (ret == JNI_OK); @@ -82,10 +82,10 @@ JvmThreadConnector::JvmThreadConnector() : attached_(false) { } JvmThreadConnector::~JvmThreadConnector() { - RTC_LOG(INFO) << "JvmThreadConnector::dtor"; + RTC_LOG(LS_INFO) << "JvmThreadConnector::dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); if (attached_) { - RTC_LOG(INFO) << "Detaching thread from JVM"; + RTC_LOG(LS_INFO) << "Detaching thread from JVM"; jint res = JVM::GetInstance()->jvm()->DetachCurrentThread(); RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res; } @@ -94,11 +94,11 @@ JvmThreadConnector::~JvmThreadConnector() { // GlobalRef implementation. GlobalRef::GlobalRef(JNIEnv* jni, jobject object) : jni_(jni), j_object_(NewGlobalRef(jni, object)) { - RTC_LOG(INFO) << "GlobalRef::ctor"; + RTC_LOG(LS_INFO) << "GlobalRef::ctor"; } GlobalRef::~GlobalRef() { - RTC_LOG(INFO) << "GlobalRef::dtor"; + RTC_LOG(LS_INFO) << "GlobalRef::dtor"; DeleteGlobalRef(jni_, j_object_); } @@ -131,11 +131,11 @@ void GlobalRef::CallVoidMethod(jmethodID methodID, ...) { // NativeRegistration implementation. NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz) : JavaClass(jni, clazz), jni_(jni) { - RTC_LOG(INFO) << "NativeRegistration::ctor"; + RTC_LOG(LS_INFO) << "NativeRegistration::ctor"; } NativeRegistration::~NativeRegistration() { - RTC_LOG(INFO) << "NativeRegistration::dtor"; + RTC_LOG(LS_INFO) << "NativeRegistration::dtor"; jni_->UnregisterNatives(j_class_); CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives"; } @@ -143,7 +143,7 @@ NativeRegistration::~NativeRegistration() { std::unique_ptr NativeRegistration::NewObject(const char* name, const char* signature, ...) { - RTC_LOG(INFO) << "NativeRegistration::NewObject"; + RTC_LOG(LS_INFO) << "NativeRegistration::NewObject"; va_list args; va_start(args, signature); jobject obj = jni_->NewObjectV( @@ -181,11 +181,11 @@ jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) { // JNIEnvironment implementation. JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) { - RTC_LOG(INFO) << "JNIEnvironment::ctor"; + RTC_LOG(LS_INFO) << "JNIEnvironment::ctor"; } JNIEnvironment::~JNIEnvironment() { - RTC_LOG(INFO) << "JNIEnvironment::dtor"; + RTC_LOG(LS_INFO) << "JNIEnvironment::dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); } @@ -193,7 +193,7 @@ std::unique_ptr JNIEnvironment::RegisterNatives( const char* name, const JNINativeMethod* methods, int num_methods) { - RTC_LOG(INFO) << "JNIEnvironment::RegisterNatives: " << name; + RTC_LOG(LS_INFO) << "JNIEnvironment::RegisterNatives: " << name; RTC_DCHECK(thread_checker_.IsCurrent()); jclass clazz = LookUpClass(name); jni_->RegisterNatives(clazz, methods, num_methods); @@ -216,7 +216,7 @@ std::string JNIEnvironment::JavaToStdString(const jstring& j_string) { // static void JVM::Initialize(JavaVM* jvm) { - RTC_LOG(INFO) << "JVM::Initialize"; + RTC_LOG(LS_INFO) << "JVM::Initialize"; RTC_CHECK(!g_jvm); g_jvm = new JVM(jvm); } @@ -234,7 +234,7 @@ void JVM::Initialize(JavaVM* jvm, jobject context) { // static void JVM::Uninitialize() { - RTC_LOG(INFO) << "JVM::Uninitialize"; + RTC_LOG(LS_INFO) << "JVM::Uninitialize"; RTC_DCHECK(g_jvm); delete g_jvm; g_jvm = nullptr; @@ -247,19 +247,19 @@ JVM* JVM::GetInstance() { } JVM::JVM(JavaVM* jvm) : jvm_(jvm) { - RTC_LOG(INFO) << "JVM::JVM"; + RTC_LOG(LS_INFO) << "JVM::JVM"; RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread."; LoadClasses(jni()); } JVM::~JVM() { - RTC_LOG(INFO) << "JVM::~JVM"; + RTC_LOG(LS_INFO) << "JVM::~JVM"; RTC_DCHECK(thread_checker_.IsCurrent()); FreeClassReferences(jni()); } std::unique_ptr JVM::environment() { - RTC_LOG(INFO) << "JVM::environment"; + RTC_LOG(LS_INFO) << "JVM::environment"; ; // The JNIEnv is used for thread-local storage. For this reason, we cannot // share a JNIEnv between threads. If a piece of code has no other way to get @@ -276,7 +276,7 @@ std::unique_ptr JVM::environment() { } JavaClass JVM::GetClass(const char* name) { - RTC_LOG(INFO) << "JVM::GetClass: " << name; + RTC_LOG(LS_INFO) << "JVM::GetClass: " << name; RTC_DCHECK(thread_checker_.IsCurrent()); return JavaClass(jni(), LookUpClass(name)); } diff --git a/modules/video_capture/BUILD.gn b/modules/video_capture/BUILD.gn index 3a5052dc7a..75c2648cae 100644 --- a/modules/video_capture/BUILD.gn +++ b/modules/video_capture/BUILD.gn @@ -138,7 +138,6 @@ if (!build_with_chromium) { "../../test:test_main", "../../test:test_support", "../../test:video_test_common", - "../utility", "//testing/gtest", "//third_party/abseil-cpp/absl/memory", ] diff --git a/modules/video_capture/device_info_impl.h b/modules/video_capture/device_info_impl.h index 4b47389609..546265049c 100644 --- a/modules/video_capture/device_info_impl.h +++ b/modules/video_capture/device_info_impl.h @@ -29,7 +29,7 @@ class DeviceInfoImpl : public VideoCaptureModule::DeviceInfo { ~DeviceInfoImpl(void) override; int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) override; int32_t GetCapability(const char* deviceUniqueIdUTF8, - const uint32_t deviceCapabilityNumber, + uint32_t deviceCapabilityNumber, VideoCaptureCapability& capability) override; int32_t GetBestMatchedCapability(const char* deviceUniqueIdUTF8, diff --git a/modules/video_capture/linux/video_capture_linux.cc b/modules/video_capture/linux/video_capture_linux.cc index 10f9713ec3..321355ffca 100644 --- a/modules/video_capture/linux/video_capture_linux.cc +++ b/modules/video_capture/linux/video_capture_linux.cc @@ -370,17 +370,6 @@ bool VideoCaptureModuleV4L2::CaptureProcess() { // _deviceFd written only in StartCapture, when this thread isn't running. retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout); - if (retVal < 0 && errno != EINTR) // continue if interrupted - { - // select failed - return false; - } else if (retVal == 0) { - // select timed out - return true; - } else if (!FD_ISSET(_deviceFd, &rSet)) { - // not event on camera handle - return true; - } { MutexLock lock(&capture_lock_); @@ -389,6 +378,18 @@ bool VideoCaptureModuleV4L2::CaptureProcess() { return false; } + if (retVal < 0 && errno != EINTR) // continue if interrupted + { + // select failed + return false; + } else if (retVal == 0) { + // select timed out + return true; + } else if (!FD_ISSET(_deviceFd, &rSet)) { + // not event on camera handle + return true; + } + if (_captureStarted) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(struct v4l2_buffer)); diff --git a/modules/video_capture/test/video_capture_unittest.cc b/modules/video_capture/test/video_capture_unittest.cc index e74a456cee..4cf3d5931c 100644 --- a/modules/video_capture/test/video_capture_unittest.cc +++ b/modules/video_capture/test/video_capture_unittest.cc @@ -21,7 +21,6 @@ #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "common_video/libyuv/include/webrtc_libyuv.h" -#include "modules/utility/include/process_thread.h" #include "modules/video_capture/video_capture_factory.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/time_utils.h" @@ -169,7 +168,7 @@ class VideoCaptureTest : public ::testing::Test { rtc::scoped_refptr module( VideoCaptureFactory::Create(unique_name)); if (module.get() == NULL) - return NULL; + return nullptr; EXPECT_FALSE(module->CaptureStarted()); diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h index 0f60092d72..3bbe217cba 100644 --- a/modules/video_capture/video_capture.h +++ b/modules/video_capture/video_capture.h @@ -44,7 +44,7 @@ class VideoCaptureModule : public rtc::RefCountInterface { // Gets the capabilities of the named device. virtual int32_t GetCapability(const char* deviceUniqueIdUTF8, - const uint32_t deviceCapabilityNumber, + uint32_t deviceCapabilityNumber, VideoCaptureCapability& capability) = 0; // Gets clockwise angle the captured frames should be rotated in order diff --git a/modules/video_capture/windows/device_info_ds.cc b/modules/video_capture/windows/device_info_ds.cc index 455501fb07..fb8d55137f 100644 --- a/modules/video_capture/windows/device_info_ds.cc +++ b/modules/video_capture/windows/device_info_ds.cc @@ -389,6 +389,9 @@ int32_t DeviceInfoDS::CreateCapabilityMap(const char* deviceUniqueIdUTF8) RTC_LOG(LS_INFO) << "Device support FORMAT_VideoInfo2"; supportFORMAT_VideoInfo = true; } + + FreeMediaType(pmt); + pmt = NULL; } } if (supportFORMAT_VideoInfo2) { diff --git a/modules/video_capture/windows/device_info_ds.h b/modules/video_capture/windows/device_info_ds.h index 2fda3257f4..1b52645cde 100644 --- a/modules/video_capture/windows/device_info_ds.h +++ b/modules/video_capture/windows/device_info_ds.h @@ -69,7 +69,7 @@ class DeviceInfoDS : public DeviceInfoImpl { uint32_t productUniqueIdUTF8Length = 0); int32_t GetWindowsCapability( - const int32_t capabilityIndex, + int32_t capabilityIndex, VideoCaptureCapabilityWindows& windowsCapability); static void GetProductId(const char* devicePath, diff --git a/modules/video_capture/windows/help_functions_ds.cc b/modules/video_capture/windows/help_functions_ds.cc index ad4c064d06..b767726107 100644 --- a/modules/video_capture/windows/help_functions_ds.cc +++ b/modules/video_capture/windows/help_functions_ds.cc @@ -30,7 +30,6 @@ LONGLONG GetMaxOfFrameArray(LONGLONG* maxFps, long size) { } IPin* GetInputPin(IBaseFilter* filter) { - HRESULT hr; IPin* pin = NULL; IEnumPins* pPinEnum = NULL; filter->EnumPins(&pPinEnum); @@ -39,7 +38,7 @@ IPin* GetInputPin(IBaseFilter* filter) { } // get first unconnected pin - hr = pPinEnum->Reset(); // set to first pin + pPinEnum->Reset(); // set to first pin while (S_OK == pPinEnum->Next(1, &pin, NULL)) { PIN_DIRECTION pPinDir; @@ -60,7 +59,6 @@ IPin* GetInputPin(IBaseFilter* filter) { } IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category) { - HRESULT hr; IPin* pin = NULL; IEnumPins* pPinEnum = NULL; filter->EnumPins(&pPinEnum); @@ -68,7 +66,7 @@ IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category) { return NULL; } // get first unconnected pin - hr = pPinEnum->Reset(); // set to first pin + pPinEnum->Reset(); // set to first pin while (S_OK == pPinEnum->Next(1, &pin, NULL)) { PIN_DIRECTION pPinDir; pin->QueryDirection(&pPinDir); diff --git a/modules/video_capture/windows/sink_filter_ds.cc b/modules/video_capture/windows/sink_filter_ds.cc index e07cd6893b..15f947a921 100644 --- a/modules/video_capture/windows/sink_filter_ds.cc +++ b/modules/video_capture/windows/sink_filter_ds.cc @@ -58,7 +58,7 @@ class EnumPins : public IEnumPins { } STDMETHOD(Clone)(IEnumPins** pins) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return E_NOTIMPL; } @@ -83,7 +83,7 @@ class EnumPins : public IEnumPins { } STDMETHOD(Skip)(ULONG count) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return E_NOTIMPL; } @@ -274,7 +274,7 @@ class MediaTypesEnum : public IEnumMediaTypes { // IEnumMediaTypes STDMETHOD(Clone)(IEnumMediaTypes** pins) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return E_NOTIMPL; } @@ -359,12 +359,12 @@ class MediaTypesEnum : public IEnumMediaTypes { media_type->subtype = MEDIASUBTYPE_MJPG; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } STDMETHOD(Skip)(ULONG count) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return E_NOTIMPL; } @@ -513,7 +513,8 @@ HRESULT CaptureInputPin::CheckDirection(IPin* pin) const { return pd == info_.dir ? VFW_E_INVALID_DIRECTION : S_OK; } -STDMETHODIMP CaptureInputPin::QueryInterface(REFIID riid, void** ppv) { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryInterface(REFIID riid, + void** ppv) { (*ppv) = nullptr; if (riid == IID_IUnknown || riid == IID_IMemInputPin) { *ppv = static_cast(this); @@ -528,8 +529,8 @@ STDMETHODIMP CaptureInputPin::QueryInterface(REFIID riid, void** ppv) { return S_OK; } -STDMETHODIMP CaptureInputPin::Connect(IPin* receive_pin, - const AM_MEDIA_TYPE* media_type) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::Connect(IPin* receive_pin, const AM_MEDIA_TYPE* media_type) { RTC_DCHECK_RUN_ON(&main_checker_); if (!media_type || !receive_pin) return E_POINTER; @@ -538,7 +539,7 @@ STDMETHODIMP CaptureInputPin::Connect(IPin* receive_pin, return VFW_E_NOT_STOPPED; if (receive_pin_) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return VFW_E_ALREADY_CONNECTED; } @@ -557,14 +558,14 @@ STDMETHODIMP CaptureInputPin::Connect(IPin* receive_pin, return connected ? S_OK : VFW_E_NO_ACCEPTABLE_TYPES; } -STDMETHODIMP CaptureInputPin::ReceiveConnection( - IPin* connector, - const AM_MEDIA_TYPE* media_type) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ReceiveConnection(IPin* connector, + const AM_MEDIA_TYPE* media_type) { RTC_DCHECK_RUN_ON(&main_checker_); RTC_DCHECK(Filter()->IsStopped()); if (receive_pin_) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return VFW_E_ALREADY_CONNECTED; } @@ -585,7 +586,7 @@ STDMETHODIMP CaptureInputPin::ReceiveConnection( return S_OK; } -STDMETHODIMP CaptureInputPin::Disconnect() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::Disconnect() { RTC_DCHECK_RUN_ON(&main_checker_); if (!Filter()->IsStopped()) return VFW_E_NOT_STOPPED; @@ -599,7 +600,7 @@ STDMETHODIMP CaptureInputPin::Disconnect() { return S_OK; } -STDMETHODIMP CaptureInputPin::ConnectedTo(IPin** pin) { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ConnectedTo(IPin** pin) { RTC_DCHECK_RUN_ON(&main_checker_); if (!receive_pin_) @@ -611,7 +612,8 @@ STDMETHODIMP CaptureInputPin::ConnectedTo(IPin** pin) { return S_OK; } -STDMETHODIMP CaptureInputPin::ConnectionMediaType(AM_MEDIA_TYPE* media_type) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ConnectionMediaType(AM_MEDIA_TYPE* media_type) { RTC_DCHECK_RUN_ON(&main_checker_); if (!receive_pin_) @@ -622,7 +624,8 @@ STDMETHODIMP CaptureInputPin::ConnectionMediaType(AM_MEDIA_TYPE* media_type) { return S_OK; } -STDMETHODIMP CaptureInputPin::QueryPinInfo(PIN_INFO* info) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryPinInfo(PIN_INFO* info) { RTC_DCHECK_RUN_ON(&main_checker_); *info = info_; if (info_.pFilter) @@ -630,13 +633,14 @@ STDMETHODIMP CaptureInputPin::QueryPinInfo(PIN_INFO* info) { return S_OK; } -STDMETHODIMP CaptureInputPin::QueryDirection(PIN_DIRECTION* pin_dir) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryDirection(PIN_DIRECTION* pin_dir) { RTC_DCHECK_RUN_ON(&main_checker_); *pin_dir = info_.dir; return S_OK; } -STDMETHODIMP CaptureInputPin::QueryId(LPWSTR* id) { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryId(LPWSTR* id) { RTC_DCHECK_RUN_ON(&main_checker_); size_t len = lstrlenW(info_.achName); *id = reinterpret_cast(CoTaskMemAlloc((len + 1) * sizeof(wchar_t))); @@ -644,7 +648,8 @@ STDMETHODIMP CaptureInputPin::QueryId(LPWSTR* id) { return S_OK; } -STDMETHODIMP CaptureInputPin::QueryAccept(const AM_MEDIA_TYPE* media_type) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryAccept(const AM_MEDIA_TYPE* media_type) { RTC_DCHECK_RUN_ON(&main_checker_); RTC_DCHECK(Filter()->IsStopped()); VideoCaptureCapability capability(resulting_capability_); @@ -653,43 +658,46 @@ STDMETHODIMP CaptureInputPin::QueryAccept(const AM_MEDIA_TYPE* media_type) { : S_OK; } -STDMETHODIMP CaptureInputPin::EnumMediaTypes(IEnumMediaTypes** types) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::EnumMediaTypes(IEnumMediaTypes** types) { RTC_DCHECK_RUN_ON(&main_checker_); *types = new ComRefCount(requested_capability_); (*types)->AddRef(); return S_OK; } -STDMETHODIMP CaptureInputPin::QueryInternalConnections(IPin** pins, - ULONG* count) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryInternalConnections(IPin** pins, ULONG* count) { return E_NOTIMPL; } -STDMETHODIMP CaptureInputPin::EndOfStream() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndOfStream() { return S_OK; } -STDMETHODIMP CaptureInputPin::BeginFlush() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::BeginFlush() { RTC_DCHECK_RUN_ON(&main_checker_); flushing_ = true; return S_OK; } -STDMETHODIMP CaptureInputPin::EndFlush() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndFlush() { RTC_DCHECK_RUN_ON(&main_checker_); flushing_ = false; runtime_error_ = false; return S_OK; } -STDMETHODIMP CaptureInputPin::NewSegment(REFERENCE_TIME start, - REFERENCE_TIME stop, - double rate) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::NewSegment(REFERENCE_TIME start, + REFERENCE_TIME stop, + double rate) { RTC_DCHECK_RUN_ON(&main_checker_); return S_OK; } -STDMETHODIMP CaptureInputPin::GetAllocator(IMemAllocator** allocator) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::GetAllocator(IMemAllocator** allocator) { RTC_DCHECK_RUN_ON(&main_checker_); if (allocator_ == nullptr) { HRESULT hr = CoCreateInstance(CLSID_MemoryAllocator, 0, @@ -704,8 +712,8 @@ STDMETHODIMP CaptureInputPin::GetAllocator(IMemAllocator** allocator) { return S_OK; } -STDMETHODIMP CaptureInputPin::NotifyAllocator(IMemAllocator* allocator, - BOOL read_only) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::NotifyAllocator(IMemAllocator* allocator, BOOL read_only) { RTC_DCHECK_RUN_ON(&main_checker_); allocator_.swap(&allocator); if (allocator_) @@ -715,12 +723,13 @@ STDMETHODIMP CaptureInputPin::NotifyAllocator(IMemAllocator* allocator, return S_OK; } -STDMETHODIMP CaptureInputPin::GetAllocatorRequirements( - ALLOCATOR_PROPERTIES* props) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* props) { return E_NOTIMPL; } -STDMETHODIMP CaptureInputPin::Receive(IMediaSample* media_sample) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::Receive(IMediaSample* media_sample) { RTC_DCHECK_RUN_ON(&capture_checker_); CaptureSinkFilter* const filter = static_cast(Filter()); @@ -765,9 +774,10 @@ STDMETHODIMP CaptureInputPin::Receive(IMediaSample* media_sample) { return S_OK; } -STDMETHODIMP CaptureInputPin::ReceiveMultiple(IMediaSample** samples, - long count, - long* processed) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ReceiveMultiple(IMediaSample** samples, + long count, + long* processed) { HRESULT hr = S_OK; *processed = 0; while (count-- > 0) { @@ -779,7 +789,7 @@ STDMETHODIMP CaptureInputPin::ReceiveMultiple(IMediaSample** samples, return hr; } -STDMETHODIMP CaptureInputPin::ReceiveCanBlock() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ReceiveCanBlock() { return S_FALSE; } @@ -800,29 +810,33 @@ HRESULT CaptureSinkFilter::SetRequestedCapability( return input_pin_->SetRequestedCapability(capability); } -STDMETHODIMP CaptureSinkFilter::GetState(DWORD msecs, FILTER_STATE* state) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::GetState(DWORD msecs, FILTER_STATE* state) { RTC_DCHECK_RUN_ON(&main_checker_); *state = state_; return S_OK; } -STDMETHODIMP CaptureSinkFilter::SetSyncSource(IReferenceClock* clock) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::SetSyncSource(IReferenceClock* clock) { RTC_DCHECK_RUN_ON(&main_checker_); return S_OK; } -STDMETHODIMP CaptureSinkFilter::GetSyncSource(IReferenceClock** clock) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::GetSyncSource(IReferenceClock** clock) { RTC_DCHECK_RUN_ON(&main_checker_); return E_NOTIMPL; } -STDMETHODIMP CaptureSinkFilter::Pause() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Pause() { RTC_DCHECK_RUN_ON(&main_checker_); state_ = State_Paused; return S_OK; } -STDMETHODIMP CaptureSinkFilter::Run(REFERENCE_TIME tStart) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::Run(REFERENCE_TIME tStart) { RTC_DCHECK_RUN_ON(&main_checker_); if (state_ == State_Stopped) Pause(); @@ -833,7 +847,7 @@ STDMETHODIMP CaptureSinkFilter::Run(REFERENCE_TIME tStart) { return S_OK; } -STDMETHODIMP CaptureSinkFilter::Stop() { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Stop() { RTC_DCHECK_RUN_ON(&main_checker_); if (state_ == State_Stopped) return S_OK; @@ -844,21 +858,24 @@ STDMETHODIMP CaptureSinkFilter::Stop() { return S_OK; } -STDMETHODIMP CaptureSinkFilter::EnumPins(IEnumPins** pins) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::EnumPins(IEnumPins** pins) { RTC_DCHECK_RUN_ON(&main_checker_); *pins = new ComRefCount(input_pin_.get()); (*pins)->AddRef(); return S_OK; } -STDMETHODIMP CaptureSinkFilter::FindPin(LPCWSTR id, IPin** pin) { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::FindPin(LPCWSTR id, + IPin** pin) { RTC_DCHECK_RUN_ON(&main_checker_); // There's no ID assigned to our input pin, so looking it up based on one // is pointless (and in practice, this method isn't being used). return VFW_E_NOT_FOUND; } -STDMETHODIMP CaptureSinkFilter::QueryFilterInfo(FILTER_INFO* info) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryFilterInfo(FILTER_INFO* info) { RTC_DCHECK_RUN_ON(&main_checker_); *info = info_; if (info->pGraph) @@ -866,8 +883,8 @@ STDMETHODIMP CaptureSinkFilter::QueryFilterInfo(FILTER_INFO* info) { return S_OK; } -STDMETHODIMP CaptureSinkFilter::JoinFilterGraph(IFilterGraph* graph, - LPCWSTR name) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::JoinFilterGraph(IFilterGraph* graph, LPCWSTR name) { RTC_DCHECK_RUN_ON(&main_checker_); RTC_DCHECK(IsStopped()); @@ -893,7 +910,8 @@ STDMETHODIMP CaptureSinkFilter::JoinFilterGraph(IFilterGraph* graph, return S_OK; } -STDMETHODIMP CaptureSinkFilter::QueryVendorInfo(LPWSTR* vendor_info) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryVendorInfo(LPWSTR* vendor_info) { return E_NOTIMPL; } @@ -922,7 +940,8 @@ bool CaptureSinkFilter::IsStopped() const { return state_ == State_Stopped; } -STDMETHODIMP CaptureSinkFilter::QueryInterface(REFIID riid, void** ppv) { +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryInterface(REFIID riid, void** ppv) { if (riid == IID_IUnknown || riid == IID_IPersist || riid == IID_IBaseFilter) { *ppv = static_cast(this); AddRef(); @@ -931,7 +950,7 @@ STDMETHODIMP CaptureSinkFilter::QueryInterface(REFIID riid, void** ppv) { return E_NOINTERFACE; } -STDMETHODIMP CaptureSinkFilter::GetClassID(CLSID* clsid) { +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::GetClassID(CLSID* clsid) { *clsid = CLSID_SINKFILTER; return S_OK; } diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index fa50637871..7d6b5da1af 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -98,11 +98,159 @@ rtc_library("nack_requester") { ] } -rtc_library("video_coding") { - visibility = [ "*" ] +rtc_library("packet_buffer") { + sources = [ + "packet_buffer.cc", + "packet_buffer.h", + ] + deps = [ + ":codec_globals_headers", + "../../api:array_view", + "../../api:rtp_packet_info", + "../../api/units:timestamp", + "../../api/video:encoded_image", + "../../api/video:video_frame_type", + "../../common_video", + "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_numerics", + "../rtp_rtcp:rtp_rtcp_format", + "../rtp_rtcp:rtp_video_header", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/types:variant", + ] +} + +rtc_library("h264_packet_buffer") { + sources = [ + "h264_packet_buffer.cc", + "h264_packet_buffer.h", + ] + deps = [ + ":codec_globals_headers", + ":packet_buffer", + "../../api:array_view", + "../../api:rtp_packet_info", + "../../api/units:timestamp", + "../../api/video:encoded_image", + "../../api/video:video_frame_type", + "../../common_video", + "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_numerics", + "../rtp_rtcp:rtp_rtcp_format", + "../rtp_rtcp:rtp_video_header", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("frame_helpers") { + sources = [ + "frame_helpers.cc", + "frame_helpers.h", + ] + deps = [ + "../../api/video:encoded_frame", + "../../rtc_base:logging", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector" ] +} + +rtc_library("frame_buffer") { + sources = [ + "frame_buffer3.cc", + "frame_buffer3.h", + ] + deps = [ + ":video_coding_utility", + "../../api/units:timestamp", + "../../api/video:encoded_frame", + "../../rtc_base:logging", + "../../rtc_base:rtc_numerics", + "../../system_wrappers:field_trial", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/container:inlined_vector", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("timing") { sources = [ "codec_timer.cc", "codec_timer.h", + "timing.cc", + "timing.h", + ] + deps = [ + "../../api/units:time_delta", + "../../api/video:video_rtp_headers", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:rtc_numerics", + "../../rtc_base/experiments:field_trial_parser", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/time:timestamp_extrapolator", + "../../system_wrappers", + "../../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("rtt_filter") { + sources = [ + "rtt_filter.cc", + "rtt_filter.h", + ] + deps = [ "../../api/units:time_delta" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/container:inlined_vector", + ] +} + +rtc_library("jitter_estimator") { + sources = [ + "jitter_estimator.cc", + "jitter_estimator.h", + ] + deps = [ + ":rtt_filter", + "../../api/units:data_size", + "../../api/units:frequency", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../rtc_base", + "../../rtc_base:safe_conversions", + "../../rtc_base/experiments:jitter_upper_bound_experiment", + "../../system_wrappers", + "../../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("inter_frame_delay") { + sources = [ + "inter_frame_delay.cc", + "inter_frame_delay.h", + ] + deps = [ + "..:module_api_public", + "../../api/units:frequency", + "../../api/units:time_delta", + "../../api/units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("video_coding") { + visibility = [ "*" ] + sources = [ "decoder_database.cc", "decoder_database.h", "fec_controller_default.cc", @@ -119,17 +267,11 @@ rtc_library("video_coding") { "h264_sps_pps_tracker.cc", "h264_sps_pps_tracker.h", "include/video_codec_initializer.h", - "inter_frame_delay.cc", - "inter_frame_delay.h", "internal_defines.h", - "jitter_estimator.cc", - "jitter_estimator.h", "loss_notification_controller.cc", "loss_notification_controller.h", "media_opt_util.cc", "media_opt_util.h", - "packet_buffer.cc", - "packet_buffer.h", "rtp_frame_id_only_ref_finder.cc", "rtp_frame_id_only_ref_finder.h", "rtp_frame_reference_finder.cc", @@ -142,12 +284,8 @@ rtc_library("video_coding") { "rtp_vp8_ref_finder.h", "rtp_vp9_ref_finder.cc", "rtp_vp9_ref_finder.h", - "rtt_filter.cc", - "rtt_filter.h", "timestamp_map.cc", "timestamp_map.h", - "timing.cc", - "timing.h", "unique_timestamp_counter.cc", "unique_timestamp_counter.h", "video_codec_initializer.cc", @@ -158,6 +296,12 @@ rtc_library("video_coding") { deps = [ ":codec_globals_headers", ":encoded_frame", + ":frame_buffer", + ":frame_helpers", + ":inter_frame_delay", + ":jitter_estimator", + ":rtt_filter", + ":timing", ":video_codec_interface", ":video_coding_utility", ":webrtc_vp9_helpers", @@ -170,7 +314,10 @@ rtc_library("video_coding") { "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api:sequence_checker", + "../../api/task_queue", "../../api/units:data_rate", + "../../api/units:data_size", + "../../api/units:frequency", "../../api/units:time_delta", "../../api/units:timestamp", "../../api/video:builtin_video_bitrate_allocator_factory", @@ -215,7 +362,6 @@ rtc_library("video_coding") { absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/container:inlined_vector", - "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/types:optional", "//third_party/abseil-cpp/absl/types:variant", ] @@ -268,6 +414,9 @@ rtc_library("video_coding_legacy") { deps = [ ":codec_globals_headers", ":encoded_frame", + ":inter_frame_delay", + ":jitter_estimator", + ":timing", ":video_codec_interface", ":video_coding", "..:module_api", @@ -315,12 +464,15 @@ rtc_source_set("codec_globals_headers") { rtc_library("video_coding_utility") { visibility = [ "*" ] sources = [ + "utility/bandwidth_quality_scaler.cc", + "utility/bandwidth_quality_scaler.h", "utility/decoded_frames_history.cc", "utility/decoded_frames_history.h", "utility/frame_dropper.cc", "utility/frame_dropper.h", "utility/framerate_controller_deprecated.cc", "utility/framerate_controller_deprecated.h", + "utility/ivf_defines.h", "utility/ivf_file_reader.cc", "utility/ivf_file_reader.h", "utility/ivf_file_writer.cc", @@ -361,6 +513,8 @@ rtc_library("video_coding_utility") { "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", "../../rtc_base:weak_ptr", + "../../rtc_base/experiments:bandwidth_quality_scaler_settings", + "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:quality_scaler_settings", "../../rtc_base/experiments:quality_scaling_experiment", "../../rtc_base/experiments:rate_control_settings", @@ -519,6 +673,7 @@ rtc_library("webrtc_vp8") { ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings:strings", "//third_party/abseil-cpp/absl/types:optional", ] if (rtc_build_libvpx) { @@ -862,6 +1017,7 @@ if (rtc_include_tests) { "codecs/h264/test/h264_impl_unittest.cc", "codecs/multiplex/test/multiplex_adapter_unittest.cc", "codecs/test/video_encoder_decoder_instantiation_tests.cc", + "codecs/test/videocodec_test_av1.cc", "codecs/test/videocodec_test_libvpx.cc", "codecs/vp8/test/vp8_impl_unittest.cc", ] @@ -870,10 +1026,6 @@ if (rtc_include_tests) { sources += [ "codecs/vp9/test/vp9_impl_unittest.cc" ] } - # TODO(jianj): Fix crash on iOS and re-enable - if (enable_libaom && !is_ios) { - sources += [ "codecs/test/videocodec_test_libaom.cc" ] - } if (rtc_use_h264) { sources += [ "codecs/test/videocodec_test_openh264.cc" ] } @@ -917,6 +1069,8 @@ if (rtc_include_tests) { "../../test:test_support", "../../test:video_test_common", "../rtp_rtcp:rtp_rtcp_format", + "codecs/av1:dav1d_decoder", + "codecs/av1:libaom_av1_decoder", "//third_party/libyuv", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -959,11 +1113,14 @@ if (rtc_include_tests) { "decoding_state_unittest.cc", "fec_controller_unittest.cc", "frame_buffer2_unittest.cc", + "frame_buffer3_unittest.cc", "frame_dependencies_calculator_unittest.cc", "generic_decoder_unittest.cc", + "h264_packet_buffer_unittest.cc", "h264_sprop_parameter_sets_unittest.cc", "h264_sps_pps_tracker_unittest.cc", "histogram_unittest.cc", + "inter_frame_delay_unittest.cc", "jitter_buffer_unittest.cc", "jitter_estimator_tests.cc", "loss_notification_controller_unittest.cc", @@ -974,12 +1131,14 @@ if (rtc_include_tests) { "rtp_frame_reference_finder_unittest.cc", "rtp_vp8_ref_finder_unittest.cc", "rtp_vp9_ref_finder_unittest.cc", + "rtt_filter_unittest.cc", "session_info_unittest.cc", "test/stream_generator.cc", "test/stream_generator.h", "timestamp_map_unittest.cc", "timing_unittest.cc", "unique_timestamp_counter_unittest.cc", + "utility/bandwidth_quality_scaler_unittest.cc", "utility/decoded_frames_history_unittest.cc", "utility/frame_dropper_unittest.cc", "utility/framerate_controller_deprecated_unittest.cc", @@ -1003,9 +1162,16 @@ if (rtc_include_tests) { ":chain_diff_calculator", ":codec_globals_headers", ":encoded_frame", + ":frame_buffer", ":frame_dependencies_calculator", + ":h264_packet_buffer", + ":inter_frame_delay", + ":jitter_estimator", ":nack_requester", + ":packet_buffer", + ":rtt_filter", ":simulcast_test_fixture_impl", + ":timing", ":video_codec_interface", ":video_codecs_test_framework", ":video_coding", @@ -1030,8 +1196,13 @@ if (rtc_include_tests) { "../../api:videocodec_test_fixture_api", "../../api/task_queue:default_task_queue_factory", "../../api/test/video:function_video_factory", + "../../api/units:data_size", + "../../api/units:frequency", + "../../api/units:time_delta", + "../../api/units:timestamp", "../../api/video:builtin_video_bitrate_allocator_factory", "../../api/video:encoded_frame", + "../../api/video:render_resolution", "../../api/video:video_adaptation", "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", @@ -1052,8 +1223,10 @@ if (rtc_include_tests) { "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", "../../rtc_base:task_queue_for_test", + "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:jitter_upper_bound_experiment", "../../rtc_base/synchronization:mutex", + "../../rtc_base/system:unused", "../../system_wrappers", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn index 322262c36a..34a75a33fe 100644 --- a/modules/video_coding/codecs/av1/BUILD.gn +++ b/modules/video_coding/codecs/av1/BUILD.gn @@ -23,6 +23,25 @@ rtc_library("av1_svc_config") { ] } +rtc_library("dav1d_decoder") { + poisonous = [ "software_video_codecs" ] + public = [ "dav1d_decoder.h" ] + sources = [ "dav1d_decoder.cc" ] + + deps = [ + "../..:video_codec_interface", + "../../../../api:scoped_refptr", + "../../../../api/video:encoded_image", + "../../../../api/video:video_frame", + "../../../../api/video_codecs:video_codecs_api", + "../../../../common_video", + "../../../../rtc_base:logging", + "//third_party/dav1d", + "//third_party/libyuv", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + rtc_library("libaom_av1_decoder") { visibility = [ "*" ] poisonous = [ "software_video_codecs" ] @@ -52,31 +71,46 @@ rtc_library("libaom_av1_encoder") { visibility = [ "*" ] poisonous = [ "software_video_codecs" ] public = [ "libaom_av1_encoder.h" ] + sources = [ "libaom_av1_encoder.cc" ] + deps = [ + "../..:video_codec_interface", + "../../../../api:scoped_refptr", + "../../../../api/video:encoded_image", + "../../../../api/video:video_frame", + "../../../../api/video_codecs:video_codecs_api", + "../../../../common_video", + "../../../../rtc_base:checks", + "../../../../rtc_base:logging", + "../../svc:scalability_structures", + "../../svc:scalable_video_controller", + "//third_party/libaom", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("libaom_av1_encoder_if_supported") { + visibility = [ "*" ] + poisonous = [ "software_video_codecs" ] + public = [ "libaom_av1_encoder_supported.h" ] + sources = [ "libaom_av1_encoder_supported.cc" ] deps = [ "../../../../api/video_codecs:video_codecs_api", "../../svc:scalability_structures", "../../svc:scalable_video_controller", ] absl_deps = [ - "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", - "//third_party/abseil-cpp/absl/types:optional", + "//third_party/abseil-cpp/absl/strings:strings", ] + defines = [] if (enable_libaom) { - sources = [ "libaom_av1_encoder.cc" ] - deps += [ - "../..:video_codec_interface", - "../../../../api:scoped_refptr", - "../../../../api/video:encoded_image", - "../../../../api/video:video_frame", - "../../../../common_video", - "../../../../rtc_base:checks", - "../../../../rtc_base:logging", - "//third_party/libaom", - ] - } else { - sources = [ "libaom_av1_encoder_absent.cc" ] + defines += [ "RTC_USE_LIBAOM_AV1_ENCODER" ] + deps += [ ":libaom_av1_encoder" ] } } diff --git a/modules/video_coding/codecs/av1/DEPS b/modules/video_coding/codecs/av1/DEPS index 25779919a7..bfb1c733d4 100644 --- a/modules/video_coding/codecs/av1/DEPS +++ b/modules/video_coding/codecs/av1/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+third_party/libaom", + "+third_party/dav1d", ] diff --git a/modules/video_coding/codecs/av1/av1_svc_config.cc b/modules/video_coding/codecs/av1/av1_svc_config.cc index b15443c563..2f1026b4df 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config.cc @@ -24,17 +24,19 @@ namespace webrtc { bool SetAv1SvcConfig(VideoCodec& video_codec) { RTC_DCHECK_EQ(video_codec.codecType, kVideoCodecAV1); - if (video_codec.ScalabilityMode().empty()) { - RTC_LOG(LS_INFO) << "No scalability mode set."; - return false; + absl::string_view scalability_mode = video_codec.ScalabilityMode(); + if (scalability_mode.empty()) { + RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'NONE'."; + scalability_mode = "NONE"; } + std::unique_ptr structure = - CreateScalabilityStructure(video_codec.ScalabilityMode()); + CreateScalabilityStructure(scalability_mode); if (structure == nullptr) { - RTC_LOG(LS_INFO) << "Failed to create structure " - << video_codec.ScalabilityMode(); + RTC_LOG(LS_WARNING) << "Failed to create structure " << scalability_mode; return false; } + ScalableVideoController::StreamLayersConfig info = structure->StreamConfig(); for (int sl_idx = 0; sl_idx < info.num_spatial_layers; ++sl_idx) { SpatialLayer& spatial_layer = video_codec.spatialLayers[sl_idx]; diff --git a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc index e6035328da..d8aec65652 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc @@ -21,9 +21,6 @@ TEST(Av1SvcConfigTest, RequireScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode(""); - EXPECT_FALSE(SetAv1SvcConfig(video_codec)); - video_codec.SetScalabilityMode("Unknown"); EXPECT_FALSE(SetAv1SvcConfig(video_codec)); @@ -31,6 +28,18 @@ TEST(Av1SvcConfigTest, RequireScalabilityMode) { EXPECT_TRUE(SetAv1SvcConfig(video_codec)); } +TEST(Av1SvcConfigTest, TreatsEmptyAsNone) { + VideoCodec video_codec; + video_codec.codecType = kVideoCodecAV1; + + video_codec.SetScalabilityMode(""); + EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + + EXPECT_TRUE(video_codec.spatialLayers[0].active); + EXPECT_EQ(video_codec.spatialLayers[0].numberOfTemporalLayers, 1); + EXPECT_FALSE(video_codec.spatialLayers[1].active); +} + TEST(Av1SvcConfigTest, SetsActiveSpatialLayersFromScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; diff --git a/modules/video_coding/codecs/av1/dav1d_decoder.cc b/modules/video_coding/codecs/av1/dav1d_decoder.cc new file mode 100644 index 0000000000..a5e4784839 --- /dev/null +++ b/modules/video_coding/codecs/av1/dav1d_decoder.cc @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/codecs/av1/dav1d_decoder.h" + +#include + +#include "api/scoped_refptr.h" +#include "api/video/encoded_image.h" +#include "api/video/i420_buffer.h" +#include "common_video/include/video_frame_buffer_pool.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/logging.h" +#include "third_party/dav1d/libdav1d/include/dav1d/dav1d.h" +#include "third_party/libyuv/include/libyuv/convert.h" + +namespace webrtc { +namespace { + +class Dav1dDecoder : public VideoDecoder { + public: + Dav1dDecoder(); + Dav1dDecoder(const Dav1dDecoder&) = delete; + Dav1dDecoder& operator=(const Dav1dDecoder&) = delete; + + ~Dav1dDecoder() override; + + bool Configure(const Settings& settings) override; + int32_t Decode(const EncodedImage& encoded_image, + bool missing_frames, + int64_t render_time_ms) override; + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override; + int32_t Release() override; + DecoderInfo GetDecoderInfo() const override; + const char* ImplementationName() const override; + + private: + VideoFrameBufferPool buffer_pool_; + Dav1dContext* context_ = nullptr; + DecodedImageCallback* decode_complete_callback_ = nullptr; +}; + +class ScopedDav1dData { + public: + ~ScopedDav1dData() { dav1d_data_unref(&data_); } + + Dav1dData& Data() { return data_; } + + private: + Dav1dData data_ = {}; +}; + +class ScopedDav1dPicture { + public: + ~ScopedDav1dPicture() { dav1d_picture_unref(&picture_); } + + Dav1dPicture& Picture() { return picture_; } + + private: + Dav1dPicture picture_ = {}; +}; + +constexpr char kDav1dName[] = "dav1d"; + +// Calling `dav1d_data_wrap` requires a `free_callback` to be registered. +void NullFreeCallback(const uint8_t* buffer, void* opaque) {} + +Dav1dDecoder::Dav1dDecoder() + : buffer_pool_(/*zero_initialize=*/false, /*max_number_of_buffers=*/150) {} + +Dav1dDecoder::~Dav1dDecoder() { + Release(); +} + +bool Dav1dDecoder::Configure(const Settings& settings) { + Dav1dSettings s; + dav1d_default_settings(&s); + + s.n_threads = std::max(2, settings.number_of_cores()); + s.max_frame_delay = 1; // For low latency decoding. + s.all_layers = 0; // Don't output a frame for every spatial layer. + s.operating_point = 31; // Decode all operating points. + + return dav1d_open(&context_, &s) == 0; +} + +int32_t Dav1dDecoder::RegisterDecodeCompleteCallback( + DecodedImageCallback* decode_complete_callback) { + decode_complete_callback_ = decode_complete_callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t Dav1dDecoder::Release() { + dav1d_close(&context_); + if (context_ != nullptr) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + buffer_pool_.Release(); + return WEBRTC_VIDEO_CODEC_OK; +} + +VideoDecoder::DecoderInfo Dav1dDecoder::GetDecoderInfo() const { + DecoderInfo info; + info.implementation_name = kDav1dName; + info.is_hardware_accelerated = false; + return info; +} + +const char* Dav1dDecoder::ImplementationName() const { + return kDav1dName; +} + +int32_t Dav1dDecoder::Decode(const EncodedImage& encoded_image, + bool /*missing_frames*/, + int64_t /*render_time_ms*/) { + if (!context_ || decode_complete_callback_ == nullptr) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + + ScopedDav1dData scoped_dav1d_data; + Dav1dData& dav1d_data = scoped_dav1d_data.Data(); + dav1d_data_wrap(&dav1d_data, encoded_image.data(), encoded_image.size(), + /*free_callback=*/&NullFreeCallback, + /*user_data=*/nullptr); + + if (int decode_res = dav1d_send_data(context_, &dav1d_data)) { + RTC_LOG(LS_WARNING) + << "Dav1dDecoder::Decode decoding failed with error code " + << decode_res; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + ScopedDav1dPicture scoped_dav1d_picture; + Dav1dPicture& dav1d_picture = scoped_dav1d_picture.Picture(); + if (int get_picture_res = dav1d_get_picture(context_, &dav1d_picture)) { + RTC_LOG(LS_WARNING) + << "Dav1dDecoder::Decode getting picture failed with error code " + << get_picture_res; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + // Only accept I420 pixel format and 8 bit depth. + if (dav1d_picture.p.layout != DAV1D_PIXEL_LAYOUT_I420 || + dav1d_picture.p.bpc != 8) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + rtc::scoped_refptr buffer = + buffer_pool_.CreateI420Buffer(dav1d_picture.p.w, dav1d_picture.p.h); + if (!buffer.get()) { + RTC_LOG(LS_WARNING) + << "Dav1dDecoder::Decode failed to get frame from the buffer pool."; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + uint8_t* y_data = static_cast(dav1d_picture.data[0]); + uint8_t* u_data = static_cast(dav1d_picture.data[1]); + uint8_t* v_data = static_cast(dav1d_picture.data[2]); + int y_stride = dav1d_picture.stride[0]; + int uv_stride = dav1d_picture.stride[1]; + libyuv::I420Copy(y_data, y_stride, // + u_data, uv_stride, // + v_data, uv_stride, // + buffer->MutableDataY(), buffer->StrideY(), // + buffer->MutableDataU(), buffer->StrideU(), // + buffer->MutableDataV(), buffer->StrideV(), // + dav1d_picture.p.w, // + dav1d_picture.p.h); // + + VideoFrame decoded_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_timestamp_rtp(encoded_image.Timestamp()) + .set_ntp_time_ms(encoded_image.ntp_time_ms_) + .set_color_space(encoded_image.ColorSpace()) + .build(); + + decode_complete_callback_->Decoded(decoded_frame, absl::nullopt, + absl::nullopt); + + return WEBRTC_VIDEO_CODEC_OK; +} + +} // namespace + +std::unique_ptr CreateDav1dDecoder() { + return std::make_unique(); +} + +} // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_absent.cc b/modules/video_coding/codecs/av1/dav1d_decoder.h similarity index 55% rename from modules/video_coding/codecs/av1/libaom_av1_encoder_absent.cc rename to modules/video_coding/codecs/av1/dav1d_decoder.h index f394260865..c9396d1e03 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_absent.cc +++ b/modules/video_coding/codecs/av1/dav1d_decoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 The WebRTC project 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 @@ -7,18 +7,17 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" +#ifndef MODULES_VIDEO_CODING_CODECS_AV1_DAV1D_DECODER_H_ +#define MODULES_VIDEO_CODING_CODECS_AV1_DAV1D_DECODER_H_ #include -#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_decoder.h" namespace webrtc { -const bool kIsLibaomAv1EncoderSupported = false; - -std::unique_ptr CreateLibaomAv1Encoder() { - return nullptr; -} +std::unique_ptr CreateDav1dDecoder(); } // namespace webrtc + +#endif // MODULES_VIDEO_CODING_CODECS_AV1_DAV1D_DECODER_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc index a814c745c3..0e427be915 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc @@ -49,21 +49,6 @@ constexpr int kLagInFrames = 0; // No look ahead. constexpr int kRtpTicksPerSecond = 90000; constexpr float kMinimumFrameRate = 1.0; -// Only positive speeds, range for real-time coding currently is: 6 - 8. -// Lower means slower/better quality, higher means fastest/lower quality. -int GetCpuSpeed(int width, int height, int number_of_cores) { - // For smaller resolutions, use lower speed setting (get some coding gain at - // the cost of increased encoding complexity). - if (number_of_cores > 4 && width * height < 320 * 180) - return 6; - else if (width * height >= 1280 * 720) - return 9; - else if (width * height >= 640 * 360) - return 8; - else - return 7; -} - aom_superblock_size_t GetSuperblockSize(int width, int height, int threads) { int resolution = width * height; if (threads >= 4 && resolution >= 960 * 540 && resolution < 1920 * 1080) @@ -93,6 +78,9 @@ class LibaomAv1Encoder final : public VideoEncoder { EncoderInfo GetEncoderInfo() const override; private: + // Get value to be used for encoder cpu_speed setting + int GetCpuSpeed(int width, int height); + // Determine number of encoder threads to use. int NumberOfThreads(int width, int height, int number_of_cores); @@ -108,6 +96,7 @@ class LibaomAv1Encoder final : public VideoEncoder { std::unique_ptr svc_controller_; bool inited_; + bool rates_configured_; absl::optional svc_params_; VideoCodec encoder_settings_; aom_image_t* frame_for_encode_; @@ -143,6 +132,7 @@ int32_t VerifyCodecSettings(const VideoCodec& codec_settings) { LibaomAv1Encoder::LibaomAv1Encoder() : inited_(false), + rates_configured_(false), frame_for_encode_(nullptr), encoded_image_callback_(nullptr) {} @@ -245,9 +235,8 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, inited_ = true; // Set control parameters - ret = aom_codec_control( - &ctx_, AOME_SET_CPUUSED, - GetCpuSpeed(cfg_.g_w, cfg_.g_h, settings.number_of_cores)); + ret = aom_codec_control(&ctx_, AOME_SET_CPUUSED, + GetCpuSpeed(cfg_.g_w, cfg_.g_h)); if (ret != AOM_CODEC_OK) { RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret << " on control AV1E_SET_CPUUSED."; @@ -283,15 +272,6 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, << " on control AV1E_SET_AQ_MODE."; return WEBRTC_VIDEO_CODEC_ERROR; } - if (SvcEnabled()) { - ret = aom_codec_control(&ctx_, AV1E_SET_SVC_PARAMS, &*svc_params_); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAV1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_SVC_PARAMS."; - return false; - } - } - ret = aom_codec_control(&ctx_, AOME_SET_MAX_INTRA_BITRATE_PCT, 300); if (ret != AOM_CODEC_OK) { RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret @@ -426,6 +406,42 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, return WEBRTC_VIDEO_CODEC_OK; } +// Only positive speeds, range for real-time coding currently is: 6 - 8. +// Lower means slower/better quality, higher means fastest/lower quality. +int LibaomAv1Encoder::GetCpuSpeed(int width, int height) { + // For smaller resolutions, use lower speed setting (get some coding gain at + // the cost of increased encoding complexity). + switch (encoder_settings_.GetVideoEncoderComplexity()) { + case VideoCodecComplexity::kComplexityHigh: + if (width * height <= 320 * 180) + return 8; + else if (width * height <= 640 * 360) + return 9; + else + return 10; + case VideoCodecComplexity::kComplexityHigher: + if (width * height <= 320 * 180) + return 7; + else if (width * height <= 640 * 360) + return 8; + else if (width * height <= 1280 * 720) + return 9; + else + return 10; + case VideoCodecComplexity::kComplexityMax: + if (width * height <= 320 * 180) + return 6; + else if (width * height <= 640 * 360) + return 7; + else if (width * height <= 1280 * 720) + return 8; + else + return 9; + default: + return 10; + } +} + int LibaomAv1Encoder::NumberOfThreads(int width, int height, int number_of_cores) { @@ -558,13 +574,14 @@ int32_t LibaomAv1Encoder::Release() { } inited_ = false; } + rates_configured_ = false; return WEBRTC_VIDEO_CODEC_OK; } int32_t LibaomAv1Encoder::Encode( const VideoFrame& frame, const std::vector* frame_types) { - if (!inited_ || encoded_image_callback_ == nullptr) { + if (!inited_ || encoded_image_callback_ == nullptr || !rates_configured_) { return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } @@ -616,20 +633,37 @@ int32_t LibaomAv1Encoder::Encode( const uint32_t duration = kRtpTicksPerSecond / static_cast(encoder_settings_.maxFramerate); - for (size_t i = 0; i < layer_frames.size(); ++i) { - ScalableVideoController::LayerFrameConfig& layer_frame = layer_frames[i]; - const bool end_of_picture = i == layer_frames.size() - 1; + const size_t num_spatial_layers = + svc_params_ ? svc_params_->number_spatial_layers : 1; + auto next_layer_frame = layer_frames.begin(); + for (size_t i = 0; i < num_spatial_layers; ++i) { + // The libaom AV1 encoder requires that `aom_codec_encode` is called for + // every spatial layer, even if the configured bitrate for that layer is + // zero. For zero bitrate spatial layers no frames will be produced. + absl::optional + non_encoded_layer_frame; + ScalableVideoController::LayerFrameConfig* layer_frame; + if (next_layer_frame != layer_frames.end() && + next_layer_frame->SpatialId() == static_cast(i)) { + layer_frame = &*next_layer_frame; + ++next_layer_frame; + } else { + // For layers that are not encoded only the spatial id matters. + non_encoded_layer_frame.emplace().S(i); + layer_frame = &*non_encoded_layer_frame; + } + const bool end_of_picture = (next_layer_frame == layer_frames.end()); aom_enc_frame_flags_t flags = - layer_frame.IsKeyframe() ? AOM_EFLAG_FORCE_KF : 0; + layer_frame->IsKeyframe() ? AOM_EFLAG_FORCE_KF : 0; if (SvcEnabled()) { - SetSvcLayerId(layer_frame); - SetSvcRefFrameConfig(layer_frame); + SetSvcLayerId(*layer_frame); + SetSvcRefFrameConfig(*layer_frame); aom_codec_err_t ret = aom_codec_control(&ctx_, AV1E_SET_ERROR_RESILIENT_MODE, - layer_frame.TemporalId() > 0 ? 1 : 0); + layer_frame->TemporalId() > 0 ? 1 : 0); if (ret != AOM_CODEC_OK) { RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret << " on control AV1E_SET_ERROR_RESILIENT_MODE."; @@ -646,6 +680,10 @@ int32_t LibaomAv1Encoder::Encode( return WEBRTC_VIDEO_CODEC_ERROR; } + if (non_encoded_layer_frame) { + continue; + } + // Get encoded image data. EncodedImage encoded_image; aom_codec_iter_t iter = nullptr; @@ -663,9 +701,10 @@ int32_t LibaomAv1Encoder::Encode( /*size=*/pkt->data.frame.sz)); if ((pkt->data.frame.flags & AOM_EFLAG_FORCE_KF) != 0) { - layer_frame.Keyframe(); + layer_frame->Keyframe(); } - encoded_image._frameType = layer_frame.IsKeyframe() + + encoded_image._frameType = layer_frame->IsKeyframe() ? VideoFrameType::kVideoFrameKey : VideoFrameType::kVideoFrameDelta; encoded_image.SetTimestamp(frame.timestamp()); @@ -675,10 +714,11 @@ int32_t LibaomAv1Encoder::Encode( // If encoded image width/height info are added to aom_codec_cx_pkt_t, // use those values in lieu of the values in frame. if (svc_params_) { - int n = svc_params_->scaling_factor_num[layer_frame.SpatialId()]; - int d = svc_params_->scaling_factor_den[layer_frame.SpatialId()]; + int n = svc_params_->scaling_factor_num[layer_frame->SpatialId()]; + int d = svc_params_->scaling_factor_den[layer_frame->SpatialId()]; encoded_image._encodedWidth = cfg_.g_w * n / d; encoded_image._encodedHeight = cfg_.g_h * n / d; + encoded_image.SetSpatialIndex(layer_frame->SpatialId()); } else { encoded_image._encodedWidth = cfg_.g_w; encoded_image._encodedHeight = cfg_.g_h; @@ -702,9 +742,9 @@ int32_t LibaomAv1Encoder::Encode( CodecSpecificInfo codec_specific_info; codec_specific_info.codecType = kVideoCodecAV1; codec_specific_info.end_of_picture = end_of_picture; - bool is_keyframe = layer_frame.IsKeyframe(); + bool is_keyframe = layer_frame->IsKeyframe(); codec_specific_info.generic_frame_info = - svc_controller_->OnEncodeDone(std::move(layer_frame)); + svc_controller_->OnEncodeDone(*layer_frame); if (is_keyframe && codec_specific_info.generic_frame_info) { codec_specific_info.template_structure = svc_controller_->DependencyStructure(); @@ -745,8 +785,16 @@ void LibaomAv1Encoder::SetRates(const RateControlParameters& parameters) { return; } + // The bitrates caluclated internally in libaom when `AV1E_SET_SVC_PARAMS` is + // called depends on the currently configured `rc_target_bitrate`. If the + // total target bitrate is not updated first a division by zero could happen. svc_controller_->OnRatesUpdated(parameters.bitrate); cfg_.rc_target_bitrate = parameters.bitrate.get_sum_kbps(); + aom_codec_err_t error_code = aom_codec_enc_config_set(&ctx_, &cfg_); + if (error_code != AOM_CODEC_OK) { + RTC_LOG(LS_WARNING) << "Error configuring encoder, error code: " + << error_code; + } if (SvcEnabled()) { for (int sid = 0; sid < svc_params_->number_spatial_layers; ++sid) { @@ -766,16 +814,11 @@ void LibaomAv1Encoder::SetRates(const RateControlParameters& parameters) { aom_codec_control(&ctx_, AV1E_SET_SVC_PARAMS, &*svc_params_); } + rates_configured_ = true; + // Set frame rate to closest integer value. encoder_settings_.maxFramerate = static_cast(parameters.framerate_fps + 0.5); - - // Update encoder context. - aom_codec_err_t error_code = aom_codec_enc_config_set(&ctx_, &cfg_); - if (error_code != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "Error configuring encoder, error code: " - << error_code; - } } VideoEncoder::EncoderInfo LibaomAv1Encoder::GetEncoderInfo() const { @@ -800,8 +843,6 @@ VideoEncoder::EncoderInfo LibaomAv1Encoder::GetEncoderInfo() const { } // namespace -const bool kIsLibaomAv1EncoderSupported = true; - std::unique_ptr CreateLibaomAv1Encoder() { return std::make_unique(); } diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.h b/modules/video_coding/codecs/av1/libaom_av1_encoder.h index 4b0ee28d40..e69df9e8bb 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder.h +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.h @@ -12,15 +12,11 @@ #include -#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "api/video_codecs/video_encoder.h" namespace webrtc { - -ABSL_CONST_INIT extern const bool kIsLibaomAv1EncoderSupported; - std::unique_ptr CreateLibaomAv1Encoder(); - } // namespace webrtc #endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc new file mode 100644 index 0000000000..0bb31085e2 --- /dev/null +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h" + +#include "modules/video_coding/svc/create_scalability_structure.h" + +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) +#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" // nogncheck +#endif + +namespace webrtc { +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) +const bool kIsLibaomAv1EncoderSupported = true; +std::unique_ptr CreateLibaomAv1EncoderIfSupported() { + return CreateLibaomAv1Encoder(); +} +bool LibaomAv1EncoderSupportsScalabilityMode( + absl::string_view scalability_mode) { + // For libaom AV1, the scalability mode is supported if we can create the + // scalability structure. + return ScalabilityStructureConfig(scalability_mode) != absl::nullopt; +} +#else +const bool kIsLibaomAv1EncoderSupported = false; +std::unique_ptr CreateLibaomAv1EncoderIfSupported() { + return nullptr; +} +bool LibaomAv1EncoderSupportsScalabilityMode( + absl::string_view scalability_mode) { + return false; +} +#endif + +} // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h new file mode 100644 index 0000000000..84dd8d6002 --- /dev/null +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ +#define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/video_codecs/video_encoder.h" + +namespace webrtc { + +ABSL_CONST_INIT extern const bool kIsLibaomAv1EncoderSupported; + +std::unique_ptr CreateLibaomAv1EncoderIfSupported(); +bool LibaomAv1EncoderSupportsScalabilityMode( + absl::string_view scalability_mode); + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc index 96057a0ce2..0c67e4dde4 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc @@ -25,6 +25,7 @@ namespace webrtc { namespace { using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::Field; using ::testing::IsEmpty; using ::testing::SizeIs; @@ -85,14 +86,68 @@ TEST(LibaomAv1EncoderTest, NoBitrateOnTopLayerRefecltedInActiveDecodeTargets) { 0b01); } +TEST(LibaomAv1EncoderTest, + SpatialScalabilityInTemporalUnitReportedAsDeltaFrame) { + std::unique_ptr encoder = CreateLibaomAv1Encoder(); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.SetScalabilityMode("L2T1"); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + rate_parameters.bitrate.SetBitrate(/*spatial_index=*/0, 0, 300'000); + rate_parameters.bitrate.SetBitrate(/*spatial_index=*/1, 0, 300'000); + encoder->SetRates(rate_parameters); + + std::vector encoded_frames = + EncodedVideoFrameProducer(*encoder).SetNumInputFrames(1).Encode(); + ASSERT_THAT(encoded_frames, SizeIs(2)); + EXPECT_THAT(encoded_frames[0].encoded_image._frameType, + Eq(VideoFrameType::kVideoFrameKey)); + EXPECT_THAT(encoded_frames[1].encoded_image._frameType, + Eq(VideoFrameType::kVideoFrameDelta)); +} + +TEST(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { + std::unique_ptr encoder = CreateLibaomAv1Encoder(); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.SetScalabilityMode("L2T1"); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + rate_parameters.bitrate.SetBitrate(/*spatial_index=*/0, 0, 300'000); + rate_parameters.bitrate.SetBitrate(/*spatial_index=*/1, 0, 0); + encoder->SetRates(rate_parameters); + + std::vector encoded_frames = + EncodedVideoFrameProducer(*encoder).SetNumInputFrames(2).Encode(); + ASSERT_THAT(encoded_frames, SizeIs(2)); + EXPECT_THAT(encoded_frames[0].encoded_image._frameType, + Eq(VideoFrameType::kVideoFrameKey)); + EXPECT_THAT(encoded_frames[1].encoded_image._frameType, + Eq(VideoFrameType::kVideoFrameDelta)); +} + TEST(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 30000); + allocation.SetBitrate(1, 0, 40000); + allocation.SetBitrate(2, 0, 30000); + std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. codec_settings.SetScalabilityMode("L3T1"); + codec_settings.maxBitrate = allocation.get_sum_kbps(); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); + std::vector encoded_frames = EncodedVideoFrameProducer(*encoder).SetNumInputFrames(2).Encode(); ASSERT_THAT(encoded_frames, SizeIs(6)); @@ -105,6 +160,10 @@ TEST(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { } TEST(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 30000); + allocation.SetBitrate(1, 0, 40000); + allocation.SetBitrate(2, 0, 30000); std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. @@ -112,8 +171,11 @@ TEST(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { // Odd width and height values should not make encoder crash. codec_settings.width = 623; codec_settings.height = 405; + codec_settings.maxBitrate = allocation.get_sum_kbps(); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); EncodedVideoFrameProducer evfp(*encoder); evfp.SetResolution(RenderResolution{623, 405}); std::vector encoded_frames = @@ -137,14 +199,20 @@ TEST(LibaomAv1EncoderTest, EncoderInfoProvidesFpsAllocation) { } TEST(LibaomAv1EncoderTest, PopulatesEncodedFrameSize) { + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 30000); + allocation.SetBitrate(1, 0, 40000); + allocation.SetBitrate(2, 0, 30000); std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.maxBitrate = allocation.get_sum_kbps(); ASSERT_GT(codec_settings.width, 4); // Configure encoder with 3 spatial layers. codec_settings.SetScalabilityMode("L3T1"); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); - + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); using Frame = EncodedVideoFrameProducer::EncodedFrame; std::vector encoded_frames = EncodedVideoFrameProducer(*encoder).SetNumInputFrames(1).Encode(); diff --git a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc index 5823266952..f97808e631 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc @@ -137,6 +137,11 @@ TEST(LibaomAv1Test, EncodeDecode) { ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 300000); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); + std::vector encoded_frames = EncodedVideoFrameProducer(*encoder).SetNumInputFrames(4).Encode(); for (size_t frame_id = 0; frame_id < encoded_frames.size(); ++frame_id) { @@ -178,15 +183,36 @@ struct SvcTestParam { class LibaomAv1SvcTest : public ::testing::TestWithParam {}; TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) { - size_t num_decode_targets = CreateScalabilityStructure(GetParam().name) - ->DependencyStructure() - .num_decode_targets; + const SvcTestParam param = GetParam(); + std::unique_ptr svc_controller = + CreateScalabilityStructure(param.name); + ASSERT_TRUE(svc_controller); + VideoBitrateAllocation allocation; + if (param.configured_bitrates.empty()) { + ScalableVideoController::StreamLayersConfig config = + svc_controller->StreamConfig(); + for (int sid = 0; sid < config.num_spatial_layers; ++sid) { + for (int tid = 0; tid < config.num_temporal_layers; ++tid) { + allocation.SetBitrate(sid, tid, 100'000); + } + } + } else { + for (const auto& kv : param.configured_bitrates) { + allocation.SetBitrate(kv.first.spatial_id, kv.first.temporal_id, + kv.second.bps()); + } + } + + size_t num_decode_targets = + svc_controller->DependencyStructure().num_decode_targets; std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(GetParam().name); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); std::vector encoded_frames = EncodedVideoFrameProducer(*encoder) .SetNumInputFrames(GetParam().num_frames_to_generate) diff --git a/modules/video_coding/codecs/h264/h264.cc b/modules/video_coding/codecs/h264/h264.cc index 14e1691153..2ac19ba0b6 100644 --- a/modules/video_coding/codecs/h264/h264.cc +++ b/modules/video_coding/codecs/h264/h264.cc @@ -44,6 +44,8 @@ bool IsH264CodecSupported() { #endif } +constexpr absl::string_view kSupportedScalabilityModes[] = {"L1T2", "L1T3"}; + } // namespace SdpVideoFormat CreateH264Format(H264Profile profile, @@ -78,14 +80,34 @@ std::vector SupportedH264Codecs() { // // We support both packetization modes 0 (mandatory) and 1 (optional, // preferred). - return {CreateH264Format(H264Profile::kProfileBaseline, H264Level::kLevel3_1, - "1"), - CreateH264Format(H264Profile::kProfileBaseline, H264Level::kLevel3_1, - "0"), - CreateH264Format(H264Profile::kProfileConstrainedBaseline, - H264Level::kLevel3_1, "1"), - CreateH264Format(H264Profile::kProfileConstrainedBaseline, - H264Level::kLevel3_1, "0")}; + return { + CreateH264Format(H264Profile::kProfileBaseline, H264Level::kLevel3_1, + "1"), + CreateH264Format(H264Profile::kProfileBaseline, H264Level::kLevel3_1, + "0"), + CreateH264Format(H264Profile::kProfileConstrainedBaseline, + H264Level::kLevel3_1, "1"), + CreateH264Format(H264Profile::kProfileConstrainedBaseline, + H264Level::kLevel3_1, "0"), + CreateH264Format(H264Profile::kProfileMain, H264Level::kLevel3_1, "1"), + CreateH264Format(H264Profile::kProfileMain, H264Level::kLevel3_1, "0")}; +} + +std::vector SupportedH264DecoderCodecs() { + TRACE_EVENT0("webrtc", __func__); + if (!IsH264CodecSupported()) + return std::vector(); + + std::vector supportedCodecs = SupportedH264Codecs(); + + // OpenH264 doesn't yet support High Predictive 4:4:4 encoding but it does + // support decoding. + supportedCodecs.push_back(CreateH264Format( + H264Profile::kProfilePredictiveHigh444, H264Level::kLevel3_1, "1")); + supportedCodecs.push_back(CreateH264Format( + H264Profile::kProfilePredictiveHigh444, H264Level::kLevel3_1, "0")); + + return supportedCodecs; } std::unique_ptr H264Encoder::Create( @@ -96,7 +118,7 @@ std::unique_ptr H264Encoder::Create( RTC_LOG(LS_INFO) << "Creating H264EncoderImpl."; return std::make_unique(codec); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif } @@ -105,6 +127,15 @@ bool H264Encoder::IsSupported() { return IsH264CodecSupported(); } +bool H264Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { + for (const auto& entry : kSupportedScalabilityModes) { + if (entry == scalability_mode) { + return true; + } + } + return false; +} + std::unique_ptr H264Decoder::Create() { RTC_DCHECK(H264Decoder::IsSupported()); #if defined(WEBRTC_USE_H264) @@ -112,7 +143,7 @@ std::unique_ptr H264Decoder::Create() { RTC_LOG(LS_INFO) << "Creating H264DecoderImpl."; return std::make_unique(); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif } diff --git a/modules/video_coding/codecs/h264/h264_decoder_impl.cc b/modules/video_coding/codecs/h264/h264_decoder_impl.cc index b42aac5330..31279b7379 100644 --- a/modules/video_coding/codecs/h264/h264_decoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_decoder_impl.cc @@ -41,8 +41,10 @@ namespace webrtc { namespace { -const AVPixelFormat kPixelFormatDefault = AV_PIX_FMT_YUV420P; -const AVPixelFormat kPixelFormatFullRange = AV_PIX_FMT_YUVJ420P; +constexpr std::array kPixelFormatsDefault = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P}; +constexpr std::array kPixelFormatsFullRange = { + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P}; const size_t kYPlaneIndex = 0; const size_t kUPlaneIndex = 1; const size_t kVPlaneIndex = 2; @@ -76,9 +78,17 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, // Necessary capability to be allowed to provide our own buffers. RTC_DCHECK(context->codec->capabilities | AV_CODEC_CAP_DR1); + // Limited or full range YUV420 or YUV444 is expected. + auto pixelFormatDefault = std::find_if( + kPixelFormatsDefault.begin(), kPixelFormatsDefault.end(), + [context](AVPixelFormat format) { return context->pix_fmt == format; }); + auto pixelFormatFullRange = std::find_if( + kPixelFormatsFullRange.begin(), kPixelFormatsFullRange.end(), + [context](AVPixelFormat format) { return context->pix_fmt == format; }); + // Limited or full range YUV420 is expected. - RTC_CHECK(context->pix_fmt == kPixelFormatDefault || - context->pix_fmt == kPixelFormatFullRange); + RTC_CHECK(pixelFormatDefault != kPixelFormatsDefault.end() || + pixelFormatFullRange != kPixelFormatsFullRange.end()); // `av_frame->width` and `av_frame->height` are set by FFmpeg. These are the // actual image's dimensions and may be different from `context->width` and @@ -112,8 +122,43 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, // http://crbug.com/390941. Our pool is set up to zero-initialize new buffers. // TODO(nisse): Delete that feature from the video pool, instead add // an explicit call to InitializeData here. - rtc::scoped_refptr frame_buffer = - decoder->ffmpeg_buffer_pool_.CreateI420Buffer(width, height); + rtc::scoped_refptr frame_buffer; + rtc::scoped_refptr i444_buffer; + rtc::scoped_refptr i420_buffer; + switch (context->pix_fmt) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + i420_buffer = + decoder->ffmpeg_buffer_pool_.CreateI420Buffer(width, height); + // Set `av_frame` members as required by FFmpeg. + av_frame->data[kYPlaneIndex] = i420_buffer->MutableDataY(); + av_frame->linesize[kYPlaneIndex] = i420_buffer->StrideY(); + av_frame->data[kUPlaneIndex] = i420_buffer->MutableDataU(); + av_frame->linesize[kUPlaneIndex] = i420_buffer->StrideU(); + av_frame->data[kVPlaneIndex] = i420_buffer->MutableDataV(); + av_frame->linesize[kVPlaneIndex] = i420_buffer->StrideV(); + RTC_DCHECK_EQ(av_frame->extended_data, av_frame->data); + frame_buffer = i420_buffer; + break; + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUVJ444P: + i444_buffer = + decoder->ffmpeg_buffer_pool_.CreateI444Buffer(width, height); + // Set `av_frame` members as required by FFmpeg. + av_frame->data[kYPlaneIndex] = i444_buffer->MutableDataY(); + av_frame->linesize[kYPlaneIndex] = i444_buffer->StrideY(); + av_frame->data[kUPlaneIndex] = i444_buffer->MutableDataU(); + av_frame->linesize[kUPlaneIndex] = i444_buffer->StrideU(); + av_frame->data[kVPlaneIndex] = i444_buffer->MutableDataV(); + av_frame->linesize[kVPlaneIndex] = i444_buffer->StrideV(); + frame_buffer = i444_buffer; + break; + default: + RTC_LOG(LS_ERROR) << "Unsupported buffer type " << context->pix_fmt + << ". Check supported supported pixel formats!"; + decoder->ReportError(); + return -1; + } int y_size = width * height; int uv_size = frame_buffer->ChromaWidth() * frame_buffer->ChromaHeight(); @@ -125,15 +170,6 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, av_frame->format = context->pix_fmt; av_frame->reordered_opaque = context->reordered_opaque; - // Set `av_frame` members as required by FFmpeg. - av_frame->data[kYPlaneIndex] = frame_buffer->MutableDataY(); - av_frame->linesize[kYPlaneIndex] = frame_buffer->StrideY(); - av_frame->data[kUPlaneIndex] = frame_buffer->MutableDataU(); - av_frame->linesize[kUPlaneIndex] = frame_buffer->StrideU(); - av_frame->data[kVPlaneIndex] = frame_buffer->MutableDataV(); - av_frame->linesize[kVPlaneIndex] = frame_buffer->StrideV(); - RTC_DCHECK_EQ(av_frame->extended_data, av_frame->data); - // Create a VideoFrame object, to keep a reference to the buffer. // TODO(nisse): The VideoFrame's timestamp and rotation info is not used. // Refactor to do not use a VideoFrame object at all. @@ -197,7 +233,6 @@ bool H264DecoderImpl::Configure(const Settings& settings) { av_context_->coded_width = resolution.Width(); av_context_->coded_height = resolution.Height(); } - av_context_->pix_fmt = kPixelFormatDefault; av_context_->extradata = nullptr; av_context_->extradata_size = 0; @@ -317,47 +352,103 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, RTC_DCHECK(input_frame); rtc::scoped_refptr frame_buffer = input_frame->video_frame_buffer(); - const webrtc::I420BufferInterface* i420_buffer = frame_buffer->GetI420(); + + // Instantiate Planar YUV8 buffer according to video frame buffer type + const webrtc::PlanarYuv8Buffer* planar_yuv8_buffer = nullptr; + VideoFrameBuffer::Type video_frame_buffer_type = frame_buffer->type(); + switch (video_frame_buffer_type) { + case VideoFrameBuffer::Type::kI420: + planar_yuv8_buffer = frame_buffer->GetI420(); + break; + case VideoFrameBuffer::Type::kI444: + planar_yuv8_buffer = frame_buffer->GetI444(); + break; + default: + // If this code is changed to allow other video frame buffer type, + // make sure that the code below which wraps I420/I444 buffer and + // code which converts to NV12 is changed + // to work with new video frame buffer type + + RTC_LOG(LS_ERROR) << "frame_buffer type: " + << static_cast(video_frame_buffer_type) + << " is not supported!"; + ReportError(); + return WEBRTC_VIDEO_CODEC_ERROR; + } // When needed, FFmpeg applies cropping by moving plane pointers and adjusting // frame width/height. Ensure that cropped buffers lie within the allocated // memory. - RTC_DCHECK_LE(av_frame_->width, i420_buffer->width()); - RTC_DCHECK_LE(av_frame_->height, i420_buffer->height()); - RTC_DCHECK_GE(av_frame_->data[kYPlaneIndex], i420_buffer->DataY()); - RTC_DCHECK_LE( - av_frame_->data[kYPlaneIndex] + - av_frame_->linesize[kYPlaneIndex] * av_frame_->height, - i420_buffer->DataY() + i420_buffer->StrideY() * i420_buffer->height()); - RTC_DCHECK_GE(av_frame_->data[kUPlaneIndex], i420_buffer->DataU()); + RTC_DCHECK_LE(av_frame_->width, planar_yuv8_buffer->width()); + RTC_DCHECK_LE(av_frame_->height, planar_yuv8_buffer->height()); + RTC_DCHECK_GE(av_frame_->data[kYPlaneIndex], planar_yuv8_buffer->DataY()); + RTC_DCHECK_LE(av_frame_->data[kYPlaneIndex] + + av_frame_->linesize[kYPlaneIndex] * av_frame_->height, + planar_yuv8_buffer->DataY() + planar_yuv8_buffer->StrideY() * + planar_yuv8_buffer->height()); + RTC_DCHECK_GE(av_frame_->data[kUPlaneIndex], planar_yuv8_buffer->DataU()); RTC_DCHECK_LE(av_frame_->data[kUPlaneIndex] + av_frame_->linesize[kUPlaneIndex] * av_frame_->height / 2, - i420_buffer->DataU() + - i420_buffer->StrideU() * i420_buffer->height() / 2); - RTC_DCHECK_GE(av_frame_->data[kVPlaneIndex], i420_buffer->DataV()); + planar_yuv8_buffer->DataU() + planar_yuv8_buffer->StrideU() * + planar_yuv8_buffer->height() / + 2); + RTC_DCHECK_GE(av_frame_->data[kVPlaneIndex], planar_yuv8_buffer->DataV()); RTC_DCHECK_LE(av_frame_->data[kVPlaneIndex] + av_frame_->linesize[kVPlaneIndex] * av_frame_->height / 2, - i420_buffer->DataV() + - i420_buffer->StrideV() * i420_buffer->height() / 2); + planar_yuv8_buffer->DataV() + planar_yuv8_buffer->StrideV() * + planar_yuv8_buffer->height() / + 2); - rtc::scoped_refptr cropped_buffer = WrapI420Buffer( - av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], - av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], - av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], - av_frame_->linesize[kVPlaneIndex], - // To keep reference alive. - [frame_buffer] {}); + rtc::scoped_refptr cropped_buffer; + if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) { + cropped_buffer = WrapI420Buffer( + av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], + av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], + av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], + av_frame_->linesize[kVPlaneIndex], + // To keep reference alive. + [frame_buffer] {}); + } else { + cropped_buffer = WrapI444Buffer( + av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], + av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], + av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], + av_frame_->linesize[kVPlaneIndex], + // To keep reference alive. + [frame_buffer] {}); + } if (preferred_output_format_ == VideoFrameBuffer::Type::kNV12) { - const I420BufferInterface* cropped_i420 = cropped_buffer->GetI420(); auto nv12_buffer = output_buffer_pool_.CreateNV12Buffer( - cropped_i420->width(), cropped_i420->height()); - libyuv::I420ToNV12(cropped_i420->DataY(), cropped_i420->StrideY(), - cropped_i420->DataU(), cropped_i420->StrideU(), - cropped_i420->DataV(), cropped_i420->StrideV(), - nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), - nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(), - i420_buffer->width(), i420_buffer->height()); + cropped_buffer->width(), cropped_buffer->height()); + + const PlanarYuv8Buffer* cropped_planar_yuv8_buffer = nullptr; + if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) { + cropped_planar_yuv8_buffer = cropped_buffer->GetI420(); + libyuv::I420ToNV12(cropped_planar_yuv8_buffer->DataY(), + cropped_planar_yuv8_buffer->StrideY(), + cropped_planar_yuv8_buffer->DataU(), + cropped_planar_yuv8_buffer->StrideU(), + cropped_planar_yuv8_buffer->DataV(), + cropped_planar_yuv8_buffer->StrideV(), + nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), + nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(), + planar_yuv8_buffer->width(), + planar_yuv8_buffer->height()); + } else { + cropped_planar_yuv8_buffer = cropped_buffer->GetI444(); + libyuv::I444ToNV12(cropped_planar_yuv8_buffer->DataY(), + cropped_planar_yuv8_buffer->StrideY(), + cropped_planar_yuv8_buffer->DataU(), + cropped_planar_yuv8_buffer->StrideU(), + cropped_planar_yuv8_buffer->DataV(), + cropped_planar_yuv8_buffer->StrideV(), + nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), + nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(), + planar_yuv8_buffer->width(), + planar_yuv8_buffer->height()); + } + cropped_buffer = nv12_buffer; } diff --git a/modules/video_coding/codecs/h264/h264_encoder_impl.cc b/modules/video_coding/codecs/h264/h264_encoder_impl.cc index 887aa58098..13dcba84df 100644 --- a/modules/video_coding/codecs/h264/h264_encoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_encoder_impl.cc @@ -81,7 +81,7 @@ VideoFrameType ConvertToVideoFrameType(EVideoFrameType type) { case videoFrameTypeInvalid: break; } - RTC_NOTREACHED() << "Unexpected/invalid frame type: " << type; + RTC_DCHECK_NOTREACHED() << "Unexpected/invalid frame type: " << type; return VideoFrameType::kEmptyFrame; } @@ -537,7 +537,7 @@ SEncParamExt H264EncoderImpl::CreateEncoderParams(size_t i) const { } else if (codec_.mode == VideoCodecMode::kScreensharing) { encoder_params.iUsageType = SCREEN_CONTENT_REAL_TIME; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } encoder_params.iPicWidth = configurations_[i].width; encoder_params.iPicHeight = configurations_[i].height; @@ -586,8 +586,8 @@ SEncParamExt H264EncoderImpl::CreateEncoderParams(size_t i) const { // theoretically use all available reference buffers. encoder_params.iNumRefFrame = encoder_params.iTemporalLayerNum - 1; } - RTC_LOG(INFO) << "OpenH264 version is " << OPENH264_MAJOR << "." - << OPENH264_MINOR; + RTC_LOG(LS_INFO) << "OpenH264 version is " << OPENH264_MAJOR << "." + << OPENH264_MINOR; switch (packetization_mode_) { case H264PacketizationMode::SingleNalUnit: // Limit the size of the packets produced. @@ -596,8 +596,8 @@ SEncParamExt H264EncoderImpl::CreateEncoderParams(size_t i) const { SM_SIZELIMITED_SLICE; encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = static_cast(max_payload_size_); - RTC_LOG(INFO) << "Encoder is configured with NALU constraint: " - << max_payload_size_ << " bytes"; + RTC_LOG(LS_INFO) << "Encoder is configured with NALU constraint: " + << max_payload_size_ << " bytes"; break; case H264PacketizationMode::NonInterleaved: // When uiSliceMode = SM_FIXEDSLCNUM_SLICE, uiSliceNum = 0 means auto @@ -635,7 +635,6 @@ VideoEncoder::EncoderInfo H264EncoderImpl::GetEncoderInfo() const { info.scaling_settings = VideoEncoder::ScalingSettings(kLowH264QpThreshold, kHighH264QpThreshold); info.is_hardware_accelerated = false; - info.has_internal_source = false; info.supports_simulcast = true; info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420}; return info; diff --git a/modules/video_coding/codecs/h264/include/h264.h b/modules/video_coding/codecs/h264/include/h264.h index bffd31cf8d..8c201d2b6a 100644 --- a/modules/video_coding/codecs/h264/include/h264.h +++ b/modules/video_coding/codecs/h264/include/h264.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/video_codecs/h264_profile_level_id.h" #include "media/base/codec.h" #include "modules/video_coding/include/video_codec_interface.h" @@ -37,15 +38,21 @@ CreateH264Format(H264Profile profile, // and is not thread-safe. RTC_EXPORT void DisableRtcUseH264(); -// Returns a vector with all supported internal H264 profiles that we can +// Returns a vector with all supported internal H264 encode profiles that we can // negotiate in SDP, in order of preference. std::vector SupportedH264Codecs(); +// Returns a vector with all supported internal H264 decode profiles that we can +// negotiate in SDP, in order of preference. This will be available for receive +// only connections. +std::vector SupportedH264DecoderCodecs(); + class RTC_EXPORT H264Encoder : public VideoEncoder { public: static std::unique_ptr Create(const cricket::VideoCodec& codec); // If H.264 is supported (any implementation). static bool IsSupported(); + static bool SupportsScalabilityMode(absl::string_view scalability_mode); ~H264Encoder() override {} }; diff --git a/modules/video_coding/codecs/h264/include/h264_globals.h b/modules/video_coding/codecs/h264/include/h264_globals.h index 073d8f9a81..b61dc8c507 100644 --- a/modules/video_coding/codecs/h264/include/h264_globals.h +++ b/modules/video_coding/codecs/h264/include/h264_globals.h @@ -52,7 +52,7 @@ inline std::string ToString(H264PacketizationMode mode) { } else if (mode == H264PacketizationMode::SingleNalUnit) { return "SingleNalUnit"; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } diff --git a/modules/video_coding/codecs/interface/libvpx_interface.cc b/modules/video_coding/codecs/interface/libvpx_interface.cc index b24922f921..4f33bef2ba 100644 --- a/modules/video_coding/codecs/interface/libvpx_interface.cc +++ b/modules/video_coding/codecs/interface/libvpx_interface.cc @@ -133,7 +133,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_POSTENCODE_DROP: return vpx_codec_control(ctx, VP9E_SET_POSTENCODE_DROP, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -181,7 +181,7 @@ class LibvpxFacade : public LibvpxInterface { // Might be intended for uint32_t but int literal used, try fallback. return codec_control(ctx, ctrl_id, static_cast(param)); } - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -199,7 +199,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_GET_LEVEL: return vpx_codec_control(ctx, VP9E_GET_LEVEL, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -213,7 +213,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_ROI_MAP: return vpx_codec_control(ctx, VP9E_SET_ROI_MAP, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -227,7 +227,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_GET_ACTIVEMAP: return vpx_codec_control(ctx, VP8E_SET_ACTIVEMAP, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -239,7 +239,7 @@ class LibvpxFacade : public LibvpxInterface { case VP8E_SET_SCALEMODE: return vpx_codec_control(ctx, VP8E_SET_SCALEMODE, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -251,7 +251,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_SVC_PARAMETERS: return vpx_codec_control_(ctx, VP9E_SET_SVC_PARAMETERS, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -263,7 +263,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_SVC_FRAME_DROP_LAYER: return vpx_codec_control_(ctx, VP9E_SET_SVC_FRAME_DROP_LAYER, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -277,7 +277,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_REGISTER_CX_CALLBACK: return vpx_codec_control_(ctx, VP9E_REGISTER_CX_CALLBACK, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -291,7 +291,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_GET_SVC_LAYER_ID: return vpx_codec_control_(ctx, VP9E_GET_SVC_LAYER_ID, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -306,7 +306,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_GET_SVC_REF_FRAME_CONFIG: return vpx_codec_control_(ctx, VP9E_GET_SVC_REF_FRAME_CONFIG, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -319,7 +319,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_SVC_SPATIAL_LAYER_SYNC: return vpx_codec_control_(ctx, VP9E_SET_SVC_SPATIAL_LAYER_SYNC, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } @@ -331,7 +331,7 @@ class LibvpxFacade : public LibvpxInterface { case VP9E_SET_EXTERNAL_RATE_CONTROL: return vpx_codec_control_(ctx, VP9E_SET_EXTERNAL_RATE_CONTROL, param); default: - RTC_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; + RTC_DCHECK_NOTREACHED() << "Unsupported libvpx ctrl_id: " << ctrl_id; } return VPX_CODEC_ERROR; } diff --git a/modules/video_coding/codecs/multiplex/multiplex_decoder_adapter.cc b/modules/video_coding/codecs/multiplex/multiplex_decoder_adapter.cc index e1bf598622..0ad3d3883a 100644 --- a/modules/video_coding/codecs/multiplex/multiplex_decoder_adapter.cc +++ b/modules/video_coding/codecs/multiplex/multiplex_decoder_adapter.cc @@ -36,11 +36,11 @@ class MultiplexDecoderAdapter::AdapterDecodedImageCallback adapter_->Decoded(stream_idx_, &decoded_image, decode_time_ms, qp); } int32_t Decoded(VideoFrame& decoded_image) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return WEBRTC_VIDEO_CODEC_OK; } int32_t Decoded(VideoFrame& decoded_image, int64_t decode_time_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return WEBRTC_VIDEO_CODEC_OK; } diff --git a/modules/video_coding/codecs/multiplex/multiplex_encoder_adapter.cc b/modules/video_coding/codecs/multiplex/multiplex_encoder_adapter.cc index 10f4517ff9..80744e2d8c 100644 --- a/modules/video_coding/codecs/multiplex/multiplex_encoder_adapter.cc +++ b/modules/video_coding/codecs/multiplex/multiplex_encoder_adapter.cc @@ -141,8 +141,6 @@ int MultiplexEncoderAdapter::InitEncode( encoder_info_.apply_alignment_to_all_simulcast_layers = true; } - encoder_info_.has_internal_source = false; - encoders_.emplace_back(std::move(encoder)); } encoder_info_.implementation_name += ")"; diff --git a/modules/video_coding/codecs/test/video_codec_unittest.h b/modules/video_coding/codecs/test/video_codec_unittest.h index 13bcbf05da..7d05882b63 100644 --- a/modules/video_coding/codecs/test/video_codec_unittest.h +++ b/modules/video_coding/codecs/test/video_codec_unittest.h @@ -54,11 +54,11 @@ class VideoCodecUnitTest : public ::testing::Test { : test_(test) {} int32_t Decoded(VideoFrame& frame) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } int32_t Decoded(VideoFrame& frame, int64_t decode_time_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } void Decoded(VideoFrame& frame, diff --git a/modules/video_coding/codecs/test/video_encoder_decoder_instantiation_tests.cc b/modules/video_coding/codecs/test/video_encoder_decoder_instantiation_tests.cc index e7a7f7dde4..41f2304748 100644 --- a/modules/video_coding/codecs/test/video_encoder_decoder_instantiation_tests.cc +++ b/modules/video_coding/codecs/test/video_encoder_decoder_instantiation_tests.cc @@ -73,7 +73,7 @@ class VideoEncoderDecoderInstantiationTest encoder_factory_ = CreateObjCEncoderFactory(); decoder_factory_ = CreateObjCDecoderFactory(); #else - RTC_NOTREACHED() << "Only support Android and iOS."; + RTC_DCHECK_NOTREACHED() << "Only support Android and iOS."; #endif } diff --git a/modules/video_coding/codecs/test/videocodec_test_libaom.cc b/modules/video_coding/codecs/test/videocodec_test_av1.cc similarity index 73% rename from modules/video_coding/codecs/test/videocodec_test_libaom.cc rename to modules/video_coding/codecs/test/videocodec_test_av1.cc index c3263e7134..30472915a9 100644 --- a/modules/video_coding/codecs/test/videocodec_test_libaom.cc +++ b/modules/video_coding/codecs/test/videocodec_test_av1.cc @@ -18,6 +18,8 @@ #include "media/engine/internal_decoder_factory.h" #include "media/engine/internal_encoder_factory.h" #include "media/engine/simulcast_encoder_adapter.h" +#include "modules/video_coding/codecs/av1/libaom_av1_decoder.h" +#include "test/field_trial.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -38,7 +40,15 @@ VideoCodecTestFixture::Config CreateConfig(std::string filename) { return config; } -TEST(VideoCodecTestLibaom, HighBitrateAV1) { +class VideoCodecTestAv1 : public ::testing::TestWithParam { + public: + VideoCodecTestAv1() : scoped_field_trial_(GetParam()) {} + + private: + ScopedFieldTrials scoped_field_trial_; +}; + +TEST_P(VideoCodecTestAv1, HighBitrate) { auto config = CreateConfig("foreman_cif"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kCifWidth, kCifHeight); @@ -51,12 +61,12 @@ TEST(VideoCodecTestLibaom, HighBitrateAV1) { std::vector rc_thresholds = { {12, 1, 0, 1, 0.3, 0.1, 0, 1}}; - std::vector quality_thresholds = {{37, 34, 0.94, 0.915}}; + std::vector quality_thresholds = {{37, 34, 0.94, 0.91}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } -TEST(VideoCodecTestLibaom, VeryLowBitrateAV1) { +TEST_P(VideoCodecTestAv1, VeryLowBitrate) { auto config = CreateConfig("foreman_cif"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kCifWidth, kCifHeight); @@ -68,7 +78,7 @@ TEST(VideoCodecTestLibaom, VeryLowBitrateAV1) { std::vector rc_thresholds = { {15, 8, 75, 2, 2, 2, 2, 1}}; - std::vector quality_thresholds = {{28, 25, 0.70, 0.60}}; + std::vector quality_thresholds = {{28, 24.8, 0.70, 0.55}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } @@ -76,7 +86,7 @@ TEST(VideoCodecTestLibaom, VeryLowBitrateAV1) { #if !defined(WEBRTC_ANDROID) constexpr int kHdWidth = 1280; constexpr int kHdHeight = 720; -TEST(VideoCodecTestLibaom, HdAV1) { +TEST_P(VideoCodecTestAv1, Hd) { auto config = CreateConfig("ConferenceMotion_1280_720_50"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kHdWidth, kHdHeight); @@ -89,12 +99,30 @@ TEST(VideoCodecTestLibaom, HdAV1) { std::vector rc_thresholds = { {13, 3, 0, 1, 0.3, 0.1, 0, 1}}; - std::vector quality_thresholds = {{36, 31.7, 0.93, 0.87}}; + std::vector quality_thresholds = { + {35.9, 31.55, 0.925, 0.865}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } #endif +std::vector GetTestValues() { + std::vector field_trial_values; +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) + field_trial_values.push_back("WebRTC-Dav1dDecoder/Enabled/"); +#endif + if (kIsLibaomAv1DecoderSupported) { + // As long as the field trial doesn't enable dav1d the libaom decoder will + // be used instead. + field_trial_values.push_back(""); + } + return field_trial_values; +} + +INSTANTIATE_TEST_SUITE_P(Decoder, + VideoCodecTestAv1, + testing::ValuesIn(GetTestValues())); + } // namespace } // namespace test } // namespace webrtc diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc index 8be6e8c7e9..1029d901a0 100644 --- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc +++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc @@ -104,7 +104,6 @@ std::string CodecSpecificToString(const VideoCodec& codec) { rtc::SimpleStringBuilder ss(buf); switch (codec.codecType) { case kVideoCodecVP8: - ss << "complexity: " << static_cast(codec.VP8().complexity); ss << "\nnum_temporal_layers: " << static_cast(codec.VP8().numberOfTemporalLayers); ss << "\ndenoising: " << codec.VP8().denoisingOn; @@ -113,7 +112,6 @@ std::string CodecSpecificToString(const VideoCodec& codec) { ss << "\nkey_frame_interval: " << codec.VP8().keyFrameInterval; break; case kVideoCodecVP9: - ss << "complexity: " << static_cast(codec.VP9().complexity); ss << "\nnum_temporal_layers: " << static_cast(codec.VP9().numberOfTemporalLayers); ss << "\nnum_spatial_layers: " @@ -150,6 +148,26 @@ std::string FilenameWithParams( std::to_string(config.codec_settings.startBitrate); } +SdpVideoFormat CreateSdpVideoFormat( + const VideoCodecTestFixtureImpl::Config& config) { + if (config.codec_settings.codecType == kVideoCodecH264) { + const char* packetization_mode = + config.h264_codec_settings.packetization_mode == + H264PacketizationMode::NonInterleaved + ? "1" + : "0"; + SdpVideoFormat::Parameters codec_params = { + {cricket::kH264FmtpProfileLevelId, + *H264ProfileLevelIdToString(H264ProfileLevelId( + config.h264_codec_settings.profile, H264Level::kLevel3_1))}, + {cricket::kH264FmtpPacketizationMode, packetization_mode}}; + + return SdpVideoFormat(config.codec_name, codec_params); + } + + return SdpVideoFormat(config.codec_name); +} + } // namespace VideoCodecTestFixtureImpl::Config::Config() = default; @@ -282,6 +300,8 @@ std::string VideoCodecTestFixtureImpl::Config::ToString() const { ss << "\nnum_simulcast_streams: " << static_cast(codec_settings.numberOfSimulcastStreams); ss << "\n\n--> codec_settings." << codec_type; + ss << "complexity: " + << static_cast(codec_settings.GetVideoEncoderComplexity()); ss << "\n" << CodecSpecificToString(codec_settings); if (codec_settings.numberOfSimulcastStreams > 1) { for (int i = 0; i < codec_settings.numberOfSimulcastStreams; ++i) { @@ -350,7 +370,7 @@ void VideoCodecTestFixtureImpl::H264KeyframeChecker::CheckEncodedFrame( EXPECT_FALSE(contains_pps) << "Delta frame should not contain PPS."; EXPECT_FALSE(contains_idr) << "Delta frame should not contain IDR."; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -611,23 +631,21 @@ void VideoCodecTestFixtureImpl::VerifyVideoStatistic( } bool VideoCodecTestFixtureImpl::CreateEncoderAndDecoder() { - SdpVideoFormat::Parameters params; - if (config_.codec_settings.codecType == kVideoCodecH264) { - const char* packetization_mode = - config_.h264_codec_settings.packetization_mode == - H264PacketizationMode::NonInterleaved - ? "1" - : "0"; - params = {{cricket::kH264FmtpProfileLevelId, - *H264ProfileLevelIdToString(H264ProfileLevelId( - config_.h264_codec_settings.profile, H264Level::kLevel3_1))}, - {cricket::kH264FmtpPacketizationMode, packetization_mode}}; - } else { - params = {}; - } - SdpVideoFormat format(config_.codec_name, params); + SdpVideoFormat encoder_format(CreateSdpVideoFormat(config_)); + SdpVideoFormat decoder_format = encoder_format; - encoder_ = encoder_factory_->CreateVideoEncoder(format); + // Override encoder and decoder formats with explicitly provided ones. + if (config_.encoder_format) { + RTC_DCHECK_EQ(config_.encoder_format->name, config_.codec_name); + encoder_format = *config_.encoder_format; + } + + if (config_.decoder_format) { + RTC_DCHECK_EQ(config_.decoder_format->name, config_.codec_name); + decoder_format = *config_.decoder_format; + } + + encoder_ = encoder_factory_->CreateVideoEncoder(encoder_format); EXPECT_TRUE(encoder_) << "Encoder not successfully created."; if (encoder_ == nullptr) { return false; @@ -636,15 +654,13 @@ bool VideoCodecTestFixtureImpl::CreateEncoderAndDecoder() { const size_t num_simulcast_or_spatial_layers = std::max( config_.NumberOfSimulcastStreams(), config_.NumberOfSpatialLayers()); for (size_t i = 0; i < num_simulcast_or_spatial_layers; ++i) { - decoders_.push_back(std::unique_ptr( - decoder_factory_->CreateVideoDecoder(format))); - } - - for (const auto& decoder : decoders_) { + std::unique_ptr decoder = + decoder_factory_->CreateVideoDecoder(decoder_format); EXPECT_TRUE(decoder) << "Decoder not successfully created."; if (decoder == nullptr) { return false; } + decoders_.push_back(std::move(decoder)); } return true; @@ -697,15 +713,6 @@ bool VideoCodecTestFixtureImpl::SetUpAndInitObjects( return false; } - task_queue->SendTask( - [this]() { - processor_ = std::make_unique( - encoder_.get(), &decoders_, source_frame_reader_.get(), config_, - &stats_, &encoded_frame_writers_, - decoded_frame_writers_.empty() ? nullptr : &decoded_frame_writers_); - }, - RTC_FROM_HERE); - if (config_.visualization_params.save_encoded_ivf || config_.visualization_params.save_decoded_y4m) { std::string encoder_name = GetCodecName(task_queue, /*is_encoder=*/true); @@ -748,6 +755,14 @@ bool VideoCodecTestFixtureImpl::SetUpAndInitObjects( } } + task_queue->SendTask( + [this]() { + processor_ = std::make_unique( + encoder_.get(), &decoders_, source_frame_reader_.get(), config_, + &stats_, &encoded_frame_writers_, + decoded_frame_writers_.empty() ? nullptr : &decoded_frame_writers_); + }, + RTC_FROM_HERE); return true; } diff --git a/modules/video_coding/codecs/test/videocodec_test_libvpx.cc b/modules/video_coding/codecs/test/videocodec_test_libvpx.cc index 0eb0d5a284..062375bd60 100644 --- a/modules/video_coding/codecs/test/videocodec_test_libvpx.cc +++ b/modules/video_coding/codecs/test/videocodec_test_libvpx.cc @@ -49,7 +49,7 @@ class QpFrameChecker : public VideoCodecTestFixture::EncodedFrameChecker { } else if (codec == kVideoCodecVP9) { EXPECT_TRUE(vp9::GetQp(encoded_frame.data(), encoded_frame.size(), &qp)); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } EXPECT_EQ(encoded_frame.qp_, qp) << "Encoder QP != parsed bitstream QP."; } diff --git a/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc b/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc index 978fd8856f..8a1cf01a66 100644 --- a/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc +++ b/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc @@ -27,6 +27,35 @@ namespace { const int kForemanNumFrames = 300; const int kForemanFramerateFps = 30; +const size_t kConstRateIntervalSec = 10; +const std::vector kBitRateHighLowHigh = { + {/*target_kbps=*/3000, /*input_fps=*/30, /*frame_num=*/0}, + {/*target_kbps=*/1500, /*input_fps=*/30, /*frame_num=*/300}, + {/*target_kbps=*/750, /*input_fps=*/30, /*frame_num=*/600}, + {/*target_kbps=*/1500, /*input_fps=*/30, /*frame_num=*/900}, + {/*target_kbps=*/3000, /*input_fps=*/30, /*frame_num=*/1200}}; + +const std::vector kBitRateLowHighLow = { + {/*target_kbps=*/750, /*input_fps=*/30, /*frame_num=*/0}, + {/*target_kbps=*/1500, /*input_fps=*/30, /*frame_num=*/300}, + {/*target_kbps=*/3000, /*input_fps=*/30, /*frame_num=*/600}, + {/*target_kbps=*/1500, /*input_fps=*/30, /*frame_num=*/900}, + {/*target_kbps=*/720, /*input_fps=*/30, /*frame_num=*/1200}}; + +const std::vector kFrameRateHighLowHigh = { + {/*target_kbps=*/2000, /*input_fps=*/30, /*frame_num=*/0}, + {/*target_kbps=*/2000, /*input_fps=*/15, /*frame_num=*/300}, + {/*target_kbps=*/2000, /*input_fps=*/7.5, /*frame_num=*/450}, + {/*target_kbps=*/2000, /*input_fps=*/15, /*frame_num=*/525}, + {/*target_kbps=*/2000, /*input_fps=*/30, /*frame_num=*/675}}; + +const std::vector kFrameRateLowHighLow = { + {/*target_kbps=*/2000, /*input_fps=*/7.5, /*frame_num=*/0}, + {/*target_kbps=*/2000, /*input_fps=*/15, /*frame_num=*/75}, + {/*target_kbps=*/2000, /*input_fps=*/30, /*frame_num=*/225}, + {/*target_kbps=*/2000, /*input_fps=*/15, /*frame_num=*/525}, + {/*target_kbps=*/2000, /*input_fps=*/7.5, /*frame_num=*/775}}; + VideoCodecTestFixture::Config CreateConfig() { VideoCodecTestFixture::Config config; config.filename = "foreman_cif"; @@ -120,7 +149,7 @@ TEST(VideoCodecTestMediaCodec, ForemanMixedRes100kbpsVp8H264) { const std::vector codecs = {cricket::kVp8CodecName, cricket::kH264CodecName}; const std::vector> resolutions = { - {128, 96}, {160, 120}, {176, 144}, {240, 136}, {320, 240}, {480, 272}}; + {128, 96}, {176, 144}, {320, 240}, {480, 272}}; const std::vector rate_profiles = { {100, kForemanFramerateFps, 0}}; const std::vector quality_thresholds = { @@ -144,5 +173,69 @@ TEST(VideoCodecTestMediaCodec, ForemanMixedRes100kbpsVp8H264) { } } +class VideoCodecTestMediaCodecRateAdaptation + : public ::testing::TestWithParam< + std::tuple, std::string>> {}; + +TEST_P(VideoCodecTestMediaCodecRateAdaptation, DISABLED_RateAdaptation) { + const std::vector rate_profile = + std::get<0>(GetParam()); + const std::string codec_name = std::get<1>(GetParam()); + + VideoCodecTestFixture::Config config; + config.filename = "FourPeople_1280x720_30"; + config.filepath = ResourcePath(config.filename, "yuv"); + config.num_frames = rate_profile.back().frame_num + + static_cast(kConstRateIntervalSec * + rate_profile.back().input_fps); + config.encode_in_real_time = true; + config.SetCodecSettings(codec_name, 1, 1, 1, false, false, false, 1280, 720); + + auto fixture = CreateTestFixtureWithConfig(config); + fixture->RunTest(rate_profile, nullptr, nullptr, nullptr); + + for (size_t i = 0; i < rate_profile.size(); ++i) { + const size_t num_frames = + static_cast(rate_profile[i].input_fps * kConstRateIntervalSec); + + auto stats = fixture->GetStats().SliceAndCalcLayerVideoStatistic( + rate_profile[i].frame_num, rate_profile[i].frame_num + num_frames - 1); + ASSERT_EQ(stats.size(), 1u); + + // Bitrate mismatch is <= 10%. + EXPECT_LE(stats[0].avg_bitrate_mismatch_pct, 10); + EXPECT_GE(stats[0].avg_bitrate_mismatch_pct, -10); + + // Avg frame transmission delay and processing latency is <=100..250ms + // depending on frame rate. + const double expected_delay_sec = + std::min(std::max(1 / rate_profile[i].input_fps, 0.1), 0.25); + EXPECT_LE(stats[0].avg_delay_sec, expected_delay_sec); + EXPECT_LE(stats[0].avg_encode_latency_sec, expected_delay_sec); + EXPECT_LE(stats[0].avg_decode_latency_sec, expected_delay_sec); + + // Frame drops are not expected. + EXPECT_EQ(stats[0].num_encoded_frames, num_frames); + EXPECT_EQ(stats[0].num_decoded_frames, num_frames); + + // Periodic keyframes are not expected. + EXPECT_EQ(stats[0].num_key_frames, i == 0 ? 1u : 0); + + // Ensure codec delivers a reasonable spatial quality. + EXPECT_GE(stats[0].avg_psnr_y, 35); + } +} + +INSTANTIATE_TEST_SUITE_P( + RateAdaptation, + VideoCodecTestMediaCodecRateAdaptation, + ::testing::Combine(::testing::Values(kBitRateLowHighLow, + kBitRateHighLowHigh, + kFrameRateLowHighLow, + kFrameRateHighLowHigh), + ::testing::Values(cricket::kVp8CodecName, + cricket::kVp9CodecName, + cricket::kH264CodecName))); + } // namespace test } // namespace webrtc diff --git a/modules/video_coding/codecs/test/videocodec_test_stats_impl.cc b/modules/video_coding/codecs/test/videocodec_test_stats_impl.cc index 07c330833f..efb7502e5d 100644 --- a/modules/video_coding/codecs/test/videocodec_test_stats_impl.cc +++ b/modules/video_coding/codecs/test/videocodec_test_stats_impl.cc @@ -332,6 +332,16 @@ VideoStatistics VideoCodecTestStatsImpl::SliceAndCalcVideoStatistic( ? 1000000.0f / mean_decode_time_us : std::numeric_limits::max(); + video_stat.avg_encode_latency_sec = + frame_encoding_time_us.GetMean().value_or(0) / 1000000.0f; + video_stat.max_encode_latency_sec = + frame_encoding_time_us.GetMax().value_or(0) / 1000000.0f; + + video_stat.avg_decode_latency_sec = + frame_decoding_time_us.GetMean().value_or(0) / 1000000.0f; + video_stat.max_decode_latency_sec = + frame_decoding_time_us.GetMax().value_or(0) / 1000000.0f; + auto MaxDelaySec = [target_bitrate_kbps]( const webrtc_impl::RunningStatistics& stats) { return 8 * stats.GetMax().value_or(0) / 1000 / target_bitrate_kbps; @@ -339,7 +349,7 @@ VideoStatistics VideoCodecTestStatsImpl::SliceAndCalcVideoStatistic( video_stat.avg_delay_sec = buffer_level_sec.GetMean().value_or(0); video_stat.max_key_frame_delay_sec = MaxDelaySec(key_frame_size_bytes); - video_stat.max_delta_frame_delay_sec = MaxDelaySec(key_frame_size_bytes); + video_stat.max_delta_frame_delay_sec = MaxDelaySec(delta_frame_size_bytes); video_stat.avg_bitrate_mismatch_pct = 100 * (bitrate_bps - target_bitrate_bps) / target_bitrate_bps; diff --git a/modules/video_coding/codecs/test/videoprocessor.h b/modules/video_coding/codecs/test/videoprocessor.h index eafe492870..217137c95e 100644 --- a/modules/video_coding/codecs/test/videoprocessor.h +++ b/modules/video_coding/codecs/test/videoprocessor.h @@ -37,7 +37,6 @@ #include "modules/video_coding/utility/ivf_file_writer.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" #include "test/testsupport/frame_reader.h" @@ -70,6 +69,9 @@ class VideoProcessor { FrameWriterList* decoded_frame_writers); ~VideoProcessor(); + VideoProcessor(const VideoProcessor&) = delete; + VideoProcessor& operator=(const VideoProcessor&) = delete; + // Reads a frame and sends it to the encoder. When the encode callback // is received, the encoded frame is buffered. After encoding is finished // buffered frame is sent to decoder. Quality evaluation is done in @@ -183,7 +185,7 @@ class VideoProcessor { // lower layer frames, we merge and store the layer frames in this method. const webrtc::EncodedImage* BuildAndStoreSuperframe( const EncodedImage& encoded_image, - const VideoCodecType codec, + VideoCodecType codec, size_t frame_number, size_t simulcast_svc_idx, bool inter_layer_predicted) RTC_RUN_ON(sequence_checker_); @@ -270,8 +272,6 @@ class VideoProcessor { // This class must be operated on a TaskQueue. RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; - - RTC_DISALLOW_COPY_AND_ASSIGN(VideoProcessor); }; } // namespace test diff --git a/modules/video_coding/codecs/vp8/default_temporal_layers.cc b/modules/video_coding/codecs/vp8/default_temporal_layers.cc index 4de8d91526..94860da1b6 100644 --- a/modules/video_coding/codecs/vp8/default_temporal_layers.cc +++ b/modules/video_coding/codecs/vp8/default_temporal_layers.cc @@ -78,10 +78,10 @@ std::vector GetTemporalIds(size_t num_layers) { // 0 0 ... return {0, 3, 2, 3, 1, 3, 2, 3}; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return {0}; } @@ -236,10 +236,10 @@ DefaultTemporalLayers::GetDependencyInfo(size_t num_layers) { {"----", {kReference, kReference, kReferenceAndUpdate}}, {"----", {kReference, kReference, kReference, kFreezeEntropy}}}; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return {{"", {kNone, kNone, kNone}}}; } @@ -694,7 +694,7 @@ FrameDependencyStructure DefaultTemporalLayers::GetTemplateStructure( return template_structure; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); // To make the compiler happy! return template_structure; } @@ -725,7 +725,7 @@ std::vector> GetTemporalDependencies( {0}, {4, 6, 8}, {4, 6, 8}, {4, 8, 10}, {4, 8}, {8, 10, 12}, {8, 10, 12}, {8, 12, 14}}; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return {}; } } diff --git a/modules/video_coding/codecs/vp8/include/vp8.h b/modules/video_coding/codecs/vp8/include/vp8.h index d05c3a68d1..22f8de623d 100644 --- a/modules/video_coding/codecs/vp8/include/vp8.h +++ b/modules/video_coding/codecs/vp8/include/vp8.h @@ -15,6 +15,7 @@ #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/vp8_frame_buffer_controller.h" #include "modules/video_coding/include/video_codec_interface.h" @@ -39,6 +40,7 @@ class VP8Encoder { static std::unique_ptr Create(); static std::unique_ptr Create(Settings settings); + static bool SupportsScalabilityMode(absl::string_view scalability_mode); ABSL_DEPRECATED("") static std::unique_ptr Create( diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc index 1f70569526..ced6f478d1 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc @@ -49,6 +49,8 @@ constexpr char kVP8IosMaxNumberOfThreadFieldTrial[] = constexpr char kVP8IosMaxNumberOfThreadFieldTrialParameter[] = "max_thread"; #endif +constexpr absl::string_view kSupportedScalabilityModes[] = {"L1T2", "L1T3"}; + constexpr char kVp8ForcePartitionResilience[] = "WebRTC-VP8-ForcePartitionResilience"; @@ -203,7 +205,7 @@ void SetRawImagePlanes(vpx_image_t* raw_image, VideoFrameBuffer* buffer) { break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -230,6 +232,15 @@ std::unique_ptr VP8Encoder::Create( std::move(settings)); } +bool VP8Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { + for (const auto& entry : kSupportedScalabilityModes) { + if (entry == scalability_mode) { + return true; + } + } + return false; +} + vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags( const Vp8FrameConfig& references) { RTC_DCHECK(!references.drop_frame); @@ -580,7 +591,7 @@ int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst, } // Allow the user to set the complexity for the base stream. - switch (inst->VP8().complexity) { + switch (inst->GetVideoEncoderComplexity()) { case VideoCodecComplexity::kComplexityHigh: cpu_speed_[0] = -5; break; @@ -845,16 +856,19 @@ uint32_t LibvpxVp8Encoder::MaxIntraTarget(uint32_t optimalBuffersize) { } uint32_t LibvpxVp8Encoder::FrameDropThreshold(size_t spatial_idx) const { - bool enable_frame_dropping = codec_.VP8().frameDroppingOn; + if (!codec_.VP8().frameDroppingOn) { + return 0; + } + // If temporal layers are used, they get to override the frame dropping // setting, as eg. ScreenshareLayers does not work as intended with frame // dropping on and DefaultTemporalLayers will have performance issues with // frame dropping off. RTC_DCHECK(frame_buffer_controller_); RTC_DCHECK_LT(spatial_idx, frame_buffer_controller_->StreamCount()); - enable_frame_dropping = - frame_buffer_controller_->SupportsEncoderFrameDropping(spatial_idx); - return enable_frame_dropping ? 30 : 0; + return frame_buffer_controller_->SupportsEncoderFrameDropping(spatial_idx) + ? 30 + : 0; } size_t LibvpxVp8Encoder::SteadyStateSize(int sid, int tid) { @@ -1173,6 +1187,8 @@ int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image, libvpx_->codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER, &qp_128); encoded_images_[encoder_idx].qp_ = qp_128; + encoded_images_[encoder_idx].SetAtTargetQuality( + qp_128 <= variable_framerate_experiment_.steady_state_qp); encoded_complete_callback_->OnEncodedImage(encoded_images_[encoder_idx], &codec_specific); const size_t steady_state_size = SteadyStateSize( @@ -1204,7 +1220,6 @@ VideoEncoder::EncoderInfo LibvpxVp8Encoder::GetEncoderInfo() const { info.has_trusted_rate_controller = rate_control_settings_.LibvpxVp8TrustedRateController(); info.is_hardware_accelerated = false; - info.has_internal_source = false; info.supports_simulcast = true; if (!resolution_bitrate_limits_.empty()) { info.resolution_bitrate_limits = resolution_bitrate_limits_; @@ -1282,8 +1297,8 @@ void LibvpxVp8Encoder::MaybeUpdatePixelFormat(vpx_img_fmt fmt) { << "Not all raw images had the right format!"; return; } - RTC_LOG(INFO) << "Updating vp8 encoder pixel format to " - << (fmt == VPX_IMG_FMT_NV12 ? "NV12" : "I420"); + RTC_LOG(LS_INFO) << "Updating vp8 encoder pixel format to " + << (fmt == VPX_IMG_FMT_NV12 ? "NV12" : "I420"); for (size_t i = 0; i < raw_images_.size(); ++i) { vpx_image_t& img = raw_images_[i]; auto d_w = img.d_w; @@ -1346,7 +1361,7 @@ LibvpxVp8Encoder::PrepareBuffers(rtc::scoped_refptr buffer) { MaybeUpdatePixelFormat(VPX_IMG_FMT_NV12); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // Prepare `raw_images_` from `mapped_buffer` and, if simulcast, scaled @@ -1388,10 +1403,11 @@ LibvpxVp8Encoder::PrepareBuffers(rtc::scoped_refptr buffer) { << " instead of " << VideoFrameBufferTypeToString(mapped_buffer->type()) << ". Can't encode frame."; - RTC_NOTREACHED() << "Scaled buffer type " - << VideoFrameBufferTypeToString(scaled_buffer->type()) - << " is not compatible with mapped buffer type " - << VideoFrameBufferTypeToString(mapped_buffer->type()); + RTC_DCHECK_NOTREACHED() + << "Scaled buffer type " + << VideoFrameBufferTypeToString(scaled_buffer->type()) + << " is not compatible with mapped buffer type " + << VideoFrameBufferTypeToString(mapped_buffer->type()); return {}; } SetRawImagePlanes(&raw_images_[i], scaled_buffer); diff --git a/modules/video_coding/codecs/vp8/screenshare_layers.cc b/modules/video_coding/codecs/vp8/screenshare_layers.cc index 1c4c6c331e..71db0b22c2 100644 --- a/modules/video_coding/codecs/vp8/screenshare_layers.cc +++ b/modules/video_coding/codecs/vp8/screenshare_layers.cc @@ -212,7 +212,7 @@ Vp8FrameConfig ScreenshareLayers::NextFrameConfig(size_t stream_index, ++stats_.num_dropped_frames_; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } DependencyInfo dependency_info; @@ -320,6 +320,7 @@ void ScreenshareLayers::OnEncodeDone(size_t stream_index, if (number_of_temporal_layers_ == 1) { vp8_info.temporalIdx = kNoTemporalIdx; vp8_info.layerSync = false; + generic_frame_info.temporal_id = 0; generic_frame_info.decode_target_indications = {kSwitch}; generic_frame_info.encoder_buffers.emplace_back( 0, /*referenced=*/!is_keyframe, /*updated=*/true); @@ -329,6 +330,7 @@ void ScreenshareLayers::OnEncodeDone(size_t stream_index, vp8_info.temporalIdx = dependency_info->frame_config.packetizer_temporal_idx; vp8_info.layerSync = dependency_info->frame_config.layer_sync; + generic_frame_info.temporal_id = vp8_info.temporalIdx; generic_frame_info.decode_target_indications = dependency_info->decode_target_indications; } else { @@ -344,6 +346,7 @@ void ScreenshareLayers::OnEncodeDone(size_t stream_index, active_layer_ = 1; info->template_structure = GetTemplateStructure(number_of_temporal_layers_); + generic_frame_info.temporal_id = vp8_info.temporalIdx; generic_frame_info.decode_target_indications = {kSwitch, kSwitch}; } else if (active_layer_ >= 0 && layers_[active_layer_].state == TemporalLayer::State::kKeyFrame) { @@ -443,7 +446,7 @@ FrameDependencyStructure ScreenshareLayers::GetTemplateStructure( return template_structure; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); // To make the compiler happy! return template_structure; } diff --git a/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc b/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc index 88ef9b8c14..e5b3bd4fdf 100644 --- a/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc +++ b/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc @@ -218,18 +218,20 @@ TEST_F(ScreenshareLayerTest, 1Layer) { // belong to the base layer. const int kSingleLayerFlags = 0; auto info = std::make_unique(); - int flags = EncodeFrame(false, info.get()); + int flags = EncodeFrame(/*base_sync=*/false, info.get()); timestamp_ += kTimestampDelta5Fps; EXPECT_EQ(static_cast(kNoTemporalIdx), info->codecSpecific.VP8.temporalIdx); EXPECT_FALSE(info->codecSpecific.VP8.layerSync); + EXPECT_EQ(info->generic_frame_info->temporal_id, 0); info = std::make_unique(); - flags = EncodeFrame(false, info.get()); + flags = EncodeFrame(/*base_sync=*/false, info.get()); EXPECT_EQ(kSingleLayerFlags, flags); EXPECT_EQ(static_cast(kNoTemporalIdx), info->codecSpecific.VP8.temporalIdx); EXPECT_FALSE(info->codecSpecific.VP8.layerSync); + EXPECT_EQ(info->generic_frame_info->temporal_id, 0); } TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) { @@ -337,7 +339,9 @@ TEST_F(ScreenshareLayerTest, 2LayersToggling) { int tl1_frames = 0; for (int i = 0; i < 50; ++i) { CodecSpecificInfo info; - EncodeFrame(false, &info); + EncodeFrame(/*base_sync=*/false, &info); + EXPECT_EQ(info.codecSpecific.VP8.temporalIdx, + info.generic_frame_info->temporal_id); timestamp_ += kTimestampDelta5Fps; switch (info.codecSpecific.VP8.temporalIdx) { case 0: @@ -561,7 +565,7 @@ TEST_F(ScreenshareLayerTest, UpdatesHistograms) { } else if (flags == -1) { dropped_frame = true; } else { - RTC_NOTREACHED() << "Unexpected flags"; + RTC_DCHECK_NOTREACHED() << "Unexpected flags"; } clock_.AdvanceTime(TimeDelta::Millis(1000 / 5)); } diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc index 00258fe3f7..0ec7a453c1 100644 --- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc +++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc @@ -76,10 +76,8 @@ class TestVp8Impl : public VideoCodecUnitTest { webrtc::test::CodecSettings(kVideoCodecVP8, codec_settings); codec_settings->width = kWidth; codec_settings->height = kHeight; - codec_settings->VP8()->denoisingOn = true; - codec_settings->VP8()->frameDroppingOn = false; - codec_settings->VP8()->automaticResizeOn = false; - codec_settings->VP8()->complexity = VideoCodecComplexity::kComplexityNormal; + codec_settings->SetVideoEncoderComplexity( + VideoCodecComplexity::kComplexityNormal); } void EncodeAndWaitForFrame(const VideoFrame& input_frame, @@ -408,7 +406,6 @@ TEST_F(TestVp8Impl, EncoderWith2TemporalLayers) { } TEST_F(TestVp8Impl, ScalingDisabledIfAutomaticResizeOff) { - codec_settings_.VP8()->frameDroppingOn = true; codec_settings_.VP8()->automaticResizeOn = false; EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_settings_, kSettings)); @@ -419,7 +416,6 @@ TEST_F(TestVp8Impl, ScalingDisabledIfAutomaticResizeOff) { } TEST_F(TestVp8Impl, ScalingEnabledIfAutomaticResizeOn) { - codec_settings_.VP8()->frameDroppingOn = true; codec_settings_.VP8()->automaticResizeOn = true; EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_settings_, kSettings)); @@ -520,7 +516,6 @@ TEST(LibvpxVp8EncoderTest, GetEncoderInfoReturnsStaticInformation) { EXPECT_FALSE(info.supports_native_handle); EXPECT_FALSE(info.is_hardware_accelerated); - EXPECT_FALSE(info.has_internal_source); EXPECT_TRUE(info.supports_simulcast); EXPECT_EQ(info.implementation_name, "libvpx"); EXPECT_EQ(info.requested_resolution_alignment, 1); diff --git a/modules/video_coding/codecs/vp9/include/vp9.h b/modules/video_coding/codecs/vp9/include/vp9.h index 7294de2f33..829680a806 100644 --- a/modules/video_coding/codecs/vp9/include/vp9.h +++ b/modules/video_coding/codecs/vp9/include/vp9.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/video_codecs/sdp_video_format.h" #include "media/base/codec.h" #include "modules/video_coding/include/video_codec_interface.h" @@ -36,6 +37,7 @@ class VP9Encoder : public VideoEncoder { static std::unique_ptr Create(); // Parses VP9 Profile from `codec` and returns the appropriate implementation. static std::unique_ptr Create(const cricket::VideoCodec& codec); + static bool SupportsScalabilityMode(absl::string_view scalability_mode); ~VP9Encoder() override {} }; diff --git a/modules/video_coding/codecs/vp9/include/vp9_globals.h b/modules/video_coding/codecs/vp9/include/vp9_globals.h index 840d63ec0f..e6f644ec11 100644 --- a/modules/video_coding/codecs/vp9/include/vp9_globals.h +++ b/modules/video_coding/codecs/vp9/include/vp9_globals.h @@ -30,8 +30,8 @@ const size_t kMaxVp9RefPics = 3; const size_t kMaxVp9FramesInGof = 0xFF; // 8 bits const size_t kMaxVp9NumberOfSpatialLayers = 8; -const size_t kMinVp9SpatialLayerWidth = 240; -const size_t kMinVp9SpatialLayerHeight = 135; +const size_t kMinVp9SpatialLayerLongSideLength = 240; +const size_t kMinVp9SpatialLayerShortSideLength = 135; enum TemporalStructureMode { kTemporalStructureMode1, // 1 temporal layer structure - i.e., IPPP... @@ -46,14 +46,14 @@ struct GofInfoVP9 { case kTemporalStructureMode1: num_frames_in_gof = 1; temporal_idx[0] = 0; - temporal_up_switch[0] = false; + temporal_up_switch[0] = true; num_ref_pics[0] = 1; pid_diff[0][0] = 1; break; case kTemporalStructureMode2: num_frames_in_gof = 2; temporal_idx[0] = 0; - temporal_up_switch[0] = false; + temporal_up_switch[0] = true; num_ref_pics[0] = 1; pid_diff[0][0] = 2; @@ -65,7 +65,7 @@ struct GofInfoVP9 { case kTemporalStructureMode3: num_frames_in_gof = 4; temporal_idx[0] = 0; - temporal_up_switch[0] = false; + temporal_up_switch[0] = true; num_ref_pics[0] = 1; pid_diff[0][0] = 4; @@ -87,7 +87,7 @@ struct GofInfoVP9 { case kTemporalStructureMode4: num_frames_in_gof = 8; temporal_idx[0] = 0; - temporal_up_switch[0] = false; + temporal_up_switch[0] = true; num_ref_pics[0] = 1; pid_diff[0][0] = 4; @@ -97,12 +97,12 @@ struct GofInfoVP9 { pid_diff[1][0] = 1; temporal_idx[2] = 1; - temporal_up_switch[2] = true; + temporal_up_switch[2] = false; num_ref_pics[2] = 1; pid_diff[2][0] = 2; temporal_idx[3] = 2; - temporal_up_switch[3] = false; + temporal_up_switch[3] = true; num_ref_pics[3] = 2; pid_diff[3][0] = 1; pid_diff[3][1] = 2; @@ -113,7 +113,7 @@ struct GofInfoVP9 { pid_diff[4][0] = 4; temporal_idx[5] = 2; - temporal_up_switch[5] = false; + temporal_up_switch[5] = true; num_ref_pics[5] = 2; pid_diff[5][0] = 1; pid_diff[5][1] = 2; @@ -125,13 +125,13 @@ struct GofInfoVP9 { pid_diff[6][1] = 4; temporal_idx[7] = 2; - temporal_up_switch[7] = false; + temporal_up_switch[7] = true; num_ref_pics[7] = 2; pid_diff[7][0] = 1; pid_diff[7][1] = 2; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -195,7 +195,10 @@ struct RTPVideoHeaderVP9 { uint8_t temporal_idx; // Temporal layer index, or kNoTemporalIdx. uint8_t spatial_idx; // Spatial layer index, or kNoSpatialIdx. bool temporal_up_switch; // True if upswitch to higher frame rate is possible - // starting from this frame. + // meaning subsequent higher temporal layer pictures + // will not depend on any picture before the current + // picture (in coding order) with temporal layer ID + // greater than `temporal_idx` of this frame. bool inter_layer_predicted; // Frame is dependent on directly lower spatial // layer frame. diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc index 9f363ffdc1..669dc55a4b 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc @@ -64,7 +64,7 @@ ColorSpace ExtractVP9ColorSpace(vpx_color_space_t space_t, transfer = ColorSpace::TransferID::kBT2020_10; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } matrix = ColorSpace::MatrixID::kBT2020_NCL; @@ -127,6 +127,7 @@ bool LibvpxVp9Decoder::Configure(const Settings& settings) { if (decoder_ == nullptr) { decoder_ = new vpx_codec_ctx_t; + memset(decoder_, 0, sizeof(*decoder_)); } vpx_codec_dec_cfg_t cfg; memset(&cfg, 0, sizeof(cfg)); @@ -274,8 +275,8 @@ int LibvpxVp9Decoder::ReturnFrame( // This buffer contains all of `img`'s image data, a reference counted // Vp9FrameBuffer. (libvpx is done with the buffers after a few // vpx_codec_decode calls or vpx_codec_destroy). - rtc::scoped_refptr img_buffer = - static_cast(img->fb_priv); + rtc::scoped_refptr img_buffer( + static_cast(img->fb_priv)); // The buffer can be used directly by the VideoFrame (without copy) by // using a Wrapped*Buffer. diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc index f920a64ead..99680cbe79 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc @@ -227,9 +227,6 @@ LibvpxVp9Encoder::LibvpxVp9Encoder(const cricket::VideoCodec& codec, first_frame_in_picture_(true), ss_info_needed_(false), force_all_active_layers_(false), - use_svc_controller_( - absl::StartsWith(trials.Lookup("WebRTC-Vp9DependencyDescriptor"), - "Enabled")), is_flexible_mode_(false), variable_framerate_experiment_(ParseVariableFramerateConfig(trials)), variable_framerate_controller_( @@ -399,22 +396,79 @@ bool LibvpxVp9Encoder::SetSvcRates( } if (svc_controller_) { - VideoBitrateAllocation allocation; for (int sid = 0; sid < num_spatial_layers_; ++sid) { + // Bitrates in `layer_target_bitrate` are accumulated for each temporal + // layer but in `VideoBitrateAllocation` they should be separated. + int previous_bitrate_kbps = 0; for (int tid = 0; tid < num_temporal_layers_; ++tid) { - allocation.SetBitrate( - sid, tid, - config_->layer_target_bitrate[sid * num_temporal_layers_ + tid] * - 1000); + int accumulated_bitrate_kbps = + config_->layer_target_bitrate[sid * num_temporal_layers_ + tid]; + int single_layer_bitrate_kbps = + accumulated_bitrate_kbps - previous_bitrate_kbps; + RTC_DCHECK_GE(single_layer_bitrate_kbps, 0); + current_bitrate_allocation_.SetBitrate( + sid, tid, single_layer_bitrate_kbps * 1'000); + previous_bitrate_kbps = accumulated_bitrate_kbps; } } - svc_controller_->OnRatesUpdated(allocation); + svc_controller_->OnRatesUpdated(current_bitrate_allocation_); + } else { + current_bitrate_allocation_ = bitrate_allocation; } - current_bitrate_allocation_ = bitrate_allocation; config_changed_ = true; return true; } +void LibvpxVp9Encoder::DisableSpatialLayer(int sid) { + RTC_DCHECK_LT(sid, num_spatial_layers_); + if (config_->ss_target_bitrate[sid] == 0) { + return; + } + config_->ss_target_bitrate[sid] = 0; + for (int tid = 0; tid < num_temporal_layers_; ++tid) { + config_->layer_target_bitrate[sid * num_temporal_layers_ + tid] = 0; + } + config_changed_ = true; +} + +void LibvpxVp9Encoder::EnableSpatialLayer(int sid) { + RTC_DCHECK_LT(sid, num_spatial_layers_); + if (config_->ss_target_bitrate[sid] > 0) { + return; + } + for (int tid = 0; tid < num_temporal_layers_; ++tid) { + config_->layer_target_bitrate[sid * num_temporal_layers_ + tid] = + current_bitrate_allocation_.GetBitrate(sid, tid) / 1000; + } + config_->ss_target_bitrate[sid] = + current_bitrate_allocation_.GetSpatialLayerSum(sid) / 1000; + RTC_DCHECK_GT(config_->ss_target_bitrate[sid], 0); + config_changed_ = true; +} + +void LibvpxVp9Encoder::SetActiveSpatialLayers() { + // Svc controller may decide to skip a frame at certain spatial layer even + // when bitrate for it is non-zero, however libvpx uses configured bitrate as + // a signal which layers should be produced. + RTC_DCHECK(svc_controller_); + RTC_DCHECK(!layer_frames_.empty()); + RTC_DCHECK(absl::c_is_sorted( + layer_frames_, [](const ScalableVideoController::LayerFrameConfig& lhs, + const ScalableVideoController::LayerFrameConfig& rhs) { + return lhs.SpatialId() < rhs.SpatialId(); + })); + + auto frame_it = layer_frames_.begin(); + for (int sid = 0; sid < num_spatial_layers_; ++sid) { + if (frame_it != layer_frames_.end() && frame_it->SpatialId() == sid) { + EnableSpatialLayer(sid); + ++frame_it; + } else { + DisableSpatialLayer(sid); + } + } +} + void LibvpxVp9Encoder::SetRates(const RateControlParameters& parameters) { if (!inited_) { RTC_LOG(LS_WARNING) << "SetRates() calll while uninitialzied."; @@ -473,9 +527,11 @@ int LibvpxVp9Encoder::InitEncode(const VideoCodec* inst, } if (encoder_ == nullptr) { encoder_ = new vpx_codec_ctx_t; + memset(encoder_, 0, sizeof(*encoder_)); } if (config_ == nullptr) { config_ = new vpx_codec_enc_cfg_t; + memset(config_, 0, sizeof(*config_)); } timestamp_ = 0; if (&codec_ != inst) { @@ -493,9 +549,7 @@ int LibvpxVp9Encoder::InitEncode(const VideoCodec* inst, num_temporal_layers_ = 1; } - if (use_svc_controller_) { - svc_controller_ = CreateVp9ScalabilityStructure(*inst); - } + svc_controller_ = CreateVp9ScalabilityStructure(*inst); framerate_controller_ = std::vector( num_spatial_layers_, FramerateControllerDeprecated(codec_.maxFramerate)); @@ -519,7 +573,7 @@ int LibvpxVp9Encoder::InitEncode(const VideoCodec* inst, case VP9Profile::kProfile1: // Encoding of profile 1 is not implemented. It would require extended // support for I444, I422, and I440 buffers. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; case VP9Profile::kProfile2: img_fmt = VPX_IMG_FMT_I42016; @@ -779,7 +833,7 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) { libvpx_->codec_control(encoder_, VP9E_SET_SVC_INTER_LAYER_PRED, 2); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } memset(&svc_drop_frame_, 0, sizeof(svc_drop_frame_)); @@ -905,7 +959,7 @@ int LibvpxVp9Encoder::Encode(const VideoFrame& input_image, const size_t gof_idx = (pics_since_key_ + 1) % gof_.num_frames_in_gof; layer_id.temporal_layer_id = gof_.temporal_idx[gof_idx]; - if (VideoCodecMode::kScreensharing == codec_.mode) { + if (codec_.mode == VideoCodecMode::kScreensharing) { const uint32_t frame_timestamp_ms = 1000 * input_image.timestamp() / kVideoPayloadTypeFrequency; @@ -978,6 +1032,7 @@ int LibvpxVp9Encoder::Encode(const VideoFrame& input_image, layer_id.temporal_layer_id_per_spatial[layer.SpatialId()] = layer.TemporalId(); } + SetActiveSpatialLayers(); } if (is_svc_ && performance_flags_.use_per_layer_speed) { @@ -1056,7 +1111,7 @@ int LibvpxVp9Encoder::Encode(const VideoFrame& input_image, break; } case VP9Profile::kProfile1: { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } case VP9Profile::kProfile2: { @@ -1157,8 +1212,7 @@ int LibvpxVp9Encoder::Encode(const VideoFrame& input_image, bool LibvpxVp9Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, absl::optional* spatial_idx, - const vpx_codec_cx_pkt& pkt, - uint32_t timestamp) { + const vpx_codec_cx_pkt& pkt) { RTC_CHECK(codec_specific != nullptr); codec_specific->codecType = kVideoCodecVP9; CodecSpecificInfoVP9* vp9_info = &(codec_specific->codecSpecific.VP9); @@ -1193,9 +1247,6 @@ bool LibvpxVp9Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, *spatial_idx = layer_id.spatial_layer_id; } - // TODO(asapersson): this info has to be obtained from the encoder. - vp9_info->temporal_up_switch = false; - const bool is_key_pic = (pics_since_key_ == 0); const bool is_inter_layer_pred_allowed = (inter_layer_pred_ == InterLayerPredMode::kOn || @@ -1228,6 +1279,20 @@ bool LibvpxVp9Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, vp9_info); if (vp9_info->flexible_mode) { vp9_info->gof_idx = kNoGofIdx; + if (!svc_controller_) { + if (num_temporal_layers_ == 1) { + vp9_info->temporal_up_switch = true; + } else { + // In flexible mode with > 1 temporal layer but no SVC controller we + // can't techincally determine if a frame is an upswitch point, use + // gof-based data as proxy for now. + // TODO(sprang): Remove once SVC controller is the only choice. + vp9_info->gof_idx = + static_cast(pics_since_key_ % gof_.num_frames_in_gof); + vp9_info->temporal_up_switch = + gof_.temporal_up_switch[vp9_info->gof_idx]; + } + } } else { vp9_info->gof_idx = static_cast(pics_since_key_ % gof_.num_frames_in_gof); @@ -1298,6 +1363,23 @@ bool LibvpxVp9Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, svc_params_.scaling_factor_den[sid]); } } + if (is_flexible_mode_) { + // Populate data for legacy temporal-upswitch state. + // We can switch up to a higher temporal layer only if all temporal layers + // higher than this (within the current spatial layer) are switch points. + vp9_info->temporal_up_switch = true; + for (int i = layer_id.temporal_layer_id + 1; i < num_temporal_layers_; + ++i) { + // Assumes decode targets are always ordered first by spatial then by + // temporal id. + size_t dti_index = + (layer_id.spatial_layer_id * num_temporal_layers_) + i; + vp9_info->temporal_up_switch &= + (codec_specific->generic_frame_info + ->decode_target_indications[dti_index] == + DecodeTargetIndication::kSwitch); + } + } } return true; } @@ -1373,8 +1455,6 @@ void LibvpxVp9Encoder::FillReferenceIndices(const vpx_codec_cx_pkt& pkt, ref_buf_list.push_back(ref_buf_.at(0)); } - size_t max_ref_temporal_layer_id = 0; - std::vector ref_pid_list; vp9_info->num_ref_pics = 0; @@ -1406,9 +1486,6 @@ void LibvpxVp9Encoder::FillReferenceIndices(const vpx_codec_cx_pkt& pkt, vp9_info->p_diff[vp9_info->num_ref_pics] = static_cast(p_diff); ++vp9_info->num_ref_pics; - - max_ref_temporal_layer_id = - std::max(max_ref_temporal_layer_id, ref_buf.temporal_layer_id); } else { RTC_DCHECK(inter_layer_predicted); // RTP spec only allows to use previous spatial layer for inter-layer @@ -1416,10 +1493,6 @@ void LibvpxVp9Encoder::FillReferenceIndices(const vpx_codec_cx_pkt& pkt, RTC_DCHECK_EQ(ref_buf.spatial_layer_id + 1, layer_id.spatial_layer_id); } } - - vp9_info->temporal_up_switch = - (max_ref_temporal_layer_id < - static_cast(layer_id.temporal_layer_id)); } void LibvpxVp9Encoder::UpdateReferenceBuffers(const vpx_codec_cx_pkt& pkt, @@ -1579,8 +1652,19 @@ void LibvpxVp9Encoder::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { encoded_image_.SetEncodedData(EncodedImageBuffer::Create( static_cast(pkt->data.frame.buf), pkt->data.frame.sz)); + codec_specific_ = {}; + absl::optional spatial_index; + if (!PopulateCodecSpecific(&codec_specific_, &spatial_index, *pkt)) { + // Drop the frame. + encoded_image_.set_size(0); + return; + } + encoded_image_.SetSpatialIndex(spatial_index); + const bool is_key_frame = - (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true : false; + ((pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true : false) && + !codec_specific_.codecSpecific.VP9.inter_layer_predicted; + // Ensure encoder issued key frame on request. RTC_DCHECK(is_key_frame || !force_key_frame_); @@ -1591,16 +1675,6 @@ void LibvpxVp9Encoder::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { force_key_frame_ = false; } - codec_specific_ = {}; - absl::optional spatial_index; - if (!PopulateCodecSpecific(&codec_specific_, &spatial_index, *pkt, - input_image_->timestamp())) { - // Drop the frame. - encoded_image_.set_size(0); - return; - } - encoded_image_.SetSpatialIndex(spatial_index); - UpdateReferenceBuffers(*pkt, pics_since_key_); TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_.size()); @@ -1680,7 +1754,6 @@ VideoEncoder::EncoderInfo LibvpxVp9Encoder::GetEncoderInfo() const { } info.has_trusted_rate_controller = trusted_rate_controller_; info.is_hardware_accelerated = false; - info.has_internal_source = false; if (inited_) { // Find the max configured fps of any active spatial layer. float max_fps = 0.0; @@ -1875,8 +1948,8 @@ void LibvpxVp9Encoder::MaybeRewrapRawWithFormat(const vpx_img_fmt fmt) { raw_ = libvpx_->img_wrap(nullptr, fmt, codec_.width, codec_.height, 1, nullptr); } else if (raw_->fmt != fmt) { - RTC_LOG(INFO) << "Switching VP9 encoder pixel format to " - << (fmt == VPX_IMG_FMT_NV12 ? "NV12" : "I420"); + RTC_LOG(LS_INFO) << "Switching VP9 encoder pixel format to " + << (fmt == VPX_IMG_FMT_NV12 ? "NV12" : "I420"); libvpx_->img_free(raw_); raw_ = libvpx_->img_wrap(nullptr, fmt, codec_.width, codec_.height, 1, nullptr); @@ -1946,7 +2019,7 @@ rtc::scoped_refptr LibvpxVp9Encoder::PrepareBufferForProfile0( break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return mapped_buffer; } diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h index c2790f06ad..b5e9cc6d4b 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h @@ -67,14 +67,12 @@ class LibvpxVp9Encoder : public VP9Encoder { bool PopulateCodecSpecific(CodecSpecificInfo* codec_specific, absl::optional* spatial_idx, - const vpx_codec_cx_pkt& pkt, - uint32_t timestamp); + const vpx_codec_cx_pkt& pkt); void FillReferenceIndices(const vpx_codec_cx_pkt& pkt, - const size_t pic_num, - const bool inter_layer_predicted, + size_t pic_num, + bool inter_layer_predicted, CodecSpecificInfoVP9* vp9_info); - void UpdateReferenceBuffers(const vpx_codec_cx_pkt& pkt, - const size_t pic_num); + void UpdateReferenceBuffers(const vpx_codec_cx_pkt& pkt, size_t pic_num); vpx_svc_ref_frame_config_t SetReferences( bool is_key_pic, size_t first_active_spatial_layer_id); @@ -82,6 +80,12 @@ class LibvpxVp9Encoder : public VP9Encoder { bool ExplicitlyConfiguredSpatialLayers() const; bool SetSvcRates(const VideoBitrateAllocation& bitrate_allocation); + // Configures which spatial layers libvpx should encode according to + // configuration provided by svc_controller_. + void EnableSpatialLayer(int sid); + void DisableSpatialLayer(int sid); + void SetActiveSpatialLayers(); + void GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt); // Callback function for outputting packets per spatial layer. @@ -102,7 +106,7 @@ class LibvpxVp9Encoder : public VP9Encoder { size_t SteadyStateSize(int sid, int tid); - void MaybeRewrapRawWithFormat(const vpx_img_fmt fmt); + void MaybeRewrapRawWithFormat(vpx_img_fmt fmt); // Prepares `raw_` to reference image data of `buffer`, or of mapped or scaled // versions of `buffer`. Returns the buffer that got referenced as a result, // allowing the caller to keep a reference to it until after encoding has @@ -144,7 +148,6 @@ class LibvpxVp9Encoder : public VP9Encoder { VideoBitrateAllocation current_bitrate_allocation_; bool ss_info_needed_; bool force_all_active_layers_; - const bool use_svc_controller_; std::unique_ptr svc_controller_; std::vector framerate_controller_; diff --git a/modules/video_coding/codecs/vp9/svc_config.cc b/modules/video_coding/codecs/vp9/svc_config.cc index cc7743ad25..92818eb4f9 100644 --- a/modules/video_coding/codecs/vp9/svc_config.cc +++ b/modules/video_coding/codecs/vp9/svc_config.cc @@ -69,12 +69,15 @@ std::vector ConfigureSvcNormalVideo(size_t input_width, std::vector spatial_layers; // Limit number of layers for given resolution. + const bool is_landscape = input_width >= input_height; + const size_t min_width = is_landscape ? kMinVp9SpatialLayerLongSideLength + : kMinVp9SpatialLayerShortSideLength; + const size_t min_height = is_landscape ? kMinVp9SpatialLayerShortSideLength + : kMinVp9SpatialLayerLongSideLength; const size_t num_layers_fit_horz = static_cast(std::floor( - 1 + std::max(0.0f, - std::log2(1.0f * input_width / kMinVp9SpatialLayerWidth)))); - const size_t num_layers_fit_vert = static_cast( - std::floor(1 + std::max(0.0f, std::log2(1.0f * input_height / - kMinVp9SpatialLayerHeight)))); + 1 + std::max(0.0f, std::log2(1.0f * input_width / min_width)))); + const size_t num_layers_fit_vert = static_cast(std::floor( + 1 + std::max(0.0f, std::log2(1.0f * input_height / min_height)))); const size_t limited_num_spatial_layers = std::min(num_layers_fit_horz, num_layers_fit_vert); if (limited_num_spatial_layers < num_spatial_layers) { diff --git a/modules/video_coding/codecs/vp9/svc_config_unittest.cc b/modules/video_coding/codecs/vp9/svc_config_unittest.cc index 1891628921..77d75ee8bc 100644 --- a/modules/video_coding/codecs/vp9/svc_config_unittest.cc +++ b/modules/video_coding/codecs/vp9/svc_config_unittest.cc @@ -22,10 +22,23 @@ TEST(SvcConfig, NumSpatialLayers) { const size_t first_active_layer = 0; const size_t num_spatial_layers = 2; - std::vector spatial_layers = - GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1), - kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30, - first_active_layer, max_num_spatial_layers, 1, false); + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), + kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30, + first_active_layer, max_num_spatial_layers, 1, false); + + EXPECT_EQ(spatial_layers.size(), num_spatial_layers); +} + +TEST(SvcConfig, NumSpatialLayersPortrait) { + const size_t max_num_spatial_layers = 6; + const size_t first_active_layer = 0; + const size_t num_spatial_layers = 2; + + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), + kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), 30, + first_active_layer, max_num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), num_spatial_layers); } @@ -34,11 +47,22 @@ TEST(SvcConfig, AlwaysSendsAtLeastOneLayer) { const size_t max_num_spatial_layers = 6; const size_t first_active_layer = 5; - std::vector spatial_layers = - GetSvcConfig(kMinVp9SpatialLayerWidth, kMinVp9SpatialLayerHeight, 30, - first_active_layer, max_num_spatial_layers, 1, false); + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerLongSideLength, kMinVp9SpatialLayerShortSideLength, 30, + first_active_layer, max_num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), 1u); - EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerWidth); + EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerLongSideLength); +} + +TEST(SvcConfig, AlwaysSendsAtLeastOneLayerPortrait) { + const size_t max_num_spatial_layers = 6; + const size_t first_active_layer = 5; + + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerShortSideLength, kMinVp9SpatialLayerLongSideLength, 30, + first_active_layer, max_num_spatial_layers, 1, false); + EXPECT_EQ(spatial_layers.size(), 1u); + EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerShortSideLength); } TEST(SvcConfig, EnforcesMinimalRequiredParity) { @@ -71,22 +95,22 @@ TEST(SvcConfig, SkipsInactiveLayers) { const size_t num_spatial_layers = 4; const size_t first_active_layer = 2; - std::vector spatial_layers = - GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1), - kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30, - first_active_layer, num_spatial_layers, 1, false); + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), + kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30, + first_active_layer, num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), 2u); EXPECT_EQ(spatial_layers.back().width, - kMinVp9SpatialLayerWidth << (num_spatial_layers - 1)); + kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1)); } TEST(SvcConfig, BitrateThresholds) { const size_t first_active_layer = 0; const size_t num_spatial_layers = 3; - std::vector spatial_layers = - GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1), - kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30, - first_active_layer, num_spatial_layers, 1, false); + std::vector spatial_layers = GetSvcConfig( + kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), + kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30, + first_active_layer, num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), num_spatial_layers); diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc index f61f673d49..753aedc00f 100644 --- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc +++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc @@ -257,8 +257,6 @@ TEST(Vp9ImplTest, ParserQpEqualsEncodedQp) { } TEST(Vp9ImplTest, EncodeAttachesTemplateStructureWithSvcController) { - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); std::unique_ptr encoder = VP9Encoder::Create(); VideoCodec codec_settings = DefaultCodecSettings(); EXPECT_EQ(encoder->InitEncode(&codec_settings, kSettings), @@ -301,8 +299,6 @@ TEST(Vp9ImplTest, EncoderWith2TemporalLayers) { } TEST(Vp9ImplTest, EncodeTemporalLayersWithSvcController) { - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); std::unique_ptr encoder = VP9Encoder::Create(); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.VP9()->numberOfTemporalLayers = 2; @@ -350,8 +346,6 @@ TEST(Vp9ImplTest, EncoderWith2SpatialLayers) { } TEST(Vp9ImplTest, EncodeSpatialLayersWithSvcController) { - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); std::unique_ptr encoder = VP9Encoder::Create(); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.VP9()->numberOfSpatialLayers = 2; @@ -487,8 +481,6 @@ TEST_F(TestVp9Impl, EnableDisableSpatialLayers) { } TEST(Vp9ImplTest, EnableDisableSpatialLayersWithSvcController) { - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); const int num_spatial_layers = 3; // Configure encoder to produce 3 spatial layers. Encode frames of layer 0 // then enable layer 1 and encode more frames and so on. @@ -563,8 +555,6 @@ MATCHER_P2(GenericLayerIs, spatial_id, temporal_id, "") { } TEST(Vp9ImplTest, SpatialUpswitchNotAtGOFBoundary) { - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); std::unique_ptr encoder = VP9Encoder::Create(); VideoCodec codec_settings = DefaultCodecSettings(); ConfigureSvc(codec_settings, /*num_spatial_layers=*/3, @@ -603,8 +593,8 @@ TEST(Vp9ImplTest, SpatialUpswitchNotAtGOFBoundary) { EXPECT_THAT(producer.SetNumInputFrames(1).Encode(), ElementsAre(GenericLayerIs(0, 0), GenericLayerIs(1, 0))); } - -TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { +// TODO(bugs.webrtc.org/13442) Enable once a forward fix has landed in WebRTC. +TEST_F(TestVp9Impl, DISABLED_DisableEnableBaseLayerTriggersKeyFrame) { // Configure encoder to produce N spatial layers. Encode frames for all // layers. Then disable all but the last layer. Then reenable all back again. test::ScopedFieldTrials override_field_trials( @@ -759,12 +749,11 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { EXPECT_EQ(encoded_frame[0]._frameType, expected_type); } } - -TEST(Vp9ImplTest, DisableEnableBaseLayerWithSvcControllerTriggersKeyFrame) { +// TODO(bugs.webrtc.org/13442) Enable once a forward fix has landed in WebRTC. +TEST(Vp9ImplTest, + DISABLED_DisableEnableBaseLayerWithSvcControllerTriggersKeyFrame) { // Configure encoder to produce N spatial layers. Encode frames for all // layers. Then disable all but the last layer. Then reenable all back again. - test::ScopedFieldTrials override_field_trials( - "WebRTC-Vp9DependencyDescriptor/Enabled/"); const size_t num_spatial_layers = 3; const size_t num_temporal_layers = 3; // Must not be multiple of temporal period to exercise all code paths. @@ -2323,8 +2312,17 @@ TEST(Vp9SpeedSettingsTrialsTest, DefaultPerLayerFlagsWithSvc) { absl::WrapUnique(vpx), trials); VideoCodec settings = DefaultCodecSettings(); - const int kNumSpatialLayers = 3; - ConfigureSvc(settings, kNumSpatialLayers, /*num_temporal_layers=*/3); + constexpr int kNumSpatialLayers = 3; + constexpr int kNumTemporalLayers = 3; + ConfigureSvc(settings, kNumSpatialLayers, kNumTemporalLayers); + VideoBitrateAllocation bitrate_allocation; + for (int si = 0; si < kNumSpatialLayers; ++si) { + for (int ti = 0; ti < kNumTemporalLayers; ++ti) { + uint32_t bitrate_bps = + settings.spatialLayers[si].targetBitrate * 1'000 / kNumTemporalLayers; + bitrate_allocation.SetBitrate(si, ti, bitrate_bps); + } + } vpx_image_t img; // Speed settings per spatial layer, for TL0. @@ -2336,6 +2334,11 @@ TEST(Vp9SpeedSettingsTrialsTest, DefaultPerLayerFlagsWithSvc) { const int kLoopFilter[VPX_MAX_LAYERS] = {1, 0, 0}; ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img)); + ON_CALL(*vpx, codec_enc_init) + .WillByDefault(WithArg<0>([](vpx_codec_ctx_t* ctx) { + memset(ctx, 0, sizeof(*ctx)); + return VPX_CODEC_OK; + })); ON_CALL(*vpx, codec_enc_config_default) .WillByDefault(DoAll(WithArg<1>([](vpx_codec_enc_cfg_t* cfg) { memset(cfg, 0, sizeof(vpx_codec_enc_cfg_t)); @@ -2360,6 +2363,9 @@ TEST(Vp9SpeedSettingsTrialsTest, DefaultPerLayerFlagsWithSvc) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings)); + encoder.SetRates(VideoEncoder::RateControlParameters(bitrate_allocation, + settings.maxFramerate)); + MockEncodedImageCallback callback; encoder.RegisterEncodeCompleteCallback(&callback); auto frame_generator = test::CreateSquareFrameGenerator( diff --git a/modules/video_coding/codecs/vp9/vp9.cc b/modules/video_coding/codecs/vp9/vp9.cc index d9caf0f039..7b787f3e4f 100644 --- a/modules/video_coding/codecs/vp9/vp9.cc +++ b/modules/video_coding/codecs/vp9/vp9.cc @@ -23,6 +23,13 @@ #include "vpx/vpx_codec.h" namespace webrtc { +namespace { +constexpr absl::string_view kSupportedScalabilityModes[] = { + "L1T2", "L1T3", "L2T1", "L2T2", "L2T3", "L3T1", + "L3T2", "L3T3", "L1T2h", "L1T3h", "L2T1h", "L2T2h", + "L2T3h", "L3T1h", "L3T2h", "L3T3h", "L2T2_KEY", "L2T3_KEY", + "L3T1_KEY", "L3T2_KEY", "L3T3_KEY"}; +} // namespace std::vector SupportedVP9Codecs() { #ifdef RTC_ENABLE_VP9 @@ -70,7 +77,7 @@ std::unique_ptr VP9Encoder::Create() { LibvpxInterface::Create(), FieldTrialBasedConfig()); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif } @@ -81,16 +88,25 @@ std::unique_ptr VP9Encoder::Create( return std::make_unique(codec, LibvpxInterface::Create(), FieldTrialBasedConfig()); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif } +bool VP9Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { + for (const auto& entry : kSupportedScalabilityModes) { + if (entry == scalability_mode) { + return true; + } + } + return false; +} + std::unique_ptr VP9Decoder::Create() { #ifdef RTC_ENABLE_VP9 return std::make_unique(); #else - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; #endif } diff --git a/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc b/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc index f399c5945f..181550ce91 100644 --- a/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc +++ b/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc @@ -79,7 +79,7 @@ Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) { // TODO(phoglund): this limit is being hit in tests since Oct 5 2016. // See https://bugs.chromium.org/p/webrtc/issues/detail?id=6484. - // RTC_NOTREACHED(); + // RTC_DCHECK_NOTREACHED(); } } } diff --git a/modules/video_coding/fec_controller_default.cc b/modules/video_coding/fec_controller_default.cc index 88315a80de..f204b01c7c 100644 --- a/modules/video_coding/fec_controller_default.cc +++ b/modules/video_coding/fec_controller_default.cc @@ -70,8 +70,9 @@ float FecControllerDefault::GetProtectionOverheadRateThreshold() { << overhead_threshold; return overhead_threshold; } else if (overhead_threshold < 0 || overhead_threshold > 1) { - RTC_LOG(WARNING) << "ProtectionOverheadRateThreshold field trial is set to " - "an invalid value, expecting a value between (0, 1]."; + RTC_LOG(LS_WARNING) + << "ProtectionOverheadRateThreshold field trial is set to " + "an invalid value, expecting a value between (0, 1]."; } // WebRTC-ProtectionOverheadRateThreshold field trial string is not found, use // the default value. diff --git a/modules/video_coding/fec_controller_default.h b/modules/video_coding/fec_controller_default.h index 6b9e8eb8e5..a97dea011b 100644 --- a/modules/video_coding/fec_controller_default.h +++ b/modules/video_coding/fec_controller_default.h @@ -19,7 +19,6 @@ #include "api/fec_controller.h" #include "modules/video_coding/media_opt_util.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" #include "system_wrappers/include/clock.h" @@ -32,6 +31,10 @@ class FecControllerDefault : public FecController { VCMProtectionCallback* protection_callback); explicit FecControllerDefault(Clock* clock); ~FecControllerDefault() override; + + FecControllerDefault(const FecControllerDefault&) = delete; + FecControllerDefault& operator=(const FecControllerDefault&) = delete; + void SetProtectionCallback( VCMProtectionCallback* protection_callback) override; void SetProtectionMethod(bool enable_fec, bool enable_nack) override; @@ -44,9 +47,8 @@ class FecControllerDefault : public FecController { uint8_t fraction_lost, std::vector loss_mask_vector, int64_t round_trip_time_ms) override; - void UpdateWithEncodedData( - const size_t encoded_image_length, - const VideoFrameType encoded_image_frametype) override; + void UpdateWithEncodedData(size_t encoded_image_length, + VideoFrameType encoded_image_frametype) override; bool UseLossVectorMask() override; float GetProtectionOverheadRateThreshold(); @@ -58,7 +60,7 @@ class FecControllerDefault : public FecController { std::unique_ptr loss_prot_logic_ RTC_GUARDED_BY(mutex_); size_t max_payload_size_ RTC_GUARDED_BY(mutex_); - RTC_DISALLOW_COPY_AND_ASSIGN(FecControllerDefault); + const float overhead_threshold_; }; diff --git a/modules/video_coding/frame_buffer.cc b/modules/video_coding/frame_buffer.cc index 937534aab0..787da1e5a9 100644 --- a/modules/video_coding/frame_buffer.cc +++ b/modules/video_coding/frame_buffer.cc @@ -241,7 +241,7 @@ void VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) { case kStateEmpty: // Should only be set to empty through Reset(). - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } _state = state; diff --git a/modules/video_coding/frame_buffer2.cc b/modules/video_coding/frame_buffer2.cc index d0e681e2d7..5680c17ea5 100644 --- a/modules/video_coding/frame_buffer2.cc +++ b/modules/video_coding/frame_buffer2.cc @@ -13,13 +13,17 @@ #include #include #include +#include #include #include #include -#include "absl/memory/memory.h" +#include "absl/container/inlined_vector.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" #include "api/video/encoded_image.h" #include "api/video/video_timing.h" +#include "modules/video_coding/frame_helpers.h" #include "modules/video_coding/include/video_coding_defines.h" #include "modules/video_coding/jitter_estimator.h" #include "modules/video_coding/timing.h" @@ -38,6 +42,10 @@ namespace { // Max number of frames the buffer will hold. constexpr size_t kMaxFramesBuffered = 800; +// Default value for the maximum decode queue size that is used when the +// low-latency renderer is used. +constexpr size_t kZeroPlayoutDelayDefaultMaxDecodeQueueSize = 8; + // Max number of decoded frame info that will be saved. constexpr int kMaxFramesHistory = 1 << 13; @@ -56,16 +64,14 @@ FrameBuffer::FrameBuffer(Clock* clock, callback_queue_(nullptr), jitter_estimator_(clock), timing_(timing), - inter_frame_delay_(clock_->TimeInMilliseconds()), stopped_(false), protection_mode_(kProtectionNack), stats_callback_(stats_callback), last_log_non_decoded_ms_(-kLogNonDecodedIntervalMs), - add_rtt_to_playout_delay_( - webrtc::field_trial::IsEnabled("WebRTC-AddRttToPlayoutDelay")), rtt_mult_settings_(RttMultExperiment::GetRttMultValue()), - zero_playout_delay_max_decode_queue_size_("max_decode_queue_size", - kMaxFramesBuffered) { + zero_playout_delay_max_decode_queue_size_( + "max_decode_queue_size", + kZeroPlayoutDelayDefaultMaxDecodeQueueSize) { ParseFieldTrial({&zero_playout_delay_max_decode_queue_size_}, field_trial::FindFullName("WebRTC-ZeroPlayoutDelay")); callback_checker_.Detach(); @@ -75,11 +81,10 @@ FrameBuffer::~FrameBuffer() { RTC_DCHECK_RUN_ON(&construction_checker_); } -void FrameBuffer::NextFrame( - int64_t max_wait_time_ms, - bool keyframe_required, - rtc::TaskQueue* callback_queue, - std::function, ReturnReason)> handler) { +void FrameBuffer::NextFrame(int64_t max_wait_time_ms, + bool keyframe_required, + rtc::TaskQueue* callback_queue, + NextFrameCallback handler) { RTC_DCHECK_RUN_ON(&callback_checker_); RTC_DCHECK(callback_queue->IsCurrent()); TRACE_EVENT0("webrtc", "FrameBuffer::NextFrame"); @@ -100,41 +105,40 @@ void FrameBuffer::NextFrame( void FrameBuffer::StartWaitForNextFrameOnQueue() { RTC_DCHECK(callback_queue_); RTC_DCHECK(!callback_task_.Running()); - int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds()); + int64_t wait_ms = FindNextFrame(clock_->CurrentTime()); callback_task_ = RepeatingTaskHandle::DelayedStart( - callback_queue_->Get(), TimeDelta::Millis(wait_ms), [this] { + callback_queue_->Get(), TimeDelta::Millis(wait_ms), + [this] { RTC_DCHECK_RUN_ON(&callback_checker_); // If this task has not been cancelled, we did not get any new frames // while waiting. Continue with frame delivery. std::unique_ptr frame; - std::function, ReturnReason)> - frame_handler; + NextFrameCallback frame_handler; { MutexLock lock(&mutex_); if (!frames_to_decode_.empty()) { // We have frames, deliver! - frame = absl::WrapUnique(GetNextFrame()); - timing_->SetLastDecodeScheduledTimestamp( - clock_->TimeInMilliseconds()); + frame = GetNextFrame(); + timing_->SetLastDecodeScheduledTimestamp(clock_->CurrentTime()); } else if (clock_->TimeInMilliseconds() < latest_return_time_ms_) { // If there's no frames to decode and there is still time left, it // means that the frame buffer was cleared between creation and // execution of this task. Continue waiting for the remaining time. - int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds()); + int64_t wait_ms = FindNextFrame(clock_->CurrentTime()); return TimeDelta::Millis(wait_ms); } frame_handler = std::move(frame_handler_); CancelCallback(); } // Deliver frame, if any. Otherwise signal timeout. - ReturnReason reason = frame ? kFrameFound : kTimeout; - frame_handler(std::move(frame), reason); + frame_handler(std::move(frame)); return TimeDelta::Zero(); // Ignored. - }); + }, + TaskQueueBase::DelayPrecision::kHigh); } -int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { - int64_t wait_ms = latest_return_time_ms_ - now_ms; +int64_t FrameBuffer::FindNextFrame(Timestamp now) { + int64_t wait_ms = latest_return_time_ms_ - now.ms(); frames_to_decode_.clear(); // `last_continuous_frame_` may be empty below, but nullopt is smaller @@ -213,14 +217,16 @@ int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { frames_to_decode_ = std::move(current_superframe); - if (frame->RenderTime() == -1) { - frame->SetRenderTime(timing_->RenderTimeMs(frame->Timestamp(), now_ms)); + absl::optional render_time = frame->RenderTimestamp(); + if (!render_time) { + render_time = timing_->RenderTime(frame->Timestamp(), now); + frame->SetRenderTime(render_time->ms()); } bool too_many_frames_queued = frames_.size() > zero_playout_delay_max_decode_queue_size_ ? true : false; - wait_ms = timing_->MaxWaitingTime(frame->RenderTime(), now_ms, - too_many_frames_queued); + wait_ms = + timing_->MaxWaitingTime(*render_time, now, too_many_frames_queued).ms(); // This will cause the frame buffer to prefer high framerate rather // than high resolution in the case of the decoder not decoding fast @@ -232,39 +238,40 @@ int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { break; } - wait_ms = std::min(wait_ms, latest_return_time_ms_ - now_ms); + wait_ms = std::min(wait_ms, latest_return_time_ms_ - now.ms()); wait_ms = std::max(wait_ms, 0); return wait_ms; } -EncodedFrame* FrameBuffer::GetNextFrame() { +std::unique_ptr FrameBuffer::GetNextFrame() { RTC_DCHECK_RUN_ON(&callback_checker_); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); // TODO(ilnik): remove `frames_out` use frames_to_decode_ directly. - std::vector frames_out; + std::vector> frames_out; RTC_DCHECK(!frames_to_decode_.empty()); bool superframe_delayed_by_retransmission = false; - size_t superframe_size = 0; - EncodedFrame* first_frame = frames_to_decode_[0]->second.frame.get(); - int64_t render_time_ms = first_frame->RenderTime(); - int64_t receive_time_ms = first_frame->ReceivedTime(); + DataSize superframe_size = DataSize::Zero(); + const EncodedFrame& first_frame = *frames_to_decode_[0]->second.frame; + absl::optional render_time = first_frame.RenderTimestamp(); + int64_t receive_time_ms = first_frame.ReceivedTime(); // Gracefully handle bad RTP timestamps and render time issues. - if (HasBadRenderTiming(*first_frame, now_ms)) { + if (!render_time || + FrameHasBadRenderTiming(*render_time, now, timing_->TargetVideoDelay())) { jitter_estimator_.Reset(); timing_->Reset(); - render_time_ms = timing_->RenderTimeMs(first_frame->Timestamp(), now_ms); + render_time = timing_->RenderTime(first_frame.Timestamp(), now); } for (FrameMap::iterator& frame_it : frames_to_decode_) { RTC_DCHECK(frame_it != frames_.end()); - EncodedFrame* frame = frame_it->second.frame.release(); + std::unique_ptr frame = std::move(frame_it->second.frame); - frame->SetRenderTime(render_time_ms); + frame->SetRenderTime(render_time->ms()); superframe_delayed_by_retransmission |= frame->delayed_by_retransmission(); receive_time_ms = std::max(receive_time_ms, frame->ReceivedTime()); - superframe_size += frame->size(); + superframe_size += DataSize::Bytes(frame->size()); PropagateDecodability(frame_it->second); decoded_frames_history_.InsertDecoded(frame_it->first, frame->Timestamp()); @@ -283,28 +290,29 @@ EncodedFrame* FrameBuffer::GetNextFrame() { frames_.erase(frames_.begin(), ++frame_it); - frames_out.push_back(frame); + frames_out.emplace_back(std::move(frame)); } if (!superframe_delayed_by_retransmission) { - int64_t frame_delay; + auto frame_delay = inter_frame_delay_.CalculateDelay( + first_frame.Timestamp(), Timestamp::Millis(receive_time_ms)); - if (inter_frame_delay_.CalculateDelay(first_frame->Timestamp(), - &frame_delay, receive_time_ms)) { - jitter_estimator_.UpdateEstimate(frame_delay, superframe_size); + if (frame_delay) { + jitter_estimator_.UpdateEstimate(*frame_delay, superframe_size); } float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0; - absl::optional rtt_mult_add_cap_ms = absl::nullopt; + absl::optional rtt_mult_add_cap_ms = absl::nullopt; if (rtt_mult_settings_.has_value()) { rtt_mult = rtt_mult_settings_->rtt_mult_setting; - rtt_mult_add_cap_ms = rtt_mult_settings_->rtt_mult_add_cap_ms; + rtt_mult_add_cap_ms = + TimeDelta::Millis(rtt_mult_settings_->rtt_mult_add_cap_ms); } timing_->SetJitterDelay( jitter_estimator_.GetJitterEstimate(rtt_mult, rtt_mult_add_cap_ms)); - timing_->UpdateCurrentDelay(render_time_ms, now_ms); + timing_->UpdateCurrentDelay(*render_time, now); } else { - if (RttMultExperiment::RttMultEnabled() || add_rtt_to_playout_delay_) + if (RttMultExperiment::RttMultEnabled()) jitter_estimator_.FrameNacked(); } @@ -312,41 +320,12 @@ EncodedFrame* FrameBuffer::GetNextFrame() { UpdateTimingFrameInfo(); if (frames_out.size() == 1) { - return frames_out[0]; + return std::move(frames_out[0]); } else { - return CombineAndDeleteFrames(frames_out); + return CombineAndDeleteFrames(std::move(frames_out)); } } -bool FrameBuffer::HasBadRenderTiming(const EncodedFrame& frame, - int64_t now_ms) { - // Assume that render timing errors are due to changes in the video stream. - int64_t render_time_ms = frame.RenderTimeMs(); - // Zero render time means render immediately. - if (render_time_ms == 0) { - return false; - } - if (render_time_ms < 0) { - return true; - } - const int64_t kMaxVideoDelayMs = 10000; - if (std::abs(render_time_ms - now_ms) > kMaxVideoDelayMs) { - int frame_delay = static_cast(std::abs(render_time_ms - now_ms)); - RTC_LOG(LS_WARNING) - << "A frame about to be decoded is out of the configured " - "delay bounds (" - << frame_delay << " > " << kMaxVideoDelayMs - << "). Resetting the video jitter buffer."; - return true; - } - if (static_cast(timing_->TargetVideoDelay()) > kMaxVideoDelayMs) { - RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " - << kMaxVideoDelayMs << " ms."; - return true; - } - return false; -} - void FrameBuffer::SetProtectionMode(VCMVideoProtection mode) { TRACE_EVENT0("webrtc", "FrameBuffer::SetProtectionMode"); MutexLock lock(&mutex_); @@ -375,7 +354,7 @@ int FrameBuffer::Size() { void FrameBuffer::UpdateRtt(int64_t rtt_ms) { MutexLock lock(&mutex_); - jitter_estimator_.UpdateRtt(rtt_ms); + jitter_estimator_.UpdateRtt(TimeDelta::Millis(rtt_ms)); } bool FrameBuffer::ValidReferences(const EncodedFrame& frame) const { @@ -470,8 +449,10 @@ int64_t FrameBuffer::InsertFrame(std::unique_ptr frame) { if (!UpdateFrameInfoWithIncomingFrame(*frame, info)) return last_continuous_frame_id; - if (!frame->delayed_by_retransmission()) - timing_->IncomingTimestamp(frame->Timestamp(), frame->ReceivedTime()); + // If ReceiveTime is negative then it is not a valid timestamp. + if (!frame->delayed_by_retransmission() && frame->ReceivedTime() >= 0) + timing_->IncomingTimestamp(frame->Timestamp(), + Timestamp::Millis(frame->ReceivedTime())); // It can happen that a frame will be reported as fully received even if a // lower spatial layer frame is missing. @@ -615,18 +596,17 @@ void FrameBuffer::UpdateJitterDelay() { if (!stats_callback_) return; - int max_decode_ms; - int current_delay_ms; - int target_delay_ms; - int jitter_buffer_ms; - int min_playout_delay_ms; - int render_delay_ms; - if (timing_->GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms, - &jitter_buffer_ms, &min_playout_delay_ms, - &render_delay_ms)) { + TimeDelta max_decode = TimeDelta::Zero(); + TimeDelta current_delay = TimeDelta::Zero(); + TimeDelta target_delay = TimeDelta::Zero(); + TimeDelta jitter_buffer = TimeDelta::Zero(); + TimeDelta min_playout_delay = TimeDelta::Zero(); + TimeDelta render_delay = TimeDelta::Zero(); + if (timing_->GetTimings(&max_decode, ¤t_delay, &target_delay, + &jitter_buffer, &min_playout_delay, &render_delay)) { stats_callback_->OnFrameBufferTimingsUpdated( - max_decode_ms, current_delay_ms, target_delay_ms, jitter_buffer_ms, - min_playout_delay_ms, render_delay_ms); + max_decode.ms(), current_delay.ms(), target_delay.ms(), + jitter_buffer.ms(), min_playout_delay.ms(), render_delay.ms()); } } @@ -657,42 +637,14 @@ void FrameBuffer::ClearFramesAndHistory() { // TODO(philipel): Avoid the concatenation of frames here, by replacing // NextFrame and GetNextFrame with methods returning multiple frames. -EncodedFrame* FrameBuffer::CombineAndDeleteFrames( - const std::vector& frames) const { +std::unique_ptr FrameBuffer::CombineAndDeleteFrames( + std::vector> frames) const { RTC_DCHECK(!frames.empty()); - EncodedFrame* first_frame = frames[0]; - EncodedFrame* last_frame = frames.back(); - size_t total_length = 0; - for (size_t i = 0; i < frames.size(); ++i) { - total_length += frames[i]->size(); + absl::InlinedVector, 4> inlined; + for (auto& frame : frames) { + inlined.push_back(std::move(frame)); } - auto encoded_image_buffer = EncodedImageBuffer::Create(total_length); - uint8_t* buffer = encoded_image_buffer->data(); - first_frame->SetSpatialLayerFrameSize(first_frame->SpatialIndex().value_or(0), - first_frame->size()); - memcpy(buffer, first_frame->data(), first_frame->size()); - buffer += first_frame->size(); - - // Spatial index of combined frame is set equal to spatial index of its top - // spatial layer. - first_frame->SetSpatialIndex(last_frame->SpatialIndex().value_or(0)); - - first_frame->video_timing_mutable()->network2_timestamp_ms = - last_frame->video_timing().network2_timestamp_ms; - first_frame->video_timing_mutable()->receive_finish_ms = - last_frame->video_timing().receive_finish_ms; - - // Append all remaining frames to the first one. - for (size_t i = 1; i < frames.size(); ++i) { - EncodedFrame* next_frame = frames[i]; - first_frame->SetSpatialLayerFrameSize( - next_frame->SpatialIndex().value_or(0), next_frame->size()); - memcpy(buffer, next_frame->data(), next_frame->size()); - buffer += next_frame->size(); - delete next_frame; - } - first_frame->SetEncodedData(encoded_image_buffer); - return first_frame; + return webrtc::CombineAndDeleteFrames(std::move(inlined)); } FrameBuffer::FrameInfo::FrameInfo() = default; diff --git a/modules/video_coding/frame_buffer2.h b/modules/video_coding/frame_buffer2.h index d46bb9acb9..5cefa8b0d8 100644 --- a/modules/video_coding/frame_buffer2.h +++ b/modules/video_coding/frame_buffer2.h @@ -45,8 +45,6 @@ namespace video_coding { class FrameBuffer { public: - enum ReturnReason { kFrameFound, kTimeout, kStopped }; - FrameBuffer(Clock* clock, VCMTiming* timing, VCMReceiveStatisticsCallback* stats_callback); @@ -61,13 +59,13 @@ class FrameBuffer { // of the last continuous frame or -1 if there is no continuous frame. int64_t InsertFrame(std::unique_ptr frame); - // Get the next frame for decoding. Will return at latest after - // `max_wait_time_ms`. - void NextFrame( - int64_t max_wait_time_ms, - bool keyframe_required, - rtc::TaskQueue* callback_queue, - std::function, ReturnReason)> handler); + using NextFrameCallback = std::function)>; + // Get the next frame for decoding. `handler` is invoked with the next frame + // or with nullptr if no frame is ready for decoding after `max_wait_time_ms`. + void NextFrame(int64_t max_wait_time_ms, + bool keyframe_required, + rtc::TaskQueue* callback_queue, + NextFrameCallback handler); // Tells the FrameBuffer which protection mode that is in use. Affects // the frame timing. @@ -120,8 +118,9 @@ class FrameBuffer { // Check that the references of `frame` are valid. bool ValidReferences(const EncodedFrame& frame) const; - int64_t FindNextFrame(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - EncodedFrame* GetNextFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + int64_t FindNextFrame(Timestamp now) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + std::unique_ptr GetNextFrame() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); void StartWaitForNextFrameOnQueue() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); void CancelCallback() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); @@ -148,15 +147,12 @@ class FrameBuffer { void ClearFramesAndHistory() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool HasBadRenderTiming(const EncodedFrame& frame, int64_t now_ms) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // The cleaner solution would be to have the NextFrame function return a // vector of frames, but until the decoding pipeline can support decoding // multiple frames at the same time we combine all frames to one frame and // return it. See bugs.webrtc.org/10064 - EncodedFrame* CombineAndDeleteFrames( - const std::vector& frames) const; + std::unique_ptr CombineAndDeleteFrames( + std::vector> frames) const; RTC_NO_UNIQUE_ADDRESS SequenceChecker construction_checker_; RTC_NO_UNIQUE_ADDRESS SequenceChecker callback_checker_; @@ -170,8 +166,7 @@ class FrameBuffer { rtc::TaskQueue* callback_queue_ RTC_GUARDED_BY(mutex_); RepeatingTaskHandle callback_task_ RTC_GUARDED_BY(mutex_); - std::function, ReturnReason)> - frame_handler_ RTC_GUARDED_BY(mutex_); + NextFrameCallback frame_handler_ RTC_GUARDED_BY(mutex_); int64_t latest_return_time_ms_ RTC_GUARDED_BY(mutex_); bool keyframe_required_ RTC_GUARDED_BY(mutex_); @@ -185,8 +180,6 @@ class FrameBuffer { VCMReceiveStatisticsCallback* const stats_callback_; int64_t last_log_non_decoded_ms_ RTC_GUARDED_BY(mutex_); - const bool add_rtt_to_playout_delay_; - // rtt_mult experiment settings. const absl::optional rtt_mult_settings_; diff --git a/modules/video_coding/frame_buffer2_unittest.cc b/modules/video_coding/frame_buffer2_unittest.cc index f2a0589411..4811635d64 100644 --- a/modules/video_coding/frame_buffer2_unittest.cc +++ b/modules/video_coding/frame_buffer2_unittest.cc @@ -16,6 +16,8 @@ #include #include +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/frame_object.h" #include "modules/video_coding/jitter_estimator.h" #include "modules/video_coding/timing.h" @@ -29,7 +31,9 @@ #include "test/time_controller/simulated_time_controller.h" using ::testing::_; +using ::testing::IsEmpty; using ::testing::Return; +using ::testing::SizeIs; namespace webrtc { namespace video_coding { @@ -38,56 +42,55 @@ class VCMTimingFake : public VCMTiming { public: explicit VCMTimingFake(Clock* clock) : VCMTiming(clock) {} - int64_t RenderTimeMs(uint32_t frame_timestamp, - int64_t now_ms) const override { - if (last_ms_ == -1) { - last_ms_ = now_ms + kDelayMs; + Timestamp RenderTime(uint32_t frame_timestamp, Timestamp now) const override { + if (last_render_time_.IsMinusInfinity()) { + last_render_time_ = now + kDelay; last_timestamp_ = frame_timestamp; } - uint32_t diff = MinDiff(frame_timestamp, last_timestamp_); + auto diff = MinDiff(frame_timestamp, last_timestamp_); + auto timeDiff = TimeDelta::Millis(diff / 90); if (AheadOf(frame_timestamp, last_timestamp_)) - last_ms_ += diff / 90; + last_render_time_ += timeDiff; else - last_ms_ -= diff / 90; + last_render_time_ -= timeDiff; last_timestamp_ = frame_timestamp; - return last_ms_; + return last_render_time_; } - int64_t MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const override { - return render_time_ms - now_ms - kDecodeTime; + TimeDelta MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const override { + return render_time - now - kDecodeTime; } - bool GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const override { + bool GetTimings(TimeDelta* max_decode, + TimeDelta* current_delay, + TimeDelta* target_delay, + TimeDelta* jitter_buffer, + TimeDelta* min_playout_delay, + TimeDelta* render_delay) const override { return true; } - int GetCurrentJitter() { - int max_decode_ms; - int current_delay_ms; - int target_delay_ms; - int jitter_buffer_ms; - int min_playout_delay_ms; - int render_delay_ms; - VCMTiming::GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms, - &jitter_buffer_ms, &min_playout_delay_ms, - &render_delay_ms); - return jitter_buffer_ms; + TimeDelta GetCurrentJitter() { + TimeDelta max_decode = TimeDelta::Zero(); + TimeDelta current_delay = TimeDelta::Zero(); + TimeDelta target_delay = TimeDelta::Zero(); + TimeDelta jitter_buffer = TimeDelta::Zero(); + TimeDelta min_playout_delay = TimeDelta::Zero(); + TimeDelta render_delay = TimeDelta::Zero(); + VCMTiming::GetTimings(&max_decode, ¤t_delay, &target_delay, + &jitter_buffer, &min_playout_delay, &render_delay); + return jitter_buffer; } private: - static constexpr int kDelayMs = 50; - static constexpr int kDecodeTime = kDelayMs / 2; + static constexpr TimeDelta kDelay = TimeDelta::Millis(50); + const TimeDelta kDecodeTime = kDelay / 2; mutable uint32_t last_timestamp_ = 0; - mutable int64_t last_ms_ = -1; + mutable Timestamp last_render_time_ = Timestamp::MinusInfinity(); }; class FrameObjectFake : public EncodedFrame { @@ -118,12 +121,12 @@ class VCMReceiveStatisticsCallbackMock : public VCMReceiveStatisticsCallback { MOCK_METHOD(void, OnDroppedFrames, (uint32_t frames_dropped), (override)); MOCK_METHOD(void, OnFrameBufferTimingsUpdated, - (int max_decode_ms, - int current_delay_ms, - int target_delay_ms, - int jitter_buffer_ms, - int min_playout_delay_ms, - int render_delay_ms), + (int max_decode, + int current_delay, + int target_delay, + int jitter_buffer, + int min_playout_delay, + int render_delay), (override)); MOCK_METHOD(void, OnTimingFrameInfoUpdated, @@ -140,8 +143,7 @@ class TestFrameBuffer2 : public ::testing::Test { static constexpr size_t kFrameSize = 10; TestFrameBuffer2() - : trial_("WebRTC-AddRttToPlayoutDelay/Enabled/"), - time_controller_(Timestamp::Seconds(0)), + : time_controller_(Timestamp::Seconds(0)), time_task_queue_( time_controller_.GetTaskQueueFactory()->CreateTaskQueue( "extract queue", @@ -198,14 +200,10 @@ class TestFrameBuffer2 : public ::testing::Test { void ExtractFrame(int64_t max_wait_time = 0, bool keyframe_required = false) { time_task_queue_.PostTask([this, max_wait_time, keyframe_required]() { - buffer_->NextFrame( - max_wait_time, keyframe_required, &time_task_queue_, - [this](std::unique_ptr frame, - video_coding::FrameBuffer::ReturnReason reason) { - if (reason != FrameBuffer::ReturnReason::kStopped) { - frames_.emplace_back(std::move(frame)); - } - }); + buffer_->NextFrame(max_wait_time, keyframe_required, &time_task_queue_, + [this](std::unique_ptr frame) { + frames_.emplace_back(std::move(frame)); + }); }); if (max_wait_time == 0) { time_controller_.AdvanceTime(TimeDelta::Millis(0)); @@ -232,8 +230,6 @@ class TestFrameBuffer2 : public ::testing::Test { uint32_t Rand() { return rand_.Rand(); } - // The ProtectionMode tests depends on rtt-multiplier experiment. - test::ScopedFieldTrials trial_; webrtc::GlobalSimulatedTimeController time_controller_; rtc::TaskQueue time_task_queue_; VCMTimingFake timing_; @@ -262,6 +258,29 @@ TEST_F(TestFrameBuffer2, WaitForFrame) { CheckFrame(0, pid, 0); } +TEST_F(TestFrameBuffer2, ClearWhileWaitingForFrame) { + const uint16_t pid = Rand(); + + // Insert a frame and wait for it for max 100ms. + InsertFrame(pid, 0, 25, true, kFrameSize); + ExtractFrame(100); + // After 10ms, clear the buffer. + time_controller_.AdvanceTime(TimeDelta::Millis(10)); + buffer_->Clear(); + // Confirm that the frame was not sent for rendering. + time_controller_.AdvanceTime(TimeDelta::Millis(15)); + EXPECT_THAT(frames_, IsEmpty()); + + // We are still waiting for a frame, since 100ms has not passed. Insert a new + // frame. This new frame should be the one that is returned as the old frame + // was cleared. + const uint16_t new_pid = pid + 1; + InsertFrame(new_pid, 0, 50, true, kFrameSize); + time_controller_.AdvanceTime(TimeDelta::Millis(25)); + ASSERT_THAT(frames_, SizeIs(1)); + CheckFrame(0, new_pid, 0); +} + TEST_F(TestFrameBuffer2, OneSuperFrame) { uint16_t pid = Rand(); uint32_t ts = Rand(); @@ -456,29 +475,7 @@ TEST_F(TestFrameBuffer2, ProtectionModeNackFEC) { ExtractFrame(); ExtractFrame(); ASSERT_EQ(4u, frames_.size()); - EXPECT_LT(timing_.GetCurrentJitter(), kRttMs); -} - -TEST_F(TestFrameBuffer2, ProtectionModeNack) { - uint16_t pid = Rand(); - uint32_t ts = Rand(); - constexpr int64_t kRttMs = 200; - - buffer_->UpdateRtt(kRttMs); - - // Jitter estimate includes RTT (after 3 retransmitted packets) - buffer_->SetProtectionMode(kProtectionNack); - InsertNackedFrame(pid, ts); - InsertNackedFrame(pid + 1, ts + 100); - InsertNackedFrame(pid + 2, ts + 200); - InsertFrame(pid + 3, 0, ts + 300, true, kFrameSize); - ExtractFrame(); - ExtractFrame(); - ExtractFrame(); - ExtractFrame(); - ASSERT_EQ(4u, frames_.size()); - - EXPECT_GT(timing_.GetCurrentJitter(), kRttMs); + EXPECT_LT(timing_.GetCurrentJitter().ms(), kRttMs); } TEST_F(TestFrameBuffer2, NoContinuousFrame) { @@ -688,5 +685,20 @@ TEST_F(TestFrameBuffer2, HigherSpatialLayerNonDecodable) { CheckFrame(2, pid + 4, 1); } +TEST_F(TestFrameBuffer2, StopWhileWaitingForFrame) { + uint16_t pid = Rand(); + uint32_t ts = Rand(); + + InsertFrame(pid, 0, ts, true, kFrameSize); + ExtractFrame(10); + buffer_->Stop(); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); + EXPECT_THAT(frames_, IsEmpty()); + + // A new frame request should exit immediately and return no new frame. + ExtractFrame(0); + EXPECT_THAT(frames_, IsEmpty()); +} + } // namespace video_coding } // namespace webrtc diff --git a/modules/video_coding/frame_buffer3.cc b/modules/video_coding/frame_buffer3.cc new file mode 100644 index 0000000000..d02a99749a --- /dev/null +++ b/modules/video_coding/frame_buffer3.cc @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/frame_buffer3.h" + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/container/inlined_vector.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/sequence_number_util.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { +bool ValidReferences(const EncodedFrame& frame) { + // All references must point backwards, and duplicates are not allowed. + for (size_t i = 0; i < frame.num_references; ++i) { + if (frame.references[i] >= frame.Id()) + return false; + + for (size_t j = i + 1; j < frame.num_references; ++j) { + if (frame.references[i] == frame.references[j]) + return false; + } + } + + return true; +} + +// Since FrameBuffer::FrameInfo is private it can't be used in the function +// signature, hence the FrameIteratorT type. +template +rtc::ArrayView GetReferences(const FrameIteratorT& it) { + return {it->second.encoded_frame->references, + std::min(it->second.encoded_frame->num_references, + EncodedFrame::kMaxFrameReferences)}; +} + +template +int64_t GetFrameId(const FrameIteratorT& it) { + return it->first; +} + +template +int64_t GetTimestamp(const FrameIteratorT& it) { + return it->second.encoded_frame->Timestamp(); +} + +template +bool IsLastFrameInTemporalUnit(const FrameIteratorT& it) { + return it->second.encoded_frame->is_last_spatial_layer; +} +} // namespace + +FrameBuffer::FrameBuffer(int max_size, int max_decode_history) + : legacy_frame_id_jump_behavior_( + !field_trial::IsDisabled("WebRTC-LegacyFrameIdJumpBehavior")), + max_size_(max_size), + decoded_frame_history_(max_decode_history) {} + +void FrameBuffer::InsertFrame(std::unique_ptr frame) { + if (!ValidReferences(*frame)) { + RTC_DLOG(LS_WARNING) << "Frame " << frame->Id() + << " has invalid references, dropping frame."; + return; + } + + if (frame->Id() <= decoded_frame_history_.GetLastDecodedFrameId()) { + if (legacy_frame_id_jump_behavior_ && frame->is_keyframe() && + AheadOf(frame->Timestamp(), + *decoded_frame_history_.GetLastDecodedFrameTimestamp())) { + RTC_DLOG(LS_WARNING) + << "Keyframe " << frame->Id() + << " has newer timestamp but older picture id, clearing buffer."; + Clear(); + } else { + // Already decoded past this frame. + return; + } + } + + if (frames_.size() == max_size_) { + if (frame->is_keyframe()) { + RTC_DLOG(LS_WARNING) << "Keyframe " << frame->Id() + << " inserted into full buffer, clearing buffer."; + Clear(); + } else { + // No space for this frame. + return; + } + } + + const int64_t frame_id = frame->Id(); + auto insert_res = frames_.emplace(frame_id, FrameInfo{std::move(frame)}); + if (!insert_res.second) { + // Frame has already been inserted. + return; + } + + if (frames_.size() == max_size_) { + RTC_DLOG(LS_WARNING) << "Frame " << frame_id + << " inserted, buffer is now full."; + } + + PropagateContinuity(insert_res.first); + FindNextAndLastDecodableTemporalUnit(); +} + +absl::InlinedVector, 4> +FrameBuffer::ExtractNextDecodableTemporalUnit() { + absl::InlinedVector, 4> res; + if (!next_decodable_temporal_unit_) { + return res; + } + + auto end_it = std::next(next_decodable_temporal_unit_->last_frame); + for (auto it = next_decodable_temporal_unit_->first_frame; it != end_it; + ++it) { + decoded_frame_history_.InsertDecoded(GetFrameId(it), GetTimestamp(it)); + res.push_back(std::move(it->second.encoded_frame)); + } + + DropNextDecodableTemporalUnit(); + return res; +} + +void FrameBuffer::DropNextDecodableTemporalUnit() { + if (!next_decodable_temporal_unit_) { + return; + } + + auto end_it = std::next(next_decodable_temporal_unit_->last_frame); + num_dropped_frames_ += std::count_if( + frames_.begin(), end_it, + [](const auto& f) { return f.second.encoded_frame != nullptr; }); + + frames_.erase(frames_.begin(), end_it); + FindNextAndLastDecodableTemporalUnit(); +} + +absl::optional FrameBuffer::LastContinuousFrameId() const { + return last_continuous_frame_id_; +} + +absl::optional FrameBuffer::LastContinuousTemporalUnitFrameId() const { + return last_continuous_temporal_unit_frame_id_; +} + +absl::optional FrameBuffer::NextDecodableTemporalUnitRtpTimestamp() + const { + if (!next_decodable_temporal_unit_) { + return absl::nullopt; + } + return GetTimestamp(next_decodable_temporal_unit_->first_frame); +} + +absl::optional FrameBuffer::LastDecodableTemporalUnitRtpTimestamp() + const { + return last_decodable_temporal_unit_timestamp_; +} + +int FrameBuffer::GetTotalNumberOfContinuousTemporalUnits() const { + return num_continuous_temporal_units_; +} +int FrameBuffer::GetTotalNumberOfDroppedFrames() const { + return num_dropped_frames_; +} + +size_t FrameBuffer::CurrentSize() const { + return frames_.size(); +} + +bool FrameBuffer::IsContinuous(const FrameIterator& it) const { + for (int64_t reference : GetReferences(it)) { + if (decoded_frame_history_.WasDecoded(reference)) { + continue; + } + + auto reference_frame_it = frames_.find(reference); + if (reference_frame_it != frames_.end() && + reference_frame_it->second.continuous) { + continue; + } + + return false; + } + + return true; +} + +void FrameBuffer::PropagateContinuity(const FrameIterator& frame_it) { + for (auto it = frame_it; it != frames_.end(); ++it) { + if (!it->second.continuous) { + if (IsContinuous(it)) { + it->second.continuous = true; + if (last_continuous_frame_id_ < GetFrameId(it)) { + last_continuous_frame_id_ = GetFrameId(it); + } + if (IsLastFrameInTemporalUnit(it)) { + num_continuous_temporal_units_++; + if (last_continuous_temporal_unit_frame_id_ < GetFrameId(it)) { + last_continuous_temporal_unit_frame_id_ = GetFrameId(it); + } + } + } + } + } +} + +void FrameBuffer::FindNextAndLastDecodableTemporalUnit() { + next_decodable_temporal_unit_.reset(); + last_decodable_temporal_unit_timestamp_.reset(); + + if (!last_continuous_temporal_unit_frame_id_) { + return; + } + + FrameIterator first_frame_it = frames_.begin(); + FrameIterator last_frame_it = frames_.begin(); + absl::InlinedVector frames_in_temporal_unit; + for (auto frame_it = frames_.begin(); frame_it != frames_.end();) { + if (GetFrameId(frame_it) > *last_continuous_temporal_unit_frame_id_) { + break; + } + + if (GetTimestamp(frame_it) != GetTimestamp(first_frame_it)) { + frames_in_temporal_unit.clear(); + first_frame_it = frame_it; + } + + frames_in_temporal_unit.push_back(GetFrameId(frame_it)); + + last_frame_it = frame_it++; + + if (IsLastFrameInTemporalUnit(last_frame_it)) { + bool temporal_unit_decodable = true; + for (auto it = first_frame_it; it != frame_it && temporal_unit_decodable; + ++it) { + for (int64_t reference : GetReferences(it)) { + if (!decoded_frame_history_.WasDecoded(reference) && + !absl::c_linear_search(frames_in_temporal_unit, reference)) { + // A frame in the temporal unit has a non-decoded reference outside + // the temporal unit, so it's not yet ready to be decoded. + temporal_unit_decodable = false; + break; + } + } + } + + if (temporal_unit_decodable) { + if (!next_decodable_temporal_unit_) { + next_decodable_temporal_unit_ = {first_frame_it, last_frame_it}; + } + + last_decodable_temporal_unit_timestamp_ = GetTimestamp(first_frame_it); + } + } + } +} + +void FrameBuffer::Clear() { + frames_.clear(); + next_decodable_temporal_unit_.reset(); + last_decodable_temporal_unit_timestamp_.reset(); + last_continuous_frame_id_.reset(); + last_continuous_temporal_unit_frame_id_.reset(); + decoded_frame_history_.Clear(); +} + +} // namespace webrtc diff --git a/modules/video_coding/frame_buffer3.h b/modules/video_coding/frame_buffer3.h new file mode 100644 index 0000000000..1f3f71a4a5 --- /dev/null +++ b/modules/video_coding/frame_buffer3.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_VIDEO_CODING_FRAME_BUFFER3_H_ +#define MODULES_VIDEO_CODING_FRAME_BUFFER3_H_ + +#include +#include +#include + +#include "absl/container/inlined_vector.h" +#include "absl/types/optional.h" +#include "api/units/timestamp.h" +#include "api/video/encoded_frame.h" +#include "modules/video_coding/utility/decoded_frames_history.h" + +namespace webrtc { +// The high level idea of the FrameBuffer is to order frames received from the +// network into a decodable stream. Frames are order by frame ID, and grouped +// into temporal units by timestamp. A temporal unit is decodable after all +// referenced frames outside the unit has been decoded, and a temporal unit is +// continuous if all referenced frames are directly or indirectly decodable. +// The FrameBuffer is thread-unsafe. +class FrameBuffer { + public: + // The `max_size` determines the maxmimum number of frames the buffer will + // store, and max_decode_history determines how far back (by frame ID) the + // buffer will store if a frame was decoded or not. + FrameBuffer(int max_size, int max_decode_history); + FrameBuffer(const FrameBuffer&) = delete; + FrameBuffer& operator=(const FrameBuffer&) = delete; + ~FrameBuffer() = default; + + // Inserted frames may only reference backwards, and must have no duplicate + // references. + void InsertFrame(std::unique_ptr frame); + + // Mark all frames belonging to the next decodable temporal unit as decoded + // and returns them. + absl::InlinedVector, 4> + ExtractNextDecodableTemporalUnit(); + + // Drop all frames in the next decodable unit. + void DropNextDecodableTemporalUnit(); + + absl::optional LastContinuousFrameId() const; + absl::optional LastContinuousTemporalUnitFrameId() const; + absl::optional NextDecodableTemporalUnitRtpTimestamp() const; + absl::optional LastDecodableTemporalUnitRtpTimestamp() const; + + int GetTotalNumberOfContinuousTemporalUnits() const; + int GetTotalNumberOfDroppedFrames() const; + size_t CurrentSize() const; + + private: + struct FrameInfo { + std::unique_ptr encoded_frame; + bool continuous = false; + }; + + using FrameMap = std::map; + using FrameIterator = FrameMap::iterator; + + struct TemporalUnit { + // Both first and last are inclusive. + FrameIterator first_frame; + FrameIterator last_frame; + }; + + bool IsContinuous(const FrameIterator& it) const; + void PropagateContinuity(const FrameIterator& frame_it); + void FindNextAndLastDecodableTemporalUnit(); + void Clear(); + + const bool legacy_frame_id_jump_behavior_; + const size_t max_size_; + FrameMap frames_; + absl::optional next_decodable_temporal_unit_; + absl::optional last_decodable_temporal_unit_timestamp_; + absl::optional last_continuous_frame_id_; + absl::optional last_continuous_temporal_unit_frame_id_; + video_coding::DecodedFramesHistory decoded_frame_history_; + + int num_continuous_temporal_units_ = 0; + int num_dropped_frames_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_FRAME_BUFFER3_H_ diff --git a/modules/video_coding/frame_buffer3_unittest.cc b/modules/video_coding/frame_buffer3_unittest.cc new file mode 100644 index 0000000000..b70cd14d63 --- /dev/null +++ b/modules/video_coding/frame_buffer3_unittest.cc @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/video_coding/frame_buffer3.h" + +#include + +#include "api/video/encoded_frame.h" +#include "test/field_trial.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Matches; + +MATCHER_P(FrameWithId, id, "") { + return Matches(Eq(id))(arg->Id()); +} + +class FakeEncodedFrame : public EncodedFrame { + public: + int64_t ReceivedTime() const override { return 0; } + int64_t RenderTime() const override { return 0; } +}; + +class Builder { + public: + Builder& Time(uint32_t rtp_timestamp) { + rtp_timestamp_ = rtp_timestamp; + return *this; + } + Builder& Id(int64_t frame_id) { + frame_id_ = frame_id; + return *this; + } + Builder& AsLast() { + last_spatial_layer_ = true; + return *this; + } + Builder& Refs(const std::vector& references) { + references_ = references; + return *this; + } + + std::unique_ptr Build() { + RTC_CHECK_LE(references_.size(), EncodedFrame::kMaxFrameReferences); + RTC_CHECK(rtp_timestamp_.has_value()); + RTC_CHECK(frame_id_.has_value()); + + auto frame = std::make_unique(); + frame->SetTimestamp(*rtp_timestamp_); + frame->SetId(*frame_id_); + frame->is_last_spatial_layer = last_spatial_layer_; + + for (int64_t ref : references_) { + frame->references[frame->num_references] = ref; + frame->num_references++; + } + + return frame; + } + + private: + absl::optional rtp_timestamp_; + absl::optional frame_id_; + bool last_spatial_layer_ = false; + std::vector references_; +}; + +TEST(FrameBuffer3Test, RejectInvalidRefs) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + // Ref must be less than the id of this frame. + buffer.InsertFrame(Builder().Time(0).Id(0).Refs({0}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); + + // Duplicate ids are also invalid. + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1, 1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); +} + +TEST(FrameBuffer3Test, LastContinuousUpdatesOnInsertedFrames) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame(Builder().Time(10).Id(1).Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(2)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); +} + +TEST(FrameBuffer3Test, LastContinuousFrameReordering) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); + + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(3)); +} + +TEST(FrameBuffer3Test, LastContinuousTemporalUnit) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(10).Id(1).Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); +} + +TEST(FrameBuffer3Test, LastContinuousTemporalUnitReordering) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(10).Id(1).Build()); + buffer.InsertFrame(Builder().Time(20).Id(3).Refs({1}).Build()); + buffer.InsertFrame(Builder().Time(20).Id(4).Refs({2, 3}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(4)); +} + +TEST(FrameBuffer3Test, NextDecodable) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), + Eq(absl::nullopt)); + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); +} + +TEST(FrameBuffer3Test, AdvanceNextDecodableOnExtraction) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(2))); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(30U)); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, AdvanceLastDecodableOnExtraction) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(10U)); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(30U)); +} + +TEST(FrameBuffer3Test, FrameUpdatesNextDecodable) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); +} + +TEST(FrameBuffer3Test, KeyframeClearsFullBuffer) { + FrameBuffer buffer(/*max_frame_slots=*/5, /*max_decode_history=*/10); + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(40).Id(4).Refs({3}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(50).Id(5).Refs({4}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); + + // Frame buffer is full + buffer.InsertFrame(Builder().Time(60).Id(6).Refs({5}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); + + buffer.InsertFrame(Builder().Time(70).Id(7).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(7)); +} + +TEST(FrameBuffer3Test, DropNextDecodableTemporalUnit) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + buffer.DropNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, OldFramesAreIgnored) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + buffer.ExtractNextDecodableTemporalUnit(); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, ReturnFullTemporalUnitKSVC) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + buffer.InsertFrame(Builder().Time(10).Id(1).Build()); + buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).Build()); + buffer.InsertFrame(Builder().Time(10).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1), FrameWithId(2), FrameWithId(3))); + + buffer.InsertFrame(Builder().Time(20).Id(4).Refs({3}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(4))); +} + +TEST(FrameBuffer3Test, InterleavedStream) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(50).Id(5).Refs({3}).AsLast().Build()); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(2))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(4))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(5))); + + buffer.InsertFrame(Builder().Time(70).Id(7).Refs({5}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(7))); + buffer.InsertFrame(Builder().Time(60).Id(6).Refs({4}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + buffer.InsertFrame(Builder().Time(90).Id(9).Refs({7}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(9))); +} + +TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { + { + test::ScopedFieldTrials field_trial( + "WebRTC-LegacyFrameIdJumpBehavior/Disabled/"); + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + buffer.InsertFrame(Builder().Time(30).Id(2).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + } + + { + // WebRTC-LegacyFrameIdJumpBehavior is disabled by default. + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + + buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + buffer.InsertFrame(Builder().Time(30).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + buffer.InsertFrame(Builder().Time(40).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + } +} + +TEST(FrameBuffer3Test, TotalNumberOfContinuousTemporalUnits) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(0)); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).Build()); + buffer.InsertFrame(Builder().Time(40).Id(5).Refs({3, 4}).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + // Reordered + buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(3)); +} + +TEST(FrameBuffer3Test, TotalNumberOfDroppedFrames) { + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); + + buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); + buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); + buffer.InsertFrame(Builder().Time(40).Id(4).Refs({1}).Build()); + buffer.InsertFrame(Builder().Time(40).Id(5).Refs({4}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); + + buffer.DropNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); + + buffer.ExtractNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); +} + +} // namespace +} // namespace webrtc diff --git a/modules/video_coding/frame_helpers.cc b/modules/video_coding/frame_helpers.cc new file mode 100644 index 0000000000..08b47ef547 --- /dev/null +++ b/modules/video_coding/frame_helpers.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/frame_helpers.h" + +#include + +#include "rtc_base/logging.h" + +namespace webrtc { + +bool FrameHasBadRenderTiming(Timestamp render_time, + Timestamp now, + TimeDelta target_video_delay) { + // Zero render time means render immediately. + if (render_time.IsZero()) { + return false; + } + if (render_time < Timestamp::Zero()) { + return true; + } + constexpr TimeDelta kMaxVideoDelay = TimeDelta::Millis(10000); + TimeDelta frame_delay = (render_time - now).Abs(); + if (frame_delay > kMaxVideoDelay) { + RTC_LOG(LS_WARNING) + << "A frame about to be decoded is out of the configured " + "delay bounds (" + << frame_delay.ms() << " > " << kMaxVideoDelay.ms() + << "). Resetting the video jitter buffer."; + return true; + } + if (target_video_delay > kMaxVideoDelay) { + RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " + << kMaxVideoDelay.ms() << " ms."; + return true; + } + return false; +} + +std::unique_ptr CombineAndDeleteFrames( + absl::InlinedVector, 4> frames) { + RTC_DCHECK(!frames.empty()); + + if (frames.size() == 1) { + return std::move(frames[0]); + } + + size_t total_length = 0; + for (const auto& frame : frames) { + total_length += frame->size(); + } + const EncodedFrame& last_frame = *frames.back(); + std::unique_ptr first_frame = std::move(frames[0]); + auto encoded_image_buffer = EncodedImageBuffer::Create(total_length); + uint8_t* buffer = encoded_image_buffer->data(); + first_frame->SetSpatialLayerFrameSize(first_frame->SpatialIndex().value_or(0), + first_frame->size()); + memcpy(buffer, first_frame->data(), first_frame->size()); + buffer += first_frame->size(); + + // Spatial index of combined frame is set equal to spatial index of its top + // spatial layer. + first_frame->SetSpatialIndex(last_frame.SpatialIndex().value_or(0)); + + first_frame->video_timing_mutable()->network2_timestamp_ms = + last_frame.video_timing().network2_timestamp_ms; + first_frame->video_timing_mutable()->receive_finish_ms = + last_frame.video_timing().receive_finish_ms; + + // Append all remaining frames to the first one. + for (size_t i = 1; i < frames.size(); ++i) { + // Let |next_frame| fall out of scope so it is deleted after copying. + std::unique_ptr next_frame = std::move(frames[i]); + first_frame->SetSpatialLayerFrameSize( + next_frame->SpatialIndex().value_or(0), next_frame->size()); + memcpy(buffer, next_frame->data(), next_frame->size()); + buffer += next_frame->size(); + } + first_frame->SetEncodedData(encoded_image_buffer); + return first_frame; +} + +} // namespace webrtc diff --git a/modules/video_coding/frame_helpers.h b/modules/video_coding/frame_helpers.h new file mode 100644 index 0000000000..b6d7b0f144 --- /dev/null +++ b/modules/video_coding/frame_helpers.h @@ -0,0 +1,30 @@ +/* + * 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_VIDEO_CODING_FRAME_HELPERS_H_ +#define MODULES_VIDEO_CODING_FRAME_HELPERS_H_ + +#include + +#include "absl/container/inlined_vector.h" +#include "api/video/encoded_frame.h" + +namespace webrtc { + +bool FrameHasBadRenderTiming(Timestamp render_time, + Timestamp now, + TimeDelta target_video_delay); + +std::unique_ptr CombineAndDeleteFrames( + absl::InlinedVector, 4> frames); + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_FRAME_HELPERS_H_ diff --git a/modules/video_coding/generic_decoder.cc b/modules/video_coding/generic_decoder.cc index fe59d5c7f1..4b55f85b96 100644 --- a/modules/video_coding/generic_decoder.cc +++ b/modules/video_coding/generic_decoder.cc @@ -142,7 +142,7 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, const TimeDelta decode_time = decode_time_ms ? TimeDelta::Millis(*decode_time_ms) : now - *frameInfo->decodeStart; - _timing->StopDecodeTimer(decode_time.ms(), now.ms()); + _timing->StopDecodeTimer(decode_time, now); decodedImage.set_processing_time( {*frameInfo->decodeStart, *frameInfo->decodeStart + decode_time}); diff --git a/modules/video_coding/h264_packet_buffer.cc b/modules/video_coding/h264_packet_buffer.cc new file mode 100644 index 0000000000..6096665bda --- /dev/null +++ b/modules/video_coding/h264_packet_buffer.cc @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/h264_packet_buffer.h" + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/rtp_packet_info.h" +#include "api/video/video_frame_type.h" +#include "common_video/h264/h264_common.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/rtp_video_header.h" +#include "modules/video_coding/codecs/h264/include/h264_globals.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/sequence_number_util.h" + +namespace webrtc { +namespace { +int64_t EuclideanMod(int64_t n, int64_t div) { + RTC_DCHECK_GT(div, 0); + return (n %= div) < 0 ? n + div : n; +} + +rtc::ArrayView GetNaluInfos( + const RTPVideoHeaderH264& h264_header) { + if (h264_header.nalus_length > kMaxNalusPerPacket) { + return {}; + } + + return rtc::MakeArrayView(h264_header.nalus, h264_header.nalus_length); +} + +bool IsFirstPacketOfFragment(const RTPVideoHeaderH264& h264_header) { + return h264_header.nalus_length > 0; +} + +bool BeginningOfIdr(const H264PacketBuffer::Packet& packet) { + const auto& h264_header = + absl::get(packet.video_header.video_type_header); + const bool contains_idr_nalu = + absl::c_any_of(GetNaluInfos(h264_header), [](const auto& nalu_info) { + return nalu_info.type == H264::NaluType::kIdr; + }); + switch (h264_header.packetization_type) { + case kH264StapA: + case kH264SingleNalu: { + return contains_idr_nalu; + } + case kH264FuA: { + return contains_idr_nalu && IsFirstPacketOfFragment(h264_header); + } + } +} + +bool HasSps(const H264PacketBuffer::Packet& packet) { + auto& h264_header = + absl::get(packet.video_header.video_type_header); + return absl::c_any_of(GetNaluInfos(h264_header), [](const auto& nalu_info) { + return nalu_info.type == H264::NaluType::kSps; + }); +} + +// TODO(bugs.webrtc.org/13157): Update the H264 depacketizer so we don't have to +// fiddle with the payload at this point. +rtc::CopyOnWriteBuffer FixVideoPayload(rtc::ArrayView payload, + const RTPVideoHeader& video_header) { + constexpr uint8_t kStartCode[] = {0, 0, 0, 1}; + + const auto& h264_header = + absl::get(video_header.video_type_header); + + rtc::CopyOnWriteBuffer result; + switch (h264_header.packetization_type) { + case kH264StapA: { + const uint8_t* payload_end = payload.data() + payload.size(); + const uint8_t* nalu_ptr = payload.data() + 1; + while (nalu_ptr < payload_end - 1) { + // The first two bytes describe the length of the segment, where a + // segment is the nalu type plus nalu payload. + uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1]; + nalu_ptr += 2; + + if (nalu_ptr + segment_length <= payload_end) { + result.AppendData(kStartCode); + result.AppendData(nalu_ptr, segment_length); + } + nalu_ptr += segment_length; + } + return result; + } + + case kH264FuA: { + if (IsFirstPacketOfFragment(h264_header)) { + result.AppendData(kStartCode); + } + result.AppendData(payload); + return result; + } + + case kH264SingleNalu: { + result.AppendData(kStartCode); + result.AppendData(payload); + return result; + } + } + + RTC_DCHECK_NOTREACHED(); + return result; +} + +} // namespace + +H264PacketBuffer::H264PacketBuffer(bool idr_only_keyframes_allowed) + : idr_only_keyframes_allowed_(idr_only_keyframes_allowed) {} + +H264PacketBuffer::InsertResult H264PacketBuffer::InsertPacket( + std::unique_ptr packet) { + RTC_DCHECK(packet->video_header.codec == kVideoCodecH264); + + InsertResult result; + if (!absl::holds_alternative( + packet->video_header.video_type_header)) { + return result; + } + + int64_t unwrapped_seq_num = seq_num_unwrapper_.Unwrap(packet->seq_num); + auto& packet_slot = GetPacket(unwrapped_seq_num); + if (packet_slot != nullptr && + AheadOrAt(packet_slot->timestamp, packet->timestamp)) { + // The incoming `packet` is old or a duplicate. + return result; + } else { + packet_slot = std::move(packet); + } + + result.packets = FindFrames(unwrapped_seq_num); + return result; +} + +std::unique_ptr& H264PacketBuffer::GetPacket( + int64_t unwrapped_seq_num) { + return buffer_[EuclideanMod(unwrapped_seq_num, kBufferSize)]; +} + +bool H264PacketBuffer::BeginningOfStream( + const H264PacketBuffer::Packet& packet) const { + return HasSps(packet) || + (idr_only_keyframes_allowed_ && BeginningOfIdr(packet)); +} + +std::vector> +H264PacketBuffer::FindFrames(int64_t unwrapped_seq_num) { + std::vector> found_frames; + + Packet* packet = GetPacket(unwrapped_seq_num).get(); + RTC_CHECK(packet != nullptr); + + // Check if the packet is continuous or the beginning of a new coded video + // sequence. + if (unwrapped_seq_num - 1 != last_continuous_unwrapped_seq_num_) { + if (unwrapped_seq_num <= last_continuous_unwrapped_seq_num_ || + !BeginningOfStream(*packet)) { + return found_frames; + } + + last_continuous_unwrapped_seq_num_ = unwrapped_seq_num; + } + + for (int64_t seq_num = unwrapped_seq_num; + seq_num < unwrapped_seq_num + kBufferSize;) { + RTC_DCHECK_GE(seq_num, *last_continuous_unwrapped_seq_num_); + + // Packets that were never assembled into a completed frame will stay in + // the 'buffer_'. Check that the `packet` sequence number match the expected + // unwrapped sequence number. + if (static_cast(seq_num) != packet->seq_num) { + return found_frames; + } + + last_continuous_unwrapped_seq_num_ = seq_num; + // Last packet of the frame, try to assemble the frame. + if (packet->marker_bit) { + uint32_t rtp_timestamp = packet->timestamp; + + // Iterate backwards to find where the frame starts. + for (int64_t seq_num_start = seq_num; + seq_num_start > seq_num - kBufferSize; --seq_num_start) { + auto& prev_packet = GetPacket(seq_num_start - 1); + + if (prev_packet == nullptr || prev_packet->timestamp != rtp_timestamp) { + if (MaybeAssembleFrame(seq_num_start, seq_num, found_frames)) { + // Frame was assembled, continue to look for more frames. + break; + } else { + // Frame was not assembled, no subsequent frame will be continuous. + return found_frames; + } + } + } + } + + seq_num++; + packet = GetPacket(seq_num).get(); + if (packet == nullptr) { + return found_frames; + } + } + + return found_frames; +} + +bool H264PacketBuffer::MaybeAssembleFrame( + int64_t start_seq_num_unwrapped, + int64_t end_sequence_number_unwrapped, + std::vector>& frames) { + bool has_sps = false; + bool has_pps = false; + bool has_idr = false; + + int width = -1; + int height = -1; + + for (int64_t seq_num = start_seq_num_unwrapped; + seq_num <= end_sequence_number_unwrapped; ++seq_num) { + const auto& packet = GetPacket(seq_num); + const auto& h264_header = + absl::get(packet->video_header.video_type_header); + for (const auto& nalu : GetNaluInfos(h264_header)) { + has_idr |= nalu.type == H264::NaluType::kIdr; + has_sps |= nalu.type == H264::NaluType::kSps; + has_pps |= nalu.type == H264::NaluType::kPps; + } + + width = std::max(packet->video_header.width, width); + height = std::max(packet->video_header.height, height); + } + + if (has_idr) { + if (!idr_only_keyframes_allowed_ && (!has_sps || !has_pps)) { + return false; + } + } + + for (int64_t seq_num = start_seq_num_unwrapped; + seq_num <= end_sequence_number_unwrapped; ++seq_num) { + auto& packet = GetPacket(seq_num); + + packet->video_header.is_first_packet_in_frame = + (seq_num == start_seq_num_unwrapped); + packet->video_header.is_last_packet_in_frame = + (seq_num == end_sequence_number_unwrapped); + + if (packet->video_header.is_first_packet_in_frame) { + if (width > 0 && height > 0) { + packet->video_header.width = width; + packet->video_header.height = height; + } + + packet->video_header.frame_type = has_idr + ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta; + } + + packet->video_payload = + FixVideoPayload(packet->video_payload, packet->video_header); + + frames.push_back(std::move(packet)); + } + + return true; +} + +} // namespace webrtc diff --git a/modules/video_coding/h264_packet_buffer.h b/modules/video_coding/h264_packet_buffer.h new file mode 100644 index 0000000000..1671fddb23 --- /dev/null +++ b/modules/video_coding/h264_packet_buffer.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 MODULES_VIDEO_CODING_H264_PACKET_BUFFER_H_ +#define MODULES_VIDEO_CODING_H264_PACKET_BUFFER_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/types/optional.h" +#include "modules/video_coding/packet_buffer.h" +#include "rtc_base/numerics/sequence_number_util.h" + +namespace webrtc { + +class H264PacketBuffer { + public: + // The H264PacketBuffer does the same job as the PacketBuffer but for H264 + // only. To make it fit in with surronding code the PacketBuffer input/output + // classes are used. + using Packet = video_coding::PacketBuffer::Packet; + using InsertResult = video_coding::PacketBuffer::InsertResult; + + explicit H264PacketBuffer(bool idr_only_keyframes_allowed); + + ABSL_MUST_USE_RESULT InsertResult + InsertPacket(std::unique_ptr packet); + + private: + static constexpr int kBufferSize = 2048; + + std::unique_ptr& GetPacket(int64_t unwrapped_seq_num); + bool BeginningOfStream(const Packet& packet) const; + std::vector> FindFrames(int64_t unwrapped_seq_num); + bool MaybeAssembleFrame(int64_t start_seq_num_unwrapped, + int64_t end_sequence_number_unwrapped, + std::vector>& packets); + + const bool idr_only_keyframes_allowed_; + std::array, kBufferSize> buffer_; + absl::optional last_continuous_unwrapped_seq_num_; + SeqNumUnwrapper seq_num_unwrapper_; +}; + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_H264_PACKET_BUFFER_H_ diff --git a/modules/video_coding/h264_packet_buffer_unittest.cc b/modules/video_coding/h264_packet_buffer_unittest.cc new file mode 100644 index 0000000000..4f2331da28 --- /dev/null +++ b/modules/video_coding/h264_packet_buffer_unittest.cc @@ -0,0 +1,778 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/video_coding/h264_packet_buffer.h" + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/video/render_resolution.h" +#include "common_video/h264/h264_common.h" +#include "rtc_base/system/unused.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +using H264::NaluType::kAud; +using H264::NaluType::kFuA; +using H264::NaluType::kIdr; +using H264::NaluType::kPps; +using H264::NaluType::kSlice; +using H264::NaluType::kSps; +using H264::NaluType::kStapA; + +constexpr int kBufferSize = 2048; + +std::vector StartCode() { + return {0, 0, 0, 1}; +} + +NaluInfo MakeNaluInfo(uint8_t type) { + NaluInfo res; + res.type = type; + res.sps_id = -1; + res.pps_id = -1; + return res; +} + +class Packet { + public: + explicit Packet(H264PacketizationTypes type); + + Packet& Idr(std::vector payload = {9, 9, 9}); + Packet& Slice(std::vector payload = {9, 9, 9}); + Packet& Sps(std::vector payload = {9, 9, 9}); + Packet& SpsWithResolution(RenderResolution resolution, + std::vector payload = {9, 9, 9}); + Packet& Pps(std::vector payload = {9, 9, 9}); + Packet& Aud(); + Packet& Marker(); + Packet& AsFirstFragment(); + Packet& Time(uint32_t rtp_timestamp); + Packet& SeqNum(uint16_t rtp_seq_num); + + std::unique_ptr Build(); + + private: + rtc::CopyOnWriteBuffer BuildFuaPayload() const; + rtc::CopyOnWriteBuffer BuildSingleNaluPayload() const; + rtc::CopyOnWriteBuffer BuildStapAPayload() const; + + RTPVideoHeaderH264& H264Header() { + return absl::get(video_header_.video_type_header); + } + const RTPVideoHeaderH264& H264Header() const { + return absl::get(video_header_.video_type_header); + } + + H264PacketizationTypes type_; + RTPVideoHeader video_header_; + bool first_fragment_ = false; + bool marker_bit_ = false; + uint32_t rtp_timestamp_ = 0; + uint16_t rtp_seq_num_ = 0; + std::vector> nalu_payloads_; +}; + +Packet::Packet(H264PacketizationTypes type) : type_(type) { + video_header_.video_type_header.emplace(); +} + +Packet& Packet::Idr(std::vector payload) { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kIdr); + nalu_payloads_.push_back(std::move(payload)); + return *this; +} + +Packet& Packet::Slice(std::vector payload) { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSlice); + nalu_payloads_.push_back(std::move(payload)); + return *this; +} + +Packet& Packet::Sps(std::vector payload) { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSps); + nalu_payloads_.push_back(std::move(payload)); + return *this; +} + +Packet& Packet::SpsWithResolution(RenderResolution resolution, + std::vector payload) { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSps); + video_header_.width = resolution.Width(); + video_header_.height = resolution.Height(); + nalu_payloads_.push_back(std::move(payload)); + return *this; +} + +Packet& Packet::Pps(std::vector payload) { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kPps); + nalu_payloads_.push_back(std::move(payload)); + return *this; +} + +Packet& Packet::Aud() { + auto& h264_header = H264Header(); + h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kAud); + nalu_payloads_.push_back({}); + return *this; +} + +Packet& Packet::Marker() { + marker_bit_ = true; + return *this; +} + +Packet& Packet::AsFirstFragment() { + first_fragment_ = true; + return *this; +} + +Packet& Packet::Time(uint32_t rtp_timestamp) { + rtp_timestamp_ = rtp_timestamp; + return *this; +} + +Packet& Packet::SeqNum(uint16_t rtp_seq_num) { + rtp_seq_num_ = rtp_seq_num; + return *this; +} + +std::unique_ptr Packet::Build() { + auto res = std::make_unique(); + + auto& h264_header = H264Header(); + switch (type_) { + case kH264FuA: { + RTC_CHECK_EQ(h264_header.nalus_length, 1); + res->video_payload = BuildFuaPayload(); + break; + } + case kH264SingleNalu: { + RTC_CHECK_EQ(h264_header.nalus_length, 1); + res->video_payload = BuildSingleNaluPayload(); + break; + } + case kH264StapA: { + RTC_CHECK_GT(h264_header.nalus_length, 1); + RTC_CHECK_LE(h264_header.nalus_length, kMaxNalusPerPacket); + res->video_payload = BuildStapAPayload(); + break; + } + } + + if (type_ == kH264FuA && !first_fragment_) { + h264_header.nalus_length = 0; + } + + h264_header.packetization_type = type_; + res->marker_bit = marker_bit_; + res->video_header = video_header_; + res->timestamp = rtp_timestamp_; + res->seq_num = rtp_seq_num_; + res->video_header.codec = kVideoCodecH264; + + return res; +} + +rtc::CopyOnWriteBuffer Packet::BuildFuaPayload() const { + return rtc::CopyOnWriteBuffer(nalu_payloads_[0]); +} + +rtc::CopyOnWriteBuffer Packet::BuildSingleNaluPayload() const { + rtc::CopyOnWriteBuffer res; + auto& h264_header = H264Header(); + res.AppendData(&h264_header.nalus[0].type, 1); + res.AppendData(nalu_payloads_[0]); + return res; +} + +rtc::CopyOnWriteBuffer Packet::BuildStapAPayload() const { + rtc::CopyOnWriteBuffer res; + + const uint8_t indicator = H264::NaluType::kStapA; + res.AppendData(&indicator, 1); + + auto& h264_header = H264Header(); + for (size_t i = 0; i < h264_header.nalus_length; ++i) { + // The two first bytes indicates the nalu segment size. + uint8_t length_as_array[2] = { + 0, static_cast(nalu_payloads_[i].size() + 1)}; + res.AppendData(length_as_array); + + res.AppendData(&h264_header.nalus[i].type, 1); + res.AppendData(nalu_payloads_[i]); + } + return res; +} + +rtc::ArrayView PacketPayload( + const std::unique_ptr& packet) { + return packet->video_payload; +} + +std::vector FlatVector( + const std::vector>& elems) { + std::vector res; + for (const auto& elem : elems) { + res.insert(res.end(), elem.begin(), elem.end()); + } + return res; +} + +TEST(H264PacketBufferTest, IdrIsKeyframe) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/true); + + EXPECT_THAT( + packet_buffer.InsertPacket(Packet(kH264SingleNalu).Idr().Marker().Build()) + .packets, + SizeIs(1)); +} + +TEST(H264PacketBufferTest, IdrIsNotKeyframe) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT( + packet_buffer.InsertPacket(Packet(kH264SingleNalu).Idr().Marker().Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, IdrIsKeyframeFuaRequiresFirstFragmet) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/true); + + // Not marked as the first fragment + EXPECT_THAT( + packet_buffer + .InsertPacket(Packet(kH264FuA).Idr().SeqNum(0).Time(0).Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket( + Packet(kH264FuA).Idr().SeqNum(1).Time(0).Marker().Build()) + .packets, + IsEmpty()); + + // Marked as first fragment + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264FuA) + .Idr() + .SeqNum(2) + .Time(1) + .AsFirstFragment() + .Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket( + Packet(kH264FuA).Idr().SeqNum(3).Time(1).Marker().Build()) + .packets, + SizeIs(2)); +} + +TEST(H264PacketBufferTest, SpsPpsIdrIsKeyframeSingleNalus) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Sps().SeqNum(0).Time(0).Build())); + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Pps().SeqNum(1).Time(0).Build())); + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264SingleNalu).Idr().SeqNum(2).Time(0).Marker().Build()) + .packets, + SizeIs(3)); +} + +TEST(H264PacketBufferTest, PpsIdrIsNotKeyframeSingleNalus) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Pps().SeqNum(0).Time(0).Build())); + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264SingleNalu).Idr().SeqNum(1).Time(0).Marker().Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, SpsIdrIsNotKeyframeSingleNalus) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Sps().SeqNum(0).Time(0).Build())); + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264SingleNalu).Idr().SeqNum(1).Time(0).Marker().Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, SpsPpsIdrIsKeyframeStapA) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets, + SizeIs(1)); +} + +TEST(H264PacketBufferTest, PpsIdrIsNotKeyframeStapA) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264StapA).Pps().Idr().SeqNum(0).Time(0).Marker().Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, SpsIdrIsNotKeyframeStapA) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264StapA).Sps().Idr().SeqNum(2).Time(2).Marker().Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(3) + .Time(3) + .Marker() + .Build()) + .packets, + SizeIs(1)); +} + +TEST(H264PacketBufferTest, InsertingSpsPpsLastCompletesKeyframe) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Idr().SeqNum(2).Time(1).Marker().Build())); + + EXPECT_THAT(packet_buffer + .InsertPacket( + Packet(kH264StapA).Sps().Pps().SeqNum(1).Time(1).Build()) + .packets, + SizeIs(2)); +} + +TEST(H264PacketBufferTest, InsertingMidFuaCompletesFrame) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264FuA).Slice().SeqNum(1).Time(1).AsFirstFragment().Build())); + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264FuA).Slice().SeqNum(3).Time(1).Marker().Build())); + EXPECT_THAT( + packet_buffer + .InsertPacket(Packet(kH264FuA).Slice().SeqNum(2).Time(1).Build()) + .packets, + SizeIs(3)); +} + +TEST(H264PacketBufferTest, SeqNumJumpDoesNotCompleteFrame) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + EXPECT_THAT( + packet_buffer + .InsertPacket(Packet(kH264FuA).Slice().SeqNum(1).Time(1).Build()) + .packets, + IsEmpty()); + + // Add `kBufferSize` to make the index of the sequence number wrap and end up + // where the packet with sequence number 2 would have ended up. + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(2 + kBufferSize) + .Time(3) + .Marker() + .Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, OldFramesAreNotCompletedAfterBufferWrap) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264SingleNalu) + .Slice() + .SeqNum(1) + .Time(1) + .Marker() + .Build()) + .packets, + IsEmpty()); + + // New keyframe, preceedes packet with sequence number 1 in the buffer. + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(kBufferSize) + .Time(kBufferSize) + .Marker() + .Build()) + .packets, + SizeIs(1)); +} + +TEST(H264PacketBufferTest, OldPacketsDontBlockNewPackets) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(kBufferSize) + .Time(kBufferSize) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(kBufferSize + 1) + .Time(kBufferSize + 1) + .AsFirstFragment() + .Build())); + + RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(kBufferSize + 3) + .Time(kBufferSize + 1) + .Marker() + .Build())); + EXPECT_THAT( + packet_buffer + .InsertPacket(Packet(kH264FuA).Slice().SeqNum(2).Time(2).Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(kBufferSize + 2) + .Time(kBufferSize + 1) + .Build()) + .packets, + SizeIs(3)); +} + +TEST(H264PacketBufferTest, OldPacketDoesntCompleteFrame) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(kBufferSize) + .Time(kBufferSize) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(kBufferSize + 3) + .Time(kBufferSize + 1) + .Marker() + .Build()) + .packets, + IsEmpty()); + + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264FuA).Slice().SeqNum(2).Time(2).Marker().Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264FuA) + .Slice() + .SeqNum(kBufferSize + 1) + .Time(kBufferSize + 1) + .AsFirstFragment() + .Build()) + .packets, + IsEmpty()); +} + +TEST(H264PacketBufferTest, FrameBoundariesAreSet) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + auto key = packet_buffer.InsertPacket( + Packet(kH264StapA).Sps().Pps().Idr().SeqNum(1).Time(1).Marker().Build()); + + ASSERT_THAT(key.packets, SizeIs(1)); + EXPECT_TRUE(key.packets[0]->video_header.is_first_packet_in_frame); + EXPECT_TRUE(key.packets[0]->video_header.is_last_packet_in_frame); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264FuA).Slice().SeqNum(2).Time(2).Build())); + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264FuA).Slice().SeqNum(3).Time(2).Build())); + auto delta = packet_buffer.InsertPacket( + Packet(kH264FuA).Slice().SeqNum(4).Time(2).Marker().Build()); + + ASSERT_THAT(delta.packets, SizeIs(3)); + EXPECT_TRUE(delta.packets[0]->video_header.is_first_packet_in_frame); + EXPECT_FALSE(delta.packets[0]->video_header.is_last_packet_in_frame); + + EXPECT_FALSE(delta.packets[1]->video_header.is_first_packet_in_frame); + EXPECT_FALSE(delta.packets[1]->video_header.is_last_packet_in_frame); + + EXPECT_FALSE(delta.packets[2]->video_header.is_first_packet_in_frame); + EXPECT_TRUE(delta.packets[2]->video_header.is_last_packet_in_frame); +} + +TEST(H264PacketBufferTest, ResolutionSetOnFirstPacket) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Aud().SeqNum(1).Time(1).Build())); + auto res = packet_buffer.InsertPacket(Packet(kH264StapA) + .SpsWithResolution({320, 240}) + .Pps() + .Idr() + .SeqNum(2) + .Time(1) + .Marker() + .Build()); + + ASSERT_THAT(res.packets, SizeIs(2)); + EXPECT_THAT(res.packets[0]->video_header.width, Eq(320)); + EXPECT_THAT(res.packets[0]->video_header.height, Eq(240)); +} + +TEST(H264PacketBufferTest, KeyframeAndDeltaFrameSetOnFirstPacket) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Aud().SeqNum(1).Time(1).Build())); + auto key = packet_buffer.InsertPacket( + Packet(kH264StapA).Sps().Pps().Idr().SeqNum(2).Time(1).Marker().Build()); + + auto delta = packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Slice().SeqNum(3).Time(2).Marker().Build()); + + ASSERT_THAT(key.packets, SizeIs(2)); + EXPECT_THAT(key.packets[0]->video_header.frame_type, + Eq(VideoFrameType::kVideoFrameKey)); + ASSERT_THAT(delta.packets, SizeIs(1)); + EXPECT_THAT(delta.packets[0]->video_header.frame_type, + Eq(VideoFrameType::kVideoFrameDelta)); +} + +TEST(H264PacketBufferTest, RtpSeqNumWrap) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264StapA).Sps().Pps().SeqNum(0xffff).Time(0).Build())); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264FuA).Idr().SeqNum(0).Time(0).Build())); + EXPECT_THAT(packet_buffer + .InsertPacket( + Packet(kH264FuA).Idr().SeqNum(1).Time(0).Marker().Build()) + .packets, + SizeIs(3)); +} + +TEST(H264PacketBufferTest, StapAFixedBitstream) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + auto packets = packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps({1, 2, 3}) + .Pps({4, 5, 6}) + .Idr({7, 8, 9}) + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets; + + ASSERT_THAT(packets, SizeIs(1)); + EXPECT_THAT(PacketPayload(packets[0]), + ElementsAreArray(FlatVector({StartCode(), + {kSps, 1, 2, 3}, + StartCode(), + {kPps, 4, 5, 6}, + StartCode(), + {kIdr, 7, 8, 9}}))); +} + +TEST(H264PacketBufferTest, SingleNaluFixedBitstream) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Sps({1, 2, 3}).SeqNum(0).Time(0).Build())); + RTC_UNUSED(packet_buffer.InsertPacket( + Packet(kH264SingleNalu).Pps({4, 5, 6}).SeqNum(1).Time(0).Build())); + auto packets = packet_buffer + .InsertPacket(Packet(kH264SingleNalu) + .Idr({7, 8, 9}) + .SeqNum(2) + .Time(0) + .Marker() + .Build()) + .packets; + + ASSERT_THAT(packets, SizeIs(3)); + EXPECT_THAT(PacketPayload(packets[0]), + ElementsAreArray(FlatVector({StartCode(), {kSps, 1, 2, 3}}))); + EXPECT_THAT(PacketPayload(packets[1]), + ElementsAreArray(FlatVector({StartCode(), {kPps, 4, 5, 6}}))); + EXPECT_THAT(PacketPayload(packets[2]), + ElementsAreArray(FlatVector({StartCode(), {kIdr, 7, 8, 9}}))); +} + +TEST(H264PacketBufferTest, StapaAndFuaFixedBitstream) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264StapA) + .Sps({1, 2, 3}) + .Pps({4, 5, 6}) + .SeqNum(0) + .Time(0) + .Build())); + RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) + .Idr({8, 8, 8}) + .SeqNum(1) + .Time(0) + .AsFirstFragment() + .Build())); + auto packets = packet_buffer + .InsertPacket(Packet(kH264FuA) + .Idr({9, 9, 9}) + .SeqNum(2) + .Time(0) + .Marker() + .Build()) + .packets; + + ASSERT_THAT(packets, SizeIs(3)); + EXPECT_THAT( + PacketPayload(packets[0]), + ElementsAreArray(FlatVector( + {StartCode(), {kSps, 1, 2, 3}, StartCode(), {kPps, 4, 5, 6}}))); + EXPECT_THAT(PacketPayload(packets[1]), + ElementsAreArray(FlatVector({StartCode(), {8, 8, 8}}))); + // Third is a continuation of second, so only the payload is expected. + EXPECT_THAT(PacketPayload(packets[2]), + ElementsAreArray(FlatVector({{9, 9, 9}}))); +} + +TEST(H264PacketBufferTest, FullPacketBufferDoesNotBlockKeyframe) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + for (int i = 0; i < kBufferSize; ++i) { + EXPECT_THAT( + packet_buffer + .InsertPacket( + Packet(kH264SingleNalu).Slice().SeqNum(i).Time(0).Build()) + .packets, + IsEmpty()); + } + + EXPECT_THAT(packet_buffer + .InsertPacket(Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(kBufferSize) + .Time(1) + .Marker() + .Build()) + .packets, + SizeIs(1)); +} + +TEST(H264PacketBufferTest, TooManyNalusInPacket) { + H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); + + std::unique_ptr packet( + Packet(kH264StapA).Sps().Pps().Idr().SeqNum(1).Time(1).Marker().Build()); + auto& h264_header = + absl::get(packet->video_header.video_type_header); + h264_header.nalus_length = kMaxNalusPerPacket + 1; + + EXPECT_THAT(packet_buffer.InsertPacket(std::move(packet)).packets, IsEmpty()); +} + +} // namespace +} // namespace webrtc diff --git a/modules/video_coding/h264_sprop_parameter_sets.h b/modules/video_coding/h264_sprop_parameter_sets.h index dbf27ef034..8a32f31cc0 100644 --- a/modules/video_coding/h264_sprop_parameter_sets.h +++ b/modules/video_coding/h264_sprop_parameter_sets.h @@ -15,13 +15,15 @@ #include #include -#include "rtc_base/constructor_magic.h" - namespace webrtc { class H264SpropParameterSets { public: H264SpropParameterSets() {} + + H264SpropParameterSets(const H264SpropParameterSets&) = delete; + H264SpropParameterSets& operator=(const H264SpropParameterSets&) = delete; + bool DecodeSprop(const std::string& sprop); const std::vector& sps_nalu() { return sps_; } const std::vector& pps_nalu() { return pps_; } @@ -29,7 +31,6 @@ class H264SpropParameterSets { private: std::vector sps_; std::vector pps_; - RTC_DISALLOW_COPY_AND_ASSIGN(H264SpropParameterSets); }; } // namespace webrtc diff --git a/modules/video_coding/inter_frame_delay.cc b/modules/video_coding/inter_frame_delay.cc index d0c21aa771..8cdb6644ae 100644 --- a/modules/video_coding/inter_frame_delay.cc +++ b/modules/video_coding/inter_frame_delay.cc @@ -10,85 +10,62 @@ #include "modules/video_coding/inter_frame_delay.h" +#include "absl/types/optional.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { -VCMInterFrameDelay::VCMInterFrameDelay(int64_t currentWallClock) { - Reset(currentWallClock); +namespace { +constexpr Frequency k90kHz = Frequency::KiloHertz(90); +} + +VCMInterFrameDelay::VCMInterFrameDelay() { + Reset(); } // Resets the delay estimate. -void VCMInterFrameDelay::Reset(int64_t currentWallClock) { - _zeroWallClock = currentWallClock; - _wrapArounds = 0; - _prevWallClock = 0; - _prevTimestamp = 0; - _dTS = 0; +void VCMInterFrameDelay::Reset() { + prev_wall_clock_ = absl::nullopt; + prev_rtp_timestamp_unwrapped_ = 0; } // Calculates the delay of a frame with the given timestamp. // This method is called when the frame is complete. -bool VCMInterFrameDelay::CalculateDelay(uint32_t timestamp, - int64_t* delay, - int64_t currentWallClock) { - if (_prevWallClock == 0) { +absl::optional VCMInterFrameDelay::CalculateDelay( + uint32_t rtp_timestamp, + Timestamp now) { + int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp); + if (!prev_wall_clock_) { // First set of data, initialization, wait for next frame. - _prevWallClock = currentWallClock; - _prevTimestamp = timestamp; - *delay = 0; - return true; + prev_wall_clock_ = now; + prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped; + return TimeDelta::Zero(); } - int32_t prevWrapArounds = _wrapArounds; - CheckForWrapArounds(timestamp); - - // This will be -1 for backward wrap arounds and +1 for forward wrap arounds. - int32_t wrapAroundsSincePrev = _wrapArounds - prevWrapArounds; - // Account for reordering in jitter variance estimate in the future? // Note that this also captures incomplete frames which are grabbed for // decoding after a later frame has been complete, i.e. real packet losses. - if ((wrapAroundsSincePrev == 0 && timestamp < _prevTimestamp) || - wrapAroundsSincePrev < 0) { - *delay = 0; - return false; + uint32_t cropped_last = static_cast(prev_rtp_timestamp_unwrapped_); + if (rtp_timestamp_unwrapped < prev_rtp_timestamp_unwrapped_ || + !IsNewerTimestamp(rtp_timestamp, cropped_last)) { + return absl::nullopt; } - // Compute the compensated timestamp difference and convert it to ms and round - // it to closest integer. - _dTS = static_cast( - (timestamp + wrapAroundsSincePrev * (static_cast(1) << 32) - - _prevTimestamp) / - 90.0 + - 0.5); + // Compute the compensated timestamp difference. + int64_t d_rtp_ticks = rtp_timestamp_unwrapped - prev_rtp_timestamp_unwrapped_; + TimeDelta dts = d_rtp_ticks / k90kHz; + TimeDelta dt = now - *prev_wall_clock_; // frameDelay is the difference of dT and dTS -- i.e. the difference of the // wall clock time difference and the timestamp difference between two // following frames. - *delay = static_cast(currentWallClock - _prevWallClock - _dTS); + TimeDelta delay = dt - dts; - _prevTimestamp = timestamp; - _prevWallClock = currentWallClock; - - return true; + prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped; + prev_wall_clock_ = now; + return delay; } -// Investigates if the timestamp clock has overflowed since the last timestamp -// and keeps track of the number of wrap arounds since reset. -void VCMInterFrameDelay::CheckForWrapArounds(uint32_t timestamp) { - if (timestamp < _prevTimestamp) { - // This difference will probably be less than -2^31 if we have had a wrap - // around (e.g. timestamp = 1, _prevTimestamp = 2^32 - 1). Since it is cast - // to a int32_t, it should be positive. - if (static_cast(timestamp - _prevTimestamp) > 0) { - // Forward wrap around. - _wrapArounds++; - } - // This difference will probably be less than -2^31 if we have had a - // backward wrap around. Since it is cast to a int32_t, it should be - // positive. - } else if (static_cast(_prevTimestamp - timestamp) > 0) { - // Backward wrap around. - _wrapArounds--; - } -} } // namespace webrtc diff --git a/modules/video_coding/inter_frame_delay.h b/modules/video_coding/inter_frame_delay.h index f121c61498..e0fee6bfba 100644 --- a/modules/video_coding/inter_frame_delay.h +++ b/modules/video_coding/inter_frame_delay.h @@ -13,46 +13,32 @@ #include +#include "absl/types/optional.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { class VCMInterFrameDelay { public: - explicit VCMInterFrameDelay(int64_t currentWallClock); + VCMInterFrameDelay(); // Resets the estimate. Zeros are given as parameters. - void Reset(int64_t currentWallClock); + void Reset(); // Calculates the delay of a frame with the given timestamp. // This method is called when the frame is complete. - // - // Input: - // - timestamp : RTP timestamp of a received frame. - // - *delay : Pointer to memory where the result should be - // stored. - // - currentWallClock : The current time in milliseconds. - // Should be -1 for normal operation, only used - // for testing. - // Return value : true if OK, false when reordered timestamps. - bool CalculateDelay(uint32_t timestamp, - int64_t* delay, - int64_t currentWallClock); + absl::optional CalculateDelay(uint32_t rtp_timestamp, + Timestamp now); private: - // Controls if the RTP timestamp counter has had a wrap around between the - // current and the previously received frame. - // - // Input: - // - timestamp : RTP timestamp of the current frame. - void CheckForWrapArounds(uint32_t timestamp); + // The previous rtp timestamp passed to the delay estimate + int64_t prev_rtp_timestamp_unwrapped_; + TimestampUnwrapper unwrapper_; - int64_t _zeroWallClock; // Local timestamp of the first video packet received - int32_t _wrapArounds; // Number of wrapArounds detected - // The previous timestamp passed to the delay estimate - uint32_t _prevTimestamp; // The previous wall clock timestamp used by the delay estimate - int64_t _prevWallClock; - // Wrap-around compensated difference between incoming timestamps - int64_t _dTS; + absl::optional prev_wall_clock_; }; } // namespace webrtc diff --git a/modules/video_coding/inter_frame_delay_unittest.cc b/modules/video_coding/inter_frame_delay_unittest.cc new file mode 100644 index 0000000000..a338ba9d3b --- /dev/null +++ b/modules/video_coding/inter_frame_delay_unittest.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/inter_frame_delay.h" + +#include + +#include "absl/types/optional.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +// Test is for frames at 30fps. At 30fps, RTP timestamps will increase by +// 90000 / 30 = 3000 ticks per frame. +constexpr Frequency k30Fps = Frequency::Hertz(30); +constexpr TimeDelta kFrameDelay = 1 / k30Fps; +constexpr uint32_t kRtpTicksPerFrame = Frequency::KiloHertz(90) / k30Fps; +constexpr Timestamp kStartTime = Timestamp::Millis(1337); + +} // namespace + +using ::testing::Eq; +using ::testing::Optional; + +TEST(InterFrameDelayTest, OldRtpTimestamp) { + VCMInterFrameDelay inter_frame_delay; + EXPECT_THAT(inter_frame_delay.CalculateDelay(180000, kStartTime), + Optional(TimeDelta::Zero())); + EXPECT_THAT(inter_frame_delay.CalculateDelay(90000, kStartTime), + Eq(absl::nullopt)); +} + +TEST(InterFrameDelayTest, NegativeWrapAroundIsSameAsOldRtpTimestamp) { + VCMInterFrameDelay inter_frame_delay; + uint32_t rtp = 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime), + Optional(TimeDelta::Zero())); + // RTP has wrapped around backwards. + rtp -= 3000; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime), + Eq(absl::nullopt)); +} + +TEST(InterFrameDelayTest, CorrectDelayForFrames) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + + // First frame is always delay 0. + uint32_t rtp = 90000; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Perfectly timed frame has 0 delay. + clock.AdvanceTime(kFrameDelay); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Slightly early frame will have a negative delay. + clock.AdvanceTime(kFrameDelay - TimeDelta::Millis(3)); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-TimeDelta::Millis(3))); + + // Slightly late frame will have positive delay. + clock.AdvanceTime(kFrameDelay + TimeDelta::Micros(5125)); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Micros(5125))); + + // Simulate faster frame RTP at the same clock delay. The frame arrives late, + // since the RTP timestamp is faster than the delay, and thus is positive. + clock.AdvanceTime(kFrameDelay); + rtp += kRtpTicksPerFrame / 2; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(kFrameDelay / 2.0)); + + // Simulate slower frame RTP at the same clock delay. The frame is early, + // since the RTP timestamp advanced more than the delay, and thus is negative. + clock.AdvanceTime(kFrameDelay); + rtp += 1.5 * kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-kFrameDelay / 2.0)); +} + +TEST(InterFrameDelayTest, PositiveWrapAround) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + + // First frame is behind the max RTP by 1500. + uint32_t rtp = std::numeric_limits::max() - 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Rtp wraps around, now 1499. + rtp += kRtpTicksPerFrame; + + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay + TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Millis(1))); +} + +TEST(InterFrameDelayTest, MultipleWrapArounds) { + // Simulate a long pauses which cause wrap arounds multiple times. + constexpr Frequency k90Khz = Frequency::KiloHertz(90); + constexpr uint32_t kHalfRtp = std::numeric_limits::max() / 2; + constexpr TimeDelta kWrapAroundDelay = kHalfRtp / k90Khz; + + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + uint32_t rtp = 0; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + rtp += kHalfRtp; + clock.AdvanceTime(kWrapAroundDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + // 1st wrap around. + rtp += kHalfRtp + 1; + clock.AdvanceTime(kWrapAroundDelay + TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Millis(1) - (1 / k90Khz))); + + rtp += kHalfRtp; + clock.AdvanceTime(kWrapAroundDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + // 2nd wrap arounds. + rtp += kHalfRtp + 1; + clock.AdvanceTime(kWrapAroundDelay - TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-TimeDelta::Millis(1) - (1 / k90Khz))); + + // Ensure short delay (large RTP delay) between wrap-arounds has correct + // jitter. + rtp += kHalfRtp; + clock.AdvanceTime(TimeDelta::Millis(10)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-(kWrapAroundDelay - TimeDelta::Millis(10)))); + // 3nd wrap arounds, this time with large RTP delay. + rtp += kHalfRtp + 1; + clock.AdvanceTime(TimeDelta::Millis(10)); + EXPECT_THAT( + inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-(kWrapAroundDelay - TimeDelta::Millis(10) + (1 / k90Khz)))); +} + +TEST(InterFrameDelayTest, NegativeWrapAroundAfterPositiveWrapAround) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + uint32_t rtp = std::numeric_limits::max() - 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Rtp wraps around, now 1499. + rtp += kRtpTicksPerFrame; + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Wrap back. + rtp -= kRtpTicksPerFrame; + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Eq(absl::nullopt)); +} + +} // namespace webrtc diff --git a/modules/video_coding/jitter_buffer.cc b/modules/video_coding/jitter_buffer.cc index c3b096188c..f51b6ec898 100644 --- a/modules/video_coding/jitter_buffer.cc +++ b/modules/video_coding/jitter_buffer.cc @@ -9,11 +9,11 @@ */ #include "modules/video_coding/jitter_buffer.h" - #include #include #include +#include "api/units/timestamp.h" #include "modules/video_coding/frame_buffer.h" #include "modules/video_coding/include/video_coding.h" #include "modules/video_coding/inter_frame_delay.h" @@ -123,7 +123,6 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock, num_packets_(0), num_duplicated_packets_(0), jitter_estimate_(clock), - inter_frame_delay_(clock_->TimeInMilliseconds()), missing_sequence_numbers_(SequenceNumberLessThan()), latest_received_sequence_number_(0), max_nack_list_size_(0), @@ -192,7 +191,7 @@ void VCMJitterBuffer::Flush() { num_consecutive_old_packets_ = 0; // Also reset the jitter and delay estimates jitter_estimate_.Reset(); - inter_frame_delay_.Reset(clock_->TimeInMilliseconds()); + inter_frame_delay_.Reset(); waiting_for_completion_.frame_size = 0; waiting_for_completion_.timestamp = 0; waiting_for_completion_.latest_packet_time = -1; @@ -392,13 +391,13 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, if (error != kNoError) return error; - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); // We are keeping track of the first and latest seq numbers, and // the number of wraps to be able to calculate how many packets we expect. if (first_packet_since_reset_) { // Now it's time to start estimating jitter // reset the delay estimate. - inter_frame_delay_.Reset(now_ms); + inter_frame_delay_.Reset(); } // Empty packets may bias the jitter estimate (lacking size component), @@ -408,9 +407,9 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, // This can get bad if we have a lot of duplicate packets, // we will then count some packet multiple times. waiting_for_completion_.frame_size += packet.sizeBytes; - waiting_for_completion_.latest_packet_time = now_ms; + waiting_for_completion_.latest_packet_time = now.ms(); } else if (waiting_for_completion_.latest_packet_time >= 0 && - waiting_for_completion_.latest_packet_time + 2000 <= now_ms) { + waiting_for_completion_.latest_packet_time + 2000 <= now.ms()) { // A packet should never be more than two seconds late UpdateJitterEstimate(waiting_for_completion_, true); waiting_for_completion_.latest_packet_time = -1; @@ -425,7 +424,7 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, frame_data.rtt_ms = kDefaultRtt; frame_data.rolling_average_packets_per_frame = average_packets_per_frame_; VCMFrameBufferEnum buffer_state = - frame->InsertPacket(packet, now_ms, frame_data); + frame->InsertPacket(packet, now.ms(), frame_data); if (buffer_state > 0) { if (first_packet_since_reset_) { @@ -497,7 +496,7 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, RecycleFrameBuffer(frame); return kFlushIndicator; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return buffer_state; } @@ -572,7 +571,7 @@ void VCMJitterBuffer::FindAndInsertContinuousFramesWithState( uint32_t VCMJitterBuffer::EstimatedJitterMs() { MutexLock lock(&mutex_); const double rtt_mult = 1.0f; - return jitter_estimate_.GetJitterEstimate(rtt_mult, absl::nullopt); + return jitter_estimate_.GetJitterEstimate(rtt_mult, absl::nullopt).ms(); } void VCMJitterBuffer::SetNackSettings(size_t max_nack_list_size, @@ -873,13 +872,15 @@ void VCMJitterBuffer::UpdateJitterEstimate(int64_t latest_packet_time_ms, if (latest_packet_time_ms == -1) { return; } - int64_t frame_delay; - bool not_reordered = inter_frame_delay_.CalculateDelay( - timestamp, &frame_delay, latest_packet_time_ms); + auto frame_delay = inter_frame_delay_.CalculateDelay( + timestamp, Timestamp::Millis(latest_packet_time_ms)); + + bool not_reordered = frame_delay.has_value(); // Filter out frames which have been reordered in time by the network if (not_reordered) { // Update the jitter estimate with the new samples - jitter_estimate_.UpdateEstimate(frame_delay, frame_size, incomplete_frame); + jitter_estimate_.UpdateEstimate(*frame_delay, DataSize::Bytes(frame_size), + incomplete_frame); } } diff --git a/modules/video_coding/jitter_buffer.h b/modules/video_coding/jitter_buffer.h index 137a687ded..df7581a87e 100644 --- a/modules/video_coding/jitter_buffer.h +++ b/modules/video_coding/jitter_buffer.h @@ -19,7 +19,6 @@ #include "modules/include/module_common_types.h" #include "modules/include/module_common_types_public.h" -#include "modules/utility/include/process_thread.h" #include "modules/video_coding/decoding_state.h" #include "modules/video_coding/event_wrapper.h" #include "modules/video_coding/include/video_coding.h" @@ -27,7 +26,6 @@ #include "modules/video_coding/inter_frame_delay.h" #include "modules/video_coding/jitter_buffer_common.h" #include "modules/video_coding/jitter_estimator.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -75,6 +73,9 @@ class VCMJitterBuffer { ~VCMJitterBuffer(); + VCMJitterBuffer(const VCMJitterBuffer&) = delete; + VCMJitterBuffer& operator=(const VCMJitterBuffer&) = delete; + // Initializes and starts jitter buffer. void Start(); @@ -265,8 +266,6 @@ class VCMJitterBuffer { // average_packets_per_frame converges fast if we have fewer than this many // frames. int frame_counter_; - - RTC_DISALLOW_COPY_AND_ASSIGN(VCMJitterBuffer); }; } // namespace webrtc diff --git a/modules/video_coding/jitter_estimator.cc b/modules/video_coding/jitter_estimator.cc index 89cf51fe46..5ecd545e07 100644 --- a/modules/video_coding/jitter_estimator.cc +++ b/modules/video_coding/jitter_estimator.cc @@ -17,7 +17,10 @@ #include #include "absl/types/optional.h" -#include "modules/video_coding/internal_defines.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/rtt_filter.h" #include "rtc_base/experiments/jitter_upper_bound_experiment.h" #include "rtc_base/numerics/safe_conversions.h" @@ -28,24 +31,26 @@ namespace webrtc { namespace { static constexpr uint32_t kStartupDelaySamples = 30; static constexpr int64_t kFsAccuStartupSamples = 5; -static constexpr double kMaxFramerateEstimate = 200.0; -static constexpr int64_t kNackCountTimeoutMs = 60000; +static constexpr Frequency kMaxFramerateEstimate = Frequency::Hertz(200); +static constexpr TimeDelta kNackCountTimeout = TimeDelta::Seconds(60); static constexpr double kDefaultMaxTimestampDeviationInSigmas = 3.5; + +constexpr double kPhi = 0.97; +constexpr double kPsi = 0.9999; +constexpr uint32_t kAlphaCountMax = 400; +constexpr double kThetaLow = 0.000001; +constexpr uint32_t kNackLimit = 3; +constexpr int32_t kNumStdDevDelayOutlier = 15; +constexpr int32_t kNumStdDevFrameSizeOutlier = 3; +// ~Less than 1% chance (look up in normal distribution table)... +constexpr double kNoiseStdDevs = 2.33; +// ...of getting 30 ms freezes +constexpr double kNoiseStdDevOffset = 30.0; + } // namespace VCMJitterEstimator::VCMJitterEstimator(Clock* clock) - : _phi(0.97), - _psi(0.9999), - _alphaCountMax(400), - _thetaLow(0.000001), - _nackLimit(3), - _numStdDevDelayOutlier(15), - _numStdDevFrameSizeOutlier(3), - _noiseStdDevs(2.33), // ~Less than 1% chance - // (look up in normal distribution table)... - _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes - _rttFilter(), - fps_counter_(30), // TODO(sprang): Use an estimator with limit based on + : fps_counter_(30), // TODO(sprang): Use an estimator with limit based on // time, rather than number of samples. time_deviation_upper_bound_( JitterUpperBoundExperiment::GetUpperBoundSigmas().value_or( @@ -56,159 +61,133 @@ VCMJitterEstimator::VCMJitterEstimator(Clock* clock) Reset(); } -VCMJitterEstimator::~VCMJitterEstimator() {} - -VCMJitterEstimator& VCMJitterEstimator::operator=( - const VCMJitterEstimator& rhs) { - if (this != &rhs) { - memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov)); - memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov)); - - _avgFrameSize = rhs._avgFrameSize; - _varFrameSize = rhs._varFrameSize; - _maxFrameSize = rhs._maxFrameSize; - _fsSum = rhs._fsSum; - _fsCount = rhs._fsCount; - _lastUpdateT = rhs._lastUpdateT; - _prevEstimate = rhs._prevEstimate; - _prevFrameSize = rhs._prevFrameSize; - _avgNoise = rhs._avgNoise; - _alphaCount = rhs._alphaCount; - _filterJitterEstimate = rhs._filterJitterEstimate; - _startupCount = rhs._startupCount; - _latestNackTimestamp = rhs._latestNackTimestamp; - _nackCount = rhs._nackCount; - _rttFilter = rhs._rttFilter; - clock_ = rhs.clock_; - } - return *this; -} +VCMJitterEstimator::~VCMJitterEstimator() = default; // Resets the JitterEstimate. void VCMJitterEstimator::Reset() { - _theta[0] = 1 / (512e3 / 8); - _theta[1] = 0; - _varNoise = 4.0; + theta_[0] = 1 / (512e3 / 8); + theta_[1] = 0; + var_noise_ = 4.0; - _thetaCov[0][0] = 1e-4; - _thetaCov[1][1] = 1e2; - _thetaCov[0][1] = _thetaCov[1][0] = 0; - _Qcov[0][0] = 2.5e-10; - _Qcov[1][1] = 1e-10; - _Qcov[0][1] = _Qcov[1][0] = 0; - _avgFrameSize = 500; - _maxFrameSize = 500; - _varFrameSize = 100; - _lastUpdateT = -1; - _prevEstimate = -1.0; - _prevFrameSize = 0; - _avgNoise = 0.0; - _alphaCount = 1; - _filterJitterEstimate = 0.0; - _latestNackTimestamp = 0; - _nackCount = 0; - _latestNackTimestamp = 0; - _fsSum = 0; - _fsCount = 0; - _startupCount = 0; - _rttFilter.Reset(); + theta_cov_[0][0] = 1e-4; + theta_cov_[1][1] = 1e2; + theta_cov_[0][1] = theta_cov_[1][0] = 0; + q_cov_[0][0] = 2.5e-10; + q_cov_[1][1] = 1e-10; + q_cov_[0][1] = q_cov_[1][0] = 0; + avg_frame_size_ = kDefaultAvgAndMaxFrameSize; + max_frame_size_ = kDefaultAvgAndMaxFrameSize; + var_frame_size_ = 100; + last_update_time_ = absl::nullopt; + prev_estimate_ = absl::nullopt; + prev_frame_size_ = absl::nullopt; + avg_noise_ = 0.0; + alpha_count_ = 1; + filter_jitter_estimate_ = TimeDelta::Zero(); + latest_nack_ = Timestamp::Zero(); + nack_count_ = 0; + frame_size_sum_ = DataSize::Zero(); + frame_size_count_ = 0; + startup_count_ = 0; + rtt_filter_.Reset(); fps_counter_.Reset(); } // Updates the estimates with the new measurements. -void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, - uint32_t frameSizeBytes, - bool incompleteFrame /* = false */) { - if (frameSizeBytes == 0) { +void VCMJitterEstimator::UpdateEstimate(TimeDelta frame_delay, + DataSize frame_size, + bool incomplete_frame /* = false */) { + if (frame_size.IsZero()) { return; } - int deltaFS = frameSizeBytes - _prevFrameSize; - if (_fsCount < kFsAccuStartupSamples) { - _fsSum += frameSizeBytes; - _fsCount++; - } else if (_fsCount == kFsAccuStartupSamples) { + // Can't use DataSize since this can be negative. + double delta_frame_bytes = + frame_size.bytes() - prev_frame_size_.value_or(DataSize::Zero()).bytes(); + if (frame_size_count_ < kFsAccuStartupSamples) { + frame_size_sum_ += frame_size; + frame_size_count_++; + } else if (frame_size_count_ == kFsAccuStartupSamples) { // Give the frame size filter. - _avgFrameSize = static_cast(_fsSum) / static_cast(_fsCount); - _fsCount++; + avg_frame_size_ = frame_size_sum_ / static_cast(frame_size_count_); + frame_size_count_++; } - if (!incompleteFrame || frameSizeBytes > _avgFrameSize) { - double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes; - if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) { + if (!incomplete_frame || frame_size > avg_frame_size_) { + DataSize avg_frame_size = kPhi * avg_frame_size_ + (1 - kPhi) * frame_size; + DataSize deviation_size = DataSize::Bytes(2 * sqrt(var_frame_size_)); + if (frame_size < avg_frame_size_ + deviation_size) { // Only update the average frame size if this sample wasn't a key frame. - _avgFrameSize = avgFrameSize; + avg_frame_size_ = avg_frame_size; } // Update the variance anyway since we want to capture cases where we only // get key frames. - _varFrameSize = VCM_MAX( - _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) * - (frameSizeBytes - avgFrameSize), - 1.0); + double delta_bytes = frame_size.bytes() - avg_frame_size.bytes(); + var_frame_size_ = std::max( + kPhi * var_frame_size_ + (1 - kPhi) * (delta_bytes * delta_bytes), 1.0); } - // Update max frameSize estimate. - _maxFrameSize = - VCM_MAX(_psi * _maxFrameSize, static_cast(frameSizeBytes)); + // Update max_frame_size_ estimate. + max_frame_size_ = std::max(kPsi * max_frame_size_, frame_size); - if (_prevFrameSize == 0) { - _prevFrameSize = frameSizeBytes; + if (!prev_frame_size_) { + prev_frame_size_ = frame_size; return; } - _prevFrameSize = frameSizeBytes; + prev_frame_size_ = frame_size; - // Cap frameDelayMS based on the current time deviation noise. - int64_t max_time_deviation_ms = - static_cast(time_deviation_upper_bound_ * sqrt(_varNoise) + 0.5); - frameDelayMS = std::max(std::min(frameDelayMS, max_time_deviation_ms), - -max_time_deviation_ms); + // Cap frame_delay based on the current time deviation noise. + TimeDelta max_time_deviation = + TimeDelta::Millis(time_deviation_upper_bound_ * sqrt(var_noise_) + 0.5); + frame_delay.Clamp(-max_time_deviation, max_time_deviation); // Only update the Kalman filter if the sample is not considered an extreme // outlier. Even if it is an extreme outlier from a delay point of view, if // the frame size also is large the deviation is probably due to an incorrect // line slope. - double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS); + double deviation = DeviationFromExpectedDelay(frame_delay, delta_frame_bytes); - if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) || - frameSizeBytes > - _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) { + if (fabs(deviation) < kNumStdDevDelayOutlier * sqrt(var_noise_) || + frame_size.bytes() > + avg_frame_size_.bytes() + + kNumStdDevFrameSizeOutlier * sqrt(var_frame_size_)) { // Update the variance of the deviation from the line given by the Kalman // filter. - EstimateRandomJitter(deviation, incompleteFrame); + EstimateRandomJitter(deviation, incomplete_frame); // Prevent updating with frames which have been congested by a large frame, // and therefore arrives almost at the same time as that frame. // This can occur when we receive a large frame (key frame) which has been // delayed. The next frame is of normal size (delta frame), and thus deltaFS // will be << 0. This removes all frame samples which arrives after a key // frame. - if ((!incompleteFrame || deviation >= 0.0) && - static_cast(deltaFS) > -0.25 * _maxFrameSize) { + if ((!incomplete_frame || deviation >= 0) && + delta_frame_bytes > -0.25 * max_frame_size_.bytes()) { // Update the Kalman filter with the new data - KalmanEstimateChannel(frameDelayMS, deltaFS); + KalmanEstimateChannel(frame_delay, delta_frame_bytes); } } else { int nStdDev = - (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier; - EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame); + (deviation >= 0) ? kNumStdDevDelayOutlier : -kNumStdDevDelayOutlier; + EstimateRandomJitter(nStdDev * sqrt(var_noise_), incomplete_frame); } // Post process the total estimated jitter - if (_startupCount >= kStartupDelaySamples) { + if (startup_count_ >= kStartupDelaySamples) { PostProcessEstimate(); } else { - _startupCount++; + startup_count_++; } } // Updates the nack/packet ratio. void VCMJitterEstimator::FrameNacked() { - if (_nackCount < _nackLimit) { - _nackCount++; + if (nack_count_ < kNackLimit) { + nack_count_++; } - _latestNackTimestamp = clock_->TimeInMicroseconds(); + latest_nack_ = clock_->CurrentTime(); } // Updates Kalman estimate of the channel. // The caller is expected to sanity check the inputs. -void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, - int32_t deltaFSBytes) { +void VCMJitterEstimator::KalmanEstimateChannel(TimeDelta frame_delay, + double delta_frame_size_bytes) { double Mh[2]; double hMh_sigma; double kalmanGain[2]; @@ -219,34 +198,34 @@ void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, // Prediction // M = M + Q - _thetaCov[0][0] += _Qcov[0][0]; - _thetaCov[0][1] += _Qcov[0][1]; - _thetaCov[1][0] += _Qcov[1][0]; - _thetaCov[1][1] += _Qcov[1][1]; + theta_cov_[0][0] += q_cov_[0][0]; + theta_cov_[0][1] += q_cov_[0][1]; + theta_cov_[1][0] += q_cov_[1][0]; + theta_cov_[1][1] += q_cov_[1][1]; // Kalman gain // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h') // h = [dFS 1] // Mh = M*h' // hMh_sigma = h*M*h' + R - Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1]; - Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1]; + Mh[0] = theta_cov_[0][0] * delta_frame_size_bytes + theta_cov_[0][1]; + Mh[1] = theta_cov_[1][0] * delta_frame_size_bytes + theta_cov_[1][1]; // sigma weights measurements with a small deltaFS as noisy and // measurements with large deltaFS as good - if (_maxFrameSize < 1.0) { + if (max_frame_size_ < DataSize::Bytes(1)) { return; } - double sigma = (300.0 * exp(-fabs(static_cast(deltaFSBytes)) / - (1e0 * _maxFrameSize)) + + double sigma = (300.0 * exp(-fabs(delta_frame_size_bytes) / + (1e0 * max_frame_size_.bytes())) + 1) * - sqrt(_varNoise); + sqrt(var_noise_); if (sigma < 1.0) { sigma = 1.0; } - hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma; + hMh_sigma = delta_frame_size_bytes * Mh[0] + Mh[1] + sigma; if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0)) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } kalmanGain[0] = Mh[0] / hMh_sigma; @@ -254,94 +233,96 @@ void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, // Correction // theta = theta + K*(dT - h*theta) - measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]); - _theta[0] += kalmanGain[0] * measureRes; - _theta[1] += kalmanGain[1] * measureRes; + measureRes = + frame_delay.ms() - (delta_frame_size_bytes * theta_[0] + theta_[1]); + theta_[0] += kalmanGain[0] * measureRes; + theta_[1] += kalmanGain[1] * measureRes; - if (_theta[0] < _thetaLow) { - _theta[0] = _thetaLow; + if (theta_[0] < kThetaLow) { + theta_[0] = kThetaLow; } // M = (I - K*h)*M - t00 = _thetaCov[0][0]; - t01 = _thetaCov[0][1]; - _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 - - kalmanGain[0] * _thetaCov[1][0]; - _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 - - kalmanGain[0] * _thetaCov[1][1]; - _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t00; - _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t01; + t00 = theta_cov_[0][0]; + t01 = theta_cov_[0][1]; + theta_cov_[0][0] = (1 - kalmanGain[0] * delta_frame_size_bytes) * t00 - + kalmanGain[0] * theta_cov_[1][0]; + theta_cov_[0][1] = (1 - kalmanGain[0] * delta_frame_size_bytes) * t01 - + kalmanGain[0] * theta_cov_[1][1]; + theta_cov_[1][0] = theta_cov_[1][0] * (1 - kalmanGain[1]) - + kalmanGain[1] * delta_frame_size_bytes * t00; + theta_cov_[1][1] = theta_cov_[1][1] * (1 - kalmanGain[1]) - + kalmanGain[1] * delta_frame_size_bytes * t01; // Covariance matrix, must be positive semi-definite. - RTC_DCHECK(_thetaCov[0][0] + _thetaCov[1][1] >= 0 && - _thetaCov[0][0] * _thetaCov[1][1] - - _thetaCov[0][1] * _thetaCov[1][0] >= + RTC_DCHECK(theta_cov_[0][0] + theta_cov_[1][1] >= 0 && + theta_cov_[0][0] * theta_cov_[1][1] - + theta_cov_[0][1] * theta_cov_[1][0] >= 0 && - _thetaCov[0][0] >= 0); + theta_cov_[0][0] >= 0); } // Calculate difference in delay between a sample and the expected delay // estimated by the Kalman filter double VCMJitterEstimator::DeviationFromExpectedDelay( - int64_t frameDelayMS, - int32_t deltaFSBytes) const { - return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]); + TimeDelta frame_delay, + double delta_frame_size_bytes) const { + return frame_delay.ms() - (theta_[0] * delta_frame_size_bytes + theta_[1]); } // Estimates the random jitter by calculating the variance of the sample // distance from the line given by theta. void VCMJitterEstimator::EstimateRandomJitter(double d_dT, - bool incompleteFrame) { - uint64_t now = clock_->TimeInMicroseconds(); - if (_lastUpdateT != -1) { - fps_counter_.AddSample(now - _lastUpdateT); + bool incomplete_frame) { + Timestamp now = clock_->CurrentTime(); + if (last_update_time_.has_value()) { + fps_counter_.AddSample((now - *last_update_time_).us()); } - _lastUpdateT = now; + last_update_time_ = now; - if (_alphaCount == 0) { - RTC_NOTREACHED(); + if (alpha_count_ == 0) { + RTC_DCHECK_NOTREACHED(); return; } double alpha = - static_cast(_alphaCount - 1) / static_cast(_alphaCount); - _alphaCount++; - if (_alphaCount > _alphaCountMax) - _alphaCount = _alphaCountMax; + static_cast(alpha_count_ - 1) / static_cast(alpha_count_); + alpha_count_++; + if (alpha_count_ > kAlphaCountMax) + alpha_count_ = kAlphaCountMax; // In order to avoid a low frame rate stream to react slower to changes, // scale the alpha weight relative a 30 fps stream. - double fps = GetFrameRate(); - if (fps > 0.0) { - double rate_scale = 30.0 / fps; + Frequency fps = GetFrameRate(); + if (fps > Frequency::Zero()) { + constexpr Frequency k30Fps = Frequency::Hertz(30); + double rate_scale = k30Fps / fps; // At startup, there can be a lot of noise in the fps estimate. // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps // at sample #kStartupDelaySamples. - if (_alphaCount < kStartupDelaySamples) { + if (alpha_count_ < kStartupDelaySamples) { rate_scale = - (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) / + (alpha_count_ * rate_scale + (kStartupDelaySamples - alpha_count_)) / kStartupDelaySamples; } alpha = pow(alpha, rate_scale); } - double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT; - double varNoise = - alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise); - if (!incompleteFrame || varNoise > _varNoise) { - _avgNoise = avgNoise; - _varNoise = varNoise; + double avgNoise = alpha * avg_noise_ + (1 - alpha) * d_dT; + double varNoise = alpha * var_noise_ + + (1 - alpha) * (d_dT - avg_noise_) * (d_dT - avg_noise_); + if (!incomplete_frame || varNoise > var_noise_) { + avg_noise_ = avgNoise; + var_noise_ = varNoise; } - if (_varNoise < 1.0) { + if (var_noise_ < 1.0) { // The variance should never be zero, since we might get stuck and consider // all samples as outliers. - _varNoise = 1.0; + var_noise_ = 1.0; } } double VCMJitterEstimator::NoiseThreshold() const { - double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset; + double noiseThreshold = kNoiseStdDevs * sqrt(var_noise_) - kNoiseStdDevOffset; if (noiseThreshold < 1.0) { noiseThreshold = 1.0; } @@ -349,88 +330,91 @@ double VCMJitterEstimator::NoiseThreshold() const { } // Calculates the current jitter estimate from the filtered estimates. -double VCMJitterEstimator::CalculateEstimate() { - double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold(); +TimeDelta VCMJitterEstimator::CalculateEstimate() { + double retMs = + theta_[0] * (max_frame_size_.bytes() - avg_frame_size_.bytes()) + + NoiseThreshold(); + TimeDelta ret = TimeDelta::Millis(retMs); + + constexpr TimeDelta kMinPrevEstimate = TimeDelta::Micros(10); + constexpr TimeDelta kMaxEstimate = TimeDelta::Seconds(10); // A very low estimate (or negative) is neglected. - if (ret < 1.0) { - if (_prevEstimate <= 0.01) { - ret = 1.0; + if (ret < TimeDelta::Millis(1)) { + if (!prev_estimate_ || prev_estimate_ <= kMinPrevEstimate) { + ret = TimeDelta::Millis(1); } else { - ret = _prevEstimate; + ret = *prev_estimate_; } } - if (ret > 10000.0) { // Sanity - ret = 10000.0; + if (ret > kMaxEstimate) { // Sanity + ret = kMaxEstimate; } - _prevEstimate = ret; + prev_estimate_ = ret; return ret; } void VCMJitterEstimator::PostProcessEstimate() { - _filterJitterEstimate = CalculateEstimate(); + filter_jitter_estimate_ = CalculateEstimate(); } -void VCMJitterEstimator::UpdateRtt(int64_t rttMs) { - _rttFilter.Update(rttMs); +void VCMJitterEstimator::UpdateRtt(TimeDelta rtt) { + rtt_filter_.Update(rtt); } // Returns the current filtered estimate if available, // otherwise tries to calculate an estimate. -int VCMJitterEstimator::GetJitterEstimate( - double rttMultiplier, - absl::optional rttMultAddCapMs) { - double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER; - uint64_t now = clock_->TimeInMicroseconds(); +TimeDelta VCMJitterEstimator::GetJitterEstimate( + double rtt_multiplier, + absl::optional rtt_mult_add_cap) { + TimeDelta jitter = CalculateEstimate() + OPERATING_SYSTEM_JITTER; + Timestamp now = clock_->CurrentTime(); - if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000) - _nackCount = 0; + if (now - latest_nack_ > kNackCountTimeout) + nack_count_ = 0; - if (_filterJitterEstimate > jitterMS) - jitterMS = _filterJitterEstimate; - if (_nackCount >= _nackLimit) { - if (rttMultAddCapMs.has_value()) { - jitterMS += - std::min(_rttFilter.RttMs() * rttMultiplier, rttMultAddCapMs.value()); + if (filter_jitter_estimate_ > jitter) + jitter = filter_jitter_estimate_; + if (nack_count_ >= kNackLimit) { + if (rtt_mult_add_cap.has_value()) { + jitter += std::min(rtt_filter_.Rtt() * rtt_multiplier, + rtt_mult_add_cap.value()); } else { - jitterMS += _rttFilter.RttMs() * rttMultiplier; + jitter += rtt_filter_.Rtt() * rtt_multiplier; } } if (enable_reduced_delay_) { - static const double kJitterScaleLowThreshold = 5.0; - static const double kJitterScaleHighThreshold = 10.0; - double fps = GetFrameRate(); + static const Frequency kJitterScaleLowThreshold = Frequency::Hertz(5); + static const Frequency kJitterScaleHighThreshold = Frequency::Hertz(10); + Frequency fps = GetFrameRate(); // Ignore jitter for very low fps streams. if (fps < kJitterScaleLowThreshold) { - if (fps == 0.0) { - return rtc::checked_cast(std::max(0.0, jitterMS) + 0.5); + if (fps.IsZero()) { + return std::max(TimeDelta::Zero(), jitter); } - return 0; + return TimeDelta::Zero(); } // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold. if (fps < kJitterScaleHighThreshold) { - jitterMS = - (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) * - (fps - kJitterScaleLowThreshold) * jitterMS; + jitter = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) * + (fps - kJitterScaleLowThreshold) * jitter; } } - return rtc::checked_cast(std::max(0.0, jitterMS) + 0.5); + return std::max(TimeDelta::Zero(), jitter); } -double VCMJitterEstimator::GetFrameRate() const { - if (fps_counter_.ComputeMean() <= 0.0) - return 0; +Frequency VCMJitterEstimator::GetFrameRate() const { + TimeDelta mean_frame_period = TimeDelta::Micros(fps_counter_.ComputeMean()); + if (mean_frame_period <= TimeDelta::Zero()) + return Frequency::Zero(); - double fps = 1000000.0 / fps_counter_.ComputeMean(); + Frequency fps = 1 / mean_frame_period; // Sanity check. - RTC_DCHECK_GE(fps, 0.0); - if (fps > kMaxFramerateEstimate) { - fps = kMaxFramerateEstimate; - } - return fps; + RTC_DCHECK_GE(fps, Frequency::Zero()); + return std::min(fps, kMaxFramerateEstimate); } } // namespace webrtc diff --git a/modules/video_coding/jitter_estimator.h b/modules/video_coding/jitter_estimator.h index 1d69b95769..026fb7e580 100644 --- a/modules/video_coding/jitter_estimator.h +++ b/modules/video_coding/jitter_estimator.h @@ -11,6 +11,11 @@ #ifndef MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ #define MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ +#include "absl/types/optional.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/rtt_filter.h" #include "rtc_base/rolling_accumulator.h" @@ -22,7 +27,8 @@ class VCMJitterEstimator { public: explicit VCMJitterEstimator(Clock* clock); virtual ~VCMJitterEstimator(); - VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs); + VCMJitterEstimator(const VCMJitterEstimator&) = delete; + VCMJitterEstimator& operator=(const VCMJitterEstimator&) = delete; // Resets the estimate to the initial state. void Reset(); @@ -30,24 +36,25 @@ class VCMJitterEstimator { // Updates the jitter estimate with the new data. // // Input: - // - frameDelay : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - frameSize : Frame size of the current frame. - // - incompleteFrame : Flags if the frame is used to update the + // - frame_delay : Delay-delta calculated by UTILDelayEstimate. + // - frame_size : Frame size of the current frame. + // - incomplete_frame : Flags if the frame is used to update the // estimate before it was complete. // Default is false. - void UpdateEstimate(int64_t frameDelayMS, - uint32_t frameSizeBytes, - bool incompleteFrame = false); + void UpdateEstimate(TimeDelta frame_delay, + DataSize frame_size, + bool incomplete_frame = false); - // Returns the current jitter estimate in milliseconds and adds an RTT - // dependent term in cases of retransmission. + // Returns the current jitter estimate and adds an RTT dependent term in cases + // of retransmission. // Input: - // - rttMultiplier : RTT param multiplier (when applicable). + // - rtt_multiplier : RTT param multiplier (when applicable). + // - rtt_mult_add_cap : Multiplier cap from the RTTMultExperiment. // - // Return value : Jitter estimate in milliseconds. - virtual int GetJitterEstimate(double rttMultiplier, - absl::optional rttMultAddCapMs); + // Return value : Jitter estimate. + virtual TimeDelta GetJitterEstimate( + double rtt_multiplier, + absl::optional rtt_mult_add_cap); // Updates the nack counter. void FrameNacked(); @@ -55,45 +62,47 @@ class VCMJitterEstimator { // Updates the RTT filter. // // Input: - // - rttMs : RTT in ms. - void UpdateRtt(int64_t rttMs); + // - rtt : Round trip time. + void UpdateRtt(TimeDelta rtt); // A constant describing the delay from the jitter buffer to the delay on the // receiving side which is not accounted for by the jitter buffer nor the // decoding delay estimate. - static const uint32_t OPERATING_SYSTEM_JITTER = 10; + static constexpr TimeDelta OPERATING_SYSTEM_JITTER = TimeDelta::Millis(10); protected: // These are protected for better testing possibilities. - double _theta[2]; // Estimated line parameters (slope, offset) - double _varNoise; // Variance of the time-deviation from the line + double theta_[2]; // Estimated line parameters (slope, offset) + double var_noise_; // Variance of the time-deviation from the line private: // Updates the Kalman filter for the line describing the frame size dependent // jitter. // // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - deltaFSBytes : Frame size delta, i.e. frame size at time T - // : minus frame size at time T-1. - void KalmanEstimateChannel(int64_t frameDelayMS, int32_t deltaFSBytes); + // - frame_delay + // Delay-delta calculated by UTILDelayEstimate. + // - delta_frame_size_bytes + // Frame size delta, i.e. frame size at time T minus frame size + // at time T-1. + void KalmanEstimateChannel(TimeDelta frame_delay, + double delta_frame_size_bytes); // Updates the random jitter estimate, i.e. the variance of the time // deviations from the line given by the Kalman filter. // // Input: // - d_dT : The deviation from the kalman estimate. - // - incompleteFrame : True if the frame used to update the + // - incomplete_frame : True if the frame used to update the // estimate with was incomplete. - void EstimateRandomJitter(double d_dT, bool incompleteFrame); + void EstimateRandomJitter(double d_dT, bool incomplete_frame); double NoiseThreshold() const; // Calculates the current jitter estimate. // - // Return value : The current jitter estimate in milliseconds. - double CalculateEstimate(); + // Return value : The current jitter estimate. + TimeDelta CalculateEstimate(); // Post process the calculated estimate. void PostProcessEstimate(); @@ -102,52 +111,48 @@ class VCMJitterEstimator { // estimated by the Kalman filter. // // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - deltaFS : Frame size delta, i.e. frame size at time - // T minus frame size at time T-1. + // - frame_delay : Delay-delta calculated by UTILDelayEstimate. + // - delta_frame_size_bytes : Frame size delta, i.e. frame size at + // time + // T minus frame size at time T-1. // - // Return value : The difference in milliseconds. - double DeviationFromExpectedDelay(int64_t frameDelayMS, - int32_t deltaFSBytes) const; + // Return value : The delay difference in ms. + double DeviationFromExpectedDelay(TimeDelta frame_delay, + double delta_frame_size_bytes) const; - double GetFrameRate() const; + Frequency GetFrameRate() const; - // Constants, filter parameters. - const double _phi; - const double _psi; - const uint32_t _alphaCountMax; - const double _thetaLow; - const uint32_t _nackLimit; - const int32_t _numStdDevDelayOutlier; - const int32_t _numStdDevFrameSizeOutlier; - const double _noiseStdDevs; - const double _noiseStdDevOffset; + double theta_cov_[2][2]; // Estimate covariance + double q_cov_[2][2]; // Process noise covariance - double _thetaCov[2][2]; // Estimate covariance - double _Qcov[2][2]; // Process noise covariance - double _avgFrameSize; // Average frame size - double _varFrameSize; // Frame size variance - double _maxFrameSize; // Largest frame size received (descending - // with a factor _psi) - uint32_t _fsSum; - uint32_t _fsCount; + static constexpr DataSize kDefaultAvgAndMaxFrameSize = DataSize::Bytes(500); + DataSize avg_frame_size_ = kDefaultAvgAndMaxFrameSize; // Average frame size + double var_frame_size_; // Frame size variance. Unit is bytes^2. + // Largest frame size received (descending with a factor kPsi) + DataSize max_frame_size_ = kDefaultAvgAndMaxFrameSize; + DataSize frame_size_sum_ = DataSize::Zero(); + uint32_t frame_size_count_; - int64_t _lastUpdateT; - double _prevEstimate; // The previously returned jitter estimate - uint32_t _prevFrameSize; // Frame size of the previous frame - double _avgNoise; // Average of the random jitter - uint32_t _alphaCount; - double _filterJitterEstimate; // The filtered sum of jitter estimates + absl::optional last_update_time_; + // The previously returned jitter estimate + absl::optional prev_estimate_; + // Frame size of the previous frame + absl::optional prev_frame_size_; + // Average of the random jitter + double avg_noise_; + uint32_t alpha_count_; + // The filtered sum of jitter estimates + TimeDelta filter_jitter_estimate_ = TimeDelta::Zero(); - uint32_t _startupCount; - - int64_t - _latestNackTimestamp; // Timestamp in ms when the latest nack was seen - uint32_t _nackCount; // Keeps track of the number of nacks received, - // but never goes above _nackLimit - VCMRttFilter _rttFilter; + uint32_t startup_count_; + // Time when the latest nack was seen + Timestamp latest_nack_ = Timestamp::Zero(); + // Keeps track of the number of nacks received, but never goes above + // kNackLimit. + uint32_t nack_count_; + VCMRttFilter rtt_filter_; + // Tracks frame rates in microseconds. rtc::RollingAccumulator fps_counter_; const double time_deviation_upper_bound_; const bool enable_reduced_delay_; diff --git a/modules/video_coding/jitter_estimator_tests.cc b/modules/video_coding/jitter_estimator_tests.cc index 14baae7e81..f4bb7fc1ea 100644 --- a/modules/video_coding/jitter_estimator_tests.cc +++ b/modules/video_coding/jitter_estimator_tests.cc @@ -14,6 +14,9 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" #include "modules/video_coding/jitter_estimator.h" #include "rtc_base/experiments/jitter_upper_bound_experiment.h" #include "rtc_base/numerics/histogram_percentile_counter.h" @@ -33,10 +36,6 @@ class TestVCMJitterEstimator : public ::testing::Test { estimator_ = std::make_unique(&fake_clock_); } - void AdvanceClock(int64_t microseconds) { - fake_clock_.AdvanceTimeMicroseconds(microseconds); - } - SimulatedClock fake_clock_; std::unique_ptr estimator_; }; @@ -46,11 +45,16 @@ class ValueGenerator { public: explicit ValueGenerator(int32_t amplitude) : amplitude_(amplitude), counter_(0) {} - virtual ~ValueGenerator() {} - int64_t Delay() const { return ((counter_ % 11) - 5) * amplitude_; } + virtual ~ValueGenerator() = default; - uint32_t FrameSize() const { return 1000 + Delay(); } + TimeDelta Delay() const { + return TimeDelta::Millis((counter_ % 11) - 5) * amplitude_; + } + + DataSize FrameSize() const { + return DataSize::Bytes(1000 + Delay().ms() / 5); + } void Advance() { ++counter_; } @@ -62,12 +66,13 @@ class ValueGenerator { // 5 fps, disable jitter delay altogether. TEST_F(TestVCMJitterEstimator, TestLowRate) { ValueGenerator gen(10); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; + TimeDelta time_delta = 1 / Frequency::Hertz(5); for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); if (i > 2) - EXPECT_EQ(estimator_->GetJitterEstimate(0, absl::nullopt), 0); + EXPECT_EQ(estimator_->GetJitterEstimate(0, absl::nullopt), + TimeDelta::Zero()); gen.Advance(); } } @@ -78,12 +83,13 @@ TEST_F(TestVCMJitterEstimator, TestLowRateDisabled) { SetUp(); ValueGenerator gen(10); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; + TimeDelta time_delta = 1 / Frequency::Hertz(5); for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); if (i > 2) - EXPECT_GT(estimator_->GetJitterEstimate(0, absl::nullopt), 0); + EXPECT_GT(estimator_->GetJitterEstimate(0, absl::nullopt), + TimeDelta::Zero()); gen.Advance(); } } @@ -97,7 +103,7 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { percentiles(1000) {} double upper_bound; double rtt_mult; - absl::optional rtt_mult_add_cap_ms; + absl::optional rtt_mult_add_cap_ms; rtc::HistogramPercentileCounter percentiles; }; std::vector test_cases(4); @@ -113,11 +119,11 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { // Large upper bound, rtt_mult = 1, and large rtt_mult addition cap value. test_cases[2].upper_bound = 1000.0; test_cases[2].rtt_mult = 1.0; - test_cases[2].rtt_mult_add_cap_ms = 200.0; + test_cases[2].rtt_mult_add_cap_ms = TimeDelta::Millis(200); // Large upper bound, rtt_mult = 1, and small rtt_mult addition cap value. test_cases[3].upper_bound = 1000.0; test_cases[3].rtt_mult = 1.0; - test_cases[3].rtt_mult_add_cap_ms = 10.0; + test_cases[3].rtt_mult_add_cap_ms = TimeDelta::Millis(10); // Test jitter buffer upper_bound and rtt_mult addition cap sizes. for (TestContext& context : test_cases) { @@ -130,16 +136,17 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { SetUp(); ValueGenerator gen(50); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 30; - constexpr int64_t kRttMs = 250; + TimeDelta time_delta = 1 / Frequency::Hertz(30); + constexpr TimeDelta kRtt = TimeDelta::Millis(250); for (int i = 0; i < 100; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); estimator_->FrameNacked(); // To test rtt_mult. - estimator_->UpdateRtt(kRttMs); // To test rtt_mult. + estimator_->UpdateRtt(kRtt); // To test rtt_mult. context.percentiles.Add( - static_cast(estimator_->GetJitterEstimate( - context.rtt_mult, context.rtt_mult_add_cap_ms))); + estimator_ + ->GetJitterEstimate(context.rtt_mult, context.rtt_mult_add_cap_ms) + .ms()); gen.Advance(); } } diff --git a/modules/video_coding/nack_requester_unittest.cc b/modules/video_coding/nack_requester_unittest.cc index c9659e37ea..0e5d4159d3 100644 --- a/modules/video_coding/nack_requester_unittest.cc +++ b/modules/video_coding/nack_requester_unittest.cc @@ -56,7 +56,7 @@ class TestNackRequester : public ::testing::TestWithParam, bool WaitForSendNack() { if (timed_out_) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/modules/video_coding/receiver.cc b/modules/video_coding/receiver.cc index e156a1c28d..e09a056948 100644 --- a/modules/video_coding/receiver.cc +++ b/modules/video_coding/receiver.cc @@ -69,7 +69,7 @@ int32_t VCMReceiver::InsertPacket(const VCMPacket& packet) { // We don't want to include timestamps which have suffered from // retransmission here, since we compensate with extra retransmission // delay within the jitter estimate. - timing_->IncomingTimestamp(packet.timestamp, clock_->TimeInMilliseconds()); + timing_->IncomingTimestamp(packet.timestamp, clock_->CurrentTime()); } return VCM_OK; } @@ -94,16 +94,18 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, } if (min_playout_delay_ms >= 0) - timing_->set_min_playout_delay(min_playout_delay_ms); + timing_->set_min_playout_delay(TimeDelta::Millis(min_playout_delay_ms)); if (max_playout_delay_ms >= 0) - timing_->set_max_playout_delay(max_playout_delay_ms); + timing_->set_max_playout_delay(TimeDelta::Millis(max_playout_delay_ms)); // We have a frame - Set timing and render timestamp. - timing_->SetJitterDelay(jitter_buffer_.EstimatedJitterMs()); - const int64_t now_ms = clock_->TimeInMilliseconds(); + timing_->SetJitterDelay( + TimeDelta::Millis(jitter_buffer_.EstimatedJitterMs())); + const Timestamp now = clock_->CurrentTime(); + const int64_t now_ms = now.ms(); timing_->UpdateCurrentDelay(frame_timestamp); - render_time_ms = timing_->RenderTimeMs(frame_timestamp, now_ms); + render_time_ms = timing_->RenderTime(frame_timestamp, now).ms(); // Check render timing. bool timing_error = false; // Assume that render timing errors are due to changes in the video stream. @@ -117,7 +119,7 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, << frame_delay << " > " << max_video_delay_ms_ << "). Resetting the video jitter buffer."; timing_error = true; - } else if (static_cast(timing_->TargetVideoDelay()) > + } else if (static_cast(timing_->TargetVideoDelay().ms()) > max_video_delay_ms_) { RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " << max_video_delay_ms_ @@ -140,8 +142,11 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, uint16_t new_max_wait_time = static_cast(VCM_MAX(available_wait_time, 0)); uint32_t wait_time_ms = rtc::saturated_cast( - timing_->MaxWaitingTime(render_time_ms, clock_->TimeInMilliseconds(), - /*too_many_frames_queued=*/false)); + timing_ + ->MaxWaitingTime(Timestamp::Millis(render_time_ms), + clock_->CurrentTime(), + /*too_many_frames_queued=*/false) + .ms()); if (new_max_wait_time < wait_time_ms) { // We're not allowed to wait until the frame is supposed to be rendered, // waiting as long as we're allowed to avoid busy looping, and then return diff --git a/modules/video_coding/receiver_unittest.cc b/modules/video_coding/receiver_unittest.cc index 1b6ee34bd7..73c88a5800 100644 --- a/modules/video_coding/receiver_unittest.cc +++ b/modules/video_coding/receiver_unittest.cc @@ -124,7 +124,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -152,7 +152,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -182,7 +182,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert enough frames to have too long non-decodable sequence, except that @@ -212,7 +212,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -448,9 +448,9 @@ TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { int64_t arrive_timestamps[kNumFrames]; int64_t render_timestamps[kNumFrames]; - int render_delay_ms; - int max_decode_ms; - int dummy; + TimeDelta render_delay_ms = TimeDelta::Zero(); + TimeDelta max_decode_ms = TimeDelta::Zero(); + TimeDelta dummy = TimeDelta::Zero(); timing_.GetTimings(&max_decode_ms, &dummy, &dummy, &dummy, &dummy, &render_delay_ms); @@ -479,8 +479,9 @@ TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding); int64_t end_time = clock_.TimeInMilliseconds(); if (frame) { - EXPECT_EQ(frame->RenderTimeMs() - max_decode_ms - render_delay_ms, - end_time); + EXPECT_EQ( + frame->RenderTimeMs() - max_decode_ms.ms() - render_delay_ms.ms(), + end_time); receiver_.ReleaseFrame(frame); ++num_frames_return; } else { diff --git a/modules/video_coding/rtp_seq_num_only_ref_finder.cc b/modules/video_coding/rtp_seq_num_only_ref_finder.cc index 4381cf0952..59b027e2ce 100644 --- a/modules/video_coding/rtp_seq_num_only_ref_finder.cc +++ b/modules/video_coding/rtp_seq_num_only_ref_finder.cc @@ -116,7 +116,7 @@ void RtpSeqNumOnlyRefFinder::RetryStashedFrames( case kHandOff: complete_frame = true; res.push_back(std::move(*frame_it)); - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; case kDrop: frame_it = stashed_frames_.erase(frame_it); } diff --git a/modules/video_coding/rtp_vp8_ref_finder.cc b/modules/video_coding/rtp_vp8_ref_finder.cc index 298e1fb755..6496232eee 100644 --- a/modules/video_coding/rtp_vp8_ref_finder.cc +++ b/modules/video_coding/rtp_vp8_ref_finder.cc @@ -218,7 +218,7 @@ void RtpVp8RefFinder::RetryStashedFrames( case kHandOff: complete_frame = true; res.push_back(std::move(*frame_it)); - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; case kDrop: frame_it = stashed_frames_.erase(frame_it); } diff --git a/modules/video_coding/rtp_vp9_ref_finder.cc b/modules/video_coding/rtp_vp9_ref_finder.cc index cab5792785..7a1f946904 100644 --- a/modules/video_coding/rtp_vp9_ref_finder.cc +++ b/modules/video_coding/rtp_vp9_ref_finder.cc @@ -16,17 +16,44 @@ #include "rtc_base/logging.h" namespace webrtc { - RtpFrameReferenceFinder::ReturnVector RtpVp9RefFinder::ManageFrame( std::unique_ptr frame) { - FrameDecision decision = ManageFrameInternal(frame.get()); + const RTPVideoHeaderVP9& codec_header = absl::get( + frame->GetRtpVideoHeader().video_type_header); + + frame->SetSpatialIndex(codec_header.spatial_idx); + frame->SetId(codec_header.picture_id & (kFrameIdLength - 1)); + + FrameDecision decision; + if (codec_header.temporal_idx >= kMaxTemporalLayers || + codec_header.spatial_idx >= kMaxSpatialLayers) { + decision = kDrop; + } else if (codec_header.flexible_mode) { + decision = ManageFrameFlexible(frame.get(), codec_header); + } else { + if (codec_header.tl0_pic_idx == kNoTl0PicIdx) { + RTC_LOG(LS_WARNING) << "TL0PICIDX is expected to be present in " + "non-flexible mode."; + decision = kDrop; + } else { + int64_t unwrapped_tl0 = + tl0_unwrapper_.Unwrap(codec_header.tl0_pic_idx & 0xFF); + decision = ManageFrameGof(frame.get(), codec_header, unwrapped_tl0); + + if (decision == kStash) { + if (stashed_frames_.size() > kMaxStashedFrames) { + stashed_frames_.pop_back(); + } + + stashed_frames_.push_front( + {.unwrapped_tl0 = unwrapped_tl0, .frame = std::move(frame)}); + } + } + } RtpFrameReferenceFinder::ReturnVector res; switch (decision) { case kStash: - if (stashed_frames_.size() > kMaxStashedFrames) - stashed_frames_.pop_back(); - stashed_frames_.push_front(std::move(frame)); return res; case kHandOff: res.push_back(std::move(frame)); @@ -39,46 +66,28 @@ RtpFrameReferenceFinder::ReturnVector RtpVp9RefFinder::ManageFrame( return res; } -RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameInternal( - RtpFrameObject* frame) { - const RTPVideoHeader& video_header = frame->GetRtpVideoHeader(); - const RTPVideoHeaderVP9& codec_header = - absl::get(video_header.video_type_header); - - // Protect against corrupted packets with arbitrary large temporal idx. - if (codec_header.temporal_idx >= kMaxTemporalLayers || - codec_header.spatial_idx >= kMaxSpatialLayers) - return kDrop; - - frame->SetSpatialIndex(codec_header.spatial_idx); - frame->SetId(codec_header.picture_id & (kFrameIdLength - 1)); - - if (last_picture_id_ == -1) - last_picture_id_ = frame->Id(); - - if (codec_header.flexible_mode) { - if (codec_header.num_ref_pics > EncodedFrame::kMaxFrameReferences) { - return kDrop; - } - frame->num_references = codec_header.num_ref_pics; - for (size_t i = 0; i < frame->num_references; ++i) { - frame->references[i] = - Subtract(frame->Id(), codec_header.pid_diff[i]); - } - - FlattenFrameIdAndRefs(frame, codec_header.inter_layer_predicted); - return kHandOff; - } - - if (codec_header.tl0_pic_idx == kNoTl0PicIdx) { - RTC_LOG(LS_WARNING) << "TL0PICIDX is expected to be present in " - "non-flexible mode."; +RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameFlexible( + RtpFrameObject* frame, + const RTPVideoHeaderVP9& codec_header) { + if (codec_header.num_ref_pics > EncodedFrame::kMaxFrameReferences) { return kDrop; } + frame->num_references = codec_header.num_ref_pics; + for (size_t i = 0; i < frame->num_references; ++i) { + frame->references[i] = + Subtract(frame->Id(), codec_header.pid_diff[i]); + } + + FlattenFrameIdAndRefs(frame, codec_header.inter_layer_predicted); + return kHandOff; +} + +RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameGof( + RtpFrameObject* frame, + const RTPVideoHeaderVP9& codec_header, + int64_t unwrapped_tl0) { GofInfo* info; - int64_t unwrapped_tl0 = - tl0_unwrapper_.Unwrap(codec_header.tl0_pic_idx & 0xFF); if (codec_header.ss_data_available) { if (codec_header.temporal_idx != 0) { RTC_LOG(LS_WARNING) << "Received scalability structure on a non base " @@ -303,20 +312,23 @@ void RtpVp9RefFinder::RetryStashedFrames( bool complete_frame = false; do { complete_frame = false; - for (auto frame_it = stashed_frames_.begin(); - frame_it != stashed_frames_.end();) { - FrameDecision decision = ManageFrameInternal(frame_it->get()); + for (auto it = stashed_frames_.begin(); it != stashed_frames_.end();) { + const RTPVideoHeaderVP9& codec_header = absl::get( + it->frame->GetRtpVideoHeader().video_type_header); + RTC_DCHECK(!codec_header.flexible_mode); + FrameDecision decision = + ManageFrameGof(it->frame.get(), codec_header, it->unwrapped_tl0); switch (decision) { case kStash: - ++frame_it; + ++it; break; case kHandOff: complete_frame = true; - res.push_back(std::move(*frame_it)); - ABSL_FALLTHROUGH_INTENDED; + res.push_back(std::move(it->frame)); + [[fallthrough]]; case kDrop: - frame_it = stashed_frames_.erase(frame_it); + it = stashed_frames_.erase(it); } } } while (complete_frame); @@ -342,7 +354,7 @@ void RtpVp9RefFinder::FlattenFrameIdAndRefs(RtpFrameObject* frame, void RtpVp9RefFinder::ClearTo(uint16_t seq_num) { auto it = stashed_frames_.begin(); while (it != stashed_frames_.end()) { - if (AheadOf(seq_num, (*it)->first_seq_num())) { + if (AheadOf(seq_num, it->frame->first_seq_num())) { it = stashed_frames_.erase(it); } else { ++it; diff --git a/modules/video_coding/rtp_vp9_ref_finder.h b/modules/video_coding/rtp_vp9_ref_finder.h index 436cb1c84a..2971f686b1 100644 --- a/modules/video_coding/rtp_vp9_ref_finder.h +++ b/modules/video_coding/rtp_vp9_ref_finder.h @@ -48,7 +48,16 @@ class RtpVp9RefFinder { uint16_t last_picture_id; }; - FrameDecision ManageFrameInternal(RtpFrameObject* frame); + struct UnwrappedTl0Frame { + int64_t unwrapped_tl0; + std::unique_ptr frame; + }; + + FrameDecision ManageFrameFlexible(RtpFrameObject* frame, + const RTPVideoHeaderVP9& vp9_header); + FrameDecision ManageFrameGof(RtpFrameObject* frame, + const RTPVideoHeaderVP9& vp9_header, + int64_t unwrapped_tl0); void RetryStashedFrames(RtpFrameReferenceFinder::ReturnVector& res); bool MissingRequiredFrameVp9(uint16_t picture_id, const GofInfo& info); @@ -60,13 +69,9 @@ class RtpVp9RefFinder { void FlattenFrameIdAndRefs(RtpFrameObject* frame, bool inter_layer_predicted); - // Save the last picture id in order to detect when there is a gap in frames - // that have not yet been fully received. - int last_picture_id_ = -1; - // Frames that have been fully received but didn't have all the information // needed to determine their references. - std::deque> stashed_frames_; + std::deque stashed_frames_; // Where the current scalability structure is in the // `scalability_structures_` array. diff --git a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc index 6de7ce106f..66b284f020 100644 --- a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc +++ b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc @@ -23,6 +23,7 @@ using ::testing::Matches; using ::testing::MatchResultListener; using ::testing::Pointee; using ::testing::Property; +using ::testing::SizeIs; using ::testing::UnorderedElementsAreArray; namespace webrtc { @@ -702,4 +703,17 @@ TEST_F(RtpVp9RefFinderTest, SpatialIndex) { Contains(Pointee(Property(&EncodedFrame::SpatialIndex, 2)))); } +TEST_F(RtpVp9RefFinderTest, StashedFramesDoNotWrapTl0Backwards) { + GofInfoVP9 ss; + ss.SetGofInfoVP9(kTemporalStructureMode1); + + Insert(Frame().Pid(0).SidAndTid(0, 0).Tl0(0)); + EXPECT_THAT(frames_, SizeIs(0)); + + Insert(Frame().Pid(128).SidAndTid(0, 0).Tl0(128).AsKeyFrame().Gof(&ss)); + EXPECT_THAT(frames_, SizeIs(1)); + Insert(Frame().Pid(129).SidAndTid(0, 0).Tl0(129)); + EXPECT_THAT(frames_, SizeIs(2)); +} + } // namespace webrtc diff --git a/modules/video_coding/rtt_filter.cc b/modules/video_coding/rtt_filter.cc index 75813a46ad..eaf3b2b301 100644 --- a/modules/video_coding/rtt_filter.cc +++ b/modules/video_coding/rtt_filter.cc @@ -14,152 +14,148 @@ #include #include -#include "modules/video_coding/internal_defines.h" +#include + +#include "absl/algorithm/container.h" +#include "absl/container/inlined_vector.h" +#include "api/units/time_delta.h" namespace webrtc { +namespace { + +constexpr TimeDelta kMaxRtt = TimeDelta::Seconds(3); +constexpr uint32_t kFilterFactorMax = 35; +constexpr double kJumpStddev = 2.5; +constexpr double kDriftStdDev = 3.5; + +} // namespace + VCMRttFilter::VCMRttFilter() - : _filtFactMax(35), - _jumpStdDevs(2.5), - _driftStdDevs(3.5), - _detectThreshold(kMaxDriftJumpCount) { + : avg_rtt_(TimeDelta::Zero()), + var_rtt_(0), + max_rtt_(TimeDelta::Zero()), + jump_buf_(kMaxDriftJumpCount, TimeDelta::Zero()), + drift_buf_(kMaxDriftJumpCount, TimeDelta::Zero()) { Reset(); } -VCMRttFilter& VCMRttFilter::operator=(const VCMRttFilter& rhs) { - if (this != &rhs) { - _gotNonZeroUpdate = rhs._gotNonZeroUpdate; - _avgRtt = rhs._avgRtt; - _varRtt = rhs._varRtt; - _maxRtt = rhs._maxRtt; - _filtFactCount = rhs._filtFactCount; - _jumpCount = rhs._jumpCount; - _driftCount = rhs._driftCount; - memcpy(_jumpBuf, rhs._jumpBuf, sizeof(_jumpBuf)); - memcpy(_driftBuf, rhs._driftBuf, sizeof(_driftBuf)); - } - return *this; -} - void VCMRttFilter::Reset() { - _gotNonZeroUpdate = false; - _avgRtt = 0; - _varRtt = 0; - _maxRtt = 0; - _filtFactCount = 1; - _jumpCount = 0; - _driftCount = 0; - memset(_jumpBuf, 0, sizeof(_jumpBuf)); - memset(_driftBuf, 0, sizeof(_driftBuf)); + got_non_zero_update_ = false; + avg_rtt_ = TimeDelta::Zero(); + var_rtt_ = 0; + max_rtt_ = TimeDelta::Zero(); + filt_fact_count_ = 1; + absl::c_fill(jump_buf_, TimeDelta::Zero()); + absl::c_fill(drift_buf_, TimeDelta::Zero()); } -void VCMRttFilter::Update(int64_t rttMs) { - if (!_gotNonZeroUpdate) { - if (rttMs == 0) { +void VCMRttFilter::Update(TimeDelta rtt) { + if (!got_non_zero_update_) { + if (rtt.IsZero()) { return; } - _gotNonZeroUpdate = true; + got_non_zero_update_ = true; } // Sanity check - if (rttMs > 3000) { - rttMs = 3000; + if (rtt > kMaxRtt) { + rtt = kMaxRtt; } - double filtFactor = 0; - if (_filtFactCount > 1) { - filtFactor = static_cast(_filtFactCount - 1) / _filtFactCount; + double filt_factor = 0; + if (filt_fact_count_ > 1) { + filt_factor = static_cast(filt_fact_count_ - 1) / filt_fact_count_; } - _filtFactCount++; - if (_filtFactCount > _filtFactMax) { - // This prevents filtFactor from going above - // (_filtFactMax - 1) / _filtFactMax, - // e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98 - _filtFactCount = _filtFactMax; + filt_fact_count_++; + if (filt_fact_count_ > kFilterFactorMax) { + // This prevents filt_factor from going above + // (_filt_fact_max - 1) / filt_fact_max_, + // e.g., filt_fact_max_ = 50 => filt_factor = 49/50 = 0.98 + filt_fact_count_ = kFilterFactorMax; } - double oldAvg = _avgRtt; - double oldVar = _varRtt; - _avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs; - _varRtt = filtFactor * _varRtt + - (1 - filtFactor) * (rttMs - _avgRtt) * (rttMs - _avgRtt); - _maxRtt = VCM_MAX(rttMs, _maxRtt); - if (!JumpDetection(rttMs) || !DriftDetection(rttMs)) { + TimeDelta old_avg = avg_rtt_; + int64_t old_var = var_rtt_; + avg_rtt_ = filt_factor * avg_rtt_ + (1 - filt_factor) * rtt; + int64_t delta_ms = (rtt - avg_rtt_).ms(); + var_rtt_ = filt_factor * var_rtt_ + (1 - filt_factor) * (delta_ms * delta_ms); + max_rtt_ = std::max(rtt, max_rtt_); + if (!JumpDetection(rtt) || !DriftDetection(rtt)) { // In some cases we don't want to update the statistics - _avgRtt = oldAvg; - _varRtt = oldVar; + avg_rtt_ = old_avg; + var_rtt_ = old_var; } } -bool VCMRttFilter::JumpDetection(int64_t rttMs) { - double diffFromAvg = _avgRtt - rttMs; - if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt)) { - int diffSign = (diffFromAvg >= 0) ? 1 : -1; - int jumpCountSign = (_jumpCount >= 0) ? 1 : -1; - if (diffSign != jumpCountSign) { +bool VCMRttFilter::JumpDetection(TimeDelta rtt) { + TimeDelta diff_from_avg = avg_rtt_ - rtt; + // Unit of var_rtt_ is ms^2. + TimeDelta jump_threshold = TimeDelta::Millis(kJumpStddev * sqrt(var_rtt_)); + if (diff_from_avg.Abs() > jump_threshold) { + bool positive_diff = diff_from_avg >= TimeDelta::Zero(); + if (!jump_buf_.empty() && positive_diff != last_jump_positive_) { // Since the signs differ the samples currently // in the buffer is useless as they represent a // jump in a different direction. - _jumpCount = 0; + jump_buf_.clear(); } - if (abs(_jumpCount) < kMaxDriftJumpCount) { - // Update the buffer used for the short time - // statistics. + if (jump_buf_.size() < kMaxDriftJumpCount) { + // Update the buffer used for the short time statistics. // The sign of the diff is used for updating the counter since // we want to use the same buffer for keeping track of when // the RTT jumps down and up. - _jumpBuf[abs(_jumpCount)] = rttMs; - _jumpCount += diffSign; + jump_buf_.push_back(rtt); + last_jump_positive_ = positive_diff; } - if (abs(_jumpCount) >= _detectThreshold) { + if (jump_buf_.size() >= kMaxDriftJumpCount) { // Detected an RTT jump - ShortRttFilter(_jumpBuf, abs(_jumpCount)); - _filtFactCount = _detectThreshold + 1; - _jumpCount = 0; + ShortRttFilter(jump_buf_); + filt_fact_count_ = kMaxDriftJumpCount + 1; + jump_buf_.clear(); } else { return false; } } else { - _jumpCount = 0; + jump_buf_.clear(); } return true; } -bool VCMRttFilter::DriftDetection(int64_t rttMs) { - if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt)) { - if (_driftCount < kMaxDriftJumpCount) { - // Update the buffer used for the short time - // statistics. - _driftBuf[_driftCount] = rttMs; - _driftCount++; +bool VCMRttFilter::DriftDetection(TimeDelta rtt) { + // Unit of sqrt of var_rtt_ is ms. + TimeDelta drift_threshold = TimeDelta::Millis(kDriftStdDev * sqrt(var_rtt_)); + if (max_rtt_ - avg_rtt_ > drift_threshold) { + if (drift_buf_.size() < kMaxDriftJumpCount) { + // Update the buffer used for the short time statistics. + drift_buf_.push_back(rtt); } - if (_driftCount >= _detectThreshold) { + if (drift_buf_.size() >= kMaxDriftJumpCount) { // Detected an RTT drift - ShortRttFilter(_driftBuf, _driftCount); - _filtFactCount = _detectThreshold + 1; - _driftCount = 0; + ShortRttFilter(drift_buf_); + filt_fact_count_ = kMaxDriftJumpCount + 1; + drift_buf_.clear(); } } else { - _driftCount = 0; + drift_buf_.clear(); } return true; } -void VCMRttFilter::ShortRttFilter(int64_t* buf, uint32_t length) { - if (length == 0) { - return; - } - _maxRtt = 0; - _avgRtt = 0; - for (uint32_t i = 0; i < length; i++) { - if (buf[i] > _maxRtt) { - _maxRtt = buf[i]; +void VCMRttFilter::ShortRttFilter(const BufferList& buf) { + RTC_DCHECK_EQ(buf.size(), kMaxDriftJumpCount); + max_rtt_ = TimeDelta::Zero(); + avg_rtt_ = TimeDelta::Zero(); + for (const TimeDelta& rtt : buf) { + if (rtt > max_rtt_) { + max_rtt_ = rtt; } - _avgRtt += buf[i]; + avg_rtt_ += rtt; } - _avgRtt = _avgRtt / static_cast(length); + avg_rtt_ = avg_rtt_ / static_cast(buf.size()); } -int64_t VCMRttFilter::RttMs() const { - return static_cast(_maxRtt + 0.5); +TimeDelta VCMRttFilter::Rtt() const { + return max_rtt_; } + } // namespace webrtc diff --git a/modules/video_coding/rtt_filter.h b/modules/video_coding/rtt_filter.h index 073fabb85b..a611aafcb8 100644 --- a/modules/video_coding/rtt_filter.h +++ b/modules/video_coding/rtt_filter.h @@ -13,52 +13,55 @@ #include +#include "absl/container/inlined_vector.h" +#include "api/units/time_delta.h" + namespace webrtc { class VCMRttFilter { public: VCMRttFilter(); - - VCMRttFilter& operator=(const VCMRttFilter& rhs); + VCMRttFilter(const VCMRttFilter&) = delete; + VCMRttFilter& operator=(const VCMRttFilter&) = delete; // Resets the filter. void Reset(); // Updates the filter with a new sample. - void Update(int64_t rttMs); - // A getter function for the current RTT level in ms. - int64_t RttMs() const; + void Update(TimeDelta rtt); + // A getter function for the current RTT level. + TimeDelta Rtt() const; private: // The size of the drift and jump memory buffers // and thus also the detection threshold for these // detectors in number of samples. - enum { kMaxDriftJumpCount = 5 }; + static constexpr int kMaxDriftJumpCount = 5; + using BufferList = absl::InlinedVector; + // Detects RTT jumps by comparing the difference between // samples and average to the standard deviation. // Returns true if the long time statistics should be updated // and false otherwise - bool JumpDetection(int64_t rttMs); + bool JumpDetection(TimeDelta rtt); + // Detects RTT drifts by comparing the difference between // max and average to the standard deviation. // Returns true if the long time statistics should be updated // and false otherwise - bool DriftDetection(int64_t rttMs); - // Computes the short time average and maximum of the vector buf. - void ShortRttFilter(int64_t* buf, uint32_t length); + bool DriftDetection(TimeDelta rtt); - bool _gotNonZeroUpdate; - double _avgRtt; - double _varRtt; - int64_t _maxRtt; - uint32_t _filtFactCount; - const uint32_t _filtFactMax; - const double _jumpStdDevs; - const double _driftStdDevs; - int32_t _jumpCount; - int32_t _driftCount; - const int32_t _detectThreshold; - int64_t _jumpBuf[kMaxDriftJumpCount]; - int64_t _driftBuf[kMaxDriftJumpCount]; + // Computes the short time average and maximum of the vector buf. + void ShortRttFilter(const BufferList& buf); + + bool got_non_zero_update_; + TimeDelta avg_rtt_; + // Variance units are TimeDelta^2. Store as ms^2. + int64_t var_rtt_; + TimeDelta max_rtt_; + uint32_t filt_fact_count_; + bool last_jump_positive_ = false; + BufferList jump_buf_; + BufferList drift_buf_; }; } // namespace webrtc diff --git a/modules/video_coding/rtt_filter_unittest.cc b/modules/video_coding/rtt_filter_unittest.cc new file mode 100644 index 0000000000..15d7d66b83 --- /dev/null +++ b/modules/video_coding/rtt_filter_unittest.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/rtt_filter.h" + +#include "api/units/time_delta.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(RttFilterTest, RttIsCapped) { + VCMRttFilter rtt_filter; + rtt_filter.Update(TimeDelta::Seconds(500)); + + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Seconds(3)); +} + +// If the difference between samples is more than away 2.5 stddev from the mean +// then this is considered a jump. After more than 5 data points at the new +// level, the RTT is reset to the new level. +TEST(RttFilterTest, PositiveJumpDetection) { + VCMRttFilter rtt_filter; + + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + + // Trigger 5 jumps. + rtt_filter.Update(TimeDelta::Millis(1400)); + rtt_filter.Update(TimeDelta::Millis(1500)); + rtt_filter.Update(TimeDelta::Millis(1600)); + rtt_filter.Update(TimeDelta::Millis(1600)); + + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600)); + + rtt_filter.Update(TimeDelta::Millis(1600)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600)); +} + +TEST(RttFilterTest, NegativeJumpDetection) { + VCMRttFilter rtt_filter; + + for (int i = 0; i < 10; ++i) + rtt_filter.Update(TimeDelta::Millis(1500)); + + // Trigger 5 negative data points that jump rtt down. + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + // Before 5 data points at the new level, max RTT is still 1500. + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1500)); + + rtt_filter.Update(TimeDelta::Millis(300)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(300)); +} + +TEST(RttFilterTest, JumpsResetByDirectionShift) { + VCMRttFilter rtt_filter; + for (int i = 0; i < 10; ++i) + rtt_filter.Update(TimeDelta::Millis(1500)); + + // Trigger 4 negative jumps, then a positive one. This resets the jump + // detection. + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(2000)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000)); + + rtt_filter.Update(TimeDelta::Millis(300)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000)); +} + +// If the difference between the max and average is more than 3.5 stddevs away +// then a drift is detected, and a short filter is applied to find a new max +// rtt. +TEST(RttFilterTest, DriftDetection) { + VCMRttFilter rtt_filter; + + // Descend RTT by 30ms and settle at 700ms RTT. A drift is detected after rtt + // of 700ms is reported around 50 times for these targets. + constexpr TimeDelta kStartRtt = TimeDelta::Millis(1000); + constexpr TimeDelta kDriftTarget = TimeDelta::Millis(700); + constexpr TimeDelta kDelta = TimeDelta::Millis(30); + for (TimeDelta rtt = kStartRtt; rtt >= kDriftTarget; rtt -= kDelta) + rtt_filter.Update(rtt); + + EXPECT_EQ(rtt_filter.Rtt(), kStartRtt); + + for (int i = 0; i < 50; ++i) + rtt_filter.Update(kDriftTarget); + EXPECT_EQ(rtt_filter.Rtt(), kDriftTarget); +} + +} // namespace webrtc diff --git a/modules/video_coding/svc/create_scalability_structure.cc b/modules/video_coding/svc/create_scalability_structure.cc index 39710d82ff..8e5c06fca9 100644 --- a/modules/video_coding/svc/create_scalability_structure.cc +++ b/modules/video_coding/svc/create_scalability_structure.cc @@ -27,6 +27,7 @@ struct NamedStructureFactory { absl::string_view name; // Use function pointer to make NamedStructureFactory trivally destructable. std::unique_ptr (*factory)(); + ScalableVideoController::StreamLayersConfig config; }; // Wrap std::make_unique function to have correct return type. @@ -44,22 +45,90 @@ std::unique_ptr CreateH() { return std::make_unique(factor); } +constexpr ScalableVideoController::StreamLayersConfig kConfigNone = { + /*num_spatial_layers=*/1, /*num_temporal_layers=*/1, + /*uses_reference_scaling=*/false}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL1T2 = { + /*num_spatial_layers=*/1, /*num_temporal_layers=*/2, + /*uses_reference_scaling=*/false}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL1T3 = { + /*num_spatial_layers=*/1, /*num_temporal_layers=*/3, + /*uses_reference_scaling=*/false}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL2T1 = { + /*num_spatial_layers=*/2, + /*num_temporal_layers=*/1, + /*uses_reference_scaling=*/true, + {1, 1}, + {2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL2T1h = { + /*num_spatial_layers=*/2, + /*num_temporal_layers=*/1, + /*uses_reference_scaling=*/true, + {2, 1}, + {3, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL2T2 = { + /*num_spatial_layers=*/2, + /*num_temporal_layers=*/2, + /*uses_reference_scaling=*/true, + {1, 1}, + {2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL2T3 = { + /*num_spatial_layers=*/2, + /*num_temporal_layers=*/3, + /*uses_reference_scaling=*/true, + {1, 1}, + {2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL3T1 = { + /*num_spatial_layers=*/3, + /*num_temporal_layers=*/1, + /*uses_reference_scaling=*/true, + {1, 1, 1}, + {4, 2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigL3T3 = { + /*num_spatial_layers=*/3, + /*num_temporal_layers=*/3, + /*uses_reference_scaling=*/true, + {1, 1, 1}, + {4, 2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigS2T1 = { + /*num_spatial_layers=*/2, + /*num_temporal_layers=*/1, + /*uses_reference_scaling=*/false, + {1, 1}, + {2, 1}}; + +constexpr ScalableVideoController::StreamLayersConfig kConfigS3T3 = { + /*num_spatial_layers=*/3, + /*num_temporal_layers=*/3, + /*uses_reference_scaling=*/false, + {1, 1, 1}, + {4, 2, 1}}; + constexpr NamedStructureFactory kFactories[] = { - {"NONE", Create}, - {"L1T2", Create}, - {"L1T3", Create}, - {"L2T1", Create}, - {"L2T1h", CreateH}, - {"L2T1_KEY", Create}, - {"L2T2", Create}, - {"L2T2_KEY", Create}, - {"L2T2_KEY_SHIFT", Create}, - {"L2T3_KEY", Create}, - {"L3T1", Create}, - {"L3T3", Create}, - {"L3T3_KEY", Create}, - {"S2T1", Create}, - {"S3T3", Create}, + {"NONE", Create, kConfigNone}, + {"L1T2", Create, kConfigL1T2}, + {"L1T3", Create, kConfigL1T3}, + {"L2T1", Create, kConfigL2T1}, + {"L2T1h", CreateH, kConfigL2T1h}, + {"L2T1_KEY", Create, kConfigL2T1}, + {"L2T2", Create, kConfigL2T2}, + {"L2T2_KEY", Create, kConfigL2T2}, + {"L2T2_KEY_SHIFT", Create, kConfigL2T2}, + {"L2T3_KEY", Create, kConfigL2T3}, + {"L3T1", Create, kConfigL3T1}, + {"L3T3", Create, kConfigL3T3}, + {"L3T3_KEY", Create, kConfigL3T3}, + {"S2T1", Create, kConfigS2T1}, + {"S3T3", Create, kConfigS3T3}, }; } // namespace @@ -75,4 +144,15 @@ std::unique_ptr CreateScalabilityStructure( return nullptr; } +absl::optional +ScalabilityStructureConfig(absl::string_view name) { + RTC_DCHECK(!name.empty()); + for (const auto& entry : kFactories) { + if (entry.name == name) { + return entry.config; + } + } + return absl::nullopt; +} + } // namespace webrtc diff --git a/modules/video_coding/svc/create_scalability_structure.h b/modules/video_coding/svc/create_scalability_structure.h index 9a14221fd2..fde034433e 100644 --- a/modules/video_coding/svc/create_scalability_structure.h +++ b/modules/video_coding/svc/create_scalability_structure.h @@ -14,6 +14,7 @@ #include #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "modules/video_coding/svc/scalable_video_controller.h" namespace webrtc { @@ -24,6 +25,11 @@ namespace webrtc { std::unique_ptr CreateScalabilityStructure( absl::string_view name); +// Returns descrption of the scalability structure identified by 'name', +// Return nullopt for unknown name. +absl::optional +ScalabilityStructureConfig(absl::string_view name); + } // namespace webrtc #endif // MODULES_VIDEO_CODING_SVC_CREATE_SCALABILITY_STRUCTURE_H_ diff --git a/modules/video_coding/svc/scalability_structure_full_svc.cc b/modules/video_coding/svc/scalability_structure_full_svc.cc index b89de99330..892059297c 100644 --- a/modules/video_coding/svc/scalability_structure_full_svc.cc +++ b/modules/video_coding/svc/scalability_structure_full_svc.cc @@ -52,6 +52,7 @@ ScalabilityStructureFullSvc::StreamConfig() const { result.scaling_factor_den[sid - 1] = resolution_factor_.den * result.scaling_factor_den[sid]; } + result.uses_reference_scaling = num_spatial_layers_ > 1; return result; } @@ -122,7 +123,7 @@ ScalabilityStructureFullSvc::NextPattern() const { } return kDeltaT0; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kNone; } @@ -172,7 +173,6 @@ ScalabilityStructureFullSvc::NextFrameConfig(bool restart) { config.Update(BufferIndex(sid, /*tid=*/0)); } - can_reference_t0_frame_for_spatial_id_.set(sid); spatial_dependency_buffer_id = BufferIndex(sid, /*tid=*/0); } break; @@ -227,7 +227,7 @@ ScalabilityStructureFullSvc::NextFrameConfig(bool restart) { } break; case kNone: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } @@ -255,6 +255,9 @@ GenericFrameInfo ScalabilityStructureFullSvc::OnEncodeDone( // pattern defered here from the `NextFrameConfig`. // In particular creating VP9 references rely on this behavior. last_pattern_ = static_cast(config.Id()); + if (config.TemporalId() == 0) { + can_reference_t0_frame_for_spatial_id_.set(config.SpatialId()); + } if (config.TemporalId() == 1) { can_reference_t1_frame_for_spatial_id_.set(config.SpatialId()); } diff --git a/modules/video_coding/svc/scalability_structure_full_svc_unittest.cc b/modules/video_coding/svc/scalability_structure_full_svc_unittest.cc index 9ccbe21f75..1c0a8be8f1 100644 --- a/modules/video_coding/svc/scalability_structure_full_svc_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_full_svc_unittest.cc @@ -21,6 +21,27 @@ namespace { using ::testing::IsEmpty; using ::testing::SizeIs; +TEST(ScalabilityStructureL3T3Test, SkipT0FrameByEncoderKeepsReferencesValid) { + std::vector frames; + ScalabilityStructureL3T3 structure; + ScalabilityStructureWrapper wrapper(structure); + + // Only S0T0 decode target is enabled. + structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/1, /*s1=*/0)); + // Encoder generates S0T0 key frame. + wrapper.GenerateFrames(/*num_temporal_units=*/1, frames); + EXPECT_THAT(frames, SizeIs(1)); + // Spatial layers 1 is enabled. + structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/1, /*s1=*/1)); + // Encoder tries to generate S0T0 and S1T0 delta frames but they are dropped. + structure.NextFrameConfig(/*restart=*/false); + // Encoder successfully generates S0T0 and S1T0 delta frames. + wrapper.GenerateFrames(/*num_temporal_units=*/1, frames); + EXPECT_THAT(frames, SizeIs(3)); + + EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames)); +} + TEST(ScalabilityStructureL3T3Test, SkipS1T1FrameKeepsStructureValid) { ScalabilityStructureL3T3 structure; ScalabilityStructureWrapper wrapper(structure); diff --git a/modules/video_coding/svc/scalability_structure_key_svc.cc b/modules/video_coding/svc/scalability_structure_key_svc.cc index 1cee80e84b..0ef7e8f156 100644 --- a/modules/video_coding/svc/scalability_structure_key_svc.cc +++ b/modules/video_coding/svc/scalability_structure_key_svc.cc @@ -51,6 +51,7 @@ ScalabilityStructureKeySvc::StreamConfig() const { result.scaling_factor_num[sid - 1] = 1; result.scaling_factor_den[sid - 1] = 2 * result.scaling_factor_den[sid]; } + result.uses_reference_scaling = true; return result; } @@ -197,7 +198,7 @@ ScalabilityStructureKeySvc::NextPattern(FramePattern last_pattern) const { } return kDeltaT0; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kNone; } @@ -226,7 +227,7 @@ ScalabilityStructureKeySvc::NextFrameConfig(bool restart) { case kNone: break; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return {}; } diff --git a/modules/video_coding/svc/scalability_structure_l2t2_key_shift.cc b/modules/video_coding/svc/scalability_structure_l2t2_key_shift.cc index c53ff8f07b..4d15942d3e 100644 --- a/modules/video_coding/svc/scalability_structure_l2t2_key_shift.cc +++ b/modules/video_coding/svc/scalability_structure_l2t2_key_shift.cc @@ -51,6 +51,7 @@ ScalabilityStructureL2T2KeyShift::StreamConfig() const { result.num_temporal_layers = 2; result.scaling_factor_num[0] = 1; result.scaling_factor_den[0] = 2; + result.uses_reference_scaling = true; return result; } diff --git a/modules/video_coding/svc/scalability_structure_simulcast.cc b/modules/video_coding/svc/scalability_structure_simulcast.cc index c236066736..e5fa4c4368 100644 --- a/modules/video_coding/svc/scalability_structure_simulcast.cc +++ b/modules/video_coding/svc/scalability_structure_simulcast.cc @@ -65,6 +65,7 @@ ScalabilityStructureSimulcast::StreamConfig() const { result.scaling_factor_num[sid - 1] = 1; result.scaling_factor_den[sid - 1] = 2 * result.scaling_factor_den[sid]; } + result.uses_reference_scaling = false; return result; } @@ -105,7 +106,7 @@ ScalabilityStructureSimulcast::NextPattern() const { } return kDeltaT0; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kDeltaT0; } @@ -183,7 +184,7 @@ ScalabilityStructureSimulcast::NextFrameConfig(bool restart) { } break; case kNone: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } diff --git a/modules/video_coding/svc/scalability_structure_unittest.cc b/modules/video_coding/svc/scalability_structure_unittest.cc index 8bd933be5d..9368f57db7 100644 --- a/modules/video_coding/svc/scalability_structure_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_unittest.cc @@ -16,6 +16,7 @@ #include #include "absl/types/optional.h" +#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "modules/video_coding/svc/create_scalability_structure.h" #include "modules/video_coding/svc/scalability_structure_test_helpers.h" @@ -29,12 +30,14 @@ namespace { using ::testing::AllOf; using ::testing::Contains; using ::testing::Each; +using ::testing::ElementsAreArray; using ::testing::Field; using ::testing::Ge; using ::testing::IsEmpty; using ::testing::Le; using ::testing::Lt; using ::testing::Not; +using ::testing::NotNull; using ::testing::SizeIs; using ::testing::TestWithParam; using ::testing::Values; @@ -50,6 +53,28 @@ struct SvcTestParam { class ScalabilityStructureTest : public TestWithParam {}; +TEST_P(ScalabilityStructureTest, + StaticConfigMatchesConfigReturnedByController) { + std::unique_ptr controller = + CreateScalabilityStructure(GetParam().name); + absl::optional static_config = + ScalabilityStructureConfig(GetParam().name); + ASSERT_THAT(controller, NotNull()); + ASSERT_NE(static_config, absl::nullopt); + ScalableVideoController::StreamLayersConfig config = + controller->StreamConfig(); + EXPECT_EQ(config.num_spatial_layers, static_config->num_spatial_layers); + EXPECT_EQ(config.num_temporal_layers, static_config->num_temporal_layers); + EXPECT_THAT( + rtc::MakeArrayView(config.scaling_factor_num, config.num_spatial_layers), + ElementsAreArray(static_config->scaling_factor_num, + static_config->num_spatial_layers)); + EXPECT_THAT( + rtc::MakeArrayView(config.scaling_factor_den, config.num_spatial_layers), + ElementsAreArray(static_config->scaling_factor_den, + static_config->num_spatial_layers)); +} + TEST_P(ScalabilityStructureTest, NumberOfDecodeTargetsAndChainsAreInRangeAndConsistent) { FrameDependencyStructure structure = diff --git a/modules/video_coding/svc/scalable_video_controller.h b/modules/video_coding/svc/scalable_video_controller.h index d2d8486863..c7362657ec 100644 --- a/modules/video_coding/svc/scalable_video_controller.h +++ b/modules/video_coding/svc/scalable_video_controller.h @@ -27,6 +27,8 @@ class ScalableVideoController { struct StreamLayersConfig { int num_spatial_layers = 1; int num_temporal_layers = 1; + // Indicates if frames can reference frames of a different resolution. + bool uses_reference_scaling = true; // Spatial layers scaling. Frames with spatial_id = i expected to be encoded // with original_resolution * scaling_factor_num[i] / scaling_factor_den[i]. int scaling_factor_num[DependencyDescriptor::kMaxSpatialIds] = {1, 1, 1, 1}; diff --git a/modules/video_coding/svc/scalable_video_controller_no_layering.cc b/modules/video_coding/svc/scalable_video_controller_no_layering.cc index 3934e57804..a9d530dd9d 100644 --- a/modules/video_coding/svc/scalable_video_controller_no_layering.cc +++ b/modules/video_coding/svc/scalable_video_controller_no_layering.cc @@ -25,6 +25,7 @@ ScalableVideoControllerNoLayering::StreamConfig() const { StreamLayersConfig result; result.num_spatial_layers = 1; result.num_temporal_layers = 1; + result.uses_reference_scaling = false; return result; } diff --git a/modules/video_coding/test/stream_generator.h b/modules/video_coding/test/stream_generator.h index f542bab3fc..ddb23ebb76 100644 --- a/modules/video_coding/test/stream_generator.h +++ b/modules/video_coding/test/stream_generator.h @@ -15,7 +15,6 @@ #include #include "modules/video_coding/packet.h" -#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -29,6 +28,10 @@ const int kDefaultFramePeriodMs = 1000 / kDefaultFrameRate; class StreamGenerator { public: StreamGenerator(uint16_t start_seq_num, int64_t current_time); + + StreamGenerator(const StreamGenerator&) = delete; + StreamGenerator& operator=(const StreamGenerator&) = delete; + void Init(uint16_t start_seq_num, int64_t current_time); // `time_ms` denotes the timestamp you want to put on the frame, and the unit @@ -64,8 +67,6 @@ class StreamGenerator { uint16_t sequence_number_; int64_t start_time_; uint8_t packet_buffer_[kMaxPacketSize]; - - RTC_DISALLOW_COPY_AND_ASSIGN(StreamGenerator); }; } // namespace webrtc diff --git a/modules/video_coding/timing.cc b/modules/video_coding/timing.cc index b4a712b8b8..89159f6a3a 100644 --- a/modules/video_coding/timing.cc +++ b/modules/video_coding/timing.cc @@ -10,32 +10,37 @@ #include "modules/video_coding/timing.h" - #include +#include "api/units/time_delta.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/time/timestamp_extrapolator.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/field_trial.h" namespace webrtc { +namespace { + +// Default pacing that is used for the low-latency renderer path. +constexpr TimeDelta kZeroPlayoutDelayDefaultMinPacing = TimeDelta::Millis(8); +} // namespace VCMTiming::VCMTiming(Clock* clock) : clock_(clock), - ts_extrapolator_(std::make_unique( - clock_->TimeInMilliseconds())), + ts_extrapolator_( + std::make_unique(clock_->CurrentTime())), codec_timer_(std::make_unique()), - render_delay_ms_(kDefaultRenderDelayMs), - min_playout_delay_ms_(0), - max_playout_delay_ms_(10000), - jitter_delay_ms_(0), - current_delay_ms_(0), + render_delay_(kDefaultRenderDelay), + min_playout_delay_(TimeDelta::Zero()), + max_playout_delay_(TimeDelta::Seconds(10)), + jitter_delay_(TimeDelta::Zero()), + current_delay_(TimeDelta::Zero()), prev_frame_timestamp_(0), - timing_frame_info_(), num_decoded_frames_(0), low_latency_renderer_enabled_("enabled", true), - zero_playout_delay_min_pacing_("min_pacing", TimeDelta::Millis(0)), - last_decode_scheduled_ts_(0) { + zero_playout_delay_min_pacing_("min_pacing", + kZeroPlayoutDelayDefaultMinPacing), + last_decode_scheduled_(Timestamp::Zero()) { ParseFieldTrial({&low_latency_renderer_enabled_}, field_trial::FindFullName("WebRTC-LowLatencyRenderer")); ParseFieldTrial({&zero_playout_delay_min_pacing_}, @@ -44,218 +49,208 @@ VCMTiming::VCMTiming(Clock* clock) void VCMTiming::Reset() { MutexLock lock(&mutex_); - ts_extrapolator_->Reset(clock_->TimeInMilliseconds()); + ts_extrapolator_->Reset(clock_->CurrentTime()); codec_timer_ = std::make_unique(); - render_delay_ms_ = kDefaultRenderDelayMs; - min_playout_delay_ms_ = 0; - jitter_delay_ms_ = 0; - current_delay_ms_ = 0; + render_delay_ = kDefaultRenderDelay; + min_playout_delay_ = TimeDelta::Zero(); + jitter_delay_ = TimeDelta::Zero(); + current_delay_ = TimeDelta::Zero(); prev_frame_timestamp_ = 0; } -void VCMTiming::set_render_delay(int render_delay_ms) { +void VCMTiming::set_render_delay(TimeDelta render_delay) { MutexLock lock(&mutex_); - render_delay_ms_ = render_delay_ms; + render_delay_ = render_delay; } -void VCMTiming::set_min_playout_delay(int min_playout_delay_ms) { +void VCMTiming::set_min_playout_delay(TimeDelta min_playout_delay) { MutexLock lock(&mutex_); - min_playout_delay_ms_ = min_playout_delay_ms; + min_playout_delay_ = min_playout_delay; } -int VCMTiming::min_playout_delay() { +TimeDelta VCMTiming::min_playout_delay() { MutexLock lock(&mutex_); - return min_playout_delay_ms_; + return min_playout_delay_; } -void VCMTiming::set_max_playout_delay(int max_playout_delay_ms) { +void VCMTiming::set_max_playout_delay(TimeDelta max_playout_delay) { MutexLock lock(&mutex_); - max_playout_delay_ms_ = max_playout_delay_ms; + max_playout_delay_ = max_playout_delay; } -int VCMTiming::max_playout_delay() { +TimeDelta VCMTiming::max_playout_delay() { MutexLock lock(&mutex_); - return max_playout_delay_ms_; + return max_playout_delay_; } -void VCMTiming::SetJitterDelay(int jitter_delay_ms) { +void VCMTiming::SetJitterDelay(TimeDelta jitter_delay) { MutexLock lock(&mutex_); - if (jitter_delay_ms != jitter_delay_ms_) { - jitter_delay_ms_ = jitter_delay_ms; + if (jitter_delay != jitter_delay_) { + jitter_delay_ = jitter_delay; // When in initial state, set current delay to minimum delay. - if (current_delay_ms_ == 0) { - current_delay_ms_ = jitter_delay_ms_; + if (current_delay_.IsZero()) { + current_delay_ = jitter_delay_; } } } void VCMTiming::UpdateCurrentDelay(uint32_t frame_timestamp) { MutexLock lock(&mutex_); - int target_delay_ms = TargetDelayInternal(); + TimeDelta target_delay = TargetDelayInternal(); - if (current_delay_ms_ == 0) { + if (current_delay_.IsZero()) { // Not initialized, set current delay to target. - current_delay_ms_ = target_delay_ms; - } else if (target_delay_ms != current_delay_ms_) { - int64_t delay_diff_ms = - static_cast(target_delay_ms) - current_delay_ms_; + current_delay_ = target_delay; + } else if (target_delay != current_delay_) { + TimeDelta delay_diff = target_delay - current_delay_; // Never change the delay with more than 100 ms every second. If we're // changing the delay in too large steps we will get noticeable freezes. By // limiting the change we can increase the delay in smaller steps, which // will be experienced as the video is played in slow motion. When lowering // the delay the video will be played at a faster pace. - int64_t max_change_ms = 0; + TimeDelta max_change = TimeDelta::Zero(); if (frame_timestamp < 0x0000ffff && prev_frame_timestamp_ > 0xffff0000) { // wrap - max_change_ms = kDelayMaxChangeMsPerS * - (frame_timestamp + (static_cast(1) << 32) - - prev_frame_timestamp_) / - 90000; + max_change = + TimeDelta::Millis(kDelayMaxChangeMsPerS * + (frame_timestamp + (static_cast(1) << 32) - + prev_frame_timestamp_) / + 90000); } else { - max_change_ms = kDelayMaxChangeMsPerS * - (frame_timestamp - prev_frame_timestamp_) / 90000; + max_change = + TimeDelta::Millis(kDelayMaxChangeMsPerS * + (frame_timestamp - prev_frame_timestamp_) / 90000); } - if (max_change_ms <= 0) { + if (max_change <= TimeDelta::Zero()) { // Any changes less than 1 ms are truncated and will be postponed. // Negative change will be due to reordering and should be ignored. return; } - delay_diff_ms = std::max(delay_diff_ms, -max_change_ms); - delay_diff_ms = std::min(delay_diff_ms, max_change_ms); + delay_diff = std::max(delay_diff, -max_change); + delay_diff = std::min(delay_diff, max_change); - current_delay_ms_ = current_delay_ms_ + delay_diff_ms; + current_delay_ = current_delay_ + delay_diff; } prev_frame_timestamp_ = frame_timestamp; } -void VCMTiming::UpdateCurrentDelay(int64_t render_time_ms, - int64_t actual_decode_time_ms) { +void VCMTiming::UpdateCurrentDelay(Timestamp render_time, + Timestamp actual_decode_time) { MutexLock lock(&mutex_); - uint32_t target_delay_ms = TargetDelayInternal(); - int64_t delayed_ms = - actual_decode_time_ms - - (render_time_ms - RequiredDecodeTimeMs() - render_delay_ms_); - if (delayed_ms < 0) { + TimeDelta target_delay = TargetDelayInternal(); + TimeDelta delayed = + (actual_decode_time - render_time) + RequiredDecodeTime() + render_delay_; + if (delayed < TimeDelta::Zero()) { return; } - if (current_delay_ms_ + delayed_ms <= target_delay_ms) { - current_delay_ms_ += delayed_ms; + if (current_delay_ + delayed <= target_delay) { + current_delay_ += delayed; } else { - current_delay_ms_ = target_delay_ms; + current_delay_ = target_delay; } } -void VCMTiming::StopDecodeTimer(uint32_t /*time_stamp*/, - int32_t decode_time_ms, - int64_t now_ms, - int64_t /*render_time_ms*/) { - StopDecodeTimer(decode_time_ms, now_ms); -} - -void VCMTiming::StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms) { +void VCMTiming::StopDecodeTimer(TimeDelta decode_time, Timestamp now) { MutexLock lock(&mutex_); - codec_timer_->AddTiming(decode_time_ms, now_ms); - RTC_DCHECK_GE(decode_time_ms, 0); + codec_timer_->AddTiming(decode_time.ms(), now.ms()); + RTC_DCHECK_GE(decode_time, TimeDelta::Zero()); ++num_decoded_frames_; } -void VCMTiming::IncomingTimestamp(uint32_t time_stamp, int64_t now_ms) { +void VCMTiming::IncomingTimestamp(uint32_t rtp_timestamp, Timestamp now) { MutexLock lock(&mutex_); - ts_extrapolator_->Update(now_ms, time_stamp); + ts_extrapolator_->Update(now, rtp_timestamp); } -int64_t VCMTiming::RenderTimeMs(uint32_t frame_timestamp, - int64_t now_ms) const { +Timestamp VCMTiming::RenderTime(uint32_t frame_timestamp, Timestamp now) const { MutexLock lock(&mutex_); - return RenderTimeMsInternal(frame_timestamp, now_ms); + return RenderTimeInternal(frame_timestamp, now); } void VCMTiming::SetLastDecodeScheduledTimestamp( - int64_t last_decode_scheduled_ts) { + Timestamp last_decode_scheduled) { MutexLock lock(&mutex_); - last_decode_scheduled_ts_ = last_decode_scheduled_ts; + last_decode_scheduled_ = last_decode_scheduled; } -int64_t VCMTiming::RenderTimeMsInternal(uint32_t frame_timestamp, - int64_t now_ms) const { - constexpr int kLowLatencyRendererMaxPlayoutDelayMs = 500; - if (min_playout_delay_ms_ == 0 && - (max_playout_delay_ms_ == 0 || +Timestamp VCMTiming::RenderTimeInternal(uint32_t frame_timestamp, + Timestamp now) const { + constexpr TimeDelta kLowLatencyRendererMaxPlayoutDelay = + TimeDelta::Millis(500); + if (min_playout_delay_.IsZero() && + (max_playout_delay_.IsZero() || (low_latency_renderer_enabled_ && - max_playout_delay_ms_ <= kLowLatencyRendererMaxPlayoutDelayMs))) { + max_playout_delay_ <= kLowLatencyRendererMaxPlayoutDelay))) { // Render as soon as possible or with low-latency renderer algorithm. - return 0; + return Timestamp::Zero(); } // Note that TimestampExtrapolator::ExtrapolateLocalTime is not a const // method; it mutates the object's wraparound state. - int64_t estimated_complete_time_ms = - ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp); - if (estimated_complete_time_ms == -1) { - estimated_complete_time_ms = now_ms; - } + Timestamp estimated_complete_time = + ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp).value_or(now); - // Make sure the actual delay stays in the range of `min_playout_delay_ms_` - // and `max_playout_delay_ms_`. - int actual_delay = std::max(current_delay_ms_, min_playout_delay_ms_); - actual_delay = std::min(actual_delay, max_playout_delay_ms_); - return estimated_complete_time_ms + actual_delay; + // Make sure the actual delay stays in the range of `min_playout_delay_` + // and `max_playout_delay_`. + TimeDelta actual_delay = + current_delay_.Clamped(min_playout_delay_, max_playout_delay_); + return estimated_complete_time + actual_delay; } -int VCMTiming::RequiredDecodeTimeMs() const { +TimeDelta VCMTiming::RequiredDecodeTime() const { const int decode_time_ms = codec_timer_->RequiredDecodeTimeMs(); RTC_DCHECK_GE(decode_time_ms, 0); - return decode_time_ms; + return TimeDelta::Millis(decode_time_ms); } -int64_t VCMTiming::MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const { +TimeDelta VCMTiming::MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const { MutexLock lock(&mutex_); - if (render_time_ms == 0 && zero_playout_delay_min_pacing_->us() > 0 && - min_playout_delay_ms_ == 0 && max_playout_delay_ms_ > 0) { - // `render_time_ms` == 0 indicates that the frame should be decoded and + if (render_time.IsZero() && zero_playout_delay_min_pacing_->us() > 0 && + min_playout_delay_.IsZero() && max_playout_delay_ > TimeDelta::Zero()) { + // `render_time` == 0 indicates that the frame should be decoded and // rendered as soon as possible. However, the decoder can be choked if too // many frames are sent at once. Therefore, limit the interframe delay to // |zero_playout_delay_min_pacing_| unless too many frames are queued in // which case the frames are sent to the decoder at once. if (too_many_frames_queued) { - return 0; + return TimeDelta::Zero(); } - int64_t earliest_next_decode_start_time = - last_decode_scheduled_ts_ + zero_playout_delay_min_pacing_->ms(); - int64_t max_wait_time_ms = now_ms >= earliest_next_decode_start_time - ? 0 - : earliest_next_decode_start_time - now_ms; - return max_wait_time_ms; + Timestamp earliest_next_decode_start_time = + last_decode_scheduled_ + zero_playout_delay_min_pacing_; + TimeDelta max_wait_time = now >= earliest_next_decode_start_time + ? TimeDelta::Zero() + : earliest_next_decode_start_time - now; + return max_wait_time; } - return render_time_ms - now_ms - RequiredDecodeTimeMs() - render_delay_ms_; + return render_time - now - RequiredDecodeTime() - render_delay_; } -int VCMTiming::TargetVideoDelay() const { +TimeDelta VCMTiming::TargetVideoDelay() const { MutexLock lock(&mutex_); return TargetDelayInternal(); } -int VCMTiming::TargetDelayInternal() const { - return std::max(min_playout_delay_ms_, - jitter_delay_ms_ + RequiredDecodeTimeMs() + render_delay_ms_); +TimeDelta VCMTiming::TargetDelayInternal() const { + return std::max(min_playout_delay_, + jitter_delay_ + RequiredDecodeTime() + render_delay_); } -bool VCMTiming::GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const { +bool VCMTiming::GetTimings(TimeDelta* max_decode, + TimeDelta* current_delay, + TimeDelta* target_delay, + TimeDelta* jitter_buffer, + TimeDelta* min_playout_delay, + TimeDelta* render_delay) const { MutexLock lock(&mutex_); - *max_decode_ms = RequiredDecodeTimeMs(); - *current_delay_ms = current_delay_ms_; - *target_delay_ms = TargetDelayInternal(); - *jitter_buffer_ms = jitter_delay_ms_; - *min_playout_delay_ms = min_playout_delay_ms_; - *render_delay_ms = render_delay_ms_; + *max_decode = RequiredDecodeTime(); + *current_delay = current_delay_; + *target_delay = TargetDelayInternal(); + *jitter_buffer = jitter_delay_; + *min_playout_delay = min_playout_delay_; + *render_delay = render_delay_; return (num_decoded_frames_ > 0); } diff --git a/modules/video_coding/timing.h b/modules/video_coding/timing.h index 07c12e919a..8ab2f685ec 100644 --- a/modules/video_coding/timing.h +++ b/modules/video_coding/timing.h @@ -29,6 +29,9 @@ class TimestampExtrapolator; class VCMTiming { public: + static constexpr auto kDefaultRenderDelay = TimeDelta::Millis(10); + static constexpr auto kDelayMaxChangeMsPerS = 100; + explicit VCMTiming(Clock* clock); virtual ~VCMTiming() = default; @@ -36,19 +39,19 @@ class VCMTiming { void Reset(); // Set the amount of time needed to render an image. Defaults to 10 ms. - void set_render_delay(int render_delay_ms); + void set_render_delay(TimeDelta render_delay); // Set the minimum time the video must be delayed on the receiver to // get the desired jitter buffer level. - void SetJitterDelay(int required_delay_ms); + void SetJitterDelay(TimeDelta required_delay); - // Set/get the minimum playout delay from capture to render in ms. - void set_min_playout_delay(int min_playout_delay_ms); - int min_playout_delay(); + // Set/get the minimum playout delay from capture to render. + void set_min_playout_delay(TimeDelta min_playout_delay); + TimeDelta min_playout_delay(); // Set/get the maximum playout delay from capture to render in ms. - void set_max_playout_delay(int max_playout_delay_ms); - int max_playout_delay(); + void set_max_playout_delay(TimeDelta max_playout_delay); + TimeDelta max_playout_delay(); // Increases or decreases the current delay to get closer to the target delay. // Calculates how long it has been since the previous call to this function, @@ -59,51 +62,44 @@ class VCMTiming { // Given the actual decode time in ms and the render time in ms for a frame, // this function calculates how late the frame is and increases the delay // accordingly. - void UpdateCurrentDelay(int64_t render_time_ms, - int64_t actual_decode_time_ms); + void UpdateCurrentDelay(Timestamp render_time, Timestamp actual_decode_time); // Stops the decoder timer, should be called when the decoder returns a frame // or when the decoded frame callback is called. - void StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms); - // TODO(kron): Remove once downstream projects has been changed to use the - // above function. - void StopDecodeTimer(uint32_t time_stamp, - int32_t decode_time_ms, - int64_t now_ms, - int64_t render_time_ms); + void StopDecodeTimer(TimeDelta decode_time, Timestamp now); // Used to report that a frame is passed to decoding. Updates the timestamp // filter which is used to map between timestamps and receiver system time. - void IncomingTimestamp(uint32_t time_stamp, int64_t last_packet_time_ms); + void IncomingTimestamp(uint32_t rtp_timestamp, Timestamp last_packet_time); // Returns the receiver system time when the frame with timestamp // `frame_timestamp` should be rendered, assuming that the system time - // currently is `now_ms`. - virtual int64_t RenderTimeMs(uint32_t frame_timestamp, int64_t now_ms) const; + // currently is `now`. + virtual Timestamp RenderTime(uint32_t frame_timestamp, Timestamp now) const; // Returns the maximum time in ms that we can wait for a frame to become - // complete before we must pass it to the decoder. render_time_ms==0 indicates + // complete before we must pass it to the decoder. render_time==0 indicates // that the frames should be processed as quickly as possible, with possibly // only a small delay added to make sure that the decoder is not overloaded. // In this case, the parameter too_many_frames_queued is used to signal that // the decode queue is full and that the frame should be decoded as soon as // possible. - virtual int64_t MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const; + virtual TimeDelta MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const; // Returns the current target delay which is required delay + decode time + // render delay. - int TargetVideoDelay() const; + TimeDelta TargetVideoDelay() const; // Return current timing information. Returns true if the first frame has been // decoded, false otherwise. - virtual bool GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const; + virtual bool GetTimings(TimeDelta* max_decode, + TimeDelta* current_delay, + TimeDelta* target_delay, + TimeDelta* jitter_buffer, + TimeDelta* min_playout_delay, + TimeDelta* render_delay) const; void SetTimingFrameInfo(const TimingFrameInfo& info); absl::optional GetTimingFrameInfo(); @@ -113,16 +109,13 @@ class VCMTiming { absl::optional MaxCompositionDelayInFrames() const; // Updates the last time a frame was scheduled for decoding. - void SetLastDecodeScheduledTimestamp(int64_t last_decode_scheduled_ts); - - enum { kDefaultRenderDelayMs = 10 }; - enum { kDelayMaxChangeMsPerS = 100 }; + void SetLastDecodeScheduledTimestamp(Timestamp last_decode_scheduled); protected: - int RequiredDecodeTimeMs() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - int64_t RenderTimeMsInternal(uint32_t frame_timestamp, int64_t now_ms) const + TimeDelta RequiredDecodeTime() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Timestamp RenderTimeInternal(uint32_t frame_timestamp, Timestamp now) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - int TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + TimeDelta TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); private: mutable Mutex mutex_; @@ -131,16 +124,16 @@ class VCMTiming { RTC_PT_GUARDED_BY(mutex_); std::unique_ptr codec_timer_ RTC_GUARDED_BY(mutex_) RTC_PT_GUARDED_BY(mutex_); - int render_delay_ms_ RTC_GUARDED_BY(mutex_); + TimeDelta render_delay_ RTC_GUARDED_BY(mutex_); // Best-effort playout delay range for frames from capture to render. // The receiver tries to keep the delay between `min_playout_delay_ms_` // and `max_playout_delay_ms_` taking the network jitter into account. // A special case is where min_playout_delay_ms_ = max_playout_delay_ms_ = 0, // in which case the receiver tries to play the frames as they arrive. - int min_playout_delay_ms_ RTC_GUARDED_BY(mutex_); - int max_playout_delay_ms_ RTC_GUARDED_BY(mutex_); - int jitter_delay_ms_ RTC_GUARDED_BY(mutex_); - int current_delay_ms_ RTC_GUARDED_BY(mutex_); + TimeDelta min_playout_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta max_playout_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta jitter_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta current_delay_ RTC_GUARDED_BY(mutex_); uint32_t prev_frame_timestamp_ RTC_GUARDED_BY(mutex_); absl::optional timing_frame_info_ RTC_GUARDED_BY(mutex_); size_t num_decoded_frames_ RTC_GUARDED_BY(mutex_); @@ -158,7 +151,7 @@ class VCMTiming { // Timestamp at which the last frame was scheduled to be sent to the decoder. // Used only when the RTP header extension playout delay is set to min=0 ms // which is indicated by a render time set to 0. - int64_t last_decode_scheduled_ts_ RTC_GUARDED_BY(mutex_); + Timestamp last_decode_scheduled_ RTC_GUARDED_BY(mutex_); }; } // namespace webrtc diff --git a/modules/video_coding/timing_unittest.cc b/modules/video_coding/timing_unittest.cc index cc87a3b4e0..1f5c12f7f4 100644 --- a/modules/video_coding/timing_unittest.cc +++ b/modules/video_coding/timing_unittest.cc @@ -10,13 +10,18 @@ #include "modules/video_coding/timing.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" namespace webrtc { namespace { -const int kFps = 25; + +constexpr Frequency k25Fps = Frequency::Hertz(25); +constexpr Frequency k90kHz = Frequency::KiloHertz(90); + } // namespace TEST(ReceiverTimingTest, JitterDelay) { @@ -29,187 +34,191 @@ TEST(ReceiverTimingTest, JitterDelay) { timing.Reset(); - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); - uint32_t jitter_delay_ms = 20; - timing.SetJitterDelay(jitter_delay_ms); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); + TimeDelta jitter_delay = TimeDelta::Millis(20); + timing.SetJitterDelay(jitter_delay); timing.UpdateCurrentDelay(timestamp); - timing.set_render_delay(0); - uint32_t wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + timing.set_render_delay(TimeDelta::Zero()); + auto wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // First update initializes the render time. Since we have no decode delay - // we get wait_time_ms = renderTime - now - renderDelay = jitter. - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + // we get wait_time = renderTime - now - renderDelay = jitter. + EXPECT_EQ(jitter_delay, wait_time); - jitter_delay_ms += VCMTiming::kDelayMaxChangeMsPerS + 10; + jitter_delay += TimeDelta::Millis(VCMTiming::kDelayMaxChangeMsPerS + 10); timestamp += 90000; clock.AdvanceTimeMilliseconds(1000); - timing.SetJitterDelay(jitter_delay_ms); + timing.SetJitterDelay(jitter_delay); timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // Since we gradually increase the delay we only get 100 ms every second. - EXPECT_EQ(jitter_delay_ms - 10, wait_time_ms); + EXPECT_EQ(jitter_delay - TimeDelta::Millis(10), wait_time); timestamp += 90000; clock.AdvanceTimeMilliseconds(1000); timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); // Insert frames without jitter, verify that this gives the exact wait time. const int kNumFrames = 300; for (int i = 0; i < kNumFrames; i++) { - clock.AdvanceTimeMilliseconds(1000 / kFps); - timestamp += 90000 / kFps; - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); + clock.AdvanceTime(1 / k25Fps); + timestamp += k90kHz / k25Fps; + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); } timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); // Add decode time estimates for 1 second. - const uint32_t kDecodeTimeMs = 10; - for (int i = 0; i < kFps; i++) { - clock.AdvanceTimeMilliseconds(kDecodeTimeMs); - timing.StopDecodeTimer(kDecodeTimeMs, clock.TimeInMilliseconds()); - timestamp += 90000 / kFps; - clock.AdvanceTimeMilliseconds(1000 / kFps - kDecodeTimeMs); - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); + const TimeDelta kDecodeTime = TimeDelta::Millis(10); + for (int i = 0; i < k25Fps.hertz(); i++) { + clock.AdvanceTime(kDecodeTime); + timing.StopDecodeTimer(kDecodeTime, clock.CurrentTime()); + timestamp += k90kHz / k25Fps; + clock.AdvanceTime(1 / k25Fps - kDecodeTime); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); } timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); - const int kMinTotalDelayMs = 200; - timing.set_min_playout_delay(kMinTotalDelayMs); + const TimeDelta kMinTotalDelay = TimeDelta::Millis(200); + timing.set_min_playout_delay(kMinTotalDelay); clock.AdvanceTimeMilliseconds(5000); timestamp += 5 * 90000; timing.UpdateCurrentDelay(timestamp); - const int kRenderDelayMs = 10; - timing.set_render_delay(kRenderDelayMs); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + const TimeDelta kRenderDelay = TimeDelta::Millis(10); + timing.set_render_delay(kRenderDelay); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // We should at least have kMinTotalDelayMs - decodeTime (10) - renderTime // (10) to wait. - EXPECT_EQ(kMinTotalDelayMs - kDecodeTimeMs - kRenderDelayMs, wait_time_ms); + EXPECT_EQ(kMinTotalDelay - kDecodeTime - kRenderDelay, wait_time); // The total video delay should be equal to the min total delay. - EXPECT_EQ(kMinTotalDelayMs, timing.TargetVideoDelay()); + EXPECT_EQ(kMinTotalDelay, timing.TargetVideoDelay()); // Reset playout delay. - timing.set_min_playout_delay(0); + timing.set_min_playout_delay(TimeDelta::Zero()); clock.AdvanceTimeMilliseconds(5000); timestamp += 5 * 90000; timing.UpdateCurrentDelay(timestamp); } TEST(ReceiverTimingTest, TimestampWrapAround) { - SimulatedClock clock(0); + constexpr auto kStartTime = Timestamp::Millis(1337); + SimulatedClock clock(kStartTime); VCMTiming timing(&clock); + // Provoke a wrap-around. The fifth frame will have wrapped at 25 fps. - uint32_t timestamp = 0xFFFFFFFFu - 3 * 90000 / kFps; + constexpr uint32_t kRtpTicksPerFrame = k90kHz / k25Fps; + uint32_t timestamp = 0xFFFFFFFFu - 3 * kRtpTicksPerFrame; for (int i = 0; i < 5; ++i) { - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); - clock.AdvanceTimeMilliseconds(1000 / kFps); - timestamp += 90000 / kFps; - EXPECT_EQ(3 * 1000 / kFps, - timing.RenderTimeMs(0xFFFFFFFFu, clock.TimeInMilliseconds())); - EXPECT_EQ(3 * 1000 / kFps + 1, - timing.RenderTimeMs(89u, // One ms later in 90 kHz. - clock.TimeInMilliseconds())); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); + clock.AdvanceTime(1 / k25Fps); + timestamp += kRtpTicksPerFrame; + EXPECT_EQ(kStartTime + 3 / k25Fps, + timing.RenderTime(0xFFFFFFFFu, clock.CurrentTime())); + // One ms later in 90 kHz. + EXPECT_EQ(kStartTime + 3 / k25Fps + TimeDelta::Millis(1), + timing.RenderTime(89u, clock.CurrentTime())); } } TEST(ReceiverTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) { // This is the default path when the RTP playout delay header extension is set - // to min==0. + // to min==0 and max==0. constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + constexpr TimeDelta kTimeDelta = 1 / Frequency::Hertz(60); + constexpr Timestamp kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock); timing.Reset(); + timing.set_max_playout_delay(TimeDelta::Zero()); for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + Timestamp now = clock.CurrentTime(); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); } // Another frame submitted at the same time also returns a negative max // waiting time. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + Timestamp now = clock.CurrentTime(); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); // MaxWaitingTime should be less than zero even if there's a burst of frames. - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); } TEST(ReceiverTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) { // The minimum pacing is enabled by a field trial and active if the RTP // playout delay header extension is set to min==0. - constexpr int64_t kMinPacingMs = 3; + constexpr TimeDelta kMinPacing = TimeDelta::Millis(3); test::ScopedFieldTrials override_field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + constexpr TimeDelta kTimeDelta = 1 / Frequency::Hertz(60); + constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock); timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + Timestamp now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - timing.SetLastDecodeScheduledTimestamp(now_ms); + TimeDelta::Zero()); + timing.SetLastDecodeScheduledTimestamp(now); } // Another frame submitted at the same time is paced according to the field // trial setting. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + auto now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // If there's a burst of frames, the wait time is calculated based on next // decode time. - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + kMinPacing); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // Allow a few ms to pass, this should be subtracted from the MaxWaitingTime. - constexpr int64_t kTwoMs = 2; - clock.AdvanceTimeMilliseconds(kTwoMs); - now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + constexpr TimeDelta kTwoMs = TimeDelta::Millis(2); + clock.AdvanceTime(kTwoMs); + now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs - kTwoMs); + kMinPacing - kTwoMs); // A frame is decoded at the current time, the wait time should be restored to // pacing delay. - timing.SetLastDecodeScheduledTimestamp(now_ms); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + timing.SetLastDecodeScheduledTimestamp(now); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); } TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) { @@ -218,65 +227,65 @@ TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) { test::ScopedFieldTrials override_field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; + const TimeDelta kTimeDelta = TimeDelta::Millis(1000.0 / 60.0); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock); timing.Reset(); - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - int64_t render_time_ms = now_ms + 30; + clock.AdvanceTime(kTimeDelta); + auto now = clock.CurrentTime(); + Timestamp render_time = now + TimeDelta::Millis(30); // Estimate the internal processing delay from the first frame. - int64_t estimated_processing_delay = - (render_time_ms - now_ms) - - timing.MaxWaitingTime(render_time_ms, now_ms, + TimeDelta estimated_processing_delay = + (render_time - now) - + timing.MaxWaitingTime(render_time, now, /*too_many_frames_queued=*/false); - EXPECT_GT(estimated_processing_delay, 0); + EXPECT_GT(estimated_processing_delay, TimeDelta::Zero()); // Any other frame submitted at the same time should be scheduled according to // its render time. for (int i = 0; i < 5; ++i) { - render_time_ms += kTimeDeltaMs; - EXPECT_EQ(timing.MaxWaitingTime(render_time_ms, now_ms, + render_time += kTimeDelta; + EXPECT_EQ(timing.MaxWaitingTime(render_time, now, /*too_many_frames_queued=*/false), - render_time_ms - now_ms - estimated_processing_delay); + render_time - now - estimated_processing_delay); } } -TEST(ReceiverTiminTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { +TEST(ReceiverTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { // The minimum pacing is enabled by a field trial and active if the RTP // playout delay header extension is set to min==0. - constexpr int64_t kMinPacingMs = 3; + constexpr TimeDelta kMinPacing = TimeDelta::Millis(3); test::ScopedFieldTrials override_field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + const TimeDelta kTimeDelta = TimeDelta::Millis(1000.0 / 60.0); + constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock); timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + auto now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - timing.SetLastDecodeScheduledTimestamp(now_ms); + TimeDelta::Zero()); + timing.SetLastDecodeScheduledTimestamp(now); } // Another frame submitted at the same time is paced according to the field // trial setting. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + auto now_ms = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // MaxWaitingTime returns 0 even if there's a burst of frames if // too_many_frames_queued is set to true. - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/true), - 0); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/true), - 0); + TimeDelta::Zero()); } } // namespace webrtc diff --git a/modules/video_coding/utility/bandwidth_quality_scaler.cc b/modules/video_coding/utility/bandwidth_quality_scaler.cc new file mode 100644 index 0000000000..7632100858 --- /dev/null +++ b/modules/video_coding/utility/bandwidth_quality_scaler.cc @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/utility/bandwidth_quality_scaler.h" + +#include +#include +#include +#include + +#include "api/video/video_adaptation_reason.h" +#include "api/video_codecs/video_encoder.h" +#include "rtc_base/checks.h" +#include "rtc_base/experiments/bandwidth_quality_scaler_settings.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/exp_filter.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/task_utils/to_queued_task.h" +#include "rtc_base/time_utils.h" +#include "rtc_base/weak_ptr.h" + +namespace webrtc { + +namespace { + +constexpr int kDefaultMaxWindowSizeMs = 5000; +constexpr float kHigherMaxBitrateTolerationFactor = 0.95; +constexpr float kLowerMinBitrateTolerationFactor = 0.8; +constexpr int kDefaultBitrateStateUpdateIntervalSeconds = 5; +} // namespace + +BandwidthQualityScaler::BandwidthQualityScaler( + BandwidthQualityScalerUsageHandlerInterface* handler) + : kBitrateStateUpdateInterval(TimeDelta::Seconds( + BandwidthQualityScalerSettings::ParseFromFieldTrials() + .BitrateStateUpdateInterval() + .value_or(kDefaultBitrateStateUpdateIntervalSeconds))), + handler_(handler), + encoded_bitrate_(kDefaultMaxWindowSizeMs, RateStatistics::kBpsScale), + weak_ptr_factory_(this) { + RTC_DCHECK_RUN_ON(&task_checker_); + RTC_DCHECK(handler_ != nullptr); + + StartCheckForBitrate(); +} + +BandwidthQualityScaler::~BandwidthQualityScaler() { + RTC_DCHECK_RUN_ON(&task_checker_); +} + +void BandwidthQualityScaler::StartCheckForBitrate() { + RTC_DCHECK_RUN_ON(&task_checker_); + TaskQueueBase::Current()->PostDelayedTask( + ToQueuedTask([this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), this] { + if (!this_weak_ptr) { + // The caller BandwidthQualityScaler has been deleted. + return; + } + RTC_DCHECK_RUN_ON(&task_checker_); + switch (CheckBitrate()) { + case BandwidthQualityScaler::CheckBitrateResult::kHighBitRate: { + handler_->OnReportUsageBandwidthHigh(); + last_frame_size_pixels_.reset(); + break; + } + case BandwidthQualityScaler::CheckBitrateResult::kLowBitRate: { + handler_->OnReportUsageBandwidthLow(); + last_frame_size_pixels_.reset(); + break; + } + case BandwidthQualityScaler::CheckBitrateResult::kNormalBitrate: { + break; + } + case BandwidthQualityScaler::CheckBitrateResult:: + kInsufficientSamples: { + break; + } + } + StartCheckForBitrate(); + }), + kBitrateStateUpdateInterval.ms()); +} + +void BandwidthQualityScaler::ReportEncodeInfo(int frame_size_bytes, + int64_t time_sent_in_ms, + uint32_t encoded_width, + uint32_t encoded_height) { + RTC_DCHECK_RUN_ON(&task_checker_); + last_time_sent_in_ms_ = time_sent_in_ms; + last_frame_size_pixels_ = encoded_width * encoded_height; + encoded_bitrate_.Update(frame_size_bytes, time_sent_in_ms); +} + +void BandwidthQualityScaler::SetResolutionBitrateLimits( + const std::vector& + resolution_bitrate_limits) { + if (resolution_bitrate_limits.empty()) { + resolution_bitrate_limits_ = EncoderInfoSettings:: + GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted(); + } else { + resolution_bitrate_limits_ = resolution_bitrate_limits; + } +} + +BandwidthQualityScaler::CheckBitrateResult +BandwidthQualityScaler::CheckBitrate() { + RTC_DCHECK_RUN_ON(&task_checker_); + if (!last_frame_size_pixels_.has_value() || + !last_time_sent_in_ms_.has_value()) { + return BandwidthQualityScaler::CheckBitrateResult::kInsufficientSamples; + } + + absl::optional current_bitrate_bps = + encoded_bitrate_.Rate(last_time_sent_in_ms_.value()); + if (!current_bitrate_bps.has_value()) { + // We can't get a valid bitrate due to not enough data points. + return BandwidthQualityScaler::CheckBitrateResult::kInsufficientSamples; + } + absl::optional suitable_bitrate_limit = + EncoderInfoSettings:: + GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( + last_frame_size_pixels_, resolution_bitrate_limits_); + + if (!suitable_bitrate_limit.has_value()) { + return BandwidthQualityScaler::CheckBitrateResult::kInsufficientSamples; + } + + // Multiply by toleration factor to solve the frequent adaptation due to + // critical value. + if (current_bitrate_bps > suitable_bitrate_limit->max_bitrate_bps * + kHigherMaxBitrateTolerationFactor) { + return BandwidthQualityScaler::CheckBitrateResult::kLowBitRate; + } else if (current_bitrate_bps < + suitable_bitrate_limit->min_start_bitrate_bps * + kLowerMinBitrateTolerationFactor) { + return BandwidthQualityScaler::CheckBitrateResult::kHighBitRate; + } + return BandwidthQualityScaler::CheckBitrateResult::kNormalBitrate; +} + +BandwidthQualityScalerUsageHandlerInterface:: + ~BandwidthQualityScalerUsageHandlerInterface() {} +} // namespace webrtc diff --git a/modules/video_coding/utility/bandwidth_quality_scaler.h b/modules/video_coding/utility/bandwidth_quality_scaler.h new file mode 100644 index 0000000000..2816e588be --- /dev/null +++ b/modules/video_coding/utility/bandwidth_quality_scaler.h @@ -0,0 +1,95 @@ +/* + * 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_VIDEO_CODING_UTILITY_BANDWIDTH_QUALITY_SCALER_H_ +#define MODULES_VIDEO_CODING_UTILITY_BANDWIDTH_QUALITY_SCALER_H_ + +#include +#include + +#include +#include + +#include "absl/types/optional.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/video_codecs/video_encoder.h" +#include "rtc_base/experiments/encoder_info_settings.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/exp_filter.h" +#include "rtc_base/rate_statistics.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/task_queue.h" +#include "rtc_base/weak_ptr.h" + +namespace webrtc { + +class BandwidthQualityScalerUsageHandlerInterface { + public: + virtual ~BandwidthQualityScalerUsageHandlerInterface(); + + virtual void OnReportUsageBandwidthHigh() = 0; + virtual void OnReportUsageBandwidthLow() = 0; +}; + +// BandwidthQualityScaler runs asynchronously and monitors bandwidth values of +// encoded frames. It holds a reference to a +// BandwidthQualityScalerUsageHandlerInterface implementation to signal an +// overuse or underuse of bandwidth (which indicate a desire to scale the video +// stream down or up). +class BandwidthQualityScaler { + public: + explicit BandwidthQualityScaler( + BandwidthQualityScalerUsageHandlerInterface* handler); + virtual ~BandwidthQualityScaler(); + + void ReportEncodeInfo(int frame_size_bytes, + int64_t time_sent_in_ms, + uint32_t encoded_width, + uint32_t encoded_height); + + // We prioritise to using the |resolution_bitrate_limits| provided by the + // current decoder. If not provided, we will use the default data by + // GetDefaultResolutionBitrateLimits(). + void SetResolutionBitrateLimits( + const std::vector& + resolution_bitrate_limits); + + const TimeDelta kBitrateStateUpdateInterval; + + private: + enum class CheckBitrateResult { + kInsufficientSamples, + kNormalBitrate, + kHighBitRate, + kLowBitRate, + }; + + // We will periodically check encode bitrate, this function will make + // resolution up or down decisions and report the decision to the adapter. + void StartCheckForBitrate(); + CheckBitrateResult CheckBitrate(); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker task_checker_; + BandwidthQualityScalerUsageHandlerInterface* const handler_ + RTC_GUARDED_BY(&task_checker_); + + absl::optional last_time_sent_in_ms_ RTC_GUARDED_BY(&task_checker_); + RateStatistics encoded_bitrate_ RTC_GUARDED_BY(&task_checker_); + absl::optional last_frame_size_pixels_ RTC_GUARDED_BY(&task_checker_); + rtc::WeakPtrFactory weak_ptr_factory_; + + std::vector resolution_bitrate_limits_; +}; + +} // namespace webrtc +#endif // MODULES_VIDEO_CODING_UTILITY_BANDWIDTH_QUALITY_SCALER_H_ diff --git a/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc b/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc new file mode 100644 index 0000000000..7f1263ceda --- /dev/null +++ b/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/utility/bandwidth_quality_scaler.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/experiments/encoder_info_settings.h" +#include "rtc_base/task_queue_for_test.h" +#include "rtc_base/time_utils.h" +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +constexpr int kFramerateFps = 30; +constexpr int kDefaultBitrateStateUpdateIntervalSeconds = 5; +constexpr int kDefaultEncodeDeltaTimeMs = 33; // 1/30(s) => 33(ms) + +} // namespace + +class FakeBandwidthQualityScalerHandler + : public BandwidthQualityScalerUsageHandlerInterface { + public: + ~FakeBandwidthQualityScalerHandler() override = default; + void OnReportUsageBandwidthHigh() override { + adapt_down_event_count_++; + event_.Set(); + } + + void OnReportUsageBandwidthLow() override { + adapt_up_event_count_++; + event_.Set(); + } + + rtc::Event event_; + int adapt_up_event_count_ = 0; + int adapt_down_event_count_ = 0; +}; + +class BandwidthQualityScalerUnderTest : public BandwidthQualityScaler { + public: + explicit BandwidthQualityScalerUnderTest( + BandwidthQualityScalerUsageHandlerInterface* handler) + : BandwidthQualityScaler(handler) {} + + int GetBitrateStateUpdateIntervalMs() { + return this->kBitrateStateUpdateInterval.ms() + 200; + } +}; + +class BandwidthQualityScalerTest + : public ::testing::Test, + public ::testing::WithParamInterface { + protected: + enum ScaleDirection { + kKeepScaleNormalBandwidth, + kKeepScaleAboveMaxBandwidth, + kKeepScaleUnderMinBandwidth, + }; + + enum FrameType { + kKeyFrame, + kNormalFrame, + kNormalFrame_Overuse, + kNormalFrame_Underuse, + }; + struct FrameConfig { + FrameConfig(int frame_num, + FrameType frame_type, + int actual_width, + int actual_height) + : frame_num(frame_num), + frame_type(frame_type), + actual_width(actual_width), + actual_height(actual_height) {} + + int frame_num; + FrameType frame_type; + int actual_width; + int actual_height; + }; + + BandwidthQualityScalerTest() + : scoped_field_trial_(GetParam()), + task_queue_("BandwidthQualityScalerTestQueue"), + handler_(std::make_unique()) { + task_queue_.SendTask( + [this] { + bandwidth_quality_scaler_ = + std::unique_ptr( + new BandwidthQualityScalerUnderTest(handler_.get())); + bandwidth_quality_scaler_->SetResolutionBitrateLimits( + EncoderInfoSettings:: + GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted()); + // Only for testing. Set first_timestamp_ in RateStatistics to 0. + bandwidth_quality_scaler_->ReportEncodeInfo(0, 0, 0, 0); + }, + RTC_FROM_HERE); + } + + ~BandwidthQualityScalerTest() { + task_queue_.SendTask([this] { bandwidth_quality_scaler_ = nullptr; }, + RTC_FROM_HERE); + } + + int GetFrameSizeBytes( + const FrameConfig& config, + const VideoEncoder::ResolutionBitrateLimits& bitrate_limits) { + int scale = 8 * kFramerateFps; + switch (config.frame_type) { + case FrameType::kKeyFrame: { + // 4 is experimental value. Based on the test, the number of bytes of + // the key frame is about four times of the normal frame + return bitrate_limits.max_bitrate_bps * 4 / scale; + } + case FrameType::kNormalFrame_Overuse: { + return bitrate_limits.max_bitrate_bps * 3 / 2 / scale; + } + case FrameType::kNormalFrame_Underuse: { + return bitrate_limits.min_start_bitrate_bps * 3 / 4 / scale; + } + case FrameType::kNormalFrame: { + return (bitrate_limits.max_bitrate_bps + + bitrate_limits.min_start_bitrate_bps) / + 2 / scale; + } + } + return -1; + } + + absl::optional + GetDefaultSuitableBitrateLimit(int frame_size_pixels) { + return EncoderInfoSettings:: + GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( + frame_size_pixels, + EncoderInfoSettings:: + GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted()); + } + + void TriggerBandwidthQualityScalerTest( + const std::vector& frame_configs) { + task_queue_.SendTask( + [frame_configs, this] { + RTC_CHECK(!frame_configs.empty()); + + int total_frame_nums = 0; + for (const FrameConfig& frame_config : frame_configs) { + total_frame_nums += frame_config.frame_num; + } + + EXPECT_EQ(kFramerateFps * kDefaultBitrateStateUpdateIntervalSeconds, + total_frame_nums); + + uint32_t time_send_to_scaler_ms_ = rtc::TimeMillis(); + for (size_t i = 0; i < frame_configs.size(); ++i) { + const FrameConfig& config = frame_configs[i]; + absl::optional + suitable_bitrate = GetDefaultSuitableBitrateLimit( + config.actual_width * config.actual_height); + EXPECT_TRUE(suitable_bitrate); + for (int j = 0; j <= config.frame_num; ++j) { + time_send_to_scaler_ms_ += kDefaultEncodeDeltaTimeMs; + int frame_size_bytes = + GetFrameSizeBytes(config, suitable_bitrate.value()); + RTC_CHECK(frame_size_bytes > 0); + bandwidth_quality_scaler_->ReportEncodeInfo( + frame_size_bytes, time_send_to_scaler_ms_, + config.actual_width, config.actual_height); + } + } + }, + RTC_FROM_HERE); + } + + test::ScopedFieldTrials scoped_field_trial_; + TaskQueueForTest task_queue_; + std::unique_ptr bandwidth_quality_scaler_; + std::unique_ptr handler_; +}; + +INSTANTIATE_TEST_SUITE_P( + FieldTrials, + BandwidthQualityScalerTest, + ::testing::Values("WebRTC-Video-BandwidthQualityScalerSettings/" + "bitrate_state_update_interval_s_:1/", + "WebRTC-Video-BandwidthQualityScalerSettings/" + "bitrate_state_update_interval_s_:2/")); + +TEST_P(BandwidthQualityScalerTest, AllNormalFrame_640x360) { + const std::vector frame_configs{ + FrameConfig(150, FrameType::kNormalFrame, 640, 360)}; + TriggerBandwidthQualityScalerTest(frame_configs); + + // When resolution is 640*360, experimental working bitrate range is + // [500000,800000] bps. Encoded bitrate is 654253, so it falls in the range + // without any operation(up/down). + EXPECT_FALSE(handler_->event_.Wait( + bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); + EXPECT_EQ(0, handler_->adapt_down_event_count_); + EXPECT_EQ(0, handler_->adapt_up_event_count_); +} + +TEST_P(BandwidthQualityScalerTest, AllNoramlFrame_AboveMaxBandwidth_640x360) { + const std::vector frame_configs{ + FrameConfig(150, FrameType::kNormalFrame_Overuse, 640, 360)}; + TriggerBandwidthQualityScalerTest(frame_configs); + + // When resolution is 640*360, experimental working bitrate range is + // [500000,800000] bps. Encoded bitrate is 1208000 > 800000 * 0.95, so it + // triggers adapt_up_event_count_. + EXPECT_TRUE(handler_->event_.Wait( + bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); + EXPECT_EQ(0, handler_->adapt_down_event_count_); + EXPECT_EQ(1, handler_->adapt_up_event_count_); +} + +TEST_P(BandwidthQualityScalerTest, AllNormalFrame_Underuse_640x360) { + const std::vector frame_configs{ + FrameConfig(150, FrameType::kNormalFrame_Underuse, 640, 360)}; + TriggerBandwidthQualityScalerTest(frame_configs); + + // When resolution is 640*360, experimental working bitrate range is + // [500000,800000] bps. Encoded bitrate is 377379 < 500000 * 0.8, so it + // triggers adapt_down_event_count_. + EXPECT_TRUE(handler_->event_.Wait( + bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); + EXPECT_EQ(1, handler_->adapt_down_event_count_); + EXPECT_EQ(0, handler_->adapt_up_event_count_); +} + +TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest1_640x360) { + const std::vector frame_configs{ + FrameConfig(5, FrameType::kNormalFrame_Underuse, 640, 360), + FrameConfig(110, FrameType::kNormalFrame, 640, 360), + FrameConfig(20, FrameType::kNormalFrame_Overuse, 640, 360), + FrameConfig(15, FrameType::kKeyFrame, 640, 360), + }; + TriggerBandwidthQualityScalerTest(frame_configs); + + // When resolution is 640*360, experimental working bitrate range is + // [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it + // triggers adapt_up_event_count_. + EXPECT_TRUE(handler_->event_.Wait( + bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); + EXPECT_EQ(0, handler_->adapt_down_event_count_); + EXPECT_EQ(1, handler_->adapt_up_event_count_); +} + +TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest2_640x360) { + const std::vector frame_configs{ + FrameConfig(10, FrameType::kNormalFrame_Underuse, 640, 360), + FrameConfig(50, FrameType::kNormalFrame, 640, 360), + FrameConfig(5, FrameType::kKeyFrame, 640, 360), + FrameConfig(85, FrameType::kNormalFrame_Overuse, 640, 360), + }; + TriggerBandwidthQualityScalerTest(frame_configs); + + // When resolution is 640*360, experimental working bitrate range is + // [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it + // triggers adapt_up_event_count_. + EXPECT_TRUE(handler_->event_.Wait( + bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); + EXPECT_EQ(0, handler_->adapt_down_event_count_); + EXPECT_EQ(1, handler_->adapt_up_event_count_); +} + +} // namespace webrtc diff --git a/modules/video_coding/utility/decoded_frames_history.cc b/modules/video_coding/utility/decoded_frames_history.cc index 005bb26ea6..1138aa8448 100644 --- a/modules/video_coding/utility/decoded_frames_history.cc +++ b/modules/video_coding/utility/decoded_frames_history.cc @@ -50,7 +50,7 @@ void DecodedFramesHistory::InsertDecoded(int64_t frame_id, uint32_t timestamp) { last_frame_id_ = frame_id; } -bool DecodedFramesHistory::WasDecoded(int64_t frame_id) { +bool DecodedFramesHistory::WasDecoded(int64_t frame_id) const { if (!last_frame_id_) return false; @@ -74,11 +74,12 @@ void DecodedFramesHistory::Clear() { last_frame_id_.reset(); } -absl::optional DecodedFramesHistory::GetLastDecodedFrameId() { +absl::optional DecodedFramesHistory::GetLastDecodedFrameId() const { return last_decoded_frame_; } -absl::optional DecodedFramesHistory::GetLastDecodedFrameTimestamp() { +absl::optional DecodedFramesHistory::GetLastDecodedFrameTimestamp() + const { return last_decoded_frame_timestamp_; } diff --git a/modules/video_coding/utility/decoded_frames_history.h b/modules/video_coding/utility/decoded_frames_history.h index 06008dc22e..9b8bf65821 100644 --- a/modules/video_coding/utility/decoded_frames_history.h +++ b/modules/video_coding/utility/decoded_frames_history.h @@ -31,12 +31,12 @@ class DecodedFramesHistory { void InsertDecoded(int64_t frame_id, uint32_t timestamp); // Query if the following (frame_id, spatial_id) pair was inserted before. // Should be at most less by window_size-1 than the last inserted frame id. - bool WasDecoded(int64_t frame_id); + bool WasDecoded(int64_t frame_id) const; void Clear(); - absl::optional GetLastDecodedFrameId(); - absl::optional GetLastDecodedFrameTimestamp(); + absl::optional GetLastDecodedFrameId() const; + absl::optional GetLastDecodedFrameTimestamp() const; private: int FrameIdToIndex(int64_t frame_id) const; diff --git a/modules/video_coding/utility/ivf_defines.h b/modules/video_coding/utility/ivf_defines.h new file mode 100644 index 0000000000..83d6691b87 --- /dev/null +++ b/modules/video_coding/utility/ivf_defines.h @@ -0,0 +1,23 @@ +/* + * 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 file contains definitions that are common to the IvfFileReader and + * IvfFileWriter classes. + */ + +#ifndef MODULES_VIDEO_CODING_UTILITY_IVF_DEFINES_H_ +#define MODULES_VIDEO_CODING_UTILITY_IVF_DEFINES_H_ + +namespace webrtc { +constexpr size_t kIvfHeaderSize = 32; +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_UTILITY_IVF_DEFINES_H_ diff --git a/modules/video_coding/utility/ivf_file_reader.cc b/modules/video_coding/utility/ivf_file_reader.cc index f326c8cb53..85d1fa00d7 100644 --- a/modules/video_coding/utility/ivf_file_reader.cc +++ b/modules/video_coding/utility/ivf_file_reader.cc @@ -15,12 +15,12 @@ #include "api/video_codecs/video_codec.h" #include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/video_coding/utility/ivf_defines.h" #include "rtc_base/logging.h" namespace webrtc { namespace { -constexpr size_t kIvfHeaderSize = 32; constexpr size_t kIvfFrameHeaderSize = 12; constexpr int kCodecTypeBytesCount = 4; @@ -104,10 +104,10 @@ bool IvfFileReader::Reset() { has_error_ = false; const char* codec_name = CodecTypeToPayloadString(codec_type_); - RTC_LOG(INFO) << "Opened IVF file with codec data of type " << codec_name - << " at resolution " << width_ << " x " << height_ << ", using " - << (using_capture_timestamps_ ? "1" : "90") - << "kHz clock resolution."; + RTC_LOG(LS_INFO) << "Opened IVF file with codec data of type " << codec_name + << " at resolution " << width_ << " x " << height_ + << ", using " << (using_capture_timestamps_ ? "1" : "90") + << "kHz clock resolution."; return true; } diff --git a/modules/video_coding/utility/ivf_file_reader.h b/modules/video_coding/utility/ivf_file_reader.h index 5e0634f9fd..75f2e3ac8c 100644 --- a/modules/video_coding/utility/ivf_file_reader.h +++ b/modules/video_coding/utility/ivf_file_reader.h @@ -26,6 +26,10 @@ class IvfFileReader { // Creates IvfFileReader. Returns nullptr if error acquired. static std::unique_ptr Create(FileWrapper file); ~IvfFileReader(); + + IvfFileReader(const IvfFileReader&) = delete; + IvfFileReader& operator=(const IvfFileReader&) = delete; + // Reinitializes reader. Returns false if any error acquired. bool Reset(); @@ -71,8 +75,6 @@ class IvfFileReader { absl::optional next_frame_header_; bool has_error_; - - RTC_DISALLOW_COPY_AND_ASSIGN(IvfFileReader); }; } // namespace webrtc diff --git a/modules/video_coding/utility/ivf_file_writer.cc b/modules/video_coding/utility/ivf_file_writer.cc index 77c90ee158..668390a78c 100644 --- a/modules/video_coding/utility/ivf_file_writer.cc +++ b/modules/video_coding/utility/ivf_file_writer.cc @@ -14,6 +14,7 @@ #include "api/video_codecs/video_codec.h" #include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/video_coding/utility/ivf_defines.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -22,7 +23,11 @@ namespace webrtc { -const size_t kIvfHeaderSize = 32; +namespace { + +constexpr int kDefaultWidth = 1280; +constexpr int kDefaultHeight = 720; +} // namespace IvfFileWriter::IvfFileWriter(FileWrapper file, size_t byte_limit) : codec_type_(kVideoCodecGeneric), @@ -122,10 +127,14 @@ bool IvfFileWriter::WriteHeader() { bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image, VideoCodecType codec_type) { - width_ = encoded_image._encodedWidth; - height_ = encoded_image._encodedHeight; - RTC_CHECK_GT(width_, 0); - RTC_CHECK_GT(height_, 0); + if (encoded_image._encodedWidth == 0 || encoded_image._encodedHeight == 0) { + width_ = kDefaultWidth; + height_ = kDefaultHeight; + } else { + width_ = encoded_image._encodedWidth; + height_ = encoded_image._encodedHeight; + } + using_capture_timestamps_ = encoded_image.Timestamp() == 0; codec_type_ = codec_type; diff --git a/modules/video_coding/utility/ivf_file_writer.h b/modules/video_coding/utility/ivf_file_writer.h index 874f60adfc..b53459b5de 100644 --- a/modules/video_coding/utility/ivf_file_writer.h +++ b/modules/video_coding/utility/ivf_file_writer.h @@ -18,7 +18,6 @@ #include "api/video/encoded_image.h" #include "api/video/video_codec_type.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/file_wrapper.h" #include "rtc_base/time_utils.h" @@ -34,6 +33,9 @@ class IvfFileWriter { size_t byte_limit); ~IvfFileWriter(); + IvfFileWriter(const IvfFileWriter&) = delete; + IvfFileWriter& operator=(const IvfFileWriter&) = delete; + bool WriteFrame(const EncodedImage& encoded_image, VideoCodecType codec_type); bool Close(); @@ -57,8 +59,6 @@ class IvfFileWriter { bool using_capture_timestamps_; rtc::TimestampWrapAroundHandler wrap_handler_; FileWrapper file_; - - RTC_DISALLOW_COPY_AND_ASSIGN(IvfFileWriter); }; } // namespace webrtc diff --git a/modules/video_coding/utility/ivf_file_writer_unittest.cc b/modules/video_coding/utility/ivf_file_writer_unittest.cc index 1a84d4441b..c5d30a1286 100644 --- a/modules/video_coding/utility/ivf_file_writer_unittest.cc +++ b/modules/video_coding/utility/ivf_file_writer_unittest.cc @@ -25,6 +25,10 @@ namespace { static const int kHeaderSize = 32; static const int kFrameHeaderSize = 12; static uint8_t dummy_payload[4] = {0, 1, 2, 3}; +// As the default parameter when the width and height of encodedImage are 0, +// the values are copied from ivf_file_writer.cc +constexpr int kDefaultWidth = 1280; +constexpr int kDefaultHeight = 720; } // namespace class IvfFileWriterTest : public ::testing::Test { @@ -196,4 +200,112 @@ TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) { out_file.Close(); } +TEST_F(IvfFileWriterTest, UseDefaultValueWhenWidthAndHeightAreZero) { + const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; + const int kWidth = 0; + const int kHeight = 0; + const int kNumFramesToWrite = 2; + const int kNumFramesToFit = 1; + + file_writer_ = IvfFileWriter::Wrap( + FileWrapper::OpenWriteOnly(file_name_), + kHeaderSize + + kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + ASSERT_TRUE(file_writer_.get()); + + ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, + kNumFramesToWrite, true)); + ASSERT_FALSE(file_writer_->Close()); + + FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_); + // When the width and height are zero, we should expect the width and height + // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and + // kHeight. + VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight, + kNumFramesToFit, true); + VerifyDummyTestFrames(&out_file, kNumFramesToFit); + + out_file.Close(); +} + +TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyWidthIsZero) { + const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; + const int kWidth = 0; + const int kHeight = 360; + const int kNumFramesToWrite = 2; + const int kNumFramesToFit = 1; + + file_writer_ = IvfFileWriter::Wrap( + FileWrapper::OpenWriteOnly(file_name_), + kHeaderSize + + kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + ASSERT_TRUE(file_writer_.get()); + + ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, + kNumFramesToWrite, true)); + ASSERT_FALSE(file_writer_->Close()); + + FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_); + // When the width and height are zero, we should expect the width and height + // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and + // kHeight. + VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight, + kNumFramesToFit, true); + VerifyDummyTestFrames(&out_file, kNumFramesToFit); + + out_file.Close(); +} + +TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyHeightIsZero) { + const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; + const int kWidth = 240; + const int kHeight = 0; + const int kNumFramesToWrite = 2; + const int kNumFramesToFit = 1; + + file_writer_ = IvfFileWriter::Wrap( + FileWrapper::OpenWriteOnly(file_name_), + kHeaderSize + + kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + ASSERT_TRUE(file_writer_.get()); + + ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, + kNumFramesToWrite, true)); + ASSERT_FALSE(file_writer_->Close()); + + FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_); + // When the width and height are zero, we should expect the width and height + // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and + // kHeight. + VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight, + kNumFramesToFit, true); + VerifyDummyTestFrames(&out_file, kNumFramesToFit); + + out_file.Close(); +} + +TEST_F(IvfFileWriterTest, UseDefaultValueWhenHeightAndWidthAreNotZero) { + const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; + const int kWidth = 360; + const int kHeight = 240; + const int kNumFramesToWrite = 2; + const int kNumFramesToFit = 1; + + file_writer_ = IvfFileWriter::Wrap( + FileWrapper::OpenWriteOnly(file_name_), + kHeaderSize + + kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + ASSERT_TRUE(file_writer_.get()); + + ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, + kNumFramesToWrite, true)); + ASSERT_FALSE(file_writer_->Close()); + + FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_); + VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true); + VerifyDummyTestFrames(&out_file, kNumFramesToFit); + + out_file.Close(); +} + } // namespace webrtc diff --git a/modules/video_coding/utility/simulcast_rate_allocator.h b/modules/video_coding/utility/simulcast_rate_allocator.h index 9b2f9696e6..6f93dbde74 100644 --- a/modules/video_coding/utility/simulcast_rate_allocator.h +++ b/modules/video_coding/utility/simulcast_rate_allocator.h @@ -19,7 +19,6 @@ #include "api/video/video_bitrate_allocation.h" #include "api/video/video_bitrate_allocator.h" #include "api/video_codecs/video_codec.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/experiments/rate_control_settings.h" #include "rtc_base/experiments/stable_target_rate_experiment.h" @@ -30,6 +29,9 @@ class SimulcastRateAllocator : public VideoBitrateAllocator { explicit SimulcastRateAllocator(const VideoCodec& codec); ~SimulcastRateAllocator() override; + SimulcastRateAllocator(const SimulcastRateAllocator&) = delete; + SimulcastRateAllocator& operator=(const SimulcastRateAllocator&) = delete; + VideoBitrateAllocation Allocate( VideoBitrateAllocationParameters parameters) override; const VideoCodec& GetCodec() const; @@ -61,8 +63,6 @@ class SimulcastRateAllocator : public VideoBitrateAllocator { const RateControlSettings rate_control_settings_; std::vector stream_enabled_; bool legacy_conference_mode_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator); }; } // namespace webrtc diff --git a/modules/video_coding/utility/simulcast_test_fixture_impl.cc b/modules/video_coding/utility/simulcast_test_fixture_impl.cc index 435f472475..0789f6ab00 100644 --- a/modules/video_coding/utility/simulcast_test_fixture_impl.cc +++ b/modules/video_coding/utility/simulcast_test_fixture_impl.cc @@ -143,7 +143,7 @@ class SimulcastTestFixtureImpl::TestDecodedImageCallback return 0; } int32_t Decoded(VideoFrame& decoded_image, int64_t decode_time_ms) override { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } void Decoded(VideoFrame& decoded_image, diff --git a/modules/video_coding/utility/simulcast_test_fixture_impl.h b/modules/video_coding/utility/simulcast_test_fixture_impl.h index a3d3fc66a8..cdfdc609d5 100644 --- a/modules/video_coding/utility/simulcast_test_fixture_impl.h +++ b/modules/video_coding/utility/simulcast_test_fixture_impl.h @@ -64,10 +64,10 @@ class SimulcastTestFixtureImpl final : public SimulcastTestFixture { void SetUpCodec(const int* temporal_layer_profile); void SetUpRateAllocator(); void SetRates(uint32_t bitrate_kbps, uint32_t fps); - void RunActiveStreamsTest(const std::vector active_streams); - void UpdateActiveStreams(const std::vector active_streams); + void RunActiveStreamsTest(std::vector active_streams); + void UpdateActiveStreams(std::vector active_streams); void ExpectStreams(VideoFrameType frame_type, - const std::vector expected_streams_active); + std::vector expected_streams_active); void ExpectStreams(VideoFrameType frame_type, int expected_video_streams); void VerifyTemporalIdxAndSyncForAllSpatialLayers( TestEncodedImageCallback* encoder_callback, diff --git a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc index 867967ddc0..bf9d51f692 100644 --- a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc +++ b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc @@ -158,7 +158,8 @@ void Vp9ReadQp(BitstreamReader& br, Vp9UncompressedHeader* frame_info) { void Vp9ReadSegmentationParams(BitstreamReader& br, Vp9UncompressedHeader* frame_info) { constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0}; - constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {1, 1, 0, 0}; + constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {true, true, false, + false}; frame_info->segmentation_enabled = br.Read(); if (!frame_info->segmentation_enabled) { diff --git a/modules/video_coding/video_codec_initializer_unittest.cc b/modules/video_coding/video_codec_initializer_unittest.cc index 6c1c2e7a38..902f991f29 100644 --- a/modules/video_coding/video_codec_initializer_unittest.cc +++ b/modules/video_coding/video_codec_initializer_unittest.cc @@ -302,8 +302,8 @@ TEST_F(VideoCodecInitializerTest, Vp9SvcAdjustedLayering) { VideoStream stream = DefaultStream(); stream.num_temporal_layers = 3; // Set resolution which is only enough to produce 2 spatial layers. - stream.width = kMinVp9SpatialLayerWidth * 2; - stream.height = kMinVp9SpatialLayerHeight * 2; + stream.width = kMinVp9SpatialLayerLongSideLength * 2; + stream.height = kMinVp9SpatialLayerShortSideLength * 2; streams_.push_back(stream); diff --git a/modules/video_coding/video_receiver.cc b/modules/video_coding/video_receiver.cc index 079de6de14..055d524a58 100644 --- a/modules/video_coding/video_receiver.cc +++ b/modules/video_coding/video_receiver.cc @@ -203,8 +203,9 @@ int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs) { } // If this frame was too late, we should adjust the delay accordingly - _timing->UpdateCurrentDelay(frame->RenderTimeMs(), - clock_->TimeInMilliseconds()); + if (frame->RenderTimeMs() > 0) + _timing->UpdateCurrentDelay(Timestamp::Millis(frame->RenderTimeMs()), + clock_->CurrentTime()); if (first_frame_received_()) { RTC_LOG(LS_INFO) << "Received first complete decodable video frame"; diff --git a/modules/video_processing/util/skin_detection.h b/modules/video_processing/util/skin_detection.h index 7be791f2d8..7f2e17aa87 100644 --- a/modules/video_processing/util/skin_detection.h +++ b/modules/video_processing/util/skin_detection.h @@ -19,11 +19,11 @@ typedef unsigned char uint8_t; bool MbHasSkinColor(const uint8_t* y_src, const uint8_t* u_src, const uint8_t* v_src, - const int stride_y, - const int stride_u, - const int stride_v, - const int mb_row, - const int mb_col); + int stride_y, + int stride_u, + int stride_v, + int mb_row, + int mb_col); } // namespace webrtc diff --git a/net/dcsctp/common/BUILD.gn b/net/dcsctp/common/BUILD.gn index 591fa44207..273a002f8b 100644 --- a/net/dcsctp/common/BUILD.gn +++ b/net/dcsctp/common/BUILD.gn @@ -10,7 +10,7 @@ import("../../../webrtc.gni") rtc_source_set("internal_types") { deps = [ - "../public:strong_alias", + "../../../rtc_base:rtc_base_approved", "../public:types", ] sources = [ "internal_types.h" ] @@ -54,3 +54,12 @@ if (rtc_include_tests) { ] } } + +rtc_library("handover_testing") { + deps = [ "../public:socket" ] + testonly = true + sources = [ + "handover_testing.cc", + "handover_testing.h", + ] +} diff --git a/net/dcsctp/common/handover_testing.cc b/net/dcsctp/common/handover_testing.cc new file mode 100644 index 0000000000..1081766ea5 --- /dev/null +++ b/net/dcsctp/common/handover_testing.cc @@ -0,0 +1,22 @@ +/* + * 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 "net/dcsctp/common/handover_testing.h" + +namespace dcsctp { +namespace { +// Default transformer function does nothing - dcSCTP does not implement +// state serialization that could be tested by setting +// `g_handover_state_transformer_for_test`. +void NoTransformation(DcSctpSocketHandoverState*) {} +} // namespace + +void (*g_handover_state_transformer_for_test)(DcSctpSocketHandoverState*) = + NoTransformation; +} // namespace dcsctp diff --git a/net/dcsctp/common/handover_testing.h b/net/dcsctp/common/handover_testing.h new file mode 100644 index 0000000000..396016afec --- /dev/null +++ b/net/dcsctp/common/handover_testing.h @@ -0,0 +1,29 @@ +/* + * 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 NET_DCSCTP_COMMON_HANDOVER_TESTING_H_ +#define NET_DCSCTP_COMMON_HANDOVER_TESTING_H_ + +#include "net/dcsctp/public/dcsctp_handover_state.h" + +namespace dcsctp { +// This global function is to facilitate testing of the socket handover state +// (`DcSctpSocketHandoverState`) serialization. dcSCTP library users have to +// implement state serialization if it's needed. To test the serialization one +// can set a custom `g_handover_state_transformer_for_test` at startup, link to +// the dcSCTP tests and run the resulting binary. Custom function can serialize +// and deserialize the passed state. All dcSCTP handover tests call +// `g_handover_state_transformer_for_test`. If some part of the state is +// serialized incorrectly or is forgotten, high chance that it will fail the +// tests. +extern void (*g_handover_state_transformer_for_test)( + DcSctpSocketHandoverState*); +} // namespace dcsctp + +#endif // NET_DCSCTP_COMMON_HANDOVER_TESTING_H_ diff --git a/net/dcsctp/common/internal_types.h b/net/dcsctp/common/internal_types.h index e7dabf303d..2354b92cc4 100644 --- a/net/dcsctp/common/internal_types.h +++ b/net/dcsctp/common/internal_types.h @@ -13,31 +13,32 @@ #include #include -#include "net/dcsctp/public/strong_alias.h" #include "net/dcsctp/public/types.h" +#include "rtc_base/strong_alias.h" namespace dcsctp { // Stream Sequence Number (SSN) -using SSN = StrongAlias; +using SSN = webrtc::StrongAlias; // Message Identifier (MID) -using MID = StrongAlias; +using MID = webrtc::StrongAlias; // Fragment Sequence Number (FSN) -using FSN = StrongAlias; +using FSN = webrtc::StrongAlias; // Transmission Sequence Number (TSN) -using TSN = StrongAlias; +using TSN = webrtc::StrongAlias; // Reconfiguration Request Sequence Number -using ReconfigRequestSN = StrongAlias; +using ReconfigRequestSN = + webrtc::StrongAlias; // Verification Tag, used for packet validation. -using VerificationTag = StrongAlias; +using VerificationTag = webrtc::StrongAlias; // Tie Tag, used as a nonce when connecting. -using TieTag = StrongAlias; +using TieTag = webrtc::StrongAlias; } // namespace dcsctp #endif // NET_DCSCTP_COMMON_INTERNAL_TYPES_H_ diff --git a/net/dcsctp/common/sequence_numbers.h b/net/dcsctp/common/sequence_numbers.h index 52b638b54a..919fc5014a 100644 --- a/net/dcsctp/common/sequence_numbers.h +++ b/net/dcsctp/common/sequence_numbers.h @@ -26,7 +26,7 @@ namespace dcsctp { // need to be unwrapped in order, as long as the difference to the previous one // is not larger than half the range of the wrapped sequence number. // -// The WrappedType must be a StrongAlias type. +// The WrappedType must be a webrtc::StrongAlias type. template class UnwrappedSequenceNumber { public: diff --git a/net/dcsctp/common/sequence_numbers_test.cc b/net/dcsctp/common/sequence_numbers_test.cc index f5fa788876..c4842f089e 100644 --- a/net/dcsctp/common/sequence_numbers_test.cc +++ b/net/dcsctp/common/sequence_numbers_test.cc @@ -14,7 +14,7 @@ namespace dcsctp { namespace { -using Wrapped = StrongAlias; +using Wrapped = webrtc::StrongAlias; using TestSequence = UnwrappedSequenceNumber; TEST(SequenceNumbersTest, SimpleUnwrapping) { diff --git a/net/dcsctp/fuzzers/BUILD.gn b/net/dcsctp/fuzzers/BUILD.gn index 9edbae44d7..c00a02251c 100644 --- a/net/dcsctp/fuzzers/BUILD.gn +++ b/net/dcsctp/fuzzers/BUILD.gn @@ -12,6 +12,7 @@ rtc_library("dcsctp_fuzzers") { testonly = true deps = [ "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", diff --git a/net/dcsctp/fuzzers/dcsctp_fuzzers.h b/net/dcsctp/fuzzers/dcsctp_fuzzers.h index f3de0722f4..90cfa35099 100644 --- a/net/dcsctp/fuzzers/dcsctp_fuzzers.h +++ b/net/dcsctp/fuzzers/dcsctp_fuzzers.h @@ -16,6 +16,7 @@ #include #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/dcsctp_socket.h" namespace dcsctp { @@ -58,7 +59,9 @@ class FuzzerCallbacks : public DcSctpSocketCallbacks { void SendPacket(rtc::ArrayView data) override { sent_packets_.emplace_back(std::vector(data.begin(), data.end())); } - std::unique_ptr CreateTimeout() override { + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) override { + // The fuzzer timeouts don't implement |precision|. return std::make_unique(active_timeouts_); } TimeMs TimeMillis() override { return TimeMs(42); } diff --git a/net/dcsctp/packet/chunk/data_chunk.cc b/net/dcsctp/packet/chunk/data_chunk.cc index cf65f53d29..769be2db91 100644 --- a/net/dcsctp/packet/chunk/data_chunk.cc +++ b/net/dcsctp/packet/chunk/data_chunk.cc @@ -93,7 +93,7 @@ std::string DataChunk::ToString() const { ? "complete" : *options().is_beginning ? "first" : *options().is_end ? "last" : "middle") - << ", tsn=" << *tsn() << ", stream_id=" << *stream_id() + << ", tsn=" << *tsn() << ", sid=" << *stream_id() << ", ssn=" << *ssn() << ", ppid=" << *ppid() << ", length=" << payload().size(); return sb.Release(); } diff --git a/net/dcsctp/packet/chunk/data_chunk_test.cc b/net/dcsctp/packet/chunk/data_chunk_test.cc index 6a5ca82bae..def99ceb23 100644 --- a/net/dcsctp/packet/chunk/data_chunk_test.cc +++ b/net/dcsctp/packet/chunk/data_chunk_test.cc @@ -67,7 +67,7 @@ TEST(DataChunkTest, SerializeAndDeserialize) { EXPECT_THAT(chunk.payload(), ElementsAre(1, 2, 3, 4, 5)); EXPECT_EQ(deserialized.ToString(), - "DATA, type=ordered::middle, tsn=123, stream_id=456, ppid=9090, " + "DATA, type=ordered::middle, tsn=123, sid=456, ssn=789, ppid=9090, " "length=5"); } } // namespace diff --git a/net/dcsctp/packet/chunk/data_common.h b/net/dcsctp/packet/chunk/data_common.h index b15a034593..b67efeee1e 100644 --- a/net/dcsctp/packet/chunk/data_common.h +++ b/net/dcsctp/packet/chunk/data_common.h @@ -24,7 +24,7 @@ namespace dcsctp { class AnyDataChunk : public Chunk { public: // Represents the "immediate ack" flag on DATA/I-DATA, from RFC7053. - using ImmediateAckFlag = StrongAlias; + using ImmediateAckFlag = webrtc::StrongAlias; // Data chunk options. // See https://tools.ietf.org/html/rfc4960#section-3.3.1 diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc index f01505094d..e432114c50 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc @@ -87,6 +87,9 @@ void ForwardTsnChunk::SerializeTo(std::vector& out) const { std::string ForwardTsnChunk::ToString() const { rtc::StringBuilder sb; sb << "FORWARD-TSN, new_cumulative_tsn=" << *new_cumulative_tsn(); + for (const auto& skipped : skipped_streams()) { + sb << ", skip " << skipped.stream_id.value() << ":" << *skipped.ssn; + } return sb.str(); } } // namespace dcsctp diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc index 9420c1f2ef..51f97f2396 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc @@ -56,7 +56,8 @@ TEST(ForwardTsnChunkTest, SerializeAndDeserialize) { ElementsAre(ForwardTsnChunk::SkippedStream(StreamID(1), SSN(23)), ForwardTsnChunk::SkippedStream(StreamID(42), SSN(99)))); - EXPECT_EQ(deserialized.ToString(), "FORWARD-TSN, new_cumulative_tsn=123"); + EXPECT_EQ(deserialized.ToString(), + "FORWARD-TSN, new_cumulative_tsn=123, skip 1:23, skip 42:99"); } } // namespace diff --git a/net/dcsctp/packet/data.h b/net/dcsctp/packet/data.h index f2d2e74904..c1754ed59a 100644 --- a/net/dcsctp/packet/data.h +++ b/net/dcsctp/packet/data.h @@ -34,11 +34,11 @@ namespace dcsctp { struct Data { // Indicates if a chunk is the first in a fragmented message and maps to the // "beginning" flag in DATA/I-DATA chunk. - using IsBeginning = StrongAlias; + using IsBeginning = webrtc::StrongAlias; // Indicates if a chunk is the last in a fragmented message and maps to the // "end" flag in DATA/I-DATA chunk. - using IsEnd = StrongAlias; + using IsEnd = webrtc::StrongAlias; Data(StreamID stream_id, SSN ssn, diff --git a/net/dcsctp/packet/sctp_packet.cc b/net/dcsctp/packet/sctp_packet.cc index 3e419c5978..cc66235122 100644 --- a/net/dcsctp/packet/sctp_packet.cc +++ b/net/dcsctp/packet/sctp_packet.cc @@ -82,8 +82,8 @@ size_t SctpPacket::Builder::bytes_remaining() const { // The packet header (CommonHeader) hasn't been written yet: return max_packet_size_ - kHeaderSize; } else if (out_.size() > max_packet_size_) { - RTC_NOTREACHED() << "Exceeded max size, data=" << out_.size() - << ", max_size=" << max_packet_size_; + RTC_DCHECK_NOTREACHED() << "Exceeded max size, data=" << out_.size() + << ", max_size=" << max_packet_size_; return 0; } return max_packet_size_ - out_.size(); diff --git a/net/dcsctp/public/BUILD.gn b/net/dcsctp/public/BUILD.gn index 23530a6b52..4fad23e9b8 100644 --- a/net/dcsctp/public/BUILD.gn +++ b/net/dcsctp/public/BUILD.gn @@ -8,14 +8,10 @@ import("../../../webrtc.gni") -rtc_source_set("strong_alias") { - sources = [ "strong_alias.h" ] -} - rtc_source_set("types") { deps = [ - ":strong_alias", "../../../api:array_view", + "../../../rtc_base:rtc_base_approved", ] sources = [ "dcsctp_message.h", @@ -29,11 +25,14 @@ rtc_source_set("socket") { deps = [ ":types", "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", ] sources = [ + "dcsctp_handover_state.cc", + "dcsctp_handover_state.h", "dcsctp_socket.h", "packet_observer.h", "timeout.h", @@ -88,17 +87,14 @@ if (rtc_include_tests) { deps = [ ":mocks", - ":strong_alias", ":types", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", "../../../rtc_base:rtc_base_approved", - "../../../rtc_base/containers:flat_map", "../../../test:test_support", ] sources = [ "mock_dcsctp_socket_test.cc", - "strong_alias_test.cc", "types_test.cc", ] } diff --git a/net/dcsctp/public/dcsctp_handover_state.cc b/net/dcsctp/public/dcsctp_handover_state.cc new file mode 100644 index 0000000000..6a1bd06eba --- /dev/null +++ b/net/dcsctp/public/dcsctp_handover_state.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "net/dcsctp/public/dcsctp_handover_state.h" + +#include + +#include "absl/strings/string_view.h" + +namespace dcsctp { +namespace { +constexpr absl::string_view HandoverUnreadinessReasonToString( + HandoverUnreadinessReason reason) { + switch (reason) { + case HandoverUnreadinessReason::kWrongConnectionState: + return "WRONG_CONNECTION_STATE"; + case HandoverUnreadinessReason::kSendQueueNotEmpty: + return "SEND_QUEUE_NOT_EMPTY"; + case HandoverUnreadinessReason::kDataTrackerTsnBlocksPending: + return "DATA_TRACKER_TSN_BLOCKS_PENDING"; + case HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap: + return "REASSEMBLY_QUEUE_DELIVERED_TSN_GAP"; + case HandoverUnreadinessReason::kStreamResetDeferred: + return "STREAM_RESET_DEFERRED"; + case HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks: + return "ORDERED_STREAM_HAS_UNASSEMBLED_CHUNKS"; + case HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks: + return "UNORDERED_STREAM_HAS_UNASSEMBLED_CHUNKS"; + case HandoverUnreadinessReason::kRetransmissionQueueOutstandingData: + return "RETRANSMISSION_QUEUE_OUTSTANDING_DATA"; + case HandoverUnreadinessReason::kRetransmissionQueueFastRecovery: + return "RETRANSMISSION_QUEUE_FAST_RECOVERY"; + case HandoverUnreadinessReason::kRetransmissionQueueNotEmpty: + return "RETRANSMISSION_QUEUE_NOT_EMPTY"; + case HandoverUnreadinessReason::kPendingStreamReset: + return "PENDING_STREAM_RESET"; + case HandoverUnreadinessReason::kPendingStreamResetRequest: + return "PENDING_STREAM_RESET_REQUEST"; + } +} +} // namespace + +std::string HandoverReadinessStatus::ToString() const { + std::string result; + for (uint32_t bit = 1; + bit <= static_cast(HandoverUnreadinessReason::kMax); + bit *= 2) { + auto flag = static_cast(bit); + if (Contains(flag)) { + if (!result.empty()) { + result.append(","); + } + absl::string_view s = HandoverUnreadinessReasonToString(flag); + result.append(s.data(), s.size()); + } + } + if (result.empty()) { + result = "READY"; + } + return result; +} +} // namespace dcsctp diff --git a/net/dcsctp/public/dcsctp_handover_state.h b/net/dcsctp/public/dcsctp_handover_state.h new file mode 100644 index 0000000000..a58535d45f --- /dev/null +++ b/net/dcsctp/public/dcsctp_handover_state.h @@ -0,0 +1,132 @@ +/* + * 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 NET_DCSCTP_PUBLIC_DCSCTP_HANDOVER_STATE_H_ +#define NET_DCSCTP_PUBLIC_DCSCTP_HANDOVER_STATE_H_ + +#include +#include +#include + +#include "rtc_base/strong_alias.h" + +namespace dcsctp { + +// Stores state snapshot of a dcSCTP socket. The snapshot can be used to +// recreate the socket - possibly in another process. This state should be +// treaded as opaque - the calling client should not inspect or alter it except +// for serialization. Serialization is not provided by dcSCTP. If needed it has +// to be implemented in the calling client. +struct DcSctpSocketHandoverState { + enum class SocketState { + kClosed, + kConnected, + }; + SocketState socket_state = SocketState::kClosed; + + uint32_t my_verification_tag = 0; + uint32_t my_initial_tsn = 0; + uint32_t peer_verification_tag = 0; + uint32_t peer_initial_tsn = 0; + uint64_t tie_tag = 0; + + struct Capabilities { + bool partial_reliability = false; + bool message_interleaving = false; + bool reconfig = false; + }; + Capabilities capabilities; + + struct OutgoingStream { + uint32_t id = 0; + uint32_t next_ssn = 0; + uint32_t next_unordered_mid = 0; + uint32_t next_ordered_mid = 0; + }; + struct Transmission { + uint32_t next_tsn = 0; + uint32_t next_reset_req_sn = 0; + uint32_t cwnd = 0; + uint32_t rwnd = 0; + uint32_t ssthresh = 0; + uint32_t partial_bytes_acked = 0; + std::vector streams; + }; + Transmission tx; + + struct OrderedStream { + uint32_t id = 0; + uint32_t next_ssn = 0; + }; + struct UnorderedStream { + uint32_t id = 0; + }; + struct Receive { + bool seen_packet = false; + uint32_t last_cumulative_acked_tsn = 0; + uint32_t last_assembled_tsn = 0; + uint32_t last_completed_deferred_reset_req_sn = 0; + uint32_t last_completed_reset_req_sn = 0; + std::vector ordered_streams; + std::vector unordered_streams; + }; + Receive rx; +}; + +// A list of possible reasons for a socket to be not ready for handover. +enum class HandoverUnreadinessReason : uint32_t { + kWrongConnectionState = 1, + kSendQueueNotEmpty = 2, + kPendingStreamResetRequest = 4, + kDataTrackerTsnBlocksPending = 8, + kPendingStreamReset = 16, + kReassemblyQueueDeliveredTSNsGap = 32, + kStreamResetDeferred = 64, + kOrderedStreamHasUnassembledChunks = 128, + kUnorderedStreamHasUnassembledChunks = 256, + kRetransmissionQueueOutstandingData = 512, + kRetransmissionQueueFastRecovery = 1024, + kRetransmissionQueueNotEmpty = 2048, + kMax = kRetransmissionQueueNotEmpty, +}; + +// Return value of `DcSctpSocketInterface::GetHandoverReadiness`. Set of +// `HandoverUnreadinessReason` bits. When no bit is set, the socket is in the +// state in which a snapshot of the state can be made by +// `GetHandoverStateAndClose()`. +class HandoverReadinessStatus + : public webrtc::StrongAlias { + public: + // Constructs an empty `HandoverReadinessStatus` which represents ready state. + constexpr HandoverReadinessStatus() + : webrtc::StrongAlias(0) {} + // Constructs status object that contains a single reason for not being + // handover ready. + constexpr explicit HandoverReadinessStatus(HandoverUnreadinessReason reason) + : webrtc::StrongAlias( + static_cast(reason)) {} + + // Convenience methods + constexpr bool IsReady() const { return value() == 0; } + constexpr bool Contains(HandoverUnreadinessReason reason) const { + return value() & static_cast(reason); + } + HandoverReadinessStatus& Add(HandoverUnreadinessReason reason) { + return Add(HandoverReadinessStatus(reason)); + } + HandoverReadinessStatus& Add(HandoverReadinessStatus status) { + value() |= status.value(); + return *this; + } + std::string ToString() const; +}; + +} // namespace dcsctp + +#endif // NET_DCSCTP_PUBLIC_DCSCTP_HANDOVER_STATE_H_ diff --git a/net/dcsctp/public/dcsctp_options.h b/net/dcsctp/public/dcsctp_options.h index 92bb3a3dcb..c394552e22 100644 --- a/net/dcsctp/public/dcsctp_options.h +++ b/net/dcsctp/public/dcsctp_options.h @@ -128,8 +128,20 @@ struct DcSctpOptions { // unacknowledged packet. Whatever is smallest of RTO/2 and this will be used. DurationMs delayed_ack_max_timeout = DurationMs(200); - // Do slow start as TCP - double cwnd instead of increasing it by MTU. - bool slow_start_tcp_style = false; + // The minimum limit for the measured RTT variance + // + // Setting this below the expected delayed ack timeout (+ margin) of the peer + // might result in unnecessary retransmissions, as the maximum time it takes + // to ACK a DATA chunk is typically RTT + ATO (delayed ack timeout), and when + // the SCTP channel is quite idle, and heartbeats dominate the source of RTT + // measurement, the RTO would converge with the smoothed RTT (SRTT). The + // default ATO is 200ms in usrsctp, and a 20ms (10%) margin would include the + // processing time of received packets and the clock granularity when setting + // the delayed ack timer on the peer. + // + // This is described for TCP in + // https://datatracker.ietf.org/doc/html/rfc6298#section-4. + DurationMs min_rtt_variance = DurationMs(220); // The initial congestion window size, in number of MTUs. // See https://tools.ietf.org/html/rfc4960#section-7.2.1 which defaults at ~3 diff --git a/net/dcsctp/public/dcsctp_socket.h b/net/dcsctp/public/dcsctp_socket.h index 248646e85f..7001585389 100644 --- a/net/dcsctp/public/dcsctp_socket.h +++ b/net/dcsctp/public/dcsctp_socket.h @@ -17,6 +17,8 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" +#include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/public/packet_observer.h" @@ -205,6 +207,31 @@ struct Metrics { absl::optional peer_rwnd_bytes = absl::nullopt; }; +// Represent known SCTP implementations. +enum class SctpImplementation { + // There is not enough information toto determine any SCTP implementation. + kUnknown, + // This implementation. + kDcsctp, + // https://github.com/sctplab/usrsctp. + kUsrSctp, + // Any other implementation. + kOther, +}; + +inline constexpr absl::string_view ToString(SctpImplementation implementation) { + switch (implementation) { + case SctpImplementation::kUnknown: + return "unknown"; + case SctpImplementation::kDcsctp: + return "dcsctp"; + case SctpImplementation::kUsrSctp: + return "usrsctp"; + case SctpImplementation::kOther: + return "other"; + } +} + // Callbacks that the DcSctpSocket will call synchronously to the owning // client. It is allowed to call back into the library from callbacks that start // with "On". It has been explicitly documented when it's not allowed to call @@ -238,9 +265,26 @@ class DcSctpSocketCallbacks { // Called when the library wants to create a Timeout. The callback must return // an object that implements that interface. // + // Low precision tasks are scheduled more efficiently by using leeway to + // reduce Idle Wake Ups and is the preferred precision whenever possible. High + // precision timeouts do not have this leeway, but is still limited by OS + // timer precision. At the time of writing, kLow's additional leeway may be up + // to 17 ms, but please see webrtc::TaskQueueBase::DelayPrecision for + // up-to-date information. + // // Note that it's NOT ALLOWED to call into this library from within this // callback. - virtual std::unique_ptr CreateTimeout() = 0; + virtual std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) { + // TODO(hbos): When dependencies have migrated to this new signature, make + // this pure virtual and delete the other version. + return CreateTimeout(); + } + // TODO(hbos): When dependencies have migrated to the other signature, delete + // this version. + virtual std::unique_ptr CreateTimeout() { + return CreateTimeout(webrtc::TaskQueueBase::DelayPrecision::kLow); + } // Returns the current time in milliseconds (from any epoch). // @@ -355,6 +399,14 @@ class DcSctpSocketInterface { // `DcSctpSocketCallbacks::OnConnected` will be called on success. virtual void Connect() = 0; + // Puts this socket to the state in which the original socket was when its + // `DcSctpSocketHandoverState` was captured by `GetHandoverStateAndClose`. + // `RestoreFromState` is allowed only on the closed socket. + // `DcSctpSocketCallbacks::OnConnected` will be called if a connected socket + // state is restored. + // `DcSctpSocketCallbacks::OnError` will be called on error. + virtual void RestoreFromState(const DcSctpSocketHandoverState& state) = 0; + // Gracefully shutdowns the socket and sends all outstanding data. This is an // asynchronous operation and `DcSctpSocketCallbacks::OnClosed` will be called // on success. @@ -417,6 +469,29 @@ class DcSctpSocketInterface { // Retrieves the latest metrics. virtual Metrics GetMetrics() const = 0; + + // Returns empty bitmask if the socket is in the state in which a snapshot of + // the state can be made by `GetHandoverStateAndClose()`. Return value is + // invalidated by a call to any non-const method. + virtual HandoverReadinessStatus GetHandoverReadiness() const = 0; + + // Collects a snapshot of the socket state that can be used to reconstruct + // this socket in another process. On success this socket object is closed + // synchronously and no callbacks will be made after the method has returned. + // The method fails if the socket is not in a state ready for handover. + // nullopt indicates the failure. `DcSctpSocketCallbacks::OnClosed` will be + // called on success. + virtual absl::optional + GetHandoverStateAndClose() = 0; + + // Returns the detected SCTP implementation of the peer. As this is not + // explicitly signalled during the connection establishment, heuristics is + // used to analyze e.g. the state cookie in the INIT-ACK chunk. + // + // If this method is called too early (before + // `DcSctpSocketCallbacks::OnConnected` has triggered), this will likely + // return `SctpImplementation::kUnknown`. + virtual SctpImplementation peer_implementation() const = 0; }; } // namespace dcsctp diff --git a/net/dcsctp/public/mock_dcsctp_socket.h b/net/dcsctp/public/mock_dcsctp_socket.h index b382773fdf..d207899a18 100644 --- a/net/dcsctp/public/mock_dcsctp_socket.h +++ b/net/dcsctp/public/mock_dcsctp_socket.h @@ -26,6 +26,11 @@ class MockDcSctpSocket : public DcSctpSocketInterface { MOCK_METHOD(void, Connect, (), (override)); + MOCK_METHOD(void, + RestoreFromState, + (const DcSctpSocketHandoverState&), + (override)); + MOCK_METHOD(void, Shutdown, (), (override)); MOCK_METHOD(void, Close, (), (override)); @@ -59,6 +64,17 @@ class MockDcSctpSocket : public DcSctpSocketInterface { (override)); MOCK_METHOD(Metrics, GetMetrics, (), (const, override)); + + MOCK_METHOD(HandoverReadinessStatus, + GetHandoverReadiness, + (), + (const, override)); + MOCK_METHOD(absl::optional, + GetHandoverStateAndClose, + (), + (override)); + + MOCK_METHOD(SctpImplementation, peer_implementation, (), (const)); }; } // namespace dcsctp diff --git a/net/dcsctp/public/types.h b/net/dcsctp/public/types.h index d516daffe3..8faec08ad3 100644 --- a/net/dcsctp/public/types.h +++ b/net/dcsctp/public/types.h @@ -14,28 +14,28 @@ #include #include -#include "net/dcsctp/public/strong_alias.h" +#include "rtc_base/strong_alias.h" namespace dcsctp { // Stream Identifier -using StreamID = StrongAlias; +using StreamID = webrtc::StrongAlias; // Payload Protocol Identifier (PPID) -using PPID = StrongAlias; +using PPID = webrtc::StrongAlias; // Timeout Identifier -using TimeoutID = StrongAlias; +using TimeoutID = webrtc::StrongAlias; // Indicates if a message is allowed to be received out-of-order compared to // other messages on the same stream. -using IsUnordered = StrongAlias; +using IsUnordered = webrtc::StrongAlias; // Duration, as milliseconds. Overflows after 24 days. -class DurationMs : public StrongAlias { +class DurationMs : public webrtc::StrongAlias { public: constexpr explicit DurationMs(const UnderlyingType& v) - : StrongAlias(v) {} + : webrtc::StrongAlias(v) {} // Convenience methods for working with time. constexpr DurationMs& operator+=(DurationMs d) { @@ -72,10 +72,10 @@ constexpr inline int32_t operator/(DurationMs lhs, DurationMs rhs) { } // Represents time, in milliseconds since a client-defined epoch. -class TimeMs : public StrongAlias { +class TimeMs : public webrtc::StrongAlias { public: constexpr explicit TimeMs(const UnderlyingType& v) - : StrongAlias(v) {} + : webrtc::StrongAlias(v) {} // Convenience methods for working with time. constexpr TimeMs& operator+=(DurationMs d) { @@ -105,6 +105,18 @@ constexpr inline DurationMs operator-(TimeMs lhs, TimeMs rhs) { return DurationMs(*lhs - *rhs); } +// The maximum number of times the socket should attempt to retransmit a +// message which fails the first time in unreliable mode. +class MaxRetransmits : public webrtc::StrongAlias { + public: + constexpr explicit MaxRetransmits(const UnderlyingType& v) + : webrtc::StrongAlias(v) {} + + // There should be no limit - the message should be sent reliably. + static constexpr MaxRetransmits NoLimit() { + return MaxRetransmits(std::numeric_limits::max()); + } +}; } // namespace dcsctp #endif // NET_DCSCTP_PUBLIC_TYPES_H_ diff --git a/net/dcsctp/rx/BUILD.gn b/net/dcsctp/rx/BUILD.gn index fb92513158..de96678a3e 100644 --- a/net/dcsctp/rx/BUILD.gn +++ b/net/dcsctp/rx/BUILD.gn @@ -17,6 +17,7 @@ rtc_library("data_tracker") { "../common:sequence_numbers", "../packet:chunk", "../packet:data", + "../public:socket", "../timer", ] sources = [ @@ -36,6 +37,7 @@ rtc_source_set("reassembly_streams") { "../common:sequence_numbers", "../packet:chunk", "../packet:data", + "../public:socket", "../public:types", ] sources = [ "reassembly_streams.h" ] @@ -79,6 +81,7 @@ rtc_library("reassembly_queue") { "../packet:chunk", "../packet:data", "../packet:parameter", + "../public:socket", "../public:types", ] sources = [ @@ -101,10 +104,12 @@ if (rtc_include_tests) { ":reassembly_streams", ":traditional_reassembly_streams", "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", "../../../rtc_base:rtc_base_approved", "../../../test:test_support", + "../common:handover_testing", "../common:sequence_numbers", "../packet:chunk", "../packet:data", diff --git a/net/dcsctp/rx/data_tracker.cc b/net/dcsctp/rx/data_tracker.cc index 5b563a8463..f31847b524 100644 --- a/net/dcsctp/rx/data_tracker.cc +++ b/net/dcsctp/rx/data_tracker.cc @@ -356,4 +356,17 @@ absl::string_view DataTracker::ToString(AckState ack_state) { } } +HandoverReadinessStatus DataTracker::GetHandoverReadiness() const { + HandoverReadinessStatus status; + if (!additional_tsn_blocks_.empty()) { + status.Add(HandoverUnreadinessReason::kDataTrackerTsnBlocksPending); + } + return status; +} + +void DataTracker::AddHandoverState(DcSctpSocketHandoverState& state) { + state.rx.last_cumulative_acked_tsn = last_cumulative_acked_tsn().value(); + state.rx.seen_packet = seen_packet_; +} + } // namespace dcsctp diff --git a/net/dcsctp/rx/data_tracker.h b/net/dcsctp/rx/data_tracker.h index 167f5a04e7..fb8add82a2 100644 --- a/net/dcsctp/rx/data_tracker.h +++ b/net/dcsctp/rx/data_tracker.h @@ -24,6 +24,7 @@ #include "net/dcsctp/packet/chunk/data_common.h" #include "net/dcsctp/packet/chunk/sack_chunk.h" #include "net/dcsctp/packet/data.h" +#include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/timer/timer.h" namespace dcsctp { @@ -51,13 +52,17 @@ class DataTracker { // cumulative acked TSN. static constexpr uint32_t kMaxAcceptedOutstandingFragments = 100000; - explicit DataTracker(absl::string_view log_prefix, - Timer* delayed_ack_timer, - TSN peer_initial_tsn) + DataTracker(absl::string_view log_prefix, + Timer* delayed_ack_timer, + TSN peer_initial_tsn, + const DcSctpSocketHandoverState* handover_state = nullptr) : log_prefix_(std::string(log_prefix) + "dtrack: "), + seen_packet_(handover_state != nullptr ? handover_state->rx.seen_packet + : false), delayed_ack_timer_(*delayed_ack_timer), - last_cumulative_acked_tsn_( - tsn_unwrapper_.Unwrap(TSN(*peer_initial_tsn - 1))) {} + last_cumulative_acked_tsn_(tsn_unwrapper_.Unwrap( + handover_state ? TSN(handover_state->rx.last_cumulative_acked_tsn) + : TSN(*peer_initial_tsn - 1))) {} // Indicates if the provided TSN is valid. If this return false, the data // should be dropped and not added to any other buffers, which essentially @@ -101,6 +106,10 @@ class DataTracker { void HandleDelayedAckTimerExpiry(); + HandoverReadinessStatus GetHandoverReadiness() const; + + void AddHandoverState(DcSctpSocketHandoverState& state); + private: enum class AckState { // No need to send an ACK. @@ -166,7 +175,7 @@ class DataTracker { const std::string log_prefix_; // If a packet has ever been seen. - bool seen_packet_ = false; + bool seen_packet_; Timer& delayed_ack_timer_; AckState ack_state_ = AckState::kIdle; UnwrappedTSN::Unwrapper tsn_unwrapper_; diff --git a/net/dcsctp/rx/data_tracker_test.cc b/net/dcsctp/rx/data_tracker_test.cc index 5c2e56fb2b..3d8380a05c 100644 --- a/net/dcsctp/rx/data_tracker_test.cc +++ b/net/dcsctp/rx/data_tracker_test.cc @@ -15,6 +15,8 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" +#include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/packet/chunk/sack_chunk.h" #include "net/dcsctp/timer/fake_timeout.h" #include "net/dcsctp/timer/timer.h" @@ -35,28 +37,41 @@ class DataTrackerTest : public testing::Test { protected: DataTrackerTest() : timeout_manager_([this]() { return now_; }), - timer_manager_([this]() { return timeout_manager_.CreateTimeout(); }), + timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return timeout_manager_.CreateTimeout(precision); + }), timer_(timer_manager_.CreateTimer( "test/delayed_ack", []() { return absl::nullopt; }, TimerOptions(DurationMs(0)))), - buf_("log: ", timer_.get(), kInitialTSN) {} + tracker_( + std::make_unique("log: ", timer_.get(), kInitialTSN)) { + } void Observer(std::initializer_list tsns) { for (const uint32_t tsn : tsns) { - buf_.Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false)); + tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false)); } } + void HandoverTracker() { + EXPECT_TRUE(tracker_->GetHandoverReadiness().IsReady()); + DcSctpSocketHandoverState state; + tracker_->AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + tracker_ = std::make_unique("log: ", timer_.get(), kInitialTSN, + &state); + } + TimeMs now_ = TimeMs(0); FakeTimeoutManager timeout_manager_; TimerManager timer_manager_; std::unique_ptr timer_; - DataTracker buf_; + std::unique_ptr tracker_; }; TEST_F(DataTrackerTest, Empty) { - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), IsEmpty()); @@ -64,7 +79,7 @@ TEST_F(DataTrackerTest, Empty) { TEST_F(DataTrackerTest, ObserverSingleInOrderPacket) { Observer({11}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), IsEmpty()); @@ -72,7 +87,7 @@ TEST_F(DataTrackerTest, ObserverSingleInOrderPacket) { TEST_F(DataTrackerTest, ObserverManyInOrderMovesCumulativeTsnAck) { Observer({11, 12, 13}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), IsEmpty()); @@ -80,7 +95,7 @@ TEST_F(DataTrackerTest, ObserverManyInOrderMovesCumulativeTsnAck) { TEST_F(DataTrackerTest, ObserveOutOfOrderMovesCumulativeTsnAck) { Observer({12, 13, 14, 11}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), IsEmpty()); @@ -88,7 +103,7 @@ TEST_F(DataTrackerTest, ObserveOutOfOrderMovesCumulativeTsnAck) { TEST_F(DataTrackerTest, SingleGap) { Observer({12}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); EXPECT_THAT(sack.duplicate_tsns(), IsEmpty()); @@ -96,7 +111,7 @@ TEST_F(DataTrackerTest, SingleGap) { TEST_F(DataTrackerTest, ExampleFromRFC4960Section334) { Observer({11, 12, 14, 15, 17}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3), SackChunk::GapAckBlock(5, 5))); @@ -105,33 +120,33 @@ TEST_F(DataTrackerTest, ExampleFromRFC4960Section334) { TEST_F(DataTrackerTest, AckAlreadyReceivedChunk) { Observer({11}); - SackChunk sack1 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty()); // Receive old chunk Observer({8}); - SackChunk sack2 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty()); } TEST_F(DataTrackerTest, DoubleSendRetransmittedChunk) { Observer({11, 13, 14, 15}); - SackChunk sack1 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack1.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 4))); // Fill in the hole. Observer({12, 16, 17, 18}); - SackChunk sack2 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(18)); EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty()); // Receive chunk 12 again. Observer({12, 19, 20, 21}); - SackChunk sack3 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack3 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack3.cumulative_tsn_ack(), TSN(21)); EXPECT_THAT(sack3.gap_ack_blocks(), IsEmpty()); } @@ -140,9 +155,9 @@ TEST_F(DataTrackerTest, ForwardTsnSimple) { // Messages (11, 12, 13), (14, 15) - first message expires. Observer({11, 12, 15}); - buf_.HandleForwardTsn(TSN(13)); + tracker_->HandleForwardTsn(TSN(13)); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); } @@ -151,21 +166,21 @@ TEST_F(DataTrackerTest, ForwardTsnSkipsFromGapBlock) { // Messages (11, 12, 13), (14, 15) - first message expires. Observer({11, 12, 14}); - buf_.HandleForwardTsn(TSN(13)); + tracker_->HandleForwardTsn(TSN(13)); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); } TEST_F(DataTrackerTest, ExampleFromRFC3758) { - buf_.HandleForwardTsn(TSN(102)); + tracker_->HandleForwardTsn(TSN(102)); Observer({102, 104, 105, 107}); - buf_.HandleForwardTsn(TSN(103)); + tracker_->HandleForwardTsn(TSN(103)); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(105)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); } @@ -173,40 +188,40 @@ TEST_F(DataTrackerTest, ExampleFromRFC3758) { TEST_F(DataTrackerTest, EmptyAllAcks) { Observer({11, 13, 14, 15}); - buf_.HandleForwardTsn(TSN(100)); + tracker_->HandleForwardTsn(TSN(100)); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(100)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); } TEST_F(DataTrackerTest, SetsArwndCorrectly) { - SackChunk sack1 = buf_.CreateSelectiveAck(/*a_rwnd=*/100); + SackChunk sack1 = tracker_->CreateSelectiveAck(/*a_rwnd=*/100); EXPECT_EQ(sack1.a_rwnd(), 100u); - SackChunk sack2 = buf_.CreateSelectiveAck(/*a_rwnd=*/101); + SackChunk sack2 = tracker_->CreateSelectiveAck(/*a_rwnd=*/101); EXPECT_EQ(sack2.a_rwnd(), 101u); } TEST_F(DataTrackerTest, WillIncreaseCumAckTsn) { - EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(10)); - EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(10))); - EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(11))); - EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(12))); + EXPECT_EQ(tracker_->last_cumulative_acked_tsn(), TSN(10)); + EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(10))); + EXPECT_TRUE(tracker_->will_increase_cum_ack_tsn(TSN(11))); + EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(12))); Observer({11, 12, 13, 14, 15}); - EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(15)); - EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(15))); - EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(16))); - EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(17))); + EXPECT_EQ(tracker_->last_cumulative_acked_tsn(), TSN(15)); + EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(15))); + EXPECT_TRUE(tracker_->will_increase_cum_ack_tsn(TSN(16))); + EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(17))); } TEST_F(DataTrackerTest, ForceShouldSendSackImmediately) { - EXPECT_FALSE(buf_.ShouldSendAck()); + EXPECT_FALSE(tracker_->ShouldSendAck()); - buf_.ForceImmediateSack(); + tracker_->ForceImmediateSack(); - EXPECT_TRUE(buf_.ShouldSendAck()); + EXPECT_TRUE(tracker_->ShouldSendAck()); } TEST_F(DataTrackerTest, WillAcceptValidTSNs) { @@ -215,7 +230,7 @@ TEST_F(DataTrackerTest, WillAcceptValidTSNs) { int limit = static_cast(DataTracker::kMaxAcceptedOutstandingFragments); for (int i = -limit; i <= limit; ++i) { - EXPECT_TRUE(buf_.IsTSNValid(TSN(*last_tsn + i))); + EXPECT_TRUE(tracker_->IsTSNValid(TSN(*last_tsn + i))); } } @@ -224,15 +239,15 @@ TEST_F(DataTrackerTest, WillNotAcceptInvalidTSNs) { TSN last_tsn = TSN(*kInitialTSN - 1); size_t limit = DataTracker::kMaxAcceptedOutstandingFragments; - EXPECT_FALSE(buf_.IsTSNValid(TSN(*last_tsn + limit + 1))); - EXPECT_FALSE(buf_.IsTSNValid(TSN(*last_tsn - (limit + 1)))); - EXPECT_FALSE(buf_.IsTSNValid(TSN(*last_tsn + 0x8000000))); - EXPECT_FALSE(buf_.IsTSNValid(TSN(*last_tsn - 0x8000000))); + EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn + limit + 1))); + EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn - (limit + 1)))); + EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn + 0x8000000))); + EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn - 0x8000000))); } TEST_F(DataTrackerTest, ReportSingleDuplicateTsns) { Observer({11, 12, 11}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(11))); @@ -240,7 +255,7 @@ TEST_F(DataTrackerTest, ReportSingleDuplicateTsns) { TEST_F(DataTrackerTest, ReportMultipleDuplicateTsns) { Observer({11, 12, 13, 14, 12, 13, 12, 13, 15, 16}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(16)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(12), TSN(13))); @@ -248,7 +263,7 @@ TEST_F(DataTrackerTest, ReportMultipleDuplicateTsns) { TEST_F(DataTrackerTest, ReportDuplicateTsnsInGapAckBlocks) { Observer({11, /*12,*/ 13, 14, 13, 14, 15, 16}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 5))); EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(13), TSN(14))); @@ -256,13 +271,13 @@ TEST_F(DataTrackerTest, ReportDuplicateTsnsInGapAckBlocks) { TEST_F(DataTrackerTest, ClearsDuplicateTsnsAfterCreatingSack) { Observer({11, 12, 13, 14, 12, 13, 12, 13, 15, 16}); - SackChunk sack1 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(16)); EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack1.duplicate_tsns(), UnorderedElementsAre(TSN(12), TSN(13))); Observer({17}); - SackChunk sack2 = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(17)); EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack2.duplicate_tsns(), IsEmpty()); @@ -271,11 +286,11 @@ TEST_F(DataTrackerTest, ClearsDuplicateTsnsAfterCreatingSack) { TEST_F(DataTrackerTest, LimitsNumberOfDuplicatesReported) { for (size_t i = 0; i < DataTracker::kMaxDuplicateTsnReported + 10; ++i) { TSN tsn(11 + i); - buf_.Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); - buf_.Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); + tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); + tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); } - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); EXPECT_THAT(sack.duplicate_tsns(), SizeIs(DataTracker::kMaxDuplicateTsnReported)); @@ -284,10 +299,10 @@ TEST_F(DataTrackerTest, LimitsNumberOfDuplicatesReported) { TEST_F(DataTrackerTest, LimitsNumberOfGapAckBlocksReported) { for (size_t i = 0; i < DataTracker::kMaxGapAckBlocksReported + 10; ++i) { TSN tsn(11 + i * 2); - buf_.Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); + tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false)); } - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack.gap_ack_blocks(), SizeIs(DataTracker::kMaxGapAckBlocksReported)); @@ -295,99 +310,99 @@ TEST_F(DataTrackerTest, LimitsNumberOfGapAckBlocksReported) { TEST_F(DataTrackerTest, SendsSackForFirstPacketObserved) { Observer({11}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); } TEST_F(DataTrackerTest, SendsSackEverySecondPacketWhenThereIsNoPacketLoss) { Observer({11}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({12}); - buf_.ObservePacketEnd(); - EXPECT_FALSE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); EXPECT_TRUE(timer_->is_running()); Observer({13}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({14}); - buf_.ObservePacketEnd(); - EXPECT_FALSE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); EXPECT_TRUE(timer_->is_running()); Observer({15}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); } TEST_F(DataTrackerTest, SendsSackEveryPacketOnPacketLoss) { Observer({11}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({13}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({14}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({15}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({16}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); // Fill the hole. Observer({12}); - buf_.ObservePacketEnd(); - EXPECT_FALSE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); EXPECT_TRUE(timer_->is_running()); // Goes back to every second packet Observer({17}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({18}); - buf_.ObservePacketEnd(); - EXPECT_FALSE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); EXPECT_TRUE(timer_->is_running()); } TEST_F(DataTrackerTest, SendsSackOnDuplicateDataChunks) { Observer({11}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({11}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); Observer({12}); - buf_.ObservePacketEnd(); - EXPECT_FALSE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); EXPECT_TRUE(timer_->is_running()); // Goes back to every second packet Observer({13}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); // Duplicate again Observer({12}); - buf_.ObservePacketEnd(); - EXPECT_TRUE(buf_.ShouldSendAck()); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); } TEST_F(DataTrackerTest, GapAckBlockAddSingleBlock) { Observer({12}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); } @@ -395,7 +410,7 @@ TEST_F(DataTrackerTest, GapAckBlockAddSingleBlock) { TEST_F(DataTrackerTest, GapAckBlockAddsAnother) { Observer({12}); Observer({14}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2), SackChunk::GapAckBlock(4, 4))); @@ -404,7 +419,7 @@ TEST_F(DataTrackerTest, GapAckBlockAddsAnother) { TEST_F(DataTrackerTest, GapAckBlockAddsDuplicate) { Observer({12}); Observer({12}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); EXPECT_THAT(sack.duplicate_tsns(), ElementsAre(TSN(12))); @@ -413,7 +428,7 @@ TEST_F(DataTrackerTest, GapAckBlockAddsDuplicate) { TEST_F(DataTrackerTest, GapAckBlockExpandsToRight) { Observer({12}); Observer({13}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3))); } @@ -423,7 +438,7 @@ TEST_F(DataTrackerTest, GapAckBlockExpandsToRightWithOther) { Observer({20}); Observer({30}); Observer({21}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2), // @@ -434,7 +449,7 @@ TEST_F(DataTrackerTest, GapAckBlockExpandsToRightWithOther) { TEST_F(DataTrackerTest, GapAckBlockExpandsToLeft) { Observer({13}); Observer({12}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3))); } @@ -444,7 +459,7 @@ TEST_F(DataTrackerTest, GapAckBlockExpandsToLeftWithOther) { Observer({21}); Observer({30}); Observer({20}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2), // @@ -458,7 +473,7 @@ TEST_F(DataTrackerTest, GapAckBlockExpandsToLRightAndMerges) { Observer({22}); Observer({30}); Observer({21}); - SackChunk sack = buf_.CreateSelectiveAck(kArwnd); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2), // @@ -468,73 +483,73 @@ TEST_F(DataTrackerTest, GapAckBlockExpandsToLRightAndMerges) { TEST_F(DataTrackerTest, GapAckBlockMergesManyBlocksIntoOne) { Observer({22}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12))); Observer({30}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12), // SackChunk::GapAckBlock(20, 20))); Observer({24}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12), // SackChunk::GapAckBlock(14, 14), // SackChunk::GapAckBlock(20, 20))); Observer({28}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12), // SackChunk::GapAckBlock(14, 14), // SackChunk::GapAckBlock(18, 18), // SackChunk::GapAckBlock(20, 20))); Observer({26}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12), // SackChunk::GapAckBlock(14, 14), // SackChunk::GapAckBlock(16, 16), // SackChunk::GapAckBlock(18, 18), // SackChunk::GapAckBlock(20, 20))); Observer({29}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 12), // SackChunk::GapAckBlock(14, 14), // SackChunk::GapAckBlock(16, 16), // SackChunk::GapAckBlock(18, 20))); Observer({23}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 14), // SackChunk::GapAckBlock(16, 16), // SackChunk::GapAckBlock(18, 20))); Observer({27}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 14), // SackChunk::GapAckBlock(16, 20))); Observer({25}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(12, 20))); Observer({20}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(10, 10), // SackChunk::GapAckBlock(12, 20))); Observer({32}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(10, 10), // SackChunk::GapAckBlock(12, 20), // SackChunk::GapAckBlock(22, 22))); Observer({21}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(10, 20), // SackChunk::GapAckBlock(22, 22))); Observer({31}); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(10, 22))); } TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeCumAckTsn) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(8)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(10)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(8)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(10)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 4), // SackChunk::GapAckBlock(10, 12), SackChunk::GapAckBlock(20, 21))); @@ -543,9 +558,9 @@ TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeCumAckTsn) { TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeFirstBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(11)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(11)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(6, 8), // SackChunk::GapAckBlock(16, 17))); } @@ -553,27 +568,27 @@ TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeFirstBlock) { TEST_F(DataTrackerTest, GapAckBlockRemoveAtBeginningOfFirstBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(12)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(12)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(6, 8), // SackChunk::GapAckBlock(16, 17))); } TEST_F(DataTrackerTest, GapAckBlockRemoveAtMiddleOfFirstBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(13)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(13)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(6, 8), // SackChunk::GapAckBlock(16, 17))); } TEST_F(DataTrackerTest, GapAckBlockRemoveAtEndOfFirstBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(14)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(14)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(6, 8), // SackChunk::GapAckBlock(16, 17))); } @@ -581,9 +596,9 @@ TEST_F(DataTrackerTest, GapAckBlockRemoveAtEndOfFirstBlock) { TEST_F(DataTrackerTest, GapAckBlockRemoveRightAfterFirstBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(18)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(18)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(18)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(18)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 4), // SackChunk::GapAckBlock(12, 13))); } @@ -591,46 +606,117 @@ TEST_F(DataTrackerTest, GapAckBlockRemoveRightAfterFirstBlock) { TEST_F(DataTrackerTest, GapAckBlockRemoveRightBeforeSecondBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(19)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(19)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(8, 9))); } TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtStartOfSecondBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(20)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(20)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(8, 9))); } TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtMiddleOfSecondBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(21)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(21)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(8, 9))); } TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtEndOfSecondBlock) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(22)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), + tracker_->HandleForwardTsn(TSN(22)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(8, 9))); } TEST_F(DataTrackerTest, GapAckBlockRemoveeFarAfterAllBlocks) { Observer({12, 13, 14, 20, 21, 22, 30, 31}); - buf_.HandleForwardTsn(TSN(40)); - EXPECT_EQ(buf_.CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(40)); - EXPECT_THAT(buf_.CreateSelectiveAck(kArwnd).gap_ack_blocks(), IsEmpty()); + tracker_->HandleForwardTsn(TSN(40)); + EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(40)); + EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), IsEmpty()); } +TEST_F(DataTrackerTest, HandoverEmpty) { + HandoverTracker(); + Observer({11}); + SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); + EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11)); + EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); +} + +TEST_F(DataTrackerTest, + HandoverWhileSendingSackEverySecondPacketWhenThereIsNoPacketLoss) { + Observer({11}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + + HandoverTracker(); + + Observer({12}); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); + Observer({13}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + EXPECT_FALSE(timer_->is_running()); + Observer({14}); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); + EXPECT_TRUE(timer_->is_running()); + Observer({15}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + EXPECT_FALSE(timer_->is_running()); +} + +TEST_F(DataTrackerTest, HandoverWhileSendingSackEveryPacketOnPacketLoss) { + Observer({11}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + Observer({13}); + EXPECT_EQ(tracker_->GetHandoverReadiness(), + HandoverReadinessStatus().Add( + HandoverUnreadinessReason::kDataTrackerTsnBlocksPending)); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + Observer({14}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + EXPECT_EQ(tracker_->GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kDataTrackerTsnBlocksPending)); + Observer({15}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + Observer({16}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + // Fill the hole. + Observer({12}); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); + // Goes back to every second packet + Observer({17}); + tracker_->ObservePacketEnd(); + EXPECT_TRUE(tracker_->ShouldSendAck()); + + HandoverTracker(); + + Observer({18}); + tracker_->ObservePacketEnd(); + EXPECT_FALSE(tracker_->ShouldSendAck()); + EXPECT_TRUE(timer_->is_running()); +} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/rx/reassembly_queue.cc b/net/dcsctp/rx/reassembly_queue.cc index 581b9fcc49..cbf198b136 100644 --- a/net/dcsctp/rx/reassembly_queue.cc +++ b/net/dcsctp/rx/reassembly_queue.cc @@ -34,20 +34,29 @@ #include "rtc_base/logging.h" namespace dcsctp { -ReassemblyQueue::ReassemblyQueue(absl::string_view log_prefix, - TSN peer_initial_tsn, - size_t max_size_bytes) +ReassemblyQueue::ReassemblyQueue( + absl::string_view log_prefix, + TSN peer_initial_tsn, + size_t max_size_bytes, + const DcSctpSocketHandoverState* handover_state) : log_prefix_(std::string(log_prefix) + "reasm: "), max_size_bytes_(max_size_bytes), watermark_bytes_(max_size_bytes * kHighWatermarkLimit), - last_assembled_tsn_watermark_( - tsn_unwrapper_.Unwrap(TSN(*peer_initial_tsn - 1))), + last_assembled_tsn_watermark_(tsn_unwrapper_.Unwrap( + handover_state ? TSN(handover_state->rx.last_assembled_tsn) + : TSN(*peer_initial_tsn - 1))), + last_completed_reset_req_seq_nbr_( + handover_state + ? ReconfigRequestSN( + handover_state->rx.last_completed_deferred_reset_req_sn) + : ReconfigRequestSN(0)), streams_(std::make_unique( log_prefix_, [this](rtc::ArrayView tsns, DcSctpMessage message) { AddReassembledMessage(tsns, std::move(message)); - })) {} + }, + handover_state)) {} void ReassemblyQueue::Add(TSN tsn, Data data) { RTC_DCHECK(IsConsistent()); @@ -166,9 +175,7 @@ bool ReassemblyQueue::MaybeResetStreamsDeferred(TSN cum_ack_tsn) { // https://tools.ietf.org/html/rfc6525#section-5.2.2 // "Any queued TSNs (queued at step E2) MUST now be released and processed // normally." - for (auto& p : deferred_chunks) { - const TSN& tsn = p.first; - Data& data = p.second; + for (auto& [tsn, data] : deferred_chunks) { queued_bytes_ -= data.size(); Add(tsn, std::move(data)); } @@ -203,8 +210,16 @@ void ReassemblyQueue::AddReassembledMessage( << ", payload=" << message.payload().size() << " bytes"; for (const UnwrappedTSN tsn : tsns) { - // Update watermark, or insert into delivered_tsns_ - if (tsn == last_assembled_tsn_watermark_.next_value()) { + if (tsn <= last_assembled_tsn_watermark_) { + // This can be provoked by a misbehaving peer by sending FORWARD-TSN with + // invalid SSNs, allowing ordered messages to stay in the queue that + // should've been discarded. + RTC_DLOG(LS_VERBOSE) + << log_prefix_ + << "Message is built from fragments already seen - skipping"; + return; + } else if (tsn == last_assembled_tsn_watermark_.next_value()) { + // Update watermark, or insert into delivered_tsns_ last_assembled_tsn_watermark_.Increment(); } else { delivered_tsns_.insert(tsn); @@ -212,14 +227,21 @@ void ReassemblyQueue::AddReassembledMessage( } // With new TSNs in delivered_tsns, gaps might be filled. + MaybeMoveLastAssembledWatermarkFurther(); + + reassembled_messages_.emplace_back(std::move(message)); +} + +void ReassemblyQueue::MaybeMoveLastAssembledWatermarkFurther() { + // `delivered_tsns_` contain TSNS when there is a gap between ranges of + // assembled TSNs. `last_assembled_tsn_watermark_` should not be adjacent to + // that list, because if so, it can be moved. while (!delivered_tsns_.empty() && *delivered_tsns_.begin() == last_assembled_tsn_watermark_.next_value()) { last_assembled_tsn_watermark_.Increment(); delivered_tsns_.erase(delivered_tsns_.begin()); } - - reassembled_messages_.emplace_back(std::move(message)); } void ReassemblyQueue::Handle(const AnyForwardTsnChunk& forward_tsn) { @@ -230,16 +252,43 @@ void ReassemblyQueue::Handle(const AnyForwardTsnChunk& forward_tsn) { delivered_tsns_.erase(delivered_tsns_.begin(), delivered_tsns_.upper_bound(tsn)); + MaybeMoveLastAssembledWatermarkFurther(); + queued_bytes_ -= streams_->HandleForwardTsn(tsn, forward_tsn.skipped_streams()); RTC_DCHECK(IsConsistent()); } bool ReassemblyQueue::IsConsistent() const { + // `delivered_tsns_` and `last_assembled_tsn_watermark_` mustn't overlap or be + // adjacent. + if (!delivered_tsns_.empty() && + last_assembled_tsn_watermark_.next_value() >= *delivered_tsns_.begin()) { + return false; + } + // Allow queued_bytes_ to be larger than max_size_bytes, as it's not actively // enforced in this class. This comparison will still trigger if queued_bytes_ // became "negative". return (queued_bytes_ >= 0 && queued_bytes_ <= 2 * max_size_bytes_); } +HandoverReadinessStatus ReassemblyQueue::GetHandoverReadiness() const { + HandoverReadinessStatus status = streams_->GetHandoverReadiness(); + if (!delivered_tsns_.empty()) { + status.Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap); + } + if (deferred_reset_streams_.has_value()) { + status.Add(HandoverUnreadinessReason::kStreamResetDeferred); + } + return status; +} + +void ReassemblyQueue::AddHandoverState(DcSctpSocketHandoverState& state) { + state.rx.last_assembled_tsn = last_assembled_tsn_watermark_.Wrap().value(); + state.rx.last_completed_deferred_reset_req_sn = + last_completed_reset_req_seq_nbr_.value(); + streams_->AddHandoverState(state); +} + } // namespace dcsctp diff --git a/net/dcsctp/rx/reassembly_queue.h b/net/dcsctp/rx/reassembly_queue.h index 25cda70c58..9cc0c61eb6 100644 --- a/net/dcsctp/rx/reassembly_queue.h +++ b/net/dcsctp/rx/reassembly_queue.h @@ -27,6 +27,7 @@ #include "net/dcsctp/packet/data.h" #include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h" #include "net/dcsctp/packet/parameter/reconfiguration_response_parameter.h" +#include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/rx/reassembly_streams.h" @@ -70,7 +71,8 @@ class ReassemblyQueue { ReassemblyQueue(absl::string_view log_prefix, TSN peer_initial_tsn, - size_t max_size_bytes); + size_t max_size_bytes, + const DcSctpSocketHandoverState* handover_state = nullptr); // Adds a data chunk to the queue, with a `tsn` and other parameters in // `data`. @@ -118,10 +120,15 @@ class ReassemblyQueue { // Returns the watermark limit, in bytes. size_t watermark_bytes() const { return watermark_bytes_; } + HandoverReadinessStatus GetHandoverReadiness() const; + + void AddHandoverState(DcSctpSocketHandoverState& state); + private: bool IsConsistent() const; void AddReassembledMessage(rtc::ArrayView tsns, DcSctpMessage message); + void MaybeMoveLastAssembledWatermarkFurther(); struct DeferredResetStreams { explicit DeferredResetStreams(OutgoingSSNResetRequestParameter req) @@ -150,7 +157,7 @@ class ReassemblyQueue { // Contains the last request sequence number of the // OutgoingSSNResetRequestParameter that was performed. - ReconfigRequestSN last_completed_reset_req_seq_nbr_ = ReconfigRequestSN(0); + ReconfigRequestSN last_completed_reset_req_seq_nbr_; // The number of "payload bytes" that are in this queue, in total. size_t queued_bytes_ = 0; diff --git a/net/dcsctp/rx/reassembly_queue_test.cc b/net/dcsctp/rx/reassembly_queue_test.cc index e38372c7d1..bc1b776837 100644 --- a/net/dcsctp/rx/reassembly_queue_test.cc +++ b/net/dcsctp/rx/reassembly_queue_test.cc @@ -18,6 +18,7 @@ #include #include "api/array_view.h" +#include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" #include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h" @@ -31,6 +32,7 @@ namespace dcsctp { namespace { using ::testing::ElementsAre; +using ::testing::SizeIs; // The default maximum size of the Reassembly Queue. static constexpr size_t kBufferSize = 10000; @@ -294,5 +296,114 @@ TEST_F(ReassemblyQueueTest, ShouldntDeliverBeforeForwardedTsn) { EXPECT_FALSE(reasm.HasMessages()); } +TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenDeliveredTsnsHaveGap) { + ReassemblyQueue reasm("log: ", TSN(10), kBufferSize); + reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "B")); + EXPECT_FALSE(reasm.HasMessages()); + + reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE")); + EXPECT_TRUE(reasm.HasMessages()); + EXPECT_EQ( + reasm.GetHandoverReadiness(), + HandoverReadinessStatus() + .Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap) + .Add( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks)); + + EXPECT_THAT(reasm.FlushMessages(), + ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload))); + EXPECT_EQ( + reasm.GetHandoverReadiness(), + HandoverReadinessStatus() + .Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap) + .Add( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks)); + + reasm.Handle(ForwardTsnChunk(TSN(13), {})); + EXPECT_EQ(reasm.GetHandoverReadiness(), HandoverReadinessStatus()); +} + +TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenResetStreamIsDeferred) { + ReassemblyQueue reasm("log: ", TSN(10), kBufferSize); + DataGeneratorOptions opts; + opts.message_id = MID(0); + reasm.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + opts.message_id = MID(1); + reasm.Add(TSN(11), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + EXPECT_THAT(reasm.FlushMessages(), SizeIs(2)); + + reasm.ResetStreams( + OutgoingSSNResetRequestParameter( + ReconfigRequestSN(10), ReconfigRequestSN(3), TSN(13), {StreamID(1)}), + TSN(11)); + EXPECT_EQ(reasm.GetHandoverReadiness(), + HandoverReadinessStatus().Add( + HandoverUnreadinessReason::kStreamResetDeferred)); + + opts.message_id = MID(3); + opts.ppid = PPID(3); + reasm.Add(TSN(13), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm.MaybeResetStreamsDeferred(TSN(11)); + + opts.message_id = MID(2); + opts.ppid = PPID(2); + reasm.Add(TSN(13), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm.MaybeResetStreamsDeferred(TSN(15)); + EXPECT_EQ(reasm.GetHandoverReadiness(), + HandoverReadinessStatus().Add( + HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)); + + EXPECT_THAT(reasm.FlushMessages(), SizeIs(2)); + EXPECT_EQ(reasm.GetHandoverReadiness(), + HandoverReadinessStatus().Add( + HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)); + + reasm.Handle(ForwardTsnChunk(TSN(15), {})); + EXPECT_EQ(reasm.GetHandoverReadiness(), HandoverReadinessStatus()); +} + +TEST_F(ReassemblyQueueTest, HandoverInInitialState) { + ReassemblyQueue reasm1("log: ", TSN(10), kBufferSize); + + EXPECT_EQ(reasm1.GetHandoverReadiness(), HandoverReadinessStatus()); + DcSctpSocketHandoverState state; + reasm1.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + ReassemblyQueue reasm2("log: ", TSN(100), kBufferSize, &state); + + reasm2.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE")); + EXPECT_THAT(reasm2.FlushMessages(), SizeIs(1)); +} + +TEST_F(ReassemblyQueueTest, HandoverAfterHavingAssembedOneMessage) { + ReassemblyQueue reasm1("log: ", TSN(10), kBufferSize); + reasm1.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE")); + EXPECT_THAT(reasm1.FlushMessages(), SizeIs(1)); + + EXPECT_EQ(reasm1.GetHandoverReadiness(), HandoverReadinessStatus()); + DcSctpSocketHandoverState state; + reasm1.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + ReassemblyQueue reasm2("log: ", TSN(100), kBufferSize, &state); + + reasm2.Add(TSN(11), gen_.Ordered({1, 2, 3, 4}, "BE")); + EXPECT_THAT(reasm2.FlushMessages(), SizeIs(1)); +} + +TEST_F(ReassemblyQueueTest, HandleInconsistentForwardTSN) { + // Found when fuzzing. + ReassemblyQueue reasm("log: ", TSN(10), kBufferSize); + // Add TSN=43, SSN=7. Can't be reassembled as previous SSNs aren't known. + reasm.Add(TSN(43), Data(kStreamID, SSN(7), MID(0), FSN(0), kPPID, + std::vector(10), Data::IsBeginning(true), + Data::IsEnd(true), IsUnordered(false))); + + // Invalid, as TSN=44 have to have SSN>=7, but peer says 6. + reasm.Handle(ForwardTsnChunk( + TSN(44), {ForwardTsnChunk::SkippedStream(kStreamID, SSN(6))})); + + // Don't assemble SSN=7, as that TSN is skipped. + EXPECT_FALSE(reasm.HasMessages()); +} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/rx/reassembly_streams.h b/net/dcsctp/rx/reassembly_streams.h index a8b42b5a2d..06f1a781ce 100644 --- a/net/dcsctp/rx/reassembly_streams.h +++ b/net/dcsctp/rx/reassembly_streams.h @@ -21,6 +21,7 @@ #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" #include "net/dcsctp/packet/data.h" +#include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/public/dcsctp_message.h" namespace dcsctp { @@ -77,6 +78,9 @@ class ReassemblyStreams { // either a few streams, or all streams (when the list is empty) to be // reset - to have their next SSN or Message ID to be zero. virtual void ResetStreams(rtc::ArrayView stream_ids) = 0; + + virtual HandoverReadinessStatus GetHandoverReadiness() const = 0; + virtual void AddHandoverState(DcSctpSocketHandoverState& state) = 0; }; } // namespace dcsctp diff --git a/net/dcsctp/rx/traditional_reassembly_streams.cc b/net/dcsctp/rx/traditional_reassembly_streams.cc index 4108d37236..f5dc8cacc8 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams.cc +++ b/net/dcsctp/rx/traditional_reassembly_streams.cc @@ -78,15 +78,39 @@ absl::optional::iterator> FindEnd( } } // namespace +TraditionalReassemblyStreams::TraditionalReassemblyStreams( + absl::string_view log_prefix, + OnAssembledMessage on_assembled_message, + const DcSctpSocketHandoverState* handover_state) + : log_prefix_(log_prefix), + on_assembled_message_(std::move(on_assembled_message)) { + if (handover_state) { + for (const DcSctpSocketHandoverState::OrderedStream& state_stream : + handover_state->rx.ordered_streams) { + ordered_streams_.emplace( + std::piecewise_construct, + std::forward_as_tuple(StreamID(state_stream.id)), + std::forward_as_tuple(this, SSN(state_stream.next_ssn))); + } + for (const DcSctpSocketHandoverState::UnorderedStream& state_stream : + handover_state->rx.unordered_streams) { + unordered_streams_.emplace( + std::piecewise_construct, + std::forward_as_tuple(StreamID(state_stream.id)), + std::forward_as_tuple(this)); + } + } +} + int TraditionalReassemblyStreams::UnorderedStream::Add(UnwrappedTSN tsn, Data data) { int queued_bytes = data.size(); - auto p = chunks_.emplace(tsn, std::move(data)); - if (!p.second /* !inserted */) { + auto [it, inserted] = chunks_.emplace(tsn, std::move(data)); + if (!inserted) { return 0; } - queued_bytes -= TryToAssembleMessage(p.first); + queued_bytes -= TryToAssembleMessage(it); return queued_bytes; } @@ -201,8 +225,8 @@ int TraditionalReassemblyStreams::OrderedStream::Add(UnwrappedTSN tsn, int queued_bytes = data.size(); UnwrappedSSN ssn = ssn_unwrapper_.Unwrap(data.ssn); - auto p = chunks_by_ssn_[ssn].emplace(tsn, std::move(data)); - if (!p.second /* !inserted */) { + auto [unused, inserted] = chunks_by_ssn_[ssn].emplace(tsn, std::move(data)); + if (!inserted) { return 0; } @@ -237,11 +261,11 @@ size_t TraditionalReassemblyStreams::OrderedStream::EraseTo(SSN ssn) { int TraditionalReassemblyStreams::Add(UnwrappedTSN tsn, Data data) { if (data.is_unordered) { - auto it = unordered_streams_.emplace(data.stream_id, this).first; + auto it = unordered_streams_.try_emplace(data.stream_id, this).first; return it->second.Add(tsn, std::move(data)); } - auto it = ordered_streams_.emplace(data.stream_id, this).first; + auto it = ordered_streams_.try_emplace(data.stream_id, this).first; return it->second.Add(tsn, std::move(data)); } @@ -249,17 +273,16 @@ size_t TraditionalReassemblyStreams::HandleForwardTsn( UnwrappedTSN new_cumulative_ack_tsn, rtc::ArrayView skipped_streams) { size_t bytes_removed = 0; - // The `skipped_streams` only over ordered messages - need to + // The `skipped_streams` only cover ordered messages - need to // iterate all unordered streams manually to remove those chunks. - for (auto& entry : unordered_streams_) { - bytes_removed += entry.second.EraseTo(new_cumulative_ack_tsn); + for (auto& [unused, stream] : unordered_streams_) { + bytes_removed += stream.EraseTo(new_cumulative_ack_tsn); } for (const auto& skipped_stream : skipped_streams) { - auto it = ordered_streams_.find(skipped_stream.stream_id); - if (it != ordered_streams_.end()) { - bytes_removed += it->second.EraseTo(skipped_stream.ssn); - } + auto it = + ordered_streams_.try_emplace(skipped_stream.stream_id, this).first; + bytes_removed += it->second.EraseTo(skipped_stream.ssn); } return bytes_removed; @@ -268,9 +291,7 @@ size_t TraditionalReassemblyStreams::HandleForwardTsn( void TraditionalReassemblyStreams::ResetStreams( rtc::ArrayView stream_ids) { if (stream_ids.empty()) { - for (auto& entry : ordered_streams_) { - const StreamID& stream_id = entry.first; - OrderedStream& stream = entry.second; + for (auto& [stream_id, stream] : ordered_streams_) { RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Resetting implicit stream_id=" << *stream_id; stream.Reset(); @@ -286,4 +307,39 @@ void TraditionalReassemblyStreams::ResetStreams( } } } + +HandoverReadinessStatus TraditionalReassemblyStreams::GetHandoverReadiness() + const { + HandoverReadinessStatus status; + for (const auto& [unused, stream] : ordered_streams_) { + if (stream.has_unassembled_chunks()) { + status.Add(HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks); + break; + } + } + for (const auto& [unused, stream] : unordered_streams_) { + if (stream.has_unassembled_chunks()) { + status.Add( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks); + break; + } + } + return status; +} + +void TraditionalReassemblyStreams::AddHandoverState( + DcSctpSocketHandoverState& state) { + for (const auto& [stream_id, stream] : ordered_streams_) { + DcSctpSocketHandoverState::OrderedStream state_stream; + state_stream.id = stream_id.value(); + state_stream.next_ssn = stream.next_ssn().value(); + state.rx.ordered_streams.push_back(std::move(state_stream)); + } + for (const auto& [stream_id, unused] : unordered_streams_) { + DcSctpSocketHandoverState::UnorderedStream state_stream; + state_stream.id = stream_id.value(); + state.rx.unordered_streams.push_back(std::move(state_stream)); + } +} + } // namespace dcsctp diff --git a/net/dcsctp/rx/traditional_reassembly_streams.h b/net/dcsctp/rx/traditional_reassembly_streams.h index d7ae2dd1b3..2fac9ff683 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams.h +++ b/net/dcsctp/rx/traditional_reassembly_streams.h @@ -29,9 +29,10 @@ namespace dcsctp { // RFC4960 is to be followed. class TraditionalReassemblyStreams : public ReassemblyStreams { public: - TraditionalReassemblyStreams(absl::string_view log_prefix, - OnAssembledMessage on_assembled_message) - : log_prefix_(log_prefix), on_assembled_message_(on_assembled_message) {} + TraditionalReassemblyStreams( + absl::string_view log_prefix, + OnAssembledMessage on_assembled_message, + const DcSctpSocketHandoverState* handover_state = nullptr); int Add(UnwrappedTSN tsn, Data data) override; @@ -42,6 +43,9 @@ class TraditionalReassemblyStreams : public ReassemblyStreams { void ResetStreams(rtc::ArrayView stream_ids) override; + HandoverReadinessStatus GetHandoverReadiness() const override; + void AddHandoverState(DcSctpSocketHandoverState& state) override; + private: using ChunkMap = std::map; @@ -51,8 +55,7 @@ class TraditionalReassemblyStreams : public ReassemblyStreams { explicit StreamBase(TraditionalReassemblyStreams* parent) : parent_(*parent) {} - size_t AssembleMessage(const ChunkMap::iterator start, - const ChunkMap::iterator end); + size_t AssembleMessage(ChunkMap::iterator start, ChunkMap::iterator end); TraditionalReassemblyStreams& parent_; }; @@ -65,6 +68,7 @@ class TraditionalReassemblyStreams : public ReassemblyStreams { int Add(UnwrappedTSN tsn, Data data); // Returns the number of bytes removed from the queue. size_t EraseTo(UnwrappedTSN tsn); + bool has_unassembled_chunks() const { return !chunks_.empty(); } private: // Given an iterator to any chunk within the map, try to assemble a message @@ -81,14 +85,17 @@ class TraditionalReassemblyStreams : public ReassemblyStreams { // messages when possible. class OrderedStream : StreamBase { public: - explicit OrderedStream(TraditionalReassemblyStreams* parent) - : StreamBase(parent), next_ssn_(ssn_unwrapper_.Unwrap(SSN(0))) {} + explicit OrderedStream(TraditionalReassemblyStreams* parent, + SSN next_ssn = SSN(0)) + : StreamBase(parent), next_ssn_(ssn_unwrapper_.Unwrap(next_ssn)) {} int Add(UnwrappedTSN tsn, Data data); size_t EraseTo(SSN ssn); void Reset() { ssn_unwrapper_.Reset(); next_ssn_ = ssn_unwrapper_.Unwrap(SSN(0)); } + SSN next_ssn() const { return next_ssn_.Wrap(); } + bool has_unassembled_chunks() const { return !chunks_by_ssn_.empty(); } private: // Try to assemble one or several messages in order from the stream. diff --git a/net/dcsctp/rx/traditional_reassembly_streams_test.cc b/net/dcsctp/rx/traditional_reassembly_streams_test.cc index 30d29a05dc..759962473d 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams_test.cc +++ b/net/dcsctp/rx/traditional_reassembly_streams_test.cc @@ -13,6 +13,7 @@ #include #include +#include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -24,8 +25,10 @@ namespace dcsctp { namespace { +using ::testing::ElementsAre; using ::testing::MockFunction; using ::testing::NiceMock; +using ::testing::Property; class TraditionalReassemblyStreamsTest : public testing::Test { protected: @@ -148,5 +151,107 @@ TEST_F(TraditionalReassemblyStreamsTest, EXPECT_EQ(streams.HandleForwardTsn(tsn(4), skipped), 8u); } +TEST_F(TraditionalReassemblyStreamsTest, NoStreamsCanBeHandedOver) { + NiceMock> on_assembled; + + TraditionalReassemblyStreams streams1("", on_assembled.AsStdFunction()); + EXPECT_TRUE(streams1.GetHandoverReadiness().IsReady()); + + DcSctpSocketHandoverState state; + streams1.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + TraditionalReassemblyStreams streams2("", on_assembled.AsStdFunction(), + &state); + + EXPECT_EQ(streams2.Add(tsn(1), gen_.Ordered({1}, "B")), 1); + EXPECT_EQ(streams2.Add(tsn(2), gen_.Ordered({2, 3, 4})), 3); + EXPECT_EQ(streams2.Add(tsn(1), gen_.Unordered({1}, "B")), 1); + EXPECT_EQ(streams2.Add(tsn(2), gen_.Unordered({2, 3, 4})), 3); +} + +TEST_F(TraditionalReassemblyStreamsTest, + OrderedStreamsCanBeHandedOverWhenNoUnassembledChunksExist) { + NiceMock> on_assembled; + + TraditionalReassemblyStreams streams1("", on_assembled.AsStdFunction()); + + EXPECT_EQ(streams1.Add(tsn(1), gen_.Ordered({1}, "B")), 1); + EXPECT_EQ(streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks)); + EXPECT_EQ(streams1.Add(tsn(2), gen_.Ordered({2, 3, 4})), 3); + EXPECT_EQ(streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks)); + EXPECT_EQ(streams1.Add(tsn(3), gen_.Ordered({5, 6})), 2); + EXPECT_EQ(streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks)); + + ForwardTsnChunk::SkippedStream skipped[] = { + ForwardTsnChunk::SkippedStream(StreamID(1), SSN(0))}; + EXPECT_EQ(streams1.HandleForwardTsn(tsn(3), skipped), 6u); + EXPECT_TRUE(streams1.GetHandoverReadiness().IsReady()); + + DcSctpSocketHandoverState state; + streams1.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + TraditionalReassemblyStreams streams2("", on_assembled.AsStdFunction(), + &state); + EXPECT_EQ(streams2.Add(tsn(4), gen_.Ordered({7})), 1); +} + +TEST_F(TraditionalReassemblyStreamsTest, + UnorderedStreamsCanBeHandedOverWhenNoUnassembledChunksExist) { + NiceMock> on_assembled; + + TraditionalReassemblyStreams streams1("", on_assembled.AsStdFunction()); + + EXPECT_EQ(streams1.Add(tsn(1), gen_.Unordered({1}, "B")), 1); + EXPECT_EQ( + streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks)); + EXPECT_EQ(streams1.Add(tsn(2), gen_.Unordered({2, 3, 4})), 3); + EXPECT_EQ( + streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks)); + EXPECT_EQ(streams1.Add(tsn(3), gen_.Unordered({5, 6})), 2); + EXPECT_EQ( + streams1.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks)); + + EXPECT_EQ(streams1.HandleForwardTsn(tsn(3), {}), 6u); + EXPECT_TRUE(streams1.GetHandoverReadiness().IsReady()); + + DcSctpSocketHandoverState state; + streams1.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + TraditionalReassemblyStreams streams2("", on_assembled.AsStdFunction(), + &state); + EXPECT_EQ(streams2.Add(tsn(4), gen_.Unordered({7})), 1); +} + +TEST_F(TraditionalReassemblyStreamsTest, CanDeleteFirstOrderedMessage) { + NiceMock> on_assembled; + EXPECT_CALL(on_assembled, + Call(ElementsAre(tsn(2)), + Property(&DcSctpMessage::payload, ElementsAre(2, 3, 4)))); + + TraditionalReassemblyStreams streams("", on_assembled.AsStdFunction()); + + // Not received, SID=1. TSN=1, SSN=0 + gen_.Ordered({1}, "BE"); + // And deleted (SID=1, TSN=1, SSN=0) + ForwardTsnChunk::SkippedStream skipped[] = { + ForwardTsnChunk::SkippedStream(StreamID(1), SSN(0))}; + EXPECT_EQ(streams.HandleForwardTsn(tsn(1), skipped), 0u); + + // Receive SID=1, TSN=2, SSN=1 + EXPECT_EQ(streams.Add(tsn(2), gen_.Ordered({2, 3, 4}, "BE")), 0); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/BUILD.gn b/net/dcsctp/socket/BUILD.gn index 895292001c..083adaa9dd 100644 --- a/net/dcsctp/socket/BUILD.gn +++ b/net/dcsctp/socket/BUILD.gn @@ -98,6 +98,7 @@ rtc_library("transmission_control_block") { ":packet_sender", ":stream_reset_handler", "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", @@ -136,6 +137,7 @@ rtc_library("dcsctp_socket") { "../../../api:array_view", "../../../api:refcountedbase", "../../../api:scoped_refptr", + "../../../api/task_queue:task_queue", "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", @@ -160,6 +162,7 @@ rtc_library("dcsctp_socket") { "../tx:send_queue", ] sources = [ + "callback_deferrer.cc", "callback_deferrer.h", "dcsctp_socket.cc", "dcsctp_socket.h", @@ -179,6 +182,7 @@ if (rtc_include_tests) { sources = [ "mock_dcsctp_socket_callbacks.h" ] deps = [ "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base:logging", "../../../rtc_base:rtc_base_approved", "../../../test:test_support", @@ -221,10 +225,20 @@ if (rtc_include_tests) { ":packet_sender", ":stream_reset_handler", "../../../api:array_view", + "../../../api:create_network_emulation_manager", + "../../../api:network_emulation_manager_api", + "../../../api/task_queue:task_queue", + "../../../api/units:time_delta", + "../../../call:simulated_network", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", + "../../../rtc_base:rtc_base", "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:rtc_base_tests_utils", + "../../../rtc_base:socket_address", + "../../../rtc_base/task_utils:to_queued_task", "../../../test:test_support", + "../common:handover_testing", "../common:internal_types", "../packet:chunk", "../packet:error_cause", @@ -239,6 +253,7 @@ if (rtc_include_tests) { "../testing:data_generator", "../testing:testing_macros", "../timer", + "../timer:task_queue_timeout", "../tx:mock_send_queue", "../tx:retransmission_queue", ] @@ -249,6 +264,7 @@ if (rtc_include_tests) { "//third_party/abseil-cpp/absl/types:optional", ] sources = [ + "dcsctp_socket_network_test.cc", "dcsctp_socket_test.cc", "heartbeat_handler_test.cc", "packet_sender_test.cc", diff --git a/net/dcsctp/socket/DEPS b/net/dcsctp/socket/DEPS new file mode 100644 index 0000000000..d4966290e3 --- /dev/null +++ b/net/dcsctp/socket/DEPS @@ -0,0 +1,5 @@ +specific_include_rules = { + "dcsctp_socket_network_test.cc": [ + "+call", + ] +} diff --git a/net/dcsctp/socket/callback_deferrer.cc b/net/dcsctp/socket/callback_deferrer.cc new file mode 100644 index 0000000000..34d809cbef --- /dev/null +++ b/net/dcsctp/socket/callback_deferrer.cc @@ -0,0 +1,161 @@ +/* + * 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 "net/dcsctp/socket/callback_deferrer.h" + +namespace dcsctp { +namespace { +// A wrapper around the move-only DcSctpMessage, to let it be captured in a +// lambda. +class MessageDeliverer { + public: + explicit MessageDeliverer(DcSctpMessage&& message) + : state_(rtc::make_ref_counted(std::move(message))) {} + + void Deliver(DcSctpSocketCallbacks& c) { + // Really ensure that it's only called once. + RTC_DCHECK(!state_->has_delivered); + state_->has_delivered = true; + c.OnMessageReceived(std::move(state_->message)); + } + + private: + struct State : public rtc::RefCountInterface { + explicit State(DcSctpMessage&& m) + : has_delivered(false), message(std::move(m)) {} + bool has_delivered; + DcSctpMessage message; + }; + rtc::scoped_refptr state_; +}; +} // namespace + +void CallbackDeferrer::Prepare() { + RTC_DCHECK(!prepared_); + prepared_ = true; +} + +void CallbackDeferrer::TriggerDeferred() { + // Need to swap here. The client may call into the library from within a + // callback, and that might result in adding new callbacks to this instance, + // and the vector can't be modified while iterated on. + RTC_DCHECK(prepared_); + std::vector> deferred; + deferred.swap(deferred_); + prepared_ = false; + + for (auto& cb : deferred) { + cb(underlying_); + } +} + +SendPacketStatus CallbackDeferrer::SendPacketWithStatus( + rtc::ArrayView data) { + // Will not be deferred - call directly. + return underlying_.SendPacketWithStatus(data); +} + +std::unique_ptr CallbackDeferrer::CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) { + // Will not be deferred - call directly. + return underlying_.CreateTimeout(precision); +} + +TimeMs CallbackDeferrer::TimeMillis() { + // Will not be deferred - call directly. + return underlying_.TimeMillis(); +} + +uint32_t CallbackDeferrer::GetRandomInt(uint32_t low, uint32_t high) { + // Will not be deferred - call directly. + return underlying_.GetRandomInt(low, high); +} + +void CallbackDeferrer::OnMessageReceived(DcSctpMessage message) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [deliverer = MessageDeliverer(std::move(message))]( + DcSctpSocketCallbacks& cb) mutable { deliverer.Deliver(cb); }); +} + +void CallbackDeferrer::OnError(ErrorKind error, absl::string_view message) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [error, message = std::string(message)](DcSctpSocketCallbacks& cb) { + cb.OnError(error, message); + }); +} + +void CallbackDeferrer::OnAborted(ErrorKind error, absl::string_view message) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [error, message = std::string(message)](DcSctpSocketCallbacks& cb) { + cb.OnAborted(error, message); + }); +} + +void CallbackDeferrer::OnConnected() { + RTC_DCHECK(prepared_); + deferred_.emplace_back([](DcSctpSocketCallbacks& cb) { cb.OnConnected(); }); +} + +void CallbackDeferrer::OnClosed() { + RTC_DCHECK(prepared_); + deferred_.emplace_back([](DcSctpSocketCallbacks& cb) { cb.OnClosed(); }); +} + +void CallbackDeferrer::OnConnectionRestarted() { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [](DcSctpSocketCallbacks& cb) { cb.OnConnectionRestarted(); }); +} + +void CallbackDeferrer::OnStreamsResetFailed( + rtc::ArrayView outgoing_streams, + absl::string_view reason) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [streams = std::vector(outgoing_streams.begin(), + outgoing_streams.end()), + reason = std::string(reason)](DcSctpSocketCallbacks& cb) { + cb.OnStreamsResetFailed(streams, reason); + }); +} + +void CallbackDeferrer::OnStreamsResetPerformed( + rtc::ArrayView outgoing_streams) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [streams = std::vector(outgoing_streams.begin(), + outgoing_streams.end())]( + DcSctpSocketCallbacks& cb) { cb.OnStreamsResetPerformed(streams); }); +} + +void CallbackDeferrer::OnIncomingStreamsReset( + rtc::ArrayView incoming_streams) { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [streams = std::vector(incoming_streams.begin(), + incoming_streams.end())]( + DcSctpSocketCallbacks& cb) { cb.OnIncomingStreamsReset(streams); }); +} + +void CallbackDeferrer::OnBufferedAmountLow(StreamID stream_id) { + RTC_DCHECK(prepared_); + deferred_.emplace_back([stream_id](DcSctpSocketCallbacks& cb) { + cb.OnBufferedAmountLow(stream_id); + }); +} + +void CallbackDeferrer::OnTotalBufferedAmountLow() { + RTC_DCHECK(prepared_); + deferred_.emplace_back( + [](DcSctpSocketCallbacks& cb) { cb.OnTotalBufferedAmountLow(); }); +} +} // namespace dcsctp diff --git a/net/dcsctp/socket/callback_deferrer.h b/net/dcsctp/socket/callback_deferrer.h index b3251c84d5..863093c5c5 100644 --- a/net/dcsctp/socket/callback_deferrer.h +++ b/net/dcsctp/socket/callback_deferrer.h @@ -21,12 +21,12 @@ #include "api/array_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_socket.h" #include "rtc_base/ref_counted_object.h" namespace dcsctp { - // Defers callbacks until they can be safely triggered. // // There are a lot of callbacks from the dcSCTP library to the client, @@ -44,140 +44,50 @@ namespace dcsctp { // There are a number of exceptions, which is clearly annotated in the API. class CallbackDeferrer : public DcSctpSocketCallbacks { public: + class ScopedDeferrer { + public: + explicit ScopedDeferrer(CallbackDeferrer& callback_deferrer) + : callback_deferrer_(callback_deferrer) { + callback_deferrer_.Prepare(); + } + + ~ScopedDeferrer() { callback_deferrer_.TriggerDeferred(); } + + private: + CallbackDeferrer& callback_deferrer_; + }; + explicit CallbackDeferrer(DcSctpSocketCallbacks& underlying) : underlying_(underlying) {} - void TriggerDeferred() { - // Need to swap here. The client may call into the library from within a - // callback, and that might result in adding new callbacks to this instance, - // and the vector can't be modified while iterated on. - std::vector> deferred; - deferred.swap(deferred_); - - for (auto& cb : deferred) { - cb(underlying_); - } - } - + // Implementation of DcSctpSocketCallbacks SendPacketStatus SendPacketWithStatus( - rtc::ArrayView data) override { - // Will not be deferred - call directly. - return underlying_.SendPacketWithStatus(data); - } - - std::unique_ptr CreateTimeout() override { - // Will not be deferred - call directly. - return underlying_.CreateTimeout(); - } - - TimeMs TimeMillis() override { - // Will not be deferred - call directly. - return underlying_.TimeMillis(); - } - - uint32_t GetRandomInt(uint32_t low, uint32_t high) override { - // Will not be deferred - call directly. - return underlying_.GetRandomInt(low, high); - } - - void OnMessageReceived(DcSctpMessage message) override { - deferred_.emplace_back( - [deliverer = MessageDeliverer(std::move(message))]( - DcSctpSocketCallbacks& cb) mutable { deliverer.Deliver(cb); }); - } - - void OnError(ErrorKind error, absl::string_view message) override { - deferred_.emplace_back( - [error, message = std::string(message)](DcSctpSocketCallbacks& cb) { - cb.OnError(error, message); - }); - } - - void OnAborted(ErrorKind error, absl::string_view message) override { - deferred_.emplace_back( - [error, message = std::string(message)](DcSctpSocketCallbacks& cb) { - cb.OnAborted(error, message); - }); - } - - void OnConnected() override { - deferred_.emplace_back([](DcSctpSocketCallbacks& cb) { cb.OnConnected(); }); - } - - void OnClosed() override { - deferred_.emplace_back([](DcSctpSocketCallbacks& cb) { cb.OnClosed(); }); - } - - void OnConnectionRestarted() override { - deferred_.emplace_back( - [](DcSctpSocketCallbacks& cb) { cb.OnConnectionRestarted(); }); - } - + rtc::ArrayView data) override; + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) override; + TimeMs TimeMillis() override; + uint32_t GetRandomInt(uint32_t low, uint32_t high) override; + void OnMessageReceived(DcSctpMessage message) override; + void OnError(ErrorKind error, absl::string_view message) override; + void OnAborted(ErrorKind error, absl::string_view message) override; + void OnConnected() override; + void OnClosed() override; + void OnConnectionRestarted() override; void OnStreamsResetFailed(rtc::ArrayView outgoing_streams, - absl::string_view reason) override { - deferred_.emplace_back( - [streams = std::vector(outgoing_streams.begin(), - outgoing_streams.end()), - reason = std::string(reason)](DcSctpSocketCallbacks& cb) { - cb.OnStreamsResetFailed(streams, reason); - }); - } - + absl::string_view reason) override; void OnStreamsResetPerformed( - rtc::ArrayView outgoing_streams) override { - deferred_.emplace_back( - [streams = std::vector(outgoing_streams.begin(), - outgoing_streams.end())]( - DcSctpSocketCallbacks& cb) { - cb.OnStreamsResetPerformed(streams); - }); - } - + rtc::ArrayView outgoing_streams) override; void OnIncomingStreamsReset( - rtc::ArrayView incoming_streams) override { - deferred_.emplace_back( - [streams = std::vector(incoming_streams.begin(), - incoming_streams.end())]( - DcSctpSocketCallbacks& cb) { cb.OnIncomingStreamsReset(streams); }); - } - - void OnBufferedAmountLow(StreamID stream_id) override { - deferred_.emplace_back([stream_id](DcSctpSocketCallbacks& cb) { - cb.OnBufferedAmountLow(stream_id); - }); - } - - void OnTotalBufferedAmountLow() override { - deferred_.emplace_back( - [](DcSctpSocketCallbacks& cb) { cb.OnTotalBufferedAmountLow(); }); - } + rtc::ArrayView incoming_streams) override; + void OnBufferedAmountLow(StreamID stream_id) override; + void OnTotalBufferedAmountLow() override; private: - // A wrapper around the move-only DcSctpMessage, to let it be captured in a - // lambda. - class MessageDeliverer { - public: - explicit MessageDeliverer(DcSctpMessage&& message) - : state_(rtc::make_ref_counted(std::move(message))) {} - - void Deliver(DcSctpSocketCallbacks& c) { - // Really ensure that it's only called once. - RTC_DCHECK(!state_->has_delivered); - state_->has_delivered = true; - c.OnMessageReceived(std::move(state_->message)); - } - - private: - struct State : public rtc::RefCountInterface { - explicit State(DcSctpMessage&& m) - : has_delivered(false), message(std::move(m)) {} - bool has_delivered; - DcSctpMessage message; - }; - rtc::scoped_refptr state_; - }; + void Prepare(); + void TriggerDeferred(); DcSctpSocketCallbacks& underlying_; + bool prepared_ = false; std::vector> deferred_; }; } // namespace dcsctp diff --git a/net/dcsctp/socket/dcsctp_socket.cc b/net/dcsctp/socket/dcsctp_socket.cc index 2983b0f5c7..b93584ed47 100644 --- a/net/dcsctp/socket/dcsctp_socket.cc +++ b/net/dcsctp/socket/dcsctp_socket.cc @@ -22,6 +22,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/packet/chunk/abort_chunk.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" @@ -139,6 +140,19 @@ TieTag MakeTieTag(DcSctpSocketCallbacks& cb) { static_cast(tie_tag_lower)); } +SctpImplementation DeterminePeerImplementation( + rtc::ArrayView cookie) { + if (cookie.size() > 8) { + absl::string_view magic(reinterpret_cast(cookie.data()), 8); + if (magic == "dcSCTP00") { + return SctpImplementation::kDcsctp; + } + if (magic == "KAME-BSD") { + return SctpImplementation::kUsrSctp; + } + } + return SctpImplementation::kOther; +} } // namespace DcSctpSocket::DcSctpSocket(absl::string_view log_prefix, @@ -149,7 +163,9 @@ DcSctpSocket::DcSctpSocket(absl::string_view log_prefix, packet_observer_(std::move(packet_observer)), options_(options), callbacks_(callbacks), - timer_manager_([this]() { return callbacks_.CreateTimeout(); }), + timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return callbacks_.CreateTimeout(precision); + }), t1_init_(timer_manager_.CreateTimer( "t1-init", absl::bind_front(&DcSctpSocket::OnInitTimerExpiry, this), @@ -180,10 +196,13 @@ DcSctpSocket::DcSctpSocket(absl::string_view log_prefix, [this]() { callbacks_.OnTotalBufferedAmountLow(); }) {} std::string DcSctpSocket::log_prefix() const { - return log_prefix_ + "[" + std::string(ToString(state_)) + "] "; + return log_prefix_ + "[" + std::string(ToString(state_)) + "] "; } bool DcSctpSocket::IsConsistent() const { + if (tcb_ != nullptr && tcb_->reassembly_queue().HasMessages()) { + return false; + } switch (state_) { case State::kClosed: return (tcb_ == nullptr && !t1_init_->is_running() && @@ -265,6 +284,9 @@ void DcSctpSocket::MakeConnectionParameters() { } void DcSctpSocket::Connect() { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + if (state_ == State::kClosed) { MakeConnectionParameters(); RTC_DLOG(LS_INFO) @@ -280,10 +302,52 @@ void DcSctpSocket::Connect() { << "Called Connect on a socket that is not closed"; } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); +} + +void DcSctpSocket::RestoreFromState(const DcSctpSocketHandoverState& state) { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + + if (state_ != State::kClosed) { + callbacks_.OnError(ErrorKind::kUnsupportedOperation, + "Only closed socket can be restored from state"); + } else { + if (state.socket_state == + DcSctpSocketHandoverState::SocketState::kConnected) { + VerificationTag my_verification_tag = + VerificationTag(state.my_verification_tag); + connect_params_.verification_tag = my_verification_tag; + + Capabilities capabilities; + capabilities.partial_reliability = state.capabilities.partial_reliability; + capabilities.message_interleaving = + state.capabilities.message_interleaving; + capabilities.reconfig = state.capabilities.reconfig; + + send_queue_.RestoreFromState(state); + + tcb_ = std::make_unique( + timer_manager_, log_prefix_, options_, capabilities, callbacks_, + send_queue_, my_verification_tag, TSN(state.my_initial_tsn), + VerificationTag(state.peer_verification_tag), + TSN(state.peer_initial_tsn), static_cast(0), + TieTag(state.tie_tag), packet_sender_, + [this]() { return state_ == State::kEstablished; }, &state); + RTC_DLOG(LS_VERBOSE) << log_prefix() << "Created peer TCB from state: " + << tcb_->ToString(); + + SetState(State::kEstablished, "restored from handover state"); + callbacks_.OnConnected(); + } + } + + RTC_DCHECK(IsConsistent()); } void DcSctpSocket::Shutdown() { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + if (tcb_ != nullptr) { // https://tools.ietf.org/html/rfc4960#section-9.2 // "Upon receipt of the SHUTDOWN primitive from its upper layer, the @@ -307,10 +371,12 @@ void DcSctpSocket::Shutdown() { InternalClose(ErrorKind::kNoError, ""); } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); } void DcSctpSocket::Close() { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + if (state_ != State::kClosed) { if (tcb_ != nullptr) { SctpPacket::Builder b = tcb_->PacketBuilder(); @@ -325,7 +391,6 @@ void DcSctpSocket::Close() { RTC_DLOG(LS_INFO) << log_prefix() << "Called Close on a closed socket"; } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); } void DcSctpSocket::CloseConnectionBecauseOfTooManyTransmissionErrors() { @@ -357,6 +422,9 @@ void DcSctpSocket::InternalClose(ErrorKind error, absl::string_view message) { SendStatus DcSctpSocket::Send(DcSctpMessage message, const SendOptions& send_options) { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + if (message.payload().empty()) { callbacks_.OnError(ErrorKind::kProtocolViolation, "Unable to send empty message"); @@ -391,12 +459,14 @@ SendStatus DcSctpSocket::Send(DcSctpMessage message, } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); return SendStatus::kSuccess; } ResetStreamsStatus DcSctpSocket::ResetStreams( rtc::ArrayView outgoing_streams) { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + if (tcb_ == nullptr) { callbacks_.OnError(ErrorKind::kWrongSequence, "Can't reset streams as the socket is not connected"); @@ -418,49 +488,50 @@ ResetStreamsStatus DcSctpSocket::ResetStreams( } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); return ResetStreamsStatus::kPerformed; } SocketState DcSctpSocket::state() const { + RTC_DCHECK_RUN_ON(&thread_checker_); switch (state_) { case State::kClosed: return SocketState::kClosed; case State::kCookieWait: - ABSL_FALLTHROUGH_INTENDED; case State::kCookieEchoed: return SocketState::kConnecting; case State::kEstablished: return SocketState::kConnected; case State::kShutdownPending: - ABSL_FALLTHROUGH_INTENDED; case State::kShutdownSent: - ABSL_FALLTHROUGH_INTENDED; case State::kShutdownReceived: - ABSL_FALLTHROUGH_INTENDED; case State::kShutdownAckSent: return SocketState::kShuttingDown; } } void DcSctpSocket::SetMaxMessageSize(size_t max_message_size) { + RTC_DCHECK_RUN_ON(&thread_checker_); options_.max_message_size = max_message_size; } size_t DcSctpSocket::buffered_amount(StreamID stream_id) const { + RTC_DCHECK_RUN_ON(&thread_checker_); return send_queue_.buffered_amount(stream_id); } size_t DcSctpSocket::buffered_amount_low_threshold(StreamID stream_id) const { + RTC_DCHECK_RUN_ON(&thread_checker_); return send_queue_.buffered_amount_low_threshold(stream_id); } void DcSctpSocket::SetBufferedAmountLowThreshold(StreamID stream_id, size_t bytes) { + RTC_DCHECK_RUN_ON(&thread_checker_); send_queue_.SetBufferedAmountLowThreshold(stream_id, bytes); } Metrics DcSctpSocket::GetMetrics() const { + RTC_DCHECK_RUN_ON(&thread_checker_); Metrics metrics = metrics_; if (tcb_ != nullptr) { @@ -600,6 +671,9 @@ bool DcSctpSocket::ValidatePacket(const SctpPacket& packet) { } void DcSctpSocket::HandleTimeout(TimeoutID timeout_id) { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + timer_manager_.HandleTimeout(timeout_id); if (tcb_ != nullptr && tcb_->HasTooManyTxErrors()) { @@ -608,10 +682,12 @@ void DcSctpSocket::HandleTimeout(TimeoutID timeout_id) { } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); } void DcSctpSocket::ReceivePacket(rtc::ArrayView data) { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + ++metrics_.rx_packets_count; if (packet_observer_ != nullptr) { @@ -627,7 +703,6 @@ void DcSctpSocket::ReceivePacket(rtc::ArrayView data) { callbacks_.OnError(ErrorKind::kParseFailed, "Failed to parse received SCTP packet"); RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); return; } @@ -642,7 +717,6 @@ void DcSctpSocket::ReceivePacket(rtc::ArrayView data) { RTC_DLOG(LS_VERBOSE) << log_prefix() << "Packet failed verification tag check - dropping"; RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); return; } @@ -660,7 +734,6 @@ void DcSctpSocket::ReceivePacket(rtc::ArrayView data) { } RTC_DCHECK(IsConsistent()); - callbacks_.TriggerDeferred(); } void DcSctpSocket::DebugPrintOutgoing(rtc::ArrayView payload) { @@ -1109,6 +1182,8 @@ void DcSctpSocket::HandleInitAck( Capabilities capabilities = GetCapabilities(options_, chunk->parameters()); t1_init_->Stop(); + peer_implementation_ = DeterminePeerImplementation(cookie->data()); + tcb_ = std::make_unique( timer_manager_, log_prefix_, options_, capabilities, callbacks_, send_queue_, connect_params_.verification_tag, @@ -1576,4 +1651,40 @@ void DcSctpSocket::SendShutdownAck() { t2_shutdown_->Start(); } +HandoverReadinessStatus DcSctpSocket::GetHandoverReadiness() const { + RTC_DCHECK_RUN_ON(&thread_checker_); + HandoverReadinessStatus status; + if (state_ != State::kClosed && state_ != State::kEstablished) { + status.Add(HandoverUnreadinessReason::kWrongConnectionState); + } + status.Add(send_queue_.GetHandoverReadiness()); + if (tcb_) { + status.Add(tcb_->GetHandoverReadiness()); + } + return status; +} + +absl::optional +DcSctpSocket::GetHandoverStateAndClose() { + RTC_DCHECK_RUN_ON(&thread_checker_); + CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); + + if (!GetHandoverReadiness().IsReady()) { + return absl::nullopt; + } + + DcSctpSocketHandoverState state; + + if (state_ == State::kClosed) { + state.socket_state = DcSctpSocketHandoverState::SocketState::kClosed; + } else if (state_ == State::kEstablished) { + state.socket_state = DcSctpSocketHandoverState::SocketState::kConnected; + tcb_->AddHandoverState(state); + send_queue_.AddHandoverState(state); + InternalClose(ErrorKind::kNoError, "handover"); + } + + return std::move(state); +} + } // namespace dcsctp diff --git a/net/dcsctp/socket/dcsctp_socket.h b/net/dcsctp/socket/dcsctp_socket.h index 60359bd173..b1b3ea9d9b 100644 --- a/net/dcsctp/socket/dcsctp_socket.h +++ b/net/dcsctp/socket/dcsctp_socket.h @@ -17,6 +17,7 @@ #include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/sequence_checker.h" #include "net/dcsctp/packet/chunk/abort_chunk.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" @@ -85,6 +86,7 @@ class DcSctpSocket : public DcSctpSocketInterface { void ReceivePacket(rtc::ArrayView data) override; void HandleTimeout(TimeoutID timeout_id) override; void Connect() override; + void RestoreFromState(const DcSctpSocketHandoverState& state) override; void Shutdown() override; void Close() override; SendStatus Send(DcSctpMessage message, @@ -98,7 +100,11 @@ class DcSctpSocket : public DcSctpSocketInterface { size_t buffered_amount_low_threshold(StreamID stream_id) const override; void SetBufferedAmountLowThreshold(StreamID stream_id, size_t bytes) override; Metrics GetMetrics() const override; - + HandoverReadinessStatus GetHandoverReadiness() const override; + absl::optional GetHandoverStateAndClose() override; + SctpImplementation peer_implementation() const override { + return peer_implementation_; + } // Returns this socket's verification tag, or zero if not yet connected. VerificationTag verification_tag() const { return tcb_ != nullptr ? tcb_->my_verification_tag() : VerificationTag(0); @@ -248,6 +254,7 @@ class DcSctpSocket : public DcSctpSocketInterface { const std::string log_prefix_; const std::unique_ptr packet_observer_; + RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_; Metrics metrics_; DcSctpOptions options_; @@ -273,6 +280,8 @@ class DcSctpSocket : public DcSctpSocketInterface { State state_ = State::kClosed; // If the connection is established, contains a transmission control block. std::unique_ptr tcb_; + + SctpImplementation peer_implementation_ = SctpImplementation::kUnknown; }; } // namespace dcsctp diff --git a/net/dcsctp/socket/dcsctp_socket_network_test.cc b/net/dcsctp/socket/dcsctp_socket_network_test.cc new file mode 100644 index 0000000000..486679ae15 --- /dev/null +++ b/net/dcsctp/socket/dcsctp_socket_network_test.cc @@ -0,0 +1,526 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/test/create_network_emulation_manager.h" +#include "api/test/network_emulation_manager.h" +#include "api/units/time_delta.h" +#include "call/simulated_network.h" +#include "net/dcsctp/public/dcsctp_options.h" +#include "net/dcsctp/public/dcsctp_socket.h" +#include "net/dcsctp/public/types.h" +#include "net/dcsctp/socket/dcsctp_socket.h" +#include "net/dcsctp/testing/testing_macros.h" +#include "net/dcsctp/timer/task_queue_timeout.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/gunit.h" +#include "rtc_base/logging.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/strings/string_format.h" +#include "rtc_base/task_utils/to_queued_task.h" +#include "rtc_base/time_utils.h" +#include "test/gmock.h" + +#if !defined(WEBRTC_ANDROID) && defined(NDEBUG) && \ + !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) +#define DCSCTP_NDEBUG_TEST(t) t +#else +// In debug mode, and when MSAN or TSAN sanitizers are enabled, these tests are +// too expensive to run due to extensive consistency checks that iterate on all +// outstanding chunks. Same with low-end Android devices, which have +// difficulties with these tests. +#define DCSCTP_NDEBUG_TEST(t) DISABLED_##t +#endif + +namespace dcsctp { +namespace { +using ::testing::AllOf; +using ::testing::Ge; +using ::testing::Le; +using ::testing::SizeIs; + +constexpr StreamID kStreamId(1); +constexpr PPID kPpid(53); +constexpr size_t kSmallPayloadSize = 10; +constexpr size_t kLargePayloadSize = 10000; +constexpr size_t kHugePayloadSize = 262144; +constexpr size_t kBufferedAmountLowThreshold = kLargePayloadSize * 2; +constexpr int kPrintBandwidthDurationMillis = 1000; +constexpr webrtc::TimeDelta kBenchmarkRuntime(webrtc::TimeDelta::Seconds(10)); +constexpr webrtc::TimeDelta kAWhile(webrtc::TimeDelta::Seconds(1)); + +inline int GetUniqueSeed() { + static int seed = 0; + return ++seed; +} + +DcSctpOptions MakeOptionsForTest() { + DcSctpOptions options; + + // Throughput numbers are affected by the MTU. Ensure it's constant. + options.mtu = 1200; + + // By disabling the heartbeat interval, there will no timers at all running + // when the socket is idle, which makes it easy to just continue the test + // until there are no more scheduled tasks. Note that it _will_ run for longer + // than necessary as timers aren't cancelled when they are stopped (as that's + // not supported), but it's still simulated time and passes quickly. + options.heartbeat_interval = DurationMs(0); + return options; +} + +// When doing throughput tests, knowing what each actor should do. +enum class ActorMode { + kAtRest, + kThroughputSender, + kThroughputReceiver, + kLimitedRetransmissionSender, +}; + +enum class MessageId : uint32_t { + kPrintBandwidth = 1, +}; + +// An abstraction around EmulatedEndpoint, representing a bound socket that +// will send its packet to a given destination. +class BoundSocket : public webrtc::EmulatedNetworkReceiverInterface { + public: + void Bind(webrtc::EmulatedEndpoint* endpoint) { + endpoint_ = endpoint; + uint16_t port = endpoint->BindReceiver(0, this).value(); + source_address_ = + rtc::SocketAddress(endpoint_->GetPeerLocalAddress(), port); + } + + void SetDestination(const BoundSocket& socket) { + dest_address_ = socket.source_address_; + } + + void SetReceiver(std::function receiver) { + receiver_ = std::move(receiver); + } + + void SendPacket(rtc::ArrayView data) { + endpoint_->SendPacket(source_address_, dest_address_, + rtc::CopyOnWriteBuffer(data.data(), data.size())); + } + + private: + // Implementation of `webrtc::EmulatedNetworkReceiverInterface`. + void OnPacketReceived(webrtc::EmulatedIpPacket packet) override { + receiver_(std::move(packet.data)); + } + + std::function receiver_; + webrtc::EmulatedEndpoint* endpoint_ = nullptr; + rtc::SocketAddress source_address_; + rtc::SocketAddress dest_address_; +}; + +// Sends at a constant rate but with random packet sizes. +class SctpActor : public rtc::MessageHandlerAutoCleanup, + public DcSctpSocketCallbacks, + public sigslot::has_slots<> { + public: + SctpActor(absl::string_view name, + BoundSocket& emulated_socket, + const DcSctpOptions& sctp_options) + : log_prefix_(std::string(name) + ": "), + thread_(rtc::Thread::Current()), + emulated_socket_(emulated_socket), + timeout_factory_( + *thread_, + [this]() { return TimeMillis(); }, + [this](dcsctp::TimeoutID timeout_id) { + sctp_socket_.HandleTimeout(timeout_id); + }), + random_(GetUniqueSeed()), + sctp_socket_(name, *this, nullptr, sctp_options), + last_bandwidth_printout_(TimeMs(TimeMillis())) { + emulated_socket.SetReceiver([this](rtc::CopyOnWriteBuffer buf) { + // The receiver will be executed on the NetworkEmulation task queue, but + // the dcSCTP socket is owned by `thread_` and is not thread-safe. + thread_->PostTask(webrtc::ToQueuedTask( + [this, buf] { this->sctp_socket_.ReceivePacket(buf); })); + }); + } + + void OnMessage(rtc::Message* pmsg) override { + if (pmsg->message_id == static_cast(MessageId::kPrintBandwidth)) { + TimeMs now = TimeMillis(); + DurationMs duration = now - last_bandwidth_printout_; + + double bitrate_mbps = + static_cast(received_bytes_ * 8) / *duration / 1000; + RTC_LOG(LS_INFO) << log_prefix() + << rtc::StringFormat("Received %0.2f Mbps", + bitrate_mbps); + + received_bitrate_mbps_.push_back(bitrate_mbps); + received_bytes_ = 0; + last_bandwidth_printout_ = now; + // Print again in a second. + if (mode_ == ActorMode::kThroughputReceiver) { + thread_->PostDelayed(RTC_FROM_HERE, kPrintBandwidthDurationMillis, this, + static_cast(MessageId::kPrintBandwidth)); + } + } + } + + void SendPacket(rtc::ArrayView data) override { + emulated_socket_.SendPacket(data); + } + + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) override { + return timeout_factory_.CreateTimeout(precision); + } + + TimeMs TimeMillis() override { return TimeMs(rtc::TimeMillis()); } + + uint32_t GetRandomInt(uint32_t low, uint32_t high) override { + return random_.Rand(low, high); + } + + void OnMessageReceived(DcSctpMessage message) override { + received_bytes_ += message.payload().size(); + last_received_message_ = std::move(message); + } + + void OnError(ErrorKind error, absl::string_view message) override { + RTC_LOG(LS_WARNING) << log_prefix() << "Socket error: " << ToString(error) + << "; " << message; + } + + void OnAborted(ErrorKind error, absl::string_view message) override { + RTC_LOG(LS_ERROR) << log_prefix() << "Socket abort: " << ToString(error) + << "; " << message; + } + + void OnConnected() override {} + + void OnClosed() override {} + + void OnConnectionRestarted() override {} + + void OnStreamsResetFailed(rtc::ArrayView outgoing_streams, + absl::string_view reason) override {} + + void OnStreamsResetPerformed( + rtc::ArrayView outgoing_streams) override {} + + void OnIncomingStreamsReset( + rtc::ArrayView incoming_streams) override {} + + void NotifyOutgoingMessageBufferEmpty() override {} + + void OnBufferedAmountLow(StreamID stream_id) override { + if (mode_ == ActorMode::kThroughputSender) { + std::vector payload(kHugePayloadSize); + sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), + SendOptions()); + + } else if (mode_ == ActorMode::kLimitedRetransmissionSender) { + while (sctp_socket_.buffered_amount(kStreamId) < + kBufferedAmountLowThreshold * 2) { + SendOptions send_options; + send_options.max_retransmissions = 0; + sctp_socket_.Send( + DcSctpMessage(kStreamId, kPpid, + std::vector(kLargePayloadSize)), + send_options); + + send_options.max_retransmissions = absl::nullopt; + sctp_socket_.Send( + DcSctpMessage(kStreamId, kPpid, + std::vector(kSmallPayloadSize)), + send_options); + } + } + } + + absl::optional ConsumeReceivedMessage() { + if (!last_received_message_.has_value()) { + return absl::nullopt; + } + DcSctpMessage ret = *std::move(last_received_message_); + last_received_message_ = absl::nullopt; + return ret; + } + + DcSctpSocket& sctp_socket() { return sctp_socket_; } + + void SetActorMode(ActorMode mode) { + mode_ = mode; + if (mode_ == ActorMode::kThroughputSender) { + sctp_socket_.SetBufferedAmountLowThreshold(kStreamId, + kBufferedAmountLowThreshold); + std::vector payload(kHugePayloadSize); + sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), + SendOptions()); + + } else if (mode_ == ActorMode::kLimitedRetransmissionSender) { + sctp_socket_.SetBufferedAmountLowThreshold(kStreamId, + kBufferedAmountLowThreshold); + std::vector payload(kHugePayloadSize); + sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), + SendOptions()); + + } else if (mode == ActorMode::kThroughputReceiver) { + thread_->PostDelayed(RTC_FROM_HERE, kPrintBandwidthDurationMillis, this, + static_cast(MessageId::kPrintBandwidth)); + } + } + + // Returns the average bitrate, stripping the first `remove_first_n` that + // represent the time it took to ramp up the congestion control algorithm. + double avg_received_bitrate_mbps(size_t remove_first_n = 3) const { + std::vector bitrates = received_bitrate_mbps_; + bitrates.erase(bitrates.begin(), bitrates.begin() + remove_first_n); + // The last entry isn't full - remove it as well. + bitrates.pop_back(); + + double sum = 0; + for (double bitrate : bitrates) { + sum += bitrate; + } + + return sum / bitrates.size(); + } + + private: + std::string log_prefix() const { + rtc::StringBuilder sb; + sb << log_prefix_; + sb << rtc::TimeMillis(); + sb << ": "; + return sb.Release(); + } + + ActorMode mode_ = ActorMode::kAtRest; + const std::string log_prefix_; + rtc::Thread* thread_; + BoundSocket& emulated_socket_; + TaskQueueTimeoutFactory timeout_factory_; + webrtc::Random random_; + DcSctpSocket sctp_socket_; + size_t received_bytes_ = 0; + absl::optional last_received_message_; + TimeMs last_bandwidth_printout_; + // Per-second received bitrates, in Mbps + std::vector received_bitrate_mbps_; +}; + +class DcSctpSocketNetworkTest : public testing::Test { + protected: + DcSctpSocketNetworkTest() + : options_(MakeOptionsForTest()), + emulation_(webrtc::CreateNetworkEmulationManager( + webrtc::TimeMode::kSimulated)) {} + + void MakeNetwork(const webrtc::BuiltInNetworkBehaviorConfig& config) { + webrtc::EmulatedEndpoint* endpoint_a = + emulation_->CreateEndpoint(webrtc::EmulatedEndpointConfig()); + webrtc::EmulatedEndpoint* endpoint_z = + emulation_->CreateEndpoint(webrtc::EmulatedEndpointConfig()); + + webrtc::EmulatedNetworkNode* node1 = emulation_->CreateEmulatedNode(config); + webrtc::EmulatedNetworkNode* node2 = emulation_->CreateEmulatedNode(config); + + emulation_->CreateRoute(endpoint_a, {node1}, endpoint_z); + emulation_->CreateRoute(endpoint_z, {node2}, endpoint_a); + + emulated_socket_a_.Bind(endpoint_a); + emulated_socket_z_.Bind(endpoint_z); + + emulated_socket_a_.SetDestination(emulated_socket_z_); + emulated_socket_z_.SetDestination(emulated_socket_a_); + } + + void Sleep(webrtc::TimeDelta duration) { + // Sleep in one-millisecond increments, to let timers expire when expected. + for (int i = 0; i < duration.ms(); ++i) { + emulation_->time_controller()->AdvanceTime(webrtc::TimeDelta::Millis(1)); + } + } + + DcSctpOptions options_; + std::unique_ptr emulation_; + BoundSocket emulated_socket_a_; + BoundSocket emulated_socket_z_; +}; + +TEST_F(DcSctpSocketNetworkTest, CanConnectAndShutdown) { + webrtc::BuiltInNetworkBehaviorConfig pipe_config; + MakeNetwork(pipe_config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + EXPECT_THAT(sender.sctp_socket().state(), SocketState::kClosed); + + sender.sctp_socket().Connect(); + Sleep(kAWhile); + EXPECT_THAT(sender.sctp_socket().state(), SocketState::kConnected); + + sender.sctp_socket().Shutdown(); + Sleep(kAWhile); + EXPECT_THAT(sender.sctp_socket().state(), SocketState::kClosed); +} + +TEST_F(DcSctpSocketNetworkTest, CanSendLargeMessage) { + webrtc::BuiltInNetworkBehaviorConfig pipe_config; + pipe_config.queue_delay_ms = 30; + MakeNetwork(pipe_config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + sender.sctp_socket().Connect(); + + constexpr size_t kPayloadSize = 100 * 1024; + + std::vector payload(kPayloadSize); + sender.sctp_socket().Send(DcSctpMessage(kStreamId, kPpid, payload), + SendOptions()); + + Sleep(kAWhile); + + ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage message, + receiver.ConsumeReceivedMessage()); + + EXPECT_THAT(message.payload(), SizeIs(kPayloadSize)); + + sender.sctp_socket().Shutdown(); + Sleep(kAWhile); +} + +TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithLowBandwidth) { + webrtc::BuiltInNetworkBehaviorConfig pipe_config; + pipe_config.queue_delay_ms = 30; + pipe_config.link_capacity_kbps = 1000; + MakeNetwork(pipe_config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + sender.sctp_socket().Connect(); + + sender.SetActorMode(ActorMode::kThroughputSender); + receiver.SetActorMode(ActorMode::kThroughputReceiver); + + Sleep(kBenchmarkRuntime); + sender.SetActorMode(ActorMode::kAtRest); + receiver.SetActorMode(ActorMode::kAtRest); + + Sleep(kAWhile); + + sender.sctp_socket().Shutdown(); + + Sleep(kAWhile); + + // Verify that the bitrates are in the range of 0.5-1.0 Mbps. + double bitrate = receiver.avg_received_bitrate_mbps(); + EXPECT_THAT(bitrate, AllOf(Ge(0.5), Le(1.0))); +} + +TEST_F(DcSctpSocketNetworkTest, + DCSCTP_NDEBUG_TEST(CanSendMessagesReliablyWithMediumBandwidth)) { + webrtc::BuiltInNetworkBehaviorConfig pipe_config; + pipe_config.queue_delay_ms = 30; + pipe_config.link_capacity_kbps = 18000; + MakeNetwork(pipe_config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + sender.sctp_socket().Connect(); + + sender.SetActorMode(ActorMode::kThroughputSender); + receiver.SetActorMode(ActorMode::kThroughputReceiver); + + Sleep(kBenchmarkRuntime); + sender.SetActorMode(ActorMode::kAtRest); + receiver.SetActorMode(ActorMode::kAtRest); + + Sleep(kAWhile); + + sender.sctp_socket().Shutdown(); + + Sleep(kAWhile); + + // Verify that the bitrates are in the range of 16-18 Mbps. + double bitrate = receiver.avg_received_bitrate_mbps(); + EXPECT_THAT(bitrate, AllOf(Ge(16), Le(18))); +} + +TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithMuchPacketLoss) { + webrtc::BuiltInNetworkBehaviorConfig config; + config.queue_delay_ms = 30; + config.loss_percent = 1; + MakeNetwork(config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + sender.sctp_socket().Connect(); + + sender.SetActorMode(ActorMode::kThroughputSender); + receiver.SetActorMode(ActorMode::kThroughputReceiver); + + Sleep(kBenchmarkRuntime); + sender.SetActorMode(ActorMode::kAtRest); + receiver.SetActorMode(ActorMode::kAtRest); + + Sleep(kAWhile); + + sender.sctp_socket().Shutdown(); + + Sleep(kAWhile); + + // TCP calculator gives: 1200 MTU, 60ms RTT and 1% packet loss -> 1.6Mbps. + // This test is doing slightly better (doesn't have any additional header + // overhead etc). Verify that the bitrates are in the range of 1.5-2.5 Mbps. + double bitrate = receiver.avg_received_bitrate_mbps(); + EXPECT_THAT(bitrate, AllOf(Ge(1.5), Le(2.5))); +} + +TEST_F(DcSctpSocketNetworkTest, DCSCTP_NDEBUG_TEST(HasHighBandwidth)) { + webrtc::BuiltInNetworkBehaviorConfig pipe_config; + pipe_config.queue_delay_ms = 30; + MakeNetwork(pipe_config); + + SctpActor sender("A", emulated_socket_a_, options_); + SctpActor receiver("Z", emulated_socket_z_, options_); + sender.sctp_socket().Connect(); + + sender.SetActorMode(ActorMode::kThroughputSender); + receiver.SetActorMode(ActorMode::kThroughputReceiver); + + Sleep(kBenchmarkRuntime); + + sender.SetActorMode(ActorMode::kAtRest); + receiver.SetActorMode(ActorMode::kAtRest); + Sleep(kAWhile); + + sender.sctp_socket().Shutdown(); + Sleep(kAWhile); + + // Verify that the bitrate is in the range of 540-640 Mbps + double bitrate = receiver.avg_received_bitrate_mbps(); + EXPECT_THAT(bitrate, AllOf(Ge(540), Le(640))); +} +} // namespace +} // namespace dcsctp diff --git a/net/dcsctp/socket/dcsctp_socket_test.cc b/net/dcsctp/socket/dcsctp_socket_test.cc index 5f99cc91be..d30043d332 100644 --- a/net/dcsctp/socket/dcsctp_socket_test.cc +++ b/net/dcsctp/socket/dcsctp_socket_test.cc @@ -21,6 +21,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/cookie_echo_chunk.h" #include "net/dcsctp/packet/chunk/data_chunk.h" @@ -232,13 +233,12 @@ TSN AddTo(TSN tsn, int delta) { return TSN(*tsn + delta); } -DcSctpOptions MakeOptionsForTest(bool enable_message_interleaving) { - DcSctpOptions options; +DcSctpOptions FixupOptions(DcSctpOptions options = {}) { + DcSctpOptions fixup = options; // To make the interval more predictable in tests. - options.heartbeat_interval_include_rtt = false; - options.enable_message_interleaving = enable_message_interleaving; - options.max_burst = kMaxBurstPackets; - return options; + fixup.heartbeat_interval_include_rtt = false; + fixup.max_burst = kMaxBurstPackets; + return fixup; } std::unique_ptr GetPacketObserver(absl::string_view name) { @@ -248,530 +248,648 @@ std::unique_ptr GetPacketObserver(absl::string_view name) { return nullptr; } -class DcSctpSocketTest : public testing::Test { - protected: - explicit DcSctpSocketTest(bool enable_message_interleaving = false) - : options_(MakeOptionsForTest(enable_message_interleaving)), - cb_a_("A"), - cb_z_("Z"), - sock_a_(std::make_unique( - "A", cb_a_, GetPacketObserver("A"), options_)), - sock_z_(std::make_unique( - "Z", cb_z_, GetPacketObserver("Z"), options_)) {} +struct SocketUnderTest { + explicit SocketUnderTest(absl::string_view name, + const DcSctpOptions& opts = {}) + : options(FixupOptions(opts)), + cb(name), + socket(name, cb, GetPacketObserver(name), options) {} - void AdvanceTime(DurationMs duration) { - cb_a_.AdvanceTime(duration); - cb_z_.AdvanceTime(duration); - } - - static void ExchangeMessages(DcSctpSocket& sock_a, - MockDcSctpSocketCallbacks& cb_a, - DcSctpSocket& sock_z, - MockDcSctpSocketCallbacks& cb_z) { - bool delivered_packet = false; - do { - delivered_packet = false; - std::vector packet_from_a = cb_a.ConsumeSentPacket(); - if (!packet_from_a.empty()) { - delivered_packet = true; - sock_z.ReceivePacket(std::move(packet_from_a)); - } - std::vector packet_from_z = cb_z.ConsumeSentPacket(); - if (!packet_from_z.empty()) { - delivered_packet = true; - sock_a.ReceivePacket(std::move(packet_from_z)); - } - } while (delivered_packet); - } - - void RunTimers(MockDcSctpSocketCallbacks& cb, DcSctpSocket& socket) { - for (;;) { - absl::optional timeout_id = cb.GetNextExpiredTimeout(); - if (!timeout_id.has_value()) { - break; - } - socket.HandleTimeout(*timeout_id); - } - } - - void RunTimers() { - RunTimers(cb_a_, *sock_a_); - RunTimers(cb_z_, *sock_z_); - } - - // Calls Connect() on `sock_a_` and make the connection established. - void ConnectSockets() { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - - sock_a_->Connect(); - // Z reads INIT, INIT_ACK, COOKIE_ECHO, COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); - - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); - } - - const DcSctpOptions options_; - testing::NiceMock cb_a_; - testing::NiceMock cb_z_; - std::unique_ptr sock_a_; - std::unique_ptr sock_z_; + const DcSctpOptions options; + testing::NiceMock cb; + DcSctpSocket socket; }; -TEST_F(DcSctpSocketTest, EstablishConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); +void ExchangeMessages(SocketUnderTest& a, SocketUnderTest& z) { + bool delivered_packet = false; + do { + delivered_packet = false; + std::vector packet_from_a = a.cb.ConsumeSentPacket(); + if (!packet_from_a.empty()) { + delivered_packet = true; + z.socket.ReceivePacket(std::move(packet_from_a)); + } + std::vector packet_from_z = z.cb.ConsumeSentPacket(); + if (!packet_from_z.empty()) { + delivered_packet = true; + a.socket.ReceivePacket(std::move(packet_from_z)); + } + } while (delivered_packet); +} - sock_a_->Connect(); +void RunTimers(SocketUnderTest& s) { + for (;;) { + absl::optional timeout_id = s.cb.GetNextExpiredTimeout(); + if (!timeout_id.has_value()) { + break; + } + s.socket.HandleTimeout(*timeout_id); + } +} + +void AdvanceTime(SocketUnderTest& a, SocketUnderTest& z, DurationMs duration) { + a.cb.AdvanceTime(duration); + z.cb.AdvanceTime(duration); + + RunTimers(a); + RunTimers(z); +} + +// Calls Connect() on `sock_a_` and make the connection established. +void ConnectSockets(SocketUnderTest& a, SocketUnderTest& z) { + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + + a.socket.Connect(); + // Z reads INIT, INIT_ACK, COOKIE_ECHO, COOKIE_ACK + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); +} + +std::unique_ptr HandoverSocket( + std::unique_ptr sut) { + EXPECT_EQ(sut->socket.GetHandoverReadiness(), HandoverReadinessStatus()); + + bool is_closed = sut->socket.state() == SocketState::kClosed; + if (!is_closed) { + EXPECT_CALL(sut->cb, OnClosed).Times(1); + } + absl::optional handover_state = + sut->socket.GetHandoverStateAndClose(); + EXPECT_TRUE(handover_state.has_value()); + g_handover_state_transformer_for_test(&*handover_state); + + auto handover_socket = std::make_unique("H", sut->options); + if (!is_closed) { + EXPECT_CALL(handover_socket->cb, OnConnected).Times(1); + } + handover_socket->socket.RestoreFromState(*handover_state); + return handover_socket; +} + +// Test parameter that controls whether to perform handovers during the test. A +// test can have multiple points where it conditionally hands over socket Z. +// Either socket Z will be handed over at all those points or handed over never. +enum class HandoverMode { + kNoHandover, + kPerformHandovers, +}; + +class DcSctpSocketParametrizedTest + : public ::testing::Test, + public ::testing::WithParamInterface { + protected: + // Trigger handover for `sut` depending on the current test param. + std::unique_ptr MaybeHandoverSocket( + std::unique_ptr sut) { + if (GetParam() == HandoverMode::kPerformHandovers) { + return HandoverSocket(std::move(sut)); + } + return sut; + } + + // Trigger handover for socket Z depending on the current test param. + // Then checks message passing to verify the handed over socket is functional. + void MaybeHandoverSocketAndSendMessage(SocketUnderTest& a, + std::unique_ptr z) { + if (GetParam() == HandoverMode::kPerformHandovers) { + z = HandoverSocket(std::move(z)); + } + + ExchangeMessages(a, *z); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + ExchangeMessages(a, *z); + + absl::optional msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(1)); + } +}; + +INSTANTIATE_TEST_SUITE_P(Handovers, + DcSctpSocketParametrizedTest, + testing::Values(HandoverMode::kNoHandover, + HandoverMode::kPerformHandovers), + [](const auto& test_info) { + return test_info.param == + HandoverMode::kPerformHandovers + ? "WithHandovers" + : "NoHandover"; + }); + +TEST(DcSctpSocketTest, EstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, EstablishConnectionWithSetupCollision) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); - sock_a_->Connect(); - sock_z_->Connect(); +TEST(DcSctpSocketTest, EstablishConnectionWithSetupCollision) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + a.socket.Connect(); + z.socket.Connect(); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + ExchangeMessages(a, z); + + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ShuttingDownWhileEstablishingConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(0); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - sock_a_->Connect(); +TEST(DcSctpSocketTest, ShuttingDownWhileEstablishingConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(0); + EXPECT_CALL(z.cb, OnConnected).Times(1); + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Drop COOKIE_ACK, just to more easily verify shutdown protocol. - cb_z_.ConsumeSentPacket(); + z.cb.ConsumeSentPacket(); // As Socket A has received INIT_ACK, it has a TCB and is connected, while // Socket Z needs to receive COOKIE_ECHO to get there. Socket A still has // timers running at this point. - EXPECT_EQ(sock_a_->state(), SocketState::kConnecting); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnecting); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); // Socket A is now shut down, which should make it stop those timers. - sock_a_->Shutdown(); + a.socket.Shutdown(); - EXPECT_CALL(cb_a_, OnClosed).Times(1); - EXPECT_CALL(cb_z_, OnClosed).Times(1); + EXPECT_CALL(a.cb, OnClosed).Times(1); + EXPECT_CALL(z.cb, OnClosed).Times(1); // Z reads SHUTDOWN, produces SHUTDOWN_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads SHUTDOWN_ACK, produces SHUTDOWN_COMPLETE - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads SHUTDOWN_COMPLETE. - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); - EXPECT_TRUE(cb_z_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); + EXPECT_TRUE(z.cb.ConsumeSentPacket().empty()); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); - EXPECT_EQ(sock_z_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); + EXPECT_EQ(z.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, EstablishSimultaneousConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); - sock_a_->Connect(); +TEST(DcSctpSocketTest, EstablishSimultaneousConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + a.socket.Connect(); // INIT isn't received by Z, as it wasn't ready yet. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); - sock_z_->Connect(); + z.socket.Connect(); // A reads INIT, produces INIT_ACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads INIT_ACK, sends COOKIE_ECHO - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ECHO - establishes connection. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); // Proceed with the remaining packets. - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, z); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, EstablishConnectionLostCookieAck) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); +TEST(DcSctpSocketTest, EstablishConnectionLostCookieAck) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Connect(); + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // COOKIE_ACK is lost. - cb_z_.ConsumeSentPacket(); + z.cb.ConsumeSentPacket(); - EXPECT_EQ(sock_a_->state(), SocketState::kConnecting); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnecting); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); // This will make A re-send the COOKIE_ECHO - AdvanceTime(DurationMs(options_.t1_cookie_timeout)); - RunTimers(); + AdvanceTime(a, z, DurationMs(a.options.t1_cookie_timeout)); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendInitAndEstablishConnection) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendInitAndEstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // INIT is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, InitChunk::kType); - AdvanceTime(options_.t1_init_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.t1_init_timeout); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendingInitTooManyTimesAborts) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendingInitTooManyTimesAborts) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // INIT is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, InitChunk::kType); - for (int i = 0; i < *options_.max_init_retransmits; ++i) { - AdvanceTime(options_.t1_init_timeout * (1 << i)); - RunTimers(); + for (int i = 0; i < *a.options.max_init_retransmits; ++i) { + AdvanceTime(a, z, a.options.t1_init_timeout * (1 << i)); // INIT is resent ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket resent_init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(resent_init_packet.descriptors()[0].type, InitChunk::kType); } // Another timeout, after the max init retransmits. - AdvanceTime(options_.t1_init_timeout * (1 << *options_.max_init_retransmits)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime( + a, z, a.options.t1_init_timeout * (1 << *a.options.max_init_retransmits)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, ResendCookieEchoAndEstablishConnection) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendCookieEchoAndEstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, CookieEchoChunk::kType); - AdvanceTime(options_.t1_init_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.t1_init_timeout); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, CookieEchoChunk::kType); - for (int i = 0; i < *options_.max_init_retransmits; ++i) { - AdvanceTime(options_.t1_cookie_timeout * (1 << i)); - RunTimers(); + for (int i = 0; i < *a.options.max_init_retransmits; ++i) { + AdvanceTime(a, z, a.options.t1_cookie_timeout * (1 << i)); // COOKIE_ECHO is resent ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket resent_init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(resent_init_packet.descriptors()[0].type, CookieEchoChunk::kType); } // Another timeout, after the max init retransmits. - AdvanceTime(options_.t1_cookie_timeout * - (1 << *options_.max_init_retransmits)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime( + a, z, + a.options.t1_cookie_timeout * (1 << *a.options.max_init_retransmits)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions); - sock_a_->Connect(); +TEST(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions); + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket cookie_echo_packet1, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_THAT(cookie_echo_packet1.descriptors(), SizeIs(2)); EXPECT_EQ(cookie_echo_packet1.descriptors()[0].type, CookieEchoChunk::kType); EXPECT_EQ(cookie_echo_packet1.descriptors()[1].type, DataChunk::kType); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // There are DATA chunks in the sent packet (that was lost), which means that // the T3-RTX timer is running, but as the socket is in kCookieEcho state, it // will be T1-COOKIE that drives retransmissions, so when the T3-RTX expires, // nothing should be retransmitted. - ASSERT_TRUE(options_.rto_initial < options_.t1_cookie_timeout); - AdvanceTime(options_.rto_initial); - RunTimers(); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + ASSERT_TRUE(a.options.rto_initial < a.options.t1_cookie_timeout); + AdvanceTime(a, z, a.options.rto_initial); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // When T1-COOKIE expires, both the COOKIE-ECHO and DATA should be present. - AdvanceTime(options_.t1_cookie_timeout - options_.rto_initial); - RunTimers(); + AdvanceTime(a, z, a.options.t1_cookie_timeout - a.options.rto_initial); // And this COOKIE-ECHO and DATA is also lost - never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket cookie_echo_packet2, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_THAT(cookie_echo_packet2.descriptors(), SizeIs(2)); EXPECT_EQ(cookie_echo_packet2.descriptors()[0].type, CookieEchoChunk::kType); EXPECT_EQ(cookie_echo_packet2.descriptors()[1].type, DataChunk::kType); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // COOKIE_ECHO has exponential backoff. - AdvanceTime(options_.t1_cookie_timeout * 2); - RunTimers(); + AdvanceTime(a, z, a.options.t1_cookie_timeout * 2); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); - EXPECT_THAT(cb_z_.ConsumeReceivedMessage()->payload(), + ExchangeMessages(a, z); + EXPECT_THAT(z.cb.ConsumeReceivedMessage()->payload(), SizeIs(kLargeMessageSize)); } -TEST_F(DcSctpSocketTest, ShutdownConnection) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ShutdownConnection) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); RTC_LOG(LS_INFO) << "Shutting down"; - sock_a_->Shutdown(); + EXPECT_CALL(z->cb, OnClosed).Times(1); + a.socket.Shutdown(); // Z reads SHUTDOWN, produces SHUTDOWN_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads SHUTDOWN_ACK, produces SHUTDOWN_COMPLETE - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Z reads SHUTDOWN_COMPLETE. - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); - EXPECT_EQ(sock_z_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); + EXPECT_EQ(z->socket.state(), SocketState::kClosed); + + z = MaybeHandoverSocket(std::move(z)); + EXPECT_EQ(z->socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) { - ConnectSockets(); +TEST(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Shutdown(); + ConnectSockets(a, z); + + a.socket.Shutdown(); // Drop first SHUTDOWN packet. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); - EXPECT_EQ(sock_a_->state(), SocketState::kShuttingDown); + EXPECT_EQ(a.socket.state(), SocketState::kShuttingDown); - for (int i = 0; i < *options_.max_retransmissions; ++i) { - AdvanceTime(DurationMs(options_.rto_initial * (1 << i))); - RunTimers(); + for (int i = 0; i < *a.options.max_retransmissions; ++i) { + AdvanceTime(a, z, DurationMs(a.options.rto_initial * (1 << i))); // Dropping every shutdown chunk. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(packet.descriptors()[0].type, ShutdownChunk::kType); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); } // The last expiry, makes it abort the connection. - AdvanceTime(options_.rto_initial * (1 << *options_.max_retransmissions)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime(a, z, + a.options.rto_initial * (1 << *a.options.max_retransmissions)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(packet.descriptors()[0].type, AbortChunk::kType); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); } -TEST_F(DcSctpSocketTest, EstablishConnectionWhileSendingData) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, EstablishConnectionWhileSendingData) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.socket.Connect(); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); } -TEST_F(DcSctpSocketTest, SendMessageAfterEstablished) { - ConnectSockets(); +TEST(DcSctpSocketTest, SendMessageAfterEstablished) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + ConnectSockets(a, z); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + + absl::optional msg = z.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); } -TEST_F(DcSctpSocketTest, TimeoutResendsPacket) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, TimeoutResendsPacket) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - cb_a_.ConsumeSentPacket(); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.cb.ConsumeSentPacket(); RTC_LOG(LS_INFO) << "Advancing time"; - AdvanceTime(options_.rto_initial); - RunTimers(); + AdvanceTime(a, *z, a.options.rto_initial); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, SendALotOfBytesMissedSecondPacket) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendALotOfBytesMissedSecondPacket) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Second DATA (lost) - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); // Retransmit and handle the rest - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); EXPECT_THAT(msg->payload(), testing::ElementsAreArray(payload)); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, SendingHeartbeatAnswersWithAck) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendingHeartbeatAnswersWithAck) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a HEARTBEAT chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); uint8_t info[] = {1, 2, 3, 4}; Parameters::Builder params_builder; params_builder.Add(HeartbeatInfoParameter(info)); b.Add(HeartbeatRequestChunk(params_builder.Build())); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // HEARTBEAT_ACK is sent as a reply. Capture it. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket ack_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); ASSERT_THAT(ack_packet.descriptors(), SizeIs(1)); ASSERT_HAS_VALUE_AND_ASSIGN( HeartbeatAckChunk ack, HeartbeatAckChunk::Parse(ack_packet.descriptors()[0].data)); ASSERT_HAS_VALUE_AND_ASSIGN(HeartbeatInfoParameter info_param, ack.info()); EXPECT_THAT(info_param.info(), ElementsAre(1, 2, 3, 4)); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, ExpectHeartbeatToBeSent) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ExpectHeartbeatToBeSent) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - AdvanceTime(options_.heartbeat_interval); - RunTimers(); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); - std::vector hb_packet_raw = cb_a_.ConsumeSentPacket(); + AdvanceTime(a, *z, a.options.heartbeat_interval); + + std::vector hb_packet_raw = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, SctpPacket::Parse(hb_packet_raw)); ASSERT_THAT(hb_packet.descriptors(), SizeIs(1)); @@ -784,77 +902,85 @@ TEST_F(DcSctpSocketTest, ExpectHeartbeatToBeSent) { EXPECT_THAT(hb.info()->info(), SizeIs(8)); // Feed it to Sock-z and expect a HEARTBEAT_ACK that will be propagated back. - sock_z_->ReceivePacket(hb_packet_raw); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + z->socket.ReceivePacket(hb_packet_raw); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, CloseConnectionAfterTooManyLostHeartbeats) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + CloseConnectionAfterTooManyLostHeartbeats) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), testing::IsEmpty()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(z->cb, OnClosed).Times(1); + EXPECT_THAT(a.cb.ConsumeSentPacket(), testing::IsEmpty()); // Force-close socket Z so that it doesn't interfere from now on. - sock_z_->Close(); + z->socket.Close(); - DurationMs time_to_next_hearbeat = options_.heartbeat_interval; + DurationMs time_to_next_hearbeat = a.options.heartbeat_interval; - for (int i = 0; i < *options_.max_retransmissions; ++i) { + for (int i = 0; i < *a.options.max_retransmissions; ++i) { RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending..."; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); // Dropping every heartbeat. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(hb_packet.descriptors()[0].type, HeartbeatRequestChunk::kType); RTC_LOG(LS_INFO) << "Letting the heartbeat expire."; - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); - time_to_next_hearbeat = options_.heartbeat_interval - DurationMs(1000); + time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000); } RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending..."; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); // Last heartbeat - EXPECT_THAT(cb_a_.ConsumeSentPacket(), Not(IsEmpty())); + EXPECT_THAT(a.cb.ConsumeSentPacket(), Not(IsEmpty())); - EXPECT_CALL(cb_a_, OnAborted).Times(1); + EXPECT_CALL(a.cb, OnAborted).Times(1); // Should suffice as exceeding RTO - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); + + z = MaybeHandoverSocket(std::move(z)); } -TEST_F(DcSctpSocketTest, RecoversAfterASuccessfulAck) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), testing::IsEmpty()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_THAT(a.cb.ConsumeSentPacket(), testing::IsEmpty()); + EXPECT_CALL(z->cb, OnClosed).Times(1); // Force-close socket Z so that it doesn't interfere from now on. - sock_z_->Close(); + z->socket.Close(); - DurationMs time_to_next_hearbeat = options_.heartbeat_interval; + DurationMs time_to_next_hearbeat = a.options.heartbeat_interval; - for (int i = 0; i < *options_.max_retransmissions; ++i) { - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + for (int i = 0; i < *a.options.max_retransmissions; ++i) { + AdvanceTime(a, *z, time_to_next_hearbeat); // Dropping every heartbeat. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); RTC_LOG(LS_INFO) << "Letting the heartbeat expire."; - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); - time_to_next_hearbeat = options_.heartbeat_interval - DurationMs(1000); + time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000); } RTC_LOG(LS_INFO) << "Getting the last heartbeat - and acking it"; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); - std::vector hb_packet_raw = cb_a_.ConsumeSentPacket(); + std::vector hb_packet_raw = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, SctpPacket::Parse(hb_packet_raw)); ASSERT_THAT(hb_packet.descriptors(), SizeIs(1)); @@ -862,333 +988,363 @@ TEST_F(DcSctpSocketTest, RecoversAfterASuccessfulAck) { HeartbeatRequestChunk hb, HeartbeatRequestChunk::Parse(hb_packet.descriptors()[0].data)); - SctpPacket::Builder b(sock_a_->verification_tag(), options_); + SctpPacket::Builder b(a.socket.verification_tag(), a.options); b.Add(HeartbeatAckChunk(std::move(hb).extract_parameters())); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // Should suffice as exceeding RTO - which will not fire. - EXPECT_CALL(cb_a_, OnAborted).Times(0); - AdvanceTime(DurationMs(1000)); - RunTimers(); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_CALL(a.cb, OnAborted).Times(0); + AdvanceTime(a, *z, DurationMs(1000)); + + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // Verify that we get new heartbeats again. RTC_LOG(LS_INFO) << "Expecting a new heartbeat"; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket another_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(another_packet.descriptors()[0].type, HeartbeatRequestChunk::kType); } -TEST_F(DcSctpSocketTest, ResetStream) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ResetStream) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), {}); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), {}); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Reset the outgoing stream. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(1)})); // Receiving the packet will trigger a callback, indicating that A has // reset its stream. It will also send a RE-CONFIG with a response. - EXPECT_CALL(cb_z_, OnIncomingStreamsReset).Times(1); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + EXPECT_CALL(z->cb, OnIncomingStreamsReset).Times(1); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Receiving a response will trigger a callback. Streams are now reset. - EXPECT_CALL(cb_a_, OnStreamsResetPerformed).Times(1); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + EXPECT_CALL(a.cb, OnStreamsResetPerformed).Times(1); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, ResetStreamWillMakeChunksStartAtZeroSsn) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ResetStreamWillMakeChunksStartAtZeroSsn) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - std::vector payload(options_.mtu - 100); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + std::vector payload(a.options.mtu - 100); - auto packet1 = cb_a_.ConsumeSentPacket(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + + auto packet1 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet1); + z->socket.ReceivePacket(packet1); - auto packet2 = cb_a_.ConsumeSentPacket(); + auto packet2 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet2, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet2); + z->socket.ReceivePacket(packet2); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->stream_id(), StreamID(1)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->stream_id(), StreamID(1)); // Reset the outgoing stream. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(1)})); // RE-CONFIG, req - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // RE-CONFIG, resp - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - auto packet3 = cb_a_.ConsumeSentPacket(); + auto packet3 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet3, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet3); + z->socket.ReceivePacket(packet3); - auto packet4 = cb_a_.ConsumeSentPacket(); + auto packet4 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet4, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet4); + z->socket.ReceivePacket(packet4); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, ResetStreamWillOnlyResetTheRequestedStreams) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + ResetStreamWillOnlyResetTheRequestedStreams) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - std::vector payload(options_.mtu - 100); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + std::vector payload(a.options.mtu - 100); // Send two ordered messages on SID 1 - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - auto packet1 = cb_a_.ConsumeSentPacket(); + auto packet1 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet1, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet1); + z->socket.ReceivePacket(packet1); - auto packet2 = cb_a_.ConsumeSentPacket(); + auto packet2 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet2, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet2); + z->socket.ReceivePacket(packet2); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Do the same, for SID 3 - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - auto packet3 = cb_a_.ConsumeSentPacket(); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + auto packet3 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet3, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet3, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet3); - auto packet4 = cb_a_.ConsumeSentPacket(); + z->socket.ReceivePacket(packet3); + auto packet4 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet4, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet4, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet4); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + z->socket.ReceivePacket(packet4); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Receive all messages. - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->stream_id(), StreamID(1)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->stream_id(), StreamID(1)); - absl::optional msg3 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg3 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg3.has_value()); EXPECT_EQ(msg3->stream_id(), StreamID(3)); - absl::optional msg4 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg4 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg4.has_value()); EXPECT_EQ(msg4->stream_id(), StreamID(3)); // Reset SID 1. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(3)})); + a.socket.ResetStreams(std::vector({StreamID(3)})); // RE-CONFIG, req - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // RE-CONFIG, resp - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Send a message on SID 1 and 3 - SID 1 should not be reset, but 3 should. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - auto packet5 = cb_a_.ConsumeSentPacket(); + auto packet5 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet5, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet5, HasDataChunkWithSsn(SSN(2))); // Unchanged. - sock_z_->ReceivePacket(packet5); + z->socket.ReceivePacket(packet5); - auto packet6 = cb_a_.ConsumeSentPacket(); + auto packet6 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet6, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet6, HasDataChunkWithSsn(SSN(0))); // Reset. - sock_z_->ReceivePacket(packet6); + z->socket.ReceivePacket(packet6); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, OnePeerReconnects) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, OnePeerReconnects) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(1); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(1); // Let's be evil here - reconnect while a fragmented packet was about to be // sent. The receiving side should get it in full. std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Create a new association, z2 - and don't use z anymore. - testing::NiceMock cb_z2("Z2"); - DcSctpSocket sock_z2("Z2", cb_z2, nullptr, options_); - - sock_z2.Connect(); + SocketUnderTest z2("Z2"); + z2.socket.Connect(); // Retransmit and handle the rest. As there will be some chunks in-flight that // have the wrong verification tag, those will yield errors. - ExchangeMessages(*sock_a_, cb_a_, sock_z2, cb_z2); + ExchangeMessages(a, z2); - absl::optional msg = cb_z2.ConsumeReceivedMessage(); + absl::optional msg = z2.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); EXPECT_THAT(msg->payload(), testing::ElementsAreArray(payload)); } -TEST_F(DcSctpSocketTest, SendMessageWithLimitedRtx) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendMessageWithLimitedRtx) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions send_options; send_options.max_retransmissions = 0; - std::vector payload(options_.mtu - 100); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); + std::vector payload(a.options.mtu - 100); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Second DATA (lost) - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); // Third DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Handle SACK for first DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Handle delayed SACK for third DATA - AdvanceTime(options_.delayed_ack_max_timeout); - RunTimers(); + AdvanceTime(a, *z, a.options.delayed_ack_max_timeout); // Handle SACK for second DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Now the missing data chunk will be marked as nacked, but it might still be // in-flight and the reported gap could be due to out-of-order delivery. So // the RetransmissionQueue will not mark it as "to be retransmitted" until // after the t3-rtx timer has expired. - AdvanceTime(options_.rto_initial); - RunTimers(); + AdvanceTime(a, *z, a.options.rto_initial); // The chunk will be marked as retransmitted, and then as abandoned, which // will trigger a FORWARD-TSN to be sent. // FORWARD-TSN (third) - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Which will trigger a SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->ppid(), PPID(51)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->ppid(), PPID(53)); - absl::optional msg3 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg3 = z->cb.ConsumeReceivedMessage(); EXPECT_FALSE(msg3.has_value()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, SendManyFragmentedMessagesWithLimitedRtx) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendManyFragmentedMessagesWithLimitedRtx) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions send_options; send_options.unordered = IsUnordered(true); send_options.max_retransmissions = 0; - std::vector payload(options_.mtu * 2 - 100 /* margin */); + std::vector payload(a.options.mtu * 2 - 100 /* margin */); // Sending first message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); // Sending second message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); // Sending third message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); // Sending fourth message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(54), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(54), payload), send_options); // First DATA, first fragment - std::vector packet = cb_a_.ConsumeSentPacket(); + std::vector packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(51))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // First DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(51))); // Second DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(52))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Second DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(52))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); // Third DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(53))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Third DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(53))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); // Fourth DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(54))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Fourth DATA, second fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(54))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); // Let the RTX timer expire, and exchange FORWARD-TSN/SACKs - AdvanceTime(options_.rto_initial); - RunTimers(); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + AdvanceTime(a, *z, a.options.rto_initial); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + ExchangeMessages(a, *z); + + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->ppid(), PPID(54)); - ASSERT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + ASSERT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } struct FakeChunkConfig : ChunkConfig { @@ -1210,17 +1366,21 @@ class FakeChunk : public Chunk, public TLVTrait { std::string ToString() const override { return "FAKE"; } }; -TEST_F(DcSctpSocketTest, ReceivingUnknownChunkRespondsWithError) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ReceivingUnknownChunkRespondsWithError) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a FAKE chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); b.Add(FakeChunk()); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // ERROR is sent as a reply. Capture it. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket reply_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); ASSERT_THAT(reply_packet.descriptors(), SizeIs(1)); ASSERT_HAS_VALUE_AND_ASSIGN( ErrorChunk error, ErrorChunk::Parse(reply_packet.descriptors()[0].data)); @@ -1228,46 +1388,53 @@ TEST_F(DcSctpSocketTest, ReceivingUnknownChunkRespondsWithError) { UnrecognizedChunkTypeCause cause, error.error_causes().get()); EXPECT_THAT(cause.unrecognized_chunk(), ElementsAre(0x49, 0x00, 0x00, 0x04)); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, ReceivingErrorChunkReportsAsCallback) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, ReceivingErrorChunkReportsAsCallback) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a ERROR chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); b.Add( ErrorChunk(Parameters::Builder() .Add(UnrecognizedChunkTypeCause({0x49, 0x00, 0x00, 0x04})) .Build())); - EXPECT_CALL(cb_a_, OnError(ErrorKind::kPeerReported, - HasSubstr("Unrecognized Chunk Type"))); - sock_a_->ReceivePacket(b.Build()); + EXPECT_CALL(a.cb, OnError(ErrorKind::kPeerReported, + HasSubstr("Unrecognized Chunk Type"))); + a.socket.ReceivePacket(b.Build()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { - // Create a new association, z2 - and don't use z anymore. - testing::NiceMock cb_z2("Z2"); - DcSctpOptions options = options_; +TEST(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { + SocketUnderTest a("A"); + constexpr size_t kReceiveWindowBufferSize = 2000; - options.max_receiver_window_buffer_size = kReceiveWindowBufferSize; - options.mtu = 3000; - DcSctpSocket sock_z2("Z2", cb_z2, nullptr, options); + SocketUnderTest z( + "Z", {.mtu = 3000, + .max_receiver_window_buffer_size = kReceiveWindowBufferSize}); - EXPECT_CALL(cb_z2, OnClosed).Times(0); - EXPECT_CALL(cb_z2, OnAborted).Times(0); + EXPECT_CALL(z.cb, OnClosed).Times(0); + EXPECT_CALL(z.cb, OnAborted).Times(0); - sock_a_->Connect(); - std::vector init_data = cb_a_.ConsumeSentPacket(); + a.socket.Connect(); + std::vector init_data = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, SctpPacket::Parse(init_data)); ASSERT_HAS_VALUE_AND_ASSIGN( InitChunk init_chunk, InitChunk::Parse(init_packet.descriptors()[0].data)); - sock_z2.ReceivePacket(init_data); - sock_a_->ReceivePacket(cb_z2.ConsumeSentPacket()); - sock_z2.ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z2.ConsumeSentPacket()); + z.socket.ReceivePacket(init_data); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Fill up Z2 to the high watermark limit. constexpr size_t kWatermarkLimit = @@ -1277,98 +1444,105 @@ TEST_F(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { TSN tsn = init_chunk.initial_tsn(); AnyDataChunk::Options opts; opts.is_beginning = Data::IsBeginning(true); - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(tsn, StreamID(1), SSN(0), PPID(53), std::vector(kWatermarkLimit + 1), opts)) .Build()); // First DATA will always trigger a SACK. It's not interesting. - EXPECT_THAT(cb_z2.ConsumeSentPacket(), + EXPECT_THAT(z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(tsn), HasSackWithNoGapAckBlocks())); // This DATA should be accepted - it's advancing cum ack tsn. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 1), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 1), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // The receiver might have moved into delayed ack mode. - cb_z2.AdvanceTime(options.rto_initial); - RunTimers(cb_z2, sock_z2); + AdvanceTime(a, z, z.options.rto_initial); EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA will not be accepted - it's not advancing cum ack tsn. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // Sack will be sent in IMMEDIATE mode when this is happening. EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA will not be accepted either. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 4), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 4), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // Sack will be sent in IMMEDIATE mode when this is happening. EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA should be accepted, and it fills the reassembly queue. - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(AddTo(tsn, 2), StreamID(1), SSN(0), PPID(53), std::vector(kRemainingSize), /*options=*/{})) .Build()); // The receiver might have moved into delayed ack mode. - cb_z2.AdvanceTime(options.rto_initial); - RunTimers(cb_z2, sock_z2); + AdvanceTime(a, z, z.options.rto_initial); EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 2)), HasSackWithNoGapAckBlocks())); - EXPECT_CALL(cb_z2, OnAborted(ErrorKind::kResourceExhaustion, _)); - EXPECT_CALL(cb_z2, OnClosed).Times(0); + EXPECT_CALL(z.cb, OnAborted(ErrorKind::kResourceExhaustion, _)); + EXPECT_CALL(z.cb, OnClosed).Times(0); // This DATA will make the connection close. It's too full now. - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), PPID(53), std::vector(kSmallMessageSize), /*options=*/{})) .Build()); } -TEST_F(DcSctpSocketTest, SetMaxMessageSize) { - sock_a_->SetMaxMessageSize(42u); - EXPECT_EQ(sock_a_->options().max_message_size, 42u); +TEST(DcSctpSocketTest, SetMaxMessageSize) { + SocketUnderTest a("A"); + + a.socket.SetMaxMessageSize(42u); + EXPECT_EQ(a.socket.options().max_message_size, 42u); } -TEST_F(DcSctpSocketTest, SendsMessagesWithLowLifetime) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendsMessagesWithLowLifetime) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Mock that the time always goes forward. TimeMs now(0); - EXPECT_CALL(cb_a_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); - EXPECT_CALL(cb_z_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); @@ -1381,23 +1555,30 @@ TEST_F(DcSctpSocketTest, SendsMessagesWithLowLifetime) { send_options.unordered = IsUnordered((i % 2) == 0); send_options.lifetime = DurationMs(i % 3); // 0, 1, 2 ms - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); } - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); for (int i = 0; i < kIterations; ++i) { - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z->cb.ConsumeReceivedMessage().has_value()); } - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); // Validate that the sockets really make the time move forward. EXPECT_GE(*now, kIterations * 2); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, DiscardsMessagesWithLowLifetimeIfMustBuffer) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + DiscardsMessagesWithLowLifetimeIfMustBuffer) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions lifetime_0; lifetime_0.unordered = IsUnordered(true); @@ -1409,202 +1590,255 @@ TEST_F(DcSctpSocketTest, DiscardsMessagesWithLowLifetimeIfMustBuffer) { // Mock that the time always goes forward. TimeMs now(0); - EXPECT_CALL(cb_a_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); - EXPECT_CALL(cb_z_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); // Fill up the send buffer with a large message. std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // And queue a few small messages with lifetime=0 or 1 ms - can't be sent. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), lifetime_0); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {4, 5, 6}), lifetime_1); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {7, 8, 9}), lifetime_0); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), lifetime_0); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {4, 5, 6}), lifetime_1); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {7, 8, 9}), lifetime_0); // Handle all that was sent until congestion window got full. for (;;) { - std::vector packet_from_a = cb_a_.ConsumeSentPacket(); + std::vector packet_from_a = a.cb.ConsumeSentPacket(); if (packet_from_a.empty()) { break; } - sock_z_->ReceivePacket(std::move(packet_from_a)); + z->socket.ReceivePacket(std::move(packet_from_a)); } // Shouldn't be enough to send that large message. - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); // Exchange the rest of the messages, with the time ever increasing. - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); // The large message should be delivered. It was sent reliably. - ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage m1, cb_z_.ConsumeReceivedMessage()); + ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage m1, z->cb.ConsumeReceivedMessage()); EXPECT_EQ(m1.stream_id(), StreamID(1)); EXPECT_THAT(m1.payload(), SizeIs(kLargeMessageSize)); // But none of the smaller messages. - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, HasReasonableBufferedAmountValues) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, HasReasonableBufferedAmountValues) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_EQ(sock_a_->buffered_amount(StreamID(1)), 0u); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kSmallMessageSize)), - kSendOptions); + EXPECT_EQ(a.socket.buffered_amount(StreamID(1)), 0u); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kSmallMessageSize)), + kSendOptions); // Sending a small message will directly send it as a single packet, so // nothing is left in the queue. - EXPECT_EQ(sock_a_->buffered_amount(StreamID(1)), 0u); + EXPECT_EQ(a.socket.buffered_amount(StreamID(1)), 0u); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions); // Sending a message will directly start sending a few packets, so the // buffered amount is not the full message size. - EXPECT_GT(sock_a_->buffered_amount(StreamID(1)), 0u); - EXPECT_LT(sock_a_->buffered_amount(StreamID(1)), kLargeMessageSize); + EXPECT_GT(a.socket.buffered_amount(StreamID(1)), 0u); + EXPECT_LT(a.socket.buffered_amount(StreamID(1)), kLargeMessageSize); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, HasDefaultOnBufferedAmountLowValueZero) { - EXPECT_EQ(sock_a_->buffered_amount_low_threshold(StreamID(1)), 0u); +TEST(DcSctpSocketTest, HasDefaultOnBufferedAmountLowValueZero) { + SocketUnderTest a("A"); + EXPECT_EQ(a.socket.buffered_amount_low_threshold(StreamID(1)), 0u); } -TEST_F(DcSctpSocketTest, TriggersOnBufferedAmountLowWithDefaultValueZero) { - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + TriggersOnBufferedAmountLowWithDefaultValueZero) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kSmallMessageSize)), - kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kSmallMessageSize)), + kSendOptions); + ExchangeMessages(a, *z); + + EXPECT_CALL(a.cb, OnBufferedAmountLow).WillRepeatedly(testing::Return()); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, DoesntTriggerOnBufferedAmountLowIfBelowThreshold) { +TEST_P(DcSctpSocketParametrizedTest, + DoesntTriggerOnBufferedAmountLowIfBelowThreshold) { static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize * 10; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), - kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(0); - sock_a_->Send( + a.socket.SetBufferedAmountLowThreshold(StreamID(1), + kBufferedAmountLowThreshold); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(0); + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, TriggersOnBufferedAmountMultipleTimes) { +TEST_P(DcSctpSocketParametrizedTest, TriggersOnBufferedAmountMultipleTimes) { static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize / 2; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), - kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(3); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(2))).Times(2); - sock_a_->Send( + a.socket.SetBufferedAmountLowThreshold(StreamID(1), + kBufferedAmountLowThreshold); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(3); + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(2))).Times(2); + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(2), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(2), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, TriggersOnBufferedAmountLowOnlyWhenCrossingThreshold) { +TEST_P(DcSctpSocketParametrizedTest, + TriggersOnBufferedAmountLowOnlyWhenCrossingThreshold) { static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize * 1.5; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), - kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); + a.socket.SetBufferedAmountLowThreshold(StreamID(1), + kBufferedAmountLowThreshold); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); // Add a few messages to fill up the congestion window. When that is full, // messages will start to be fully buffered. - while (sock_a_->buffered_amount(StreamID(1)) <= kBufferedAmountLowThreshold) { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kMessageSize)), - kSendOptions); + while (a.socket.buffered_amount(StreamID(1)) <= kBufferedAmountLowThreshold) { + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kMessageSize)), + kSendOptions); } - size_t initial_buffered = sock_a_->buffered_amount(StreamID(1)); + size_t initial_buffered = a.socket.buffered_amount(StreamID(1)); ASSERT_GT(initial_buffered, kBufferedAmountLowThreshold); // Start ACKing packets, which will empty the send queue, and trigger the // callback. - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(1); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(1); + ExchangeMessages(a, *z); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, DoesntTriggerOnTotalBufferAmountLowWhenBelow) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + DoesntTriggerOnTotalBufferAmountLowWhenBelow) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions); + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(0); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions); + + ExchangeMessages(a, *z); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, TriggersOnTotalBufferAmountLowWhenCrossingThreshold) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, + TriggersOnTotalBufferAmountLowWhenCrossingThreshold) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(0); // Fill up the send queue completely. for (;;) { - if (sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions) == SendStatus::kErrorResourceExhaustion) { + if (a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions) == SendStatus::kErrorResourceExhaustion) { break; } } - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(1); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(1); + ExchangeMessages(a, *z); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, InitialMetricsAreZeroed) { - Metrics metrics = sock_a_->GetMetrics(); +TEST(DcSctpSocketTest, InitialMetricsAreZeroed) { + SocketUnderTest a("A"); + + Metrics metrics = a.socket.GetMetrics(); EXPECT_EQ(metrics.tx_packets_count, 0u); EXPECT_EQ(metrics.tx_messages_count, 0u); EXPECT_EQ(metrics.cwnd_bytes.has_value(), false); @@ -1615,84 +1849,90 @@ TEST_F(DcSctpSocketTest, InitialMetricsAreZeroed) { EXPECT_EQ(metrics.peer_rwnd_bytes.has_value(), false); } -TEST_F(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) { - ConnectSockets(); +TEST(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - const size_t initial_a_rwnd = options_.max_receiver_window_buffer_size * + ConnectSockets(a, z); + + const size_t initial_a_rwnd = a.options.max_receiver_window_buffer_size * ReassemblyQueue::kHighWatermarkLimit; - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 2u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 2u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 0u); - EXPECT_EQ(*sock_a_->GetMetrics().cwnd_bytes, - options_.cwnd_mtus_initial * options_.mtu); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 0u); + EXPECT_EQ(*a.socket.GetMetrics().cwnd_bytes, + a.options.cwnd_mtus_initial * a.options.mtu); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 2u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 0u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 2u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 0u); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 1u); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 1u); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z.cb.ConsumeReceivedMessage().has_value()); - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 3u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 3u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 1u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 3u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 3u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 1u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 3u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 1u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 3u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 1u); // Send one more (large - fragmented), and receive the delayed SACK. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(options_.mtu * 2 + 1)), - kSendOptions); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 3u); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(a.options.mtu * 2 + 1)), + kSendOptions); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 3u); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 1u); - EXPECT_GT(*sock_a_->GetMetrics().peer_rwnd_bytes, 0u); - EXPECT_LT(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 1u); + EXPECT_GT(*a.socket.GetMetrics().peer_rwnd_bytes, 0u); + EXPECT_LT(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z.cb.ConsumeReceivedMessage().has_value()); - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 6u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 4u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 6u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 4u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 2u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 6u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 2u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 6u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 2u); // Delayed sack - AdvanceTime(options_.delayed_ack_max_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.delayed_ack_max_timeout); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 5u); - EXPECT_EQ(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 5u); + EXPECT_EQ(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); } -TEST_F(DcSctpSocketTest, UnackDataAlsoIncludesSendQueue) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, UnackDataAlsoIncludesSendQueue) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions); size_t payload_bytes = - options_.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; + a.options.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; - size_t expected_sent_packets = options_.cwnd_mtus_initial; + size_t expected_sent_packets = a.options.cwnd_mtus_initial; size_t expected_queued_bytes = kLargeMessageSize - expected_sent_packets * payload_bytes; @@ -1701,35 +1941,48 @@ TEST_F(DcSctpSocketTest, UnackDataAlsoIncludesSendQueue) { // Due to alignment, padding etc, it's hard to calculate the exact number, but // it should be in this range. - EXPECT_GE(sock_a_->GetMetrics().unack_data_count, + EXPECT_GE(a.socket.GetMetrics().unack_data_count, expected_sent_packets + expected_queued_packets); - EXPECT_LE(sock_a_->GetMetrics().unack_data_count, + EXPECT_LE(a.socket.GetMetrics().unack_data_count, expected_sent_packets + expected_queued_packets + 2); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, DoesntSendMoreThanMaxBurstPackets) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, DoesntSendMoreThanMaxBurstPackets) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(kLargeMessageSize)), - kSendOptions); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(kLargeMessageSize)), + kSendOptions); for (int i = 0; i < kMaxBurstPackets; ++i) { - std::vector packet = cb_a_.ConsumeSentPacket(); + std::vector packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, Not(IsEmpty())); - sock_z_->ReceivePacket(std::move(packet)); // DATA + z->socket.ReceivePacket(std::move(packet)); // DATA } - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); + + ExchangeMessages(a, *z); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, SendsOnlyLargePackets) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, SendsOnlyLargePackets) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // A really large message, to ensure that the congestion window is often full. constexpr size_t kMessageSize = 100000; - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); @@ -1737,21 +1990,21 @@ TEST_F(DcSctpSocketTest, SendsOnlyLargePackets) { std::vector data_packet_sizes; do { delivered_packet = false; - std::vector packet_from_a = cb_a_.ConsumeSentPacket(); + std::vector packet_from_a = a.cb.ConsumeSentPacket(); if (!packet_from_a.empty()) { data_packet_sizes.push_back(packet_from_a.size()); delivered_packet = true; - sock_z_->ReceivePacket(std::move(packet_from_a)); + z->socket.ReceivePacket(std::move(packet_from_a)); } - std::vector packet_from_z = cb_z_.ConsumeSentPacket(); + std::vector packet_from_z = z->cb.ConsumeSentPacket(); if (!packet_from_z.empty()) { delivered_packet = true; - sock_a_->ReceivePacket(std::move(packet_from_z)); + a.socket.ReceivePacket(std::move(packet_from_z)); } } while (delivered_packet); size_t packet_payload_bytes = - options_.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; + a.options.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; // +1 accounts for padding, and rounding up. size_t expected_packets = (kMessageSize + packet_payload_bytes - 1) / packet_payload_bytes + 1; @@ -1763,90 +2016,203 @@ TEST_F(DcSctpSocketTest, SendsOnlyLargePackets) { for (size_t size : data_packet_sizes) { // The 4 is for padding/alignment. - EXPECT_GE(size, options_.mtu - 4); + EXPECT_GE(size, a.options.mtu - 4); } + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, DoesntBundleForwardTsnWithData) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, DoesntBundleForwardTsnWithData) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Force an RTT measurement using heartbeats. - AdvanceTime(options_.heartbeat_interval); - RunTimers(); + AdvanceTime(a, *z, a.options.heartbeat_interval); // HEARTBEAT - std::vector hb_req_a = cb_a_.ConsumeSentPacket(); - std::vector hb_req_z = cb_z_.ConsumeSentPacket(); + std::vector hb_req_a = a.cb.ConsumeSentPacket(); + std::vector hb_req_z = z->cb.ConsumeSentPacket(); constexpr DurationMs kRtt = DurationMs(80); - AdvanceTime(kRtt); - sock_z_->ReceivePacket(hb_req_a); - sock_a_->ReceivePacket(hb_req_z); + AdvanceTime(a, *z, kRtt); + z->socket.ReceivePacket(hb_req_a); + a.socket.ReceivePacket(hb_req_z); // HEARTBEAT_ACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); SendOptions send_options; send_options.max_retransmissions = 0; - std::vector payload(options_.mtu - 100); + std::vector payload(a.options.mtu - 100); // Send an initial message that is received, but the SACK was lost - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); // DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // SACK (lost) - std::vector sack = cb_z_.ConsumeSentPacket(); + std::vector sack = z->cb.ConsumeSentPacket(); // Queue enough messages to fill the congestion window. do { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - } while (!cb_a_.ConsumeSentPacket().empty()); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + } while (!a.cb.ConsumeSentPacket().empty()); // Enqueue at least one more. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); // Let all of them expire by T3-RTX and inspect what's sent. - AdvanceTime(options_.rto_initial); - RunTimers(); + AdvanceTime(a, *z, a.options.rto_initial); - std::vector sent1 = cb_a_.ConsumeSentPacket(); + std::vector sent1 = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet1, SctpPacket::Parse(sent1)); EXPECT_THAT(packet1.descriptors(), SizeIs(1)); EXPECT_EQ(packet1.descriptors()[0].type, ForwardTsnChunk::kType); - std::vector sent2 = cb_a_.ConsumeSentPacket(); + std::vector sent2 = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet2, SctpPacket::Parse(sent2)); EXPECT_GE(packet2.descriptors().size(), 1u); EXPECT_EQ(packet2.descriptors()[0].type, DataChunk::kType); // Drop all remaining packets that A has sent. - while (!cb_a_.ConsumeSentPacket().empty()) { + while (!a.cb.ConsumeSentPacket().empty()) { } // Replay the SACK, and see if a FORWARD-TSN is sent again. - sock_a_->ReceivePacket(sack); + a.socket.ReceivePacket(sack); // It shouldn't be sent as not enough time has passed yet. Instead, more // DATA chunks are sent, that are in the queue. - std::vector sent3 = cb_a_.ConsumeSentPacket(); + std::vector sent3 = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet3, SctpPacket::Parse(sent3)); EXPECT_GE(packet2.descriptors().size(), 1u); EXPECT_EQ(packet3.descriptors()[0].type, DataChunk::kType); // Now let RTT time pass, to allow a FORWARD-TSN to be sent again. - AdvanceTime(kRtt); - sock_a_->ReceivePacket(sack); + AdvanceTime(a, *z, kRtt); + a.socket.ReceivePacket(sack); - std::vector sent4 = cb_a_.ConsumeSentPacket(); + std::vector sent4 = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet4, SctpPacket::Parse(sent4)); EXPECT_THAT(packet4.descriptors(), SizeIs(1)); EXPECT_EQ(packet4.descriptors()[0].type, ForwardTsnChunk::kType); } +TEST(DcSctpSocketTest, SendMessagesAfterHandover) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + + // Send message before handover to move socket to a not initial state + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + z->cb.ConsumeReceivedMessage(); + + z = HandoverSocket(std::move(z)); + + absl::optional msg; + + RTC_LOG(LS_INFO) << "Sending A #1"; + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {3, 4}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + + msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(1)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(3, 4)); + + RTC_LOG(LS_INFO) << "Sending A #2"; + + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {5, 6}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + + msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(2)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(5, 6)); + + RTC_LOG(LS_INFO) << "Sending Z #1"; + + z->socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), kSendOptions); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // ack + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // data + + msg = a.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(1)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(1, 2, 3)); +} + +TEST(DcSctpSocketTest, CanDetectDcsctpImplementation) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); + + EXPECT_EQ(a.socket.peer_implementation(), SctpImplementation::kDcsctp); + + // As A initiated the connection establishment, Z will not receive enough + // information to know about A's implementation + EXPECT_EQ(z.socket.peer_implementation(), SctpImplementation::kUnknown); +} + +TEST(DcSctpSocketTest, BothCanDetectDcsctpImplementation) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + a.socket.Connect(); + z.socket.Connect(); + + ExchangeMessages(a, z); + + EXPECT_EQ(a.socket.peer_implementation(), SctpImplementation::kDcsctp); + EXPECT_EQ(z.socket.peer_implementation(), SctpImplementation::kDcsctp); +} + +TEST_P(DcSctpSocketParametrizedTest, CanLoseFirstOrderedMessage) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + SendOptions send_options; + send_options.unordered = IsUnordered(false); + send_options.max_retransmissions = 0; + std::vector payload(a.options.mtu - 100); + + // Send a first message (SID=1, SSN=0) + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + + // First DATA is lost, and retransmission timer will delete it. + a.cb.ConsumeSentPacket(); + AdvanceTime(a, *z, a.options.rto_initial); + ExchangeMessages(a, *z); + + // Send a second message (SID=0, SSN=1). + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + ExchangeMessages(a, *z); + + // The Z socket should receive the second message, but not the first. + absl::optional msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->ppid(), PPID(52)); + + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); + + MaybeHandoverSocketAndSendMessage(a, std::move(z)); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/heartbeat_handler.cc b/net/dcsctp/socket/heartbeat_handler.cc index 87baa147d9..9588b85b59 100644 --- a/net/dcsctp/socket/heartbeat_handler.cc +++ b/net/dcsctp/socket/heartbeat_handler.cc @@ -153,9 +153,10 @@ void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) { return; } - DurationMs duration(*ctx_->callbacks().TimeMillis() - *info->created_at()); - - ctx_->ObserveRTT(duration); + TimeMs now = ctx_->callbacks().TimeMillis(); + if (info->created_at() > TimeMs(0) && info->created_at() <= now) { + ctx_->ObserveRTT(now - info->created_at()); + } // https://tools.ietf.org/html/rfc4960#section-8.1 // "The counter shall be reset each time ... a HEARTBEAT ACK is received from diff --git a/net/dcsctp/socket/heartbeat_handler_test.cc b/net/dcsctp/socket/heartbeat_handler_test.cc index 2c5df9fd92..faa0e3da06 100644 --- a/net/dcsctp/socket/heartbeat_handler_test.cc +++ b/net/dcsctp/socket/heartbeat_handler_test.cc @@ -13,6 +13,7 @@ #include #include +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h" #include "net/dcsctp/packet/chunk/heartbeat_request_chunk.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" @@ -44,7 +45,9 @@ class HeartbeatHandlerTestBase : public testing::Test { explicit HeartbeatHandlerTestBase(DurationMs heartbeat_interval) : options_(MakeOptions(heartbeat_interval)), context_(&callbacks_), - timer_manager_([this]() { return callbacks_.CreateTimeout(); }), + timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return callbacks_.CreateTimeout(precision); + }), handler_("log: ", options_, &context_, &timer_manager_) {} void AdvanceTime(DurationMs duration) { @@ -135,6 +138,29 @@ TEST_F(HeartbeatHandlerTest, SendsHeartbeatRequestsOnIdleChannel) { handler_.HandleHeartbeatAck(std::move(ack)); } +TEST_F(HeartbeatHandlerTest, DoesntObserveInvalidHeartbeats) { + AdvanceTime(options_.heartbeat_interval); + + // Grab the request, and make a response. + std::vector payload = callbacks_.ConsumeSentPacket(); + ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(payload)); + ASSERT_THAT(packet.descriptors(), SizeIs(1)); + + ASSERT_HAS_VALUE_AND_ASSIGN( + HeartbeatRequestChunk req, + HeartbeatRequestChunk::Parse(packet.descriptors()[0].data)); + + HeartbeatAckChunk ack(std::move(req).extract_parameters()); + + EXPECT_CALL(context_, ObserveRTT).Times(0); + + // Go backwards in time - which make the HEARTBEAT-ACK have an invalid + // timestamp in it, as it will be in the future. + callbacks_.AdvanceTime(DurationMs(-100)); + + handler_.HandleHeartbeatAck(std::move(ack)); +} + TEST_F(HeartbeatHandlerTest, IncreasesErrorIfNotAckedInTime) { DurationMs rto(105); EXPECT_CALL(context_, current_rto).WillOnce(Return(rto)); diff --git a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h index 894dd9ac5a..803f688a84 100644 --- a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h +++ b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h @@ -20,6 +20,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_socket.h" #include "net/dcsctp/public/timeout.h" @@ -87,7 +88,9 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks { (rtc::ArrayView data), (override)); - std::unique_ptr CreateTimeout() override { + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) override { + // The fake timeout manager does not implement |precision|. return timeout_manager_.CreateTimeout(); } diff --git a/net/dcsctp/socket/state_cookie_test.cc b/net/dcsctp/socket/state_cookie_test.cc index eab41a7a56..870620ba14 100644 --- a/net/dcsctp/socket/state_cookie_test.cc +++ b/net/dcsctp/socket/state_cookie_test.cc @@ -36,5 +36,18 @@ TEST(StateCookieTest, SerializeAndDeserialize) { EXPECT_TRUE(deserialized.capabilities().reconfig); } +TEST(StateCookieTest, ValidateMagicValue) { + Capabilities capabilities = {/*partial_reliability=*/true, + /*message_interleaving=*/false, + /*reconfig=*/true}; + StateCookie cookie(VerificationTag(123), TSN(456), + /*a_rwnd=*/789, TieTag(101112), capabilities); + std::vector serialized = cookie.Serialize(); + ASSERT_THAT(serialized, SizeIs(StateCookie::kCookieSize)); + + absl::string_view magic(reinterpret_cast(serialized.data()), 8); + EXPECT_EQ(magic, "dcSCTP00"); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/stream_reset_handler.cc b/net/dcsctp/socket/stream_reset_handler.cc index 40cb8f6166..1c6ce09e56 100644 --- a/net/dcsctp/socket/stream_reset_handler.cc +++ b/net/dcsctp/socket/stream_reset_handler.cc @@ -343,4 +343,20 @@ absl::optional StreamResetHandler::OnReconfigTimerExpiry() { return ctx_->current_rto(); } +HandoverReadinessStatus StreamResetHandler::GetHandoverReadiness() const { + HandoverReadinessStatus status; + if (!streams_to_reset_.empty()) { + status.Add(HandoverUnreadinessReason::kPendingStreamReset); + } + if (current_request_.has_value()) { + status.Add(HandoverUnreadinessReason::kPendingStreamResetRequest); + } + return status; +} + +void StreamResetHandler::AddHandoverState(DcSctpSocketHandoverState& state) { + state.rx.last_completed_reset_req_sn = last_processed_req_seq_nbr_.value(); + state.tx.next_reset_req_sn = next_outgoing_req_seq_nbr_.value(); +} + } // namespace dcsctp diff --git a/net/dcsctp/socket/stream_reset_handler.h b/net/dcsctp/socket/stream_reset_handler.h index 7f72889356..a691eb8312 100644 --- a/net/dcsctp/socket/stream_reset_handler.h +++ b/net/dcsctp/socket/stream_reset_handler.h @@ -70,7 +70,8 @@ class StreamResetHandler { TimerManager* timer_manager, DataTracker* data_tracker, ReassemblyQueue* reassembly_queue, - RetransmissionQueue* retransmission_queue) + RetransmissionQueue* retransmission_queue, + const DcSctpSocketHandoverState* handover_state = nullptr) : log_prefix_(std::string(log_prefix) + "reset: "), ctx_(context), data_tracker_(data_tracker), @@ -80,9 +81,15 @@ class StreamResetHandler { "re-config", absl::bind_front(&StreamResetHandler::OnReconfigTimerExpiry, this), TimerOptions(DurationMs(0)))), - next_outgoing_req_seq_nbr_(ReconfigRequestSN(*ctx_->my_initial_tsn())), + next_outgoing_req_seq_nbr_( + handover_state + ? ReconfigRequestSN(handover_state->tx.next_reset_req_sn) + : ReconfigRequestSN(*ctx_->my_initial_tsn())), last_processed_req_seq_nbr_( - ReconfigRequestSN(*ctx_->peer_initial_tsn() - 1)) {} + handover_state ? ReconfigRequestSN( + handover_state->rx.last_completed_reset_req_sn) + : ReconfigRequestSN(*ctx_->peer_initial_tsn() - 1)) { + } // Initiates reset of the provided streams. While there can only be one // ongoing stream reset request at any time, this method can be called at any @@ -100,6 +107,10 @@ class StreamResetHandler { // Called when handling and incoming RE-CONFIG chunk. void HandleReConfig(ReConfigChunk chunk); + HandoverReadinessStatus GetHandoverReadiness() const; + + void AddHandoverState(DcSctpSocketHandoverState& state); + private: // Represents a stream request operation. There can only be one ongoing at // any time, and a sent request may either succeed, fail or result in the diff --git a/net/dcsctp/socket/stream_reset_handler_test.cc b/net/dcsctp/socket/stream_reset_handler_test.cc index 8955c839e8..6f6874f2a0 100644 --- a/net/dcsctp/socket/stream_reset_handler_test.cc +++ b/net/dcsctp/socket/stream_reset_handler_test.cc @@ -17,6 +17,8 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" +#include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/reconfig_chunk.h" #include "net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h" @@ -38,7 +40,6 @@ namespace dcsctp { namespace { -using ::testing::_; using ::testing::IsEmpty; using ::testing::NiceMock; using ::testing::Return; @@ -87,7 +88,9 @@ class StreamResetHandlerTest : public testing::Test { protected: StreamResetHandlerTest() : ctx_(&callbacks_), - timer_manager_([this]() { return callbacks_.CreateTimeout(); }), + timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return callbacks_.CreateTimeout(precision); + }), delayed_ack_timer_(timer_manager_.CreateTimer( "test/delayed_ack", []() { return absl::nullopt; }, @@ -96,9 +99,13 @@ class StreamResetHandlerTest : public testing::Test { "test/t3_rtx", []() { return absl::nullopt; }, TimerOptions(DurationMs(0)))), - buf_("log: ", delayed_ack_timer_.get(), kPeerInitialTsn), - reasm_("log: ", kPeerInitialTsn, kArwnd), - retransmission_queue_( + data_tracker_(std::make_unique("log: ", + delayed_ack_timer_.get(), + kPeerInitialTsn)), + reasm_(std::make_unique("log: ", + kPeerInitialTsn, + kArwnd)), + retransmission_queue_(std::make_unique( "", kMyInitialTsn, kArwnd, @@ -106,13 +113,14 @@ class StreamResetHandlerTest : public testing::Test { [](DurationMs rtt_ms) {}, []() {}, *t3_rtx_timer_, - /*options=*/{}), - handler_("log: ", - &ctx_, - &timer_manager_, - &buf_, - &reasm_, - &retransmission_queue_) { + DcSctpOptions())), + handler_( + std::make_unique("log: ", + &ctx_, + &timer_manager_, + data_tracker_.get(), + reasm_.get(), + retransmission_queue_.get())) { EXPECT_CALL(ctx_, current_rto).WillRepeatedly(Return(kRto)); } @@ -131,7 +139,7 @@ class StreamResetHandlerTest : public testing::Test { // that are sent in the response RE-CONFIG. std::vector HandleAndCatchResponse( ReConfigChunk chunk) { - handler_.HandleReConfig(std::move(chunk)); + handler_->HandleReConfig(std::move(chunk)); std::vector payload = callbacks_.ConsumeSentPacket(); if (payload.empty()) { @@ -169,6 +177,35 @@ class StreamResetHandlerTest : public testing::Test { return responses; } + void PerformHandover() { + EXPECT_TRUE(handler_->GetHandoverReadiness().IsReady()); + EXPECT_TRUE(data_tracker_->GetHandoverReadiness().IsReady()); + EXPECT_TRUE(reasm_->GetHandoverReadiness().IsReady()); + EXPECT_TRUE(retransmission_queue_->GetHandoverReadiness().IsReady()); + + DcSctpSocketHandoverState state; + handler_->AddHandoverState(state); + data_tracker_->AddHandoverState(state); + reasm_->AddHandoverState(state); + + retransmission_queue_->AddHandoverState(state); + + g_handover_state_transformer_for_test(&state); + + data_tracker_ = std::make_unique( + "log: ", delayed_ack_timer_.get(), kPeerInitialTsn, &state); + reasm_ = std::make_unique("log: ", kPeerInitialTsn, kArwnd, + &state); + retransmission_queue_ = std::make_unique( + "", kMyInitialTsn, kArwnd, producer_, [](DurationMs rtt_ms) {}, []() {}, + *t3_rtx_timer_, DcSctpOptions(), + /*supports_partial_reliability=*/true, + /*use_message_interleaving=*/false, &state); + handler_ = std::make_unique( + "log: ", &ctx_, &timer_manager_, data_tracker_.get(), reasm_.get(), + retransmission_queue_.get(), &state); + } + DataGenerator gen_; NiceMock callbacks_; NiceMock ctx_; @@ -176,16 +213,16 @@ class StreamResetHandlerTest : public testing::Test { TimerManager timer_manager_; std::unique_ptr delayed_ack_timer_; std::unique_ptr t3_rtx_timer_; - DataTracker buf_; - ReassemblyQueue reasm_; - RetransmissionQueue retransmission_queue_; - StreamResetHandler handler_; + std::unique_ptr data_tracker_; + std::unique_ptr reasm_; + std::unique_ptr retransmission_queue_; + std::unique_ptr handler_; }; TEST_F(StreamResetHandlerTest, ChunkWithNoParametersReturnsError) { EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); EXPECT_CALL(callbacks_, OnError).Times(1); - handler_.HandleReConfig(ReConfigChunk(Parameters())); + handler_->HandleReConfig(ReConfigChunk(Parameters())); } TEST_F(StreamResetHandlerTest, ChunkWithInvalidParametersReturnsError) { @@ -200,32 +237,32 @@ TEST_F(StreamResetHandlerTest, ChunkWithInvalidParametersReturnsError) { EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); EXPECT_CALL(callbacks_, OnError).Times(1); - handler_.HandleReConfig(ReConfigChunk(builder.Build())); + handler_->HandleReConfig(ReConfigChunk(builder.Build())); } TEST_F(StreamResetHandlerTest, FailToDeliverWithoutResettingStream) { - reasm_.Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); - reasm_.Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); - buf_.Observe(kPeerInitialTsn); - buf_.Observe(AddTo(kPeerInitialTsn, 1)); - EXPECT_THAT(reasm_.FlushMessages(), + data_tracker_->Observe(kPeerInitialTsn); + data_tracker_->Observe(AddTo(kPeerInitialTsn, 1)); + EXPECT_THAT(reasm_->FlushMessages(), UnorderedElementsAre( SctpMessageIs(StreamID(1), PPID(53), kShortPayload), SctpMessageIs(StreamID(1), PPID(53), kShortPayload))); gen_.ResetStream(); - reasm_.Add(AddTo(kPeerInitialTsn, 2), gen_.Ordered({1, 2, 3, 4}, "BE")); - EXPECT_THAT(reasm_.FlushMessages(), IsEmpty()); + reasm_->Add(AddTo(kPeerInitialTsn, 2), gen_.Ordered({1, 2, 3, 4}, "BE")); + EXPECT_THAT(reasm_->FlushMessages(), IsEmpty()); } TEST_F(StreamResetHandlerTest, ResetStreamsNotDeferred) { - reasm_.Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); - reasm_.Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); - buf_.Observe(kPeerInitialTsn); - buf_.Observe(AddTo(kPeerInitialTsn, 1)); - EXPECT_THAT(reasm_.FlushMessages(), + data_tracker_->Observe(kPeerInitialTsn); + data_tracker_->Observe(AddTo(kPeerInitialTsn, 1)); + EXPECT_THAT(reasm_->FlushMessages(), UnorderedElementsAre( SctpMessageIs(StreamID(1), PPID(53), kShortPayload), SctpMessageIs(StreamID(1), PPID(53), kShortPayload))); @@ -241,8 +278,8 @@ TEST_F(StreamResetHandlerTest, ResetStreamsNotDeferred) { EXPECT_EQ(responses[0].result(), ResponseResult::kSuccessPerformed); gen_.ResetStream(); - reasm_.Add(AddTo(kPeerInitialTsn, 2), gen_.Ordered({1, 2, 3, 4}, "BE")); - EXPECT_THAT(reasm_.FlushMessages(), + reasm_->Add(AddTo(kPeerInitialTsn, 2), gen_.Ordered({1, 2, 3, 4}, "BE")); + EXPECT_THAT(reasm_->FlushMessages(), UnorderedElementsAre( SctpMessageIs(StreamID(1), PPID(53), kShortPayload))); } @@ -250,14 +287,15 @@ TEST_F(StreamResetHandlerTest, ResetStreamsNotDeferred) { TEST_F(StreamResetHandlerTest, ResetStreamsDeferred) { DataGeneratorOptions opts; opts.message_id = MID(0); - reasm_.Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE", opts)); opts.message_id = MID(1); - reasm_.Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->Add(AddTo(kPeerInitialTsn, 1), + gen_.Ordered({1, 2, 3, 4}, "BE", opts)); - buf_.Observe(kPeerInitialTsn); - buf_.Observe(AddTo(kPeerInitialTsn, 1)); - EXPECT_THAT(reasm_.FlushMessages(), + data_tracker_->Observe(kPeerInitialTsn); + data_tracker_->Observe(AddTo(kPeerInitialTsn, 1)); + EXPECT_THAT(reasm_->FlushMessages(), UnorderedElementsAre( SctpMessageIs(StreamID(1), PPID(53), kShortPayload), SctpMessageIs(StreamID(1), PPID(53), kShortPayload))); @@ -274,26 +312,30 @@ TEST_F(StreamResetHandlerTest, ResetStreamsDeferred) { opts.message_id = MID(1); opts.ppid = PPID(5); - reasm_.Add(AddTo(kPeerInitialTsn, 5), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); - reasm_.MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); + reasm_->Add(AddTo(kPeerInitialTsn, 5), + gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); opts.message_id = MID(0); opts.ppid = PPID(4); - reasm_.Add(AddTo(kPeerInitialTsn, 4), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); - reasm_.MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); + reasm_->Add(AddTo(kPeerInitialTsn, 4), + gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); opts.message_id = MID(3); opts.ppid = PPID(3); - reasm_.Add(AddTo(kPeerInitialTsn, 3), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); - reasm_.MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); + reasm_->Add(AddTo(kPeerInitialTsn, 3), + gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 1)); opts.message_id = MID(2); opts.ppid = PPID(2); - reasm_.Add(AddTo(kPeerInitialTsn, 2), gen_.Ordered({1, 2, 3, 4}, "BE", opts)); - reasm_.MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 5)); + reasm_->Add(AddTo(kPeerInitialTsn, 2), + gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->MaybeResetStreamsDeferred(AddTo(kPeerInitialTsn, 5)); EXPECT_THAT( - reasm_.FlushMessages(), + reasm_->FlushMessages(), UnorderedElementsAre(SctpMessageIs(StreamID(1), PPID(2), kShortPayload), SctpMessageIs(StreamID(1), PPID(3), kShortPayload), SctpMessageIs(StreamID(1), PPID(4), kShortPayload), @@ -302,10 +344,10 @@ TEST_F(StreamResetHandlerTest, ResetStreamsDeferred) { TEST_F(StreamResetHandlerTest, SendOutgoingRequestDirectly) { EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams(std::vector({StreamID(42)})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig = handler_.MakeStreamResetRequest(); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req, @@ -313,19 +355,19 @@ TEST_F(StreamResetHandlerTest, SendOutgoingRequestDirectly) { EXPECT_EQ(req.request_sequence_number(), kMyInitialReqSn); EXPECT_EQ(req.sender_last_assigned_tsn(), - TSN(*retransmission_queue_.next_tsn() - 1)); + TSN(*retransmission_queue_->next_tsn() - 1)); EXPECT_THAT(req.stream_ids(), UnorderedElementsAre(StreamID(42))); } TEST_F(StreamResetHandlerTest, ResetMultipleStreamsInOneRequest) { EXPECT_CALL(producer_, PrepareResetStreams).Times(3); - handler_.ResetStreams(std::vector({StreamID(42)})); - handler_.ResetStreams( + handler_->ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams( std::vector({StreamID(43), StreamID(44), StreamID(41)})); - handler_.ResetStreams(std::vector({StreamID(42), StreamID(40)})); + handler_->ResetStreams(std::vector({StreamID(42), StreamID(40)})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig = handler_.MakeStreamResetRequest(); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req, @@ -333,7 +375,7 @@ TEST_F(StreamResetHandlerTest, ResetMultipleStreamsInOneRequest) { EXPECT_EQ(req.request_sequence_number(), kMyInitialReqSn); EXPECT_EQ(req.sender_last_assigned_tsn(), - TSN(*retransmission_queue_.next_tsn() - 1)); + TSN(*retransmission_queue_->next_tsn() - 1)); EXPECT_THAT(req.stream_ids(), UnorderedElementsAre(StreamID(40), StreamID(41), StreamID(42), StreamID(43), StreamID(44))); @@ -341,25 +383,25 @@ TEST_F(StreamResetHandlerTest, ResetMultipleStreamsInOneRequest) { TEST_F(StreamResetHandlerTest, SendOutgoingRequestDeferred) { EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams(std::vector({StreamID(42)})); EXPECT_CALL(producer_, CanResetStreams()) .WillOnce(Return(false)) .WillOnce(Return(false)) .WillOnce(Return(true)); - EXPECT_FALSE(handler_.MakeStreamResetRequest().has_value()); - EXPECT_FALSE(handler_.MakeStreamResetRequest().has_value()); - EXPECT_TRUE(handler_.MakeStreamResetRequest().has_value()); + EXPECT_FALSE(handler_->MakeStreamResetRequest().has_value()); + EXPECT_FALSE(handler_->MakeStreamResetRequest().has_value()); + EXPECT_TRUE(handler_->MakeStreamResetRequest().has_value()); } TEST_F(StreamResetHandlerTest, SendOutgoingResettingOnPositiveResponse) { EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams(std::vector({StreamID(42)})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig = handler_.MakeStreamResetRequest(); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req, @@ -376,16 +418,16 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResettingOnPositiveResponse) { // Processing a response shouldn't result in sending anything. EXPECT_CALL(callbacks_, OnError).Times(0); EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); - handler_.HandleReConfig(std::move(response_reconfig)); + handler_->HandleReConfig(std::move(response_reconfig)); } TEST_F(StreamResetHandlerTest, SendOutgoingResetRollbackOnError) { EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams(std::vector({StreamID(42)})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig = handler_.MakeStreamResetRequest(); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req, @@ -402,18 +444,18 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRollbackOnError) { // Only requests should result in sending responses. EXPECT_CALL(callbacks_, OnError).Times(0); EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); - handler_.HandleReConfig(std::move(response_reconfig)); + handler_->HandleReConfig(std::move(response_reconfig)); } TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { static constexpr StreamID kStreamToReset = StreamID(42); EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({kStreamToReset})); + handler_->ResetStreams(std::vector({kStreamToReset})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig1 = handler_.MakeStreamResetRequest(); + absl::optional reconfig1 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig1.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req1, @@ -431,7 +473,7 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { // Processing a response shouldn't result in sending anything. EXPECT_CALL(callbacks_, OnError).Times(0); EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); - handler_.HandleReConfig(std::move(response_reconfig)); + handler_->HandleReConfig(std::move(response_reconfig)); // Let some time pass, so that the reconfig timer expires, and retries the // same request. @@ -458,23 +500,23 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { EXPECT_CALL(producer_, PrepareResetStreams).Times(1); - handler_.ResetStreams(std::vector({StreamID(42)})); + handler_->ResetStreams(std::vector({StreamID(42)})); EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig1 = handler_.MakeStreamResetRequest(); + absl::optional reconfig1 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig1.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req1, reconfig1->parameters().get()); EXPECT_EQ(req1.request_sequence_number(), kMyInitialReqSn); EXPECT_EQ(req1.sender_last_assigned_tsn(), - AddTo(retransmission_queue_.next_tsn(), -1)); + AddTo(retransmission_queue_->next_tsn(), -1)); EXPECT_THAT(req1.stream_ids(), UnorderedElementsAre(StreamID(42))); // Streams reset while the request is in-flight will be queued. StreamID stream_ids[] = {StreamID(41), StreamID(43)}; - handler_.ResetStreams(stream_ids); - EXPECT_EQ(handler_.MakeStreamResetRequest(), absl::nullopt); + handler_->ResetStreams(stream_ids); + EXPECT_EQ(handler_->MakeStreamResetRequest(), absl::nullopt); Parameters::Builder builder; builder.Add(ReconfigurationResponseParameter( @@ -487,18 +529,18 @@ TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { // Processing a response shouldn't result in sending anything. EXPECT_CALL(callbacks_, OnError).Times(0); EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(0); - handler_.HandleReConfig(std::move(response_reconfig)); + handler_->HandleReConfig(std::move(response_reconfig)); // Response has been processed. A new request can be sent. EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); - absl::optional reconfig2 = handler_.MakeStreamResetRequest(); + absl::optional reconfig2 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig2.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( OutgoingSSNResetRequestParameter req2, reconfig2->parameters().get()); EXPECT_EQ(req2.request_sequence_number(), AddTo(kMyInitialReqSn, 1)); EXPECT_EQ(req2.sender_last_assigned_tsn(), - TSN(*retransmission_queue_.next_tsn() - 1)); + TSN(*retransmission_queue_->next_tsn() - 1)); EXPECT_THAT(req2.stream_ids(), UnorderedElementsAre(StreamID(41), StreamID(43))); } @@ -516,12 +558,12 @@ TEST_F(StreamResetHandlerTest, SendIncomingResetJustReturnsNothingPerformed) { } TEST_F(StreamResetHandlerTest, SendSameRequestTwiceReturnsNothingToDo) { - reasm_.Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); - reasm_.Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE")); + reasm_->Add(AddTo(kPeerInitialTsn, 1), gen_.Ordered({1, 2, 3, 4}, "BE")); - buf_.Observe(kPeerInitialTsn); - buf_.Observe(AddTo(kPeerInitialTsn, 1)); - EXPECT_THAT(reasm_.FlushMessages(), + data_tracker_->Observe(kPeerInitialTsn); + data_tracker_->Observe(AddTo(kPeerInitialTsn, 1)); + EXPECT_THAT(reasm_->FlushMessages(), UnorderedElementsAre( SctpMessageIs(StreamID(1), PPID(53), kShortPayload), SctpMessageIs(StreamID(1), PPID(53), kShortPayload))); @@ -546,5 +588,125 @@ TEST_F(StreamResetHandlerTest, SendSameRequestTwiceReturnsNothingToDo) { EXPECT_THAT(responses2, SizeIs(1)); EXPECT_EQ(responses2[0].result(), ResponseResult::kSuccessNothingToDo); } + +TEST_F(StreamResetHandlerTest, + HandoverIsAllowedOnlyWhenNoStreamIsBeingOrWillBeReset) { + EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + handler_->ResetStreams(std::vector({StreamID(42)})); + EXPECT_EQ( + handler_->GetHandoverReadiness(), + HandoverReadinessStatus(HandoverUnreadinessReason::kPendingStreamReset)); + + EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + ASSERT_TRUE(handler_->MakeStreamResetRequest().has_value()); + EXPECT_EQ(handler_->GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kPendingStreamResetRequest)); + + // Reset more streams while the request is in-flight. + StreamID stream_ids[] = {StreamID(41), StreamID(43)}; + handler_->ResetStreams(stream_ids); + EXPECT_EQ(handler_->GetHandoverReadiness(), + HandoverReadinessStatus() + .Add(HandoverUnreadinessReason::kPendingStreamResetRequest) + .Add(HandoverUnreadinessReason::kPendingStreamReset)); + + // Processing a response to first request. + EXPECT_CALL(producer_, CommitResetStreams()).Times(1); + handler_->HandleReConfig( + ReConfigChunk(Parameters::Builder() + .Add(ReconfigurationResponseParameter( + kMyInitialReqSn, ResponseResult::kSuccessPerformed)) + .Build())); + EXPECT_EQ( + handler_->GetHandoverReadiness(), + HandoverReadinessStatus(HandoverUnreadinessReason::kPendingStreamReset)); + + // Second request can be sent. + EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + ASSERT_TRUE(handler_->MakeStreamResetRequest().has_value()); + EXPECT_EQ(handler_->GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kPendingStreamResetRequest)); + + // Processing a response to second request. + EXPECT_CALL(producer_, CommitResetStreams()).Times(1); + handler_->HandleReConfig(ReConfigChunk( + Parameters::Builder() + .Add(ReconfigurationResponseParameter( + AddTo(kMyInitialReqSn, 1), ResponseResult::kSuccessPerformed)) + .Build())); + + // Seconds response has been processed. No pending resets. + EXPECT_TRUE(handler_->GetHandoverReadiness().IsReady()); +} + +TEST_F(StreamResetHandlerTest, HandoverInInitialState) { + PerformHandover(); + + EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + handler_->ResetStreams(std::vector({StreamID(42)})); + + EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + absl::optional reconfig = handler_->MakeStreamResetRequest(); + ASSERT_TRUE(reconfig.has_value()); + ASSERT_HAS_VALUE_AND_ASSIGN( + OutgoingSSNResetRequestParameter req, + reconfig->parameters().get()); + + EXPECT_EQ(req.request_sequence_number(), kMyInitialReqSn); + EXPECT_EQ(req.sender_last_assigned_tsn(), + TSN(*retransmission_queue_->next_tsn() - 1)); + EXPECT_THAT(req.stream_ids(), UnorderedElementsAre(StreamID(42))); +} + +TEST_F(StreamResetHandlerTest, HandoverAfterHavingResetOneStream) { + // Reset one stream + { + EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + handler_->ResetStreams(std::vector({StreamID(42)})); + + EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + ASSERT_HAS_VALUE_AND_ASSIGN(ReConfigChunk reconfig, + handler_->MakeStreamResetRequest()); + ASSERT_HAS_VALUE_AND_ASSIGN( + OutgoingSSNResetRequestParameter req, + reconfig.parameters().get()); + EXPECT_EQ(req.request_sequence_number(), kMyInitialReqSn); + EXPECT_EQ(req.sender_last_assigned_tsn(), + TSN(*retransmission_queue_->next_tsn() - 1)); + EXPECT_THAT(req.stream_ids(), UnorderedElementsAre(StreamID(42))); + + EXPECT_CALL(producer_, CommitResetStreams()).Times(1); + handler_->HandleReConfig( + ReConfigChunk(Parameters::Builder() + .Add(ReconfigurationResponseParameter( + req.request_sequence_number(), + ResponseResult::kSuccessPerformed)) + .Build())); + } + + PerformHandover(); + + // Reset another stream after handover + { + EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + handler_->ResetStreams(std::vector({StreamID(43)})); + + EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + ASSERT_HAS_VALUE_AND_ASSIGN(ReConfigChunk reconfig, + handler_->MakeStreamResetRequest()); + ASSERT_HAS_VALUE_AND_ASSIGN( + OutgoingSSNResetRequestParameter req, + reconfig.parameters().get()); + + EXPECT_EQ(req.request_sequence_number(), + ReconfigRequestSN(kMyInitialReqSn.value() + 1)); + EXPECT_EQ(req.sender_last_assigned_tsn(), + TSN(*retransmission_queue_->next_tsn() - 1)); + EXPECT_THAT(req.stream_ids(), UnorderedElementsAre(StreamID(43))); + } +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/transmission_control_block.cc b/net/dcsctp/socket/transmission_control_block.cc index f0f1ab9782..0fc0d4c029 100644 --- a/net/dcsctp/socket/transmission_control_block.cc +++ b/net/dcsctp/socket/transmission_control_block.cc @@ -140,9 +140,7 @@ void TransmissionControlBlock::SendBufferedPackets(SctpPacket::Builder& builder, auto chunks = retransmission_queue_.GetChunksToSend(now, builder.bytes_remaining()); - for (auto& elem : chunks) { - TSN tsn = elem.first; - Data data = std::move(elem.second); + for (auto& [tsn, data] : chunks) { if (capabilities_.message_interleaving) { builder.Add(IDataChunk(tsn, std::move(data), false)); } else { @@ -183,4 +181,30 @@ std::string TransmissionControlBlock::ToString() const { return sb.Release(); } +HandoverReadinessStatus TransmissionControlBlock::GetHandoverReadiness() const { + HandoverReadinessStatus status; + status.Add(data_tracker_.GetHandoverReadiness()); + status.Add(stream_reset_handler_.GetHandoverReadiness()); + status.Add(reassembly_queue_.GetHandoverReadiness()); + status.Add(retransmission_queue_.GetHandoverReadiness()); + return status; +} + +void TransmissionControlBlock::AddHandoverState( + DcSctpSocketHandoverState& state) { + state.capabilities.partial_reliability = capabilities_.partial_reliability; + state.capabilities.message_interleaving = capabilities_.message_interleaving; + state.capabilities.reconfig = capabilities_.reconfig; + + state.my_verification_tag = my_verification_tag().value(); + state.peer_verification_tag = peer_verification_tag().value(); + state.my_initial_tsn = my_initial_tsn().value(); + state.peer_initial_tsn = peer_initial_tsn().value(); + state.tie_tag = tie_tag().value(); + + data_tracker_.AddHandoverState(state); + stream_reset_handler_.AddHandoverState(state); + reassembly_queue_.AddHandoverState(state); + retransmission_queue_.AddHandoverState(state); +} } // namespace dcsctp diff --git a/net/dcsctp/socket/transmission_control_block.h b/net/dcsctp/socket/transmission_control_block.h index c3766d1546..8cefbc65f4 100644 --- a/net/dcsctp/socket/transmission_control_block.h +++ b/net/dcsctp/socket/transmission_control_block.h @@ -19,6 +19,7 @@ #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/cookie_echo_chunk.h" #include "net/dcsctp/packet/sctp_packet.h" @@ -44,20 +45,22 @@ namespace dcsctp { // closed or restarted, this object will be deleted and/or replaced. class TransmissionControlBlock : public Context { public: - TransmissionControlBlock(TimerManager& timer_manager, - absl::string_view log_prefix, - const DcSctpOptions& options, - const Capabilities& capabilities, - DcSctpSocketCallbacks& callbacks, - SendQueue& send_queue, - VerificationTag my_verification_tag, - TSN my_initial_tsn, - VerificationTag peer_verification_tag, - TSN peer_initial_tsn, - size_t a_rwnd, - TieTag tie_tag, - PacketSender& packet_sender, - std::function is_connection_established) + TransmissionControlBlock( + TimerManager& timer_manager, + absl::string_view log_prefix, + const DcSctpOptions& options, + const Capabilities& capabilities, + DcSctpSocketCallbacks& callbacks, + SendQueue& send_queue, + VerificationTag my_verification_tag, + TSN my_initial_tsn, + VerificationTag peer_verification_tag, + TSN peer_initial_tsn, + size_t a_rwnd, + TieTag tie_tag, + PacketSender& packet_sender, + std::function is_connection_established, + const DcSctpSocketHandoverState* handover_state = nullptr) : log_prefix_(log_prefix), options_(options), timer_manager_(timer_manager), @@ -76,7 +79,9 @@ class TransmissionControlBlock : public Context { this), TimerOptions(options.delayed_ack_max_timeout, TimerBackoffAlgorithm::kExponential, - /*max_restarts=*/0))), + /*max_restarts=*/0, + /*max_backoff_duration=*/absl::nullopt, + webrtc::TaskQueueBase::DelayPrecision::kHigh))), my_verification_tag_(my_verification_tag), my_initial_tsn_(my_initial_tsn), peer_verification_tag_(peer_verification_tag), @@ -86,10 +91,14 @@ class TransmissionControlBlock : public Context { packet_sender_(packet_sender), rto_(options), tx_error_counter_(log_prefix, options), - data_tracker_(log_prefix, delayed_ack_timer_.get(), peer_initial_tsn), + data_tracker_(log_prefix, + delayed_ack_timer_.get(), + peer_initial_tsn, + handover_state), reassembly_queue_(log_prefix, peer_initial_tsn, - options.max_receiver_window_buffer_size), + options.max_receiver_window_buffer_size, + handover_state), retransmission_queue_( log_prefix, my_initial_tsn, @@ -100,13 +109,15 @@ class TransmissionControlBlock : public Context { *t3_rtx_, options, capabilities.partial_reliability, - capabilities.message_interleaving), + capabilities.message_interleaving, + handover_state), stream_reset_handler_(log_prefix, this, &timer_manager, &data_tracker_, &reassembly_queue_, - &retransmission_queue_), + &retransmission_queue_, + handover_state), heartbeat_handler_(log_prefix, options, this, &timer_manager_) {} // Implementation of `Context`. @@ -188,6 +199,10 @@ class TransmissionControlBlock : public Context { // Returns a textual representation of this object, for logging. std::string ToString() const; + HandoverReadinessStatus GetHandoverReadiness() const; + + void AddHandoverState(DcSctpSocketHandoverState& state); + private: // Will be called when the retransmission timer (t3-rtx) expires. absl::optional OnRtxTimerExpiry(); diff --git a/net/dcsctp/testing/data_generator.h b/net/dcsctp/testing/data_generator.h index 859450b1c3..f917c740a7 100644 --- a/net/dcsctp/testing/data_generator.h +++ b/net/dcsctp/testing/data_generator.h @@ -38,14 +38,14 @@ class DataGenerator { // "is_end" flag. Data Ordered(std::vector payload, absl::string_view flags = "", - const DataGeneratorOptions opts = {}); + DataGeneratorOptions opts = {}); // Generates unordered "data" with the provided `payload` and flags, which can // contain "B" for setting the "is_beginning" flag, and/or "E" for setting the // "is_end" flag. Data Unordered(std::vector payload, absl::string_view flags = "", - const DataGeneratorOptions opts = {}); + DataGeneratorOptions opts = {}); // Resets the Message ID identifier - simulating a "stream reset". void ResetStream() { message_id_ = MID(0); } diff --git a/net/dcsctp/timer/BUILD.gn b/net/dcsctp/timer/BUILD.gn index 286bbe9cf5..4470903ffc 100644 --- a/net/dcsctp/timer/BUILD.gn +++ b/net/dcsctp/timer/BUILD.gn @@ -11,13 +11,13 @@ import("../../../webrtc.gni") rtc_library("timer") { deps = [ "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", "../../../rtc_base/containers:flat_map", "../../../rtc_base/containers:flat_set", "../public:socket", - "../public:strong_alias", "../public:types", ] sources = [ @@ -41,7 +41,6 @@ rtc_library("task_queue_timeout") { "../../../rtc_base/task_utils:pending_task_safety_flag", "../../../rtc_base/task_utils:to_queued_task", "../public:socket", - "../public:strong_alias", "../public:types", ] sources = [ @@ -59,6 +58,8 @@ if (rtc_include_tests) { ":task_queue_timeout", ":timer", "../../../api:array_view", + "../../../api/task_queue:task_queue", + "../../../api/task_queue/test:mock_task_queue_base", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", "../../../rtc_base:rtc_base_approved", diff --git a/net/dcsctp/timer/fake_timeout.h b/net/dcsctp/timer/fake_timeout.h index f2bf10325e..74ffe5af29 100644 --- a/net/dcsctp/timer/fake_timeout.h +++ b/net/dcsctp/timer/fake_timeout.h @@ -18,6 +18,7 @@ #include #include "absl/types/optional.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/timeout.h" #include "rtc_base/checks.h" #include "rtc_base/containers/flat_set.h" @@ -27,8 +28,8 @@ namespace dcsctp { // A timeout used in tests. class FakeTimeout : public Timeout { public: - explicit FakeTimeout(std::function get_time, - std::function on_delete) + FakeTimeout(std::function get_time, + std::function on_delete) : get_time_(std::move(get_time)), on_delete_(std::move(on_delete)) {} ~FakeTimeout() override { on_delete_(this); } @@ -68,12 +69,17 @@ class FakeTimeoutManager { explicit FakeTimeoutManager(std::function get_time) : get_time_(std::move(get_time)) {} - std::unique_ptr CreateTimeout() { + std::unique_ptr CreateTimeout() { auto timer = std::make_unique( get_time_, [this](FakeTimeout* timer) { timers_.erase(timer); }); timers_.insert(timer.get()); return timer; } + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision) { + // FakeTimeout does not support implement |precision|. + return CreateTimeout(); + } // NOTE: This can't return a vector, as calling EvaluateHasExpired requires // calling socket->HandleTimeout directly afterwards, as the owning Timer diff --git a/net/dcsctp/timer/task_queue_timeout.cc b/net/dcsctp/timer/task_queue_timeout.cc index 6d3054eeb8..4d1dc1907b 100644 --- a/net/dcsctp/timer/task_queue_timeout.cc +++ b/net/dcsctp/timer/task_queue_timeout.cc @@ -16,8 +16,10 @@ namespace dcsctp { TaskQueueTimeoutFactory::TaskQueueTimeout::TaskQueueTimeout( - TaskQueueTimeoutFactory& parent) + TaskQueueTimeoutFactory& parent, + webrtc::TaskQueueBase::DelayPrecision precision) : parent_(parent), + precision_(precision), pending_task_safety_flag_(webrtc::PendingTaskSafetyFlag::Create()) {} TaskQueueTimeoutFactory::TaskQueueTimeout::~TaskQueueTimeout() { @@ -54,7 +56,8 @@ void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms, } posted_task_expiration_ = timeout_expiration_; - parent_.task_queue_.PostDelayedTask( + parent_.task_queue_.PostDelayedTaskWithPrecision( + precision_, webrtc::ToQueuedTask( pending_task_safety_flag_, [timeout_id, this]() { diff --git a/net/dcsctp/timer/task_queue_timeout.h b/net/dcsctp/timer/task_queue_timeout.h index e8d12df592..600b292443 100644 --- a/net/dcsctp/timer/task_queue_timeout.h +++ b/net/dcsctp/timer/task_queue_timeout.h @@ -45,14 +45,17 @@ class TaskQueueTimeoutFactory { on_expired_(std::move(on_expired)) {} // Creates an implementation of `Timeout`. - std::unique_ptr CreateTimeout() { - return std::make_unique(*this); + std::unique_ptr CreateTimeout( + webrtc::TaskQueueBase::DelayPrecision precision = + webrtc::TaskQueueBase::DelayPrecision::kLow) { + return std::make_unique(*this, precision); } private: class TaskQueueTimeout : public Timeout { public: - explicit TaskQueueTimeout(TaskQueueTimeoutFactory& parent); + TaskQueueTimeout(TaskQueueTimeoutFactory& parent, + webrtc::TaskQueueBase::DelayPrecision precision); ~TaskQueueTimeout(); void Start(DurationMs duration_ms, TimeoutID timeout_id) override; @@ -60,6 +63,7 @@ class TaskQueueTimeoutFactory { private: TaskQueueTimeoutFactory& parent_; + const webrtc::TaskQueueBase::DelayPrecision precision_; // A safety flag to ensure that posted tasks to the task queue don't // reference these object when they go out of scope. Note that this safety // flag will be re-created if the scheduled-but-not-yet-expired task is not diff --git a/net/dcsctp/timer/task_queue_timeout_test.cc b/net/dcsctp/timer/task_queue_timeout_test.cc index 9d3846953b..da01ecf991 100644 --- a/net/dcsctp/timer/task_queue_timeout_test.cc +++ b/net/dcsctp/timer/task_queue_timeout_test.cc @@ -11,13 +11,18 @@ #include +#include "api/task_queue/queued_task.h" +#include "api/task_queue/task_queue_base.h" +#include "api/task_queue/test/mock_task_queue_base.h" #include "rtc_base/gunit.h" #include "test/gmock.h" #include "test/time_controller/simulated_time_controller.h" namespace dcsctp { namespace { +using ::testing::_; using ::testing::MockFunction; +using ::testing::NiceMock; class TaskQueueTimeoutTest : public testing::Test { protected: @@ -111,5 +116,38 @@ TEST_F(TaskQueueTimeoutTest, KilledBeforeExpired) { EXPECT_CALL(on_expired_, Call).Times(0); AdvanceTime(DurationMs(1000)); } + +TEST(TaskQueueTimeoutWithMockTaskQueueTest, CanSetTimeoutPrecisionToLow) { + NiceMock mock_task_queue; + EXPECT_CALL(mock_task_queue, PostDelayedTask(_, _)); + TaskQueueTimeoutFactory factory( + mock_task_queue, []() { return TimeMs(1337); }, + [](TimeoutID timeout_id) {}); + std::unique_ptr timeout = + factory.CreateTimeout(webrtc::TaskQueueBase::DelayPrecision::kLow); + timeout->Start(DurationMs(1), TimeoutID(1)); +} + +TEST(TaskQueueTimeoutWithMockTaskQueueTest, CanSetTimeoutPrecisionToHigh) { + NiceMock mock_task_queue; + EXPECT_CALL(mock_task_queue, PostDelayedHighPrecisionTask(_, _)); + TaskQueueTimeoutFactory factory( + mock_task_queue, []() { return TimeMs(1337); }, + [](TimeoutID timeout_id) {}); + std::unique_ptr timeout = + factory.CreateTimeout(webrtc::TaskQueueBase::DelayPrecision::kHigh); + timeout->Start(DurationMs(1), TimeoutID(1)); +} + +TEST(TaskQueueTimeoutWithMockTaskQueueTest, TimeoutPrecisionIsLowByDefault) { + NiceMock mock_task_queue; + EXPECT_CALL(mock_task_queue, PostDelayedTask(_, _)); + TaskQueueTimeoutFactory factory( + mock_task_queue, []() { return TimeMs(1337); }, + [](TimeoutID timeout_id) {}); + std::unique_ptr timeout = factory.CreateTimeout(); + timeout->Start(DurationMs(1), TimeoutID(1)); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/timer/timer.cc b/net/dcsctp/timer/timer.cc index b3168e3b11..bde07638a5 100644 --- a/net/dcsctp/timer/timer.cc +++ b/net/dcsctp/timer/timer.cc @@ -144,9 +144,11 @@ std::unique_ptr TimerManager::CreateTimer(absl::string_view name, // after 800 million reconnections on a single socket. Ensure this will never // happen. RTC_CHECK_NE(*id, std::numeric_limits::max()); + std::unique_ptr timeout = create_timeout_(options.precision); + RTC_CHECK(timeout != nullptr); auto timer = absl::WrapUnique(new Timer( id, name, std::move(on_expired), [this, id]() { timers_.erase(id); }, - create_timeout_(), options)); + std::move(timeout), options)); timers_[id] = timer.get(); return timer; } diff --git a/net/dcsctp/timer/timer.h b/net/dcsctp/timer/timer.h index 1d846b67c6..31b496dc81 100644 --- a/net/dcsctp/timer/timer.h +++ b/net/dcsctp/timer/timer.h @@ -21,13 +21,14 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "net/dcsctp/public/strong_alias.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/timeout.h" +#include "rtc_base/strong_alias.h" namespace dcsctp { -using TimerID = StrongAlias; -using TimerGeneration = StrongAlias; +using TimerID = webrtc::StrongAlias; +using TimerGeneration = webrtc::StrongAlias; enum class TimerBackoffAlgorithm { // The base duration will be used for any restart. @@ -52,10 +53,21 @@ struct TimerOptions { TimerBackoffAlgorithm backoff_algorithm, absl::optional max_restarts, absl::optional max_backoff_duration) + : TimerOptions(duration, + backoff_algorithm, + max_restarts, + max_backoff_duration, + webrtc::TaskQueueBase::DelayPrecision::kLow) {} + TimerOptions(DurationMs duration, + TimerBackoffAlgorithm backoff_algorithm, + absl::optional max_restarts, + absl::optional max_backoff_duration, + webrtc::TaskQueueBase::DelayPrecision precision) : duration(duration), backoff_algorithm(backoff_algorithm), max_restarts(max_restarts), - max_backoff_duration(max_backoff_duration) {} + max_backoff_duration(max_backoff_duration), + precision(precision) {} // The initial timer duration. Can be overridden with `set_duration`. const DurationMs duration; @@ -67,6 +79,8 @@ struct TimerOptions { const absl::optional max_restarts; // The maximum timeout value for exponential backoff. const absl::optional max_backoff_duration; + // The precision of the webrtc::TaskQueueBase used for scheduling. + const webrtc::TaskQueueBase::DelayPrecision precision; }; // A high-level timer (in contrast to the low-level `Timeout` class). @@ -172,7 +186,8 @@ class Timer { class TimerManager { public: explicit TimerManager( - std::function()> create_timeout) + std::function( + webrtc::TaskQueueBase::DelayPrecision)> create_timeout) : create_timeout_(std::move(create_timeout)) {} // Creates a timer with name `name` that will expire (when started) after @@ -185,7 +200,9 @@ class TimerManager { void HandleTimeout(TimeoutID timeout_id); private: - const std::function()> create_timeout_; + const std::function( + webrtc::TaskQueueBase::DelayPrecision)> + create_timeout_; std::map timers_; TimerID next_id_ = TimerID(0); }; diff --git a/net/dcsctp/timer/timer_test.cc b/net/dcsctp/timer/timer_test.cc index 5550f7fabf..4aebe65b48 100644 --- a/net/dcsctp/timer/timer_test.cc +++ b/net/dcsctp/timer/timer_test.cc @@ -12,6 +12,7 @@ #include #include "absl/types/optional.h" +#include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/timeout.h" #include "net/dcsctp/timer/fake_timeout.h" #include "rtc_base/gunit.h" @@ -25,7 +26,9 @@ class TimerTest : public testing::Test { protected: TimerTest() : timeout_manager_([this]() { return now_; }), - manager_([this]() { return timeout_manager_.CreateTimeout(); }) { + manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return timeout_manager_.CreateTimeout(precision); + }) { ON_CALL(on_expired_, Call).WillByDefault(Return(absl::nullopt)); } @@ -423,5 +426,34 @@ TEST_F(TimerTest, DurationStaysWithinMaxTimerBackOffDuration) { AdvanceTimeAndRunTimers(DurationMs(1)); } +TEST(TimerManagerTest, TimerManagerPassesPrecisionToCreateTimeoutMethod) { + FakeTimeoutManager timeout_manager([&]() { return TimeMs(0); }); + absl::optional create_timer_precison; + TimerManager manager([&](webrtc::TaskQueueBase::DelayPrecision precision) { + create_timer_precison = precision; + return timeout_manager.CreateTimeout(precision); + }); + // Default TimerOptions. + manager.CreateTimer( + "test_timer", []() { return absl::optional(); }, + TimerOptions(DurationMs(123))); + EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow); + // High precision TimerOptions. + manager.CreateTimer( + "test_timer", []() { return absl::optional(); }, + TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential, + absl::nullopt, absl::nullopt, + webrtc::TaskQueueBase::DelayPrecision::kHigh)); + EXPECT_EQ(create_timer_precison, + webrtc::TaskQueueBase::DelayPrecision::kHigh); + // Low precision TimerOptions. + manager.CreateTimer( + "test_timer", []() { return absl::optional(); }, + TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential, + absl::nullopt, absl::nullopt, + webrtc::TaskQueueBase::DelayPrecision::kLow)); + EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/tx/BUILD.gn b/net/dcsctp/tx/BUILD.gn index 50e424cc25..44dbd0b8f8 100644 --- a/net/dcsctp/tx/BUILD.gn +++ b/net/dcsctp/tx/BUILD.gn @@ -67,7 +67,7 @@ rtc_library("retransmission_timeout") { ] } -rtc_library("retransmission_queue") { +rtc_library("outstanding_data") { deps = [ ":retransmission_timeout", ":send_queue", @@ -79,6 +79,35 @@ rtc_library("retransmission_queue") { "../common:str_join", "../packet:chunk", "../packet:data", + "../public:socket", + "../public:types", + "../timer", + ] + sources = [ + "outstanding_data.cc", + "outstanding_data.h", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("retransmission_queue") { + deps = [ + ":outstanding_data", + ":retransmission_timeout", + ":send_queue", + "../../../api:array_view", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../common:math", + "../common:sequence_numbers", + "../common:str_join", + "../packet:chunk", + "../packet:data", + "../public:socket", "../public:types", "../timer", ] @@ -110,16 +139,21 @@ if (rtc_include_tests) { deps = [ ":mock_send_queue", + ":outstanding_data", ":retransmission_error_counter", ":retransmission_queue", ":retransmission_timeout", ":rr_send_queue", ":send_queue", "../../../api:array_view", + "../../../api/task_queue:task_queue", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", "../../../rtc_base:rtc_base_approved", "../../../test:test_support", + "../common:handover_testing", + "../common:math", + "../common:sequence_numbers", "../packet:chunk", "../packet:data", "../public:socket", @@ -130,6 +164,7 @@ if (rtc_include_tests) { ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] sources = [ + "outstanding_data_test.cc", "retransmission_error_counter_test.cc", "retransmission_queue_test.cc", "retransmission_timeout_test.cc", diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc new file mode 100644 index 0000000000..05277d0584 --- /dev/null +++ b/net/dcsctp/tx/outstanding_data.cc @@ -0,0 +1,479 @@ +/* + * 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 "net/dcsctp/tx/outstanding_data.h" + +#include +#include +#include +#include + +#include "net/dcsctp/common/math.h" +#include "net/dcsctp/common/sequence_numbers.h" +#include "net/dcsctp/public/types.h" +#include "rtc_base/logging.h" + +namespace dcsctp { + +// The number of times a packet must be NACKed before it's retransmitted. +// See https://tools.ietf.org/html/rfc4960#section-7.2.4 +constexpr uint8_t kNumberOfNacksForRetransmission = 3; + +// Returns how large a chunk will be, serialized, carrying the data +size_t OutstandingData::GetSerializedChunkSize(const Data& data) const { + return RoundUpTo4(data_chunk_header_size_ + data.size()); +} + +void OutstandingData::Item::Ack() { + ack_state_ = AckState::kAcked; + should_be_retransmitted_ = false; +} + +OutstandingData::Item::NackAction OutstandingData::Item::Nack( + bool retransmit_now) { + ack_state_ = AckState::kNacked; + ++nack_count_; + if ((retransmit_now || nack_count_ >= kNumberOfNacksForRetransmission) && + !is_abandoned_) { + // Nacked enough times - it's considered lost. + if (num_retransmissions_ < *max_retransmissions_) { + should_be_retransmitted_ = true; + return NackAction::kRetransmit; + } + Abandon(); + return NackAction::kAbandon; + } + return NackAction::kNothing; +} + +void OutstandingData::Item::Retransmit() { + ack_state_ = AckState::kUnacked; + should_be_retransmitted_ = false; + + nack_count_ = 0; + ++num_retransmissions_; +} + +void OutstandingData::Item::Abandon() { + is_abandoned_ = true; + should_be_retransmitted_ = false; +} + +bool OutstandingData::Item::has_expired(TimeMs now) const { + return expires_at_ <= now; +} + +bool OutstandingData::IsConsistent() const { + size_t actual_outstanding_bytes = 0; + size_t actual_outstanding_items = 0; + + std::set actual_to_be_retransmitted; + for (const auto& [tsn, item] : outstanding_data_) { + if (item.is_outstanding()) { + actual_outstanding_bytes += GetSerializedChunkSize(item.data()); + ++actual_outstanding_items; + } + + if (item.should_be_retransmitted()) { + actual_to_be_retransmitted.insert(tsn); + } + } + + if (outstanding_data_.empty() && + next_tsn_ != last_cumulative_tsn_ack_.next_value()) { + return false; + } + + return actual_outstanding_bytes == outstanding_bytes_ && + actual_outstanding_items == outstanding_items_ && + actual_to_be_retransmitted == to_be_retransmitted_; +} + +void OutstandingData::AckChunk(AckInfo& ack_info, + std::map::iterator iter) { + if (!iter->second.is_acked()) { + size_t serialized_size = GetSerializedChunkSize(iter->second.data()); + ack_info.bytes_acked += serialized_size; + if (iter->second.is_outstanding()) { + outstanding_bytes_ -= serialized_size; + --outstanding_items_; + } + if (iter->second.should_be_retransmitted()) { + to_be_retransmitted_.erase(iter->first); + } + iter->second.Ack(); + ack_info.highest_tsn_acked = + std::max(ack_info.highest_tsn_acked, iter->first); + } +} + +OutstandingData::AckInfo OutstandingData::HandleSack( + UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + bool is_in_fast_retransmit) { + OutstandingData::AckInfo ack_info(cumulative_tsn_ack); + // Erase all items up to cumulative_tsn_ack. + RemoveAcked(cumulative_tsn_ack, ack_info); + + // ACK packets reported in the gap ack blocks + AckGapBlocks(cumulative_tsn_ack, gap_ack_blocks, ack_info); + + // NACK and possibly mark for retransmit chunks that weren't acked. + NackBetweenAckBlocks(cumulative_tsn_ack, gap_ack_blocks, + is_in_fast_retransmit, ack_info); + + RTC_DCHECK(IsConsistent()); + return ack_info; +} + +void OutstandingData::RemoveAcked(UnwrappedTSN cumulative_tsn_ack, + AckInfo& ack_info) { + auto first_unacked = outstanding_data_.upper_bound(cumulative_tsn_ack); + + for (auto iter = outstanding_data_.begin(); iter != first_unacked; ++iter) { + AckChunk(ack_info, iter); + } + + outstanding_data_.erase(outstanding_data_.begin(), first_unacked); + last_cumulative_tsn_ack_ = cumulative_tsn_ack; +} + +void OutstandingData::AckGapBlocks( + UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + AckInfo& ack_info) { + // Mark all non-gaps as ACKED (but they can't be removed) as (from RFC) + // "SCTP considers the information carried in the Gap Ack Blocks in the + // SACK chunk as advisory.". Note that when NR-SACK is supported, this can be + // handled differently. + + for (auto& block : gap_ack_blocks) { + auto start = outstanding_data_.lower_bound( + UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start)); + auto end = outstanding_data_.upper_bound( + UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end)); + for (auto iter = start; iter != end; ++iter) { + AckChunk(ack_info, iter); + } + } +} + +void OutstandingData::NackBetweenAckBlocks( + UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + bool is_in_fast_recovery, + OutstandingData::AckInfo& ack_info) { + // Mark everything between the blocks as NACKED/TO_BE_RETRANSMITTED. + // https://tools.ietf.org/html/rfc4960#section-7.2.4 + // "Mark the DATA chunk(s) with three miss indications for retransmission." + // "For each incoming SACK, miss indications are incremented only for + // missing TSNs prior to the highest TSN newly acknowledged in the SACK." + // + // What this means is that only when there is a increasing stream of data + // received and there are new packets seen (since last time), packets that are + // in-flight and between gaps should be nacked. This means that SCTP relies on + // the T3-RTX-timer to re-send packets otherwise. + UnwrappedTSN max_tsn_to_nack = ack_info.highest_tsn_acked; + if (is_in_fast_recovery && cumulative_tsn_ack > last_cumulative_tsn_ack_) { + // https://tools.ietf.org/html/rfc4960#section-7.2.4 + // "If an endpoint is in Fast Recovery and a SACK arrives that advances + // the Cumulative TSN Ack Point, the miss indications are incremented for + // all TSNs reported missing in the SACK." + max_tsn_to_nack = UnwrappedTSN::AddTo( + cumulative_tsn_ack, + gap_ack_blocks.empty() ? 0 : gap_ack_blocks.rbegin()->end); + } + + UnwrappedTSN prev_block_last_acked = cumulative_tsn_ack; + for (auto& block : gap_ack_blocks) { + UnwrappedTSN cur_block_first_acked = + UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start); + for (auto iter = outstanding_data_.upper_bound(prev_block_last_acked); + iter != outstanding_data_.lower_bound(cur_block_first_acked); ++iter) { + if (iter->first <= max_tsn_to_nack) { + ack_info.has_packet_loss = + NackItem(iter->first, iter->second, /*retransmit_now=*/false); + } + } + prev_block_last_acked = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end); + } + + // Note that packets are not NACKED which are above the highest gap-ack-block + // (or above the cumulative ack TSN if no gap-ack-blocks) as only packets + // up until the highest_tsn_acked (see above) should be considered when + // NACKing. +} + +bool OutstandingData::NackItem(UnwrappedTSN tsn, + Item& item, + bool retransmit_now) { + if (item.is_outstanding()) { + outstanding_bytes_ -= GetSerializedChunkSize(item.data()); + --outstanding_items_; + } + + switch (item.Nack(retransmit_now)) { + case Item::NackAction::kNothing: + return false; + case Item::NackAction::kRetransmit: + to_be_retransmitted_.insert(tsn); + RTC_DLOG(LS_VERBOSE) << *tsn.Wrap() << " marked for retransmission"; + break; + case Item::NackAction::kAbandon: + AbandonAllFor(item); + break; + } + return true; +} + +void OutstandingData::AbandonAllFor(const Item& item) { + // Erase all remaining chunks from the producer, if any. + if (discard_from_send_queue_(item.data().is_unordered, item.data().stream_id, + item.data().message_id)) { + // There were remaining chunks to be produced for this message. Since the + // receiver may have already received all chunks (up till now) for this + // message, we can't just FORWARD-TSN to the last fragment in this + // (abandoned) message and start sending a new message, as the receiver will + // then see a new message before the end of the previous one was seen (or + // skipped over). So create a new fragment, representing the end, that the + // received will never see as it is abandoned immediately and used as cum + // TSN in the sent FORWARD-TSN. + UnwrappedTSN tsn = next_tsn_; + next_tsn_.Increment(); + Data message_end(item.data().stream_id, item.data().ssn, + item.data().message_id, item.data().fsn, item.data().ppid, + std::vector(), Data::IsBeginning(false), + Data::IsEnd(true), item.data().is_unordered); + Item& added_item = + outstanding_data_ + .emplace(tsn, + Item(std::move(message_end), MaxRetransmits::NoLimit(), + TimeMs(0), TimeMs::InfiniteFuture())) + .first->second; + // The added chunk shouldn't be included in `outstanding_bytes`, so set it + // as acked. + added_item.Ack(); + RTC_DLOG(LS_VERBOSE) << "Adding unsent end placeholder for message at tsn=" + << *tsn.Wrap(); + } + + for (auto& [tsn, other] : outstanding_data_) { + if (!other.is_abandoned() && + other.data().stream_id == item.data().stream_id && + other.data().is_unordered == item.data().is_unordered && + other.data().message_id == item.data().message_id) { + RTC_DLOG(LS_VERBOSE) << "Marking chunk " << *tsn.Wrap() + << " as abandoned"; + if (other.should_be_retransmitted()) { + to_be_retransmitted_.erase(tsn); + } + other.Abandon(); + } + } +} + +std::vector> OutstandingData::GetChunksToBeRetransmitted( + size_t max_size) { + std::vector> result; + + for (auto it = to_be_retransmitted_.begin(); + it != to_be_retransmitted_.end();) { + UnwrappedTSN tsn = *it; + auto elem = outstanding_data_.find(tsn); + RTC_DCHECK(elem != outstanding_data_.end()); + Item& item = elem->second; + RTC_DCHECK(item.should_be_retransmitted()); + RTC_DCHECK(!item.is_outstanding()); + RTC_DCHECK(!item.is_abandoned()); + RTC_DCHECK(!item.is_acked()); + + size_t serialized_size = GetSerializedChunkSize(item.data()); + if (serialized_size <= max_size) { + item.Retransmit(); + result.emplace_back(tsn.Wrap(), item.data().Clone()); + max_size -= serialized_size; + outstanding_bytes_ += serialized_size; + ++outstanding_items_; + it = to_be_retransmitted_.erase(it); + } else { + ++it; + } + // No point in continuing if the packet is full. + if (max_size <= data_chunk_header_size_) { + break; + } + } + + RTC_DCHECK(IsConsistent()); + return result; +} + +void OutstandingData::ExpireOutstandingChunks(TimeMs now) { + for (const auto& [tsn, item] : outstanding_data_) { + // Chunks that are nacked can be expired. Care should be taken not to expire + // unacked (in-flight) chunks as they might have been received, but the SACK + // is either delayed or in-flight and may be received later. + if (item.is_abandoned()) { + // Already abandoned. + } else if (item.is_nacked() && item.has_expired(now)) { + RTC_DLOG(LS_VERBOSE) << "Marking nacked chunk " << *tsn.Wrap() + << " and message " << *item.data().message_id + << " as expired"; + AbandonAllFor(item); + } else { + // A non-expired chunk. No need to iterate any further. + break; + } + } + RTC_DCHECK(IsConsistent()); +} + +UnwrappedTSN OutstandingData::highest_outstanding_tsn() const { + return outstanding_data_.empty() ? last_cumulative_tsn_ack_ + : outstanding_data_.rbegin()->first; +} + +absl::optional OutstandingData::Insert( + const Data& data, + MaxRetransmits max_retransmissions, + TimeMs time_sent, + TimeMs expires_at) { + UnwrappedTSN tsn = next_tsn_; + next_tsn_.Increment(); + + // All chunks are always padded to be even divisible by 4. + size_t chunk_size = GetSerializedChunkSize(data); + outstanding_bytes_ += chunk_size; + ++outstanding_items_; + auto it = outstanding_data_ + .emplace(tsn, Item(data.Clone(), max_retransmissions, time_sent, + expires_at)) + .first; + + if (it->second.has_expired(time_sent)) { + // No need to send it - it was expired when it was in the send + // queue. + RTC_DLOG(LS_VERBOSE) << "Marking freshly produced chunk " + << *it->first.Wrap() << " and message " + << *it->second.data().message_id << " as expired"; + AbandonAllFor(it->second); + RTC_DCHECK(IsConsistent()); + return absl::nullopt; + } + + RTC_DCHECK(IsConsistent()); + return tsn; +} + +void OutstandingData::NackAll() { + for (auto& [tsn, item] : outstanding_data_) { + if (!item.is_acked()) { + NackItem(tsn, item, /*retransmit_now=*/true); + } + } + RTC_DCHECK(IsConsistent()); +} + +absl::optional OutstandingData::MeasureRTT(TimeMs now, + UnwrappedTSN tsn) const { + auto it = outstanding_data_.find(tsn); + if (it != outstanding_data_.end() && !it->second.has_been_retransmitted()) { + // https://tools.ietf.org/html/rfc4960#section-6.3.1 + // "Karn's algorithm: RTT measurements MUST NOT be made using + // packets that were retransmitted (and thus for which it is ambiguous + // whether the reply was for the first instance of the chunk or for a + // later instance)" + return now - it->second.time_sent(); + } + return absl::nullopt; +} + +std::vector> +OutstandingData::GetChunkStatesForTesting() const { + std::vector> states; + states.emplace_back(last_cumulative_tsn_ack_.Wrap(), State::kAcked); + for (const auto& [tsn, item] : outstanding_data_) { + State state; + if (item.is_abandoned()) { + state = State::kAbandoned; + } else if (item.should_be_retransmitted()) { + state = State::kToBeRetransmitted; + } else if (item.is_acked()) { + state = State::kAcked; + } else if (item.is_outstanding()) { + state = State::kInFlight; + } else { + state = State::kNacked; + } + + states.emplace_back(tsn.Wrap(), state); + } + return states; +} + +bool OutstandingData::ShouldSendForwardTsn() const { + if (!outstanding_data_.empty()) { + auto it = outstanding_data_.begin(); + return it->first == last_cumulative_tsn_ack_.next_value() && + it->second.is_abandoned(); + } + return false; +} + +ForwardTsnChunk OutstandingData::CreateForwardTsn() const { + std::map skipped_per_ordered_stream; + UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_; + + for (const auto& [tsn, item] : outstanding_data_) { + if ((tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) { + break; + } + new_cumulative_ack = tsn; + if (!item.data().is_unordered && + item.data().ssn > skipped_per_ordered_stream[item.data().stream_id]) { + skipped_per_ordered_stream[item.data().stream_id] = item.data().ssn; + } + } + + std::vector skipped_streams; + skipped_streams.reserve(skipped_per_ordered_stream.size()); + for (const auto& [stream_id, ssn] : skipped_per_ordered_stream) { + skipped_streams.emplace_back(stream_id, ssn); + } + return ForwardTsnChunk(new_cumulative_ack.Wrap(), std::move(skipped_streams)); +} + +IForwardTsnChunk OutstandingData::CreateIForwardTsn() const { + std::map, MID> skipped_per_stream; + UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_; + + for (const auto& [tsn, item] : outstanding_data_) { + if ((tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) { + break; + } + new_cumulative_ack = tsn; + std::pair stream_id = + std::make_pair(item.data().is_unordered, item.data().stream_id); + + if (item.data().message_id > skipped_per_stream[stream_id]) { + skipped_per_stream[stream_id] = item.data().message_id; + } + } + + std::vector skipped_streams; + skipped_streams.reserve(skipped_per_stream.size()); + for (const auto& [stream, message_id] : skipped_per_stream) { + skipped_streams.emplace_back(stream.first, stream.second, message_id); + } + + return IForwardTsnChunk(new_cumulative_ack.Wrap(), + std::move(skipped_streams)); +} + +} // namespace dcsctp diff --git a/net/dcsctp/tx/outstanding_data.h b/net/dcsctp/tx/outstanding_data.h new file mode 100644 index 0000000000..dc9aab7f96 --- /dev/null +++ b/net/dcsctp/tx/outstanding_data.h @@ -0,0 +1,285 @@ +/* + * 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 NET_DCSCTP_TX_OUTSTANDING_DATA_H_ +#define NET_DCSCTP_TX_OUTSTANDING_DATA_H_ + +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "net/dcsctp/common/sequence_numbers.h" +#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" +#include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h" +#include "net/dcsctp/packet/chunk/sack_chunk.h" +#include "net/dcsctp/packet/data.h" + +namespace dcsctp { + +// This class keeps track of outstanding data chunks (sent, not yet acked) and +// handles acking, nacking, rescheduling and abandoning. +class OutstandingData { + public: + // State for DATA chunks (message fragments) in the queue - used in tests. + enum class State { + // The chunk has been sent but not received yet (from the sender's point of + // view, as no SACK has been received yet that reference this chunk). + kInFlight, + // A SACK has been received which explicitly marked this chunk as missing - + // it's now NACKED and may be retransmitted if NACKED enough times. + kNacked, + // A chunk that will be retransmitted when possible. + kToBeRetransmitted, + // A SACK has been received which explicitly marked this chunk as received. + kAcked, + // A chunk whose message has expired or has been retransmitted too many + // times (RFC3758). It will not be retransmitted anymore. + kAbandoned, + }; + + // Contains variables scoped to a processing of an incoming SACK. + struct AckInfo { + explicit AckInfo(UnwrappedTSN cumulative_tsn_ack) + : highest_tsn_acked(cumulative_tsn_ack) {} + + // Bytes acked by increasing cumulative_tsn_ack and gap_ack_blocks. + size_t bytes_acked = 0; + + // Indicates if this SACK indicates that packet loss has occurred. Just + // because a packet is missing in the SACK doesn't necessarily mean that + // there is packet loss as that packet might be in-flight and received + // out-of-order. But when it has been reported missing consecutive times, it + // will eventually be considered "lost" and this will be set. + bool has_packet_loss = false; + + // Highest TSN Newly Acknowledged, an SCTP variable. + UnwrappedTSN highest_tsn_acked; + }; + + OutstandingData( + size_t data_chunk_header_size, + UnwrappedTSN next_tsn, + UnwrappedTSN last_cumulative_tsn_ack, + std::function discard_from_send_queue) + : data_chunk_header_size_(data_chunk_header_size), + next_tsn_(next_tsn), + last_cumulative_tsn_ack_(last_cumulative_tsn_ack), + discard_from_send_queue_(std::move(discard_from_send_queue)) {} + + AckInfo HandleSack( + UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + bool is_in_fast_retransmit); + + // Given `max_size` of space left in a packet, which chunks can be added to + // it? + std::vector> GetChunksToBeRetransmitted(size_t max_size); + + size_t outstanding_bytes() const { return outstanding_bytes_; } + + // Returns the number of DATA chunks that are in-flight. + size_t outstanding_items() const { return outstanding_items_; } + + // Given the current time `now_ms`, expire and abandon outstanding (sent at + // least once) chunks that have a limited lifetime. + void ExpireOutstandingChunks(TimeMs now); + + bool empty() const { return outstanding_data_.empty(); } + + bool has_data_to_be_retransmitted() const { + return !to_be_retransmitted_.empty(); + } + + UnwrappedTSN last_cumulative_tsn_ack() const { + return last_cumulative_tsn_ack_; + } + + UnwrappedTSN next_tsn() const { return next_tsn_; } + + UnwrappedTSN highest_outstanding_tsn() const; + + // Schedules `data` to be sent, with the provided partial reliability + // parameters. Returns the TSN if the item was actually added and scheduled to + // be sent, and absl::nullopt if it shouldn't be sent. + absl::optional Insert(const Data& data, + MaxRetransmits max_retransmissions, + TimeMs time_sent, + TimeMs expires_at); + + // Nacks all outstanding data. + void NackAll(); + + // Creates a FORWARD-TSN chunk. + ForwardTsnChunk CreateForwardTsn() const; + + // Creates an I-FORWARD-TSN chunk. + IForwardTsnChunk CreateIForwardTsn() const; + + // Given the current time and a TSN, it returns the measured RTT between when + // the chunk was sent and now. It takes into acccount Karn's algorithm, so if + // the chunk has ever been retransmitted, it will return absl::nullopt. + absl::optional MeasureRTT(TimeMs now, UnwrappedTSN tsn) const; + + // Returns the internal state of all queued chunks. This is only used in + // unit-tests. + std::vector> GetChunkStatesForTesting() const; + + // Returns true if the next chunk that is not acked by the peer has been + // abandoned, which means that a FORWARD-TSN should be sent. + bool ShouldSendForwardTsn() const; + + private: + // A fragmented message's DATA chunk while in the retransmission queue, and + // its associated metadata. + class Item { + public: + enum class NackAction { + kNothing, + kRetransmit, + kAbandon, + }; + + explicit Item(Data data, + MaxRetransmits max_retransmissions, + TimeMs time_sent, + TimeMs expires_at) + : max_retransmissions_(max_retransmissions), + time_sent_(time_sent), + expires_at_(expires_at), + data_(std::move(data)) {} + + TimeMs time_sent() const { return time_sent_; } + + const Data& data() const { return data_; } + + // Acks an item. + void Ack(); + + // Nacks an item. If it has been nacked enough times, or if `retransmit_now` + // is set, it might be marked for retransmission. If the item has reached + // its max retransmission value, it will instead be abandoned. The action + // performed is indicated as return value. + NackAction Nack(bool retransmit_now = false); + + // Prepares the item to be retransmitted. Sets it as outstanding and + // clears all nack counters. + void Retransmit(); + + // Marks this item as abandoned. + void Abandon(); + + bool is_outstanding() const { return ack_state_ == AckState::kUnacked; } + bool is_acked() const { return ack_state_ == AckState::kAcked; } + bool is_nacked() const { return ack_state_ == AckState::kNacked; } + bool is_abandoned() const { return is_abandoned_; } + + // Indicates if this chunk should be retransmitted. + bool should_be_retransmitted() const { return should_be_retransmitted_; } + // Indicates if this chunk has ever been retransmitted. + bool has_been_retransmitted() const { return num_retransmissions_ > 0; } + + // Given the current time, and the current state of this DATA chunk, it will + // indicate if it has expired (SCTP Partial Reliability Extension). + bool has_expired(TimeMs now) const; + + private: + enum class AckState { + kUnacked, + kAcked, + kNacked, + }; + // Indicates the presence of this chunk, if it's in flight (Unacked), has + // been received (Acked) or is lost (Nacked). + AckState ack_state_ = AckState::kUnacked; + // Indicates if this chunk has been abandoned, which is a terminal state. + bool is_abandoned_ = false; + // Indicates if this chunk should be retransmitted. + bool should_be_retransmitted_ = false; + + // The number of times the DATA chunk has been nacked (by having received a + // SACK which doesn't include it). Will be cleared on retransmissions. + uint8_t nack_count_ = 0; + // The number of times the DATA chunk has been retransmitted. + uint16_t num_retransmissions_ = 0; + // If the message was sent with a maximum number of retransmissions, this is + // set to that number. The value zero (0) means that it will never be + // retransmitted. + const MaxRetransmits max_retransmissions_; + // When the packet was sent, and placed in this queue. + const TimeMs time_sent_; + // At this exact millisecond, the item is considered expired. If the message + // is not to be expired, this is set to the infinite future. + const TimeMs expires_at_; + // The actual data to send/retransmit. + Data data_; + }; + + // Returns how large a chunk will be, serialized, carrying the data + size_t GetSerializedChunkSize(const Data& data) const; + + // Given a `cumulative_tsn_ack` from an incoming SACK, will remove those items + // in the retransmission queue up until this value and will update `ack_info` + // by setting `bytes_acked_by_cumulative_tsn_ack`. + void RemoveAcked(UnwrappedTSN cumulative_tsn_ack, AckInfo& ack_info); + + // Will mark the chunks covered by the `gap_ack_blocks` from an incoming SACK + // as "acked" and update `ack_info` by adding new TSNs to `added_tsns`. + void AckGapBlocks(UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + AckInfo& ack_info); + + // Mark chunks reported as "missing", as "nacked" or "to be retransmitted" + // depending how many times this has happened. Only packets up until + // `ack_info.highest_tsn_acked` (highest TSN newly acknowledged) are + // nacked/retransmitted. The method will set `ack_info.has_packet_loss`. + void NackBetweenAckBlocks( + UnwrappedTSN cumulative_tsn_ack, + rtc::ArrayView gap_ack_blocks, + bool is_in_fast_recovery, + OutstandingData::AckInfo& ack_info); + + // Acks the chunk referenced by `iter` and updates state in `ack_info` and the + // object's state. + void AckChunk(AckInfo& ack_info, std::map::iterator iter); + + // Helper method to nack an item and perform the correct operations given the + // action indicated when nacking an item (e.g. retransmitting or abandoning). + // The return value indicate if an action was performed, meaning that packet + // loss was detected and acted upon. + bool NackItem(UnwrappedTSN tsn, Item& item, bool retransmit_now); + + // Given that a message fragment, `item` has been abandoned, abandon all other + // fragments that share the same message - both never-before-sent fragments + // that are still in the SendQueue and outstanding chunks. + void AbandonAllFor(const OutstandingData::Item& item); + + bool IsConsistent() const; + + // The size of the data chunk (DATA/I-DATA) header that is used. + const size_t data_chunk_header_size_; + // Next TSN to used. + UnwrappedTSN next_tsn_; + // The last cumulative TSN ack number. + UnwrappedTSN last_cumulative_tsn_ack_; + // Callback when to discard items from the send queue. + std::function discard_from_send_queue_; + + std::map outstanding_data_; + // The number of bytes that are in-flight (sent but not yet acked or nacked). + size_t outstanding_bytes_ = 0; + // The number of DATA chunks that are in-flight (sent but not yet acked or + // nacked). + size_t outstanding_items_ = 0; + // Data chunks that are to be retransmitted. + std::set to_be_retransmitted_; +}; +} // namespace dcsctp +#endif // NET_DCSCTP_TX_OUTSTANDING_DATA_H_ diff --git a/net/dcsctp/tx/outstanding_data_test.cc b/net/dcsctp/tx/outstanding_data_test.cc new file mode 100644 index 0000000000..c161cbb6da --- /dev/null +++ b/net/dcsctp/tx/outstanding_data_test.cc @@ -0,0 +1,401 @@ +/* + * 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 "net/dcsctp/tx/outstanding_data.h" + +#include + +#include "absl/types/optional.h" +#include "net/dcsctp/common/math.h" +#include "net/dcsctp/common/sequence_numbers.h" +#include "net/dcsctp/packet/chunk/data_chunk.h" +#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" +#include "net/dcsctp/public/types.h" +#include "net/dcsctp/testing/data_generator.h" +#include "net/dcsctp/testing/testing_macros.h" +#include "rtc_base/gunit.h" +#include "test/gmock.h" + +namespace dcsctp { +namespace { +using ::testing::MockFunction; +using State = ::dcsctp::OutstandingData::State; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Pair; +using ::testing::Return; +using ::testing::StrictMock; + +constexpr TimeMs kNow(42); + +class OutstandingDataTest : public testing::Test { + protected: + OutstandingDataTest() + : gen_(MID(42)), + buf_(DataChunk::kHeaderSize, + unwrapper_.Unwrap(TSN(10)), + unwrapper_.Unwrap(TSN(9)), + on_discard_.AsStdFunction()) {} + + UnwrappedTSN::Unwrapper unwrapper_; + DataGenerator gen_; + StrictMock> on_discard_; + OutstandingData buf_; +}; + +TEST_F(OutstandingDataTest, HasInitialState) { + EXPECT_TRUE(buf_.empty()); + EXPECT_EQ(buf_.outstanding_bytes(), 0u); + EXPECT_EQ(buf_.outstanding_items(), 0u); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(10)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(9)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked))); + EXPECT_FALSE(buf_.ShouldSendForwardTsn()); +} + +TEST_F(OutstandingDataTest, InsertChunk) { + ASSERT_HAS_VALUE_AND_ASSIGN( + UnwrappedTSN tsn, + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture())); + + EXPECT_EQ(tsn.Wrap(), TSN(10)); + + EXPECT_EQ(buf_.outstanding_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(buf_.outstanding_items(), 1u); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(10)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), + Pair(TSN(10), State::kInFlight))); +} + +TEST_F(OutstandingDataTest, AcksSingleChunk) { + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(10)), {}, false); + + EXPECT_EQ(ack.bytes_acked, DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(10)); + EXPECT_FALSE(ack.has_packet_loss); + + EXPECT_EQ(buf_.outstanding_bytes(), 0u); + EXPECT_EQ(buf_.outstanding_items(), 0u); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(10)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(10)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(10), State::kAcked))); +} + +TEST_F(OutstandingDataTest, AcksPreviousChunkDoesntUpdate) { + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), {}, false); + + EXPECT_EQ(buf_.outstanding_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(buf_.outstanding_items(), 1u); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(10)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), + Pair(TSN(10), State::kInFlight))); +} + +TEST_F(OutstandingDataTest, AcksAndNacksWithGapAckBlocks) { + buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + + std::vector gab = {SackChunk::GapAckBlock(2, 2)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab, false); + EXPECT_EQ(ack.bytes_acked, DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(11)); + EXPECT_FALSE(ack.has_packet_loss); + + EXPECT_EQ(buf_.outstanding_bytes(), 0u); + EXPECT_EQ(buf_.outstanding_items(), 0u); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(12)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(11)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kNacked), // + Pair(TSN(11), State::kAcked))); +} + +TEST_F(OutstandingDataTest, NacksThreeTimesWithSameTsnDoesntRetransmit) { + buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + + std::vector gab1 = {SackChunk::GapAckBlock(2, 2)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kNacked), // + Pair(TSN(11), State::kAcked))); +} + +TEST_F(OutstandingDataTest, NacksThreeTimesResultsInRetransmission) { + buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + + std::vector gab1 = {SackChunk::GapAckBlock(2, 2)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab2 = {SackChunk::GapAckBlock(2, 3)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab2, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab3 = {SackChunk::GapAckBlock(2, 4)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab3, false); + EXPECT_EQ(ack.bytes_acked, DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(13)); + EXPECT_TRUE(ack.has_packet_loss); + + EXPECT_TRUE(buf_.has_data_to_be_retransmitted()); + + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kToBeRetransmitted), // + Pair(TSN(11), State::kAcked), // + Pair(TSN(12), State::kAcked), // + Pair(TSN(13), State::kAcked))); + + EXPECT_THAT(buf_.GetChunksToBeRetransmitted(1000), + ElementsAre(Pair(TSN(10), _))); +} + +TEST_F(OutstandingDataTest, NacksThreeTimesResultsInAbandoning) { + static constexpr MaxRetransmits kMaxRetransmissions(0); + buf_.Insert(gen_.Ordered({1}, "B"), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + + std::vector gab1 = {SackChunk::GapAckBlock(2, 2)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab2 = {SackChunk::GapAckBlock(2, 3)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab2, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(false)); + std::vector gab3 = {SackChunk::GapAckBlock(2, 4)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab3, false); + EXPECT_EQ(ack.bytes_acked, DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(13)); + EXPECT_TRUE(ack.has_packet_loss); + + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(14)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kAbandoned), // + Pair(TSN(11), State::kAbandoned), // + Pair(TSN(12), State::kAbandoned), // + Pair(TSN(13), State::kAbandoned))); +} + +TEST_F(OutstandingDataTest, NacksThreeTimesResultsInAbandoningWithPlaceholder) { + static constexpr MaxRetransmits kMaxRetransmissions(0); + buf_.Insert(gen_.Ordered({1}, "B"), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + + std::vector gab1 = {SackChunk::GapAckBlock(2, 2)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab2 = {SackChunk::GapAckBlock(2, 3)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab2, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(true)); + std::vector gab3 = {SackChunk::GapAckBlock(2, 4)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab3, false); + EXPECT_EQ(ack.bytes_acked, DataChunk::kHeaderSize + RoundUpTo4(1)); + EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(13)); + EXPECT_TRUE(ack.has_packet_loss); + + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(15)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kAbandoned), // + Pair(TSN(11), State::kAbandoned), // + Pair(TSN(12), State::kAbandoned), // + Pair(TSN(13), State::kAbandoned), // + Pair(TSN(14), State::kAbandoned))); +} + +TEST_F(OutstandingDataTest, ExpiresChunkBeforeItIsInserted) { + static constexpr TimeMs kExpiresAt = kNow + DurationMs(1); + EXPECT_TRUE(buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), + kNow, kExpiresAt) + .has_value()); + EXPECT_TRUE(buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), + kNow + DurationMs(0), kExpiresAt) + .has_value()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(false)); + EXPECT_FALSE(buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), + kNow + DurationMs(1), kExpiresAt) + .has_value()); + + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); + EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(13)); + EXPECT_EQ(buf_.highest_outstanding_tsn().Wrap(), TSN(12)); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kAbandoned), // + Pair(TSN(11), State::kAbandoned), + Pair(TSN(12), State::kAbandoned))); +} + +TEST_F(OutstandingDataTest, CanGenerateForwardTsn) { + static constexpr MaxRetransmits kMaxRetransmissions(0); + buf_.Insert(gen_.Ordered({1}, "B"), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), kMaxRetransmissions, kNow, + TimeMs::InfiniteFuture()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(false)); + buf_.NackAll(); + + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kAbandoned), // + Pair(TSN(11), State::kAbandoned), + Pair(TSN(12), State::kAbandoned))); + + EXPECT_TRUE(buf_.ShouldSendForwardTsn()); + ForwardTsnChunk chunk = buf_.CreateForwardTsn(); + EXPECT_EQ(chunk.new_cumulative_tsn(), TSN(12)); +} + +TEST_F(OutstandingDataTest, AckWithGapBlocksFromRFC4960Section334) { + buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + testing::ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kInFlight), // + Pair(TSN(11), State::kInFlight), // + Pair(TSN(12), State::kInFlight), // + Pair(TSN(13), State::kInFlight), // + Pair(TSN(14), State::kInFlight), // + Pair(TSN(15), State::kInFlight), // + Pair(TSN(16), State::kInFlight), // + Pair(TSN(17), State::kInFlight))); + + std::vector gab = {SackChunk::GapAckBlock(2, 3), + SackChunk::GapAckBlock(5, 5)}; + buf_.HandleSack(unwrapper_.Unwrap(TSN(12)), gab, false); + + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(12), State::kAcked), // + Pair(TSN(13), State::kNacked), // + Pair(TSN(14), State::kAcked), // + Pair(TSN(15), State::kAcked), // + Pair(TSN(16), State::kNacked), // + Pair(TSN(17), State::kAcked))); +} + +TEST_F(OutstandingDataTest, MeasureRTT) { + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), + kNow + DurationMs(1), TimeMs::InfiniteFuture()); + buf_.Insert(gen_.Ordered({1}, "BE"), MaxRetransmits::NoLimit(), + kNow + DurationMs(2), TimeMs::InfiniteFuture()); + + static constexpr DurationMs kDuration(123); + ASSERT_HAS_VALUE_AND_ASSIGN( + DurationMs duration, + buf_.MeasureRTT(kNow + kDuration, unwrapper_.Unwrap(TSN(11)))); + + EXPECT_EQ(duration, kDuration - DurationMs(1)); +} + +} // namespace +} // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_queue.cc b/net/dcsctp/tx/retransmission_queue.cc index 0156a56c96..d980710826 100644 --- a/net/dcsctp/tx/retransmission_queue.cc +++ b/net/dcsctp/tx/retransmission_queue.cc @@ -36,6 +36,7 @@ #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/public/types.h" #include "net/dcsctp/timer/timer.h" +#include "net/dcsctp/tx/outstanding_data.h" #include "net/dcsctp/tx/send_queue.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -44,17 +45,13 @@ namespace dcsctp { namespace { -// The number of times a packet must be NACKed before it's retransmitted. -// See https://tools.ietf.org/html/rfc4960#section-7.2.4 -constexpr size_t kNumberOfNacksForRetransmission = 3; - // Allow sending only slightly less than an MTU, to account for headers. constexpr float kMinBytesRequiredToSendFactor = 0.9; } // namespace RetransmissionQueue::RetransmissionQueue( absl::string_view log_prefix, - TSN initial_tsn, + TSN my_initial_tsn, size_t a_rwnd, SendQueue& send_queue, std::function on_new_rtt, @@ -62,7 +59,8 @@ RetransmissionQueue::RetransmissionQueue( Timer& t3_rtx, const DcSctpOptions& options, bool supports_partial_reliability, - bool use_message_interleaving) + bool use_message_interleaving, + const DcSctpSocketHandoverState* handover_state) : options_(options), min_bytes_required_to_send_(options.mtu * kMinBytesRequiredToSendFactor), partial_reliability_(supports_partial_reliability), @@ -74,36 +72,31 @@ RetransmissionQueue::RetransmissionQueue( on_clear_retransmission_counter_( std::move(on_clear_retransmission_counter)), t3_rtx_(t3_rtx), - cwnd_(options_.cwnd_mtus_initial * options_.mtu), - rwnd_(a_rwnd), + cwnd_(handover_state ? handover_state->tx.cwnd + : options_.cwnd_mtus_initial * options_.mtu), + rwnd_(handover_state ? handover_state->tx.rwnd : a_rwnd), // https://tools.ietf.org/html/rfc4960#section-7.2.1 // "The initial value of ssthresh MAY be arbitrarily high (for // example, implementations MAY use the size of the receiver advertised // window)."" - ssthresh_(rwnd_), - next_tsn_(tsn_unwrapper_.Unwrap(initial_tsn)), - last_cumulative_tsn_ack_(tsn_unwrapper_.Unwrap(TSN(*initial_tsn - 1))), - send_queue_(send_queue) {} + ssthresh_(handover_state ? handover_state->tx.ssthresh : rwnd_), + partial_bytes_acked_( + handover_state ? handover_state->tx.partial_bytes_acked : 0), + send_queue_(send_queue), + outstanding_data_( + data_chunk_header_size_, + tsn_unwrapper_.Unwrap(handover_state + ? TSN(handover_state->tx.next_tsn) + : my_initial_tsn), + tsn_unwrapper_.Unwrap(handover_state + ? TSN(handover_state->tx.next_tsn - 1) + : TSN(*my_initial_tsn - 1)), + [this](IsUnordered unordered, StreamID stream_id, MID message_id) { + return send_queue_.Discard(unordered, stream_id, message_id); + }) {} bool RetransmissionQueue::IsConsistent() const { - size_t actual_outstanding_bytes = 0; - size_t actual_outstanding_items = 0; - - std::set actual_to_be_retransmitted; - for (const auto& elem : outstanding_data_) { - if (elem.second.is_outstanding()) { - actual_outstanding_bytes += GetSerializedChunkSize(elem.second.data()); - ++actual_outstanding_items; - } - - if (elem.second.should_be_retransmitted()) { - actual_to_be_retransmitted.insert(elem.first); - } - } - - return actual_outstanding_bytes == outstanding_bytes_ && - actual_outstanding_items == outstanding_items_ && - actual_to_be_retransmitted == to_be_retransmitted_; + return true; } // Returns how large a chunk will be, serialized, carrying the data @@ -111,102 +104,6 @@ size_t RetransmissionQueue::GetSerializedChunkSize(const Data& data) const { return RoundUpTo4(data_chunk_header_size_ + data.size()); } -void RetransmissionQueue::RemoveAcked(UnwrappedTSN cumulative_tsn_ack, - AckInfo& ack_info) { - auto first_unacked = outstanding_data_.upper_bound(cumulative_tsn_ack); - - for (auto iter = outstanding_data_.begin(); iter != first_unacked; ++iter) { - AckChunk(ack_info, iter); - } - - outstanding_data_.erase(outstanding_data_.begin(), first_unacked); -} - -void RetransmissionQueue::AckGapBlocks( - UnwrappedTSN cumulative_tsn_ack, - rtc::ArrayView gap_ack_blocks, - AckInfo& ack_info) { - // Mark all non-gaps as ACKED (but they can't be removed) as (from RFC) - // "SCTP considers the information carried in the Gap Ack Blocks in the - // SACK chunk as advisory.". Note that when NR-SACK is supported, this can be - // handled differently. - - for (auto& block : gap_ack_blocks) { - auto start = outstanding_data_.lower_bound( - UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start)); - auto end = outstanding_data_.upper_bound( - UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end)); - for (auto iter = start; iter != end; ++iter) { - AckChunk(ack_info, iter); - } - } -} - -void RetransmissionQueue::AckChunk( - AckInfo& ack_info, - std::map::iterator iter) { - if (!iter->second.is_acked()) { - size_t serialized_size = GetSerializedChunkSize(iter->second.data()); - ack_info.bytes_acked += serialized_size; - ack_info.acked_tsns.push_back(iter->first.Wrap()); - if (iter->second.is_outstanding()) { - outstanding_bytes_ -= serialized_size; - --outstanding_items_; - } - if (iter->second.should_be_retransmitted()) { - to_be_retransmitted_.erase(iter->first); - } - iter->second.Ack(); - ack_info.highest_tsn_acked = - std::max(ack_info.highest_tsn_acked, iter->first); - } -} - -void RetransmissionQueue::NackBetweenAckBlocks( - UnwrappedTSN cumulative_tsn_ack, - rtc::ArrayView gap_ack_blocks, - AckInfo& ack_info) { - // Mark everything between the blocks as NACKED/TO_BE_RETRANSMITTED. - // https://tools.ietf.org/html/rfc4960#section-7.2.4 - // "Mark the DATA chunk(s) with three miss indications for retransmission." - // "For each incoming SACK, miss indications are incremented only for - // missing TSNs prior to the highest TSN newly acknowledged in the SACK." - // - // What this means is that only when there is a increasing stream of data - // received and there are new packets seen (since last time), packets that are - // in-flight and between gaps should be nacked. This means that SCTP relies on - // the T3-RTX-timer to re-send packets otherwise. - UnwrappedTSN max_tsn_to_nack = ack_info.highest_tsn_acked; - if (is_in_fast_recovery() && cumulative_tsn_ack > last_cumulative_tsn_ack_) { - // https://tools.ietf.org/html/rfc4960#section-7.2.4 - // "If an endpoint is in Fast Recovery and a SACK arrives that advances - // the Cumulative TSN Ack Point, the miss indications are incremented for - // all TSNs reported missing in the SACK." - max_tsn_to_nack = UnwrappedTSN::AddTo( - cumulative_tsn_ack, - gap_ack_blocks.empty() ? 0 : gap_ack_blocks.rbegin()->end); - } - - UnwrappedTSN prev_block_last_acked = cumulative_tsn_ack; - for (auto& block : gap_ack_blocks) { - UnwrappedTSN cur_block_first_acked = - UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start); - for (auto iter = outstanding_data_.upper_bound(prev_block_last_acked); - iter != outstanding_data_.lower_bound(cur_block_first_acked); ++iter) { - if (iter->first <= max_tsn_to_nack) { - ack_info.has_packet_loss = - NackItem(iter->first, iter->second, /*retransmit_now=*/false); - } - } - prev_block_last_acked = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end); - } - - // Note that packets are not NACKED which are above the highest gap-ack-block - // (or above the cumulative ack TSN if no gap-ack-blocks) as only packets - // up until the highest_tsn_acked (see above) should be considered when - // NACKing. -} - void RetransmissionQueue::MaybeExitFastRecovery( UnwrappedTSN cumulative_tsn_ack) { // https://tools.ietf.org/html/rfc4960#section-7.2.4 @@ -237,11 +134,7 @@ void RetransmissionQueue::HandleIncreasedCumulativeTsnAck( // conditions are met, then cwnd MUST be increased by, at most, the // lesser of 1) the total size of the previously outstanding DATA // chunk(s) acknowledged, and 2) the destination's path MTU." - if (options_.slow_start_tcp_style) { - cwnd_ += std::min(total_bytes_acked, cwnd_); - } else { - cwnd_ += std::min(total_bytes_acked, options_.mtu); - } + cwnd_ += std::min(total_bytes_acked, options_.mtu); RTC_DLOG(LS_VERBOSE) << log_prefix_ << "SS increase cwnd=" << cwnd_ << " (" << old_cwnd << ")"; } @@ -262,8 +155,10 @@ void RetransmissionQueue::HandleIncreasedCumulativeTsnAck( // data outstanding (i.e., before arrival of the SACK, flightsize was // greater than or equal to cwnd), increase cwnd by MTU, and reset // partial_bytes_acked to (partial_bytes_acked - cwnd)." - cwnd_ += options_.mtu; + + // Errata: https://datatracker.ietf.org/doc/html/rfc8540#section-3.12 partial_bytes_acked_ -= cwnd_; + cwnd_ += options_.mtu; RTC_DLOG(LS_VERBOSE) << log_prefix_ << "CA increase cwnd=" << cwnd_ << " (" << old_cwnd << ") ssthresh=" << ssthresh_ << ", pba=" << partial_bytes_acked_ << " (" @@ -299,9 +194,7 @@ void RetransmissionQueue::HandlePacketLoss(UnwrappedTSN highest_tsn_acked) { // https://tools.ietf.org/html/rfc4960#section-7.2.4 // "If not in Fast Recovery, enter Fast Recovery and mark the highest // outstanding TSN as the Fast Recovery exit point." - fast_recovery_exit_tsn_ = outstanding_data_.empty() - ? last_cumulative_tsn_ack_ - : outstanding_data_.rbegin()->first; + fast_recovery_exit_tsn_ = outstanding_data_.highest_outstanding_tsn(); RTC_DLOG(LS_VERBOSE) << log_prefix_ << "fast recovery initiated with exit_point=" << *fast_recovery_exit_tsn_->Wrap(); @@ -316,7 +209,9 @@ void RetransmissionQueue::HandlePacketLoss(UnwrappedTSN highest_tsn_acked) { } void RetransmissionQueue::UpdateReceiverWindow(uint32_t a_rwnd) { - rwnd_ = outstanding_bytes_ >= a_rwnd ? 0 : a_rwnd - outstanding_bytes_; + rwnd_ = outstanding_data_.outstanding_bytes() >= a_rwnd + ? 0 + : a_rwnd - outstanding_data_.outstanding_bytes(); } void RetransmissionQueue::StartT3RtxTimerIfOutstandingData() { @@ -354,20 +249,14 @@ bool RetransmissionQueue::IsSackValid(const SackChunk& sack) const { // received, as the gap ack blocks or dup tsn fields may have changed. UnwrappedTSN cumulative_tsn_ack = tsn_unwrapper_.PeekUnwrap(sack.cumulative_tsn_ack()); - if (cumulative_tsn_ack < last_cumulative_tsn_ack_) { + if (cumulative_tsn_ack < outstanding_data_.last_cumulative_tsn_ack()) { // https://tools.ietf.org/html/rfc4960#section-6.2.1 // "If Cumulative TSN Ack is less than the Cumulative TSN Ack Point, // then drop the SACK. Since Cumulative TSN Ack is monotonically // increasing, a SACK whose Cumulative TSN Ack is less than the Cumulative // TSN Ack Point indicates an out-of- order SACK." return false; - } else if (outstanding_data_.empty() && - cumulative_tsn_ack > last_cumulative_tsn_ack_) { - // No in-flight data and cum-tsn-ack above what was last ACKed - not valid. - return false; - } else if (!outstanding_data_.empty() && - cumulative_tsn_ack > outstanding_data_.rbegin()->first) { - // There is in-flight data, but the cum-tsn-ack is beyond that - not valid. + } else if (cumulative_tsn_ack > outstanding_data_.highest_outstanding_tsn()) { return false; } return true; @@ -378,7 +267,9 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { return false; } - size_t old_outstanding_bytes = outstanding_bytes_; + UnwrappedTSN old_last_cumulative_tsn_ack = + outstanding_data_.last_cumulative_tsn_ack(); + size_t old_outstanding_bytes = outstanding_data_.outstanding_bytes(); size_t old_rwnd = rwnd_; UnwrappedTSN cumulative_tsn_ack = tsn_unwrapper_.Unwrap(sack.cumulative_tsn_ack()); @@ -387,33 +278,23 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { UpdateRTT(now, cumulative_tsn_ack); } - AckInfo ack_info(cumulative_tsn_ack); - // Erase all items up to cumulative_tsn_ack. - RemoveAcked(cumulative_tsn_ack, ack_info); - - // ACK packets reported in the gap ack blocks - AckGapBlocks(cumulative_tsn_ack, sack.gap_ack_blocks(), ack_info); - - // NACK and possibly mark for retransmit chunks that weren't acked. - NackBetweenAckBlocks(cumulative_tsn_ack, sack.gap_ack_blocks(), ack_info); + OutstandingData::AckInfo ack_info = outstanding_data_.HandleSack( + cumulative_tsn_ack, sack.gap_ack_blocks(), is_in_fast_retransmit_); // Update of outstanding_data_ is now done. Congestion control remains. UpdateReceiverWindow(sack.a_rwnd()); - RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Received SACK. Acked TSN: " - << StrJoin(ack_info.acked_tsns, ",", - [](rtc::StringBuilder& sb, TSN tsn) { - sb << *tsn; - }) - << ", cum_tsn_ack=" << *cumulative_tsn_ack.Wrap() << " (" - << *last_cumulative_tsn_ack_.Wrap() - << "), outstanding_bytes=" << outstanding_bytes_ << " (" + RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Received SACK, cum_tsn_ack=" + << *cumulative_tsn_ack.Wrap() << " (" + << *old_last_cumulative_tsn_ack.Wrap() + << "), outstanding_bytes=" + << outstanding_data_.outstanding_bytes() << " (" << old_outstanding_bytes << "), rwnd=" << rwnd_ << " (" << old_rwnd << ")"; MaybeExitFastRecovery(cumulative_tsn_ack); - if (cumulative_tsn_ack > last_cumulative_tsn_ack_) { + if (cumulative_tsn_ack > old_last_cumulative_tsn_ack) { // https://tools.ietf.org/html/rfc4960#section-6.3.2 // "Whenever a SACK is received that acknowledges the DATA chunk // with the earliest outstanding TSN for that address, restart the T3-rtx @@ -438,7 +319,6 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { on_clear_retransmission_counter_(); } - last_cumulative_tsn_ack_ = cumulative_tsn_ack; StartT3RtxTimerIfOutstandingData(); RTC_DCHECK(IsConsistent()); return true; @@ -454,28 +334,24 @@ void RetransmissionQueue::UpdateRTT(TimeMs now, // TODO(boivie): Consider occasionally sending DATA chunks with I-bit set and // use only those packets for measurement. - auto it = outstanding_data_.find(cumulative_tsn_ack); - if (it != outstanding_data_.end()) { - if (!it->second.has_been_retransmitted()) { - // https://tools.ietf.org/html/rfc4960#section-6.3.1 - // "Karn's algorithm: RTT measurements MUST NOT be made using - // packets that were retransmitted (and thus for which it is ambiguous - // whether the reply was for the first instance of the chunk or for a - // later instance)" - DurationMs rtt = now - it->second.time_sent(); - on_new_rtt_(rtt); - } + absl::optional rtt = + outstanding_data_.MeasureRTT(now, cumulative_tsn_ack); + + if (rtt.has_value()) { + on_new_rtt_(*rtt); } } void RetransmissionQueue::HandleT3RtxTimerExpiry() { size_t old_cwnd = cwnd_; - size_t old_outstanding_bytes = outstanding_bytes_; + size_t old_outstanding_bytes = outstanding_bytes(); // https://tools.ietf.org/html/rfc4960#section-6.3.3 // "For the destination address for which the timer expires, adjust // its ssthresh with rules defined in Section 7.2.3 and set the cwnd <- MTU." ssthresh_ = std::max(cwnd_ / 2, 4 * options_.mtu); cwnd_ = 1 * options_.mtu; + // Errata: https://datatracker.ietf.org/doc/html/rfc8540#section-3.11 + partial_bytes_acked_ = 0; // https://tools.ietf.org/html/rfc4960#section-6.3.3 // "For the destination address for which the timer expires, set RTO @@ -495,13 +371,7 @@ void RetransmissionQueue::HandleT3RtxTimerExpiry() { // T3-rtx timer expired but did not fit in one MTU (rule E3 above) should be // marked for retransmission and sent as soon as cwnd allows (normally, when a // SACK arrives)." - for (auto& elem : outstanding_data_) { - UnwrappedTSN tsn = elem.first; - TxData& item = elem.second; - if (!item.is_acked()) { - NackItem(tsn, item, /*retransmit_now=*/true); - } - } + outstanding_data_.NackAll(); // https://tools.ietf.org/html/rfc4960#section-6.3.3 // "Start the retransmission timer T3-rtx on the destination address @@ -511,69 +381,11 @@ void RetransmissionQueue::HandleT3RtxTimerExpiry() { RTC_DLOG(LS_INFO) << log_prefix_ << "t3-rtx expired. new cwnd=" << cwnd_ << " (" << old_cwnd << "), ssthresh=" << ssthresh_ - << ", outstanding_bytes " << outstanding_bytes_ << " (" + << ", outstanding_bytes " << outstanding_bytes() << " (" << old_outstanding_bytes << ")"; RTC_DCHECK(IsConsistent()); } -bool RetransmissionQueue::NackItem(UnwrappedTSN tsn, - TxData& item, - bool retransmit_now) { - if (item.is_outstanding()) { - outstanding_bytes_ -= GetSerializedChunkSize(item.data()); - --outstanding_items_; - } - - switch (item.Nack(retransmit_now)) { - case TxData::NackAction::kNothing: - return false; - case TxData::NackAction::kRetransmit: - to_be_retransmitted_.insert(tsn); - RTC_DLOG(LS_VERBOSE) << log_prefix_ << *tsn.Wrap() - << " marked for retransmission"; - break; - case TxData::NackAction::kAbandon: - AbandonAllFor(item); - break; - } - return true; -} - -std::vector> -RetransmissionQueue::GetChunksToBeRetransmitted(size_t max_size) { - std::vector> result; - - for (auto it = to_be_retransmitted_.begin(); - it != to_be_retransmitted_.end();) { - UnwrappedTSN tsn = *it; - auto elem = outstanding_data_.find(tsn); - RTC_DCHECK(elem != outstanding_data_.end()); - TxData& item = elem->second; - RTC_DCHECK(item.should_be_retransmitted()); - RTC_DCHECK(!item.is_outstanding()); - RTC_DCHECK(!item.is_abandoned()); - RTC_DCHECK(!item.is_acked()); - - size_t serialized_size = GetSerializedChunkSize(item.data()); - if (serialized_size <= max_size) { - item.Retransmit(); - result.emplace_back(tsn.Wrap(), item.data().Clone()); - max_size -= serialized_size; - outstanding_bytes_ += serialized_size; - ++outstanding_items_; - it = to_be_retransmitted_.erase(it); - } else { - ++it; - } - // No point in continuing if the packet is full. - if (max_size <= data_chunk_header_size_) { - break; - } - } - - return result; -} - std::vector> RetransmissionQueue::GetChunksToSend( TimeMs now, size_t bytes_remaining_in_packet) { @@ -581,9 +393,9 @@ std::vector> RetransmissionQueue::GetChunksToSend( RTC_DCHECK(IsDivisibleBy4(bytes_remaining_in_packet)); std::vector> to_be_sent; - size_t old_outstanding_bytes = outstanding_bytes_; + size_t old_outstanding_bytes = outstanding_bytes(); size_t old_rwnd = rwnd_; - if (is_in_fast_retransmit()) { + if (is_in_fast_retransmit_) { // https://tools.ietf.org/html/rfc4960#section-7.2.4 // "Determine how many of the earliest (i.e., lowest TSN) DATA chunks // marked for retransmission will fit into a single packet ... Retransmit @@ -591,7 +403,8 @@ std::vector> RetransmissionQueue::GetChunksToSend( // performed, the sender SHOULD ignore the value of cwnd and SHOULD NOT // delay retransmission for this single packet." is_in_fast_retransmit_ = false; - to_be_sent = GetChunksToBeRetransmitted(bytes_remaining_in_packet); + to_be_sent = + outstanding_data_.GetChunksToBeRetransmitted(bytes_remaining_in_packet); size_t to_be_sent_bytes = absl::c_accumulate( to_be_sent, 0, [&](size_t r, const std::pair& d) { return r + GetSerializedChunkSize(d.second); @@ -607,7 +420,7 @@ std::vector> RetransmissionQueue::GetChunksToSend( size_t max_bytes = RoundDownTo4(std::min(max_bytes_to_send(), bytes_remaining_in_packet)); - to_be_sent = GetChunksToBeRetransmitted(max_bytes); + to_be_sent = outstanding_data_.GetChunksToBeRetransmitted(max_bytes); max_bytes -= absl::c_accumulate( to_be_sent, 0, [&](size_t r, const std::pair& d) { return r + GetSerializedChunkSize(d.second); @@ -621,37 +434,20 @@ std::vector> RetransmissionQueue::GetChunksToSend( break; } - UnwrappedTSN tsn = next_tsn_; - next_tsn_.Increment(); - - // All chunks are always padded to be even divisible by 4. size_t chunk_size = GetSerializedChunkSize(chunk_opt->data); max_bytes -= chunk_size; - outstanding_bytes_ += chunk_size; - ++outstanding_items_; rwnd_ -= chunk_size; - auto item_it = - outstanding_data_ - .emplace(tsn, - RetransmissionQueue::TxData( - chunk_opt->data.Clone(), - partial_reliability_ ? chunk_opt->max_retransmissions - : absl::nullopt, - now, - partial_reliability_ ? chunk_opt->expires_at - : absl::nullopt)) - .first; - if (item_it->second.has_expired(now)) { - // No need to send it - it was expired when it was in the send - // queue. - RTC_DLOG(LS_VERBOSE) - << log_prefix_ << "Marking freshly produced chunk " - << *item_it->first.Wrap() << " and message " - << *item_it->second.data().message_id << " as expired"; - AbandonAllFor(item_it->second); - } else { - to_be_sent.emplace_back(tsn.Wrap(), std::move(chunk_opt->data)); + absl::optional tsn = outstanding_data_.Insert( + chunk_opt->data, + partial_reliability_ ? chunk_opt->max_retransmissions + : MaxRetransmits::NoLimit(), + now, + partial_reliability_ ? chunk_opt->expires_at + : TimeMs::InfiniteFuture()); + + if (tsn.has_value()) { + to_be_sent.emplace_back(tsn->Wrap(), std::move(chunk_opt->data)); } } } @@ -676,7 +472,7 @@ std::vector> RetransmissionQueue::GetChunksToSend( [&](size_t r, const std::pair& d) { return r + GetSerializedChunkSize(d.second); }) - << " bytes. outstanding_bytes=" << outstanding_bytes_ + << " bytes. outstanding_bytes=" << outstanding_bytes() << " (" << old_outstanding_bytes << "), cwnd=" << cwnd_ << ", rwnd=" << rwnd_ << " (" << old_rwnd << ")"; } @@ -684,29 +480,6 @@ std::vector> RetransmissionQueue::GetChunksToSend( return to_be_sent; } -std::vector> -RetransmissionQueue::GetChunkStatesForTesting() const { - std::vector> states; - states.emplace_back(last_cumulative_tsn_ack_.Wrap(), State::kAcked); - for (const auto& elem : outstanding_data_) { - State state; - if (elem.second.is_abandoned()) { - state = State::kAbandoned; - } else if (elem.second.should_be_retransmitted()) { - state = State::kToBeRetransmitted; - } else if (elem.second.is_acked()) { - state = State::kAcked; - } else if (elem.second.is_outstanding()) { - state = State::kInFlight; - } else { - state = State::kNacked; - } - - states.emplace_back(elem.first.Wrap(), state); - } - return states; -} - bool RetransmissionQueue::can_send_data() const { return cwnd_ < options_.avoid_fragmentation_cwnd_mtus * options_.mtu || max_bytes_to_send() >= min_bytes_required_to_send_; @@ -716,191 +489,26 @@ bool RetransmissionQueue::ShouldSendForwardTsn(TimeMs now) { if (!partial_reliability_) { return false; } - ExpireOutstandingChunks(now); - if (!outstanding_data_.empty()) { - auto it = outstanding_data_.begin(); - return it->first == last_cumulative_tsn_ack_.next_value() && - it->second.is_abandoned(); - } + outstanding_data_.ExpireOutstandingChunks(now); + bool ret = outstanding_data_.ShouldSendForwardTsn(); RTC_DCHECK(IsConsistent()); - return false; -} - -void RetransmissionQueue::TxData::Ack() { - ack_state_ = AckState::kAcked; - should_be_retransmitted_ = false; -} - -RetransmissionQueue::TxData::NackAction RetransmissionQueue::TxData::Nack( - bool retransmit_now) { - ack_state_ = AckState::kNacked; - ++nack_count_; - if ((retransmit_now || nack_count_ >= kNumberOfNacksForRetransmission) && - !is_abandoned_) { - // Nacked enough times - it's considered lost. - if (!max_retransmissions_.has_value() || - num_retransmissions_ < max_retransmissions_) { - should_be_retransmitted_ = true; - return NackAction::kRetransmit; - } - Abandon(); - return NackAction::kAbandon; - } - return NackAction::kNothing; -} - -void RetransmissionQueue::TxData::Retransmit() { - ack_state_ = AckState::kUnacked; - should_be_retransmitted_ = false; - - nack_count_ = 0; - ++num_retransmissions_; -} - -void RetransmissionQueue::TxData::Abandon() { - is_abandoned_ = true; - should_be_retransmitted_ = false; -} - -bool RetransmissionQueue::TxData::has_expired(TimeMs now) const { - return expires_at_.has_value() && *expires_at_ <= now; -} - -void RetransmissionQueue::ExpireOutstandingChunks(TimeMs now) { - for (const auto& elem : outstanding_data_) { - UnwrappedTSN tsn = elem.first; - const TxData& item = elem.second; - - // Chunks that are nacked can be expired. Care should be taken not to expire - // unacked (in-flight) chunks as they might have been received, but the SACK - // is either delayed or in-flight and may be received later. - if (item.is_abandoned()) { - // Already abandoned. - } else if (item.is_nacked() && item.has_expired(now)) { - RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Marking nacked chunk " - << *tsn.Wrap() << " and message " - << *item.data().message_id << " as expired"; - AbandonAllFor(item); - } else { - // A non-expired chunk. No need to iterate any further. - break; - } - } -} - -void RetransmissionQueue::AbandonAllFor( - const RetransmissionQueue::TxData& item) { - // Erase all remaining chunks from the producer, if any. - if (send_queue_.Discard(item.data().is_unordered, item.data().stream_id, - item.data().message_id)) { - // There were remaining chunks to be produced for this message. Since the - // receiver may have already received all chunks (up till now) for this - // message, we can't just FORWARD-TSN to the last fragment in this - // (abandoned) message and start sending a new message, as the receiver will - // then see a new message before the end of the previous one was seen (or - // skipped over). So create a new fragment, representing the end, that the - // received will never see as it is abandoned immediately and used as cum - // TSN in the sent FORWARD-TSN. - UnwrappedTSN tsn = next_tsn_; - next_tsn_.Increment(); - Data message_end(item.data().stream_id, item.data().ssn, - item.data().message_id, item.data().fsn, item.data().ppid, - std::vector(), Data::IsBeginning(false), - Data::IsEnd(true), item.data().is_unordered); - TxData& added_item = - outstanding_data_ - .emplace(tsn, RetransmissionQueue::TxData(std::move(message_end), - absl::nullopt, TimeMs(0), - absl::nullopt)) - .first->second; - // The added chunk shouldn't be included in `outstanding_bytes`, so set it - // as acked. - added_item.Ack(); - RTC_DLOG(LS_VERBOSE) << log_prefix_ - << "Adding unsent end placeholder for message at tsn=" - << *tsn.Wrap(); - } - for (auto& elem : outstanding_data_) { - UnwrappedTSN tsn = elem.first; - TxData& other = elem.second; - - if (!other.is_abandoned() && - other.data().stream_id == item.data().stream_id && - other.data().is_unordered == item.data().is_unordered && - other.data().message_id == item.data().message_id) { - RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Marking chunk " << *tsn.Wrap() - << " as abandoned"; - if (other.should_be_retransmitted()) { - to_be_retransmitted_.erase(tsn); - } - other.Abandon(); - } - } + return ret; } size_t RetransmissionQueue::max_bytes_to_send() const { - size_t left = outstanding_bytes_ >= cwnd_ ? 0 : cwnd_ - outstanding_bytes_; + size_t left = outstanding_bytes() >= cwnd_ ? 0 : cwnd_ - outstanding_bytes(); + + if (outstanding_bytes() == 0) { + // https://datatracker.ietf.org/doc/html/rfc4960#section-6.1 + // ... However, regardless of the value of rwnd (including if it is 0), the + // data sender can always have one DATA chunk in flight to the receiver if + // allowed by cwnd (see rule B, below). + return left; + } + return std::min(rwnd(), left); } -ForwardTsnChunk RetransmissionQueue::CreateForwardTsn() const { - std::map skipped_per_ordered_stream; - UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_; - - for (const auto& elem : outstanding_data_) { - UnwrappedTSN tsn = elem.first; - const TxData& item = elem.second; - - if ((tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) { - break; - } - new_cumulative_ack = tsn; - if (!item.data().is_unordered && - item.data().ssn > skipped_per_ordered_stream[item.data().stream_id]) { - skipped_per_ordered_stream[item.data().stream_id] = item.data().ssn; - } - } - - std::vector skipped_streams; - skipped_streams.reserve(skipped_per_ordered_stream.size()); - for (const auto& elem : skipped_per_ordered_stream) { - skipped_streams.emplace_back(elem.first, elem.second); - } - return ForwardTsnChunk(new_cumulative_ack.Wrap(), std::move(skipped_streams)); -} - -IForwardTsnChunk RetransmissionQueue::CreateIForwardTsn() const { - std::map, MID> skipped_per_stream; - UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_; - - for (const auto& elem : outstanding_data_) { - UnwrappedTSN tsn = elem.first; - const TxData& item = elem.second; - - if ((tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) { - break; - } - new_cumulative_ack = tsn; - std::pair stream_id = - std::make_pair(item.data().is_unordered, item.data().stream_id); - - if (item.data().message_id > skipped_per_stream[stream_id]) { - skipped_per_stream[stream_id] = item.data().message_id; - } - } - - std::vector skipped_streams; - skipped_streams.reserve(skipped_per_stream.size()); - for (const auto& elem : skipped_per_stream) { - const std::pair& stream = elem.first; - MID message_id = elem.second; - skipped_streams.emplace_back(stream.first, stream.second, message_id); - } - - return IForwardTsnChunk(new_cumulative_ack.Wrap(), - std::move(skipped_streams)); -} - void RetransmissionQueue::PrepareResetStreams( rtc::ArrayView streams) { // TODO(boivie): These calls are now only affecting the send queue. The @@ -919,4 +527,25 @@ void RetransmissionQueue::RollbackResetStreams() { send_queue_.RollbackResetStreams(); } +HandoverReadinessStatus RetransmissionQueue::GetHandoverReadiness() const { + HandoverReadinessStatus status; + if (!outstanding_data_.empty()) { + status.Add(HandoverUnreadinessReason::kRetransmissionQueueOutstandingData); + } + if (fast_recovery_exit_tsn_.has_value()) { + status.Add(HandoverUnreadinessReason::kRetransmissionQueueFastRecovery); + } + if (outstanding_data_.has_data_to_be_retransmitted()) { + status.Add(HandoverUnreadinessReason::kRetransmissionQueueNotEmpty); + } + return status; +} + +void RetransmissionQueue::AddHandoverState(DcSctpSocketHandoverState& state) { + state.tx.next_tsn = next_tsn().value(); + state.tx.rwnd = rwnd_; + state.tx.cwnd = cwnd_; + state.tx.ssthresh = ssthresh_; + state.tx.partial_bytes_acked = partial_bytes_acked_; +} } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_queue.h b/net/dcsctp/tx/retransmission_queue.h index e4175c6aab..08f11db744 100644 --- a/net/dcsctp/tx/retransmission_queue.h +++ b/net/dcsctp/tx/retransmission_queue.h @@ -26,8 +26,10 @@ #include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h" #include "net/dcsctp/packet/chunk/sack_chunk.h" #include "net/dcsctp/packet/data.h" +#include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/timer/timer.h" +#include "net/dcsctp/tx/outstanding_data.h" #include "net/dcsctp/tx/retransmission_timeout.h" #include "net/dcsctp/tx/send_queue.h" @@ -44,40 +46,26 @@ namespace dcsctp { class RetransmissionQueue { public: static constexpr size_t kMinimumFragmentedPayload = 10; - // State for DATA chunks (message fragments) in the queue - used in tests. - enum class State { - // The chunk has been sent but not received yet (from the sender's point of - // view, as no SACK has been received yet that reference this chunk). - kInFlight, - // A SACK has been received which explicitly marked this chunk as missing - - // it's now NACKED and may be retransmitted if NACKED enough times. - kNacked, - // A chunk that will be retransmitted when possible. - kToBeRetransmitted, - // A SACK has been received which explicitly marked this chunk as received. - kAcked, - // A chunk whose message has expired or has been retransmitted too many - // times (RFC3758). It will not be retransmitted anymore. - kAbandoned, - }; - - // Creates a RetransmissionQueue which will send data using `initial_tsn` as - // the first TSN to use for sent fragments. It will poll data from - // `send_queue` and call `on_send_queue_empty` when it is empty. When - // SACKs are received, it will estimate the RTT, and call `on_new_rtt`. When - // an outstanding chunk has been ACKed, it will call + using State = OutstandingData::State; + // Creates a RetransmissionQueue which will send data using `my_initial_tsn` + // (or a value from `DcSctpSocketHandoverState` if given) as the first TSN + // to use for sent fragments. It will poll data from `send_queue`. When SACKs + // are received, it will estimate the RTT, and call `on_new_rtt`. When an + // outstanding chunk has been ACKed, it will call // `on_clear_retransmission_counter` and will also use `t3_rtx`, which is the // SCTP retransmission timer to manage retransmissions. - RetransmissionQueue(absl::string_view log_prefix, - TSN initial_tsn, - size_t a_rwnd, - SendQueue& send_queue, - std::function on_new_rtt, - std::function on_clear_retransmission_counter, - Timer& t3_rtx, - const DcSctpOptions& options, - bool supports_partial_reliability = true, - bool use_message_interleaving = false); + RetransmissionQueue( + absl::string_view log_prefix, + TSN my_initial_tsn, + size_t a_rwnd, + SendQueue& send_queue, + std::function on_new_rtt, + std::function on_clear_retransmission_counter, + Timer& t3_rtx, + const DcSctpOptions& options, + bool supports_partial_reliability = true, + bool use_message_interleaving = false, + const DcSctpSocketHandoverState* handover_state = nullptr); // Handles a received SACK. Returns true if the `sack` was processed and // false if it was discarded due to received out-of-order and not relevant. @@ -97,10 +85,13 @@ class RetransmissionQueue { // Returns the internal state of all queued chunks. This is only used in // unit-tests. - std::vector> GetChunkStatesForTesting() const; + std::vector> GetChunkStatesForTesting() + const { + return outstanding_data_.GetChunkStatesForTesting(); + } // Returns the next TSN that will be allocated for sent DATA chunks. - TSN next_tsn() const { return next_tsn_.Wrap(); } + TSN next_tsn() const { return outstanding_data_.next_tsn().Wrap(); } // Returns the size of the congestion window, in bytes. This is the number of // bytes that may be in-flight. @@ -113,10 +104,14 @@ class RetransmissionQueue { size_t rwnd() const { return rwnd_; } // Returns the number of bytes of packets that are in-flight. - size_t outstanding_bytes() const { return outstanding_bytes_; } + size_t outstanding_bytes() const { + return outstanding_data_.outstanding_bytes(); + } // Returns the number of DATA chunks that are in-flight. - size_t outstanding_items() const { return outstanding_items_; } + size_t outstanding_items() const { + return outstanding_data_.outstanding_items(); + } // Indicates if the congestion control algorithm allows data to be sent. bool can_send_data() const; @@ -127,10 +122,14 @@ class RetransmissionQueue { bool ShouldSendForwardTsn(TimeMs now); // Creates a FORWARD-TSN chunk. - ForwardTsnChunk CreateForwardTsn() const; + ForwardTsnChunk CreateForwardTsn() const { + return outstanding_data_.CreateForwardTsn(); + } // Creates an I-FORWARD-TSN chunk. - IForwardTsnChunk CreateIForwardTsn() const; + IForwardTsnChunk CreateIForwardTsn() const { + return outstanding_data_.CreateIForwardTsn(); + } // See the SendQueue for a longer description of these methods related // to stream resetting. @@ -139,118 +138,16 @@ class RetransmissionQueue { void CommitResetStreams(); void RollbackResetStreams(); + HandoverReadinessStatus GetHandoverReadiness() const; + + void AddHandoverState(DcSctpSocketHandoverState& state); + private: enum class CongestionAlgorithmPhase { kSlowStart, kCongestionAvoidance, }; - // A fragmented message's DATA chunk while in the retransmission queue, and - // its associated metadata. - class TxData { - public: - enum class NackAction { - kNothing, - kRetransmit, - kAbandon, - }; - - explicit TxData(Data data, - absl::optional max_retransmissions, - TimeMs time_sent, - absl::optional expires_at) - : max_retransmissions_(max_retransmissions), - time_sent_(time_sent), - expires_at_(expires_at), - data_(std::move(data)) {} - - TimeMs time_sent() const { return time_sent_; } - - const Data& data() const { return data_; } - - // Acks an item. - void Ack(); - - // Nacks an item. If it has been nacked enough times, or if `retransmit_now` - // is set, it might be marked for retransmission. If the item has reached - // its max retransmission value, it will instead be abandoned. The action - // performed is indicated as return value. - NackAction Nack(bool retransmit_now = false); - - // Prepares the item to be retransmitted. Sets it as outstanding and - // clears all nack counters. - void Retransmit(); - - // Marks this item as abandoned. - void Abandon(); - - bool is_outstanding() const { return ack_state_ == AckState::kUnacked; } - bool is_acked() const { return ack_state_ == AckState::kAcked; } - bool is_nacked() const { return ack_state_ == AckState::kNacked; } - bool is_abandoned() const { return is_abandoned_; } - - // Indicates if this chunk should be retransmitted. - bool should_be_retransmitted() const { return should_be_retransmitted_; } - // Indicates if this chunk has ever been retransmitted. - bool has_been_retransmitted() const { return num_retransmissions_ > 0; } - - // Given the current time, and the current state of this DATA chunk, it will - // indicate if it has expired (SCTP Partial Reliability Extension). - bool has_expired(TimeMs now) const; - - private: - enum class AckState { - kUnacked, - kAcked, - kNacked, - }; - // Indicates the presence of this chunk, if it's in flight (Unacked), has - // been received (Acked) or is lost (Nacked). - AckState ack_state_ = AckState::kUnacked; - // Indicates if this chunk has been abandoned, which is a terminal state. - bool is_abandoned_ = false; - // Indicates if this chunk should be retransmitted. - bool should_be_retransmitted_ = false; - - // The number of times the DATA chunk has been nacked (by having received a - // SACK which doesn't include it). Will be cleared on retransmissions. - size_t nack_count_ = 0; - // The number of times the DATA chunk has been retransmitted. - size_t num_retransmissions_ = 0; - // If the message was sent with a maximum number of retransmissions, this is - // set to that number. The value zero (0) means that it will never be - // retransmitted. - const absl::optional max_retransmissions_; - // When the packet was sent, and placed in this queue. - const TimeMs time_sent_; - // If the message was sent with an expiration time, this is set. - const absl::optional expires_at_; - // The actual data to send/retransmit. - Data data_; - }; - - // Contains variables scoped to a processing of an incoming SACK. - struct AckInfo { - explicit AckInfo(UnwrappedTSN cumulative_tsn_ack) - : highest_tsn_acked(cumulative_tsn_ack) {} - - // All TSNs that have been acked (for the first time) in this SACK. - std::vector acked_tsns; - - // Bytes acked by increasing cumulative_tsn_ack and gap_ack_blocks. - size_t bytes_acked = 0; - - // Indicates if this SACK indicates that packet loss has occurred. Just - // because a packet is missing in the SACK doesn't necessarily mean that - // there is packet loss as that packet might be in-flight and received - // out-of-order. But when it has been reported missing consecutive times, it - // will eventually be considered "lost" and this will be set. - bool has_packet_loss = false; - - // Highest TSN Newly Acknowledged, an SCTP variable. - UnwrappedTSN highest_tsn_acked; - }; - bool IsConsistent() const; // Returns how large a chunk will be, serialized, carrying the data @@ -261,48 +158,12 @@ class RetransmissionQueue { return fast_recovery_exit_tsn_.has_value(); } - // Indicates if the congestion control algorithm is in "fast retransmit". - bool is_in_fast_retransmit() const { return is_in_fast_retransmit_; } - // Indicates if the provided SACK is valid given what has previously been // received. If it returns false, the SACK is most likely a duplicate of // something already seen, so this returning false doesn't necessarily mean // that the SACK is illegal. bool IsSackValid(const SackChunk& sack) const; - // Given a `cumulative_tsn_ack` from an incoming SACK, will remove those items - // in the retransmission queue up until this value and will update `ack_info` - // by setting `bytes_acked_by_cumulative_tsn_ack` and `acked_tsns`. - void RemoveAcked(UnwrappedTSN cumulative_tsn_ack, AckInfo& ack_info); - - // Helper method to nack an item and perform the correct operations given the - // action indicated when nacking an item (e.g. retransmitting or abandoning). - // The return value indicate if an action was performed, meaning that packet - // loss was detected and acted upon. - bool NackItem(UnwrappedTSN cumulative_tsn_ack, - TxData& item, - bool retransmit_now); - - // Will mark the chunks covered by the `gap_ack_blocks` from an incoming SACK - // as "acked" and update `ack_info` by adding new TSNs to `added_tsns`. - void AckGapBlocks(UnwrappedTSN cumulative_tsn_ack, - rtc::ArrayView gap_ack_blocks, - AckInfo& ack_info); - - // Acks the chunk referenced by `iter` and updates state in `ack_info` and the - // object's state. - void AckChunk(AckInfo& ack_info, - std::map::iterator iter); - - // Mark chunks reported as "missing", as "nacked" or "to be retransmitted" - // depending how many times this has happened. Only packets up until - // `ack_info.highest_tsn_acked` (highest TSN newly acknowledged) are - // nacked/retransmitted. The method will set `ack_info.has_packet_loss`. - void NackBetweenAckBlocks( - UnwrappedTSN cumulative_tsn_ack, - rtc::ArrayView gap_ack_blocks, - AckInfo& ack_info); - // When a SACK chunk is received, this method will be called which _may_ call // into the `RetransmissionTimeout` to update the RTO. void UpdateRTT(TimeMs now, UnwrappedTSN cumulative_tsn_ack); @@ -324,21 +185,10 @@ class RetransmissionQueue { void HandlePacketLoss(UnwrappedTSN highest_tsn_acked); // Update the view of the receiver window size. void UpdateReceiverWindow(uint32_t a_rwnd); - // Given `max_size` of space left in a packet, which chunks can be added to - // it? - std::vector> GetChunksToBeRetransmitted(size_t max_size); // If there is data sent and not ACKED, ensure that the retransmission timer // is running. void StartT3RtxTimerIfOutstandingData(); - // Given the current time `now_ms`, expire and abandon outstanding (sent at - // least once) chunks that have a limited lifetime. - void ExpireOutstandingChunks(TimeMs now); - // Given that a message fragment, `item` has been abandoned, abandon all other - // fragments that share the same message - both never-before-sent fragments - // that are still in the SendQueue and outstanding chunks. - void AbandonAllFor(const RetransmissionQueue::TxData& item); - // Returns the current congestion control algorithm phase. CongestionAlgorithmPhase phase() const { return (cwnd_ <= ssthresh_) @@ -375,30 +225,19 @@ class RetransmissionQueue { // Slow Start Threshold. See RFC4960. size_t ssthresh_; // Partial Bytes Acked. See RFC4960. - size_t partial_bytes_acked_ = 0; + size_t partial_bytes_acked_; // If set, fast recovery is enabled until this TSN has been cumulative // acked. absl::optional fast_recovery_exit_tsn_ = absl::nullopt; // Indicates if the congestion algorithm is in fast retransmit. bool is_in_fast_retransmit_ = false; - // Next TSN to used. - UnwrappedTSN next_tsn_; - // The last cumulative TSN ack number - UnwrappedTSN last_cumulative_tsn_ack_; // The send queue. SendQueue& send_queue_; // All the outstanding data chunks that are in-flight and that have not been // cumulative acked. Note that it also contains chunks that have been acked in // gap ack blocks. - std::map outstanding_data_; - // Data chunks that are to be retransmitted. - std::set to_be_retransmitted_; - // The number of bytes that are in-flight (sent but not yet acked or nacked). - size_t outstanding_bytes_ = 0; - // The number of DATA chunks that are in-flight (sent but not yet acked or - // nacked). - size_t outstanding_items_ = 0; + OutstandingData outstanding_data_; }; } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_queue_test.cc b/net/dcsctp/tx/retransmission_queue_test.cc index c64aeb19df..8294a3111d 100644 --- a/net/dcsctp/tx/retransmission_queue_test.cc +++ b/net/dcsctp/tx/retransmission_queue_test.cc @@ -18,6 +18,9 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/task_queue/task_queue_base.h" +#include "net/dcsctp/common/handover_testing.h" +#include "net/dcsctp/common/math.h" #include "net/dcsctp/packet/chunk/data_chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -61,7 +64,9 @@ class RetransmissionQueueTest : public testing::Test { : options_(MakeOptions()), gen_(MID(42)), timeout_manager_([this]() { return now_; }), - timer_manager_([this]() { return timeout_manager_.CreateTimeout(); }), + timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { + return timeout_manager_.CreateTimeout(precision); + }), timer_(timer_manager_.CreateTimer( "test/t3_rtx", []() { return absl::nullopt; }, @@ -89,6 +94,18 @@ class RetransmissionQueueTest : public testing::Test { supports_partial_reliability, use_message_interleaving); } + RetransmissionQueue CreateQueueByHandover(RetransmissionQueue& queue) { + EXPECT_EQ(queue.GetHandoverReadiness(), HandoverReadinessStatus()); + DcSctpSocketHandoverState state; + queue.AddHandoverState(state); + g_handover_state_transformer_for_test(&state); + return RetransmissionQueue( + "", TSN(10), kArwnd, producer_, on_rtt_.AsStdFunction(), + on_clear_retransmission_counter_.AsStdFunction(), *timer_, options_, + /*supports_partial_reliability=*/true, + /*use_message_interleaving=*/false, &state); + } + DcSctpOptions options_; DataGenerator gen_; TimeMs now_ = TimeMs(0); @@ -340,7 +357,7 @@ TEST_F(RetransmissionQueueTest, LimitedRetransmissionOnlyWithRfc3758Support) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "BE")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -370,7 +387,7 @@ TEST_F(RetransmissionQueueTest, LimitsRetransmissionsAsUdp) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "BE")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -412,7 +429,7 @@ TEST_F(RetransmissionQueueTest, LimitsRetransmissionsToThreeSends) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "BE")); - dts.max_retransmissions = 3; + dts.max_retransmissions = MaxRetransmits(3); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -503,17 +520,17 @@ TEST_F(RetransmissionQueueTest, ProducesValidForwardTsn) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "B")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({5, 6, 7, 8}, "")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({9, 10, 11, 12}, "")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -558,17 +575,17 @@ TEST_F(RetransmissionQueueTest, ProducesValidForwardTsnWhenFullySent) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "B")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({5, 6, 7, 8}, "")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({9, 10, 11, 12}, "E")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -613,28 +630,28 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) { DataGeneratorOptions opts; opts.stream_id = StreamID(1); SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "B", opts)); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { DataGeneratorOptions opts; opts.stream_id = StreamID(2); SendQueue::DataToSend dts(gen_.Unordered({1, 2, 3, 4}, "B", opts)); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { DataGeneratorOptions opts; opts.stream_id = StreamID(3); SendQueue::DataToSend dts(gen_.Ordered({9, 10, 11, 12}, "B", opts)); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { DataGeneratorOptions opts; opts.stream_id = StreamID(4); SendQueue::DataToSend dts(gen_.Ordered({13, 14, 15, 16}, "B", opts)); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -728,7 +745,7 @@ TEST_F(RetransmissionQueueTest, MeasureRTT) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "B")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -893,17 +910,17 @@ TEST_F(RetransmissionQueueTest, AccountsNackedAbandonedChunksAsNotOutstanding) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "B")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({5, 6, 7, 8}, "")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({9, 10, 11, 12}, "")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); @@ -992,7 +1009,7 @@ TEST_F(RetransmissionQueueTest, LimitsRetransmissionsOnlyWhenNackedThreeTimes) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "BE")); - dts.max_retransmissions = 0; + dts.max_retransmissions = MaxRetransmits(0); return dts; }) .WillOnce(CreateChunk()) @@ -1063,7 +1080,7 @@ TEST_F(RetransmissionQueueTest, AbandonsRtxLimit2WhenNackedNineTimes) { EXPECT_CALL(producer_, Produce) .WillOnce([this](TimeMs, size_t) { SendQueue::DataToSend dts(gen_.Ordered({1, 2, 3, 4}, "BE")); - dts.max_retransmissions = 2; + dts.max_retransmissions = MaxRetransmits(2); return dts; }) .WillOnce(CreateChunk()) @@ -1275,5 +1292,204 @@ TEST_F(RetransmissionQueueTest, AllowsSmallFragmentsOnSmallCongestionWindow) { EXPECT_TRUE(queue.can_send_data()); } +TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenHasNoOutstandingData) { + RetransmissionQueue queue = CreateQueue(); + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1)); + EXPECT_EQ( + queue.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kRetransmissionQueueOutstandingData)); + + queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {})); + EXPECT_EQ(queue.GetHandoverReadiness(), HandoverReadinessStatus()); +} + +TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) { + RetransmissionQueue queue = CreateQueue(); + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(8)); + EXPECT_EQ( + queue.GetHandoverReadiness(), + HandoverReadinessStatus( + HandoverUnreadinessReason::kRetransmissionQueueOutstandingData)); + + // Send more chunks, but leave some chunks unacked to force retransmission + // after three NACKs. + + // Send 18 + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1)); + + // Ack 12, 14-15, 17-18 + queue.HandleSack(now_, SackChunk(TSN(12), kArwnd, + {SackChunk::GapAckBlock(2, 3), + SackChunk::GapAckBlock(5, 6)}, + {})); + + // Send 19 + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1)); + + // Ack 12, 14-15, 17-19 + queue.HandleSack(now_, SackChunk(TSN(12), kArwnd, + {SackChunk::GapAckBlock(2, 3), + SackChunk::GapAckBlock(5, 7)}, + {})); + + // Send 20 + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1)); + + // Ack 12, 14-15, 17-20 + // This will trigger "fast retransmit" mode and only chunks 13 and 16 will be + // resent right now. The send queue will not even be queried. + queue.HandleSack(now_, SackChunk(TSN(12), kArwnd, + {SackChunk::GapAckBlock(2, 3), + SackChunk::GapAckBlock(5, 8)}, + {})); + EXPECT_EQ( + queue.GetHandoverReadiness(), + HandoverReadinessStatus() + .Add(HandoverUnreadinessReason::kRetransmissionQueueOutstandingData) + .Add(HandoverUnreadinessReason::kRetransmissionQueueFastRecovery) + .Add(HandoverUnreadinessReason::kRetransmissionQueueNotEmpty)); + + // Send "fast retransmit" mode chunks + EXPECT_CALL(producer_, Produce).Times(0); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(2)); + EXPECT_EQ( + queue.GetHandoverReadiness(), + HandoverReadinessStatus() + .Add(HandoverUnreadinessReason::kRetransmissionQueueOutstandingData) + .Add(HandoverUnreadinessReason::kRetransmissionQueueFastRecovery)); + + // Ack 20 to confirm the retransmission + queue.HandleSack(now_, SackChunk(TSN(20), kArwnd, {}, {})); + EXPECT_EQ(queue.GetHandoverReadiness(), HandoverReadinessStatus()); +} + +TEST_F(RetransmissionQueueTest, HandoverTest) { + RetransmissionQueue queue = CreateQueue(); + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(2)); + queue.HandleSack(now_, SackChunk(TSN(11), kArwnd, {}, {})); + + RetransmissionQueue handedover_queue = CreateQueueByHandover(queue); + + EXPECT_CALL(producer_, Produce) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillOnce(CreateChunk()) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + EXPECT_THAT(GetSentPacketTSNs(handedover_queue), + testing::ElementsAre(TSN(12), TSN(13), TSN(14))); + + handedover_queue.HandleSack(now_, SackChunk(TSN(13), kArwnd, {}, {})); + EXPECT_THAT(handedover_queue.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(13), State::kAcked), // + Pair(TSN(14), State::kInFlight))); +} + +TEST_F(RetransmissionQueueTest, CanAlwaysSendOnePacket) { + RetransmissionQueue queue = CreateQueue(); + + // A large payload - enough to not fit two DATA in same packet. + size_t mtu = RoundDownTo4(options_.mtu); + std::vector payload(mtu - 100); + + EXPECT_CALL(producer_, Produce) + .WillOnce([this, payload](TimeMs, size_t) { + return SendQueue::DataToSend(gen_.Ordered(payload, "B")); + }) + .WillOnce([this, payload](TimeMs, size_t) { + return SendQueue::DataToSend(gen_.Ordered(payload, "")); + }) + .WillOnce([this, payload](TimeMs, size_t) { + return SendQueue::DataToSend(gen_.Ordered(payload, "")); + }) + .WillOnce([this, payload](TimeMs, size_t) { + return SendQueue::DataToSend(gen_.Ordered(payload, "")); + }) + .WillOnce([this, payload](TimeMs, size_t) { + return SendQueue::DataToSend(gen_.Ordered(payload, "E")); + }) + .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; }); + + // Produce all chunks and put them in the retransmission queue. + std::vector> chunks_to_send = + queue.GetChunksToSend(now_, 5 * mtu); + EXPECT_THAT(chunks_to_send, + ElementsAre(Pair(TSN(10), _), Pair(TSN(11), _), Pair(TSN(12), _), + Pair(TSN(13), _), Pair(TSN(14), _))); + EXPECT_THAT(queue.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(9), State::kAcked), // + Pair(TSN(10), State::kInFlight), // + Pair(TSN(11), State::kInFlight), // + Pair(TSN(12), State::kInFlight), + Pair(TSN(13), State::kInFlight), + Pair(TSN(14), State::kInFlight))); + + // Ack 12, and report an empty receiver window (the peer obviously has a + // tiny receive window). + queue.HandleSack( + now_, SackChunk(TSN(9), /*rwnd=*/0, {SackChunk::GapAckBlock(3, 3)}, {})); + + // Force TSN 10 to be retransmitted. + queue.HandleT3RtxTimerExpiry(); + + // Even if the receiver window is empty, it will allow TSN 10 to be sent. + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), ElementsAre(Pair(TSN(10), _))); + + // But not more than that, as there now is outstanding data. + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), IsEmpty()); + + // Don't ack any new data, and still have receiver window zero. + queue.HandleSack( + now_, SackChunk(TSN(9), /*rwnd=*/0, {SackChunk::GapAckBlock(3, 3)}, {})); + + // There is in-flight data, so new data should not be allowed to be send since + // the receiver window is full. + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), IsEmpty()); + + // Ack that packet (no more in-flight data), but still report an empty + // receiver window. + queue.HandleSack( + now_, SackChunk(TSN(10), /*rwnd=*/0, {SackChunk::GapAckBlock(2, 2)}, {})); + + // Then TSN 11 can be sent, as there is no in-flight data. + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), ElementsAre(Pair(TSN(11), _))); + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), IsEmpty()); + + // Ack and recover the receiver window + queue.HandleSack(now_, SackChunk(TSN(12), /*rwnd=*/5 * mtu, {}, {})); + + // That will unblock sending remaining chunks. + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), ElementsAre(Pair(TSN(13), _))); + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), ElementsAre(Pair(TSN(14), _))); + EXPECT_THAT(queue.GetChunksToSend(now_, mtu), IsEmpty()); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_timeout.cc b/net/dcsctp/tx/retransmission_timeout.cc index 2cb59f13e1..aa2863f931 100644 --- a/net/dcsctp/tx/retransmission_timeout.cc +++ b/net/dcsctp/tx/retransmission_timeout.cc @@ -20,10 +20,11 @@ RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options) : min_rto_(*options.rto_min), max_rto_(*options.rto_max), max_rtt_(*options.rtt_max), + min_rtt_variance_(*options.min_rtt_variance), rto_(*options.rto_initial) {} void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) { - int32_t rtt = *measured_rtt; + const int32_t rtt = *measured_rtt; // Unrealistic values will be skipped. If a wrongly measured (or otherwise // corrupt) value was processed, it could change the state in a way that would @@ -40,20 +41,20 @@ void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) { scaled_rtt_var_ = (rtt / 2) << kRttVarShift; first_measurement_ = false; } else { - rtt -= (scaled_srtt_ >> kRttShift); - scaled_srtt_ += rtt; - if (rtt < 0) { - rtt = -rtt; + int32_t rtt_diff = rtt - (scaled_srtt_ >> kRttShift); + scaled_srtt_ += rtt_diff; + if (rtt_diff < 0) { + rtt_diff = -rtt_diff; } - rtt -= (scaled_rtt_var_ >> kRttVarShift); - scaled_rtt_var_ += rtt; + rtt_diff -= (scaled_rtt_var_ >> kRttVarShift); + scaled_rtt_var_ += rtt_diff; } - rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_; - // If the RTO becomes smaller or equal to RTT, expiration timers will be - // scheduled at the same time as packets are expected. Only happens in - // extremely stable RTTs, i.e. in simulations. - rto_ = std::max(rto_, rtt + 1); + if (scaled_rtt_var_ < min_rtt_variance_) { + scaled_rtt_var_ = min_rtt_variance_; + } + + rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_; // Clamp RTO between min and max. rto_ = std::min(std::max(rto_, min_rto_), max_rto_); diff --git a/net/dcsctp/tx/retransmission_timeout.h b/net/dcsctp/tx/retransmission_timeout.h index f3a95532d0..7cbcc6fcc9 100644 --- a/net/dcsctp/tx/retransmission_timeout.h +++ b/net/dcsctp/tx/retransmission_timeout.h @@ -44,6 +44,7 @@ class RetransmissionTimeout { const int32_t min_rto_; const int32_t max_rto_; const int32_t max_rtt_; + const int32_t min_rtt_variance_; // If this is the first measurement bool first_measurement_ = true; // Smoothed Round-Trip Time, shifted by kRttShift diff --git a/net/dcsctp/tx/retransmission_timeout_test.cc b/net/dcsctp/tx/retransmission_timeout_test.cc index d2d071948e..f3b20a86ba 100644 --- a/net/dcsctp/tx/retransmission_timeout_test.cc +++ b/net/dcsctp/tx/retransmission_timeout_test.cc @@ -20,6 +20,7 @@ constexpr DurationMs kMaxRtt = DurationMs(8'000); constexpr DurationMs kInitialRto = DurationMs(200); constexpr DurationMs kMaxRto = DurationMs(800); constexpr DurationMs kMinRto = DurationMs(120); +constexpr DurationMs kMinRttVariance = DurationMs(220); DcSctpOptions MakeOptions() { DcSctpOptions options; @@ -27,6 +28,7 @@ DcSctpOptions MakeOptions() { options.rto_initial = kInitialRto; options.rto_max = kMaxRto; options.rto_min = kMinRto; + options.min_rtt_variance = kMinRttVariance; return options; } @@ -82,13 +84,13 @@ TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) { rto_.ObserveRTT(DurationMs(124)); EXPECT_EQ(*rto_.rto(), 372); rto_.ObserveRTT(DurationMs(128)); - EXPECT_EQ(*rto_.rto(), 314); + EXPECT_EQ(*rto_.rto(), 344); rto_.ObserveRTT(DurationMs(123)); - EXPECT_EQ(*rto_.rto(), 268); + EXPECT_EQ(*rto_.rto(), 344); rto_.ObserveRTT(DurationMs(125)); - EXPECT_EQ(*rto_.rto(), 233); + EXPECT_EQ(*rto_.rto(), 344); rto_.ObserveRTT(DurationMs(127)); - EXPECT_EQ(*rto_.rto(), 209); + EXPECT_EQ(*rto_.rto(), 344); } TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) { @@ -130,7 +132,7 @@ TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) { rto_.ObserveRTT(DurationMs(124)); EXPECT_EQ(*rto_.rto(), 372); rto_.ObserveRTT(DurationMs(124)); - EXPECT_EQ(*rto_.rto(), 340); + EXPECT_EQ(*rto_.rto(), 367); } TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) { @@ -141,10 +143,32 @@ TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) { // any jitter will increase the RTO. RetransmissionTimeout rto_(MakeOptions()); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 1000; ++i) { rto_.ObserveRTT(DurationMs(124)); } - EXPECT_GT(*rto_.rto(), 124); + EXPECT_EQ(*rto_.rto(), 344); +} + +TEST(RetransmissionTimeoutTest, CanSpecifySmallerMinimumRttVariance) { + DcSctpOptions options = MakeOptions(); + options.min_rtt_variance = kMinRttVariance - DurationMs(100); + RetransmissionTimeout rto_(options); + + for (int i = 0; i < 1000; ++i) { + rto_.ObserveRTT(DurationMs(124)); + } + EXPECT_EQ(*rto_.rto(), 244); +} + +TEST(RetransmissionTimeoutTest, CanSpecifyLargerMinimumRttVariance) { + DcSctpOptions options = MakeOptions(); + options.min_rtt_variance = kMinRttVariance + DurationMs(100); + RetransmissionTimeout rto_(options); + + for (int i = 0; i < 1000; ++i) { + rto_.ObserveRTT(DurationMs(124)); + } + EXPECT_EQ(*rto_.rto(), 444); } } // namespace diff --git a/net/dcsctp/tx/rr_send_queue.cc b/net/dcsctp/tx/rr_send_queue.cc index 254214e554..f8f5ff2946 100644 --- a/net/dcsctp/tx/rr_send_queue.cc +++ b/net/dcsctp/tx/rr_send_queue.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,18 @@ namespace dcsctp { +RRSendQueue::RRSendQueue(absl::string_view log_prefix, + size_t buffer_size, + std::function on_buffered_amount_low, + size_t total_buffered_amount_low_threshold, + std::function on_total_buffered_amount_low) + : log_prefix_(std::string(log_prefix) + "fcfs: "), + buffer_size_(buffer_size), + on_buffered_amount_low_(std::move(on_buffered_amount_low)), + total_buffered_amount_(std::move(on_total_buffered_amount_low)) { + total_buffered_amount_.SetLowThreshold(total_buffered_amount_low_threshold); +} + bool RRSendQueue::OutgoingStream::HasDataToSend(TimeMs now) { while (!items_.empty()) { RRSendQueue::OutgoingStream::Item& item = items_.front(); @@ -36,7 +49,7 @@ bool RRSendQueue::OutgoingStream::HasDataToSend(TimeMs now) { } // Message has expired. Remove it and inspect the next one. - if (item.expires_at.has_value() && *item.expires_at <= now) { + if (item.expires_at <= now) { buffered_amount_.Decrease(item.remaining_size); total_buffered_amount_.Decrease(item.remaining_size); items_.pop_front(); @@ -53,10 +66,17 @@ bool RRSendQueue::OutgoingStream::HasDataToSend(TimeMs now) { return false; } +void RRSendQueue::OutgoingStream::AddHandoverState( + DcSctpSocketHandoverState::OutgoingStream& state) const { + state.next_ssn = next_ssn_.value(); + state.next_ordered_mid = next_ordered_mid_.value(); + state.next_unordered_mid = next_unordered_mid_.value(); +} + bool RRSendQueue::IsConsistent() const { size_t total_buffered_amount = 0; - for (const auto& stream_entry : streams_) { - total_buffered_amount += stream_entry.second.buffered_amount().value(); + for (const auto& [unused, stream] : streams_) { + total_buffered_amount += stream.buffered_amount().value(); } if (previous_message_has_ended_) { @@ -105,7 +125,7 @@ void RRSendQueue::ThresholdWatcher::SetLowThreshold(size_t low_threshold) { } void RRSendQueue::OutgoingStream::Add(DcSctpMessage message, - absl::optional expires_at, + TimeMs expires_at, const SendOptions& send_options) { buffered_amount_.Increase(message.payload().size()); total_buffered_amount_.Increase(message.payload().size()); @@ -166,7 +186,14 @@ absl::optional RRSendQueue::OutgoingStream::Produce( item->message_id.value(), fsn, ppid, std::move(payload), is_beginning, is_end, item->send_options.unordered)); - chunk.max_retransmissions = item->send_options.max_retransmissions; + if (item->send_options.max_retransmissions.has_value() && + *item->send_options.max_retransmissions >= + std::numeric_limits::min() && + *item->send_options.max_retransmissions <= + std::numeric_limits::max()) { + chunk.max_retransmissions = + MaxRetransmits(*item->send_options.max_retransmissions); + } chunk.expires_at = item->expires_at; if (is_end) { @@ -258,7 +285,7 @@ void RRSendQueue::Add(TimeMs now, RTC_DCHECK(!message.payload().empty()); // Any limited lifetime should start counting from now - when the message // has been added to the queue. - absl::optional expires_at = absl::nullopt; + TimeMs expires_at = TimeMs::InfiniteFuture(); if (send_options.lifetime.has_value()) { // `expires_at` is the time when it expires. Which is slightly larger than // the message's lifetime, as the message is alive during its entire @@ -363,9 +390,8 @@ void RRSendQueue::PrepareResetStreams(rtc::ArrayView streams) { bool RRSendQueue::CanResetStreams() const { // Streams can be reset if those streams that are paused don't have any // messages that are partially sent. - for (auto& stream : streams_) { - if (stream.second.is_paused() && - stream.second.has_partially_sent_message()) { + for (auto& [unused, stream] : streams_) { + if (stream.is_paused() && stream.has_partially_sent_message()) { return false; } } @@ -373,17 +399,17 @@ bool RRSendQueue::CanResetStreams() const { } void RRSendQueue::CommitResetStreams() { - for (auto& stream_entry : streams_) { - if (stream_entry.second.is_paused()) { - stream_entry.second.Reset(); + for (auto& [unused, stream] : streams_) { + if (stream.is_paused()) { + stream.Reset(); } } RTC_DCHECK(IsConsistent()); } void RRSendQueue::RollbackResetStreams() { - for (auto& stream_entry : streams_) { - stream_entry.second.Resume(); + for (auto& [unused, stream] : streams_) { + stream.Resume(); } RTC_DCHECK(IsConsistent()); } @@ -391,8 +417,7 @@ void RRSendQueue::RollbackResetStreams() { void RRSendQueue::Reset() { // Recalculate buffered amount, as partially sent messages may have been put // fully back in the queue. - for (auto& stream_entry : streams_) { - OutgoingStream& stream = stream_entry.second; + for (auto& [unused, stream] : streams_) { stream.Reset(); } previous_message_has_ended_ = true; @@ -433,4 +458,33 @@ RRSendQueue::OutgoingStream& RRSendQueue::GetOrCreateStreamInfo( total_buffered_amount_)) .first->second; } + +HandoverReadinessStatus RRSendQueue::GetHandoverReadiness() const { + HandoverReadinessStatus status; + if (!IsEmpty()) { + status.Add(HandoverUnreadinessReason::kSendQueueNotEmpty); + } + return status; +} + +void RRSendQueue::AddHandoverState(DcSctpSocketHandoverState& state) { + for (const auto& [stream_id, stream] : streams_) { + DcSctpSocketHandoverState::OutgoingStream state_stream; + state_stream.id = stream_id.value(); + stream.AddHandoverState(state_stream); + state.tx.streams.push_back(std::move(state_stream)); + } +} + +void RRSendQueue::RestoreFromState(const DcSctpSocketHandoverState& state) { + for (const DcSctpSocketHandoverState::OutgoingStream& state_stream : + state.tx.streams) { + StreamID stream_id(state_stream.id); + streams_.emplace(stream_id, OutgoingStream( + [this, stream_id]() { + on_buffered_amount_low_(stream_id); + }, + total_buffered_amount_, &state_stream)); + } +} } // namespace dcsctp diff --git a/net/dcsctp/tx/rr_send_queue.h b/net/dcsctp/tx/rr_send_queue.h index ed077cdef7..fecb6e0f2e 100644 --- a/net/dcsctp/tx/rr_send_queue.h +++ b/net/dcsctp/tx/rr_send_queue.h @@ -47,13 +47,7 @@ class RRSendQueue : public SendQueue { size_t buffer_size, std::function on_buffered_amount_low, size_t total_buffered_amount_low_threshold, - std::function on_total_buffered_amount_low) - : log_prefix_(std::string(log_prefix) + "fcfs: "), - buffer_size_(buffer_size), - on_buffered_amount_low_(std::move(on_buffered_amount_low)), - total_buffered_amount_(std::move(on_total_buffered_amount_low)) { - total_buffered_amount_.SetLowThreshold(total_buffered_amount_low_threshold); - } + std::function on_total_buffered_amount_low); // Indicates if the buffer is full. Note that it's up to the caller to ensure // that the buffer is not full prior to adding new items to it. @@ -86,6 +80,10 @@ class RRSendQueue : public SendQueue { size_t buffered_amount_low_threshold(StreamID stream_id) const override; void SetBufferedAmountLowThreshold(StreamID stream_id, size_t bytes) override; + HandoverReadinessStatus GetHandoverReadiness() const; + void AddHandoverState(DcSctpSocketHandoverState& state); + void RestoreFromState(const DcSctpSocketHandoverState& state); + private: // Represents a value and a "low threshold" that when the value reaches or // goes under the "low threshold", will trigger `on_threshold_reached` @@ -113,14 +111,19 @@ class RRSendQueue : public SendQueue { // Per-stream information. class OutgoingStream { public: - explicit OutgoingStream(std::function on_buffered_amount_low, - ThresholdWatcher& total_buffered_amount) - : buffered_amount_(std::move(on_buffered_amount_low)), + explicit OutgoingStream( + std::function on_buffered_amount_low, + ThresholdWatcher& total_buffered_amount, + const DcSctpSocketHandoverState::OutgoingStream* state = nullptr) + : next_unordered_mid_(MID(state ? state->next_unordered_mid : 0)), + next_ordered_mid_(MID(state ? state->next_ordered_mid : 0)), + next_ssn_(SSN(state ? state->next_ssn : 0)), + buffered_amount_(std::move(on_buffered_amount_low)), total_buffered_amount_(total_buffered_amount) {} // Enqueues a message to this stream. void Add(DcSctpMessage message, - absl::optional expires_at, + TimeMs expires_at, const SendOptions& send_options); // Possibly produces a data chunk to send. @@ -150,11 +153,14 @@ class RRSendQueue : public SendQueue { // expired non-partially sent message. bool HasDataToSend(TimeMs now); + void AddHandoverState( + DcSctpSocketHandoverState::OutgoingStream& state) const; + private: // An enqueued message and metadata. struct Item { explicit Item(DcSctpMessage msg, - absl::optional expires_at, + TimeMs expires_at, const SendOptions& send_options) : message(std::move(msg)), expires_at(expires_at), @@ -162,7 +168,7 @@ class RRSendQueue : public SendQueue { remaining_offset(0), remaining_size(message.payload().size()) {} DcSctpMessage message; - absl::optional expires_at; + TimeMs expires_at; SendOptions send_options; // The remaining payload (offset and size) to be sent, when it has been // fragmented. @@ -181,10 +187,10 @@ class RRSendQueue : public SendQueue { // Streams are pause when they are about to be reset. bool is_paused_ = false; // MIDs are different for unordered and ordered messages sent on a stream. - MID next_unordered_mid_ = MID(0); - MID next_ordered_mid_ = MID(0); + MID next_unordered_mid_; + MID next_ordered_mid_; - SSN next_ssn_ = SSN(0); + SSN next_ssn_; // Enqueued messages, and metadata. std::deque items_; diff --git a/net/dcsctp/tx/send_queue.h b/net/dcsctp/tx/send_queue.h index 877dbdda59..a821d20785 100644 --- a/net/dcsctp/tx/send_queue.h +++ b/net/dcsctp/tx/send_queue.h @@ -32,8 +32,8 @@ class SendQueue { Data data; // Partial reliability - RFC3758 - absl::optional max_retransmissions; - absl::optional expires_at; + MaxRetransmits max_retransmissions = MaxRetransmits::NoLimit(); + TimeMs expires_at = TimeMs::InfiniteFuture(); }; virtual ~SendQueue() = default; diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index 866c37c664..f02d7457db 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -94,10 +94,13 @@ rtc_library("rtc_p2p") { "../api:rtc_error", "../api:scoped_refptr", "../api:sequence_checker", + "../api:webrtc_key_value_config", + "../api:wrapping_async_dns_resolver", "../api/crypto:options", "../api/rtc_event_log", "../api/task_queue", "../api/transport:enums", + "../api/transport:field_trial_based_config", "../api/transport:stun_types", "../logging:ice_log", "../rtc_base", @@ -110,8 +113,10 @@ rtc_library("rtc_p2p") { "../rtc_base:rtc_numerics", "../rtc_base:socket", "../rtc_base:socket_address", + "../rtc_base:socket_factory", "../rtc_base:socket_server", "../rtc_base:threading", + "../rtc_base/containers:flat_map", "../rtc_base/experiments:field_trial_parser", "../rtc_base/system:no_unique_address", @@ -240,6 +245,7 @@ if (rtc_include_tests) { "../api:mock_async_dns_resolver", "../api:packet_socket_factory", "../api:scoped_refptr", + "../api:webrtc_key_value_config", "../api/transport:stun_types", "../api/units:time_delta", "../rtc_base", @@ -259,6 +265,7 @@ if (rtc_include_tests) { "../system_wrappers:metrics", "../test:field_trial", "../test:rtc_expect_death", + "../test:scoped_key_value_config", "../test:test_support", "//testing/gtest", ] diff --git a/p2p/base/async_stun_tcp_socket.cc b/p2p/base/async_stun_tcp_socket.cc index f4ead6696c..5f8f07227f 100644 --- a/p2p/base/async_stun_tcp_socket.cc +++ b/p2p/base/async_stun_tcp_socket.cc @@ -45,12 +45,11 @@ AsyncStunTCPSocket* AsyncStunTCPSocket::Create( const rtc::SocketAddress& bind_address, const rtc::SocketAddress& remote_address) { return new AsyncStunTCPSocket( - AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address), - false); + AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address)); } -AsyncStunTCPSocket::AsyncStunTCPSocket(rtc::Socket* socket, bool listen) - : rtc::AsyncTCPSocketBase(socket, listen, kBufSize) {} +AsyncStunTCPSocket::AsyncStunTCPSocket(rtc::Socket* socket) + : rtc::AsyncTCPSocketBase(socket, kBufSize) {} int AsyncStunTCPSocket::Send(const void* pv, size_t cb, @@ -126,10 +125,6 @@ void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) { } } -void AsyncStunTCPSocket::HandleIncomingConnection(rtc::Socket* socket) { - SignalNewConnection(this, new AsyncStunTCPSocket(socket, false)); -} - size_t AsyncStunTCPSocket::GetExpectedLength(const void* data, size_t len, int* pad_bytes) { diff --git a/p2p/base/async_stun_tcp_socket.h b/p2p/base/async_stun_tcp_socket.h index 2b7bc95627..f0df42b52a 100644 --- a/p2p/base/async_stun_tcp_socket.h +++ b/p2p/base/async_stun_tcp_socket.h @@ -15,7 +15,6 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_tcp_socket.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" @@ -30,21 +29,21 @@ class AsyncStunTCPSocket : public rtc::AsyncTCPSocketBase { const rtc::SocketAddress& bind_address, const rtc::SocketAddress& remote_address); - AsyncStunTCPSocket(rtc::Socket* socket, bool listen); + explicit AsyncStunTCPSocket(rtc::Socket* socket); + + AsyncStunTCPSocket(const AsyncStunTCPSocket&) = delete; + AsyncStunTCPSocket& operator=(const AsyncStunTCPSocket&) = delete; int Send(const void* pv, size_t cb, const rtc::PacketOptions& options) override; void ProcessInput(char* data, size_t* len) override; - void HandleIncomingConnection(rtc::Socket* socket) override; private: // This method returns the message hdr + length written in the header. // This method also returns the number of padding bytes needed/added to the // turn message. `pad_bytes` should be used only when `is_turn` is true. size_t GetExpectedLength(const void* data, size_t len, int* pad_bytes); - - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncStunTCPSocket); }; } // namespace cricket diff --git a/p2p/base/async_stun_tcp_socket_unittest.cc b/p2p/base/async_stun_tcp_socket_unittest.cc index 40b5405750..72d6a7fde0 100644 --- a/p2p/base/async_stun_tcp_socket_unittest.cc +++ b/p2p/base/async_stun_tcp_socket_unittest.cc @@ -16,7 +16,9 @@ #include #include #include +#include +#include "absl/memory/memory.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -58,6 +60,15 @@ static unsigned char kTurnChannelDataMessageWithOddLength[] = { static const rtc::SocketAddress kClientAddr("11.11.11.11", 0); static const rtc::SocketAddress kServerAddr("22.22.22.22", 0); +class AsyncStunServerTCPSocket : public rtc::AsyncTcpListenSocket { + public: + explicit AsyncStunServerTCPSocket(std::unique_ptr socket) + : AsyncTcpListenSocket(std::move(socket)) {} + void HandleIncomingConnection(rtc::Socket* socket) override { + SignalNewConnection(this, new AsyncStunTCPSocket(socket)); + } +}; + class AsyncStunTCPSocketTest : public ::testing::Test, public sigslot::has_slots<> { protected: @@ -67,15 +78,17 @@ class AsyncStunTCPSocketTest : public ::testing::Test, virtual void SetUp() { CreateSockets(); } void CreateSockets() { - rtc::Socket* server = vss_->CreateSocket(kServerAddr.family(), SOCK_STREAM); + std::unique_ptr server = + absl::WrapUnique(vss_->CreateSocket(kServerAddr.family(), SOCK_STREAM)); server->Bind(kServerAddr); - recv_socket_.reset(new AsyncStunTCPSocket(server, true)); - recv_socket_->SignalNewConnection.connect( + listen_socket_ = + std::make_unique(std::move(server)); + listen_socket_->SignalNewConnection.connect( this, &AsyncStunTCPSocketTest::OnNewConnection); rtc::Socket* client = vss_->CreateSocket(kClientAddr.family(), SOCK_STREAM); send_socket_.reset(AsyncStunTCPSocket::Create( - client, kClientAddr, recv_socket_->GetLocalAddress())); + client, kClientAddr, listen_socket_->GetLocalAddress())); send_socket_->SignalSentPacket.connect( this, &AsyncStunTCPSocketTest::OnSentPacket); ASSERT_TRUE(send_socket_.get() != NULL); @@ -95,9 +108,9 @@ class AsyncStunTCPSocketTest : public ::testing::Test, ++sent_packets_; } - void OnNewConnection(rtc::AsyncPacketSocket* server, + void OnNewConnection(rtc::AsyncListenSocket* /*server*/, rtc::AsyncPacketSocket* new_socket) { - listen_socket_.reset(new_socket); + recv_socket_ = absl::WrapUnique(new_socket); new_socket->SignalReadPacket.connect(this, &AsyncStunTCPSocketTest::OnReadPacket); } @@ -123,8 +136,8 @@ class AsyncStunTCPSocketTest : public ::testing::Test, std::unique_ptr vss_; rtc::AutoSocketServerThread thread_; std::unique_ptr send_socket_; - std::unique_ptr recv_socket_; - std::unique_ptr listen_socket_; + std::unique_ptr listen_socket_; + std::unique_ptr recv_socket_; std::list recv_packets_; int sent_packets_ = 0; }; diff --git a/p2p/base/basic_async_resolver_factory.cc b/p2p/base/basic_async_resolver_factory.cc index 7f26a981ee..6824357821 100644 --- a/p2p/base/basic_async_resolver_factory.cc +++ b/p2p/base/basic_async_resolver_factory.cc @@ -15,6 +15,7 @@ #include "absl/memory/memory.h" #include "api/async_dns_resolver.h" +#include "api/wrapping_async_dns_resolver.h" #include "rtc_base/async_resolver.h" #include "rtc_base/logging.h" @@ -24,100 +25,6 @@ rtc::AsyncResolverInterface* BasicAsyncResolverFactory::Create() { return new rtc::AsyncResolver(); } -class WrappingAsyncDnsResolver; - -class WrappingAsyncDnsResolverResult : public AsyncDnsResolverResult { - public: - explicit WrappingAsyncDnsResolverResult(WrappingAsyncDnsResolver* owner) - : owner_(owner) {} - ~WrappingAsyncDnsResolverResult() {} - - // Note: Inline declaration not possible, since it refers to - // WrappingAsyncDnsResolver. - bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override; - int GetError() const override; - - private: - WrappingAsyncDnsResolver* const owner_; -}; - -class WrappingAsyncDnsResolver : public AsyncDnsResolverInterface, - public sigslot::has_slots<> { - public: - explicit WrappingAsyncDnsResolver(rtc::AsyncResolverInterface* wrapped) - : wrapped_(absl::WrapUnique(wrapped)), result_(this) {} - - ~WrappingAsyncDnsResolver() override { - // Workaround to get around the fact that sigslot-using objects can't be - // destroyed from within their callback: Alert class users early. - // TODO(bugs.webrtc.org/12651): Delete this class once the sigslot users are - // gone. - RTC_CHECK(!within_resolve_result_); - wrapped_.release()->Destroy(false); - } - - void Start(const rtc::SocketAddress& addr, - std::function callback) override { - RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_DCHECK_EQ(State::kNotStarted, state_); - state_ = State::kStarted; - callback_ = callback; - wrapped_->SignalDone.connect(this, - &WrappingAsyncDnsResolver::OnResolveResult); - wrapped_->Start(addr); - } - - const AsyncDnsResolverResult& result() const override { - RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_DCHECK_EQ(State::kResolved, state_); - return result_; - } - - private: - enum class State { kNotStarted, kStarted, kResolved }; - - friend class WrappingAsyncDnsResolverResult; - // For use by WrappingAsyncDnsResolverResult - rtc::AsyncResolverInterface* wrapped() const { - RTC_DCHECK_RUN_ON(&sequence_checker_); - return wrapped_.get(); - } - - void OnResolveResult(rtc::AsyncResolverInterface* ref) { - RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_DCHECK(state_ == State::kStarted); - RTC_DCHECK_EQ(ref, wrapped_.get()); - state_ = State::kResolved; - within_resolve_result_ = true; - callback_(); - within_resolve_result_ = false; - } - - // The class variables need to be accessed on a single thread. - SequenceChecker sequence_checker_; - std::function callback_ RTC_GUARDED_BY(sequence_checker_); - std::unique_ptr wrapped_ - RTC_GUARDED_BY(sequence_checker_); - State state_ RTC_GUARDED_BY(sequence_checker_) = State::kNotStarted; - WrappingAsyncDnsResolverResult result_ RTC_GUARDED_BY(sequence_checker_); - bool within_resolve_result_ RTC_GUARDED_BY(sequence_checker_) = false; -}; - -bool WrappingAsyncDnsResolverResult::GetResolvedAddress( - int family, - rtc::SocketAddress* addr) const { - if (!owner_->wrapped()) { - return false; - } - return owner_->wrapped()->GetResolvedAddress(family, addr); -} - -int WrappingAsyncDnsResolverResult::GetError() const { - if (!owner_->wrapped()) { - return -1; // FIXME: Find a code that makes sense. - } - return owner_->wrapped()->GetError(); -} std::unique_ptr WrappingAsyncDnsResolverFactory::Create() { diff --git a/p2p/base/basic_ice_controller.cc b/p2p/base/basic_ice_controller.cc index ac22e1d941..9025fbe2a7 100644 --- a/p2p/base/basic_ice_controller.cc +++ b/p2p/base/basic_ice_controller.cc @@ -83,6 +83,8 @@ void BasicIceController::OnConnectionDestroyed(const Connection* connection) { pinged_connections_.erase(connection); unpinged_connections_.erase(connection); connections_.erase(absl::c_find(connections_, connection)); + if (selected_connection_ == connection) + selected_connection_ = nullptr; } bool BasicIceController::HasPingableConnection() const { @@ -846,7 +848,7 @@ bool BasicIceController::GetUseCandidateAttr(const Connection* conn, return selected || better_than_selected; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } } diff --git a/p2p/base/basic_packet_socket_factory.cc b/p2p/base/basic_packet_socket_factory.cc index 3685dfac21..cdfc1e4327 100644 --- a/p2p/base/basic_packet_socket_factory.cc +++ b/p2p/base/basic_packet_socket_factory.cc @@ -14,27 +14,23 @@ #include +#include "absl/memory/memory.h" +#include "api/async_dns_resolver.h" +#include "api/wrapping_async_dns_resolver.h" #include "p2p/base/async_stun_tcp_socket.h" -#include "rtc_base/async_resolver.h" #include "rtc_base/async_tcp_socket.h" #include "rtc_base/async_udp_socket.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "rtc_base/net_helpers.h" #include "rtc_base/socket.h" #include "rtc_base/socket_adapters.h" -#include "rtc_base/socket_server.h" #include "rtc_base/ssl_adapter.h" -#include "rtc_base/thread.h" namespace rtc { -BasicPacketSocketFactory::BasicPacketSocketFactory(Thread* thread) - : thread_(thread), socket_factory_(NULL) {} - BasicPacketSocketFactory::BasicPacketSocketFactory( SocketFactory* socket_factory) - : thread_(NULL), socket_factory_(socket_factory) {} + : socket_factory_(socket_factory) {} BasicPacketSocketFactory::~BasicPacketSocketFactory() {} @@ -43,7 +39,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket( uint16_t min_port, uint16_t max_port) { // UDP sockets are simple. - Socket* socket = socket_factory()->CreateSocket(address.family(), SOCK_DGRAM); + Socket* socket = socket_factory_->CreateSocket(address.family(), SOCK_DGRAM); if (!socket) { return NULL; } @@ -55,7 +51,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket( return new AsyncUDPSocket(socket); } -AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket( +AsyncListenSocket* BasicPacketSocketFactory::CreateServerTcpSocket( const SocketAddress& local_address, uint16_t min_port, uint16_t max_port, @@ -66,8 +62,12 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket( return NULL; } + if (opts & PacketSocketFactory::OPT_TLS_FAKE) { + RTC_LOG(LS_ERROR) << "Fake TLS not supported."; + return NULL; + } Socket* socket = - socket_factory()->CreateSocket(local_address.family(), SOCK_STREAM); + socket_factory_->CreateSocket(local_address.family(), SOCK_STREAM); if (!socket) { return NULL; } @@ -78,24 +78,9 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket( return NULL; } - // Set TCP_NODELAY (via OPT_NODELAY) for improved performance; this causes - // small media packets to be sent immediately rather than being buffered up, - // reducing latency. - if (socket->SetOption(Socket::OPT_NODELAY, 1) != 0) { - RTC_LOG(LS_INFO) << "Setting TCP_NODELAY option failed with error " - << socket->GetError(); - } + RTC_CHECK(!(opts & PacketSocketFactory::OPT_STUN)); - // If using fake TLS, wrap the TCP socket in a pseudo-SSL socket. - if (opts & PacketSocketFactory::OPT_TLS_FAKE) { - RTC_DCHECK(!(opts & PacketSocketFactory::OPT_TLS)); - socket = new AsyncSSLSocket(socket); - } - - if (opts & PacketSocketFactory::OPT_STUN) - return new cricket::AsyncStunTCPSocket(socket, true); - - return new AsyncTCPSocket(socket, true); + return new AsyncTcpListenSocket(absl::WrapUnique(socket)); } AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( @@ -105,7 +90,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( const std::string& user_agent, const PacketSocketTcpOptions& tcp_options) { Socket* socket = - socket_factory()->CreateSocket(local_address.family(), SOCK_STREAM); + socket_factory_->CreateSocket(local_address.family(), SOCK_STREAM); if (!socket) { return NULL; } @@ -187,9 +172,9 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( // Finally, wrap that socket in a TCP or STUN TCP packet socket. AsyncPacketSocket* tcp_socket; if (tcp_options.opts & PacketSocketFactory::OPT_STUN) { - tcp_socket = new cricket::AsyncStunTCPSocket(socket, false); + tcp_socket = new cricket::AsyncStunTCPSocket(socket); } else { - tcp_socket = new AsyncTCPSocket(socket, false); + tcp_socket = new AsyncTCPSocket(socket); } return tcp_socket; @@ -199,6 +184,12 @@ AsyncResolverInterface* BasicPacketSocketFactory::CreateAsyncResolver() { return new AsyncResolver(); } +std::unique_ptr +BasicPacketSocketFactory::CreateAsyncDnsResolver() { + return std::make_unique( + new AsyncResolver()); +} + int BasicPacketSocketFactory::BindSocket(Socket* socket, const SocketAddress& local_address, uint16_t min_port, @@ -216,13 +207,4 @@ int BasicPacketSocketFactory::BindSocket(Socket* socket, return ret; } -SocketFactory* BasicPacketSocketFactory::socket_factory() { - if (thread_) { - RTC_DCHECK(thread_ == Thread::Current()); - return thread_->socketserver(); - } else { - return socket_factory_; - } -} - } // namespace rtc diff --git a/p2p/base/basic_packet_socket_factory.h b/p2p/base/basic_packet_socket_factory.h index 368c976a9c..f4282e1ad0 100644 --- a/p2p/base/basic_packet_socket_factory.h +++ b/p2p/base/basic_packet_socket_factory.h @@ -11,26 +11,32 @@ #ifndef P2P_BASE_BASIC_PACKET_SOCKET_FACTORY_H_ #define P2P_BASE_BASIC_PACKET_SOCKET_FACTORY_H_ +#include + +#include #include +#include "api/async_dns_resolver.h" #include "api/packet_socket_factory.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/proxy_info.h" #include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" namespace rtc { class SocketFactory; -class Thread; class BasicPacketSocketFactory : public PacketSocketFactory { public: - explicit BasicPacketSocketFactory(Thread* thread); explicit BasicPacketSocketFactory(SocketFactory* socket_factory); ~BasicPacketSocketFactory() override; AsyncPacketSocket* CreateUdpSocket(const SocketAddress& local_address, uint16_t min_port, uint16_t max_port) override; - AsyncPacketSocket* CreateServerTcpSocket(const SocketAddress& local_address, + AsyncListenSocket* CreateServerTcpSocket(const SocketAddress& local_address, uint16_t min_port, uint16_t max_port, int opts) override; @@ -41,17 +47,19 @@ class BasicPacketSocketFactory : public PacketSocketFactory { const std::string& user_agent, const PacketSocketTcpOptions& tcp_options) override; + // TODO(bugs.webrtc.org/12598) Remove when downstream stops using it. + ABSL_DEPRECATED("Use CreateAsyncDnsResolver") AsyncResolverInterface* CreateAsyncResolver() override; + std::unique_ptr CreateAsyncDnsResolver() + override; + private: int BindSocket(Socket* socket, const SocketAddress& local_address, uint16_t min_port, uint16_t max_port); - SocketFactory* socket_factory(); - - Thread* thread_; SocketFactory* socket_factory_; }; diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc index 0b1b7005fa..0a2983c69e 100644 --- a/p2p/base/connection.cc +++ b/p2p/base/connection.cc @@ -18,6 +18,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "p2p/base/port_allocator.h" #include "rtc_base/checks.h" @@ -32,7 +33,6 @@ #include "rtc_base/string_utils.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/third_party/base64/base64.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -167,6 +167,7 @@ ConnectionRequest::ConnectionRequest(Connection* connection) : StunRequest(new IceMessage()), connection_(connection) {} void ConnectionRequest::Prepare(StunMessage* request) { + RTC_DCHECK_RUN_ON(connection_->network_thread_); request->SetType(STUN_BINDING_REQUEST); std::string username; connection_->port()->CreateStunUsername( @@ -189,8 +190,7 @@ void ConnectionRequest::Prepare(StunMessage* request) { request->AddAttribute(std::make_unique( STUN_ATTR_GOOG_NETWORK_INFO, network_info)); - if (webrtc::field_trial::IsEnabled( - "WebRTC-PiggybackIceCheckAcknowledgement") && + if (connection_->field_trials_->piggyback_ice_check_acknowledgement && connection_->last_ping_id_received()) { request->AddAttribute(std::make_unique( STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED, @@ -207,16 +207,16 @@ void ConnectionRequest::Prepare(StunMessage* request) { request->AddAttribute( std::make_unique(STUN_ATTR_USE_CANDIDATE)); } - if (connection_->nomination() && - connection_->nomination() != connection_->acked_nomination()) { + if (connection_->nomination_ && + connection_->nomination_ != connection_->acked_nomination()) { request->AddAttribute(std::make_unique( - STUN_ATTR_NOMINATION, connection_->nomination())); + STUN_ATTR_NOMINATION, connection_->nomination_)); } } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) { request->AddAttribute(std::make_unique( STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker())); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // Adding PRIORITY Attribute. @@ -257,18 +257,22 @@ void ConnectionRequest::Prepare(StunMessage* request) { } void ConnectionRequest::OnResponse(StunMessage* response) { + RTC_DCHECK_RUN_ON(connection_->network_thread_); connection_->OnConnectionRequestResponse(this, response); } void ConnectionRequest::OnErrorResponse(StunMessage* response) { + RTC_DCHECK_RUN_ON(connection_->network_thread_); connection_->OnConnectionRequestErrorResponse(this, response); } void ConnectionRequest::OnTimeout() { + RTC_DCHECK_RUN_ON(connection_->network_thread_); connection_->OnConnectionRequestTimeout(this); } void ConnectionRequest::OnSent() { + RTC_DCHECK_RUN_ON(connection_->network_thread_); connection_->OnConnectionRequestSent(this); // Each request is sent only once. After a single delay , the request will // time out. @@ -282,7 +286,8 @@ int ConnectionRequest::resend_delay() { Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate) - : id_(rtc::CreateRandomId()), + : network_thread_(port->thread()), + id_(rtc::CreateRandomId()), port_(port), local_candidate_index_(index), remote_candidate_(remote_candidate), @@ -293,7 +298,6 @@ Connection::Connection(Port* port, connected_(true), pruned_(false), use_candidate_attr_(false), - remote_ice_mode_(ICEMODE_FULL), requests_(port->thread()), rtt_(DEFAULT_RTT), last_ping_sent_(0), @@ -305,6 +309,7 @@ Connection::Connection(Port* port, time_created_ms_(rtc::TimeMillis()), field_trials_(&kDefaultFieldTrials), rtt_estimate_(DEFAULT_RTT_ESTIMATE_HALF_TIME_MS) { + RTC_DCHECK_RUN_ON(network_thread_); // All of our connections start in WAITING state. // TODO(mallinath) - Start connections from STATE_FROZEN. // Wire up to send stun packets @@ -312,9 +317,16 @@ Connection::Connection(Port* port, RTC_LOG(LS_INFO) << ToString() << ": Connection created"; } -Connection::~Connection() {} +Connection::~Connection() { + RTC_DCHECK_RUN_ON(network_thread_); +} + +webrtc::TaskQueueBase* Connection::network_thread() const { + return network_thread_; +} const Candidate& Connection::local_candidate() const { + RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(local_candidate_index_ < port_->Candidates().size()); return port_->Candidates()[local_candidate_index_]; } @@ -357,6 +369,7 @@ uint64_t Connection::priority() const { } void Connection::set_write_state(WriteState value) { + RTC_DCHECK_RUN_ON(network_thread_); WriteState old_value = write_state_; write_state_ = value; if (value != old_value) { @@ -367,6 +380,7 @@ void Connection::set_write_state(WriteState value) { } void Connection::UpdateReceiving(int64_t now) { + RTC_DCHECK_RUN_ON(network_thread_); bool receiving; if (last_ping_sent() < last_ping_response_received()) { // We consider any candidate pair that has its last connectivity check @@ -392,6 +406,7 @@ void Connection::UpdateReceiving(int64_t now) { } void Connection::set_state(IceCandidatePairState state) { + RTC_DCHECK_RUN_ON(network_thread_); IceCandidatePairState old_state = state_; state_ = state; if (state != old_state) { @@ -400,6 +415,7 @@ void Connection::set_state(IceCandidatePairState state) { } void Connection::set_connected(bool value) { + RTC_DCHECK_RUN_ON(network_thread_); bool old_value = connected_; connected_ = value; if (value != old_value) { @@ -408,27 +424,74 @@ void Connection::set_connected(bool value) { } } +bool Connection::use_candidate_attr() const { + RTC_DCHECK_RUN_ON(network_thread_); + return use_candidate_attr_; +} + void Connection::set_use_candidate_attr(bool enable) { + RTC_DCHECK_RUN_ON(network_thread_); use_candidate_attr_ = enable; } +void Connection::set_nomination(uint32_t value) { + RTC_DCHECK_RUN_ON(network_thread_); + nomination_ = value; +} + +uint32_t Connection::remote_nomination() const { + RTC_DCHECK_RUN_ON(network_thread_); + return remote_nomination_; +} + +bool Connection::nominated() const { + RTC_DCHECK_RUN_ON(network_thread_); + return acked_nomination_ || remote_nomination_; +} + int Connection::unwritable_timeout() const { + RTC_DCHECK_RUN_ON(network_thread_); return unwritable_timeout_.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT); } +void Connection::set_unwritable_timeout(const absl::optional& value_ms) { + RTC_DCHECK_RUN_ON(network_thread_); + unwritable_timeout_ = value_ms; +} + int Connection::unwritable_min_checks() const { + RTC_DCHECK_RUN_ON(network_thread_); return unwritable_min_checks_.value_or(CONNECTION_WRITE_CONNECT_FAILURES); } +void Connection::set_unwritable_min_checks(const absl::optional& value) { + RTC_DCHECK_RUN_ON(network_thread_); + unwritable_min_checks_ = value; +} + int Connection::inactive_timeout() const { + RTC_DCHECK_RUN_ON(network_thread_); return inactive_timeout_.value_or(CONNECTION_WRITE_TIMEOUT); } +void Connection::set_inactive_timeout(const absl::optional& value) { + RTC_DCHECK_RUN_ON(network_thread_); + inactive_timeout_ = value; +} + int Connection::receiving_timeout() const { + RTC_DCHECK_RUN_ON(network_thread_); return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT); } +void Connection::set_receiving_timeout( + absl::optional receiving_timeout_ms) { + RTC_DCHECK_RUN_ON(network_thread_); + receiving_timeout_ = receiving_timeout_ms; +} + void Connection::SetIceFieldTrials(const IceFieldTrials* field_trials) { + RTC_DCHECK_RUN_ON(network_thread_); field_trials_ = field_trials; rtt_estimate_.SetHalfTime(field_trials->rtt_estimate_halftime_ms); } @@ -436,6 +499,7 @@ void Connection::SetIceFieldTrials(const IceFieldTrials* field_trials) { void Connection::OnSendStunPacket(const void* data, size_t size, StunRequest* req) { + RTC_DCHECK_RUN_ON(network_thread_); rtc::PacketOptions options(port_->StunDscpValue()); options.info_signaled_after_sent.packet_type = rtc::PacketType::kIceConnectivityCheck; @@ -452,6 +516,7 @@ void Connection::OnSendStunPacket(const void* data, void Connection::OnReadPacket(const char* data, size_t size, int64_t packet_time_us) { + RTC_DCHECK_RUN_ON(network_thread_); std::unique_ptr msg; std::string remote_ufrag; const rtc::SocketAddress& addr(remote_candidate_.address()); @@ -528,17 +593,17 @@ void Connection::OnReadPacket(const char* data, } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } } void Connection::HandleStunBindingOrGoogPingRequest(IceMessage* msg) { + RTC_DCHECK_RUN_ON(network_thread_); // This connection should now be receiving. ReceivedPing(msg->transaction_id()); - if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") && - last_ping_response_received_ == 0) { + if (field_trials_->extra_ice_ping && last_ping_response_received_ == 0) { if (local_candidate().type() == RELAY_PORT_TYPE || local_candidate().type() == PRFLX_PORT_TYPE || remote_candidate().type() == RELAY_PORT_TYPE || @@ -627,13 +692,13 @@ void Connection::HandleStunBindingOrGoogPingRequest(IceMessage* msg) { } } - if (webrtc::field_trial::IsEnabled( - "WebRTC-PiggybackIceCheckAcknowledgement")) { + if (field_trials_->piggyback_ice_check_acknowledgement) { HandlePiggybackCheckAcknowledgementIfAny(msg); } } void Connection::SendStunBindingResponse(const StunMessage* request) { + RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(request->type() == STUN_BINDING_REQUEST); // Retrieve the username from the request. @@ -690,6 +755,7 @@ void Connection::SendStunBindingResponse(const StunMessage* request) { } void Connection::SendGoogPingResponse(const StunMessage* request) { + RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(request->type() == GOOG_PING_REQUEST); // Fill in the response message. @@ -701,6 +767,7 @@ void Connection::SendGoogPingResponse(const StunMessage* request) { } void Connection::SendResponseMessage(const StunMessage& response) { + RTC_DCHECK_RUN_ON(network_thread_); // Where I send the response. const rtc::SocketAddress& addr = remote_candidate_.address(); @@ -731,11 +798,28 @@ void Connection::SendResponseMessage(const StunMessage& response) { } } +uint32_t Connection::acked_nomination() const { + RTC_DCHECK_RUN_ON(network_thread_); + return acked_nomination_; +} + +void Connection::set_remote_nomination(uint32_t remote_nomination) { + RTC_DCHECK_RUN_ON(network_thread_); + remote_nomination_ = remote_nomination; +} + void Connection::OnReadyToSend() { + RTC_DCHECK_RUN_ON(network_thread_); SignalReadyToSend(this); } +bool Connection::pruned() const { + RTC_DCHECK_RUN_ON(network_thread_); + return pruned_; +} + void Connection::Prune() { + RTC_DCHECK_RUN_ON(network_thread_); if (!pruned_ || active()) { RTC_LOG(LS_INFO) << ToString() << ": Connection pruned"; pruned_ = true; @@ -745,27 +829,47 @@ void Connection::Prune() { } void Connection::Destroy() { - // TODO(deadbeef, nisse): This may leak if an application closes a - // PeerConnection and then quickly destroys the PeerConnectionFactory (along - // with the networking thread on which this message is posted). Also affects - // tests, with a workaround in - // AutoSocketServerThread::~AutoSocketServerThread. - RTC_LOG(LS_VERBOSE) << ToString() << ": Connection destroyed"; - port_->thread()->Post(RTC_FROM_HERE, this, MSG_DELETE); + RTC_DCHECK_RUN_ON(network_thread_); + if (pending_delete_) + return; + + pending_delete_ = true; + + RTC_DLOG(LS_VERBOSE) << ToString() << ": Connection destroyed"; + + // Fire the 'destroyed' event before deleting the object. This is done + // intentionally to avoid a situation whereby the signal might have dangling + // pointers to objects that have been deleted by the time the async task + // that deletes the connection object runs. + SignalDestroyed(this); + SignalDestroyed.disconnect_all(); + LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed); + + // Unwind the stack before deleting the object in case upstream callers + // need to refer to the Connection's state as part of teardown. + // NOTE: We move ownership of 'this' into the capture section of the lambda + // so that the object will always be deleted, including if PostTask fails. + // In such a case (only tests), deletion would happen inside of the call + // to `Destroy()`. + network_thread_->PostTask( + webrtc::ToQueuedTask([me = absl::WrapUnique(this)]() {})); } void Connection::FailAndDestroy() { + RTC_DCHECK_RUN_ON(network_thread_); set_state(IceCandidatePairState::FAILED); Destroy(); } void Connection::FailAndPrune() { + RTC_DCHECK_RUN_ON(network_thread_); set_state(IceCandidatePairState::FAILED); Prune(); } void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) { + RTC_DCHECK_RUN_ON(network_thread_); rtc::StringBuilder oss; if (pings_since_last_response_.size() > max) { for (size_t i = 0; i < max; i++) { @@ -781,7 +885,28 @@ void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) { *s = oss.str(); } +bool Connection::reported() const { + RTC_DCHECK_RUN_ON(network_thread_); + return reported_; +} + +void Connection::set_reported(bool reported) { + RTC_DCHECK_RUN_ON(network_thread_); + reported_ = reported; +} + +bool Connection::selected() const { + RTC_DCHECK_RUN_ON(network_thread_); + return selected_; +} + +void Connection::set_selected(bool selected) { + RTC_DCHECK_RUN_ON(network_thread_); + selected_ = selected; +} + void Connection::UpdateState(int64_t now) { + RTC_DCHECK_RUN_ON(network_thread_); int rtt = ConservativeRTTEstimate(rtt_); if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) { @@ -838,7 +963,13 @@ void Connection::UpdateState(int64_t now) { } } +int64_t Connection::last_ping_sent() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_ping_sent_; +} + void Connection::Ping(int64_t now) { + RTC_DCHECK_RUN_ON(network_thread_); last_ping_sent_ = now; ConnectionRequest* req = new ConnectionRequest(this); // If not using renomination, we use "1" to mean "nominated" and "0" to mean @@ -857,13 +988,38 @@ void Connection::Ping(int64_t now) { num_pings_sent_++; } +int64_t Connection::last_ping_response_received() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_ping_response_received_; +} + +const absl::optional& Connection::last_ping_id_received() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_ping_id_received_; +} + +// Used to check if any STUN ping response has been received. +int Connection::rtt_samples() const { + RTC_DCHECK_RUN_ON(network_thread_); + return rtt_samples_; +} + +// Called whenever a valid ping is received on this connection. This is +// public because the connection intercepts the first ping for us. +int64_t Connection::last_ping_received() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_ping_received_; +} + void Connection::ReceivedPing(const absl::optional& request_id) { + RTC_DCHECK_RUN_ON(network_thread_); last_ping_received_ = rtc::TimeMillis(); last_ping_id_received_ = request_id; UpdateReceiving(last_ping_received_); } void Connection::HandlePiggybackCheckAcknowledgementIfAny(StunMessage* msg) { + RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(msg->type() == STUN_BINDING_REQUEST || msg->type() == GOOG_PING_REQUEST); const StunByteStringAttribute* last_ice_check_received_attr = @@ -884,10 +1040,21 @@ void Connection::HandlePiggybackCheckAcknowledgementIfAny(StunMessage* msg) { } } +int64_t Connection::last_send_data() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_send_data_; +} + +int64_t Connection::last_data_received() const { + RTC_DCHECK_RUN_ON(network_thread_); + return last_data_received_; +} + void Connection::ReceivedPingResponse( int rtt, const std::string& request_id, const absl::optional& nomination) { + RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_GE(rtt, 0); // We've already validated that this is a STUN binding response with // the correct local and remote username for this connection. @@ -916,7 +1083,39 @@ void Connection::ReceivedPingResponse( rtt_samples_++; } +Connection::WriteState Connection::write_state() const { + RTC_DCHECK_RUN_ON(network_thread_); + return write_state_; +} + +bool Connection::writable() const { + RTC_DCHECK_RUN_ON(network_thread_); + return write_state_ == STATE_WRITABLE; +} + +bool Connection::receiving() const { + RTC_DCHECK_RUN_ON(network_thread_); + return receiving_; +} + +// Determines whether the connection has finished connecting. This can only +// be false for TCP connections. +bool Connection::connected() const { + RTC_DCHECK_RUN_ON(network_thread_); + return connected_; +} + +bool Connection::weak() const { + return !(writable() && receiving() && connected()); +} + +bool Connection::active() const { + RTC_DCHECK_RUN_ON(network_thread_); + return write_state_ != STATE_WRITE_TIMEOUT; +} + bool Connection::dead(int64_t now) const { + RTC_DCHECK_RUN_ON(network_thread_); if (last_received() > 0) { // If it has ever received anything, we keep it alive // - if it has recevied last DEAD_CONNECTION_RECEIVE_TIMEOUT (30s) @@ -960,6 +1159,11 @@ bool Connection::dead(int64_t now) const { return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME); } +int Connection::rtt() const { + RTC_DCHECK_RUN_ON(network_thread_); + return rtt_; +} + bool Connection::stable(int64_t now) const { // A connection is stable if it's RTT has converged and it isn't missing any // responses. We should send pings at a higher rate until the RTT converges @@ -978,6 +1182,7 @@ uint32_t Connection::ComputeNetworkCost() const { } std::string Connection::ToString() const { + RTC_DCHECK_RUN_ON(network_thread_); const absl::string_view CONNECT_STATE_ABBREV[2] = { "-", // not connected (false) "C", // connected (true) @@ -1014,8 +1219,8 @@ std::string Connection::ToString() const { << ":" << remote.address().ToSensitiveString() << "|" << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()] << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast(state())] - << "|" << SELECTED_STATE_ABBREV[selected()] << "|" << remote_nomination() - << "|" << nomination() << "|" << priority() << "|"; + << "|" << SELECTED_STATE_ABBREV[selected_] << "|" << remote_nomination() + << "|" << nomination_ << "|" << priority() << "|"; if (rtt_ < DEFAULT_RTT) { ss << rtt_ << "]"; } else { @@ -1029,6 +1234,7 @@ std::string Connection::ToSensitiveString() const { } const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() { + RTC_DCHECK_RUN_ON(network_thread_); if (log_description_.has_value()) { return log_description_.value(); } @@ -1052,6 +1258,12 @@ const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() { return log_description_.value(); } +void Connection::set_ice_event_log(webrtc::IceEventLog* ice_event_log) { + RTC_DCHECK_RUN_ON(network_thread_); + ice_event_log_ = ice_event_log; +} + +// RTC_RUN_ON(network_thread_) void Connection::LogCandidatePairConfig( webrtc::IceCandidatePairConfigType type) { if (ice_event_log_ == nullptr) { @@ -1060,6 +1272,7 @@ void Connection::LogCandidatePairConfig( ice_event_log_->LogCandidatePairConfig(type, id(), ToLogDescription()); } +// RTC_RUN_ON(network_thread_) void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type, uint32_t transaction_id) { if (ice_event_log_ == nullptr) { @@ -1070,6 +1283,7 @@ void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type, void Connection::OnConnectionRequestResponse(ConnectionRequest* request, StunMessage* response) { + RTC_DCHECK_RUN_ON(network_thread_); // Log at LS_INFO if we receive a ping response on an unwritable // connection. rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE; @@ -1159,6 +1373,7 @@ void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) { << request->Elapsed() << " ms"; } +// RTC_RUN_ON(network_thread_). void Connection::OnConnectionRequestSent(ConnectionRequest* request) { // Log at LS_INFO if we send a ping on an unwritable connection. rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE; @@ -1166,7 +1381,7 @@ void Connection::OnConnectionRequestSent(ConnectionRequest* request) { << StunMethodToString(request->msg()->type()) << ", id=" << rtc::hex_encode(request->id()) << ", use_candidate=" << use_candidate_attr() - << ", nomination=" << nomination(); + << ", nomination=" << nomination_; stats_.sent_ping_requests_total++; LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckSent, request->reduced_transaction_id()); @@ -1179,6 +1394,16 @@ void Connection::HandleRoleConflictFromPeer() { port_->SignalRoleConflict(port_); } +IceCandidatePairState Connection::state() const { + RTC_DCHECK_RUN_ON(network_thread_); + return state_; +} + +int Connection::num_pings_sent() const { + RTC_DCHECK_RUN_ON(network_thread_); + return num_pings_sent_; +} + void Connection::MaybeSetRemoteIceParametersAndGeneration( const IceParameters& ice_params, int generation) { @@ -1209,20 +1434,19 @@ void Connection::MaybeUpdatePeerReflexiveCandidate( } } -void Connection::OnMessage(rtc::Message* pmsg) { - RTC_DCHECK(pmsg->message_id == MSG_DELETE); - RTC_LOG(LS_INFO) << "Connection deleted with number of pings sent: " - << num_pings_sent_; - SignalDestroyed(this); - delete this; -} - int64_t Connection::last_received() const { + RTC_DCHECK_RUN_ON(network_thread_); return std::max(last_data_received_, std::max(last_ping_received_, last_ping_response_received_)); } +int64_t Connection::receiving_unchanged_since() const { + RTC_DCHECK_RUN_ON(network_thread_); + return receiving_unchanged_since_; +} + ConnectionInfo Connection::stats() { + RTC_DCHECK_RUN_ON(network_thread_); stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate()); stats_.recv_total_bytes = recv_rate_tracker_.TotalSampleCount(); stats_.sent_bytes_second = round(send_rate_tracker_.ComputeRate()); @@ -1309,10 +1533,12 @@ void Connection::MaybeUpdateLocalCandidate(ConnectionRequest* request, } bool Connection::rtt_converged() const { + RTC_DCHECK_RUN_ON(network_thread_); return rtt_samples_ > (RTT_RATIO + 1); } bool Connection::missing_responses(int64_t now) const { + RTC_DCHECK_RUN_ON(network_thread_); if (pings_since_last_response_.empty()) { return false; } @@ -1323,6 +1549,7 @@ bool Connection::missing_responses(int64_t now) const { bool Connection::TooManyOutstandingPings( const absl::optional& max_outstanding_pings) const { + RTC_DCHECK_RUN_ON(network_thread_); if (!max_outstanding_pings.has_value()) { return false; } @@ -1333,6 +1560,7 @@ bool Connection::TooManyOutstandingPings( return true; } +// RTC_RUN_ON(network_thread_). bool Connection::ShouldSendGoogPing(const StunMessage* message) { if (remote_support_goog_ping_ == true && cached_stun_binding_ && cached_stun_binding_->EqualAttributes(message, [](int type) { @@ -1350,6 +1578,7 @@ bool Connection::ShouldSendGoogPing(const StunMessage* message) { } void Connection::ForgetLearnedState() { + RTC_DCHECK_RUN_ON(network_thread_); RTC_LOG(LS_INFO) << ToString() << ": Connection forget learned state"; requests_.Clear(); receiving_ = false; @@ -1374,6 +1603,7 @@ int ProxyConnection::Send(const void* data, RTC_DCHECK(sent < 0); error_ = port_->GetError(); stats_.sent_discarded_packets++; + stats_.sent_discarded_bytes += size; } else { send_rate_tracker_.AddSamplesAtTime(now, sent); } diff --git a/p2p/base/connection.h b/p2p/base/connection.h index 7efe7d65e9..a7b3490757 100644 --- a/p2p/base/connection.h +++ b/p2p/base/connection.h @@ -70,9 +70,7 @@ class ConnectionRequest : public StunRequest { // Represents a communication link between a port on the local client and a // port on the remote client. -class Connection : public CandidatePairInterface, - public rtc::MessageHandlerAutoCleanup, - public sigslot::has_slots<> { +class Connection : public CandidatePairInterface, public sigslot::has_slots<> { public: struct SentPing { SentPing(const std::string id, int64_t sent_time, uint32_t nomination) @@ -88,6 +86,8 @@ class Connection : public CandidatePairInterface, // A unique ID assigned when the connection is created. uint32_t id() const { return id_; } + webrtc::TaskQueueBase* network_thread() const; + // Implementation of virtual methods in CandidatePairInterface. // Returns the description of the local port const Candidate& local_candidate() const override; @@ -109,34 +109,28 @@ class Connection : public CandidatePairInterface, STATE_WRITE_TIMEOUT = 3, // we have had a large number of ping failures }; - WriteState write_state() const { return write_state_; } - bool writable() const { return write_state_ == STATE_WRITABLE; } - bool receiving() const { return receiving_; } + WriteState write_state() const; + bool writable() const; + bool receiving() const; // Determines whether the connection has finished connecting. This can only // be false for TCP connections. - bool connected() const { return connected_; } - bool weak() const { return !(writable() && receiving() && connected()); } - bool active() const { return write_state_ != STATE_WRITE_TIMEOUT; } + bool connected() const; + bool weak() const; + bool active() const; // A connection is dead if it can be safely deleted. bool dead(int64_t now) const; // Estimate of the round-trip time over this connection. - int rtt() const { return rtt_; } + int rtt() const; int unwritable_timeout() const; - void set_unwritable_timeout(const absl::optional& value_ms) { - unwritable_timeout_ = value_ms; - } + void set_unwritable_timeout(const absl::optional& value_ms); int unwritable_min_checks() const; - void set_unwritable_min_checks(const absl::optional& value) { - unwritable_min_checks_ = value; - } + void set_unwritable_min_checks(const absl::optional& value); int inactive_timeout() const; - void set_inactive_timeout(const absl::optional& value) { - inactive_timeout_ = value; - } + void set_inactive_timeout(const absl::optional& value); // Gets the `ConnectionInfo` stats, where `best_connection` has not been // populated (default value false). @@ -172,15 +166,15 @@ class Connection : public CandidatePairInterface, // still keep it around in case the other side wants to use it. But we can // safely stop pinging on it and we can allow it to time out if the other // side stops using it as well. - bool pruned() const { return pruned_; } + bool pruned() const; void Prune(); - bool use_candidate_attr() const { return use_candidate_attr_; } + bool use_candidate_attr() const; void set_use_candidate_attr(bool enable); - void set_nomination(uint32_t value) { nomination_ = value; } + void set_nomination(uint32_t value); - uint32_t remote_nomination() const { return remote_nomination_; } + uint32_t remote_nomination() const; // One or several pairs may be nominated based on if Regular or Aggressive // Nomination is used. https://tools.ietf.org/html/rfc5245#section-8 // `nominated` is defined both for the controlling or controlled agent based @@ -188,13 +182,10 @@ class Connection : public CandidatePairInterface, // gets its `remote_nomination_` set when pinged by the controlling agent with // a nomination value. The controlling agent gets its `acked_nomination_` set // when receiving a response to a nominating ping. - bool nominated() const { return acked_nomination_ || remote_nomination_; } - void set_remote_ice_mode(IceMode mode) { remote_ice_mode_ = mode; } + bool nominated() const; int receiving_timeout() const; - void set_receiving_timeout(absl::optional receiving_timeout_ms) { - receiving_timeout_ = receiving_timeout_ms; - } + void set_receiving_timeout(absl::optional receiving_timeout_ms); // Makes the connection go away. void Destroy(); @@ -211,24 +202,23 @@ class Connection : public CandidatePairInterface, void UpdateState(int64_t now); // Called when this connection should try checking writability again. - int64_t last_ping_sent() const { return last_ping_sent_; } + int64_t last_ping_sent() const; void Ping(int64_t now); void ReceivedPingResponse( int rtt, const std::string& request_id, const absl::optional& nomination = absl::nullopt); - int64_t last_ping_response_received() const { - return last_ping_response_received_; - } - const absl::optional& last_ping_id_received() const { - return last_ping_id_received_; - } + + int64_t last_ping_response_received() const; + const absl::optional& last_ping_id_received() const; + // Used to check if any STUN ping response has been received. - int rtt_samples() const { return rtt_samples_; } + int rtt_samples() const; // Called whenever a valid ping is received on this connection. This is // public because the connection intercepts the first ping for us. - int64_t last_ping_received() const { return last_ping_received_; } + int64_t last_ping_received() const; + void ReceivedPing( const absl::optional& request_id = absl::nullopt); // Handles the binding request; sends a response if this is a valid request. @@ -238,8 +228,8 @@ class Connection : public CandidatePairInterface, // connectivity check from the peer. void HandlePiggybackCheckAcknowledgementIfAny(StunMessage* msg); // Timestamp when data was last sent (or attempted to be sent). - int64_t last_send_data() const { return last_send_data_; } - int64_t last_data_received() const { return last_data_received_; } + int64_t last_send_data() const; + int64_t last_data_received() const; // Debugging description of this connection std::string ToDebugId() const; @@ -247,19 +237,19 @@ class Connection : public CandidatePairInterface, std::string ToSensitiveString() const; // Structured description of this candidate pair. const webrtc::IceCandidatePairDescription& ToLogDescription(); - void set_ice_event_log(webrtc::IceEventLog* ice_event_log) { - ice_event_log_ = ice_event_log; - } + void set_ice_event_log(webrtc::IceEventLog* ice_event_log); + // Prints pings_since_last_response_ into a string. void PrintPingsSinceLastResponse(std::string* pings, size_t max); - bool reported() const { return reported_; } - void set_reported(bool reported) { reported_ = reported; } - // The following two methods are only used for logging in ToString above, and - // this flag is set true by P2PTransportChannel for its selected candidate - // pair. - bool selected() const { return selected_; } - void set_selected(bool selected) { selected_ = selected; } + bool reported() const; + void set_reported(bool reported); + + // `set_selected` is only used for logging in ToString above. The flag is + // set true by P2PTransportChannel for its selected candidate pair. + // TODO(tommi): Remove `selected()` once not referenced downstream. + bool selected() const; + void set_selected(bool selected); // This signal will be fired if this connection is nominated by the // controlling side. @@ -268,11 +258,9 @@ class Connection : public CandidatePairInterface, // Invoked when Connection receives STUN error response with 487 code. void HandleRoleConflictFromPeer(); - IceCandidatePairState state() const { return state_; } + IceCandidatePairState state() const; - int num_pings_sent() const { return num_pings_sent_; } - - IceMode remote_ice_mode() const { return remote_ice_mode_; } + int num_pings_sent() const; uint32_t ComputeNetworkCost() const; @@ -291,9 +279,7 @@ class Connection : public CandidatePairInterface, // response in milliseconds int64_t last_received() const; // Returns the last time when the connection changed its receiving state. - int64_t receiving_unchanged_since() const { - return receiving_unchanged_since_; - } + int64_t receiving_unchanged_since() const; bool stable(int64_t now) const; @@ -328,16 +314,10 @@ class Connection : public CandidatePairInterface, const Port* PortForTest() const { return port_; } // Public for unit tests. - uint32_t acked_nomination() const { return acked_nomination_; } - - // Public for unit tests. - void set_remote_nomination(uint32_t remote_nomination) { - remote_nomination_ = remote_nomination; - } + uint32_t acked_nomination() const; + void set_remote_nomination(uint32_t remote_nomination); protected: - enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE }; - // Constructs a new connection to the given remote port. Connection(Port* port, size_t index, const Candidate& candidate); @@ -348,9 +328,12 @@ class Connection : public CandidatePairInterface, virtual void OnConnectionRequestResponse(ConnectionRequest* req, StunMessage* response); void OnConnectionRequestErrorResponse(ConnectionRequest* req, - StunMessage* response); - void OnConnectionRequestTimeout(ConnectionRequest* req); - void OnConnectionRequestSent(ConnectionRequest* req); + StunMessage* response) + RTC_RUN_ON(network_thread_); + void OnConnectionRequestTimeout(ConnectionRequest* req) + RTC_RUN_ON(network_thread_); + void OnConnectionRequestSent(ConnectionRequest* req) + RTC_RUN_ON(network_thread_); bool rtt_converged() const; @@ -364,103 +347,120 @@ class Connection : public CandidatePairInterface, void set_state(IceCandidatePairState state); void set_connected(bool value); - uint32_t nomination() const { return nomination_; } - - void OnMessage(rtc::Message* pmsg) override; - // The local port where this connection sends and receives packets. Port* port() { return port_; } const Port* port() const { return port_; } - uint32_t id_; - Port* port_; - size_t local_candidate_index_; + // NOTE: A pointer to the network thread is held by `port_` so in theory we + // shouldn't need to hold on to this pointer here, but rather defer to + // port_->thread(). However, some tests delete the classes in the wrong order + // so `port_` may be deleted before an instance of this class is deleted. + // TODO(tommi): This ^^^ should be fixed. + webrtc::TaskQueueBase* const network_thread_; + const uint32_t id_; + Port* const port_; + size_t local_candidate_index_ RTC_GUARDED_BY(network_thread_); Candidate remote_candidate_; ConnectionInfo stats_; rtc::RateTracker recv_rate_tracker_; rtc::RateTracker send_rate_tracker_; int64_t last_send_data_ = 0; + // Set to true when deletion has been scheduled and must not be done again. + // See `Destroy()` for more details. + bool pending_delete_ RTC_GUARDED_BY(network_thread_) = false; private: // Update the local candidate based on the mapped address attribute. // If the local candidate changed, fires SignalStateChange. void MaybeUpdateLocalCandidate(ConnectionRequest* request, - StunMessage* response); + StunMessage* response) + RTC_RUN_ON(network_thread_); - void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type); + void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type) + RTC_RUN_ON(network_thread_); void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type, - uint32_t transaction_id); + uint32_t transaction_id) + RTC_RUN_ON(network_thread_); // Check if this IceMessage is identical // to last message ack:ed STUN_BINDING_REQUEST. - bool ShouldSendGoogPing(const StunMessage* message); + bool ShouldSendGoogPing(const StunMessage* message) + RTC_RUN_ON(network_thread_); - WriteState write_state_; - bool receiving_; - bool connected_; - bool pruned_; - bool selected_ = false; + WriteState write_state_ RTC_GUARDED_BY(network_thread_); + bool receiving_ RTC_GUARDED_BY(network_thread_); + bool connected_ RTC_GUARDED_BY(network_thread_); + bool pruned_ RTC_GUARDED_BY(network_thread_); + bool selected_ RTC_GUARDED_BY(network_thread_) = false; // By default `use_candidate_attr_` flag will be true, // as we will be using aggressive nomination. // But when peer is ice-lite, this flag "must" be initialized to false and // turn on when connection becomes "best connection". - bool use_candidate_attr_; + bool use_candidate_attr_ RTC_GUARDED_BY(network_thread_); // Used by the controlling side to indicate that this connection will be // selected for transmission if the peer supports ICE-renomination when this // value is positive. A larger-value indicates that a connection is nominated // later and should be selected by the controlled side with higher precedence. // A zero-value indicates not nominating this connection. - uint32_t nomination_ = 0; + uint32_t nomination_ RTC_GUARDED_BY(network_thread_) = 0; // The last nomination that has been acknowledged. - uint32_t acked_nomination_ = 0; + uint32_t acked_nomination_ RTC_GUARDED_BY(network_thread_) = 0; // Used by the controlled side to remember the nomination value received from // the controlling side. When the peer does not support ICE re-nomination, its // value will be 1 if the connection has been nominated. - uint32_t remote_nomination_ = 0; + uint32_t remote_nomination_ RTC_GUARDED_BY(network_thread_) = 0; - IceMode remote_ice_mode_; - StunRequestManager requests_; - int rtt_; - int rtt_samples_ = 0; + StunRequestManager requests_ RTC_GUARDED_BY(network_thread_); + int rtt_ RTC_GUARDED_BY(network_thread_); + int rtt_samples_ RTC_GUARDED_BY(network_thread_) = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime - uint64_t total_round_trip_time_ms_ = 0; + uint64_t total_round_trip_time_ms_ RTC_GUARDED_BY(network_thread_) = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime - absl::optional current_round_trip_time_ms_; - int64_t last_ping_sent_; // last time we sent a ping to the other side - int64_t last_ping_received_; // last time we received a ping from the other - // side - int64_t last_data_received_; - int64_t last_ping_response_received_; - int64_t receiving_unchanged_since_ = 0; - std::vector pings_since_last_response_; + absl::optional current_round_trip_time_ms_ + RTC_GUARDED_BY(network_thread_); + int64_t last_ping_sent_ RTC_GUARDED_BY( + network_thread_); // last time we sent a ping to the other side + int64_t last_ping_received_ + RTC_GUARDED_BY(network_thread_); // last time we received a ping from the + // other side + int64_t last_data_received_ RTC_GUARDED_BY(network_thread_); + int64_t last_ping_response_received_ RTC_GUARDED_BY(network_thread_); + int64_t receiving_unchanged_since_ RTC_GUARDED_BY(network_thread_) = 0; + std::vector pings_since_last_response_ + RTC_GUARDED_BY(network_thread_); // Transaction ID of the last connectivity check received. Null if having not // received a ping yet. - absl::optional last_ping_id_received_; + absl::optional last_ping_id_received_ + RTC_GUARDED_BY(network_thread_); - absl::optional unwritable_timeout_; - absl::optional unwritable_min_checks_; - absl::optional inactive_timeout_; + absl::optional unwritable_timeout_ RTC_GUARDED_BY(network_thread_); + absl::optional unwritable_min_checks_ RTC_GUARDED_BY(network_thread_); + absl::optional inactive_timeout_ RTC_GUARDED_BY(network_thread_); - bool reported_; - IceCandidatePairState state_; + bool reported_ RTC_GUARDED_BY(network_thread_); + IceCandidatePairState state_ RTC_GUARDED_BY(network_thread_); // Time duration to switch from receiving to not receiving. - absl::optional receiving_timeout_; - int64_t time_created_ms_; - int num_pings_sent_ = 0; + absl::optional receiving_timeout_ RTC_GUARDED_BY(network_thread_); + int64_t time_created_ms_ RTC_GUARDED_BY(network_thread_); + int num_pings_sent_ RTC_GUARDED_BY(network_thread_) = 0; - absl::optional log_description_; - webrtc::IceEventLog* ice_event_log_ = nullptr; + absl::optional log_description_ + RTC_GUARDED_BY(network_thread_); + webrtc::IceEventLog* ice_event_log_ RTC_GUARDED_BY(network_thread_) = nullptr; // GOOG_PING_REQUEST is sent in place of STUN_BINDING_REQUEST // if configured via field trial, the remote peer supports it (signaled // in STUN_BINDING) and if the last STUN BINDING is identical to the one // that is about to be sent. - absl::optional remote_support_goog_ping_; - std::unique_ptr cached_stun_binding_; + absl::optional remote_support_goog_ping_ + RTC_GUARDED_BY(network_thread_); + std::unique_ptr cached_stun_binding_ + RTC_GUARDED_BY(network_thread_); const IceFieldTrials* field_trials_; - rtc::EventBasedExponentialMovingAverage rtt_estimate_; + rtc::EventBasedExponentialMovingAverage rtt_estimate_ + RTC_GUARDED_BY(network_thread_); friend class Port; friend class ConnectionRequest; diff --git a/p2p/base/connection_info.cc b/p2p/base/connection_info.cc index ebea2ab5b0..d0cd3239f1 100644 --- a/p2p/base/connection_info.cc +++ b/p2p/base/connection_info.cc @@ -19,6 +19,7 @@ ConnectionInfo::ConnectionInfo() timeout(false), new_connection(false), rtt(0), + sent_discarded_bytes(0), sent_total_bytes(0), sent_bytes_second(0), sent_discarded_packets(0), diff --git a/p2p/base/connection_info.h b/p2p/base/connection_info.h index b5e1c14433..1117595481 100644 --- a/p2p/base/connection_info.h +++ b/p2p/base/connection_info.h @@ -41,12 +41,15 @@ struct ConnectionInfo { bool timeout; // Has this connection timed out? bool new_connection; // Is this a newly created connection? size_t rtt; // The STUN RTT for this connection. - size_t sent_total_bytes; // Total bytes sent on this connection. + size_t sent_discarded_bytes; // Number of outgoing bytes discarded due to + // socket errors. + size_t sent_total_bytes; // Total bytes sent on this connection. Does not + // include discarded bytes. size_t sent_bytes_second; // Bps over the last measurement interval. size_t sent_discarded_packets; // Number of outgoing packets discarded due to // socket errors. size_t sent_total_packets; // Number of total outgoing packets attempted for - // sending. + // sending, including discarded packets. size_t sent_ping_requests_total; // Number of STUN ping request sent. size_t sent_ping_requests_before_first_response; // Number of STUN ping // sent before receiving the first response. diff --git a/p2p/base/default_ice_transport_factory.cc b/p2p/base/default_ice_transport_factory.cc index 0a7175cfd8..8d87d66697 100644 --- a/p2p/base/default_ice_transport_factory.cc +++ b/p2p/base/default_ice_transport_factory.cc @@ -44,10 +44,10 @@ DefaultIceTransportFactory::CreateIceTransport( int component, IceTransportInit init) { BasicIceControllerFactory factory; + init.set_ice_controller_factory(&factory); return rtc::make_ref_counted( - cricket::P2PTransportChannel::Create( - transport_name, component, init.port_allocator(), - init.async_dns_resolver_factory(), init.event_log(), &factory)); + cricket::P2PTransportChannel::Create(transport_name, component, + std::move(init))); } } // namespace webrtc diff --git a/p2p/base/dtls_transport.cc b/p2p/base/dtls_transport.cc index 172d06188f..bf8583f670 100644 --- a/p2p/base/dtls_transport.cc +++ b/p2p/base/dtls_transport.cc @@ -433,7 +433,7 @@ int DtlsTransport::SendPacket(const char* data, "webrtc::DtlsTransportState::kClosed."; return -1; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } } @@ -532,7 +532,7 @@ void DtlsTransport::OnWritableState(rtc::PacketTransportInternal* transport) { "webrtc::DtlsTransportState::kClosed."; break; case webrtc::DtlsTransportState::kNumValues: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } @@ -720,7 +720,7 @@ void DtlsTransport::MaybeStartDtls() { // packets in this state, the incoming queue must be empty. We // ignore write errors, thus any errors must be because of // configuration and therefore are our fault. - RTC_NOTREACHED() << "StartSSL failed."; + RTC_DCHECK_NOTREACHED() << "StartSSL failed."; RTC_LOG(LS_ERROR) << ToString() << ": Couldn't start DTLS handshake"; set_dtls_state(webrtc::DtlsTransportState::kFailed); return; diff --git a/p2p/base/dtls_transport.h b/p2p/base/dtls_transport.h index edfa8896ce..d503a928b4 100644 --- a/p2p/base/dtls_transport.h +++ b/p2p/base/dtls_transport.h @@ -22,7 +22,6 @@ #include "p2p/base/ice_transport_internal.h" #include "rtc_base/buffer.h" #include "rtc_base/buffer_queue.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" #include "rtc_base/strings/string_builder.h" @@ -40,6 +39,9 @@ class StreamInterfaceChannel : public rtc::StreamInterface { public: explicit StreamInterfaceChannel(IceTransportInternal* ice_transport); + StreamInterfaceChannel(const StreamInterfaceChannel&) = delete; + StreamInterfaceChannel& operator=(const StreamInterfaceChannel&) = delete; + // Push in a packet; this gets pulled out from Read(). bool OnPacketReceived(const char* data, size_t size); @@ -60,8 +62,6 @@ class StreamInterfaceChannel : public rtc::StreamInterface { IceTransportInternal* const ice_transport_; // owned by DtlsTransport rtc::StreamState state_ RTC_GUARDED_BY(sequence_checker_); rtc::BufferQueue packets_ RTC_GUARDED_BY(sequence_checker_); - - RTC_DISALLOW_COPY_AND_ASSIGN(StreamInterfaceChannel); }; // This class provides a DTLS SSLStreamAdapter inside a TransportChannel-style @@ -110,6 +110,9 @@ class DtlsTransport : public DtlsTransportInternal { ~DtlsTransport() override; + DtlsTransport(const DtlsTransport&) = delete; + DtlsTransport& operator=(const DtlsTransport&) = delete; + webrtc::DtlsTransportState dtls_state() const override; const std::string& transport_name() const override; int component() const override; @@ -248,8 +251,6 @@ class DtlsTransport : public DtlsTransportInternal { bool writable_ = false; webrtc::RtcEventLog* const event_log_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DtlsTransport); }; } // namespace cricket diff --git a/p2p/base/dtls_transport_internal.h b/p2p/base/dtls_transport_internal.h index 0b26a7fd7a..24c682ff0e 100644 --- a/p2p/base/dtls_transport_internal.h +++ b/p2p/base/dtls_transport_internal.h @@ -25,7 +25,6 @@ #include "p2p/base/ice_transport_internal.h" #include "p2p/base/packet_transport_internal.h" #include "rtc_base/callback_list.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_fingerprint.h" #include "rtc_base/ssl_stream_adapter.h" @@ -48,6 +47,9 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal { public: ~DtlsTransportInternal() override; + DtlsTransportInternal(const DtlsTransportInternal&) = delete; + DtlsTransportInternal& operator=(const DtlsTransportInternal&) = delete; + virtual webrtc::DtlsTransportState dtls_state() const = 0; virtual int component() const = 0; @@ -135,7 +137,6 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal { DtlsTransportInternal(); private: - RTC_DISALLOW_COPY_AND_ASSIGN(DtlsTransportInternal); webrtc::CallbackList dtls_handshake_error_callback_list_; webrtc::CallbackList diff --git a/p2p/base/fake_ice_transport.h b/p2p/base/fake_ice_transport.h index 8b52fe934c..c053abd5f9 100644 --- a/p2p/base/fake_ice_transport.h +++ b/p2p/base/fake_ice_transport.h @@ -364,7 +364,7 @@ class FakeIceTransport : public IceTransportInternal { if (writable_ == writable) { return; } - RTC_LOG(INFO) << "Change writable_ to " << writable; + RTC_LOG(LS_INFO) << "Change writable_ to " << writable; writable_ = writable; if (writable_) { SignalReadyToSend(this); diff --git a/p2p/base/fake_port_allocator.h b/p2p/base/fake_port_allocator.h index eca9bd50bb..47dd8e589f 100644 --- a/p2p/base/fake_port_allocator.h +++ b/p2p/base/fake_port_allocator.h @@ -39,11 +39,10 @@ class TestUDPPort : public UDPPort { uint16_t max_port, const std::string& username, const std::string& password, - const std::string& origin, bool emit_localhost_for_anyaddress) { TestUDPPort* port = new TestUDPPort(thread, factory, network, min_port, max_port, username, - password, origin, emit_localhost_for_anyaddress); + password, emit_localhost_for_anyaddress); if (!port->Init()) { delete port; port = nullptr; @@ -59,7 +58,6 @@ class TestUDPPort : public UDPPort { uint16_t max_port, const std::string& username, const std::string& password, - const std::string& origin, bool emit_localhost_for_anyaddress) : UDPPort(thread, factory, @@ -68,7 +66,6 @@ class TestUDPPort : public UDPPort { max_port, username, password, - origin, emit_localhost_for_anyaddress) {} }; @@ -118,8 +115,7 @@ class FakePortAllocatorSession : public PortAllocatorSession { ? ipv6_network_ : ipv4_network_; port_.reset(TestUDPPort::Create(network_thread_, factory_, &network, 0, 0, - username(), password(), std::string(), - false)); + username(), password(), false)); RTC_DCHECK(port_); // RingRTC change to support ICE forking port_->SignalDestroyed.connect(this, &FakePortAllocatorSession::OnPortDestroyed); @@ -211,11 +207,13 @@ class FakePortAllocatorSession : public PortAllocatorSession { class FakePortAllocator : public cricket::PortAllocator { public: + // TODO(bugs.webrtc.org/13145): Require non-null `factory`. FakePortAllocator(rtc::Thread* network_thread, rtc::PacketSocketFactory* factory) : network_thread_(network_thread), factory_(factory) { if (factory_ == NULL) { - owned_factory_.reset(new rtc::BasicPacketSocketFactory(network_thread_)); + owned_factory_.reset(new rtc::BasicPacketSocketFactory( + network_thread_ ? network_thread_->socketserver() : nullptr)); factory_ = owned_factory_.get(); } @@ -247,7 +245,7 @@ class FakePortAllocator : public cricket::PortAllocator { cricket::IceCredentialsIterator::CreateRandomIceCredentials(); auto session = new_allocator->CreateSession( content_name, 1, parameters.ufrag, parameters.pwd); - return new rtc::RefCountedObject( + return rtc::make_ref_counted( network_thread_, std::move(new_allocator), std::move(session)); } diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h index f49567f932..d00c4ea57c 100644 --- a/p2p/base/ice_transport_internal.h +++ b/p2p/base/ice_transport_internal.h @@ -43,6 +43,15 @@ struct IceTransportStats { // Initially 0 and 1 once the first candidate pair has been selected. // The counter is increase also when "unselecting" a connection. uint32_t selected_candidate_pair_changes = 0; + + // Bytes/packets sent/received. + // note: Is not the same as sum(connection_infos.bytes_sent) + // as connections are created and destroyed while the ICE transport + // is alive. + uint64_t bytes_sent = 0; + uint64_t bytes_received = 0; + uint64_t packets_sent = 0; + uint64_t packets_received = 0; }; typedef std::vector Candidates; diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc index badb7818a5..6d209da490 100644 --- a/p2p/base/p2p_transport_channel.cc +++ b/p2p/base/p2p_transport_channel.cc @@ -25,6 +25,7 @@ #include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/task_queue/queued_task.h" +#include "api/webrtc_key_value_config.h" #include "logging/rtc_event_log/ice_logger.h" #include "p2p/base/basic_async_resolver_factory.h" #include "p2p/base/basic_ice_controller.h" @@ -44,7 +45,6 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace { @@ -60,50 +60,28 @@ cricket::PortInterface::CandidateOrigin GetOrigin( return cricket::PortInterface::ORIGIN_OTHER_PORT; } -uint32_t GetWeakPingIntervalInFieldTrial() { - uint32_t weak_ping_interval = ::strtoul( - webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(), - nullptr, 10); - if (weak_ping_interval) { - return static_cast(weak_ping_interval); +uint32_t GetWeakPingIntervalInFieldTrial( + const webrtc::WebRtcKeyValueConfig* field_trials) { + if (field_trials != nullptr) { + uint32_t weak_ping_interval = + ::strtoul(field_trials->Lookup("WebRTC-StunInterPacketDelay").c_str(), + nullptr, 10); + if (weak_ping_interval) { + return static_cast(weak_ping_interval); + } } return cricket::WEAK_PING_INTERVAL; } -rtc::AdapterType GuessAdapterTypeFromNetworkCost(int network_cost) { - // The current network costs have been unchanged since they were added - // to webrtc. If they ever were to change we would need to reconsider - // this method. - switch (network_cost) { - case rtc::kNetworkCostMin: - return rtc::ADAPTER_TYPE_ETHERNET; - case rtc::kNetworkCostLow: - return rtc::ADAPTER_TYPE_WIFI; - case rtc::kNetworkCostCellular: - return rtc::ADAPTER_TYPE_CELLULAR; - case rtc::kNetworkCostCellular2G: - return rtc::ADAPTER_TYPE_CELLULAR_2G; - case rtc::kNetworkCostCellular3G: - return rtc::ADAPTER_TYPE_CELLULAR_3G; - case rtc::kNetworkCostCellular4G: - return rtc::ADAPTER_TYPE_CELLULAR_4G; - case rtc::kNetworkCostCellular5G: - return rtc::ADAPTER_TYPE_CELLULAR_5G; - case rtc::kNetworkCostUnknown: - return rtc::ADAPTER_TYPE_UNKNOWN; - case rtc::kNetworkCostMax: - return rtc::ADAPTER_TYPE_ANY; - } - return rtc::ADAPTER_TYPE_UNKNOWN; -} - rtc::RouteEndpoint CreateRouteEndpointFromCandidate( bool local, const cricket::Candidate& candidate, bool uses_turn) { auto adapter_type = candidate.network_type(); if (!local && adapter_type == rtc::ADAPTER_TYPE_UNKNOWN) { - adapter_type = GuessAdapterTypeFromNetworkCost(candidate.network_cost()); + bool vpn; + std::tie(adapter_type, vpn) = + rtc::Network::GuessAdapterFromNetworkCost(candidate.network_cost()); } // TODO(bugs.webrtc.org/9446) : Rewrite if information about remote network @@ -134,30 +112,37 @@ bool IceCredentialsChanged(const std::string& old_ufrag, return (old_ufrag != new_ufrag) || (old_pwd != new_pwd); } -// static std::unique_ptr P2PTransportChannel::Create( const std::string& transport_name, int component, - PortAllocator* allocator, - webrtc::AsyncDnsResolverFactoryInterface* async_dns_resolver_factory, - webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) { - return absl::WrapUnique(new P2PTransportChannel( - transport_name, component, allocator, async_dns_resolver_factory, - /* owned_dns_resolver_factory= */ nullptr, event_log, - ice_controller_factory)); + webrtc::IceTransportInit init) { + if (init.async_resolver_factory()) { + return absl::WrapUnique(new P2PTransportChannel( + transport_name, component, init.port_allocator(), nullptr, + std::make_unique( + init.async_resolver_factory()), + init.event_log(), init.ice_controller_factory(), init.field_trials())); + } else { + return absl::WrapUnique(new P2PTransportChannel( + transport_name, component, init.port_allocator(), + init.async_dns_resolver_factory(), nullptr, init.event_log(), + init.ice_controller_factory(), init.field_trials())); + } } -P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, - int component, - PortAllocator* allocator) +P2PTransportChannel::P2PTransportChannel( + const std::string& transport_name, + int component, + PortAllocator* allocator, + const webrtc::WebRtcKeyValueConfig* field_trials) : P2PTransportChannel(transport_name, component, allocator, /* async_dns_resolver_factory= */ nullptr, /* owned_dns_resolver_factory= */ nullptr, /* event_log= */ nullptr, - /* ice_controller_factory= */ nullptr) {} + /* ice_controller_factory= */ nullptr, + field_trials) {} // Private constructor, called from Create() P2PTransportChannel::P2PTransportChannel( @@ -168,7 +153,8 @@ P2PTransportChannel::P2PTransportChannel( std::unique_ptr owned_dns_resolver_factory, webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) + IceControllerFactoryInterface* ice_controller_factory, + const webrtc::WebRtcKeyValueConfig* field_trials) : transport_name_(transport_name), component_(component), allocator_(allocator), @@ -186,6 +172,7 @@ P2PTransportChannel::P2PTransportChannel( ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), gathering_state_(kIceGatheringNew), + weak_ping_interval_(GetWeakPingIntervalInFieldTrial(field_trials)), config_(RECEIVING_TIMEOUT, BACKUP_CONNECTION_PING_INTERVAL, GATHER_ONCE /* continual_gathering_policy */, @@ -196,7 +183,6 @@ P2PTransportChannel::P2PTransportChannel( RECEIVING_SWITCHING_DELAY) { TRACE_EVENT0("webrtc", "P2PTransportChannel::P2PTransportChannel"); RTC_DCHECK(allocator_ != nullptr); - weak_ping_interval_ = GetWeakPingIntervalInFieldTrial(); // Validate IceConfig even for mostly built-in constant default values in case // we change them. RTC_DCHECK(ValidateIceConfig(config_).ok()); @@ -212,6 +198,8 @@ P2PTransportChannel::P2PTransportChannel( this, &P2PTransportChannel::OnCandidateFilterChanged); ice_event_log_.set_event_log(event_log); + ParseFieldTrials(field_trials); + IceControllerFactoryArgs args{ [this] { return GetState(); }, [this] { return GetIceRole(); }, [this](const Connection* connection) { @@ -220,8 +208,9 @@ P2PTransportChannel::P2PTransportChannel( return IsPortPruned(connection->port()) || IsRemoteCandidatePruned(connection->remote_candidate()); }, - &field_trials_, - webrtc::field_trial::FindFullName("WebRTC-IceControllerFieldTrials")}; + &ice_field_trials_, + field_trials ? field_trials->Lookup("WebRTC-IceControllerFieldTrials") + : ""}; if (ice_controller_factory != nullptr) { ice_controller_ = ice_controller_factory->Create(args); } else { @@ -229,30 +218,12 @@ P2PTransportChannel::P2PTransportChannel( } } -// Public constructor, exposed for backwards compatibility. -// Deprecated. -P2PTransportChannel::P2PTransportChannel( - const std::string& transport_name, - int component, - PortAllocator* allocator, - webrtc::AsyncResolverFactory* async_resolver_factory, - webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) - : P2PTransportChannel( - transport_name, - component, - allocator, - nullptr, - std::make_unique( - async_resolver_factory), - event_log, - ice_controller_factory) {} - P2PTransportChannel::~P2PTransportChannel() { TRACE_EVENT0("webrtc", "P2PTransportChannel::~P2PTransportChannel"); RTC_DCHECK_RUN_ON(network_thread_); std::vector copy(connections().begin(), connections().end()); for (Connection* con : copy) { + con->SignalDestroyed.disconnect(this); con->Destroy(); } resolvers_.clear(); @@ -331,7 +302,6 @@ void P2PTransportChannel::AddAllocatorSession( void P2PTransportChannel::AddConnection(Connection* connection) { RTC_DCHECK_RUN_ON(network_thread_); - connection->set_remote_ice_mode(remote_ice_mode_); connection->set_receiving_timeout(config_.receiving_timeout); connection->set_unwritable_timeout(config_.ice_unwritable_timeout); connection->set_unwritable_min_checks(config_.ice_unwritable_min_checks); @@ -349,7 +319,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) { had_connection_ = true; connection->set_ice_event_log(&ice_event_log_); - connection->SetIceFieldTrials(&field_trials_); + connection->SetIceFieldTrials(&ice_field_trials_); LogCandidatePairConfig(connection, webrtc::IceCandidatePairConfigType::kAdded); @@ -767,72 +737,6 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { << config.stun_keepalive_interval_or_default(); } - if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing")) { - RTC_LOG(LS_INFO) << "Set WebRTC-ExtraICEPing: Enabled"; - } - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { - RTC_LOG(LS_INFO) << "Set WebRTC-TurnAddMultiMapping: Enabled"; - } - - webrtc::StructParametersParser::Create( - // go/skylift-light - "skip_relay_to_non_relay_connections", - &field_trials_.skip_relay_to_non_relay_connections, - // Limiting pings sent. - "max_outstanding_pings", &field_trials_.max_outstanding_pings, - // Delay initial selection of connection. - "initial_select_dampening", &field_trials_.initial_select_dampening, - // Delay initial selection of connections, that are receiving. - "initial_select_dampening_ping_received", - &field_trials_.initial_select_dampening_ping_received, - // Reply that we support goog ping. - "announce_goog_ping", &field_trials_.announce_goog_ping, - // Use goog ping if remote support it. - "enable_goog_ping", &field_trials_.enable_goog_ping, - // How fast does a RTT sample decay. - "rtt_estimate_halftime_ms", &field_trials_.rtt_estimate_halftime_ms, - // Make sure that nomination reaching ICE controlled asap. - "send_ping_on_switch_ice_controlling", - &field_trials_.send_ping_on_switch_ice_controlling, - // Make sure that nomination reaching ICE controlled asap. - "send_ping_on_selected_ice_controlling", - &field_trials_.send_ping_on_selected_ice_controlling, - // Reply to nomination ASAP. - "send_ping_on_nomination_ice_controlled", - &field_trials_.send_ping_on_nomination_ice_controlled, - // Allow connections to live untouched longer that 30s. - "dead_connection_timeout_ms", &field_trials_.dead_connection_timeout_ms, - // Stop gathering on strongly connected. - "stop_gather_on_strongly_connected", - &field_trials_.stop_gather_on_strongly_connected) - ->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials")); - - if (field_trials_.dead_connection_timeout_ms < 30000) { - RTC_LOG(LS_WARNING) << "dead_connection_timeout_ms set to " - << field_trials_.dead_connection_timeout_ms - << " increasing it to 30000"; - field_trials_.dead_connection_timeout_ms = 30000; - } - - if (field_trials_.skip_relay_to_non_relay_connections) { - RTC_LOG(LS_INFO) << "Set skip_relay_to_non_relay_connections"; - } - - if (field_trials_.max_outstanding_pings.has_value()) { - RTC_LOG(LS_INFO) << "Set max_outstanding_pings: " - << *field_trials_.max_outstanding_pings; - } - - if (field_trials_.initial_select_dampening.has_value()) { - RTC_LOG(LS_INFO) << "Set initial_select_dampening: " - << *field_trials_.initial_select_dampening; - } - - if (field_trials_.initial_select_dampening_ping_received.has_value()) { - RTC_LOG(LS_INFO) << "Set initial_select_dampening_ping_received: " - << *field_trials_.initial_select_dampening_ping_received; - } - webrtc::BasicRegatheringController::Config regathering_config; regathering_config.regather_on_failed_networks_interval = config_.regather_on_failed_networks_interval_or_default(); @@ -846,6 +750,107 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { RTC_DCHECK(ValidateIceConfig(config_).ok()); } +void P2PTransportChannel::ParseFieldTrials( + const webrtc::WebRtcKeyValueConfig* field_trials) { + if (field_trials == nullptr) { + return; + } + + if (field_trials->IsEnabled("WebRTC-ExtraICEPing")) { + RTC_LOG(LS_INFO) << "Set WebRTC-ExtraICEPing: Enabled"; + } + if (field_trials->IsEnabled("WebRTC-TurnAddMultiMapping")) { + RTC_LOG(LS_INFO) << "Set WebRTC-TurnAddMultiMapping: Enabled"; + } + + webrtc::StructParametersParser::Create( + // go/skylift-light + "skip_relay_to_non_relay_connections", + &ice_field_trials_.skip_relay_to_non_relay_connections, + // Limiting pings sent. + "max_outstanding_pings", &ice_field_trials_.max_outstanding_pings, + // Delay initial selection of connection. + "initial_select_dampening", &ice_field_trials_.initial_select_dampening, + // Delay initial selection of connections, that are receiving. + "initial_select_dampening_ping_received", + &ice_field_trials_.initial_select_dampening_ping_received, + // Reply that we support goog ping. + "announce_goog_ping", &ice_field_trials_.announce_goog_ping, + // Use goog ping if remote support it. + "enable_goog_ping", &ice_field_trials_.enable_goog_ping, + // How fast does a RTT sample decay. + "rtt_estimate_halftime_ms", &ice_field_trials_.rtt_estimate_halftime_ms, + // Make sure that nomination reaching ICE controlled asap. + "send_ping_on_switch_ice_controlling", + &ice_field_trials_.send_ping_on_switch_ice_controlling, + // Make sure that nomination reaching ICE controlled asap. + "send_ping_on_selected_ice_controlling", + &ice_field_trials_.send_ping_on_selected_ice_controlling, + // Reply to nomination ASAP. + "send_ping_on_nomination_ice_controlled", + &ice_field_trials_.send_ping_on_nomination_ice_controlled, + // Allow connections to live untouched longer that 30s. + "dead_connection_timeout_ms", + &ice_field_trials_.dead_connection_timeout_ms, + // Stop gathering on strongly connected. + "stop_gather_on_strongly_connected", + &ice_field_trials_.stop_gather_on_strongly_connected) + ->Parse(field_trials->Lookup("WebRTC-IceFieldTrials")); + + if (ice_field_trials_.dead_connection_timeout_ms < 30000) { + RTC_LOG(LS_WARNING) << "dead_connection_timeout_ms set to " + << ice_field_trials_.dead_connection_timeout_ms + << " increasing it to 30000"; + ice_field_trials_.dead_connection_timeout_ms = 30000; + } + + if (ice_field_trials_.skip_relay_to_non_relay_connections) { + RTC_LOG(LS_INFO) << "Set skip_relay_to_non_relay_connections"; + } + + if (ice_field_trials_.max_outstanding_pings.has_value()) { + RTC_LOG(LS_INFO) << "Set max_outstanding_pings: " + << *ice_field_trials_.max_outstanding_pings; + } + + if (ice_field_trials_.initial_select_dampening.has_value()) { + RTC_LOG(LS_INFO) << "Set initial_select_dampening: " + << *ice_field_trials_.initial_select_dampening; + } + + if (ice_field_trials_.initial_select_dampening_ping_received.has_value()) { + RTC_LOG(LS_INFO) + << "Set initial_select_dampening_ping_received: " + << *ice_field_trials_.initial_select_dampening_ping_received; + } + + // DSCP override, allow user to specify (any) int value + // that will be used for tagging all packets. + webrtc::StructParametersParser::Create("override_dscp", + &ice_field_trials_.override_dscp) + ->Parse(field_trials->Lookup("WebRTC-DscpFieldTrial")); + + if (ice_field_trials_.override_dscp) { + SetOption(rtc::Socket::OPT_DSCP, *ice_field_trials_.override_dscp); + } + + std::string field_trial_string = + field_trials->Lookup("WebRTC-SetSocketReceiveBuffer"); + int receive_buffer_size_kb = 0; + sscanf(field_trial_string.c_str(), "Enabled-%d", &receive_buffer_size_kb); + if (receive_buffer_size_kb > 0) { + RTC_LOG(LS_INFO) << "Set WebRTC-SetSocketReceiveBuffer: Enabled and set to " + << receive_buffer_size_kb << "kb"; + SetOption(rtc::Socket::OPT_RCVBUF, receive_buffer_size_kb * 1024); + } + + ice_field_trials_.piggyback_ice_check_acknowledgement = + field_trials->IsEnabled("WebRTC-PiggybackIceCheckAcknowledgement"); + + ice_field_trials_.extra_ice_ping = + field_trials->IsEnabled("WebRTC-ExtraICEPing"); +} + const IceConfig& P2PTransportChannel::config() const { RTC_DCHECK_RUN_ON(network_thread_); return config_; @@ -857,7 +862,7 @@ const IceConfig& P2PTransportChannel::config() const { RTCError P2PTransportChannel::ValidateIceConfig(const IceConfig& config) { if (config.ice_check_interval_strong_connectivity_or_default() < config.ice_check_interval_weak_connectivity.value_or( - GetWeakPingIntervalInFieldTrial())) { + GetWeakPingIntervalInFieldTrial(nullptr))) { return RTCError(RTCErrorType::INVALID_PARAMETER, "Ping interval of candidate pairs is shorter when ICE is " "strongly connected than that when ICE is weakly " @@ -1256,7 +1261,7 @@ void P2PTransportChannel::OnUnknownAddress(PortInterface* port, << remote_candidate.ToSensitiveString(); return; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, STUN_ERROR_REASON_SERVER_ERROR); return; @@ -1331,7 +1336,8 @@ void P2PTransportChannel::OnNominated(Connection* conn) { return; } - if (field_trials_.send_ping_on_nomination_ice_controlled && conn != nullptr) { + if (ice_field_trials_.send_ping_on_nomination_ice_controlled && + conn != nullptr) { PingConnection(conn); MarkConnectionPinged(conn); } @@ -1435,7 +1441,7 @@ void P2PTransportChannel::OnCandidateResolved( }); if (p == resolvers_.end()) { RTC_LOG(LS_ERROR) << "Unexpected AsyncDnsResolver return"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } Candidate candidate = p->candidate_; @@ -1574,7 +1580,7 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port, return false; } - if (field_trials_.skip_relay_to_non_relay_connections) { + if (ice_field_trials_.skip_relay_to_non_relay_connections) { if ((port->Type() != remote_candidate.type()) && (port->Type() == RELAY_PORT_TYPE || remote_candidate.type() == RELAY_PORT_TYPE)) { @@ -1611,11 +1617,11 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port, // It is not legal to try to change any of the parameters of an existing // connection; however, the other side can send a duplicate candidate. if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { - RTC_LOG(INFO) << "Attempt to change a remote candidate." - " Existing remote candidate: " - << connection->remote_candidate().ToSensitiveString() - << "New remote candidate: " - << remote_candidate.ToSensitiveString(); + RTC_LOG(LS_INFO) << "Attempt to change a remote candidate." + " Existing remote candidate: " + << connection->remote_candidate().ToSensitiveString() + << "New remote candidate: " + << remote_candidate.ToSensitiveString(); } return false; } @@ -1667,8 +1673,8 @@ void P2PTransportChannel::RememberRemoteCandidate( size_t i = 0; while (i < remote_candidates_.size()) { if (remote_candidates_[i].generation() < remote_candidate.generation()) { - RTC_LOG(INFO) << "Pruning candidate from old generation: " - << remote_candidates_[i].address().ToSensitiveString(); + RTC_LOG(LS_INFO) << "Pruning candidate from old generation: " + << remote_candidates_[i].address().ToSensitiveString(); remote_candidates_.erase(remote_candidates_.begin() + i); } else { i += 1; @@ -1677,8 +1683,8 @@ void P2PTransportChannel::RememberRemoteCandidate( // Make sure this candidate is not a duplicate. if (IsDuplicateRemoteCandidate(remote_candidate)) { - RTC_LOG(INFO) << "Duplicate candidate: " - << remote_candidate.ToSensitiveString(); + RTC_LOG(LS_INFO) << "Duplicate candidate: " + << remote_candidate.ToSensitiveString(); return; } @@ -1690,6 +1696,10 @@ void P2PTransportChannel::RememberRemoteCandidate( // port objects. int P2PTransportChannel::SetOption(rtc::Socket::Option opt, int value) { RTC_DCHECK_RUN_ON(network_thread_); + if (ice_field_trials_.override_dscp && opt == rtc::Socket::OPT_DSCP) { + value = *ice_field_trials_.override_dscp; + } + OptionMap::iterator it = options_.find(opt); if (it == options_.end()) { options_.insert(std::make_pair(opt, value)); @@ -1704,8 +1714,8 @@ int P2PTransportChannel::SetOption(rtc::Socket::Option opt, int value) { if (val < 0) { // Because this also occurs deferred, probably no point in reporting an // error - RTC_LOG(WARNING) << "SetOption(" << opt << ", " << value - << ") failed: " << port->GetError(); + RTC_LOG(LS_WARNING) << "SetOption(" << opt << ", " << value + << ") failed: " << port->GetError(); } } return 0; @@ -1744,6 +1754,7 @@ int P2PTransportChannel::SendPacket(const char* data, return -1; } + packets_sent_++; last_sent_packet_id_ = options.packet_id; rtc::PacketOptions modified_options(options); modified_options.info_signaled_after_sent.packet_type = @@ -1752,7 +1763,10 @@ int P2PTransportChannel::SendPacket(const char* data, if (sent <= 0) { RTC_DCHECK(sent < 0); error_ = selected_connection_->GetError(); + return sent; } + + bytes_sent_ += sent; return sent; } @@ -1779,6 +1793,11 @@ bool P2PTransportChannel::GetStats(IceTransportStats* ice_transport_stats) { ice_transport_stats->selected_candidate_pair_changes = selected_candidate_pair_changes_; + + ice_transport_stats->bytes_sent = bytes_sent_; + ice_transport_stats->bytes_received = bytes_received_; + ice_transport_stats->packets_sent = packets_sent_; + ice_transport_stats->packets_received = packets_received_; return true; } @@ -1803,6 +1822,17 @@ rtc::ArrayView P2PTransportChannel::connections() const { res.size()); } +void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(FindConnection(connection)); + connection->SignalDestroyed.disconnect(this); + ice_controller_->OnConnectionDestroyed(connection); + RTC_DCHECK(!FindConnection(connection)); + if (selected_connection_ == connection) + selected_connection_ = nullptr; + connection->Destroy(); +} + // Monitor connection states. void P2PTransportChannel::UpdateConnectionStates() { RTC_DCHECK_RUN_ON(network_thread_); @@ -1810,7 +1840,11 @@ void P2PTransportChannel::UpdateConnectionStates() { // We need to copy the list of connections since some may delete themselves // when we call UpdateState. - for (Connection* c : connections()) { + // NOTE: We copy the connections() vector in case `UpdateState` triggers the + // Connection to be destroyed (which will cause a callback that alters + // the connections() vector). + std::vector copy(connections().begin(), connections().end()); + for (Connection* c : copy) { c->UpdateState(now); } } @@ -1982,9 +2016,9 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn, } if (conn != nullptr && ice_role_ == ICEROLE_CONTROLLING && - ((field_trials_.send_ping_on_switch_ice_controlling && + ((ice_field_trials_.send_ping_on_switch_ice_controlling && old_selected_connection != nullptr) || - field_trials_.send_ping_on_selected_ice_controlling)) { + ice_field_trials_.send_ping_on_selected_ice_controlling)) { PingConnection(conn); MarkConnectionPinged(conn); } @@ -2088,7 +2122,7 @@ void P2PTransportChannel::UpdateState() { state == IceTransportState::STATE_COMPLETED); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } state_ = state; @@ -2121,12 +2155,31 @@ void P2PTransportChannel::MaybeStopPortAllocatorSessions() { } } +// RTC_RUN_ON(network_thread_) +void P2PTransportChannel::OnSelectedConnectionDestroyed() { + RTC_LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one."; + IceControllerEvent reason = IceControllerEvent::SELECTED_CONNECTION_DESTROYED; + SwitchSelectedConnection(nullptr, reason); + RequestSortAndStateUpdate(reason); +} + // If all connections timed out, delete them all. void P2PTransportChannel::HandleAllTimedOut() { RTC_DCHECK_RUN_ON(network_thread_); - for (Connection* connection : connections()) { + bool update_selected_connection = false; + std::vector copy(connections().begin(), connections().end()); + for (Connection* connection : copy) { + if (selected_connection_ == connection) { + selected_connection_ = nullptr; + update_selected_connection = true; + } + connection->SignalDestroyed.disconnect(this); + ice_controller_->OnConnectionDestroyed(connection); connection->Destroy(); } + + if (update_selected_connection) + OnSelectedConnectionDestroyed(); } bool P2PTransportChannel::ReadyToSend(Connection* connection) const { @@ -2224,7 +2277,7 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { // the connection is at the latest generation. It is not enough to check // that the connection becomes weakly connected because the connection may be // changing from (writable, receiving) to (writable, not receiving). - if (field_trials_.stop_gather_on_strongly_connected) { + if (ice_field_trials_.stop_gather_on_strongly_connected) { bool strongly_connected = !connection->weak(); bool latest_generation = connection->local_candidate().generation() >= allocator_session()->generation(); @@ -2260,11 +2313,7 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { // we can just set selected to nullptr and re-choose a best assuming that // there was no selected connection. if (selected_connection_ == connection) { - RTC_LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one."; - IceControllerEvent reason = - IceControllerEvent::SELECTED_CONNECTION_DESTROYED; - SwitchSelectedConnection(nullptr, reason); - RequestSortAndStateUpdate(reason); + OnSelectedConnectionDestroyed(); } else { // If a non-selected connection was destroyed, we don't need to re-sort but // we do need to update state, because we could be switching to "failed" or @@ -2282,8 +2331,8 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) { pruned_ports_.erase( std::remove(pruned_ports_.begin(), pruned_ports_.end(), port), pruned_ports_.end()); - RTC_LOG(INFO) << "Removed port because it is destroyed: " << ports_.size() - << " remaining"; + RTC_LOG(LS_INFO) << "Removed port because it is destroyed: " << ports_.size() + << " remaining"; } void P2PTransportChannel::OnPortsPruned( @@ -2292,8 +2341,8 @@ void P2PTransportChannel::OnPortsPruned( RTC_DCHECK_RUN_ON(network_thread_); for (PortInterface* port : ports) { if (PrunePort(port)) { - RTC_LOG(INFO) << "Removed port: " << port->ToString() << " " - << ports_.size() << " remaining"; + RTC_LOG(LS_INFO) << "Removed port: " << port->ToString() << " " + << ports_.size() << " remaining"; } } } @@ -2344,6 +2393,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, if (connection == selected_connection_) { // Let the client know of an incoming packet + packets_received_++; + bytes_received_ += len; RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_); last_data_received_ms_ = std::max(last_data_received_ms_, connection->last_data_received()); @@ -2355,6 +2406,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, if (!FindConnection(connection)) return; + packets_received_++; + bytes_received_ += len; RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_); last_data_received_ms_ = std::max(last_data_received_ms_, connection->last_data_received()); diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h index c0390001d4..d2c4338728 100644 --- a/p2p/base/p2p_transport_channel.h +++ b/p2p/base/p2p_transport_channel.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "absl/base/attributes.h" @@ -36,12 +37,14 @@ #include "api/async_dns_resolver.h" #include "api/async_resolver_factory.h" #include "api/candidate.h" +#include "api/ice_transport_interface.h" #include "api/rtc_error.h" #include "api/sequence_checker.h" #include "api/transport/enums.h" #include "api/transport/stun.h" #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" #include "logging/rtc_event_log/ice_logger.h" +#include "p2p/base/basic_async_resolver_factory.h" #include "p2p/base/candidate_pair_interface.h" #include "p2p/base/connection.h" #include "p2p/base/ice_controller_factory_interface.h" @@ -56,7 +59,6 @@ #include "p2p/base/transport_description.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/dscp.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" @@ -105,25 +107,21 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { static std::unique_ptr Create( const std::string& transport_name, int component, - PortAllocator* allocator, - webrtc::AsyncDnsResolverFactoryInterface* async_dns_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + webrtc::IceTransportInit init); + // For testing only. // TODO(zstein): Remove once AsyncDnsResolverFactory is required. - P2PTransportChannel(const std::string& transport_name, - int component, - PortAllocator* allocator); - ABSL_DEPRECATED("bugs.webrtc.org/12598") P2PTransportChannel( const std::string& transport_name, int component, PortAllocator* allocator, - webrtc::AsyncResolverFactory* async_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr); + ~P2PTransportChannel() override; + P2PTransportChannel(const P2PTransportChannel&) = delete; + P2PTransportChannel& operator=(const P2PTransportChannel&) = delete; + // From TransportChannelImpl: IceTransportState GetState() const override; webrtc::IceTransportState GetIceTransportState() const override; @@ -217,6 +215,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { // Public for unit tests. rtc::ArrayView connections() const; + void RemoveConnectionForTest(Connection* connection); // Public for unit tests. PortAllocatorSession* allocator_session() const { @@ -260,8 +259,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { // on release, this pointer is set. std::unique_ptr owned_dns_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + webrtc::RtcEventLog* event_log, + IceControllerFactoryInterface* ice_controller_factory, + const webrtc::WebRtcKeyValueConfig* field_trials); bool IsGettingPorts() { RTC_DCHECK_RUN_ON(network_thread_); return allocator_session()->IsGettingPorts(); @@ -283,6 +283,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { void UpdateState(); void HandleAllTimedOut(); void MaybeStopPortAllocatorSessions(); + void OnSelectedConnectionDestroyed() RTC_RUN_ON(network_thread_); // ComputeIceTransportState computes the RTCIceTransportState as described in // https://w3c.github.io/webrtc-pc/#dom-rtcicetransportstate. ComputeState @@ -433,6 +434,8 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { int64_t ComputeEstimatedDisconnectedTimeMs(int64_t now, Connection* old_connection); + void ParseFieldTrials(const webrtc::WebRtcKeyValueConfig* field_trials); + webrtc::ScopedTaskSafety task_safety_; std::string transport_name_ RTC_GUARDED_BY(network_thread_); int component_ RTC_GUARDED_BY(network_thread_); @@ -522,6 +525,12 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { Candidate candidate, const webrtc::AsyncDnsResolverResult& result); + // Bytes/packets sent/received on this channel. + uint64_t bytes_sent_ = 0; + uint64_t bytes_received_ = 0; + uint64_t packets_sent_ = 0; + uint64_t packets_received_ = 0; + // Number of times the selected_connection_ has been modified. uint32_t selected_candidate_pair_changes_ = 0; @@ -529,9 +538,8 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { // from connection->last_data_received() that uses rtc::TimeMillis(). int64_t last_data_received_ms_ = 0; - IceFieldTrials field_trials_; - - RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel); + // Parsed field trials. + IceFieldTrials ice_field_trials_; }; } // namespace cricket diff --git a/p2p/base/p2p_transport_channel_ice_field_trials.h b/p2p/base/p2p_transport_channel_ice_field_trials.h index 4987f1cbcb..f19823b21e 100644 --- a/p2p/base/p2p_transport_channel_ice_field_trials.h +++ b/p2p/base/p2p_transport_channel_ice_field_trials.h @@ -19,6 +19,9 @@ namespace cricket { // put in separate file so that they can be shared e.g // with Connection. struct IceFieldTrials { + // This struct is built using the FieldTrialParser, and then not modified. + // TODO(jonaso) : Consider how members of this struct can be made const. + bool skip_relay_to_non_relay_connections = false; absl::optional max_outstanding_pings; @@ -61,6 +64,12 @@ struct IceFieldTrials { // Stop gathering when having a strong connection. bool stop_gather_on_strongly_connected = true; + + // DSCP taging. + absl::optional override_dscp; + + bool piggyback_ice_check_acknowledgement = false; + bool extra_ice_ping = false; }; } // namespace cricket diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc index 7ff37e74ef..f663bbdffc 100644 --- a/p2p/base/p2p_transport_channel_unittest.cc +++ b/p2p/base/p2p_transport_channel_unittest.cc @@ -43,7 +43,7 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" +#include "test/scoped_key_value_config.h" namespace { @@ -266,7 +266,7 @@ class P2PTransportChannelTestBase : public ::testing::Test, ss_(new rtc::FirewallSocketServer(nss_.get())), main_(ss_.get()), stun_server_(TestStunServer::Create(ss_.get(), kStunAddr)), - turn_server_(&main_, kTurnUdpIntAddr, kTurnUdpExtAddr), + turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), socks_server1_(ss_.get(), kSocksProxyAddrs[0], ss_.get(), @@ -452,9 +452,13 @@ class P2PTransportChannelTestBase : public ::testing::Test, int component, const IceParameters& local_ice, const IceParameters& remote_ice) { - auto channel = P2PTransportChannel::Create( - "test content name", component, GetAllocator(endpoint), + webrtc::IceTransportInit init; + init.set_port_allocator(GetAllocator(endpoint)); + init.set_async_dns_resolver_factory( GetEndpoint(endpoint)->async_dns_resolver_factory_); + init.set_field_trials(&field_trials_); + auto channel = P2PTransportChannel::Create("test content name", component, + std::move(init)); channel->SignalReadyToSend.connect( this, &P2PTransportChannelTestBase::OnReadyToSend); channel->SignalCandidateGathered.connect( @@ -484,6 +488,9 @@ class P2PTransportChannelTestBase : public ::testing::Test, ep2_.cd1_.ch_.reset(); ep1_.cd2_.ch_.reset(); ep2_.cd2_.ch_.reset(); + // Process pending tasks that need to run for cleanup purposes such as + // pending deletion of Connection objects (see Connection::Destroy). + rtc::Thread::Current()->ProcessMessages(0); } P2PTransportChannel* ep1_ch1() { return ep1_.cd1_.ch_.get(); } P2PTransportChannel* ep1_ch2() { return ep1_.cd2_.ch_.get(); } @@ -992,6 +999,8 @@ class P2PTransportChannelTestBase : public ::testing::Test, void OnNominated(Connection* conn) { nominated_ = true; } bool nominated() { return nominated_; } + webrtc::test::ScopedKeyValueConfig field_trials_; + private: std::unique_ptr vss_; std::unique_ptr nss_; @@ -1179,7 +1188,7 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } @@ -1255,7 +1264,7 @@ class P2PTransportChannelTestWithFieldTrials public ::testing::WithParamInterface { public: void Test(const Result& expected) override { - webrtc::test::ScopedFieldTrials field_trials(GetParam()); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, GetParam()); P2PTransportChannelTest::Test(expected); } }; @@ -1339,6 +1348,13 @@ TEST_F(P2PTransportChannelTest, GetStats) { kMediumTimeout, clock); // Sends and receives 10 packets. TestSendRecv(&clock); + + // Try sending a packet which is discarded due to the socket being blocked. + virtual_socket_server()->SetSendingBlocked(true); + const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + int len = static_cast(strlen(data)); + EXPECT_EQ(-1, SendData(ep1_ch1(), data, len)); + IceTransportStats ice_transport_stats; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); ASSERT_GE(ice_transport_stats.connection_infos.size(), 1u); @@ -1356,11 +1372,89 @@ TEST_F(P2PTransportChannelTest, GetStats) { EXPECT_TRUE(best_conn_info->receiving); EXPECT_TRUE(best_conn_info->writable); EXPECT_FALSE(best_conn_info->timeout); - EXPECT_EQ(10U, best_conn_info->sent_total_packets); - EXPECT_EQ(0U, best_conn_info->sent_discarded_packets); + // Note that discarded packets are counted in sent_total_packets but not + // sent_total_bytes. + EXPECT_EQ(11U, best_conn_info->sent_total_packets); + EXPECT_EQ(1U, best_conn_info->sent_discarded_packets); EXPECT_EQ(10 * 36U, best_conn_info->sent_total_bytes); + EXPECT_EQ(36U, best_conn_info->sent_discarded_bytes); EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes); EXPECT_EQ(10U, best_conn_info->packets_received); + + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_sent); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_received); + + DestroyChannels(); +} + +TEST_F(P2PTransportChannelTest, GetStatsSwitchConnection) { + rtc::ScopedFakeClock clock; + IceConfig continual_gathering_config = + CreateIceConfig(1000, GATHER_CONTINUALLY); + + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); + + AddAddress(0, kAlternateAddrs[1], "rmnet0", rtc::ADAPTER_TYPE_CELLULAR); + + CreateChannels(continual_gathering_config, continual_gathering_config); + EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && + ep2_ch1()->receiving() && + ep2_ch1()->writable(), + kMediumTimeout, clock); + // Sends and receives 10 packets. + TestSendRecv(&clock); + + IceTransportStats ice_transport_stats; + ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); + ASSERT_GE(ice_transport_stats.connection_infos.size(), 2u); + ASSERT_GE(ice_transport_stats.candidate_stats_list.size(), 2u); + EXPECT_EQ(ice_transport_stats.selected_candidate_pair_changes, 1u); + + ConnectionInfo* best_conn_info = nullptr; + for (ConnectionInfo& info : ice_transport_stats.connection_infos) { + if (info.best_connection) { + best_conn_info = &info; + break; + } + } + ASSERT_TRUE(best_conn_info != nullptr); + EXPECT_TRUE(best_conn_info->new_connection); + EXPECT_TRUE(best_conn_info->receiving); + EXPECT_TRUE(best_conn_info->writable); + EXPECT_FALSE(best_conn_info->timeout); + + EXPECT_EQ(10 * 36U, best_conn_info->sent_total_bytes); + EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_sent); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_received); + + auto old_selected_connection = ep1_ch1()->selected_connection(); + ep1_ch1()->RemoveConnectionForTest( + const_cast(old_selected_connection)); + + EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() != nullptr, + kMediumTimeout, clock); + + // Sends and receives 10 packets. + TestSendRecv(&clock); + + IceTransportStats ice_transport_stats2; + ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats2)); + + int64_t sum_bytes_sent = 0; + int64_t sum_bytes_received = 0; + for (ConnectionInfo& info : ice_transport_stats.connection_infos) { + sum_bytes_sent += info.sent_total_bytes; + sum_bytes_received += info.recv_total_bytes; + } + + EXPECT_EQ(10 * 36U, sum_bytes_sent); + EXPECT_EQ(10 * 36U, sum_bytes_received); + + EXPECT_EQ(20 * 36U, ice_transport_stats2.bytes_sent); + EXPECT_EQ(20 * 36U, ice_transport_stats2.bytes_received); + DestroyChannels(); } @@ -2336,8 +2430,8 @@ TEST_F(P2PTransportChannelTest, // acknowledgement in the connectivity check from the remote peer. TEST_F(P2PTransportChannelTest, CanConnectWithPiggybackCheckAcknowledgementWhenCheckResponseBlocked) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/"); rtc::ScopedFakeClock clock; ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); IceConfig ep1_config; @@ -2455,10 +2549,12 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTestBase { void DestroyAllButBestConnection(P2PTransportChannel* channel) { const Connection* selected_connection = channel->selected_connection(); - for (Connection* conn : channel->connections()) { - if (conn != selected_connection) { - conn->Destroy(); - } + // Copy the list of connections since the original will be modified. + rtc::ArrayView view = channel->connections(); + std::vector connections(view.begin(), view.end()); + for (Connection* conn : connections) { + if (conn != selected_connection) + channel->RemoveConnectionForTest(conn); } } }; @@ -3489,6 +3585,8 @@ class P2PTransportChannelPingTest : public ::testing::Test, } } + rtc::SocketServer* ss() const { return vss_.get(); } + private: std::unique_ptr vss_; rtc::AutoSocketServerThread thread_; @@ -3818,7 +3916,7 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { // Wait for conn2 to be selected. EXPECT_EQ_WAIT(conn2, ch.selected_connection(), kMediumTimeout); // Destroy the connection to test SignalUnknownAddress. - conn1->Destroy(); + ch.RemoveConnectionForTest(conn1); EXPECT_TRUE_WAIT(GetConnectionTo(&ch, "1.1.1.1", 1) == nullptr, kMediumTimeout); @@ -3966,10 +4064,10 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // that sends a ping directly when a connection has been nominated // i.e on the ICE_CONTROLLED-side. TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_nomination_ice_controlled:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLED); @@ -4006,10 +4104,10 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { // that sends a ping directly when switching to a new connection // on the ICE_CONTROLLING-side. TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_switch_ice_controlling:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4043,10 +4141,10 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { // that sends a ping directly when selecteing a new connection // on the ICE_CONTROLLING-side (i.e also initial selection). TEST_F(P2PTransportChannelPingTest, TestPingOnSelected) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_selected_ice_controlling:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4790,10 +4888,10 @@ TEST_F(P2PTransportChannelPingTest, TestPortDestroyedAfterTimeoutAndPruned) { } TEST_F(P2PTransportChannelPingTest, TestMaxOutstandingPingsFieldTrial) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/max_outstanding_pings:3/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("max", 1, &pa); + P2PTransportChannel ch("max", 1, &pa, &field_trials); ch.SetIceConfig(ch.config()); PrepareChannel(&ch); ch.MaybeStartGathering(); @@ -4816,7 +4914,10 @@ class P2PTransportChannelMostLikelyToWorkFirstTest : public P2PTransportChannelPingTest { public: P2PTransportChannelMostLikelyToWorkFirstTest() - : turn_server_(rtc::Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr) { + : turn_server_(rtc::Thread::Current(), + ss(), + kTurnUdpIntAddr, + kTurnUdpExtAddr) { network_manager_.AddInterface(kPublicAddrs[0]); allocator_.reset( CreateBasicPortAllocator(&network_manager_, ServerAddresses(), @@ -4828,8 +4929,10 @@ class P2PTransportChannelMostLikelyToWorkFirstTest P2PTransportChannel& StartTransportChannel( bool prioritize_most_likely_to_work, - int stable_writable_connection_ping_interval) { - channel_.reset(new P2PTransportChannel("checks", 1, allocator())); + int stable_writable_connection_ping_interval, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) { + channel_.reset( + new P2PTransportChannel("checks", 1, allocator(), field_trials)); IceConfig config = channel_->config(); config.prioritize_most_likely_candidate_pairs = prioritize_most_likely_to_work; @@ -4996,9 +5099,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // I.e that we never create connection between relay and non-relay. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestSkipRelayToNonRelayConnectionsFieldTrial) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"); - P2PTransportChannel& ch = StartTransportChannel(true, 500); + P2PTransportChannel& ch = StartTransportChannel(true, 500, &field_trials); EXPECT_TRUE_WAIT(ch.ports().size() == 2, kDefaultTimeout); EXPECT_EQ(ch.ports()[0]->Type(), LOCAL_PORT_TYPE); EXPECT_EQ(ch.ports()[1]->Type(), RELAY_PORT_TYPE); @@ -5049,8 +5152,10 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) { TEST(P2PTransportChannelResolverTest, HostnameCandidateIsResolved) { ResolverFactoryFixture resolver_fixture; FakePortAllocator allocator(rtc::Thread::Current(), nullptr); - auto channel = - P2PTransportChannel::Create("tn", 0, &allocator, &resolver_fixture); + webrtc::IceTransportInit init; + init.set_port_allocator(&allocator); + init.set_async_dns_resolver_factory(&resolver_fixture); + auto channel = P2PTransportChannel::Create("tn", 0, std::move(init)); Candidate hostname_candidate; SocketAddress hostname_address("fake.test", 1000); hostname_candidate.set_address(hostname_address); @@ -5812,7 +5917,8 @@ TEST_F(P2PTransportChannelTest, // i.e surface_ice_candidates_on_ice_transport_type_changed requires // coordination outside of webrtc to function properly. TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"); rtc::ScopedFakeClock clock; @@ -5876,7 +5982,7 @@ TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening:0/"); constexpr int kMargin = 10; @@ -5884,7 +5990,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5900,7 +6006,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening:100/"); constexpr int kMargin = 10; @@ -5908,7 +6014,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5924,7 +6030,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening_ping_received:100/"); constexpr int kMargin = 10; @@ -5932,7 +6038,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5949,7 +6055,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningBoth) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/" "initial_select_dampening:100,initial_select_dampening_ping_received:" "50/"); @@ -5959,7 +6065,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningBoth) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5980,11 +6086,12 @@ TEST(P2PTransportChannel, InjectIceController) { MockIceControllerFactory factory; FakePortAllocator pa(rtc::Thread::Current(), nullptr); EXPECT_CALL(factory, RecordIceControllerCreated()).Times(1); - auto dummy = std::make_unique( - "transport_name", - /* component= */ 77, &pa, - /* async_resolver_factory = */ nullptr, - /* event_log = */ nullptr, &factory); + webrtc::IceTransportInit init; + init.set_port_allocator(&pa); + init.set_ice_controller_factory(&factory); + auto dummy = + P2PTransportChannel::Create("transport_name", + /* component= */ 77, std::move(init)); } TEST_F(P2PTransportChannelPingTest, Forking) { @@ -6112,8 +6219,12 @@ class ForgetLearnedStateControllerFactory TEST_F(P2PTransportChannelPingTest, TestForgetLearnedState) { ForgetLearnedStateControllerFactory factory; FakePortAllocator pa(rtc::Thread::Current(), nullptr); - auto ch = P2PTransportChannel::Create("ping sufficiently", 1, &pa, nullptr, - nullptr, &factory); + webrtc::IceTransportInit init; + init.set_port_allocator(&pa); + init.set_ice_controller_factory(&factory); + auto ch = + P2PTransportChannel::Create("ping sufficiently", 1, std::move(init)); + PrepareChannel(ch.get()); ch->MaybeStartGathering(); ch->AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 1)); @@ -6230,7 +6341,7 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { const std::string field_trial = std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - webrtc::test::ScopedFieldTrials field_trials(field_trial); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, field_trial); rtc::ScopedFakeClock clock; // Use local + relay @@ -6291,7 +6402,7 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { const std::string field_trial = std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - webrtc::test::ScopedFieldTrials field_trials(field_trial); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, field_trial); rtc::ScopedFakeClock clock; // Use local + relay diff --git a/p2p/base/port.cc b/p2p/base/port.cc index 65f31b366a..6f5ec61d9b 100644 --- a/p2p/base/port.cc +++ b/p2p/base/port.cc @@ -19,6 +19,7 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "p2p/base/connection.h" #include "p2p/base/port_allocator.h" #include "rtc_base/checks.h" @@ -34,7 +35,6 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -194,8 +194,10 @@ Port::~Port() { ++iter; } - for (uint32_t i = 0; i < list.size(); i++) + for (uint32_t i = 0; i < list.size(); i++) { + list[i]->SignalDestroyed.disconnect(this); delete list[i]; + } } const std::string& Port::Type() const { @@ -274,6 +276,7 @@ void Port::AddAddress(const rtc::SocketAddress& address, c.set_tcptype(tcptype); c.set_network_name(network_->name()); c.set_network_type(network_->type()); + c.set_underlying_type_for_vpn(network_->underlying_type_for_vpn()); c.set_url(url); c.set_related_address(related_address); @@ -299,7 +302,7 @@ bool Port::MaybeObfuscateAddress(Candidate* c, auto copy = *c; auto weak_ptr = weak_factory_.GetWeakPtr(); auto callback = [weak_ptr, copy, is_final](const rtc::IPAddress& addr, - const std::string& name) mutable { + absl::string_view name) mutable { RTC_DCHECK(copy.address().ipaddr() == addr); rtc::SocketAddress hostname_address(name, copy.address().port()); // In Port and Connection, we need the IP address information to @@ -606,6 +609,15 @@ rtc::DiffServCodePoint Port::StunDscpValue() const { return rtc::DSCP_NO_CHANGE; } +void Port::DestroyAllConnections() { + RTC_DCHECK_RUN_ON(thread_); + for (auto kv : connections_) { + kv.second->SignalDestroyed.disconnect(this); + kv.second->Destroy(); + } + connections_.clear(); +} + void Port::set_timeout_delay(int delay) { RTC_DCHECK_RUN_ON(thread_); // Although this method is meant to only be used by tests, some downstream @@ -697,7 +709,7 @@ bool Port::MaybeIceRoleConflict(const rtc::SocketAddress& addr, } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return ret; } @@ -715,7 +727,7 @@ bool Port::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, size_t size, const rtc::SocketAddress& remote_addr, int64_t packet_time_us) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/p2p/base/port.h b/p2p/base/port.h index a72f5e3e4b..9aaffd60de 100644 --- a/p2p/base/port.h +++ b/p2p/base/port.h @@ -432,6 +432,8 @@ class Port : public PortInterface, // Extra work to be done in subclasses when a connection is destroyed. virtual void HandleConnectionDestroyed(Connection* conn) {} + void DestroyAllConnections(); + void CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const; MdnsNameRegistrationStatus mdns_name_registration_status() const { diff --git a/p2p/base/port_allocator.h b/p2p/base/port_allocator.h index fcc0682a43..1cc7d6cb33 100644 --- a/p2p/base/port_allocator.h +++ b/p2p/base/port_allocator.h @@ -600,17 +600,6 @@ class RTC_EXPORT PortAllocator : public sigslot::has_slots<> { return turn_port_prune_policy_; } - // Gets/Sets the Origin value used for WebRTC STUN requests. - const std::string& origin() const { - CheckRunOnValidThreadIfInitialized(); - return origin_; - } - - void set_origin(const std::string& origin) { - CheckRunOnValidThreadIfInitialized(); - origin_ = origin; - } - webrtc::TurnCustomizer* turn_customizer() { CheckRunOnValidThreadIfInitialized(); return turn_customizer_; diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc index 0dd5a58a66..4153dbe7ef 100644 --- a/p2p/base/port_unittest.cc +++ b/p2p/base/port_unittest.cc @@ -63,9 +63,9 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" -#include "test/field_trial.h" #include "test/gtest.h" +using rtc::AsyncListenSocket; using rtc::AsyncPacketSocket; using rtc::ByteBufferReader; using rtc::ByteBufferWriter; @@ -270,7 +270,13 @@ class TestChannel : public sigslot::has_slots<> { port_->SignalPortComplete.connect(this, &TestChannel::OnPortComplete); port_->SignalUnknownAddress.connect(this, &TestChannel::OnUnknownAddress); // RingRTC change to support ICE forking - port_->SignalDestroyed.connect(this, &TestChannel::OnPortDestroyed); + port_->SignalDestroyed.connect(this, &TestChannel::OnSrcPortDestroyed); + } + + ~TestChannel() { + if (conn_) { + conn_->SignalDestroyed.disconnect(this); + } } int complete_count() { return complete_count_; } @@ -283,7 +289,6 @@ class TestChannel : public sigslot::has_slots<> { conn_ = port_->CreateConnection(remote_candidate, Port::ORIGIN_MESSAGE); IceMode remote_ice_mode = (ice_mode_ == ICEMODE_FULL) ? ICEMODE_LITE : ICEMODE_FULL; - conn_->set_remote_ice_mode(remote_ice_mode); conn_->set_use_candidate_attr(remote_ice_mode == ICEMODE_FULL); conn_->SignalStateChange.connect(this, &TestChannel::OnConnectionStateChange); @@ -311,7 +316,9 @@ class TestChannel : public sigslot::has_slots<> { void Ping(int64_t now) { conn_->Ping(now); } void Stop() { if (conn_) { + conn_->SignalDestroyed.disconnect(this); conn_->Destroy(); + conn_ = nullptr; } } @@ -349,7 +356,7 @@ class TestChannel : public sigslot::has_slots<> { void OnDestroyed(Connection* conn) { ASSERT_EQ(conn_, conn); - RTC_LOG(INFO) << "OnDestroy connection " << conn << " deleted"; + RTC_LOG(LS_INFO) << "OnDestroy connection " << conn << " deleted"; conn_ = NULL; // When the connection is destroyed, also clear these fields so future // connections are possible. @@ -395,18 +402,25 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { PortTest() : ss_(new rtc::VirtualSocketServer()), main_(ss_.get()), - socket_factory_(rtc::Thread::Current()), + socket_factory_(ss_.get()), nat_factory1_(ss_.get(), kNatAddr1, SocketAddress()), nat_factory2_(ss_.get(), kNatAddr2, SocketAddress()), nat_socket_factory1_(&nat_factory1_), nat_socket_factory2_(&nat_factory2_), stun_server_(TestStunServer::Create(ss_.get(), kStunAddr)), - turn_server_(&main_, kTurnUdpIntAddr, kTurnUdpExtAddr), + turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), username_(rtc::CreateRandomString(ICE_UFRAG_LENGTH)), password_(rtc::CreateRandomString(ICE_PWD_LENGTH)), role_conflict_(false), ports_destroyed_(0) {} + ~PortTest() { + // Workaround for tests that trigger async destruction of objects that we + // need to give an opportunity here to run, before proceeding with other + // teardown. + rtc::Thread::Current()->ProcessMessages(0); + } + protected: std::string password() { return password_; } @@ -505,8 +519,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr CreateUdpPort(const SocketAddress& addr, PacketSocketFactory* socket_factory) { return UDPPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0, - username_, password_, std::string(), true, - absl::nullopt); + username_, password_, true, absl::nullopt); } std::unique_ptr CreateTcpPort(const SocketAddress& addr) { return CreateTcpPort(addr, &socket_factory_); @@ -521,8 +534,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { ServerAddresses stun_servers; stun_servers.insert(kStunAddr); return StunPort::Create(&main_, factory, MakeNetwork(addr), 0, 0, username_, - password_, stun_servers, std::string(), - absl::nullopt); + password_, stun_servers, absl::nullopt); } std::unique_ptr CreateRelayPort(const SocketAddress& addr, ProtocolType int_proto, @@ -547,7 +559,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { return TurnPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0, username_, password_, ProtocolAddress(server_addr, int_proto), - kRelayCredentials, 0, "", {}, {}, nullptr, nullptr); + kRelayCredentials, 0, {}, {}, nullptr, nullptr); } std::unique_ptr CreateNatServer(const SocketAddress& addr, rtc::NATType type) { @@ -970,12 +982,12 @@ class FakePacketSocketFactory : public rtc::PacketSocketFactory { return result; } - AsyncPacketSocket* CreateServerTcpSocket(const SocketAddress& local_address, + AsyncListenSocket* CreateServerTcpSocket(const SocketAddress& local_address, uint16_t min_port, uint16_t max_port, int opts) override { EXPECT_TRUE(next_server_tcp_socket_ != NULL); - AsyncPacketSocket* result = next_server_tcp_socket_; + AsyncListenSocket* result = next_server_tcp_socket_; next_server_tcp_socket_ = NULL; return result; } @@ -995,17 +1007,20 @@ class FakePacketSocketFactory : public rtc::PacketSocketFactory { void set_next_udp_socket(AsyncPacketSocket* next_udp_socket) { next_udp_socket_ = next_udp_socket; } - void set_next_server_tcp_socket(AsyncPacketSocket* next_server_tcp_socket) { + void set_next_server_tcp_socket(AsyncListenSocket* next_server_tcp_socket) { next_server_tcp_socket_ = next_server_tcp_socket; } void set_next_client_tcp_socket(AsyncPacketSocket* next_client_tcp_socket) { next_client_tcp_socket_ = next_client_tcp_socket; } - rtc::AsyncResolverInterface* CreateAsyncResolver() override { return NULL; } + std::unique_ptr CreateAsyncDnsResolver() + override { + return nullptr; + } private: AsyncPacketSocket* next_udp_socket_; - AsyncPacketSocket* next_server_tcp_socket_; + AsyncListenSocket* next_server_tcp_socket_; absl::optional next_client_tcp_socket_; }; @@ -1056,6 +1071,24 @@ class FakeAsyncPacketSocket : public AsyncPacketSocket { State state_; }; +class FakeAsyncListenSocket : public AsyncListenSocket { + public: + // 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 { return local_address_; } + void Bind(const SocketAddress& address) { + local_address_ = address; + state_ = State::kBound; + } + virtual int GetOption(Socket::Option opt, int* value) { return 0; } + virtual int SetOption(Socket::Option opt, int value) { return 0; } + virtual State GetState() const { return state_; } + + private: + SocketAddress local_address_; + State state_ = State::kClosed; +}; + // Local -> XXXX TEST_F(PortTest, TestLocalToLocal) { TestLocalToLocal(); @@ -1479,12 +1512,51 @@ TEST_F(PortTest, TestIceRoleConflict) { } TEST_F(PortTest, TestTcpNoDelay) { + rtc::ScopedFakeClock clock; auto port1 = CreateTcpPort(kLocalAddr1); port1->SetIceRole(cricket::ICEROLE_CONTROLLING); int option_value = -1; int success = port1->GetOption(rtc::Socket::OPT_NODELAY, &option_value); ASSERT_EQ(0, success); // GetOption() should complete successfully w/ 0 - ASSERT_EQ(1, option_value); + EXPECT_EQ(1, option_value); + + auto port2 = CreateTcpPort(kLocalAddr2); + port2->SetIceRole(cricket::ICEROLE_CONTROLLED); + + // Set up a connection, and verify that option is set on connected sockets at + // both ends. + TestChannel ch1(std::move(port1)); + TestChannel ch2(std::move(port2)); + // Acquire addresses. + ch1.Start(); + ch2.Start(); + ASSERT_EQ_SIMULATED_WAIT(1, ch1.complete_count(), kDefaultTimeout, clock); + ASSERT_EQ_SIMULATED_WAIT(1, ch2.complete_count(), kDefaultTimeout, clock); + // Connect and send a ping from src to dst. + ch1.CreateConnection(GetCandidate(ch2.port())); + ASSERT_TRUE(ch1.conn() != NULL); + EXPECT_TRUE_SIMULATED_WAIT(ch1.conn()->connected(), kDefaultTimeout, + clock); // for TCP connect + ch1.Ping(); + SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + + // Accept the connection. + ch2.AcceptConnection(GetCandidate(ch1.port())); + ASSERT_TRUE(ch2.conn() != NULL); + + option_value = -1; + success = static_cast(ch1.conn()) + ->socket() + ->GetOption(rtc::Socket::OPT_NODELAY, &option_value); + ASSERT_EQ(0, success); + EXPECT_EQ(1, option_value); + + option_value = -1; + success = static_cast(ch2.conn()) + ->socket() + ->GetOption(rtc::Socket::OPT_NODELAY, &option_value); + ASSERT_EQ(0, success); + EXPECT_EQ(1, option_value); } TEST_F(PortTest, TestDelayedBindingUdp) { @@ -1503,25 +1575,9 @@ TEST_F(PortTest, TestDelayedBindingUdp) { EXPECT_EQ(1U, port->Candidates().size()); } -TEST_F(PortTest, TestDelayedBindingTcp) { - FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket(); - FakePacketSocketFactory socket_factory; - - socket_factory.set_next_server_tcp_socket(socket); - auto port = CreateTcpPort(kLocalAddr1, &socket_factory); - - socket->set_state(AsyncPacketSocket::STATE_BINDING); - port->PrepareAddress(); - - EXPECT_EQ(0U, port->Candidates().size()); - socket->SignalAddressReady(socket, kLocalAddr2); - - EXPECT_EQ(1U, port->Candidates().size()); -} - TEST_F(PortTest, TestDisableInterfaceOfTcpPort) { - FakeAsyncPacketSocket* lsocket = new FakeAsyncPacketSocket(); - FakeAsyncPacketSocket* rsocket = new FakeAsyncPacketSocket(); + FakeAsyncListenSocket* lsocket = new FakeAsyncListenSocket(); + FakeAsyncListenSocket* rsocket = new FakeAsyncListenSocket(); FakePacketSocketFactory socket_factory; socket_factory.set_next_server_tcp_socket(lsocket); @@ -1530,10 +1586,8 @@ TEST_F(PortTest, TestDisableInterfaceOfTcpPort) { socket_factory.set_next_server_tcp_socket(rsocket); auto rport = CreateTcpPort(kLocalAddr2, &socket_factory); - lsocket->set_state(AsyncPacketSocket::STATE_BINDING); - lsocket->SignalAddressReady(lsocket, kLocalAddr1); - rsocket->set_state(AsyncPacketSocket::STATE_BINDING); - rsocket->SignalAddressReady(rsocket, kLocalAddr2); + lsocket->Bind(kLocalAddr1); + rsocket->Bind(kLocalAddr2); lport->SetIceRole(cricket::ICEROLE_CONTROLLING); lport->SetIceTiebreaker(kTiebreaker1); @@ -1572,16 +1626,18 @@ void PortTest::TestCrossFamilyPorts(int type) { SocketAddress("192.168.1.3", 0), SocketAddress("192.168.1.4", 0), SocketAddress("2001:db8::1", 0), SocketAddress("2001:db8::2", 0)}; for (int i = 0; i < 4; i++) { - FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket(); if (type == SOCK_DGRAM) { + FakeAsyncPacketSocket* socket = new FakeAsyncPacketSocket(); factory.set_next_udp_socket(socket); ports[i] = CreateUdpPort(addresses[i], &factory); + socket->set_state(AsyncPacketSocket::STATE_BINDING); + socket->SignalAddressReady(socket, addresses[i]); } else if (type == SOCK_STREAM) { + FakeAsyncListenSocket* socket = new FakeAsyncListenSocket(); factory.set_next_server_tcp_socket(socket); ports[i] = CreateTcpPort(addresses[i], &factory); + socket->Bind(addresses[i]); } - socket->set_state(AsyncPacketSocket::STATE_BINDING); - socket->SignalAddressReady(socket, addresses[i]); ports[i]->PrepareAddress(); } @@ -2548,7 +2604,7 @@ TEST_F(PortTest, TestCandidateFoundation) { // Running a second turn server, to get different base IP address. SocketAddress kTurnUdpIntAddr2("99.99.98.4", STUN_SERVER_PORT); SocketAddress kTurnUdpExtAddr2("99.99.98.5", 0); - TestTurnServer turn_server2(rtc::Thread::Current(), kTurnUdpIntAddr2, + TestTurnServer turn_server2(rtc::Thread::Current(), vss(), kTurnUdpIntAddr2, kTurnUdpExtAddr2); auto turnport3 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP, kTurnUdpIntAddr2); @@ -2559,7 +2615,7 @@ TEST_F(PortTest, TestCandidateFoundation) { // Start a TCP turn server, and check that two turn candidates have // different foundations if their relay protocols are different. - TestTurnServer turn_server3(rtc::Thread::Current(), kTurnTcpIntAddr, + TestTurnServer turn_server3(rtc::Thread::Current(), vss(), kTurnTcpIntAddr, kTurnUdpExtAddr, PROTO_TCP); auto turnport4 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_TCP, PROTO_UDP); diff --git a/p2p/base/pseudo_tcp.cc b/p2p/base/pseudo_tcp.cc index 13e7a2214f..eff86e849e 100644 --- a/p2p/base/pseudo_tcp.cc +++ b/p2p/base/pseudo_tcp.cc @@ -288,7 +288,7 @@ void PseudoTcp::NotifyClock(uint32_t now) { // Check if it's time to retransmit a segment if (m_rto_base && (rtc::TimeDiff32(m_rto_base + m_rx_rto, now) <= 0)) { if (m_slist.empty()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } else { // Note: (m_slist.front().xmit == 0)) { // retransmit segments @@ -355,7 +355,7 @@ void PseudoTcp::NotifyClock(uint32_t now) { bool PseudoTcp::NotifyPacket(const char* buffer, size_t len) { if (len > MAX_PACKET) { - RTC_LOG_F(WARNING) << "packet too large"; + RTC_LOG_F(LS_WARNING) << "packet too large"; return false; } return parse(reinterpret_cast(buffer), uint32_t(len)); @@ -375,7 +375,7 @@ void PseudoTcp::GetOption(Option opt, int* value) { } else if (opt == OPT_RCVBUF) { *value = m_rbuf_len; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } void PseudoTcp::SetOption(Option opt, int value) { @@ -390,7 +390,7 @@ void PseudoTcp::SetOption(Option opt, int value) { RTC_DCHECK(m_state == TCP_LISTEN); resizeReceiveBuffer(value); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -1240,7 +1240,7 @@ void PseudoTcp::applyOption(char kind, const char* data, uint32_t len) { // Window scale factor. // http://www.ietf.org/rfc/rfc1323.txt if (len != 1) { - RTC_LOG_F(WARNING) << "Invalid window scale option received."; + RTC_LOG_F(LS_WARNING) << "Invalid window scale option received."; return; } applyWindowScaleOption(data[0]); diff --git a/p2p/base/stun_port.cc b/p2p/base/stun_port.cc index 1ef7840738..48daa13a6d 100644 --- a/p2p/base/stun_port.cc +++ b/p2p/base/stun_port.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/memory/memory.h" #include "api/transport/stun.h" #include "p2p/base/connection.h" #include "p2p/base/p2p_constants.h" @@ -98,7 +99,7 @@ class StunBindingRequest : public StunRequest { << port_->Network()->name() << ")"; port_->OnStunBindingOrResolveRequestFailed( server_addr_, SERVER_NOT_REACHABLE_ERROR, - "STUN allocate request timed out."); + "STUN binding request timed out."); } private: @@ -120,29 +121,23 @@ UDPPort::AddressResolver::AddressResolver( std::function done_callback) : socket_factory_(factory), done_(std::move(done_callback)) {} -UDPPort::AddressResolver::~AddressResolver() { - for (ResolverMap::iterator it = resolvers_.begin(); it != resolvers_.end(); - ++it) { - // TODO(guoweis): Change to asynchronous DNS resolution to prevent the hang - // when passing true to the Destroy() which is a safer way to avoid the code - // unloaded before the thread exits. Please see webrtc bug 5139. - it->second->Destroy(false); - } -} - void UDPPort::AddressResolver::Resolve(const rtc::SocketAddress& address) { if (resolvers_.find(address) != resolvers_.end()) return; - rtc::AsyncResolverInterface* resolver = - socket_factory_->CreateAsyncResolver(); - resolvers_.insert(std::pair( - address, resolver)); + auto resolver = socket_factory_->CreateAsyncDnsResolver(); + auto resolver_ptr = resolver.get(); + std::pair> + pair = std::make_pair(address, std::move(resolver)); - resolver->SignalDone.connect(this, - &UDPPort::AddressResolver::OnResolveResult); - - resolver->Start(address); + resolvers_.insert(std::move(pair)); + resolver_ptr->Start(address, [this, address] { + ResolverMap::const_iterator it = resolvers_.find(address); + if (it != resolvers_.end()) { + done_(it->first, it->second->result().GetError()); + } + }); } bool UDPPort::AddressResolver::GetResolvedAddress( @@ -153,18 +148,7 @@ bool UDPPort::AddressResolver::GetResolvedAddress( if (it == resolvers_.end()) return false; - return it->second->GetResolvedAddress(family, output); -} - -void UDPPort::AddressResolver::OnResolveResult( - rtc::AsyncResolverInterface* resolver) { - for (ResolverMap::iterator it = resolvers_.begin(); it != resolvers_.end(); - ++it) { - if (it->second == resolver) { - done_(it->first, resolver->GetError()); - return; - } - } + return it->second->result().GetResolvedAddress(family, output); } UDPPort::UDPPort(rtc::Thread* thread, @@ -173,7 +157,6 @@ UDPPort::UDPPort(rtc::Thread* thread, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress) : Port(thread, LOCAL_PORT_TYPE, factory, network, username, password), requests_(thread), @@ -182,9 +165,7 @@ UDPPort::UDPPort(rtc::Thread* thread, ready_(false), stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL), dscp_(rtc::DSCP_NO_CHANGE), - emit_local_for_anyaddress_(emit_local_for_anyaddress) { - requests_.set_origin(origin); -} + emit_local_for_anyaddress_(emit_local_for_anyaddress) {} UDPPort::UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, @@ -193,7 +174,6 @@ UDPPort::UDPPort(rtc::Thread* thread, uint16_t max_port, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress) : Port(thread, LOCAL_PORT_TYPE, @@ -209,9 +189,7 @@ UDPPort::UDPPort(rtc::Thread* thread, ready_(false), stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL), dscp_(rtc::DSCP_NO_CHANGE), - emit_local_for_anyaddress_(emit_local_for_anyaddress) { - requests_.set_origin(origin); -} + emit_local_for_anyaddress_(emit_local_for_anyaddress) {} bool UDPPort::Init() { stun_keepalive_lifetime_ = GetStunKeepaliveLifetime(); @@ -270,7 +248,7 @@ Connection* UDPPort::CreateConnection(const Candidate& address, // it would lead to a crash when accessing the local candidate of the // connection that would be created below. if (Candidates().empty()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } // When the socket is shared, the srflx candidate is gathered by the UDPPort. @@ -534,7 +512,7 @@ void UDPPort::OnStunBindingRequestSucceeded( } rtc::StringBuilder url; - url << "stun:" << stun_server_addr.ipaddr().ToString() << ":" + url << "stun:" << stun_server_addr.hostname() << ":" << stun_server_addr.port(); AddAddress(stun_reflected_addr, socket_->GetLocalAddress(), related_address, UDP_PROTOCOL_NAME, "", "", STUN_PORT_TYPE, @@ -627,12 +605,11 @@ std::unique_ptr StunPort::Create( const std::string& username, const std::string& password, const ServerAddresses& servers, - const std::string& origin, absl::optional stun_keepalive_interval) { // Using `new` to access a non-public constructor. - auto port = absl::WrapUnique(new StunPort(thread, factory, network, min_port, - max_port, username, password, - servers, origin)); + auto port = + absl::WrapUnique(new StunPort(thread, factory, network, min_port, + max_port, username, password, servers)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -647,8 +624,7 @@ StunPort::StunPort(rtc::Thread* thread, uint16_t max_port, const std::string& username, const std::string& password, - const ServerAddresses& servers, - const std::string& origin) + const ServerAddresses& servers) : UDPPort(thread, factory, network, @@ -656,7 +632,6 @@ StunPort::StunPort(rtc::Thread* thread, max_port, username, password, - origin, false) { // UDPPort will set these to local udp, updating these to STUN. set_type(STUN_PORT_TYPE); diff --git a/p2p/base/stun_port.h b/p2p/base/stun_port.h index 72f60e2093..394c1336e2 100644 --- a/p2p/base/stun_port.h +++ b/p2p/base/stun_port.h @@ -20,6 +20,7 @@ #include "p2p/base/port.h" #include "p2p/base/stun_request.h" #include "rtc_base/async_packet_socket.h" +#include "rtc_base/task_utils/pending_task_safety_flag.h" namespace cricket { @@ -38,13 +39,12 @@ class UDPPort : public Port { rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress, absl::optional stun_keepalive_interval) { // Using `new` to access a non-public constructor. - auto port = absl::WrapUnique(new UDPPort(thread, factory, network, socket, - username, password, origin, - emit_local_for_anyaddress)); + auto port = + absl::WrapUnique(new UDPPort(thread, factory, network, socket, username, + password, emit_local_for_anyaddress)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -60,13 +60,12 @@ class UDPPort : public Port { uint16_t max_port, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress, absl::optional stun_keepalive_interval) { // Using `new` to access a non-public constructor. - auto port = absl::WrapUnique( - new UDPPort(thread, factory, network, min_port, max_port, username, - password, origin, emit_local_for_anyaddress)); + auto port = absl::WrapUnique(new UDPPort(thread, factory, network, min_port, + max_port, username, password, + emit_local_for_anyaddress)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -125,7 +124,6 @@ class UDPPort : public Port { uint16_t max_port, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress); UDPPort(rtc::Thread* thread, @@ -134,7 +132,6 @@ class UDPPort : public Port { rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, - const std::string& origin, bool emit_local_for_anyaddress); bool Init(); @@ -178,14 +175,13 @@ class UDPPort : public Port { private: // A helper class which can be called repeatedly to resolve multiple - // addresses, as opposed to rtc::AsyncResolverInterface, which can only + // addresses, as opposed to rtc::AsyncDnsResolverInterface, which can only // resolve one address per instance. - class AddressResolver : public sigslot::has_slots<> { + class AddressResolver { public: explicit AddressResolver( rtc::PacketSocketFactory* factory, std::function done_callback); - ~AddressResolver() override; void Resolve(const rtc::SocketAddress& address); bool GetResolvedAddress(const rtc::SocketAddress& input, @@ -193,17 +189,18 @@ class UDPPort : public Port { rtc::SocketAddress* output) const; private: - typedef std::map + typedef std::map> ResolverMap; - void OnResolveResult(rtc::AsyncResolverInterface* resolver); - rtc::PacketSocketFactory* socket_factory_; - ResolverMap resolvers_; // The function is called when resolving the specified address is finished. // The first argument is the input address, the second argument is the error // or 0 if it succeeded. std::function done_; + // Resolver may fire callbacks that refer to done_, so ensure + // that all resolvers are destroyed first. + ResolverMap resolvers_; }; // DNS resolution of the STUN server. @@ -273,7 +270,6 @@ class StunPort : public UDPPort { const std::string& username, const std::string& password, const ServerAddresses& servers, - const std::string& origin, absl::optional stun_keepalive_interval); void PrepareAddress() override; @@ -286,8 +282,7 @@ class StunPort : public UDPPort { uint16_t max_port, const std::string& username, const std::string& password, - const ServerAddresses& servers, - const std::string& origin); + const ServerAddresses& servers); }; } // namespace cricket diff --git a/p2p/base/stun_port_unittest.cc b/p2p/base/stun_port_unittest.cc index 36733ccf5a..d483bafd84 100644 --- a/p2p/base/stun_port_unittest.cc +++ b/p2p/base/stun_port_unittest.cc @@ -48,7 +48,7 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { : ss_(new rtc::VirtualSocketServer()), thread_(ss_.get()), network_("unittest", "unittest", kLocalAddr.ipaddr(), 32), - socket_factory_(rtc::Thread::Current()), + socket_factory_(ss_.get()), stun_server_1_(cricket::TestStunServer::Create(ss_.get(), kStunAddr1)), stun_server_2_(cricket::TestStunServer::Create(ss_.get(), kStunAddr2)), done_(false), @@ -77,7 +77,7 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { stun_port_ = cricket::StunPort::Create( rtc::Thread::Current(), &socket_factory_, &network_, 0, 0, rtc::CreateRandomString(16), rtc::CreateRandomString(22), stun_servers, - std::string(), absl::nullopt); + absl::nullopt); stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_); // If `stun_keepalive_lifetime_` is negative, let the stun port // choose its lifetime from the network type. @@ -103,8 +103,8 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { socket_->SignalReadPacket.connect(this, &StunPortTestBase::OnReadPacket); stun_port_ = cricket::UDPPort::Create( rtc::Thread::Current(), &socket_factory_, &network_, socket_.get(), - rtc::CreateRandomString(16), rtc::CreateRandomString(22), std::string(), - false, absl::nullopt); + rtc::CreateRandomString(16), rtc::CreateRandomString(22), false, + absl::nullopt); ASSERT_TRUE(stun_port_ != NULL); ServerAddresses stun_servers; stun_servers.insert(server_addr); diff --git a/p2p/base/stun_request.cc b/p2p/base/stun_request.cc index 0dd85c6944..ed94ccb5fc 100644 --- a/p2p/base/stun_request.cc +++ b/p2p/base/stun_request.cc @@ -19,7 +19,6 @@ #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" #include "rtc_base/time_utils.h" // For TimeMillis -#include "system_wrappers/include/field_trial.h" namespace cricket { @@ -59,7 +58,6 @@ void StunRequestManager::Send(StunRequest* request) { void StunRequestManager::SendDelayed(StunRequest* request, int delay) { request->set_manager(this); RTC_DCHECK(requests_.find(request->id()) == requests_.end()); - request->set_origin(origin_); request->Construct(); requests_[request->id()] = request; if (delay > 0) { @@ -148,9 +146,9 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) { } else if (msg->type() == GetStunErrorResponseType(request->type())) { request->OnErrorResponse(msg); } else { - RTC_LOG(LERROR) << "Received response with wrong type: " << msg->type() - << " (expecting " - << GetStunSuccessResponseType(request->type()) << ")"; + RTC_LOG(LS_ERROR) << "Received response with wrong type: " << msg->type() + << " (expecting " + << GetStunSuccessResponseType(request->type()) << ")"; return false; } @@ -213,10 +211,6 @@ StunRequest::~StunRequest() { void StunRequest::Construct() { if (msg_->type() == 0) { - if (!origin_.empty()) { - msg_->AddAttribute( - std::make_unique(STUN_ATTR_ORIGIN, origin_)); - } Prepare(msg_); RTC_DCHECK(msg_->type() != 0); } diff --git a/p2p/base/stun_request.h b/p2p/base/stun_request.h index 566478c349..b417c705cd 100644 --- a/p2p/base/stun_request.h +++ b/p2p/base/stun_request.h @@ -67,9 +67,6 @@ class StunRequestManager { bool empty() { return requests_.empty(); } - // Set the Origin header for outgoing stun messages. - void set_origin(const std::string& origin) { origin_ = origin; } - // Raised when there are bytes to be sent. sigslot::signal3 SignalSendPacket; @@ -78,7 +75,6 @@ class StunRequestManager { rtc::Thread* const thread_; RequestMap requests_; - std::string origin_; friend class StunRequest; }; @@ -105,10 +101,6 @@ class StunRequest : public rtc::MessageHandler { return msg_->reduced_transaction_id(); } - // the origin value - const std::string& origin() const { return origin_; } - void set_origin(const std::string& origin) { origin_ = origin; } - // Returns the STUN type of the request message. int type(); @@ -124,7 +116,6 @@ class StunRequest : public rtc::MessageHandler { protected: int count_; bool timeout_; - std::string origin_; // Fills in a request object to be sent. Note that request's transaction ID // will already be set and cannot be changed. diff --git a/p2p/base/tcp_port.cc b/p2p/base/tcp_port.cc index 4c5c38e8fe..445b0d03a5 100644 --- a/p2p/base/tcp_port.cc +++ b/p2p/base/tcp_port.cc @@ -71,6 +71,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/memory/memory.h" #include "p2p/base/p2p_constants.h" #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" @@ -78,6 +79,7 @@ #include "rtc_base/logging.h" #include "rtc_base/net_helper.h" #include "rtc_base/rate_tracker.h" +#include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/third_party/sigslot/sigslot.h" namespace cricket { @@ -99,17 +101,20 @@ TCPPort::TCPPort(rtc::Thread* thread, username, password), allow_listen_(allow_listen), - socket_(NULL), error_(0) { // TODO(mallinath) - Set preference value as per RFC 6544. // http://b/issue?id=7141794 if (allow_listen_) { TryCreateServerSocket(); } + // Set TCP_NODELAY (via OPT_NODELAY) for improved performance; this causes + // small media packets to be sent immediately rather than being buffered up, + // reducing latency. + SetOption(rtc::Socket::OPT_NODELAY, 1); } TCPPort::~TCPPort() { - delete socket_; + listen_socket_ = nullptr; std::list::iterator it; for (it = incoming_.begin(); it != incoming_.end(); ++it) delete it->socket; @@ -165,18 +170,15 @@ Connection* TCPPort::CreateConnection(const Candidate& address, } void TCPPort::PrepareAddress() { - if (socket_) { - // If socket isn't bound yet the address will be added in - // OnAddressReady(). Socket may be in the CLOSED state if Listen() + if (listen_socket_) { + // Socket may be in the CLOSED state if Listen() // failed, we still want to add the socket address. RTC_LOG(LS_VERBOSE) << "Preparing TCP address, current state: " - << socket_->GetState(); - if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND || - socket_->GetState() == rtc::AsyncPacketSocket::STATE_CLOSED) - AddAddress(socket_->GetLocalAddress(), socket_->GetLocalAddress(), - rtc::SocketAddress(), TCP_PROTOCOL_NAME, "", - TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE, - ICE_TYPE_PREFERENCE_HOST_TCP, 0, "", true); + << static_cast(listen_socket_->GetState()); + AddAddress(listen_socket_->GetLocalAddress(), + listen_socket_->GetLocalAddress(), rtc::SocketAddress(), + TCP_PROTOCOL_NAME, "", TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE, + ICE_TYPE_PREFERENCE_HOST_TCP, 0, "", true); } else { RTC_LOG(LS_INFO) << ToString() << ": Not listening due to firewall restrictions."; @@ -246,19 +248,17 @@ int TCPPort::SendTo(const void* data, } int TCPPort::GetOption(rtc::Socket::Option opt, int* value) { - if (socket_) { - return socket_->GetOption(opt, value); - } else { - return SOCKET_ERROR; + auto const& it = socket_options_.find(opt); + if (it == socket_options_.end()) { + return -1; } + *value = it->second; + return 0; } int TCPPort::SetOption(rtc::Socket::Option opt, int value) { - if (socket_) { - return socket_->SetOption(opt, value); - } else { - return SOCKET_ERROR; - } + socket_options_[opt] = value; + return 0; } int TCPPort::GetError() { @@ -273,10 +273,13 @@ ProtocolType TCPPort::GetProtocol() const { return PROTO_TCP; } -void TCPPort::OnNewConnection(rtc::AsyncPacketSocket* socket, +void TCPPort::OnNewConnection(rtc::AsyncListenSocket* socket, rtc::AsyncPacketSocket* new_socket) { - RTC_DCHECK(socket == socket_); + RTC_DCHECK(socket == listen_socket_.get()); + for (const auto& option : socket_options_) { + new_socket->SetOption(option.first, option.second); + } Incoming incoming; incoming.addr = new_socket->GetRemoteAddress(); incoming.socket = new_socket; @@ -290,17 +293,16 @@ void TCPPort::OnNewConnection(rtc::AsyncPacketSocket* socket, } void TCPPort::TryCreateServerSocket() { - socket_ = socket_factory()->CreateServerTcpSocket( + listen_socket_ = absl::WrapUnique(socket_factory()->CreateServerTcpSocket( rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port(), - false /* ssl */); - if (!socket_) { + false /* ssl */)); + if (!listen_socket_) { RTC_LOG(LS_WARNING) << ToString() << ": TCP server socket creation failed; continuing anyway."; return; } - socket_->SignalNewConnection.connect(this, &TCPPort::OnNewConnection); - socket_->SignalAddressReady.connect(this, &TCPPort::OnAddressReady); + listen_socket_->SignalNewConnection.connect(this, &TCPPort::OnNewConnection); } rtc::AsyncPacketSocket* TCPPort::GetIncoming(const rtc::SocketAddress& addr, @@ -335,13 +337,6 @@ void TCPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) { Port::OnReadyToSend(); } -void TCPPort::OnAddressReady(rtc::AsyncPacketSocket* socket, - const rtc::SocketAddress& address) { - AddAddress(address, address, rtc::SocketAddress(), TCP_PROTOCOL_NAME, "", - TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, - 0, "", true); -} - // TODO(qingsi): `CONNECTION_WRITE_CONNECT_TIMEOUT` is overriden by // `ice_unwritable_timeout` in IceConfig when determining the writability state. // Replace this constant with the config parameter assuming the default value if @@ -372,7 +367,9 @@ TCPConnection::TCPConnection(TCPPort* port, } } -TCPConnection::~TCPConnection() {} +TCPConnection::~TCPConnection() { + RTC_DCHECK_RUN_ON(network_thread_); +} int TCPConnection::Send(const void* data, size_t size, @@ -499,11 +496,20 @@ void TCPConnection::OnClose(rtc::AsyncPacketSocket* socket, int error) { // events. pretending_to_be_writable_ = true; + // If this connection can't become connected and writable again in 5 + // seconds, it's time to tear this down. This is the case for the original + // TCP connection on passive side during a reconnect. // We don't attempt reconnect right here. This is to avoid a case where the // shutdown is intentional and reconnect is not necessary. We only reconnect // when the connection is used to Send() or Ping(). - port()->thread()->PostDelayed(RTC_FROM_HERE, reconnection_timeout(), this, - MSG_TCPCONNECTION_DELAYED_ONCLOSE); + port()->thread()->PostDelayedTask( + webrtc::ToQueuedTask(network_safety_, + [this]() { + if (pretending_to_be_writable_) { + Destroy(); + } + }), + reconnection_timeout()); } else if (!pretending_to_be_writable_) { // OnClose could be called when the underneath socket times out during the // initial connect() (i.e. `pretending_to_be_writable_` is false) . We have @@ -513,24 +519,6 @@ void TCPConnection::OnClose(rtc::AsyncPacketSocket* socket, int error) { } } -void TCPConnection::OnMessage(rtc::Message* pmsg) { - switch (pmsg->message_id) { - case MSG_TCPCONNECTION_DELAYED_ONCLOSE: - // If this connection can't become connected and writable again in 5 - // seconds, it's time to tear this down. This is the case for the original - // TCP connection on passive side during a reconnect. - if (pretending_to_be_writable_) { - Destroy(); - } - break; - case MSG_TCPCONNECTION_FAILED_CREATE_SOCKET: - FailAndPrune(); - break; - default: - Connection::OnMessage(pmsg); - } -} - void TCPConnection::MaybeReconnect() { // Only reconnect for an outgoing TCPConnection when OnClose was signaled and // no outstanding reconnect is pending. @@ -582,13 +570,13 @@ void TCPConnection::CreateOutgoingTcpSocket() { } else { RTC_LOG(LS_WARNING) << ToString() << ": Failed to create connection to " << remote_candidate().address().ToSensitiveString(); + set_state(IceCandidatePairState::FAILED); // We can't FailAndPrune directly here. FailAndPrune and deletes all // the StunRequests from the request_map_. And if this is in the stack // of Connection::Ping(), we are still using the request. // Unwind the stack and defer the FailAndPrune. - set_state(IceCandidatePairState::FAILED); - port()->thread()->Post(RTC_FROM_HERE, this, - MSG_TCPCONNECTION_FAILED_CREATE_SOCKET); + port()->thread()->PostTask( + webrtc::ToQueuedTask(network_safety_, [this]() { FailAndPrune(); })); } } diff --git a/p2p/base/tcp_port.h b/p2p/base/tcp_port.h index 36257b07ed..07d483cc3f 100644 --- a/p2p/base/tcp_port.h +++ b/p2p/base/tcp_port.h @@ -19,6 +19,8 @@ #include "p2p/base/connection.h" #include "p2p/base/port.h" #include "rtc_base/async_packet_socket.h" +#include "rtc_base/containers/flat_map.h" +#include "rtc_base/task_utils/pending_task_safety_flag.h" namespace cricket { @@ -52,6 +54,9 @@ class TCPPort : public Port { void PrepareAddress() override; + // Options apply to accepted sockets. + // TODO(bugs.webrtc.org/13065): Apply also to outgoing and existing + // connections. int GetOption(rtc::Socket::Option opt, int* value) override; int SetOption(rtc::Socket::Option opt, int value) override; int GetError() override; @@ -76,7 +81,7 @@ class TCPPort : public Port { bool payload) override; // Accepts incoming TCP connection. - void OnNewConnection(rtc::AsyncPacketSocket* socket, + void OnNewConnection(rtc::AsyncListenSocket* socket, rtc::AsyncPacketSocket* new_socket); private: @@ -102,11 +107,14 @@ class TCPPort : public Port { void OnReadyToSend(rtc::AsyncPacketSocket* socket); - void OnAddressReady(rtc::AsyncPacketSocket* socket, - const rtc::SocketAddress& address); - bool allow_listen_; - rtc::AsyncPacketSocket* socket_; + std::unique_ptr listen_socket_; + // Options to be applied to accepted sockets. + // TODO(bugs.webrtc:13065): Configure connect/accept in the same way, but + // currently, setting OPT_NODELAY for client sockets is done (unconditionally) + // by BasicPacketSocketFactory::CreateClientTcpSocket. + webrtc::flat_map socket_options_; + int error_; std::list incoming_; @@ -128,8 +136,6 @@ class TCPConnection : public Connection { rtc::AsyncPacketSocket* socket() { return socket_.get(); } - void OnMessage(rtc::Message* pmsg) override; - // Allow test cases to overwrite the default timeout period. int reconnection_timeout() const { return reconnection_timeout_; } void set_reconnection_timeout(int timeout_in_ms) { @@ -137,11 +143,6 @@ class TCPConnection : public Connection { } protected: - enum { - MSG_TCPCONNECTION_DELAYED_ONCLOSE = Connection::MSG_FIRST_AVAILABLE, - MSG_TCPCONNECTION_FAILED_CREATE_SOCKET, - }; - // Set waiting_for_stun_binding_complete_ to false to allow data packets in // addition to what Port::OnConnectionRequestResponse does. void OnConnectionRequestResponse(ConnectionRequest* req, @@ -183,6 +184,8 @@ class TCPConnection : public Connection { // Allow test case to overwrite the default timeout period. int reconnection_timeout_; + webrtc::ScopedTaskSafety network_safety_; + friend class TCPPort; }; diff --git a/p2p/base/tcp_port_unittest.cc b/p2p/base/tcp_port_unittest.cc index 2c9fbceeae..9af934f10e 100644 --- a/p2p/base/tcp_port_unittest.cc +++ b/p2p/base/tcp_port_unittest.cc @@ -44,15 +44,23 @@ static const SocketAddress kRemoteIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c4", class ConnectionObserver : public sigslot::has_slots<> { public: - explicit ConnectionObserver(Connection* conn) { + explicit ConnectionObserver(Connection* conn) : conn_(conn) { conn->SignalDestroyed.connect(this, &ConnectionObserver::OnDestroyed); } + ~ConnectionObserver() { + if (!connection_destroyed_) { + RTC_DCHECK(conn_); + conn_->SignalDestroyed.disconnect(this); + } + } + bool connection_destroyed() { return connection_destroyed_; } private: void OnDestroyed(Connection*) { connection_destroyed_ = true; } + Connection* const conn_; bool connection_destroyed_ = false; }; @@ -61,7 +69,7 @@ class TCPPortTest : public ::testing::Test, public sigslot::has_slots<> { TCPPortTest() : ss_(new rtc::VirtualSocketServer()), main_(ss_.get()), - socket_factory_(rtc::Thread::Current()), + socket_factory_(ss_.get()), username_(rtc::CreateRandomString(ICE_UFRAG_LENGTH)), password_(rtc::CreateRandomString(ICE_PWD_LENGTH)) {} diff --git a/p2p/base/test_turn_server.h b/p2p/base/test_turn_server.h index 479ca8bb39..7110a8a5a0 100644 --- a/p2p/base/test_turn_server.h +++ b/p2p/base/test_turn_server.h @@ -11,7 +11,9 @@ #ifndef P2P_BASE_TEST_TURN_SERVER_H_ #define P2P_BASE_TEST_TURN_SERVER_H_ +#include #include +#include #include #include "api/sequence_checker.h" @@ -51,15 +53,16 @@ class TestTurnRedirector : public TurnRedirectInterface { class TestTurnServer : public TurnAuthInterface { public: TestTurnServer(rtc::Thread* thread, + rtc::SocketFactory* socket_factory, const rtc::SocketAddress& int_addr, const rtc::SocketAddress& udp_ext_addr, ProtocolType int_protocol = PROTO_UDP, bool ignore_bad_cert = true, const std::string& common_name = "test turn server") - : server_(thread), thread_(thread) { + : server_(thread), socket_factory_(socket_factory) { AddInternalSocket(int_addr, int_protocol, ignore_bad_cert, common_name); - server_.SetExternalSocketFactory(new rtc::BasicPacketSocketFactory(thread), - udp_ext_addr); + server_.SetExternalSocketFactory( + new rtc::BasicPacketSocketFactory(socket_factory), udp_ext_addr); server_.set_realm(kTestRealm); server_.set_software(kTestSoftware); server_.set_auth_hook(this); @@ -94,30 +97,31 @@ class TestTurnServer : public TurnAuthInterface { RTC_DCHECK(thread_checker_.IsCurrent()); if (proto == cricket::PROTO_UDP) { server_.AddInternalSocket( - rtc::AsyncUDPSocket::Create(thread_->socketserver(), int_addr), - proto); + rtc::AsyncUDPSocket::Create(socket_factory_, int_addr), proto); } else if (proto == cricket::PROTO_TCP || proto == cricket::PROTO_TLS) { // For TCP we need to create a server socket which can listen for incoming // new connections. - rtc::Socket* socket = - thread_->socketserver()->CreateSocket(AF_INET, SOCK_STREAM); + rtc::Socket* socket = socket_factory_->CreateSocket(AF_INET, SOCK_STREAM); + socket->Bind(int_addr); + socket->Listen(5); if (proto == cricket::PROTO_TLS) { // For TLS, wrap the TCP socket with an SSL adapter. The adapter must // be configured with a self-signed certificate for testing. // Additionally, the client will not present a valid certificate, so we // must not fail when checking the peer's identity. - rtc::SSLAdapter* adapter = rtc::SSLAdapter::Create(socket); - adapter->SetRole(rtc::SSL_SERVER); - adapter->SetIdentity( + std::unique_ptr ssl_adapter_factory = + rtc::SSLAdapterFactory::Create(); + ssl_adapter_factory->SetRole(rtc::SSL_SERVER); + ssl_adapter_factory->SetIdentity( rtc::SSLIdentity::Create(common_name, rtc::KeyParams())); - adapter->SetIgnoreBadCert(ignore_bad_cert); - socket = adapter; + ssl_adapter_factory->SetIgnoreBadCert(ignore_bad_cert); + server_.AddInternalServerSocket(socket, proto, + std::move(ssl_adapter_factory)); + } else { + server_.AddInternalServerSocket(socket, proto); } - socket->Bind(int_addr); - socket->Listen(5); - server_.AddInternalServerSocket(socket, proto); } else { - RTC_NOTREACHED() << "Unknown protocol type: " << proto; + RTC_DCHECK_NOTREACHED() << "Unknown protocol type: " << proto; } } @@ -146,7 +150,7 @@ class TestTurnServer : public TurnAuthInterface { } TurnServer server_; - rtc::Thread* thread_; + rtc::SocketFactory* socket_factory_; webrtc::SequenceChecker thread_checker_; }; diff --git a/p2p/base/transport_description_factory.cc b/p2p/base/transport_description_factory.cc index 6beae34333..18c4a28c2b 100644 --- a/p2p/base/transport_description_factory.cc +++ b/p2p/base/transport_description_factory.cc @@ -21,8 +21,9 @@ namespace cricket { -TransportDescriptionFactory::TransportDescriptionFactory() - : secure_(SEC_DISABLED) {} +TransportDescriptionFactory::TransportDescriptionFactory( + const webrtc::WebRtcKeyValueConfig& field_trials) + : secure_(SEC_DISABLED), field_trials_(field_trials) {} TransportDescriptionFactory::~TransportDescriptionFactory() = default; @@ -109,7 +110,7 @@ std::unique_ptr TransportDescriptionFactory::CreateAnswer( } else { RTC_LOG(LS_ERROR) << "Remote offer connection role is " << role << " which is a protocol violation"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (!SetSecurityInfo(desc.get(), role)) { diff --git a/p2p/base/transport_description_factory.h b/p2p/base/transport_description_factory.h index 0be7f32929..46f1c2f9fa 100644 --- a/p2p/base/transport_description_factory.h +++ b/p2p/base/transport_description_factory.h @@ -13,6 +13,7 @@ #include +#include "api/webrtc_key_value_config.h" #include "p2p/base/ice_credentials_iterator.h" #include "p2p/base/transport_description.h" #include "rtc_base/rtc_certificate.h" @@ -37,7 +38,8 @@ struct TransportOptions { class TransportDescriptionFactory { public: // Default ctor; use methods below to set configuration. - TransportDescriptionFactory(); + explicit TransportDescriptionFactory( + const webrtc::WebRtcKeyValueConfig& field_trials); ~TransportDescriptionFactory(); SecurePolicy secure() const { return secure_; } @@ -73,12 +75,15 @@ class TransportDescriptionFactory { const TransportDescription* current_description, IceCredentialsIterator* ice_credentials) const; + const webrtc::WebRtcKeyValueConfig& trials() const { return field_trials_; } + private: bool SetSecurityInfo(TransportDescription* description, ConnectionRole role) const; SecurePolicy secure_; rtc::scoped_refptr certificate_; + const webrtc::WebRtcKeyValueConfig& field_trials_; }; } // namespace cricket diff --git a/p2p/base/transport_description_factory_unittest.cc b/p2p/base/transport_description_factory_unittest.cc index 77a56eff26..75e6e8ee09 100644 --- a/p2p/base/transport_description_factory_unittest.cc +++ b/p2p/base/transport_description_factory_unittest.cc @@ -25,6 +25,7 @@ #include "rtc_base/ssl_identity.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::TransportDescription; using cricket::TransportDescriptionFactory; @@ -36,6 +37,8 @@ class TransportDescriptionFactoryTest : public ::testing::Test { public: TransportDescriptionFactoryTest() : ice_credentials_({}), + f1_(field_trials_), + f2_(field_trials_), cert1_(rtc::RTCCertificate::Create(std::unique_ptr( new rtc::FakeSSLIdentity("User1")))), cert2_(rtc::RTCCertificate::Create(std::unique_ptr( @@ -156,6 +159,7 @@ class TransportDescriptionFactoryTest : public ::testing::Test { } } + webrtc::test::ScopedKeyValueConfig field_trials_; cricket::IceCredentialsIterator ice_credentials_; TransportDescriptionFactory f1_; TransportDescriptionFactory f2_; diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc index 8c3c82eb08..5694f3306e 100644 --- a/p2p/base/turn_port.cc +++ b/p2p/base/turn_port.cc @@ -29,7 +29,6 @@ #include "rtc_base/socket_address.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_utils/to_queued_task.h" -#include "system_wrappers/include/field_trial.h" namespace cricket { @@ -223,14 +222,13 @@ TurnPort::TurnPort(rtc::Thread* thread, const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, - webrtc::TurnCustomizer* customizer) + webrtc::TurnCustomizer* customizer, + const webrtc::WebRtcKeyValueConfig* field_trials) : Port(thread, RELAY_PORT_TYPE, factory, network, username, password), server_address_(server_address), tls_cert_verifier_(nullptr), credentials_(credentials), socket_(socket), - resolver_(NULL), error_(0), stun_dscp_value_(rtc::DSCP_NO_CHANGE), request_manager_(thread), @@ -238,9 +236,9 @@ TurnPort::TurnPort(rtc::Thread* thread, state_(STATE_CONNECTING), server_priority_(server_priority), allocate_mismatch_retries_(0), - turn_customizer_(customizer) { + turn_customizer_(customizer), + field_trials_(field_trials) { request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); - request_manager_.set_origin(origin); } TurnPort::TurnPort(rtc::Thread* thread, @@ -253,11 +251,11 @@ TurnPort::TurnPort(rtc::Thread* thread, const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier) + rtc::SSLCertificateVerifier* tls_cert_verifier, + const webrtc::WebRtcKeyValueConfig* field_trials) : Port(thread, RELAY_PORT_TYPE, factory, @@ -272,7 +270,6 @@ TurnPort::TurnPort(rtc::Thread* thread, tls_cert_verifier_(tls_cert_verifier), credentials_(credentials), socket_(NULL), - resolver_(NULL), error_(0), stun_dscp_value_(rtc::DSCP_NO_CHANGE), request_manager_(thread), @@ -280,9 +277,9 @@ TurnPort::TurnPort(rtc::Thread* thread, state_(STATE_CONNECTING), server_priority_(server_priority), allocate_mismatch_retries_(0), - turn_customizer_(customizer) { + turn_customizer_(customizer), + field_trials_(field_trials) { request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); - request_manager_.set_origin(origin); } TurnPort::~TurnPort() { @@ -297,9 +294,6 @@ TurnPort::~TurnPort() { while (!entries_.empty()) { DestroyEntry(entries_.front()); } - if (resolver_) { - resolver_->Destroy(false); - } if (!SharedSocket()) { delete socket_; } @@ -347,7 +341,7 @@ void TurnPort::PrepareAddress() { server_address_.address.SetPort(TURN_DEFAULT_PORT); } - if (!AllowedTurnPort(server_address_.address.port())) { + if (!AllowedTurnPort(server_address_.address.port(), field_trials_)) { // This can only happen after a 300 ALTERNATE SERVER, since the port can't // be created with a disallowed port number. RTC_LOG(LS_ERROR) << "Attempt to start allocation with disallowed port# " @@ -797,44 +791,43 @@ void TurnPort::ResolveTurnAddress(const rtc::SocketAddress& address) { RTC_LOG(LS_INFO) << ToString() << ": Starting TURN host lookup for " << address.ToSensitiveString(); - resolver_ = socket_factory()->CreateAsyncResolver(); - resolver_->SignalDone.connect(this, &TurnPort::OnResolveResult); - resolver_->Start(address); -} + resolver_ = socket_factory()->CreateAsyncDnsResolver(); + resolver_->Start(address, [this] { + // If DNS resolve is failed when trying to connect to the server using TCP, + // one of the reason could be due to DNS queries blocked by firewall. + // In such cases we will try to connect to the server with hostname, + // assuming socket layer will resolve the hostname through a HTTP proxy (if + // any). + auto& result = resolver_->result(); + if (result.GetError() != 0 && (server_address_.proto == PROTO_TCP || + server_address_.proto == PROTO_TLS)) { + if (!CreateTurnClientSocket()) { + OnAllocateError(SERVER_NOT_REACHABLE_ERROR, + "TURN host lookup received error."); + } + return; + } -void TurnPort::OnResolveResult(rtc::AsyncResolverInterface* resolver) { - RTC_DCHECK(resolver == resolver_); - // If DNS resolve is failed when trying to connect to the server using TCP, - // one of the reason could be due to DNS queries blocked by firewall. - // In such cases we will try to connect to the server with hostname, assuming - // socket layer will resolve the hostname through a HTTP proxy (if any). - if (resolver_->GetError() != 0 && (server_address_.proto == PROTO_TCP || - server_address_.proto == PROTO_TLS)) { - if (!CreateTurnClientSocket()) { + // Copy the original server address in `resolved_address`. For TLS based + // sockets we need hostname along with resolved address. + rtc::SocketAddress resolved_address = server_address_.address; + if (result.GetError() != 0 || + !result.GetResolvedAddress(Network()->GetBestIP().family(), + &resolved_address)) { + RTC_LOG(LS_WARNING) << ToString() << ": TURN host lookup received error " + << result.GetError(); + error_ = result.GetError(); OnAllocateError(SERVER_NOT_REACHABLE_ERROR, "TURN host lookup received error."); + return; } - return; - } - - // Copy the original server address in `resolved_address`. For TLS based - // sockets we need hostname along with resolved address. - rtc::SocketAddress resolved_address = server_address_.address; - if (resolver_->GetError() != 0 || - !resolver_->GetResolvedAddress(Network()->GetBestIP().family(), - &resolved_address)) { - RTC_LOG(LS_INFO) << ToString() << ": TURN host lookup received error " - << resolver_->GetError(); - error_ = resolver_->GetError(); - OnAllocateError(SERVER_NOT_REACHABLE_ERROR, - "TURN host lookup received error."); - return; - } - // Signal needs both resolved and unresolved address. After signal is sent - // we can copy resolved address back into `server_address_`. - SignalResolvedServerAddress(this, server_address_.address, resolved_address); - server_address_.address = resolved_address; - PrepareAddress(); + // Signal needs both resolved and unresolved address. After signal is sent + // we can copy resolved address back into `server_address_`. + SignalResolvedServerAddress(this, server_address_.address, + resolved_address); + server_address_.address = resolved_address; + PrepareAddress(); + }); } void TurnPort::OnSendStunPacket(const void* data, @@ -872,10 +865,9 @@ void TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address, related_address, // Related address. UDP_PROTOCOL_NAME, ProtoToString(server_address_.proto), // The first hop protocol. - "", // TCP canddiate type, empty for turn candidates. + "", // TCP candidate type, empty for turn candidates. RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto), - server_priority_, ReconstructedServerUrl(false /* use_hostname */), - true); + server_priority_, ReconstructedServerUrl(), true); } void TurnPort::OnAllocateError(int error_code, const std::string& reason) { @@ -891,9 +883,8 @@ void TurnPort::OnAllocateError(int error_code, const std::string& reason) { port = 0; } SignalCandidateError( - this, IceCandidateErrorEvent( - address, port, ReconstructedServerUrl(true /* use_hostname */), - error_code, reason)); + this, IceCandidateErrorEvent(address, port, ReconstructedServerUrl(), + error_code, reason)); } void TurnPort::OnRefreshError() { @@ -932,9 +923,7 @@ void TurnPort::Close() { // Stop the port from creating new connections. state_ = STATE_DISCONNECTED; // Delete all existing connections; stop sending data. - for (auto kv : connections()) { - kv.second->Destroy(); - } + DestroyAllConnections(); SignalTurnPortClosed(this); } @@ -944,7 +933,9 @@ rtc::DiffServCodePoint TurnPort::StunDscpValue() const { } // static -bool TurnPort::AllowedTurnPort(int port) { +bool TurnPort::AllowedTurnPort( + int port, + const webrtc::WebRtcKeyValueConfig* field_trials) { // Port 53, 80 and 443 are used for existing deployments. // Ports above 1024 are assumed to be OK to use. if (port == 53 || port == 80 || port == 443 || port >= 1024) { @@ -952,7 +943,7 @@ bool TurnPort::AllowedTurnPort(int port) { } // Allow any port if relevant field trial is set. This allows disabling the // check. - if (webrtc::field_trial::IsEnabled("WebRTC-Turn-AllowSystemPorts")) { + if (field_trials && field_trials->IsEnabled("WebRTC-Turn-AllowSystemPorts")) { return true; } return false; @@ -1242,7 +1233,8 @@ bool TurnPort::CreateOrRefreshEntry(const rtc::SocketAddress& addr, RTC_DCHECK(GetConnection(addr)); } - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { + if (field_trials_ && + field_trials_->IsEnabled("WebRTC-TurnAddMultiMapping")) { if (entry->get_remote_ufrag() != remote_ufrag) { RTC_LOG(LS_INFO) << ToString() << ": remote ufrag updated." @@ -1282,10 +1274,6 @@ void TurnPort::HandleConnectionDestroyed(Connection* conn) { const rtc::SocketAddress& remote_address = conn->remote_candidate().address(); TurnEntry* entry = FindEntry(remote_address); RTC_DCHECK(entry != NULL); - ScheduleEntryDestruction(entry); -} - -void TurnPort::ScheduleEntryDestruction(TurnEntry* entry) { RTC_DCHECK(!entry->destruction_timestamp().has_value()); int64_t timestamp = rtc::TimeMillis(); entry->set_destruction_timestamp(timestamp); @@ -1307,7 +1295,7 @@ bool TurnPort::SetEntryChannelId(const rtc::SocketAddress& address, return true; } -std::string TurnPort::ReconstructedServerUrl(bool use_hostname) { +std::string TurnPort::ReconstructedServerUrl() { // draft-petithuguenin-behave-turn-uris-01 // turnURI = scheme ":" turn-host [ ":" turn-port ] // [ "?transport=" transport ] @@ -1330,10 +1318,8 @@ std::string TurnPort::ReconstructedServerUrl(bool use_hostname) { break; } rtc::StringBuilder url; - url << scheme << ":" - << (use_hostname ? server_address_.address.hostname() - : server_address_.address.ipaddr().ToString()) - << ":" << server_address_.address.port() << "?transport=" << transport; + url << scheme << ":" << server_address_.address.hostname() << ":" + << server_address_.address.port() << "?transport=" << transport; return url.Release(); } @@ -1647,7 +1633,8 @@ void TurnCreatePermissionRequest::Prepare(StunMessage* request) { request->SetType(TURN_CREATE_PERMISSION_REQUEST); request->AddAttribute(std::make_unique( STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)); - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { + if (port_->field_trials_ && + port_->field_trials_->IsEnabled("WebRTC-TurnAddMultiMapping")) { request->AddAttribute(std::make_unique( STUN_ATTR_MULTI_MAPPING, remote_ufrag_)); } diff --git a/p2p/base/turn_port.h b/p2p/base/turn_port.h index 950fc09561..797d19096e 100644 --- a/p2p/base/turn_port.h +++ b/p2p/base/turn_port.h @@ -21,10 +21,10 @@ #include #include "absl/memory/memory.h" +#include "api/async_dns_resolver.h" #include "p2p/base/port.h" #include "p2p/client/basic_port_allocator.h" #include "rtc_base/async_packet_socket.h" -#include "rtc_base/async_resolver_interface.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" @@ -51,6 +51,7 @@ class TurnPort : public Port { STATE_DISCONNECTED, // TCP connection died, cannot send/receive any // packets. }; + // Create a TURN port using the shared UDP socket, `socket`. static std::unique_ptr Create( rtc::Thread* thread, @@ -62,8 +63,8 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, - webrtc::TurnCustomizer* customizer) { + webrtc::TurnCustomizer* customizer, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) { // Do basic parameter validation. if (credentials.username.size() > kMaxTurnUsernameLength) { RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " @@ -71,7 +72,7 @@ class TurnPort : public Port { return nullptr; } // Do not connect to low-numbered ports. The default STUN port is 3478. - if (!AllowedTurnPort(server_address.address.port())) { + if (!AllowedTurnPort(server_address.address.port(), field_trials)) { RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " << server_address.address.port(); return nullptr; @@ -79,8 +80,9 @@ class TurnPort : public Port { // Using `new` to access a non-public constructor. return absl::WrapUnique(new TurnPort( thread, factory, network, socket, username, password, server_address, - credentials, server_priority, origin, customizer)); + credentials, server_priority, customizer, field_trials)); } + // TODO(steveanton): Remove once downstream clients have moved to `Create`. static std::unique_ptr CreateUnique( rtc::Thread* thread, @@ -92,11 +94,11 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, - webrtc::TurnCustomizer* customizer) { + webrtc::TurnCustomizer* customizer, + const webrtc::WebRtcKeyValueConfig* field_trials) { return Create(thread, factory, network, socket, username, password, - server_address, credentials, server_priority, origin, - customizer); + server_address, credentials, server_priority, customizer, + field_trials); } // Create a TURN port that will use a new socket, bound to `network` and @@ -112,11 +114,11 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { + rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) { // Do basic parameter validation. if (credentials.username.size() > kMaxTurnUsernameLength) { RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " @@ -124,18 +126,18 @@ class TurnPort : public Port { return nullptr; } // Do not connect to low-numbered ports. The default STUN port is 3478. - if (!AllowedTurnPort(server_address.address.port())) { + if (!AllowedTurnPort(server_address.address.port(), field_trials)) { RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " << server_address.address.port(); return nullptr; } // Using `new` to access a non-public constructor. - return absl::WrapUnique( - new TurnPort(thread, factory, network, min_port, max_port, username, - password, server_address, credentials, server_priority, - origin, tls_alpn_protocols, tls_elliptic_curves, - customizer, tls_cert_verifier)); + return absl::WrapUnique(new TurnPort( + thread, factory, network, min_port, max_port, username, password, + server_address, credentials, server_priority, tls_alpn_protocols, + tls_elliptic_curves, customizer, tls_cert_verifier, field_trials)); } + // TODO(steveanton): Remove once downstream clients have moved to `Create`. static std::unique_ptr CreateUnique( rtc::Thread* thread, @@ -148,15 +150,15 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { + rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr) { return Create(thread, factory, network, min_port, max_port, username, password, server_address, credentials, server_priority, - origin, tls_alpn_protocols, tls_elliptic_curves, customizer, - tls_cert_verifier); + tls_alpn_protocols, tls_elliptic_curves, customizer, + tls_cert_verifier, field_trials); } ~TurnPort() override; @@ -267,8 +269,8 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, - webrtc::TurnCustomizer* customizer); + webrtc::TurnCustomizer* customizer, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr); TurnPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, @@ -280,13 +282,13 @@ class TurnPort : public Port { const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - const std::string& origin, const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr); + rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr); - // NOTE: This method needs to be accessible for StacPort + // NOTE: This method needs to be accessible for StunPort // return true if entry was created (i.e channel_number consumed). bool CreateOrRefreshEntry(const rtc::SocketAddress& addr, int channel_number); @@ -309,7 +311,8 @@ class TurnPort : public Port { typedef std::map SocketOptionsMap; typedef std::set AttemptedServerSet; - static bool AllowedTurnPort(int port); + static bool AllowedTurnPort(int port, + const webrtc::WebRtcKeyValueConfig* field_trials); void OnMessage(rtc::Message* pmsg) override; bool CreateTurnClientSocket(); @@ -366,14 +369,13 @@ class TurnPort : public Port { // Destroys the entry only if `timestamp` matches the destruction timestamp // in `entry`. void DestroyEntryIfNotCancelled(TurnEntry* entry, int64_t timestamp); - void ScheduleEntryDestruction(TurnEntry* entry); // Marks the connection with remote address `address` failed and // pruned (a.k.a. write-timed-out). Returns true if a connection is found. bool FailAndPruneConnection(const rtc::SocketAddress& address); // Reconstruct the URL of the server which the candidate is gathered from. - std::string ReconstructedServerUrl(bool use_hostname); + std::string ReconstructedServerUrl(); void MaybeAddTurnLoggingId(StunMessage* message); @@ -392,7 +394,7 @@ class TurnPort : public Port { rtc::AsyncPacketSocket* socket_; SocketOptionsMap socket_options_; - rtc::AsyncResolverInterface* resolver_; + std::unique_ptr resolver_; int error_; rtc::DiffServCodePoint stun_dscp_value_; @@ -416,6 +418,8 @@ class TurnPort : public Port { // must outlive the TurnPort's lifetime. webrtc::TurnCustomizer* turn_customizer_ = nullptr; + const webrtc::WebRtcKeyValueConfig* field_trials_; + // Optional TurnLoggingId. // An identifier set by application that is added to TURN_ALLOCATE_REQUEST // and can be used to match client/backend logs. diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc index a642548f5c..cc670b037a 100644 --- a/p2p/base/turn_port_unittest.cc +++ b/p2p/base/turn_port_unittest.cc @@ -41,8 +41,8 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::SocketAddress; @@ -76,7 +76,7 @@ static const SocketAddress kTurnIPv6IntAddr( static const SocketAddress kTurnUdpIPv6IntAddr( "2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT); -static const SocketAddress kTurnInvalidAddr("www.google.invalid", 3478); +static const SocketAddress kTurnInvalidAddr("www.google.invalid.", 3478); static const char kCandidateFoundation[] = "foundation"; static const char kIceUfrag1[] = "TESTICEUFRAG0001"; @@ -85,7 +85,6 @@ static const char kIcePwd1[] = "TESTICEPWD00000000000001"; static const char kIcePwd2[] = "TESTICEPWD00000000000002"; static const char kTurnUsername[] = "test"; static const char kTurnPassword[] = "test"; -static const char kTestOrigin[] = "http://example.com"; // This test configures the virtual socket server to simulate delay so that we // can verify operations take no more than the expected number of round trips. static constexpr unsigned int kSimulatedRtt = 50; @@ -113,6 +112,9 @@ static const cricket::ProtocolAddress kTurnPort80ProtoAddr(kTurnPort80Addr, cricket::PROTO_TCP); static const cricket::ProtocolAddress kTurnPort443ProtoAddr(kTurnPort443Addr, cricket::PROTO_TCP); +static const cricket::ProtocolAddress kTurnPortHostnameProtoAddr( + kTurnInvalidAddr, + cricket::PROTO_UDP); static const unsigned int MSG_TESTFINISH = 0; @@ -152,6 +154,12 @@ class TestConnectionWrapper : public sigslot::has_slots<> { this, &TestConnectionWrapper::OnConnectionDestroyed); } + ~TestConnectionWrapper() { + if (connection_) { + connection_->SignalDestroyed.disconnect(this); + } + } + Connection* connection() { return connection_; } private: @@ -172,8 +180,8 @@ class TurnPortTest : public ::testing::Test, TurnPortTest() : ss_(new TurnPortTestVirtualSocketServer()), main_(ss_.get()), - socket_factory_(rtc::Thread::Current()), - turn_server_(&main_, kTurnUdpIntAddr, kTurnUdpExtAddr), + socket_factory_(ss_.get()), + turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), turn_ready_(false), turn_error_(false), turn_unknown_address_(false), @@ -259,25 +267,14 @@ class TurnPortTest : public ::testing::Test, const std::string& password, const ProtocolAddress& server_address) { return CreateTurnPortWithAllParams(MakeNetwork(kLocalAddr1), username, - password, server_address, std::string()); + password, server_address); } bool CreateTurnPort(const rtc::SocketAddress& local_address, const std::string& username, const std::string& password, const ProtocolAddress& server_address) { return CreateTurnPortWithAllParams(MakeNetwork(local_address), username, - password, server_address, std::string()); - } - - // Should be identical to CreateTurnPort but specifies an origin value - // when creating the instance of TurnPort. - bool CreateTurnPortWithOrigin(const rtc::SocketAddress& local_address, - const std::string& username, - const std::string& password, - const ProtocolAddress& server_address, - const std::string& origin) { - return CreateTurnPortWithAllParams(MakeNetwork(local_address), username, - password, server_address, origin); + password, server_address); } bool CreateTurnPortWithNetwork(rtc::Network* network, @@ -285,7 +282,7 @@ class TurnPortTest : public ::testing::Test, const std::string& password, const ProtocolAddress& server_address) { return CreateTurnPortWithAllParams(network, username, password, - server_address, std::string()); + server_address); } // Version of CreateTurnPort that takes all possible parameters; all other @@ -294,12 +291,12 @@ class TurnPortTest : public ::testing::Test, bool CreateTurnPortWithAllParams(rtc::Network* network, const std::string& username, const std::string& password, - const ProtocolAddress& server_address, - const std::string& origin) { + const ProtocolAddress& server_address) { RelayCredentials credentials(username, password); - turn_port_ = TurnPort::Create( - &main_, &socket_factory_, network, 0, 0, kIceUfrag1, kIcePwd1, - server_address, credentials, 0, origin, {}, {}, turn_customizer_.get()); + turn_port_ = + TurnPort::Create(&main_, &socket_factory_, network, 0, 0, kIceUfrag1, + kIcePwd1, server_address, credentials, 0, {}, {}, + turn_customizer_.get(), nullptr, &field_trials_); if (!turn_port_) { return false; } @@ -334,7 +331,7 @@ class TurnPortTest : public ::testing::Test, turn_port_ = TurnPort::Create(&main_, &socket_factory_, MakeNetwork(kLocalAddr1), socket_.get(), kIceUfrag1, kIcePwd1, server_address, - credentials, 0, std::string(), nullptr); + credentials, 0, nullptr, &field_trials_); // This TURN port will be the controlling. turn_port_->SetIceRole(ICEROLE_CONTROLLING); ConnectSignals(); @@ -355,15 +352,15 @@ class TurnPortTest : public ::testing::Test, turn_port_->SignalTurnPortClosed.connect(this, &TurnPortTest::OnTurnPortClosed); // RingRTC change to support ICE forking - turn_port_->SignalDestroyed.connect(this, &TurnPortTest::OnTurnPortClosed); + turn_port_->SignalDestroyed.connect(this, &TurnPortTest::OnTurnPortDestroyed); } void CreateUdpPort() { CreateUdpPort(kLocalAddr2); } void CreateUdpPort(const SocketAddress& address) { - udp_port_ = UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address), - 0, 0, kIceUfrag2, kIcePwd2, std::string(), - false, absl::nullopt); + udp_port_ = + UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address), 0, 0, + kIceUfrag2, kIcePwd2, false, absl::nullopt); // UDP port will be controlled. udp_port_->SetIceRole(ICEROLE_CONTROLLED); udp_port_->SignalPortComplete.connect(this, @@ -765,6 +762,7 @@ class TurnPortTest : public ::testing::Test, } protected: + webrtc::test::ScopedKeyValueConfig field_trials_; rtc::ScopedFakeClock fake_clock_; // When a "create port" helper method is called with an IP, we create a // Network with that IP and add it to this list. Using a list instead of a @@ -826,6 +824,18 @@ TEST_F(TurnPortTest, TestReconstructedServerUrlForTls) { TestReconstructedServerUrl(PROTO_TLS, "turns:99.99.99.4:3478?transport=tcp"); } +TEST_F(TurnPortTest, TestReconstructedServerUrlForHostname) { + CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortHostnameProtoAddr); + // This test follows the pattern from TestTurnTcpOnAddressResolveFailure. + // As VSS doesn't provide DNS resolution, name resolve will fail, + // the error will be set and contain the url. + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout); + std::string server_url = + "turn:" + kTurnInvalidAddr.ToString() + "?transport=udp"; + ASSERT_EQ(error_event_.url, server_url); +} + // Do a normal TURN allocation. TEST_F(TurnPortTest, TestTurnAllocate) { CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); @@ -1028,7 +1038,7 @@ TEST_F(TurnPortTest, TestTurnTcpOnAddressResolveFailure) { ProtocolAddress(kTurnInvalidAddr, PROTO_TCP)); turn_port_->PrepareAddress(); EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout); - // As VSS doesn't provide a DNS resolution, name resolve will fail. TurnPort + // As VSS doesn't provide DNS resolution, name resolve will fail. TurnPort // will proceed in creating a TCP socket which will fail as there is no // server on the above domain and error will be set to SOCKET_ERROR. EXPECT_EQ(SOCKET_ERROR, turn_port_->error()); @@ -1551,17 +1561,6 @@ TEST_F(TurnPortTest, TestCandidateAddressFamilyMatch) { EXPECT_EQ(nullptr, conn); } -TEST_F(TurnPortTest, TestOriginHeader) { - CreateTurnPortWithOrigin(kLocalAddr1, kTurnUsername, kTurnPassword, - kTurnUdpProtoAddr, kTestOrigin); - turn_port_->PrepareAddress(); - EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_); - ASSERT_GT(turn_server_.server()->allocations().size(), 0U); - SocketAddress local_address = turn_port_->GetLocalAddress(); - ASSERT_TRUE(turn_server_.FindAllocation(local_address) != NULL); - EXPECT_EQ(kTestOrigin, turn_server_.FindAllocation(local_address)->origin()); -} - // Test that a CreatePermission failure will result in the connection being // pruned and failed. TEST_F(TurnPortTest, TestConnectionFailedAndPrunedOnCreatePermissionFailure) { @@ -1650,8 +1649,7 @@ TEST_F(TurnPortTest, TestResolverShutdown) { int last_fd_count = GetFDCount(); // Need to supply unresolved address to kick off resolver. CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, - ProtocolAddress(rtc::SocketAddress("www.google.invalid", 3478), - PROTO_UDP)); + ProtocolAddress(kTurnInvalidAddr, PROTO_UDP)); turn_port_->PrepareAddress(); ASSERT_TRUE_WAIT(turn_error_, kResolverTimeout); EXPECT_TRUE(turn_port_->Candidates().empty()); @@ -1857,8 +1855,8 @@ TEST_F(TurnPortTest, TestTurnDangerousAlternateServer) { } TEST_F(TurnPortTest, TestTurnDangerousServerAllowedWithFieldTrial) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Turn-AllowSystemPorts/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Turn-AllowSystemPorts/Enabled/"); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnDangerousProtoAddr); ASSERT_TRUE(turn_port_); } diff --git a/p2p/base/turn_server.cc b/p2p/base/turn_server.cc index 01856f4e64..863319e916 100644 --- a/p2p/base/turn_server.cc +++ b/p2p/base/turn_server.cc @@ -152,12 +152,15 @@ void TurnServer::AddInternalSocket(rtc::AsyncPacketSocket* socket, socket->SignalReadPacket.connect(this, &TurnServer::OnInternalPacket); } -void TurnServer::AddInternalServerSocket(rtc::Socket* socket, - ProtocolType proto) { +void TurnServer::AddInternalServerSocket( + rtc::Socket* socket, + ProtocolType proto, + std::unique_ptr ssl_adapter_factory) { RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(server_listen_sockets_.end() == server_listen_sockets_.find(socket)); - server_listen_sockets_[socket] = proto; + server_listen_sockets_[socket] = {proto, std::move(ssl_adapter_factory)}; socket->SignalReadEvent.connect(this, &TurnServer::OnNewInternalConnection); } @@ -181,13 +184,19 @@ void TurnServer::AcceptConnection(rtc::Socket* server_socket) { rtc::SocketAddress accept_addr; rtc::Socket* accepted_socket = server_socket->Accept(&accept_addr); if (accepted_socket != NULL) { - ProtocolType proto = server_listen_sockets_[server_socket]; + const ServerSocketInfo& info = server_listen_sockets_[server_socket]; + if (info.ssl_adapter_factory) { + rtc::SSLAdapter* ssl_adapter = + info.ssl_adapter_factory->CreateAdapter(accepted_socket); + ssl_adapter->StartSSL(""); + accepted_socket = ssl_adapter; + } cricket::AsyncStunTCPSocket* tcp_socket = - new cricket::AsyncStunTCPSocket(accepted_socket, false); + new cricket::AsyncStunTCPSocket(accepted_socket); tcp_socket->SignalClose.connect(this, &TurnServer::OnInternalSocketClose); // Finally add the socket so it can start communicating with the client. - AddInternalSocket(tcp_socket, proto); + AddInternalSocket(tcp_socket, info.proto); } } @@ -655,11 +664,6 @@ void TurnServerAllocation::HandleAllocateRequest(const TurnMessage* msg) { msg->GetByteString(STUN_ATTR_USERNAME); RTC_DCHECK(username_attr != NULL); username_ = username_attr->GetString(); - const StunByteStringAttribute* origin_attr = - msg->GetByteString(STUN_ATTR_ORIGIN); - if (origin_attr) { - origin_ = origin_attr->GetString(); - } // Figure out the lifetime and start the allocation timer. int lifetime_secs = ComputeLifetime(msg); diff --git a/p2p/base/turn_server.h b/p2p/base/turn_server.h index 7942c09af9..bb70defd65 100644 --- a/p2p/base/turn_server.h +++ b/p2p/base/turn_server.h @@ -23,6 +23,7 @@ #include "p2p/base/port_interface.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/socket_address.h" +#include "rtc_base/ssl_adapter.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" @@ -79,7 +80,6 @@ class TurnServerAllocation : public rtc::MessageHandlerAutoCleanup, const std::string& key() const { return key_; } const std::string& transaction_id() const { return transaction_id_; } const std::string& username() const { return username_; } - const std::string& origin() const { return origin_; } const std::string& last_nonce() const { return last_nonce_; } void set_last_nonce(const std::string& nonce) { last_nonce_ = nonce; } @@ -135,7 +135,6 @@ class TurnServerAllocation : public rtc::MessageHandlerAutoCleanup, std::string key_; std::string transaction_id_; std::string username_; - std::string origin_; std::string last_nonce_; PermissionList perms_; ChannelList channels_; @@ -237,7 +236,10 @@ class TurnServer : public sigslot::has_slots<> { // Starts listening for the connections on this socket. When someone tries // to connect, the connection will be accepted and a new internal socket // will be added. - void AddInternalServerSocket(rtc::Socket* socket, ProtocolType proto); + void AddInternalServerSocket( + rtc::Socket* socket, + ProtocolType proto, + std::unique_ptr ssl_adapter_factory = nullptr); // Specifies the factory to use for creating external sockets. void SetExternalSocketFactory(rtc::PacketSocketFactory* factory, const rtc::SocketAddress& address); @@ -320,7 +322,12 @@ class TurnServer : public sigslot::has_slots<> { RTC_RUN_ON(thread_); typedef std::map InternalSocketMap; - typedef std::map ServerSocketMap; + struct ServerSocketInfo { + ProtocolType proto; + // If non-null, used to wrap accepted sockets. + std::unique_ptr ssl_adapter_factory; + }; + typedef std::map ServerSocketMap; rtc::Thread* const thread_; const std::string nonce_key_; diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc index b63f3351f9..4cb9bf1f77 100644 --- a/p2p/client/basic_port_allocator.cc +++ b/p2p/client/basic_port_allocator.cc @@ -20,6 +20,7 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" +#include "api/transport/field_trial_based_config.h" #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/ice_credentials_iterator.h" #include "p2p/base/ice_gatherer.h" @@ -33,7 +34,6 @@ #include "rtc_base/logging.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" using rtc::CreateRandomId; @@ -58,7 +58,7 @@ int GetProtocolPriority(cricket::ProtocolType protocol) { case cricket::PROTO_TLS: return 0; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } } @@ -70,7 +70,7 @@ int GetAddressFamilyPriority(int ip_family) { case AF_INET: return 1; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } } @@ -105,9 +105,9 @@ void FilterNetworks(NetworkList* networks, NetworkFilter filter) { if (start_to_remove == networks->end()) { return; } - RTC_LOG(INFO) << "Filtered out " << filter.description << " networks:"; + RTC_LOG(LS_INFO) << "Filtered out " << filter.description << " networks:"; for (auto it = start_to_remove; it != networks->end(); ++it) { - RTC_LOG(INFO) << (*it)->ToString(); + RTC_LOG(LS_INFO) << (*it)->ToString(); } networks->erase(start_to_remove, networks->end()); } @@ -154,7 +154,7 @@ BasicPortAllocator::BasicPortAllocator( webrtc::TurnCustomizer* customizer, RelayPortFactoryInterface* relay_port_factory) : network_manager_(network_manager), socket_factory_(socket_factory) { - InitRelayPortFactory(relay_port_factory); + Init(relay_port_factory, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); RTC_DCHECK(socket_factory_ != nullptr); @@ -164,7 +164,7 @@ BasicPortAllocator::BasicPortAllocator( BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager) : network_manager_(network_manager), socket_factory_(nullptr) { - InitRelayPortFactory(nullptr); + Init(nullptr, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); } @@ -179,8 +179,9 @@ BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager, rtc::PacketSocketFactory* socket_factory, const ServerAddresses& stun_servers) : network_manager_(network_manager), socket_factory_(socket_factory) { - InitRelayPortFactory(nullptr); + Init(nullptr, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); + RTC_DCHECK(network_manager_ != nullptr); SetConfiguration(stun_servers, std::vector(), 0, webrtc::NO_PRUNE, nullptr); } @@ -265,7 +266,7 @@ BasicPortAllocator::CreateIceGatherer(const std::string& name) { // 1. Create with NetworkManager, PacketSocketFactory, and RelayPortFactory. auto new_allocator = std::make_unique(network_manager()); new_allocator->socket_factory_ = socket_factory_; - new_allocator->InitRelayPortFactory(relay_port_factory_); + new_allocator->Init(relay_port_factory_, nullptr); // 2. SetNetworkIgnoreMask(). new_allocator->SetNetworkIgnoreMask(network_ignore_mask_); @@ -281,7 +282,6 @@ BasicPortAllocator::CreateIceGatherer(const std::string& name) { new_allocator->set_step_delay(step_delay()); new_allocator->set_allow_tcp_listen(allow_tcp_listen()); new_allocator->set_candidate_filter(candidate_filter()); - new_allocator->set_origin(origin()); // 5. SetConfiguration new_allocator->SetConfiguration(stun_servers(), turn_servers(), @@ -294,7 +294,7 @@ BasicPortAllocator::CreateIceGatherer(const std::string& name) { auto session = new_allocator->CreateSession(name, 1, parameters.ufrag, parameters.pwd); - return new rtc::RefCountedObject( + return rtc::make_ref_counted( rtc::Thread::Current(), std::move(new_allocator), std::move(session)); } @@ -307,14 +307,22 @@ void BasicPortAllocator::AddTurnServer(const RelayServerConfig& turn_server) { turn_port_prune_policy(), turn_customizer()); } -void BasicPortAllocator::InitRelayPortFactory( - RelayPortFactoryInterface* relay_port_factory) { +void BasicPortAllocator::Init( + RelayPortFactoryInterface* relay_port_factory, + const webrtc::WebRtcKeyValueConfig* field_trials) { if (relay_port_factory != nullptr) { relay_port_factory_ = relay_port_factory; } else { default_relay_port_factory_.reset(new TurnPortFactory()); relay_port_factory_ = default_relay_port_factory_.get(); } + + if (field_trials != nullptr) { + field_trials_ = field_trials; + } else { + owned_field_trials_ = std::make_unique(); + field_trials_ = owned_field_trials_.get(); + } } // BasicPortAllocatorSession @@ -435,7 +443,7 @@ void BasicPortAllocatorSession::StartGettingPorts() { state_ = SessionState::GATHERING; if (!socket_factory_) { owned_socket_factory_.reset( - new rtc::BasicPacketSocketFactory(network_thread_)); + new rtc::BasicPacketSocketFactory(network_thread_->socketserver())); socket_factory_ = owned_socket_factory_.get(); } @@ -658,8 +666,9 @@ void BasicPortAllocatorSession::UpdateIceParametersInternal() { void BasicPortAllocatorSession::GetPortConfigurations() { RTC_DCHECK_RUN_ON(network_thread_); - auto config = std::make_unique(allocator_->stun_servers(), - username(), password()); + auto config = std::make_unique( + allocator_->stun_servers(), username(), password(), + allocator()->field_trials()); for (const RelayServerConfig& turn_server : allocator_->turn_servers()) { config->AddRelay(turn_server); @@ -1230,7 +1239,7 @@ void BasicPortAllocatorSession::OnPortDestroyed(PortInterface* port) { return; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort( @@ -1454,7 +1463,7 @@ void AllocationSequence::Process(int epoch) { break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (state() == kRunning) { @@ -1487,14 +1496,14 @@ void AllocationSequence::CreateUDPPorts() { port = UDPPort::Create( session_->network_thread(), session_->socket_factory(), network_, udp_socket_.get(), session_->username(), session_->password(), - session_->allocator()->origin(), emit_local_candidate_for_anyaddress, + emit_local_candidate_for_anyaddress, session_->allocator()->stun_candidate_keepalive_interval()); } else { port = UDPPort::Create( session_->network_thread(), session_->socket_factory(), network_, session_->allocator()->min_port(), session_->allocator()->max_port(), session_->username(), session_->password(), - session_->allocator()->origin(), emit_local_candidate_for_anyaddress, + emit_local_candidate_for_anyaddress, session_->allocator()->stun_candidate_keepalive_interval()); } @@ -1559,7 +1568,6 @@ void AllocationSequence::CreateStunPorts() { session_->network_thread(), session_->socket_factory(), network_, session_->allocator()->min_port(), session_->allocator()->max_port(), session_->username(), session_->password(), config_->StunServers(), - session_->allocator()->origin(), session_->allocator()->stun_candidate_keepalive_interval()); if (port) { session_->AddAllocatedPort(port.release(), this); @@ -1621,8 +1629,8 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { args.password = session_->password(); args.server_address = &(*relay_port); args.config = &config; - args.origin = session_->allocator()->origin(); args.turn_customizer = session_->allocator()->turn_customizer(); + args.field_trials = session_->allocator()->field_trials(); std::unique_ptr port; // Shared socket mode must be enabled only for UDP based ports. Hence @@ -1711,28 +1719,23 @@ void AllocationSequence::OnPortDestroyed(PortInterface* port) { relay_ports_.erase(it); } else { RTC_LOG(LS_ERROR) << "Unexpected OnPortDestroyed for nonexistent port."; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } -// PortConfiguration -PortConfiguration::PortConfiguration(const rtc::SocketAddress& stun_address, - const std::string& username, - const std::string& password) - : stun_address(stun_address), username(username), password(password) { - if (!stun_address.IsNil()) - stun_servers.insert(stun_address); -} - -PortConfiguration::PortConfiguration(const ServerAddresses& stun_servers, - const std::string& username, - const std::string& password) +PortConfiguration::PortConfiguration( + const ServerAddresses& stun_servers, + const std::string& username, + const std::string& password, + const webrtc::WebRtcKeyValueConfig* field_trials) : stun_servers(stun_servers), username(username), password(password) { if (!stun_servers.empty()) stun_address = *(stun_servers.begin()); // Note that this won't change once the config is initialized. - use_turn_server_as_stun_server_disabled = - webrtc::field_trial::IsDisabled("WebRTC-UseTurnServerAsStunServer"); + if (field_trials) { + use_turn_server_as_stun_server_disabled = + field_trials->IsDisabled("WebRTC-UseTurnServerAsStunServer"); + } } ServerAddresses PortConfiguration::StunServers() { diff --git a/p2p/client/basic_port_allocator.h b/p2p/client/basic_port_allocator.h index 16c18ed57b..ef724bc536 100644 --- a/p2p/client/basic_port_allocator.h +++ b/p2p/client/basic_port_allocator.h @@ -16,7 +16,7 @@ #include #include "api/turn_customizer.h" -#include "p2p/base/port.h" +#include "api/webrtc_key_value_config.h" #include "p2p/base/port_allocator.h" #include "p2p/client/relay_port_factory_interface.h" #include "p2p/client/turn_port_factory.h" @@ -31,8 +31,12 @@ namespace cricket { class RTC_EXPORT BasicPortAllocator : public PortAllocator { public: - // note: The (optional) relay_port_factory is owned by caller - // and must have a life time that exceeds that of BasicPortAllocator. + // The NetworkManager is a mandatory argument. The other arguments are + // optional. All these objects are owned by caller and must have a life time + // that exceeds that of BasicPortAllocator. + // TODO(bugs.webrtc.org/13145): The SocketFactory should be mandatory, but + // currenly isn't. When not specified, one is created internally, based on the + // socket server associated with the thread calling CreateSession. BasicPortAllocator(rtc::NetworkManager* network_manager, rtc::PacketSocketFactory* socket_factory, webrtc::TurnCustomizer* customizer = nullptr, @@ -81,15 +85,23 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator { void SetVpnList(const std::vector& vpn_list) override; + const webrtc::WebRtcKeyValueConfig* field_trials() const { + return field_trials_; + } + private: void OnIceRegathering(PortAllocatorSession* session, IceRegatheringReason reason); - // This function makes sure that relay_port_factory_ is set properly. - void InitRelayPortFactory(RelayPortFactoryInterface* relay_port_factory); + // This function makes sure that relay_port_factory_ and field_trials_ is set + // properly. + void Init(RelayPortFactoryInterface* relay_port_factory, + const webrtc::WebRtcKeyValueConfig* field_trials); bool MdnsObfuscationEnabled() const override; + const webrtc::WebRtcKeyValueConfig* field_trials_; + std::unique_ptr owned_field_trials_; rtc::NetworkManager* network_manager_; rtc::PacketSocketFactory* socket_factory_; int network_ignore_mask_ = rtc::kDefaultNetworkIgnoreMask; @@ -299,14 +311,10 @@ struct RTC_EXPORT PortConfiguration { typedef std::vector RelayList; RelayList relays; - // TODO(jiayl): remove this ctor when Chrome is updated. - PortConfiguration(const rtc::SocketAddress& stun_address, - const std::string& username, - const std::string& password); - PortConfiguration(const ServerAddresses& stun_servers, const std::string& username, - const std::string& password); + const std::string& password, + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr); // Returns addresses of both the explicitly configured STUN servers, // and TURN servers that should be used as STUN servers. diff --git a/p2p/client/basic_port_allocator_unittest.cc b/p2p/client/basic_port_allocator_unittest.cc index 0db560cd59..77b1f41bd1 100644 --- a/p2p/client/basic_port_allocator_unittest.cc +++ b/p2p/client/basic_port_allocator_unittest.cc @@ -43,9 +43,9 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::IPAddress; using rtc::SocketAddress; @@ -154,7 +154,10 @@ class BasicPortAllocatorTestBase : public ::testing::Test, nat_factory_(vss_.get(), kNatUdpAddr, kNatTcpAddr), nat_socket_factory_(new rtc::BasicPacketSocketFactory(&nat_factory_)), stun_server_(TestStunServer::Create(fss_.get(), kStunAddr)), - turn_server_(rtc::Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr), + turn_server_(rtc::Thread::Current(), + fss_.get(), + kTurnUdpIntAddr, + kTurnUdpExtAddr), candidate_allocation_done_(false) { ServerAddresses stun_servers; stun_servers.insert(kStunAddr); @@ -2442,12 +2445,12 @@ TEST_F(BasicPortAllocatorTest, TestUseTurnServerAsStunSever) { } TEST_F(BasicPortAllocatorTest, TestDoNotUseTurnServerAsStunSever) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-UseTurnServerAsStunServer/Disabled/"); ServerAddresses stun_servers; stun_servers.insert(kStunAddr); PortConfiguration port_config(stun_servers, "" /* user_name */, - "" /* password */); + "" /* password */, &field_trials); RelayServerConfig turn_servers = CreateTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); port_config.AddRelay(turn_servers); @@ -2461,7 +2464,6 @@ TEST_F(BasicPortAllocatorTest, TestCreateIceGathererForForking) { allocator_->set_step_delay(5); allocator_->set_allow_tcp_listen(false); allocator_->set_candidate_filter(5); - allocator_->set_origin("test"); allocator_->set_max_ipv6_networks(6); allocator_->SetNetworkIgnoreMask(7); AddTurnServers(kTurnUdpIntAddr, rtc::SocketAddress()); @@ -2481,7 +2483,6 @@ TEST_F(BasicPortAllocatorTest, TestCreateIceGathererForForking) { EXPECT_EQ(allocator_->step_delay(), forked->step_delay()); EXPECT_EQ(allocator_->allow_tcp_listen(), forked->allow_tcp_listen()); EXPECT_EQ(allocator_->candidate_filter(), forked->candidate_filter()); - EXPECT_EQ(allocator_->origin(), forked->origin()); EXPECT_EQ(allocator_->max_ipv6_networks(), forked->max_ipv6_networks()); // EXPECT_EQ(allocator_->network_ignore_mask(), forked->network_ignore_mask()); EXPECT_EQ(allocator_->stun_servers(), forked->stun_servers()); diff --git a/p2p/client/relay_port_factory_interface.h b/p2p/client/relay_port_factory_interface.h index c1cf51e5c3..cb4eb97483 100644 --- a/p2p/client/relay_port_factory_interface.h +++ b/p2p/client/relay_port_factory_interface.h @@ -26,6 +26,7 @@ class Thread; namespace webrtc { class TurnCustomizer; +class WebRtcKeyValueConfig; } // namespace webrtc namespace cricket { @@ -43,8 +44,8 @@ struct CreateRelayPortArgs { const RelayServerConfig* config; std::string username; std::string password; - std::string origin; webrtc::TurnCustomizer* turn_customizer; + const webrtc::WebRtcKeyValueConfig* field_trials = nullptr; }; inline CreateRelayPortArgs::CreateRelayPortArgs() {} diff --git a/p2p/client/turn_port_factory.cc b/p2p/client/turn_port_factory.cc index fd3420c016..07321b85d6 100644 --- a/p2p/client/turn_port_factory.cc +++ b/p2p/client/turn_port_factory.cc @@ -26,8 +26,8 @@ std::unique_ptr TurnPortFactory::Create( auto port = TurnPort::CreateUnique( args.network_thread, args.socket_factory, args.network, udp_socket, args.username, args.password, *args.server_address, - args.config->credentials, args.config->priority, args.origin, - args.turn_customizer); + args.config->credentials, args.config->priority, args.turn_customizer, + args.field_trials); if (!port) return nullptr; port->SetTlsCertPolicy(args.config->tls_cert_policy); @@ -41,9 +41,9 @@ std::unique_ptr TurnPortFactory::Create(const CreateRelayPortArgs& args, auto port = TurnPort::CreateUnique( args.network_thread, args.socket_factory, args.network, min_port, max_port, args.username, args.password, *args.server_address, - args.config->credentials, args.config->priority, args.origin, + args.config->credentials, args.config->priority, args.config->tls_alpn_protocols, args.config->tls_elliptic_curves, - args.turn_customizer, args.config->tls_cert_verifier); + args.turn_customizer, args.config->tls_cert_verifier, args.field_trials); if (!port) return nullptr; port->SetTlsCertPolicy(args.config->tls_cert_policy); diff --git a/p2p/stunprober/stun_prober.cc b/p2p/stunprober/stun_prober.cc index 4195230313..efe0fbdea8 100644 --- a/p2p/stunprober/stun_prober.cc +++ b/p2p/stunprober/stun_prober.cc @@ -21,7 +21,6 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_resolver_interface.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/task_utils/to_queued_task.h" @@ -69,6 +68,9 @@ class StunProber::Requester : public sigslot::has_slots<> { const std::vector& server_ips); ~Requester() override; + Requester(const Requester&) = delete; + Requester& operator=(const Requester&) = delete; + // There is no callback for SendStunRequest as the underneath socket send is // expected to be completed immediately. Otherwise, it'll skip this request // and move to the next one. @@ -105,8 +107,6 @@ class StunProber::Requester : public sigslot::has_slots<> { int16_t num_response_received_ = 0; webrtc::SequenceChecker& thread_checker_; - - RTC_DISALLOW_COPY_AND_ASSIGN(Requester); }; StunProber::Requester::Requester( diff --git a/p2p/stunprober/stun_prober.h b/p2p/stunprober/stun_prober.h index fe2f14ca4e..b1acd7704d 100644 --- a/p2p/stunprober/stun_prober.h +++ b/p2p/stunprober/stun_prober.h @@ -17,7 +17,6 @@ #include "api/sequence_checker.h" #include "rtc_base/byte_buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ip_address.h" #include "rtc_base/network.h" #include "rtc_base/socket_address.h" @@ -101,6 +100,9 @@ class RTC_EXPORT StunProber : public sigslot::has_slots<> { const rtc::NetworkManager::NetworkList& networks); ~StunProber() override; + StunProber(const StunProber&) = delete; + StunProber& operator=(const StunProber&) = delete; + // Begin performing the probe test against the `servers`. If // `shared_socket_mode` is false, each request will be done with a new socket. // Otherwise, a unique socket will be used for a single round of requests @@ -119,7 +121,7 @@ class RTC_EXPORT StunProber : public sigslot::has_slots<> { int stun_ta_interval_ms, int requests_per_ip, int timeout_ms, - const AsyncCallback finish_callback); + AsyncCallback finish_callback); // TODO(guoweis): The combination of Prepare() and Run() are equivalent to the // Start() above. Remove Start() once everything is migrated. @@ -241,8 +243,6 @@ class RTC_EXPORT StunProber : public sigslot::has_slots<> { rtc::NetworkManager::NetworkList networks_; webrtc::ScopedTaskSafety task_safety_; - - RTC_DISALLOW_COPY_AND_ASSIGN(StunProber); }; } // namespace stunprober diff --git a/pc/BUILD.gn b/pc/BUILD.gn index d5d11f6156..bafe028edc 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -6,6 +6,30 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. +# Visibility considerations: +# +# Most targets in this file should have visibility ":*", as they are only +# used internally. +# Some functions are cleared for wider webrtc usage; these have default +# visibility (set to "//*", not the gn default of "*"). +# These are: +# - rtc_pc_base +# - rtc_pc +# - session_description +# - simulcast_description +# - peerconnection +# - sdp_utils +# - media_stream_observer +# - video_track_source +# - libjingle_peerconnection +# +# Some targets are depended on by external users for historical reasons, +# and are therefore marked with visibility "*". This is in the process +# of being removed. +# +# Some targets are only publicly visible in Chrome builds. +# These are marked up as such. + import("../webrtc.gni") if (is_android) { import("//build/config/android/config.gni") @@ -24,6 +48,7 @@ config("rtc_pc_config") { } rtc_library("proxy") { + visibility = [ ":*" ] sources = [ "proxy.cc", "proxy.h", @@ -38,67 +63,57 @@ rtc_library("proxy") { } rtc_library("rtc_pc_base") { - visibility = [ "*" ] + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Used by Chromium and others defines = [] sources = [ - "channel.cc", - "channel.h", - "channel_interface.h", "channel_manager.cc", "channel_manager.h", - "dtls_srtp_transport.cc", - "dtls_srtp_transport.h", - "dtls_transport.cc", - "dtls_transport.h", - "external_hmac.cc", - "external_hmac.h", - "ice_transport.cc", - "ice_transport.h", - "jsep_transport.cc", - "jsep_transport.h", "jsep_transport_collection.cc", "jsep_transport_collection.h", "jsep_transport_controller.cc", "jsep_transport_controller.h", "media_session.cc", "media_session.h", - "media_stream_proxy.h", - "media_stream_track_proxy.h", - "peer_connection_factory_proxy.h", - "peer_connection_proxy.h", - "rtcp_mux_filter.cc", - "rtcp_mux_filter.h", - "rtp_media_utils.cc", - "rtp_media_utils.h", - "rtp_receiver_proxy.h", - "rtp_sender_proxy.h", - "rtp_transport.cc", - "rtp_transport.h", - "rtp_transport_internal.h", - "sctp_data_channel_transport.cc", - "sctp_data_channel_transport.h", - "sctp_transport.cc", - "sctp_transport.h", - "sctp_utils.cc", - "sctp_utils.h", - "srtp_filter.cc", - "srtp_filter.h", - "srtp_session.cc", - "srtp_session.h", - "srtp_transport.cc", - "srtp_transport.h", - "transport_stats.cc", - "transport_stats.h", - "used_ids.h", "video_track_source_proxy.cc", "video_track_source_proxy.h", ] deps = [ + ":channel", + ":channel_interface", + ":channel_manager", + ":dtls_srtp_transport", + ":dtls_transport", + ":external_hmac", + ":ice_transport", + ":jsep_transport", + ":jsep_transport_collection", + ":jsep_transport_controller", ":media_protocol_names", + ":media_session", + ":media_stream_proxy", + ":media_stream_track_proxy", + ":peer_connection_factory_proxy", + ":peer_connection_proxy", ":proxy", + ":rtcp_mux_filter", + ":rtp_media_utils", + ":rtp_receiver_proxy", + ":rtp_sender_proxy", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_data_channel_transport", + ":sctp_transport", + ":sctp_utils", ":session_description", ":simulcast_description", + ":srtp_filter", + ":srtp_session", + ":srtp_transport", + ":transport_stats", + ":used_ids", + ":video_track_source_proxy", "../api:array_view", "../api:async_dns_resolver", "../api:audio_options_api", @@ -116,13 +131,17 @@ rtc_library("rtc_pc_base") { "../api:rtp_transceiver_direction", "../api:scoped_refptr", "../api:sequence_checker", + "../api:video_track_source_constraints", + "../api:webrtc_key_value_config", "../api/crypto:options", "../api/rtc_event_log", "../api/task_queue", "../api/transport:datagram_transport_interface", "../api/transport:enums", "../api/transport:sctp_transport_factory_interface", + "../api/units:timestamp", "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:recordable_encoded_frame", "../api/video:video_bitrate_allocator_factory", "../api/video:video_frame", "../api/video:video_rtp_headers", @@ -130,6 +149,7 @@ rtc_library("rtc_pc_base") { "../call:call_interfaces", "../call:rtp_interfaces", "../call:rtp_receiver", + "../call:video_stream_api", "../common_video", "../common_video:common_video", "../logging:ice_log", @@ -148,6 +168,8 @@ rtc_library("rtc_pc_base") { "../rtc_base:socket_address", "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base/containers:flat_map", + "../rtc_base/containers:flat_set", "../rtc_base/network:sent_packet", "../rtc_base/synchronization:mutex", "../rtc_base/system:file_wrapper", @@ -167,7 +189,6 @@ rtc_library("rtc_pc_base") { "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] - if (rtc_build_libsrtp) { deps += [ "//third_party/libsrtp" ] } @@ -175,8 +196,445 @@ rtc_library("rtc_pc_base") { public_configs = [ ":rtc_pc_config" ] } +# Targets in preparation for breaking up rtc_pc_base target +rtc_source_set("channel") { + visibility = [ ":*" ] + sources = [ + "channel.cc", + "channel.h", + ] + deps = [ + ":channel_interface", + ":rtp_media_utils", + ":rtp_transport_internal", + ":session_description", + "../api:libjingle_peerconnection_api", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/crypto:options", + "../api/units:timestamp", + "../call:rtp_interfaces", + "../call:rtp_receiver", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:rtc_base_approved", + "../rtc_base:socket", + "../rtc_base:threading", + "../rtc_base/containers:flat_set", + "../rtc_base/network:sent_packet", + "../rtc_base/task_utils:pending_task_safety_flag", + "../rtc_base/task_utils:to_queued_task", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("channel_interface") { + visibility = [ ":*" ] + sources = [ "channel_interface.h" ] + deps = [ + ":rtp_transport_internal", + "../api:libjingle_peerconnection_api", + "../api:rtp_parameters", + "../media:rtc_media_base", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_source_set("channel_manager") { + visibility = [ ":*" ] +} + +rtc_source_set("dtls_srtp_transport") { + visibility = [ ":*" ] + sources = [ + "dtls_srtp_transport.cc", + "dtls_srtp_transport.h", + ] + deps = [ + ":srtp_transport", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("dtls_transport") { + visibility = [ ":*" ] + sources = [ + "dtls_transport.cc", + "dtls_transport.h", + ] + deps = [ + ":ice_transport", + "../api:libjingle_peerconnection_api", + "../api:scoped_refptr", + "../api:sequence_checker", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:threading", + "../rtc_base/synchronization:mutex", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("external_hmac") { + visibility = [ ":*" ] + sources = [ + "external_hmac.cc", + "external_hmac.h", + ] + deps = [ "../rtc_base:rtc_base_approved" ] + if (rtc_build_libsrtp) { + deps += [ "//third_party/libsrtp" ] + } +} + +rtc_source_set("ice_transport") { + visibility = [ ":*" ] + sources = [ + "ice_transport.cc", + "ice_transport.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../api:sequence_checker", + "../rtc_base:checks", + "../rtc_base:macromagic", + "../rtc_base:threading", + ] +} +rtc_source_set("jsep_transport") { + visibility = [ ":*" ] + sources = [ + "jsep_transport.cc", + "jsep_transport.h", + ] + deps = [ + ":dtls_srtp_transport", + ":dtls_transport", + ":rtcp_mux_filter", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_data_channel_transport", + ":sctp_transport", + ":session_description", + ":srtp_filter", + ":srtp_transport", + ":transport_stats", + "../api:array_view", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:scoped_refptr", + "../api/transport:datagram_transport_interface", + "../media:rtc_data_sctp_transport_internal", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:refcount", + "../rtc_base:rtc_base_approved", + "../rtc_base:threading", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} +rtc_source_set("jsep_transport_collection") { + visibility = [ ":*" ] +} +rtc_source_set("jsep_transport_controller") { + visibility = [ ":*" ] +} +rtc_source_set("media_session") { + visibility = [ "*" ] # Used by Chrome +} +rtc_source_set("media_stream_proxy") { + visibility = [ ":*" ] + sources = [ "media_stream_proxy.h" ] + deps = [ + ":proxy", + "../api:media_stream_interface", + ] +} +rtc_source_set("media_stream_track_proxy") { + visibility = [ ":*" ] + sources = [ "media_stream_track_proxy.h" ] + deps = [ + ":proxy", + "../api:media_stream_interface", + ] +} +rtc_source_set("peer_connection_factory_proxy") { + visibility = [ ":*" ] + sources = [ "peer_connection_factory_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} +rtc_source_set("peer_connection_proxy") { + visibility = [ ":*" ] + sources = [ "peer_connection_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} +rtc_source_set("rtcp_mux_filter") { + visibility = [ ":*" ] + sources = [ + "rtcp_mux_filter.cc", + "rtcp_mux_filter.h", + ] + deps = [ + ":session_description", + "../rtc_base:logging", + ] +} +rtc_source_set("rtp_media_utils") { + visibility = [ ":*" ] + sources = [ + "rtp_media_utils.cc", + "rtp_media_utils.h", + ] + deps = [ + "../api:rtp_transceiver_direction", + "../rtc_base:checks", + ] +} +rtc_source_set("rtp_receiver_proxy") { + visibility = [ ":*" ] + sources = [ "rtp_receiver_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} +rtc_source_set("rtp_sender_proxy") { + visibility = [ ":*" ] + sources = [ "rtp_sender_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} +rtc_source_set("rtp_transport") { + visibility = [ ":*" ] + sources = [ + "rtp_transport.cc", + "rtp_transport.h", + ] + deps = [ + ":rtp_transport_internal", + ":session_description", + "../api:array_view", + "../api/units:timestamp", + "../call:rtp_receiver", + "../call:video_stream_api", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:rtc_base_approved", + "../rtc_base:socket", + "../rtc_base/network:sent_packet", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("rtp_transport_internal") { + visibility = [ + ":*", + "../test/peer_scenario", + ] + sources = [ "rtp_transport_internal.h" ] + deps = [ + ":session_description", + "../call:rtp_receiver", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base/third_party/sigslot", + ] +} +rtc_source_set("sctp_data_channel_transport") { + visibility = [ ":*" ] + sources = [ + "sctp_data_channel_transport.cc", + "sctp_data_channel_transport.h", + ] + deps = [ + "../api:rtc_error", + "../api/transport:datagram_transport_interface", + "../media:rtc_data_sctp_transport_internal", + "../media:rtc_media_base", + "../rtc_base:rtc_base_approved", + "../rtc_base/third_party/sigslot", + ] +} +rtc_source_set("sctp_transport") { + visibility = [ ":*" ] + sources = [ + "sctp_transport.cc", + "sctp_transport.h", + ] + deps = [ + ":dtls_transport", + "../api:libjingle_peerconnection_api", + "../api:scoped_refptr", + "../api:sequence_checker", + "../media:rtc_data_sctp_transport_internal", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_base_approved", + "../rtc_base:threading", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} +rtc_source_set("sctp_utils") { + visibility = [ + ":*", + "../test/fuzzers:*", + ] + sources = [ + "sctp_utils.cc", + "sctp_utils.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../api:priority", + "../api/transport:datagram_transport_interface", + "../media:rtc_media_base", + "../rtc_base:rtc_base_approved", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} +rtc_source_set("srtp_filter") { + visibility = [ ":*" ] + sources = [ + "srtp_filter.cc", + "srtp_filter.h", + ] + deps = [ + ":session_description", + "../api:array_view", + "../api:libjingle_peerconnection_api", + "../api:sequence_checker", + "../rtc_base", + "../rtc_base:logging", + "../rtc_base:rtc_base_approved", + "../rtc_base/third_party/base64", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("srtp_session") { + visibility = [ ":*" ] + sources = [ + "srtp_session.cc", + "srtp_session.h", + ] + deps = [ + ":external_hmac", + "../api:array_view", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api:webrtc_key_value_config", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:rtc_base_approved", + "../rtc_base/synchronization:mutex", + "../system_wrappers:metrics", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] + if (rtc_build_libsrtp) { + deps += [ "//third_party/libsrtp" ] + } +} +rtc_source_set("srtp_transport") { + visibility = [ ":*" ] + sources = [ + "srtp_transport.cc", + "srtp_transport.h", + ] + deps = [ + ":rtp_transport", + ":srtp_session", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:webrtc_key_value_config", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/third_party/base64", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("transport_stats") { + visibility = [ ":*" ] + sources = [ + "transport_stats.cc", + "transport_stats.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../p2p:rtc_p2p", + "../rtc_base", + ] +} +rtc_source_set("used_ids") { + visibility = [ ":*" ] + sources = [ "used_ids.h" ] + deps = [ + "../api:rtp_parameters", + "../media:rtc_media_base", + "../rtc_base:checks", + "../rtc_base:logging", + ] +} +rtc_source_set("video_track_source_proxy") { + visibility = [ ":*" ] +} + rtc_source_set("session_description") { - visibility = [ "*" ] + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Used by Chrome and others + sources = [ "session_description.cc", "session_description.h", @@ -191,6 +649,7 @@ rtc_source_set("session_description") { "../p2p:rtc_p2p", "../rtc_base:checks", "../rtc_base:socket_address", + "../rtc_base:stringutils", "../rtc_base/system:rtc_export", ] absl_deps = [ @@ -200,7 +659,6 @@ rtc_source_set("session_description") { } rtc_source_set("simulcast_description") { - visibility = [ "*" ] sources = [ "simulcast_description.cc", "simulcast_description.h", @@ -210,10 +668,13 @@ rtc_source_set("simulcast_description") { "../rtc_base:socket_address", "../rtc_base/system:rtc_export", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_source_set("rtc_pc") { - visibility = [ "*" ] + if (build_with_chromium) { + visibility = [ "*" ] + } allow_poison = [ "audio_codecs" ] # TODO(bugs.webrtc.org/8396): Remove. deps = [ ":rtc_pc_base", @@ -222,82 +683,67 @@ rtc_source_set("rtc_pc") { } rtc_library("media_protocol_names") { + visibility = [ ":*" ] sources = [ "media_protocol_names.cc", "media_protocol_names.h", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } -rtc_library("peerconnection") { - visibility = [ "*" ] +rtc_source_set("peerconnection") { + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Used by Chromium and others cflags = [] - sources = [ - "data_channel_controller.cc", - "data_channel_controller.h", - "data_channel_utils.cc", - "data_channel_utils.h", - "ice_server_parsing.cc", - "ice_server_parsing.h", - "jsep_ice_candidate.cc", - "jsep_session_description.cc", - "local_audio_source.cc", - "local_audio_source.h", - "media_stream_observer.cc", - "media_stream_observer.h", - "peer_connection.cc", - "peer_connection.h", - "peer_connection_factory.cc", - "peer_connection_factory.h", - "peer_connection_internal.h", - "rtc_stats_collector.cc", - "rtc_stats_collector.h", - "rtc_stats_traversal.cc", - "rtc_stats_traversal.h", - "sctp_data_channel.cc", - "sctp_data_channel.h", - "sdp_offer_answer.cc", # TODO: Make separate target when not circular - "sdp_offer_answer.h", # dependent on peerconnection.h - "sdp_serializer.cc", - "sdp_serializer.h", - "sdp_utils.cc", - "sdp_utils.h", - "stats_collector.cc", - "stats_collector.h", - "stream_collection.h", - "track_media_info_map.cc", - "track_media_info_map.h", - "webrtc_sdp.cc", - "webrtc_sdp.h", - "webrtc_session_description_factory.cc", - "webrtc_session_description_factory.h", - ] + sources = [] deps = [ ":audio_rtp_receiver", ":audio_track", ":connection_context", + ":data_channel_controller", + ":data_channel_utils", ":dtmf_sender", + ":ice_server_parsing", ":jitter_buffer_delay", + ":jsep_ice_candidate", + ":jsep_session_description", + ":local_audio_source", ":media_protocol_names", ":media_stream", + ":media_stream_observer", + ":peer_connection", + ":peer_connection_factory", + ":peer_connection_internal", ":peer_connection_message_handler", ":proxy", ":remote_audio_source", ":rtc_pc_base", + ":rtc_stats_collector", + ":rtc_stats_traversal", ":rtp_parameters_conversion", ":rtp_receiver", ":rtp_sender", ":rtp_transceiver", ":rtp_transmission_manager", + ":sctp_data_channel", + ":sdp_offer_answer", + ":sdp_serializer", ":sdp_state_provider", + ":sdp_utils", ":session_description", ":simulcast_description", + ":stats_collector", ":stats_collector_interface", + ":stream_collection", + ":track_media_info_map", ":transceiver_list", ":usage_pattern", ":video_rtp_receiver", ":video_track", ":video_track_source", + ":webrtc_sdp", + ":webrtc_session_description_factory", "../api:array_view", "../api:async_dns_resolver", "../api:audio_options_api", @@ -385,7 +831,57 @@ rtc_library("peerconnection") { ] } +rtc_library("sctp_data_channel") { + visibility = [ ":*" ] + sources = [ + "sctp_data_channel.cc", + "sctp_data_channel.h", + ] + deps = [ + ":data_channel_utils", + ":proxy", + ":rtc_pc_base", + ":sctp_utils", + "../api:libjingle_peerconnection_api", + "../api:priority", + "../api:rtc_error", + "../api:scoped_refptr", + "../api/transport:datagram_transport_interface", + "../media:rtc_data_sctp_transport_internal", + "../media:rtc_media_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_base_approved", + "../rtc_base:threading", + "../rtc_base:threading", + "../rtc_base/system:unused", + "../rtc_base/task_utils:to_queued_task", + "../rtc_base/third_party/sigslot:sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("data_channel_utils") { + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Known to be used externally + + sources = [ + "data_channel_utils.cc", + "data_channel_utils.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../media:rtc_media_base", + "../rtc_base:checks", + ] +} + rtc_library("connection_context") { + visibility = [ ":*" ] sources = [ "connection_context.cc", "connection_context.h", @@ -407,12 +903,580 @@ rtc_library("connection_context") { "../p2p:rtc_p2p", "../rtc_base", "../rtc_base:checks", + "../rtc_base:socket_factory", + "../rtc_base:socket_server", "../rtc_base:threading", "../rtc_base/task_utils:to_queued_task", ] } +rtc_source_set("data_channel_controller") { + visibility = [ ":*" ] + sources = [ + "data_channel_controller.cc", + "data_channel_controller.h", + ] + deps = [ + ":channel", + ":data_channel_utils", + ":peer_connection_internal", + ":rtc_pc_base", + ":sctp_data_channel", + ":sctp_utils", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/transport:datagram_transport_interface", + "../media:rtc_media_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:threading", + "../rtc_base:weak_ptr", + "../rtc_base/task_utils:to_queued_task", + "../rtc_base/third_party/sigslot:sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("peer_connection_internal") { + visibility = [ ":*" ] + sources = [ "peer_connection_internal.h" ] + deps = [ + ":peer_connection_message_handler", + ":rtc_pc_base", + ":rtp_transceiver", + ":rtp_transmission_manager", + ":sctp_data_channel", + "../api:libjingle_peerconnection_api", + "../call:call_interfaces", + ] +} +rtc_source_set("rtc_stats_collector") { + visibility = [ ":*" ] + sources = [ + "rtc_stats_collector.cc", + "rtc_stats_collector.h", + ] + deps = [ + ":channel", + ":channel_interface", + ":data_channel_utils", + ":peer_connection_internal", + ":rtc_pc_base", + ":rtc_stats_traversal", + ":rtp_receiver", + ":rtp_receiver_proxy", + ":rtp_sender", + ":rtp_sender_proxy", + ":rtp_transceiver", + ":sctp_data_channel", + ":track_media_info_map", + ":transport_stats", + ":webrtc_sdp", + "../api:array_view", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:rtc_stats_api", + "../api:rtp_parameters", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/task_queue:task_queue", + "../api/units:time_delta", + "../api/video:video_rtp_headers", + "../call:call_interfaces", + "../common_video:common_video", + "../media:rtc_media_base", + "../modules/audio_processing:audio_processing_statistics", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:network_constants", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_event", + "../rtc_base:socket_address", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base:timeutils", + "../rtc_base/third_party/sigslot:sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("rtc_stats_traversal") { + visibility = [ ":*" ] + sources = [ + "rtc_stats_traversal.cc", + "rtc_stats_traversal.h", + ] + deps = [ + "../api:rtc_stats_api", + "../api:scoped_refptr", + "../rtc_base:checks", + ] +} + +rtc_source_set("sdp_offer_answer") { + visibility = [ ":*" ] + sources = [ + "sdp_offer_answer.cc", # TODO: Make separate target when not circular + "sdp_offer_answer.h", # dependent on peerconnection.h + ] + deps = [ + ":channel", + ":channel_interface", + ":connection_context", + ":data_channel_controller", + ":data_channel_utils", + ":dtls_transport", + ":ice_server_parsing", + ":media_stream", + ":media_stream_observer", + ":media_stream_proxy", + ":peer_connection_internal", + ":peer_connection_message_handler", + ":rtc_pc_base", + ":rtc_stats_collector", + ":rtp_media_utils", + ":rtp_receiver", + ":rtp_receiver_proxy", + ":rtp_sender", + ":rtp_sender_proxy", + ":rtp_transceiver", + ":rtp_transmission_manager", + ":sctp_transport", + ":sdp_state_provider", + ":session_description", + ":simulcast_description", + ":stats_collector", + ":stream_collection", + ":transceiver_list", + ":usage_pattern", + ":webrtc_session_description_factory", + "../api:array_view", + "../api:audio_options_api", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/crypto:options", + "../api/transport:datagram_transport_interface", + "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:video_bitrate_allocator_factory", + "../media:rtc_media_base", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_operations_chain", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base:weak_ptr", + "../rtc_base/experiments:field_trial_parser", + "../rtc_base/third_party/sigslot:sigslot", + "../system_wrappers:field_trial", + "../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/memory:memory", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("jsep_ice_candidate") { + visibility = [ ":*" ] +} +rtc_source_set("jsep_session_description") { + visibility = [ ":*" ] +} +rtc_source_set("local_audio_source") { + visibility = [ ":*" ] + sources = [ + "local_audio_source.cc", + "local_audio_source.h", + ] + deps = [ + "../api:audio_options_api", + "../api:media_stream_interface", + "../api:scoped_refptr", + "../rtc_base:refcount", + ] +} +rtc_source_set("peer_connection") { + visibility = [ ":*" ] + sources = [ + "peer_connection.cc", + "peer_connection.h", + ] + deps = [ + ":channel", + ":channel_interface", + ":connection_context", + ":data_channel_controller", + ":data_channel_utils", + ":dtls_transport", + ":ice_server_parsing", + ":peer_connection_internal", + ":peer_connection_message_handler", + ":rtc_pc_base", + ":rtc_stats_collector", + ":rtp_receiver", + ":rtp_receiver_proxy", + ":rtp_sender", + ":rtp_sender_proxy", + ":rtp_transceiver", + ":rtp_transmission_manager", + ":rtp_transport_internal", + ":sctp_data_channel", + ":sctp_transport", + ":sdp_offer_answer", + ":session_description", + ":simulcast_description", + ":stats_collector", + ":stream_collection", + ":transceiver_list", + ":transport_stats", + ":usage_pattern", + ":webrtc_session_description_factory", + "../api:async_dns_resolver", + "../api:libjingle_logging_api", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:packet_socket_factory", + "../api:rtc_error", + "../api:rtc_stats_api", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api:webrtc_key_value_config", + "../api/adaptation:resource_adaptation_api", + "../api/crypto:options", + "../api/rtc_event_log:rtc_event_log", + "../api/transport:bitrate_settings", + "../api/transport:datagram_transport_interface", + "../api/transport:enums", + "../api/transport:webrtc_key_value_config", + "../api/video:video_bitrate_allocator_factory", + "../api/video:video_codec_constants", + "../call:call_interfaces", + "../media:rtc_media_base", + "../media:rtc_media_config", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:network_constants", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:socket_address", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base:weak_ptr", + "../rtc_base/network:sent_packet", + "../rtc_base/task_utils:pending_task_safety_flag", + "../rtc_base/task_utils:to_queued_task", + "../rtc_base/third_party/sigslot:sigslot", + "../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("sdp_serializer") { + visibility = [ ":*" ] + sources = [ + "sdp_serializer.cc", + "sdp_serializer.h", + ] + deps = [ + ":session_description", + ":simulcast_description", + "../api:rtc_error", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../rtc_base:checks", + "../rtc_base:stringutils", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("sdp_utils") { + sources = [ + "sdp_utils.cc", + "sdp_utils.h", + ] + deps = [ + ":session_description", + "../api:libjingle_peerconnection_api", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base/system:rtc_export", + ] +} +rtc_source_set("stats_collector") { + visibility = [ ":*" ] + sources = [ + "stats_collector.cc", + "stats_collector.h", + ] + deps = [ + ":channel", + ":channel_interface", + ":data_channel_utils", + ":peer_connection_internal", + ":rtc_pc_base", + ":rtp_receiver", + ":rtp_receiver_proxy", + ":rtp_sender_proxy", + ":rtp_transceiver", + ":stats_collector_interface", + ":transport_stats", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:rtp_parameters", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api:webrtc_key_value_config", + "../api/audio_codecs:audio_codecs_api", + "../api/video:video_rtp_headers", + "../call:call_interfaces", + "../media:rtc_media_base", + "../modules/audio_processing:audio_processing_statistics", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:network_constants", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:socket_address", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base:timeutils", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("stream_collection") { + visibility = [ ":*" ] + sources = [ "stream_collection.h" ] + deps = [ "../api:libjingle_peerconnection_api" ] +} +rtc_source_set("track_media_info_map") { + visibility = [ ":*" ] + sources = [ + "track_media_info_map.cc", + "track_media_info_map.h", + ] + deps = [ + ":rtp_receiver", + ":rtp_sender", + "../api:media_stream_interface", + "../api:rtp_parameters", + "../api:scoped_refptr", + "../media:rtc_media_base", + "../rtc_base:checks", + "../rtc_base:refcount", + "../rtc_base:threading", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} +rtc_source_set("webrtc_sdp") { + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Used by Chrome and more + + sources = [ + "jsep_ice_candidate.cc", + "jsep_session_description.cc", + "webrtc_sdp.cc", + "webrtc_sdp.h", + ] + deps = [ + ":media_protocol_names", + ":rtc_pc_base", + ":sdp_serializer", + ":session_description", + ":simulcast_description", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../media:rtc_data_sctp_transport_internal", + "../media:rtc_media_base", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:network_constants", + "../rtc_base:rtc_base", + "../rtc_base:socket_address", + "../rtc_base:stringutils", + "../rtc_base/system:rtc_export", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} +rtc_source_set("webrtc_session_description_factory") { + visibility = [ ":*" ] + sources = [ + "webrtc_session_description_factory.cc", + "webrtc_session_description_factory.h", + ] + deps = [ + ":connection_context", + ":rtc_pc_base", + ":sdp_state_provider", + ":session_description", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:scoped_refptr", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base/third_party/sigslot:sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("ice_server_parsing") { + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Known to be used externally + + sources = [ + "ice_server_parsing.cc", + "ice_server_parsing.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:socket_address", + "../rtc_base:stringutils", + "../rtc_base/system:rtc_export", + ] +} + +rtc_library("media_stream_observer") { + sources = [ + "media_stream_observer.cc", + "media_stream_observer.h", + ] + deps = [ + "../api:media_stream_interface", + "../api:scoped_refptr", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ] +} +rtc_source_set("peer_connection_factory") { + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Known to be used externally + sources = [ + "peer_connection_factory.cc", + "peer_connection_factory.h", + ] + deps = [ + ":local_audio_source", + ":media_stream_proxy", + ":media_stream_track_proxy", + ":peer_connection", + ":peer_connection_factory_proxy", + ":peer_connection_proxy", + "../api:audio_options_api", + "../api:callfactory_api", + "../api:fec_controller_api", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:network_state_predictor_api", + "../api:packet_socket_factory", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/metronome", + "../api/neteq:neteq_api", + "../api/rtc_event_log:rtc_event_log", + "../api/task_queue:task_queue", + "../api/transport:bitrate_settings", + "../api/transport:network_control", + "../api/transport:sctp_transport_factory_interface", + "../api/transport:webrtc_key_value_config", + "../api/units:data_rate", + "../call:call_interfaces", + "../call:rtp_interfaces", + "../call:rtp_sender", + "../media:rtc_media_base", + "../p2p:rtc_p2p", + "../pc:audio_track", + "../pc:connection_context", + "../pc:media_stream", + "../pc:rtc_pc_base", + "../pc:rtp_parameters_conversion", + "../pc:session_description", + "../pc:video_track", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + "../rtc_base:safe_conversions", + "../rtc_base:threading", + "../rtc_base/experiments:field_trial_parser", + "../rtc_base/system:file_wrapper", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] +} + rtc_library("peer_connection_message_handler") { + visibility = [ ":*" ] sources = [ "peer_connection_message_handler.cc", "peer_connection_message_handler.h", @@ -431,6 +1495,7 @@ rtc_library("peer_connection_message_handler") { } rtc_library("usage_pattern") { + visibility = [ ":*" ] sources = [ "usage_pattern.cc", "usage_pattern.h", @@ -443,16 +1508,22 @@ rtc_library("usage_pattern") { } rtc_library("rtp_transceiver") { + visibility = [ ":*" ] sources = [ "rtp_transceiver.cc", "rtp_transceiver.h", ] deps = [ + ":channel_interface", ":proxy", ":rtc_pc_base", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", + ":rtp_transport_internal", ":session_description", "../api:array_view", "../api:libjingle_peerconnection_api", @@ -467,6 +1538,7 @@ rtc_library("rtp_transceiver") { "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:refcount", + "../rtc_base:rtc_base_approved", "../rtc_base:threading", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", @@ -480,15 +1552,19 @@ rtc_library("rtp_transceiver") { } rtc_library("rtp_transmission_manager") { + visibility = [ ":*" ] sources = [ "rtp_transmission_manager.cc", "rtp_transmission_manager.h", ] deps = [ ":audio_rtp_receiver", + ":channel", ":rtc_pc_base", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", ":stats_collector_interface", ":transceiver_list", @@ -516,6 +1592,7 @@ rtc_library("rtp_transmission_manager") { } rtc_library("transceiver_list") { + visibility = [ ":*" ] sources = [ "transceiver_list.cc", "transceiver_list.h", @@ -535,12 +1612,14 @@ rtc_library("transceiver_list") { } rtc_library("rtp_receiver") { + visibility = [ ":*" ] sources = [ "rtp_receiver.cc", "rtp_receiver.h", ] deps = [ ":media_stream", + ":media_stream_proxy", ":rtc_pc_base", ":video_track_source", "../api:libjingle_peerconnection_api", @@ -564,6 +1643,7 @@ rtc_library("rtp_receiver") { } rtc_library("audio_rtp_receiver") { + visibility = [ ":*" ] sources = [ "audio_rtp_receiver.cc", "audio_rtp_receiver.h", @@ -572,6 +1652,7 @@ rtc_library("audio_rtp_receiver") { ":audio_track", ":jitter_buffer_delay", ":media_stream", + ":media_stream_track_proxy", ":remote_audio_source", ":rtc_pc_base", ":rtp_receiver", @@ -600,6 +1681,7 @@ rtc_library("audio_rtp_receiver") { } rtc_library("video_rtp_receiver") { + visibility = [ ":*" ] sources = [ "video_rtp_receiver.cc", "video_rtp_receiver.h", @@ -607,6 +1689,7 @@ rtc_library("video_rtp_receiver") { deps = [ ":jitter_buffer_delay", ":media_stream", + ":media_stream_track_proxy", ":rtc_pc_base", ":rtp_receiver", ":video_rtp_track_source", @@ -636,6 +1719,7 @@ rtc_library("video_rtp_receiver") { } rtc_library("video_rtp_track_source") { + visibility = [ ":*" ] sources = [ "video_rtp_track_source.cc", "video_rtp_track_source.h", @@ -654,6 +1738,7 @@ rtc_library("video_rtp_track_source") { } rtc_library("audio_track") { + visibility = [ ":*" ] sources = [ "audio_track.cc", "audio_track.h", @@ -664,29 +1749,36 @@ rtc_library("audio_track") { "../api:sequence_checker", "../rtc_base:checks", "../rtc_base:refcount", + "../rtc_base/system:no_unique_address", ] } rtc_library("video_track") { + visibility = [ ":*" ] sources = [ "video_track.cc", "video_track.h", ] deps = [ + ":rtc_pc_base", "../api:media_stream_interface", "../api:scoped_refptr", "../api:sequence_checker", "../api/video:video_frame", "../media:rtc_media_base", + "../pc:rtc_pc_base", "../rtc_base", "../rtc_base:checks", "../rtc_base:refcount", "../rtc_base:rtc_base_approved", "../rtc_base:threading", + "../rtc_base/system:no_unique_address", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_source_set("sdp_state_provider") { + visibility = [ ":*" ] sources = [ "sdp_state_provider.h" ] deps = [ ":rtc_pc_base", @@ -695,6 +1787,7 @@ rtc_source_set("sdp_state_provider") { } rtc_library("jitter_buffer_delay") { + visibility = [ ":*" ] sources = [ "jitter_buffer_delay.cc", "jitter_buffer_delay.h", @@ -702,6 +1795,7 @@ rtc_library("jitter_buffer_delay") { deps = [ "../api:sequence_checker", "../rtc_base:checks", + "../rtc_base:macromagic", "../rtc_base:safe_conversions", "../rtc_base:safe_minmax", "../rtc_base/system:no_unique_address", @@ -710,11 +1804,13 @@ rtc_library("jitter_buffer_delay") { } rtc_library("remote_audio_source") { + visibility = [ ":*" ] sources = [ "remote_audio_source.cc", "remote_audio_source.h", ] deps = [ + ":channel", ":rtc_pc_base", "../api:call_api", "../api:media_stream_interface", @@ -738,6 +1834,7 @@ rtc_library("remote_audio_source") { } rtc_library("rtp_sender") { + visibility = [ ":*" ] sources = [ "rtp_sender.cc", "rtp_sender.h", @@ -769,6 +1866,7 @@ rtc_library("rtp_sender") { } rtc_library("rtp_parameters_conversion") { + visibility = [ ":*" ] sources = [ "rtp_parameters_conversion.cc", "rtp_parameters_conversion.h", @@ -792,6 +1890,7 @@ rtc_library("rtp_parameters_conversion") { } rtc_library("dtmf_sender") { + visibility = [ ":*" ] sources = [ "dtmf_sender.cc", "dtmf_sender.h", @@ -815,6 +1914,7 @@ rtc_library("dtmf_sender") { } rtc_library("media_stream") { + visibility = [ ":*" ] sources = [ "media_stream.cc", "media_stream.h", @@ -847,12 +1947,14 @@ rtc_library("video_track_source") { "../media:rtc_media_base", "../rtc_base:checks", "../rtc_base:rtc_base_approved", + "../rtc_base/system:no_unique_address", "../rtc_base/system:rtc_export", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_source_set("stats_collector_interface") { + visibility = [ ":*" ] sources = [ "stats_collector_interface.h" ] deps = [ "../api:libjingle_peerconnection_api", @@ -861,7 +1963,9 @@ rtc_source_set("stats_collector_interface") { } rtc_source_set("libjingle_peerconnection") { - visibility = [ "*" ] + # TODO(bugs.webrtc.org/13661): Reduce visibility if possible + visibility = [ "*" ] # Used by Chrome and others + deps = [ ":peerconnection", "../api:libjingle_peerconnection_api", @@ -873,6 +1977,7 @@ if (rtc_include_tests && !build_with_chromium) { testonly = true sources = [ + "audio_rtp_receiver_unittest.cc", "channel_manager_unittest.cc", "channel_unittest.cc", "dtls_srtp_transport_unittest.cc", @@ -901,12 +2006,28 @@ if (rtc_include_tests && !build_with_chromium) { } deps = [ + ":audio_rtp_receiver", + ":channel", + ":dtls_srtp_transport", + ":dtls_transport", + ":ice_transport", + ":jsep_transport", ":libjingle_peerconnection", + ":media_protocol_names", ":pc_test_utils", ":peerconnection", ":rtc_pc", ":rtc_pc_base", + ":rtcp_mux_filter", + ":rtp_media_utils", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_transport", ":session_description", + ":srtp_filter", + ":srtp_session", + ":srtp_transport", + ":used_ids", ":video_rtp_receiver", "../api:array_view", "../api:audio_options_api", @@ -915,7 +2036,12 @@ if (rtc_include_tests && !build_with_chromium) { "../api:rtc_error", "../api:rtp_headers", "../api:rtp_parameters", + "../api:scoped_refptr", + "../api/task_queue:task_queue", + "../api/transport:datagram_transport_interface", + "../api/transport:enums", "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:recordable_encoded_frame", "../api/video/test:mock_recordable_encoded_frame", "../call:rtp_interfaces", "../call:rtp_receiver", @@ -932,17 +2058,22 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:gunit_helpers", "../rtc_base:rtc_base_approved", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:socket_address", "../rtc_base:threading", + "../rtc_base/containers:flat_set", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", - "../test:field_trial", + "../test:explicit_key_value_config", + "../test:scoped_key_value_config", + "../test:test_common", "../test:test_main", "../test:test_support", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", ] if (rtc_build_libsrtp) { @@ -959,11 +2090,14 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "peer_connection_rampup_tests.cc" ] deps = [ ":pc_test_utils", + ":peer_connection", + ":peerconnection", ":peerconnection_wrapper", "../api:audio_options_api", "../api:create_peerconnection_factory", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtc_stats_api", "../api:scoped_refptr", "../api/audio:audio_mixer_api", @@ -978,12 +2112,12 @@ if (rtc_include_tests && !build_with_chromium) { "../modules/audio_processing:api", "../p2p:p2p_test_utils", "../p2p:rtc_p2p", - "../pc:peerconnection", "../rtc_base", "../rtc_base:checks", "../rtc_base:gunit_helpers", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket_address", + "../rtc_base:socket_factory", "../rtc_base:threading", "../system_wrappers", "../test:perf_test", @@ -1000,6 +2134,8 @@ if (rtc_include_tests && !build_with_chromium) { ] deps = [ ":pc_test_utils", + ":peerconnection", + ":sdp_utils", "../api:function_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", @@ -1007,7 +2143,6 @@ if (rtc_include_tests && !build_with_chromium) { "../api:rtc_stats_api", "../api:rtp_parameters", "../api:scoped_refptr", - "../pc:peerconnection", "../rtc_base:checks", "../rtc_base:gunit_helpers", "../rtc_base:rtc_base_approved", @@ -1053,6 +2188,7 @@ if (rtc_include_tests && !build_with_chromium) { "rtp_sender_receiver_unittest.cc", "rtp_transceiver_unittest.cc", "sctp_utils_unittest.cc", + "sdp_offer_answer_unittest.cc", "sdp_serializer_unittest.cc", "stats_collector_unittest.cc", "test/fake_audio_capture_module_unittest.cc", @@ -1066,24 +2202,51 @@ if (rtc_include_tests && !build_with_chromium) { deps = [ ":audio_rtp_receiver", ":audio_track", + ":channel", + ":channel_interface", + ":dtls_srtp_transport", + ":dtls_transport", ":dtmf_sender", + ":ice_server_parsing", ":integration_test_helpers", ":jitter_buffer_delay", + ":local_audio_source", + ":media_protocol_names", ":media_stream", + ":peer_connection", + ":peer_connection_factory", + ":peer_connection_factory", + ":peer_connection_proxy", ":peerconnection", ":proxy", ":remote_audio_source", ":rtc_pc_base", + ":rtc_stats_collector", + ":rtc_stats_traversal", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", + ":rtp_transport_internal", + ":sctp_data_channel", + ":sctp_transport", + ":sctp_utils", + ":sdp_serializer", + ":sdp_utils", ":session_description", + ":simulcast_description", + ":stats_collector", + ":stream_collection", + ":track_media_info_map", + ":transport_stats", ":usage_pattern", ":video_rtp_receiver", ":video_rtp_track_source", ":video_track", ":video_track_source", + ":webrtc_sdp", "../api:array_view", "../api:audio_options_api", "../api:create_peerconnection_factory", @@ -1096,9 +2259,12 @@ if (rtc_include_tests && !build_with_chromium) { "../api:mock_rtp", "../api:mock_video_track", "../api:packet_socket_factory", + "../api:priority", "../api:rtc_error", "../api:rtp_transceiver_direction", "../api:scoped_refptr", + "../api:webrtc_key_value_config", + "../api/adaptation:resource_adaptation_api", "../api/audio:audio_mixer_api", "../api/crypto:frame_decryptor_interface", "../api/crypto:frame_encryptor_interface", @@ -1107,13 +2273,22 @@ if (rtc_include_tests && !build_with_chromium) { "../api/rtc_event_log:rtc_event_log_factory", "../api/task_queue", "../api/task_queue:default_task_queue_factory", + "../api/transport:datagram_transport_interface", "../api/transport:field_trial_based_config", + "../api/transport:sctp_transport_factory_interface", "../api/transport:webrtc_key_value_config", "../api/transport/rtp:rtp_source", "../api/units:time_delta", + "../api/units:timestamp", "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:encoded_image", + "../api/video:recordable_encoded_frame", + "../api/video:video_bitrate_allocator_factory", + "../api/video:video_codec_constants", + "../api/video:video_frame", "../api/video:video_rtp_headers", "../call/adaptation:resource_adaptation_test_utilities", + "../common_video", "../logging:fake_rtc_event_log", "../media:rtc_data_sctp_transport_internal", "../media:rtc_media_config", @@ -1128,18 +2303,21 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:checks", "../rtc_base:gunit_helpers", "../rtc_base:ip_address", + "../rtc_base:network_constants", "../rtc_base:rtc_base_tests_utils", "../rtc_base:rtc_json", "../rtc_base:socket_address", + "../rtc_base:socket_factory", "../rtc_base:threading", "../rtc_base/synchronization:mutex", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", "../system_wrappers:field_trial", "../system_wrappers:metrics", - "../test:field_trial", + "../test:explicit_key_value_config", "../test:fileutils", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_common", "../test/pc/sctp:fake_sctp_transport", "./scenario_tests:pc_scenario_tests", @@ -1155,6 +2333,7 @@ if (rtc_include_tests && !build_with_chromium) { deps += [ ":libjingle_peerconnection", ":pc_test_utils", + ":rtc_pc", "../api:callfactory_api", "../api:rtc_event_log_output_file", "../api:rtc_stats_api", @@ -1178,7 +2357,6 @@ if (rtc_include_tests && !build_with_chromium) { "../modules/utility", "../p2p:p2p_test_utils", "../p2p:rtc_p2p", - "../pc:rtc_pc", "../rtc_base", "../rtc_base:rtc_base_approved", "../rtc_base:rtc_task_queue", @@ -1230,11 +2408,16 @@ if (rtc_include_tests && !build_with_chromium) { ":audio_track", ":dtmf_sender", ":jitter_buffer_delay", + ":local_audio_source", ":media_stream", ":pc_test_utils", + ":peer_connection", + ":peer_connection_factory", + ":peer_connection_proxy", ":peerconnection", ":remote_audio_source", ":rtc_pc_base", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", ":rtp_sender", @@ -1306,13 +2489,15 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:pending_task_safety_flag", + "../rtc_base/task_utils:repeating_task", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", - "../test:field_trial", + "../test:explicit_key_value_config", "../test:fileutils", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_support", "../test/pc/sctp:fake_sctp_transport", ] @@ -1343,6 +2528,7 @@ if (rtc_include_tests && !build_with_chromium) { "test/mock_peer_connection_observers.h", "test/mock_rtp_receiver_internal.h", "test/mock_rtp_sender_internal.h", + "test/mock_voice_media_channel.h", "test/peer_connection_test_wrapper.cc", "test/peer_connection_test_wrapper.h", "test/rtc_stats_obtainer.h", @@ -1350,12 +2536,16 @@ if (rtc_include_tests && !build_with_chromium) { ] deps = [ + ":channel_interface", ":jitter_buffer_delay", ":libjingle_peerconnection", + ":peer_connection_internal", ":peerconnection", ":rtc_pc_base", ":rtp_receiver", ":rtp_sender", + ":sctp_data_channel", + ":stream_collection", ":video_track_source", "../api:audio_options_api", "../api:create_frame_generator", @@ -1366,10 +2556,12 @@ if (rtc_include_tests && !build_with_chromium) { "../api:rtc_stats_api", "../api:scoped_refptr", "../api:sequence_checker", + "../api:webrtc_key_value_config", "../api/audio:audio_mixer_api", "../api/audio_codecs:audio_codecs_api", "../api/task_queue", "../api/task_queue:default_task_queue_factory", + "../api/transport:webrtc_key_value_config", "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:video_frame", "../api/video:video_rtp_headers", @@ -1396,6 +2588,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:repeating_task", "../rtc_base/third_party/sigslot", + "../test:scoped_key_value_config", "../test:test_support", "../test:video_test_common", ] diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc index 4efab24d15..3a306720c7 100644 --- a/pc/audio_rtp_receiver.cc +++ b/pc/audio_rtp_receiver.cc @@ -12,6 +12,7 @@ #include +#include #include #include @@ -20,25 +21,29 @@ #include "pc/media_stream_track_proxy.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" -#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/task_utils/to_queued_task.h" namespace webrtc { -AudioRtpReceiver::AudioRtpReceiver(rtc::Thread* worker_thread, - std::string receiver_id, - std::vector stream_ids, - bool is_unified_plan) +AudioRtpReceiver::AudioRtpReceiver( + rtc::Thread* worker_thread, + std::string receiver_id, + std::vector stream_ids, + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel /*= nullptr*/) : AudioRtpReceiver(worker_thread, receiver_id, CreateStreamsFromIds(std::move(stream_ids)), - is_unified_plan) {} + is_unified_plan, + voice_channel) {} AudioRtpReceiver::AudioRtpReceiver( rtc::Thread* worker_thread, const std::string& receiver_id, const std::vector>& streams, - bool is_unified_plan) + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel /*= nullptr*/) : worker_thread_(worker_thread), id_(receiver_id), source_(rtc::make_ref_counted( @@ -49,7 +54,8 @@ AudioRtpReceiver::AudioRtpReceiver( track_(AudioTrackProxyWithInternal::Create( rtc::Thread::Current(), AudioTrack::Create(receiver_id, source_))), - cached_track_enabled_(track_->enabled()), + media_channel_(voice_channel), + cached_track_enabled_(track_->internal()->enabled()), attachment_id_(GenerateUniqueId()), worker_thread_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()) { RTC_DCHECK(worker_thread_); @@ -61,7 +67,6 @@ AudioRtpReceiver::AudioRtpReceiver( AudioRtpReceiver::~AudioRtpReceiver() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RTC_DCHECK(stopped_); RTC_DCHECK(!media_channel_); track_->GetSource()->UnregisterAudioObserver(this); @@ -70,21 +75,25 @@ AudioRtpReceiver::~AudioRtpReceiver() { void AudioRtpReceiver::OnChanged() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - if (cached_track_enabled_ != track_->enabled()) { - cached_track_enabled_ = track_->enabled(); - worker_thread_->PostTask(ToQueuedTask( - worker_thread_safety_, - [this, enabled = cached_track_enabled_, volume = cached_volume_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - Reconfigure(enabled, volume); - })); - } + const bool enabled = track_->internal()->enabled(); + if (cached_track_enabled_ == enabled) + return; + cached_track_enabled_ = enabled; + worker_thread_->PostTask( + ToQueuedTask(worker_thread_safety_, [this, enabled]() { + RTC_DCHECK_RUN_ON(worker_thread_); + Reconfigure(enabled); + })); } // RTC_RUN_ON(worker_thread_) void AudioRtpReceiver::SetOutputVolume_w(double volume) { RTC_DCHECK_GE(volume, 0.0); RTC_DCHECK_LE(volume, 10.0); + + if (!media_channel_) + return; + ssrc_ ? media_channel_->SetOutputVolume(*ssrc_, volume) : media_channel_->SetDefaultOutputVolume(volume); } @@ -93,21 +102,19 @@ void AudioRtpReceiver::OnSetVolume(double volume) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); RTC_DCHECK_GE(volume, 0); RTC_DCHECK_LE(volume, 10); - if (stopped_) - return; - cached_volume_ = volume; - - // When the track is disabled, the volume of the source, which is the - // corresponding WebRtc Voice Engine channel will be 0. So we do not allow - // setting the volume to the source when the track is disabled. - if (track_->enabled()) { - worker_thread_->PostTask( - ToQueuedTask(worker_thread_safety_, [this, volume = cached_volume_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - SetOutputVolume_w(volume); - })); - } + bool track_enabled = track_->internal()->enabled(); + worker_thread_->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(worker_thread_); + // Update the cached_volume_ even when stopped, to allow clients to set + // the volume before starting/restarting, eg see crbug.com/1272566. + cached_volume_ = volume; + // When the track is disabled, the volume of the source, which is the + // corresponding WebRtc Voice Engine channel will be 0. So we do not + // allow setting the volume to the source when the track is disabled. + if (track_enabled) + SetOutputVolume_w(volume); + }); } rtc::scoped_refptr AudioRtpReceiver::dtls_transport() @@ -156,61 +163,47 @@ AudioRtpReceiver::GetFrameDecryptor() const { void AudioRtpReceiver::Stop() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - // TODO(deadbeef): Need to do more here to fully stop receiving packets. - if (!stopped_) { - source_->SetState(MediaSourceInterface::kEnded); - stopped_ = true; - } - - worker_thread_->Invoke(RTC_FROM_HERE, [&]() { - RTC_DCHECK_RUN_ON(worker_thread_); - if (media_channel_) - SetOutputVolume_w(0.0); - SetMediaChannel_w(nullptr); - }); -} - -void AudioRtpReceiver::StopAndEndTrack() { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - Stop(); + source_->SetState(MediaSourceInterface::kEnded); track_->internal()->set_ended(); } -void AudioRtpReceiver::RestartMediaChannel(absl::optional ssrc) { +void AudioRtpReceiver::SetSourceEnded() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - bool ok = worker_thread_->Invoke( - RTC_FROM_HERE, [&, enabled = cached_track_enabled_, - volume = cached_volume_, was_stopped = stopped_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - RTC_DCHECK(was_stopped); - return false; // Can't restart. - } + source_->SetState(MediaSourceInterface::kEnded); +} - if (!was_stopped && ssrc_ == ssrc) { - // Already running with that ssrc. - RTC_DCHECK(worker_thread_safety_->alive()); - return true; - } +// RTC_RUN_ON(&signaling_thread_checker_) +void AudioRtpReceiver::RestartMediaChannel(absl::optional ssrc) { + bool enabled = track_->internal()->enabled(); + MediaSourceInterface::SourceState state = source_->state(); + worker_thread_->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(worker_thread_); + RestartMediaChannel_w(std::move(ssrc), enabled, state); + }); + source_->SetState(MediaSourceInterface::kLive); +} - if (!was_stopped) { - source_->Stop(media_channel_, ssrc_); - } +// RTC_RUN_ON(worker_thread_) +void AudioRtpReceiver::RestartMediaChannel_w( + absl::optional ssrc, + bool track_enabled, + MediaSourceInterface::SourceState state) { + if (!media_channel_) + return; // Can't restart. - ssrc_ = std::move(ssrc); - source_->Start(media_channel_, ssrc_); - if (ssrc_) { - media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); - } + if (state != MediaSourceInterface::kInitializing) { + if (ssrc_ == ssrc) + return; + source_->Stop(media_channel_, ssrc_); + } - Reconfigure(enabled, volume); - return true; - }); + ssrc_ = std::move(ssrc); + source_->Start(media_channel_, ssrc_); + if (ssrc_) { + media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); + } - if (!ok) - return; - - stopped_ = false; + Reconfigure(track_enabled); } void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) { @@ -292,10 +285,10 @@ void AudioRtpReceiver::SetDepacketizerToDecoderFrameTransformer( } // RTC_RUN_ON(worker_thread_) -void AudioRtpReceiver::Reconfigure(bool track_enabled, double volume) { +void AudioRtpReceiver::Reconfigure(bool track_enabled) { RTC_DCHECK(media_channel_); - SetOutputVolume_w(track_enabled ? volume : 0); + SetOutputVolume_w(track_enabled ? cached_volume_ : 0); if (ssrc_ && frame_decryptor_) { // Reattach the frame decryptor if we were reconfigured. @@ -326,21 +319,12 @@ void AudioRtpReceiver::SetJitterBufferMinimumDelay( } void AudioRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK_RUN_ON(worker_thread_); RTC_DCHECK(media_channel == nullptr || media_channel->media_type() == media_type()); + if (!media_channel && media_channel_) + SetOutputVolume_w(0.0); - if (stopped_ && !media_channel) - return; - - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - SetMediaChannel_w(media_channel); - }); -} - -// RTC_RUN_ON(worker_thread_) -void AudioRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { media_channel ? worker_thread_safety_->SetAlive() : worker_thread_safety_->SetNotAlive(); media_channel_ = static_cast(media_channel); diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h index aef497db76..6f70243c0f 100644 --- a/pc/audio_rtp_receiver.h +++ b/pc/audio_rtp_receiver.h @@ -45,16 +45,24 @@ class AudioRtpReceiver : public ObserverInterface, public AudioSourceInterface::AudioObserver, public RtpReceiverInternal { public: + // The constructor supports optionally passing the voice channel to the + // instance at construction time without having to call `SetMediaChannel()` + // on the worker thread straight after construction. + // However, when using that, the assumption is that right after construction, + // a call to either `SetupUnsignaledMediaChannel` or `SetupMediaChannel` + // will be made, which will internally start the source on the worker thread. AudioRtpReceiver(rtc::Thread* worker_thread, std::string receiver_id, std::vector stream_ids, - bool is_unified_plan); + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel = nullptr); // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed. AudioRtpReceiver( rtc::Thread* worker_thread, const std::string& receiver_id, const std::vector>& streams, - bool is_unified_plan); + bool is_unified_plan, + cricket::VoiceMediaChannel* media_channel = nullptr); virtual ~AudioRtpReceiver(); // ObserverInterface implementation @@ -90,7 +98,7 @@ class AudioRtpReceiver : public ObserverInterface, // RtpReceiverInternal implementation. void Stop() override; - void StopAndEndTrack() override; + void SetSourceEnded() override; void SetupMediaChannel(uint32_t ssrc) override; void SetupUnsignaledMediaChannel() override; uint32_t ssrc() const override; @@ -114,12 +122,14 @@ class AudioRtpReceiver : public ObserverInterface, override; private: - void RestartMediaChannel(absl::optional ssrc); - void Reconfigure(bool track_enabled, double volume) + void RestartMediaChannel(absl::optional ssrc) + RTC_RUN_ON(&signaling_thread_checker_); + void RestartMediaChannel_w(absl::optional ssrc, + bool track_enabled, + MediaSourceInterface::SourceState state) RTC_RUN_ON(worker_thread_); + void Reconfigure(bool track_enabled) RTC_RUN_ON(worker_thread_); void SetOutputVolume_w(double volume) RTC_RUN_ON(worker_thread_); - void SetMediaChannel_w(cricket::MediaChannel* media_channel) - RTC_RUN_ON(worker_thread_); RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; rtc::Thread* const worker_thread_; @@ -132,8 +142,7 @@ class AudioRtpReceiver : public ObserverInterface, std::vector> streams_ RTC_GUARDED_BY(&signaling_thread_checker_); bool cached_track_enabled_ RTC_GUARDED_BY(&signaling_thread_checker_); - double cached_volume_ RTC_GUARDED_BY(&signaling_thread_checker_) = 1.0; - bool stopped_ RTC_GUARDED_BY(&signaling_thread_checker_) = true; + double cached_volume_ RTC_GUARDED_BY(worker_thread_) = 1.0; RtpReceiverObserverInterface* observer_ RTC_GUARDED_BY(&signaling_thread_checker_) = nullptr; bool received_first_packet_ RTC_GUARDED_BY(&signaling_thread_checker_) = diff --git a/pc/audio_rtp_receiver_unittest.cc b/pc/audio_rtp_receiver_unittest.cc new file mode 100644 index 0000000000..ac843fe9c2 --- /dev/null +++ b/pc/audio_rtp_receiver_unittest.cc @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#include "pc/audio_rtp_receiver.h" + +#include + +#include "pc/test/mock_voice_media_channel.h" +#include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::_; +using ::testing::InvokeWithoutArgs; +using ::testing::Mock; + +static const int kTimeOut = 100; +static const double kDefaultVolume = 1; +static const double kVolume = 3.7; +static const double kVolumeMuted = 0.0; +static const uint32_t kSsrc = 3; + +namespace webrtc { +class AudioRtpReceiverTest : public ::testing::Test { + protected: + AudioRtpReceiverTest() + : worker_(rtc::Thread::Current()), + receiver_( + rtc::make_ref_counted(worker_, + std::string(), + std::vector(), + false)), + media_channel_(rtc::Thread::Current()) { + EXPECT_CALL(media_channel_, SetRawAudioSink(kSsrc, _)); + EXPECT_CALL(media_channel_, SetBaseMinimumPlayoutDelayMs(kSsrc, _)); + } + + ~AudioRtpReceiverTest() { + EXPECT_CALL(media_channel_, SetOutputVolume(kSsrc, kVolumeMuted)); + receiver_->SetMediaChannel(nullptr); + } + + rtc::Thread* worker_; + rtc::scoped_refptr receiver_; + cricket::MockVoiceMediaChannel media_channel_; +}; + +TEST_F(AudioRtpReceiverTest, SetOutputVolumeIsCalled) { + std::atomic_int set_volume_calls(0); + + EXPECT_CALL(media_channel_, SetOutputVolume(kSsrc, kDefaultVolume)) + .WillOnce(InvokeWithoutArgs([&] { + set_volume_calls++; + return true; + })); + + receiver_->track(); + receiver_->track()->set_enabled(true); + receiver_->SetMediaChannel(&media_channel_); + EXPECT_CALL(media_channel_, SetDefaultRawAudioSink(_)).Times(0); + receiver_->SetupMediaChannel(kSsrc); + + EXPECT_CALL(media_channel_, SetOutputVolume(kSsrc, kVolume)) + .WillOnce(InvokeWithoutArgs([&] { + set_volume_calls++; + return true; + })); + + receiver_->OnSetVolume(kVolume); + EXPECT_TRUE_WAIT(set_volume_calls == 2, kTimeOut); +} + +TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) { + // Set the volume before setting the media channel. It should still be used + // as the initial volume. + receiver_->OnSetVolume(kVolume); + + receiver_->track()->set_enabled(true); + receiver_->SetMediaChannel(&media_channel_); + + // The previosly set initial volume should be propagated to the provided + // media_channel_ as soon as SetupMediaChannel is called. + EXPECT_CALL(media_channel_, SetOutputVolume(kSsrc, kVolume)); + + receiver_->SetupMediaChannel(kSsrc); +} +} // namespace webrtc diff --git a/pc/audio_track.cc b/pc/audio_track.cc index be087f693b..ae8914d634 100644 --- a/pc/audio_track.cc +++ b/pc/audio_track.cc @@ -32,7 +32,7 @@ AudioTrack::AudioTrack(const std::string& label, } AudioTrack::~AudioTrack() { - RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); set_state(MediaStreamTrackInterface::kEnded); if (audio_source_) audio_source_->UnregisterObserver(this); @@ -48,19 +48,19 @@ AudioSourceInterface* AudioTrack::GetSource() const { } void AudioTrack::AddSink(AudioTrackSinkInterface* sink) { - RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (audio_source_) audio_source_->AddSink(sink); } void AudioTrack::RemoveSink(AudioTrackSinkInterface* sink) { - RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (audio_source_) audio_source_->RemoveSink(sink); } void AudioTrack::OnChanged() { - RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (audio_source_->state() == MediaSourceInterface::kEnded) { set_state(kEnded); } else { diff --git a/pc/audio_track.h b/pc/audio_track.h index 8a705cf8fb..920bb948ba 100644 --- a/pc/audio_track.h +++ b/pc/audio_track.h @@ -17,9 +17,14 @@ #include "api/media_stream_track.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "rtc_base/system/no_unique_address.h" namespace webrtc { +// TODO(tommi): Instead of inheriting from `MediaStreamTrack<>`, implement the +// properties directly in this class. `MediaStreamTrack` doesn't guard against +// conflicting access, so we'd need to override those methods anyway in this +// class in order to make sure things are correctly checked. class AudioTrack : public MediaStreamTrack, public ObserverInterface { protected: @@ -53,7 +58,7 @@ class AudioTrack : public MediaStreamTrack, private: const rtc::scoped_refptr audio_source_; - SequenceChecker thread_checker_; + RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker signaling_thread_checker_; }; } // namespace webrtc diff --git a/pc/channel.cc b/pc/channel.cc index 03823d0027..c74519d1d6 100644 --- a/pc/channel.cc +++ b/pc/channel.cc @@ -12,26 +12,26 @@ #include #include -#include -#include +#include +#include #include -#include "absl/algorithm/container.h" #include "absl/strings/string_view.h" #include "api/rtp_parameters.h" #include "api/sequence_checker.h" -#include "api/task_queue/queued_task.h" +#include "api/units/timestamp.h" #include "media/base/codec.h" #include "media/base/rid_description.h" #include "media/base/rtp_utils.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "p2p/base/dtls_transport_internal.h" #include "pc/rtp_media_utils.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/network_route.h" -#include "rtc_base/strings/string_builder.h" -#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/strings/string_format.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/trace_event.h" @@ -39,6 +39,7 @@ namespace cricket { namespace { +using ::rtc::StringFormat; using ::rtc::UniqueRandomIdGenerator; using ::webrtc::PendingTaskSafetyFlag; using ::webrtc::SdpType; @@ -79,12 +80,6 @@ struct StreamFinder { } // namespace -static void SafeSetError(const std::string& message, std::string* error_desc) { - if (error_desc) { - *error_desc = message; - } -} - template void RtpParametersFromMediaDescription( const MediaContentDescriptionImpl* desc, @@ -105,9 +100,13 @@ void RtpParametersFromMediaDescription( template void RtpSendParametersFromMediaDescription( const MediaContentDescriptionImpl* desc, - const RtpHeaderExtensions& extensions, - bool is_stream_active, + webrtc::RtpExtension::Filter extensions_filter, RtpSendParameters* send_params) { + RtpHeaderExtensions extensions = + webrtc::RtpExtension::DeduplicateHeaderExtensions( + desc->rtp_header_extensions(), extensions_filter); + const bool is_stream_active = + webrtc::RtpTransceiverDirectionHasRecv(desc->direction()); RtpParametersFromMediaDescription(desc, extensions, is_stream_active, send_params); send_params->max_bandwidth_bps = desc->bandwidth(); @@ -118,7 +117,7 @@ BaseChannel::BaseChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -126,15 +125,18 @@ BaseChannel::BaseChannel(rtc::Thread* worker_thread, network_thread_(network_thread), signaling_thread_(signaling_thread), alive_(PendingTaskSafetyFlag::Create()), - content_name_(content_name), srtp_required_(srtp_required), - crypto_options_(crypto_options), + extensions_filter_( + crypto_options.srtp.enable_encrypted_rtp_header_extensions + ? webrtc::RtpExtension::kPreferEncryptedExtension + : webrtc::RtpExtension::kDiscardEncryptedExtension), media_channel_(std::move(media_channel)), + demuxer_criteria_(mid), ssrc_generator_(ssrc_generator) { RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK(media_channel_); RTC_DCHECK(ssrc_generator_); - demuxer_criteria_.mid = content_name; - RTC_LOG(LS_INFO) << "Created channel: " << ToString(); + RTC_DLOG(LS_INFO) << "Created channel: " << ToString(); } BaseChannel::~BaseChannel() { @@ -149,27 +151,17 @@ BaseChannel::~BaseChannel() { } std::string BaseChannel::ToString() const { - rtc::StringBuilder sb; - sb << "{mid: " << content_name_; - if (media_channel_) { - sb << ", media_type: " << MediaTypeToString(media_channel_->media_type()); - } - sb << "}"; - return sb.Release(); + return StringFormat("{mid: %s, media_type: %s}", mid().c_str(), + MediaTypeToString(media_channel_->media_type()).c_str()); } -bool BaseChannel::ConnectToRtpTransport() { +bool BaseChannel::ConnectToRtpTransport_n() { RTC_DCHECK(rtp_transport_); RTC_DCHECK(media_channel()); // We don't need to call OnDemuxerCriteriaUpdatePending/Complete because // there's no previous criteria to worry about. - bool result = rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria_, this); - if (result) { - previous_demuxer_criteria_ = demuxer_criteria_; - } else { - previous_demuxer_criteria_ = {}; - RTC_LOG(LS_ERROR) << "Failed to set up demuxing for " << ToString(); + if (!rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria_, this)) { return false; } rtp_transport_->SignalReadyToSend.connect( @@ -183,7 +175,7 @@ bool BaseChannel::ConnectToRtpTransport() { return true; } -void BaseChannel::DisconnectFromRtpTransport() { +void BaseChannel::DisconnectFromRtpTransport_n() { RTC_DCHECK(rtp_transport_); RTC_DCHECK(media_channel()); rtp_transport_->UnregisterRtpDemuxerSink(this); @@ -191,32 +183,8 @@ void BaseChannel::DisconnectFromRtpTransport() { rtp_transport_->SignalNetworkRouteChanged.disconnect(this); rtp_transport_->SignalWritableState.disconnect(this); rtp_transport_->SignalSentPacket.disconnect(this); -} - -void BaseChannel::Init_w(webrtc::RtpTransportInternal* rtp_transport) { - RTC_DCHECK_RUN_ON(worker_thread()); - - network_thread_->Invoke(RTC_FROM_HERE, [this, rtp_transport] { - SetRtpTransport(rtp_transport); - // Both RTP and RTCP channels should be set, we can call SetInterface on - // the media channel and it can set network options. - media_channel_->SetInterface(this); - }); -} - -void BaseChannel::Deinit() { - RTC_DCHECK_RUN_ON(worker_thread()); - // Packets arrive on the network thread, processing packets calls virtual - // functions, so need to stop this process in Deinit that is called in - // derived classes destructor. - network_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(network_thread()); - media_channel_->SetInterface(/*iface=*/nullptr); - - if (rtp_transport_) { - DisconnectFromRtpTransport(); - } - }); + rtp_transport_ = nullptr; + media_channel_->SetInterface(nullptr); } bool BaseChannel::SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) { @@ -227,19 +195,24 @@ bool BaseChannel::SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) { } if (rtp_transport_) { - DisconnectFromRtpTransport(); + DisconnectFromRtpTransport_n(); + // Clear the cached header extensions on the worker. + worker_thread_->PostTask(ToQueuedTask(alive_, [this] { + RTC_DCHECK_RUN_ON(worker_thread()); + rtp_header_extensions_.clear(); + })); } rtp_transport_ = rtp_transport; if (rtp_transport_) { - transport_name_ = rtp_transport_->transport_name(); - - if (!ConnectToRtpTransport()) { - RTC_LOG(LS_ERROR) << "Failed to connect to the new RtpTransport for " - << ToString() << "."; + if (!ConnectToRtpTransport_n()) { return false; } - OnTransportReadyToSend(rtp_transport_->IsReadyToSend()); + + RTC_DCHECK(!media_channel_->HasNetworkInterface()); + media_channel_->SetInterface(this); + + media_channel_->OnReadyToSend(rtp_transport_->IsReadyToSend()); UpdateWritableState_n(); // Set the cached socket options. @@ -252,6 +225,7 @@ bool BaseChannel::SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) { } } } + return true; } @@ -278,7 +252,7 @@ void BaseChannel::Enable(bool enable) { bool BaseChannel::SetLocalContent(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent"); return SetLocalContent_w(content, type, error_desc); @@ -286,31 +260,31 @@ bool BaseChannel::SetLocalContent(const MediaContentDescription* content, bool BaseChannel::SetRemoteContent(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent"); return SetRemoteContent_w(content, type, error_desc); } bool BaseChannel::SetPayloadTypeDemuxingEnabled(bool enabled) { + // TODO(bugs.webrtc.org/11993): The demuxer state needs to be managed on the + // network thread. At the moment there's a workaround for inconsistent state + // between the worker and network thread because of this (see + // OnDemuxerCriteriaUpdatePending elsewhere in this file) and + // SetPayloadTypeDemuxingEnabled_w has an Invoke over to the network thread + // to apply state updates. RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetPayloadTypeDemuxingEnabled"); return SetPayloadTypeDemuxingEnabled_w(enabled); } -bool BaseChannel::IsReadyToReceiveMedia_w() const { - // Receive data if we are enabled and have local content, - return enabled_ && - webrtc::RtpTransceiverDirectionHasRecv(local_content_direction_); -} - bool BaseChannel::IsReadyToSendMedia_w() const { // Send outgoing data if we are enabled, have local and remote content, // and we have had some form of connectivity. return enabled_ && webrtc::RtpTransceiverDirectionHasRecv(remote_content_direction_) && webrtc::RtpTransceiverDirectionHasSend(local_content_direction_) && - was_ever_writable(); + was_ever_writable_; } bool BaseChannel::SendPacket(rtc::CopyOnWriteBuffer* packet, @@ -327,6 +301,7 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt, int value) { RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); RTC_DCHECK(rtp_transport_); switch (type) { case ST_RTP: @@ -343,6 +318,7 @@ int BaseChannel::SetOption(SocketType type, void BaseChannel::OnWritableState(bool writable) { RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); if (writable) { ChannelWritable_n(); } else { @@ -352,9 +328,11 @@ void BaseChannel::OnWritableState(bool writable) { void BaseChannel::OnNetworkRouteChanged( absl::optional network_route) { + RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); + RTC_LOG(LS_INFO) << "Network route changed for " << ToString(); - RTC_DCHECK_RUN_ON(network_thread()); rtc::NetworkRoute new_route; if (network_route) { new_route = *(network_route); @@ -363,18 +341,26 @@ void BaseChannel::OnNetworkRouteChanged( // use the same transport name and MediaChannel::OnNetworkRouteChanged cannot // work correctly. Intentionally leave it broken to simplify the code and // encourage the users to stop using non-muxing RTCP. - media_channel_->OnNetworkRouteChanged(transport_name_, new_route); + media_channel_->OnNetworkRouteChanged(transport_name(), new_route); } void BaseChannel::SetFirstPacketReceivedCallback( std::function callback) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(!on_first_packet_received_ || !callback); + + // TODO(bugs.webrtc.org/11992): Rename SetFirstPacketReceivedCallback to + // something that indicates network thread initialization/uninitialization and + // call Init_n() / Deinit_n() respectively. + // if (!callback) + // Deinit_n(); + on_first_packet_received_ = std::move(callback); } void BaseChannel::OnTransportReadyToSend(bool ready) { RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); media_channel_->OnReadyToSend(ready); } @@ -382,22 +368,15 @@ bool BaseChannel::SendPacket(bool rtcp, rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options) { RTC_DCHECK_RUN_ON(network_thread()); - // Until all the code is migrated to use RtpPacketType instead of bool. - RtpPacketType packet_type = rtcp ? RtpPacketType::kRtcp : RtpPacketType::kRtp; - // SendPacket gets called from MediaEngine, on a pacer or an encoder thread. - // If the thread is not our network thread, we will post to our network - // so that the real work happens on our network. This avoids us having to - // synchronize access to all the pieces of the send path, including - // SRTP and the inner workings of the transport channels. - // The only downside is that we can't return a proper failure code if - // needed. Since UDP is unreliable anyway, this should be a non-issue. - + RTC_DCHECK(network_initialized()); TRACE_EVENT0("webrtc", "BaseChannel::SendPacket"); - // Now that we are on the correct thread, ensure we have a place to send this - // packet before doing anything. (We might get RTCP packets that we don't - // intend to send.) If we've negotiated RTCP mux, send RTCP over the RTP - // transport. + // Until all the code is migrated to use RtpPacketType instead of bool. + RtpPacketType packet_type = rtcp ? RtpPacketType::kRtcp : RtpPacketType::kRtp; + + // Ensure we have a place to send this packet before doing anything. We might + // get RTCP packets that we don't intend to send. If we've negotiated RTCP + // mux, send RTCP over the RTP transport. if (!rtp_transport_ || !rtp_transport_->IsWritable(rtcp)) { return false; } @@ -415,30 +394,25 @@ bool BaseChannel::SendPacket(bool rtcp, // The audio/video engines may attempt to send RTCP packets as soon as the // streams are created, so don't treat this as an error for RTCP. // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=6809 - if (rtcp) { - return false; - } - // However, there shouldn't be any RTP packets sent before SRTP is set up - // (and SetSend(true) is called). - RTC_LOG(LS_ERROR) << "Can't send outgoing RTP packet for " << ToString() - << " when SRTP is inactive and crypto is required"; - RTC_NOTREACHED(); + // However, there shouldn't be any RTP packets sent before SRTP is set + // up (and SetSend(true) is called). + RTC_DCHECK(rtcp) << "Can't send outgoing RTP packet for " << ToString() + << " when SRTP is inactive and crypto is required"; return false; } - std::string packet_type = rtcp ? "RTCP" : "RTP"; - RTC_DLOG(LS_WARNING) << "Sending an " << packet_type + RTC_DLOG(LS_WARNING) << "Sending an " << (rtcp ? "RTCP" : "RTP") << " packet without encryption for " << ToString() << "."; } - // Bon voyage. return rtcp ? rtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS) : rtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS); } void BaseChannel::OnRtpPacket(const webrtc::RtpPacketReceived& parsed_packet) { RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); if (on_first_packet_received_) { on_first_packet_received_(); @@ -469,41 +443,74 @@ void BaseChannel::OnRtpPacket(const webrtc::RtpPacketReceived& parsed_packet) { packet_time.IsMinusInfinity() ? -1 : packet_time.us()); } -void BaseChannel::UpdateRtpHeaderExtensionMap( - const RtpHeaderExtensions& header_extensions) { - // Update the header extension map on network thread in case there is data - // race. - // - // NOTE: This doesn't take the BUNDLE case in account meaning the RTP header - // extension maps are not merged when BUNDLE is enabled. This is fine because - // the ID for MID should be consistent among all the RTP transports. - network_thread_->Invoke(RTC_FROM_HERE, [this, &header_extensions] { +bool BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w( + bool update_demuxer, + absl::optional extensions, + std::string& error_desc) { + if (extensions) { + if (rtp_header_extensions_ == extensions) { + extensions.reset(); // No need to update header extensions. + } else { + rtp_header_extensions_ = *extensions; + } + } + + if (!update_demuxer && !extensions) + return true; // No update needed. + + // TODO(bugs.webrtc.org/13536): See if we can do this asynchronously. + + if (update_demuxer) + media_channel()->OnDemuxerCriteriaUpdatePending(); + + bool success = network_thread()->Invoke(RTC_FROM_HERE, [&]() mutable { RTC_DCHECK_RUN_ON(network_thread()); - rtp_transport_->UpdateRtpHeaderExtensionMap(header_extensions); + // NOTE: This doesn't take the BUNDLE case in account meaning the RTP header + // extension maps are not merged when BUNDLE is enabled. This is fine + // because the ID for MID should be consistent among all the RTP transports. + if (extensions) + rtp_transport_->UpdateRtpHeaderExtensionMap(*extensions); + + if (!update_demuxer) + return true; + + if (!rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria_, this)) { + error_desc = + StringFormat("Failed to apply demuxer criteria for '%s': '%s'.", + mid().c_str(), demuxer_criteria_.ToString().c_str()); + return false; + } + return true; }); + + if (update_demuxer) + media_channel()->OnDemuxerCriteriaUpdateComplete(); + + return success; } bool BaseChannel::RegisterRtpDemuxerSink_w() { - if (demuxer_criteria_ == previous_demuxer_criteria_) { - return true; - } media_channel_->OnDemuxerCriteriaUpdatePending(); // Copy demuxer criteria, since they're a worker-thread variable // and we want to pass them to the network thread - return network_thread_->Invoke( + bool ret = network_thread_->Invoke( RTC_FROM_HERE, [this, demuxer_criteria = demuxer_criteria_] { RTC_DCHECK_RUN_ON(network_thread()); - RTC_DCHECK(rtp_transport_); - bool result = - rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria, this); - if (result) { - previous_demuxer_criteria_ = demuxer_criteria; - } else { - previous_demuxer_criteria_ = {}; + if (!rtp_transport_) { + // Transport was disconnected before attempting to update the + // criteria. This can happen while setting the remote description. + // See chromium:1295469 for an example. + return false; } - media_channel_->OnDemuxerCriteriaUpdateComplete(); - return result; + // Note that RegisterRtpDemuxerSink first unregisters the sink if + // already registered. So this will change the state of the class + // whether the call succeeds or not. + return rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria, this); }); + + media_channel_->OnDemuxerCriteriaUpdateComplete(); + + return ret; } void BaseChannel::EnableMedia_w() { @@ -563,23 +570,17 @@ void BaseChannel::ChannelNotWritable_n() { RTC_LOG(LS_INFO) << "Channel not writable (" << ToString() << ")"; } -bool BaseChannel::AddRecvStream_w(const StreamParams& sp) { - return media_channel()->AddRecvStream(sp); -} - -bool BaseChannel::RemoveRecvStream_w(uint32_t ssrc) { - return media_channel()->RemoveRecvStream(ssrc); -} - -void BaseChannel::ResetUnsignaledRecvStream_w() { - media_channel()->ResetUnsignaledRecvStream(); -} - bool BaseChannel::SetPayloadTypeDemuxingEnabled_w(bool enabled) { + RTC_LOG_THREAD_BLOCK_COUNT(); + if (enabled == payload_type_demuxing_enabled_) { return true; } + payload_type_demuxing_enabled_ = enabled; + + bool config_changed = false; + if (!enabled) { // TODO(crbug.com/11477): This will remove *all* unsignaled streams (those // without an explicitly signaled SSRC), which may include streams that @@ -587,27 +588,32 @@ bool BaseChannel::SetPayloadTypeDemuxingEnabled_w(bool enabled) { // streams that were matched based on payload type alone, but currently // there is no straightforward way to identify those streams. media_channel()->ResetUnsignaledRecvStream(); - demuxer_criteria_.payload_types.clear(); - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to disable payload type demuxing for " - << ToString(); - return false; + if (!demuxer_criteria_.payload_types().empty()) { + config_changed = true; + demuxer_criteria_.payload_types().clear(); } } else if (!payload_types_.empty()) { - demuxer_criteria_.payload_types.insert(payload_types_.begin(), - payload_types_.end()); - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to enable payload type demuxing for " - << ToString(); - return false; + for (const auto& type : payload_types_) { + if (demuxer_criteria_.payload_types().insert(type).second) { + config_changed = true; + } } + } else { + RTC_DCHECK(demuxer_criteria_.payload_types().empty()); } - return true; + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); + + if (!config_changed) + return true; + + // Note: This synchronously hops to the network thread. + return RegisterRtpDemuxerSink_w(); } bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, SdpType type, - std::string* error_desc) { + std::string& error_desc) { // In the case of RIDs (where SSRCs are not negotiated), this method will // generate an SSRC for each layer in StreamParams. That representation will // be stored internally in `local_streams_`. @@ -627,11 +633,10 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, continue; } if (!media_channel()->RemoveSendStream(old_stream.first_ssrc())) { - rtc::StringBuilder desc; - desc << "Failed to remove send stream with ssrc " - << old_stream.first_ssrc() << " from m-section with mid='" - << content_name() << "'."; - SafeSetError(desc.str(), error_desc); + error_desc = StringFormat( + "Failed to remove send stream with ssrc %u from m-section with " + "mid='%s'.", + old_stream.first_ssrc(), mid().c_str()); ret = false; } } @@ -654,11 +659,10 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids()); if (new_stream.has_ssrcs() && new_stream.has_rids()) { - rtc::StringBuilder desc; - desc << "Failed to add send stream: " << new_stream.first_ssrc() - << " into m-section with mid='" << content_name() - << "'. Stream has both SSRCs and RIDs."; - SafeSetError(desc.str(), error_desc); + error_desc = StringFormat( + "Failed to add send stream: %u into m-section with mid='%s'. Stream " + "has both SSRCs and RIDs.", + new_stream.first_ssrc(), mid().c_str()); ret = false; continue; } @@ -675,10 +679,9 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0] << " into " << ToString(); } else { - rtc::StringBuilder desc; - desc << "Failed to add send stream ssrc: " << new_stream.first_ssrc() - << " into m-section with mid='" << content_name() << "'"; - SafeSetError(desc.str(), error_desc); + error_desc = StringFormat( + "Failed to add send stream ssrc: %u into m-section with mid='%s'", + new_stream.first_ssrc(), mid().c_str()); ret = false; } } @@ -686,96 +689,128 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, return ret; } -bool BaseChannel::UpdateRemoteStreams_w( - const std::vector& streams, - SdpType type, - std::string* error_desc) { +bool BaseChannel::UpdateRemoteStreams_w(const MediaContentDescription* content, + SdpType type, + std::string& error_desc) { + RTC_LOG_THREAD_BLOCK_COUNT(); + bool needs_re_registration = false; + if (!webrtc::RtpTransceiverDirectionHasSend(content->direction())) { + RTC_DLOG(LS_VERBOSE) << "UpdateRemoteStreams_w: remote side will not send " + "- disable payload type demuxing for " + << ToString(); + if (ClearHandledPayloadTypes()) { + needs_re_registration = payload_type_demuxing_enabled_; + } + } + + const std::vector& streams = content->streams(); + const bool new_has_unsignaled_ssrcs = HasStreamWithNoSsrcs(streams); + const bool old_has_unsignaled_ssrcs = HasStreamWithNoSsrcs(remote_streams_); + // Check for streams that have been removed. - bool ret = true; for (const StreamParams& old_stream : remote_streams_) { // If we no longer have an unsignaled stream, we would like to remove // the unsignaled stream params that are cached. - if (!old_stream.has_ssrcs() && !HasStreamWithNoSsrcs(streams)) { - ResetUnsignaledRecvStream_w(); + if (!old_stream.has_ssrcs() && !new_has_unsignaled_ssrcs) { + media_channel()->ResetUnsignaledRecvStream(); RTC_LOG(LS_INFO) << "Reset unsignaled remote stream for " << ToString() << "."; } else if (old_stream.has_ssrcs() && !GetStreamBySsrc(streams, old_stream.first_ssrc())) { - if (RemoveRecvStream_w(old_stream.first_ssrc())) { + if (media_channel()->RemoveRecvStream(old_stream.first_ssrc())) { RTC_LOG(LS_INFO) << "Remove remote ssrc: " << old_stream.first_ssrc() << " from " << ToString() << "."; } else { - rtc::StringBuilder desc; - desc << "Failed to remove remote stream with ssrc " - << old_stream.first_ssrc() << " from m-section with mid='" - << content_name() << "'."; - SafeSetError(desc.str(), error_desc); - ret = false; + error_desc = StringFormat( + "Failed to remove remote stream with ssrc %u from m-section with " + "mid='%s'.", + old_stream.first_ssrc(), mid().c_str()); + return false; } } } - demuxer_criteria_.ssrcs.clear(); + // Check for new streams. + webrtc::flat_set ssrcs; for (const StreamParams& new_stream : streams) { // We allow a StreamParams with an empty list of SSRCs, in which case the // MediaChannel will cache the parameters and use them for any unsignaled // stream received later. - if ((!new_stream.has_ssrcs() && !HasStreamWithNoSsrcs(remote_streams_)) || + if ((!new_stream.has_ssrcs() && !old_has_unsignaled_ssrcs) || !GetStreamBySsrc(remote_streams_, new_stream.first_ssrc())) { - if (AddRecvStream_w(new_stream)) { + if (media_channel()->AddRecvStream(new_stream)) { RTC_LOG(LS_INFO) << "Add remote ssrc: " << (new_stream.has_ssrcs() ? std::to_string(new_stream.first_ssrc()) : "unsignaled") << " to " << ToString(); } else { - rtc::StringBuilder desc; - desc << "Failed to add remote stream ssrc: " - << (new_stream.has_ssrcs() - ? std::to_string(new_stream.first_ssrc()) - : "unsignaled") - << " to " << ToString(); - SafeSetError(desc.str(), error_desc); - ret = false; + error_desc = + StringFormat("Failed to add remote stream ssrc: %s to %s", + new_stream.has_ssrcs() + ? std::to_string(new_stream.first_ssrc()).c_str() + : "unsignaled", + ToString().c_str()); + return false; } } // Update the receiving SSRCs. - demuxer_criteria_.ssrcs.insert(new_stream.ssrcs.begin(), - new_stream.ssrcs.end()); + ssrcs.insert(new_stream.ssrcs.begin(), new_stream.ssrcs.end()); } - // Re-register the sink to update the receiving ssrcs. - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to set up demuxing for " << ToString(); - ret = false; + + if (demuxer_criteria_.ssrcs() != ssrcs) { + demuxer_criteria_.ssrcs() = std::move(ssrcs); + needs_re_registration = true; } + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); + + // Re-register the sink to update after changing the demuxer criteria. + if (needs_re_registration && !RegisterRtpDemuxerSink_w()) { + error_desc = StringFormat("Failed to set up audio demuxing for mid='%s'.", + mid().c_str()); + return false; + } + remote_streams_ = streams; - return ret; + + set_remote_content_direction(content->direction()); + UpdateMediaSendRecvState_w(); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + + return true; } RtpHeaderExtensions BaseChannel::GetDeduplicatedRtpHeaderExtensions( const RtpHeaderExtensions& extensions) { - return webrtc::RtpExtension::DeduplicateHeaderExtensions( - extensions, crypto_options_.srtp.enable_encrypted_rtp_header_extensions - ? webrtc::RtpExtension::kPreferEncryptedExtension - : webrtc::RtpExtension::kDiscardEncryptedExtension); + return webrtc::RtpExtension::DeduplicateHeaderExtensions(extensions, + extensions_filter_); } -void BaseChannel::MaybeAddHandledPayloadType(int payload_type) { +bool BaseChannel::MaybeAddHandledPayloadType(int payload_type) { + bool demuxer_criteria_modified = false; if (payload_type_demuxing_enabled_) { - demuxer_criteria_.payload_types.insert(static_cast(payload_type)); + demuxer_criteria_modified = demuxer_criteria_.payload_types() + .insert(static_cast(payload_type)) + .second; } // Even if payload type demuxing is currently disabled, we need to remember // the payload types in case it's re-enabled later. payload_types_.insert(static_cast(payload_type)); + return demuxer_criteria_modified; } -void BaseChannel::ClearHandledPayloadTypes() { - demuxer_criteria_.payload_types.clear(); +bool BaseChannel::ClearHandledPayloadTypes() { + const bool was_empty = demuxer_criteria_.payload_types().empty(); + demuxer_criteria_.payload_types().clear(); payload_types_.clear(); + return !was_empty; } void BaseChannel::SignalSentPacket_n(const rtc::SentPacket& sent_packet) { RTC_DCHECK_RUN_ON(network_thread()); + RTC_DCHECK(network_initialized()); media_channel()->OnPacketSent(sent_packet); } @@ -783,7 +818,7 @@ VoiceChannel::VoiceChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -791,7 +826,7 @@ VoiceChannel::VoiceChannel(rtc::Thread* worker_thread, network_thread, signaling_thread, std::move(media_channel), - content_name, + mid, srtp_required, crypto_options, ssrc_generator) {} @@ -800,141 +835,106 @@ VoiceChannel::~VoiceChannel() { TRACE_EVENT0("webrtc", "VoiceChannel::~VoiceChannel"); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); - Deinit(); } void VoiceChannel::UpdateMediaSendRecvState_w() { // Render incoming data if we're the active call, and we have the local // content. We receive data on the default channel and multiplexed streams. - RTC_DCHECK_RUN_ON(worker_thread()); - bool recv = IsReadyToReceiveMedia_w(); - media_channel()->SetPlayout(recv); + bool ready_to_receive = enabled() && webrtc::RtpTransceiverDirectionHasRecv( + local_content_direction()); + media_channel()->SetPlayout(ready_to_receive); // Send outgoing data if we're the active call, we have the remote content, // and we have had some form of connectivity. bool send = IsReadyToSendMedia_w(); media_channel()->SetSend(send); - RTC_LOG(LS_INFO) << "Changing voice state, recv=" << recv << " send=" << send - << " for " << ToString(); + RTC_LOG(LS_INFO) << "Changing voice state, recv=" << ready_to_receive + << " send=" << send << " for " << ToString(); } bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { TRACE_EVENT0("webrtc", "VoiceChannel::SetLocalContent_w"); - RTC_DCHECK_RUN_ON(worker_thread()); - RTC_LOG(LS_INFO) << "Setting local voice description for " << ToString(); + RTC_DLOG(LS_INFO) << "Setting local voice description for " << ToString(); - RtpHeaderExtensions rtp_header_extensions = + RTC_LOG_THREAD_BLOCK_COUNT(); + + RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); - // TODO(tommi): There's a hop to the network thread here. - // some of the below is also network thread related. - UpdateRtpHeaderExtensionMap(rtp_header_extensions); + bool update_header_extensions = true; media_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed()); AudioRecvParameters recv_params = last_recv_params_; RtpParametersFromMediaDescription( - content->as_audio(), rtp_header_extensions, + content->as_audio(), header_extensions, webrtc::RtpTransceiverDirectionHasRecv(content->direction()), &recv_params); if (!media_channel()->SetRecvParameters(recv_params)) { - SafeSetError( + error_desc = StringFormat( "Failed to set local audio description recv parameters for m-section " - "with mid='" + - content_name() + "'.", - error_desc); + "with mid='%s'.", + mid().c_str()); return false; } + bool criteria_modified = false; if (webrtc::RtpTransceiverDirectionHasRecv(content->direction())) { for (const AudioCodec& codec : content->as_audio()->codecs()) { - MaybeAddHandledPayloadType(codec.id); - } - // Need to re-register the sink to update the handled payload. - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to set up audio demuxing for " << ToString(); - return false; + if (MaybeAddHandledPayloadType(codec.id)) { + criteria_modified = true; + } } } last_recv_params_ = recv_params; - // TODO(pthatcher): Move local streams into AudioSendParameters, and - // only give it to the media channel once we have a remote - // description too (without a remote description, we won't be able - // to send them anyway). if (!UpdateLocalStreams_w(content->as_audio()->streams(), type, error_desc)) { - SafeSetError( - "Failed to set local audio description streams for m-section with " - "mid='" + - content_name() + "'.", - error_desc); + RTC_DCHECK(!error_desc.empty()); return false; } set_local_content_direction(content->direction()); UpdateMediaSendRecvState_w(); - return true; + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); + + bool success = MaybeUpdateDemuxerAndRtpExtensions_w( + criteria_modified, + update_header_extensions + ? absl::optional(std::move(header_extensions)) + : absl::nullopt, + error_desc); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + + return success; } bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { TRACE_EVENT0("webrtc", "VoiceChannel::SetRemoteContent_w"); - RTC_DCHECK_RUN_ON(worker_thread()); RTC_LOG(LS_INFO) << "Setting remote voice description for " << ToString(); - const AudioContentDescription* audio = content->as_audio(); - - RtpHeaderExtensions rtp_header_extensions = - GetDeduplicatedRtpHeaderExtensions(audio->rtp_header_extensions()); - AudioSendParameters send_params = last_send_params_; - RtpSendParametersFromMediaDescription( - audio, rtp_header_extensions, - webrtc::RtpTransceiverDirectionHasRecv(audio->direction()), &send_params); - send_params.mid = content_name(); + RtpSendParametersFromMediaDescription(content->as_audio(), + extensions_filter(), &send_params); + send_params.mid = mid(); bool parameters_applied = media_channel()->SetSendParameters(send_params); if (!parameters_applied) { - SafeSetError( + error_desc = StringFormat( "Failed to set remote audio description send parameters for m-section " - "with mid='" + - content_name() + "'.", - error_desc); + "with mid='%s'.", + mid().c_str()); return false; } last_send_params_ = send_params; - if (!webrtc::RtpTransceiverDirectionHasSend(content->direction())) { - RTC_DLOG(LS_VERBOSE) << "SetRemoteContent_w: remote side will not send - " - "disable payload type demuxing for " - << ToString(); - ClearHandledPayloadTypes(); - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to update audio demuxing for " << ToString(); - return false; - } - } - - // TODO(pthatcher): Move remote streams into AudioRecvParameters, - // and only give it to the media channel once we have a local - // description too (without a local description, we won't be able to - // recv them anyway). - if (!UpdateRemoteStreams_w(audio->streams(), type, error_desc)) { - SafeSetError( - "Failed to set remote audio description streams for m-section with " - "mid='" + - content_name() + "'.", - error_desc); - return false; - } - - set_remote_content_direction(content->direction()); - UpdateMediaSendRecvState_w(); - return true; + return UpdateRemoteStreams_w(content, type, error_desc); } // RingRTC change to configure OPUS @@ -959,7 +959,7 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -967,7 +967,7 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread, network_thread, signaling_thread, std::move(media_channel), - content_name, + mid, srtp_required, crypto_options, ssrc_generator) {} @@ -976,19 +976,13 @@ VideoChannel::~VideoChannel() { TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel"); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); - Deinit(); } void VideoChannel::UpdateMediaSendRecvState_w() { // Send outgoing data if we're the active call, we have the remote content, // and we have had some form of connectivity. - RTC_DCHECK_RUN_ON(worker_thread()); bool send = IsReadyToSendMedia_w(); - if (!media_channel()->SetSend(send)) { - RTC_LOG(LS_ERROR) << "Failed to SetSend on video channel: " + ToString(); - // TODO(gangji): Report error back to server. - } - + media_channel()->SetSend(send); RTC_LOG(LS_INFO) << "Changing video state, send=" << send << " for " << ToString(); } @@ -1001,20 +995,21 @@ void VideoChannel::FillBitrateInfo(BandwidthEstimationInfo* bwe_info) { bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { TRACE_EVENT0("webrtc", "VideoChannel::SetLocalContent_w"); - RTC_DCHECK_RUN_ON(worker_thread()); - RTC_LOG(LS_INFO) << "Setting local video description for " << ToString(); + RTC_DLOG(LS_INFO) << "Setting local video description for " << ToString(); - RtpHeaderExtensions rtp_header_extensions = + RTC_LOG_THREAD_BLOCK_COUNT(); + + RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); - UpdateRtpHeaderExtensionMap(rtp_header_extensions); + bool update_header_extensions = true; media_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed()); VideoRecvParameters recv_params = last_recv_params_; RtpParametersFromMediaDescription( - content->as_video(), rtp_header_extensions, + content->as_video(), header_extensions, webrtc::RtpTransceiverDirectionHasRecv(content->direction()), &recv_params); @@ -1029,11 +1024,10 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, send_codec.packetization.reset(); needs_send_params_update = true; } else if (recv_codec->packetization != send_codec.packetization) { - SafeSetError( + error_desc = StringFormat( "Failed to set local answer due to invalid codec packetization " - "specified in m-section with mid='" + - content_name() + "'.", - error_desc); + "specified in m-section with mid='%s'.", + mid().c_str()); return false; } } @@ -1041,22 +1035,18 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, } if (!media_channel()->SetRecvParameters(recv_params)) { - SafeSetError( + error_desc = StringFormat( "Failed to set local video description recv parameters for m-section " - "with mid='" + - content_name() + "'.", - error_desc); + "with mid='%s'.", + mid().c_str()); return false; } + bool criteria_modified = false; if (webrtc::RtpTransceiverDirectionHasRecv(content->direction())) { for (const VideoCodec& codec : content->as_video()->codecs()) { - MaybeAddHandledPayloadType(codec.id); - } - // Need to re-register the sink to update the handled payload. - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to set up video demuxing for " << ToString(); - return false; + if (MaybeAddHandledPayloadType(codec.id)) + criteria_modified = true; } } @@ -1064,52 +1054,49 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, if (needs_send_params_update) { if (!media_channel()->SetSendParameters(send_params)) { - SafeSetError("Failed to set send parameters for m-section with mid='" + - content_name() + "'.", - error_desc); + error_desc = StringFormat( + "Failed to set send parameters for m-section with mid='%s'.", + mid().c_str()); return false; } last_send_params_ = send_params; } - // TODO(pthatcher): Move local streams into VideoSendParameters, and - // only give it to the media channel once we have a remote - // description too (without a remote description, we won't be able - // to send them anyway). if (!UpdateLocalStreams_w(content->as_video()->streams(), type, error_desc)) { - SafeSetError( - "Failed to set local video description streams for m-section with " - "mid='" + - content_name() + "'.", - error_desc); + RTC_DCHECK(!error_desc.empty()); return false; } set_local_content_direction(content->direction()); UpdateMediaSendRecvState_w(); - return true; + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); + + bool success = MaybeUpdateDemuxerAndRtpExtensions_w( + criteria_modified, + update_header_extensions + ? absl::optional(std::move(header_extensions)) + : absl::nullopt, + error_desc); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + + return success; } bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, SdpType type, - std::string* error_desc) { + std::string& error_desc) { TRACE_EVENT0("webrtc", "VideoChannel::SetRemoteContent_w"); - RTC_DCHECK_RUN_ON(worker_thread()); RTC_LOG(LS_INFO) << "Setting remote video description for " << ToString(); const VideoContentDescription* video = content->as_video(); - RtpHeaderExtensions rtp_header_extensions = - GetDeduplicatedRtpHeaderExtensions(video->rtp_header_extensions()); - VideoSendParameters send_params = last_send_params_; - RtpSendParametersFromMediaDescription( - video, rtp_header_extensions, - webrtc::RtpTransceiverDirectionHasRecv(video->direction()), &send_params); - if (video->conference_mode()) { - send_params.conference_mode = true; - } - send_params.mid = content_name(); + RtpSendParametersFromMediaDescription(video, extensions_filter(), + &send_params); + send_params.mid = mid(); + send_params.conference_mode = video->conference_mode(); VideoRecvParameters recv_params = last_recv_params_; @@ -1122,11 +1109,10 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, recv_codec.packetization.reset(); needs_recv_params_update = true; } else if (send_codec->packetization != recv_codec.packetization) { - SafeSetError( + error_desc = StringFormat( "Failed to set remote answer due to invalid codec packetization " - "specifid in m-section with mid='" + - content_name() + "'.", - error_desc); + "specifid in m-section with mid='%s'.", + mid().c_str()); return false; } } @@ -1134,51 +1120,25 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, } if (!media_channel()->SetSendParameters(send_params)) { - SafeSetError( + error_desc = StringFormat( "Failed to set remote video description send parameters for m-section " - "with mid='" + - content_name() + "'.", - error_desc); + "with mid='%s'.", + mid().c_str()); return false; } last_send_params_ = send_params; if (needs_recv_params_update) { if (!media_channel()->SetRecvParameters(recv_params)) { - SafeSetError("Failed to set recv parameters for m-section with mid='" + - content_name() + "'.", - error_desc); + error_desc = StringFormat( + "Failed to set recv parameters for m-section with mid='%s'.", + mid().c_str()); return false; } last_recv_params_ = recv_params; } - if (!webrtc::RtpTransceiverDirectionHasSend(content->direction())) { - RTC_DLOG(LS_VERBOSE) << "SetRemoteContent_w: remote side will not send - " - "disable payload type demuxing for " - << ToString(); - ClearHandledPayloadTypes(); - if (!RegisterRtpDemuxerSink_w()) { - RTC_LOG(LS_ERROR) << "Failed to update video demuxing for " << ToString(); - return false; - } - } - - // TODO(pthatcher): Move remote streams into VideoRecvParameters, - // and only give it to the media channel once we have a local - // description too (without a local description, we won't be able to - // recv them anyway). - if (!UpdateRemoteStreams_w(video->streams(), type, error_desc)) { - SafeSetError( - "Failed to set remote video description streams for m-section with " - "mid='" + - content_name() + "'.", - error_desc); - return false; - } - set_remote_content_direction(content->direction()); - UpdateMediaSendRecvState_w(); - return true; + return UpdateRemoteStreams_w(content, type, error_desc); } } // namespace cricket diff --git a/pc/channel.h b/pc/channel.h index 36e4abf409..c61f42909e 100644 --- a/pc/channel.h +++ b/pc/channel.h @@ -11,50 +11,35 @@ #ifndef PC_CHANNEL_H_ #define PC_CHANNEL_H_ -#include #include -#include +#include #include -#include #include #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "api/call/audio_sink.h" #include "api/crypto/crypto_options.h" -#include "api/function_view.h" #include "api/jsep.h" #include "api/media_types.h" -#include "api/rtp_receiver_interface.h" +#include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" -#include "api/video/video_sink_interface.h" -#include "api/video/video_source_interface.h" #include "call/rtp_demuxer.h" #include "call/rtp_packet_sink_interface.h" #include "media/base/media_channel.h" -#include "media/base/media_engine.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "p2p/base/dtls_transport_internal.h" -#include "p2p/base/packet_transport_internal.h" #include "pc/channel_interface.h" -#include "pc/dtls_srtp_transport.h" -#include "pc/media_session.h" -#include "pc/rtp_transport.h" #include "pc/rtp_transport_internal.h" #include "pc/session_description.h" -#include "pc/srtp_filter.h" -#include "pc/srtp_transport.h" #include "rtc_base/async_packet_socket.h" -#include "rtc_base/async_udp_socket.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/location.h" -#include "rtc_base/network.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" #include "rtc_base/socket.h" @@ -62,17 +47,10 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" -#include "rtc_base/thread_message.h" #include "rtc_base/unique_id_generator.h" -namespace webrtc { -class AudioSinkInterface; -} // namespace webrtc - namespace cricket { -struct CryptoParams; - // BaseChannel contains logic common to voice and video, including enable, // marshaling calls to a worker and network threads, and connection and media // monitors. @@ -86,10 +64,6 @@ struct CryptoParams; // and methods with _s suffix on signaling thread. // Network and worker threads may be the same thread. // -// WARNING! SUBCLASSES MUST CALL Deinit() IN THEIR DESTRUCTORS! -// This is required to avoid a data race between the destructor modifying the -// vtable, and the media channel's thread using BaseChannel as the -// NetworkInterface. class BaseChannel : public ChannelInterface, // TODO(tommi): Remove has_slots inheritance. @@ -109,27 +83,21 @@ class BaseChannel : public ChannelInterface, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); virtual ~BaseChannel(); - virtual void Init_w(webrtc::RtpTransportInternal* rtp_transport); - - // Deinit may be called multiple times and is simply ignored if it's already - // done. - void Deinit(); rtc::Thread* worker_thread() const { return worker_thread_; } rtc::Thread* network_thread() const { return network_thread_; } - const std::string& content_name() const override { return content_name_; } + const std::string& mid() const override { return demuxer_criteria_.mid(); } // TODO(deadbeef): This is redundant; remove this. - const std::string& transport_name() const override { + absl::string_view transport_name() const override { RTC_DCHECK_RUN_ON(network_thread()); if (rtp_transport_) return rtp_transport_->transport_name(); - // TODO(tommi): Delete this variable. - return transport_name_; + return ""; } // This function returns true if using SRTP (DTLS-based keying or SDES). @@ -152,10 +120,10 @@ class BaseChannel : public ChannelInterface, // Channel control bool SetLocalContent(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) override; bool SetRemoteContent(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) override; // Controls whether this channel will receive packets on the basis of // matching payload type alone. This is needed for legacy endpoints that // don't signal SSRCs or use MID/RID, but doesn't make sense if there is @@ -192,31 +160,47 @@ class BaseChannel : public ChannelInterface, } protected: - bool was_ever_writable() const { - RTC_DCHECK_RUN_ON(worker_thread()); - return was_ever_writable_; - } - void set_local_content_direction(webrtc::RtpTransceiverDirection direction) { - RTC_DCHECK_RUN_ON(worker_thread()); + void set_local_content_direction(webrtc::RtpTransceiverDirection direction) + RTC_RUN_ON(worker_thread()) { local_content_direction_ = direction; } - void set_remote_content_direction(webrtc::RtpTransceiverDirection direction) { - RTC_DCHECK_RUN_ON(worker_thread()); + + webrtc::RtpTransceiverDirection local_content_direction() const + RTC_RUN_ON(worker_thread()) { + return local_content_direction_; + } + + void set_remote_content_direction(webrtc::RtpTransceiverDirection direction) + RTC_RUN_ON(worker_thread()) { remote_content_direction_ = direction; } - // These methods verify that: + + webrtc::RtpTransceiverDirection remote_content_direction() const + RTC_RUN_ON(worker_thread()) { + return remote_content_direction_; + } + + webrtc::RtpExtension::Filter extensions_filter() const { + return extensions_filter_; + } + + bool network_initialized() RTC_RUN_ON(network_thread()) { + return media_channel_->HasNetworkInterface(); + } + + bool enabled() const RTC_RUN_ON(worker_thread()) { return enabled_; } + rtc::Thread* signaling_thread() const { return signaling_thread_; } + + // Call to verify that: // * The required content description directions have been set. // * The channel is enabled. - // * And for sending: - // - The SRTP filter is active if it's needed. - // - The transport has been writable before, meaning it should be at least - // possible to succeed in sending a packet. + // * The SRTP filter is active if it's needed. + // * The transport has been writable before, meaning it should be at least + // possible to succeed in sending a packet. // // When any of these properties change, UpdateMediaSendRecvState_w should be // called. - bool IsReadyToReceiveMedia_w() const RTC_RUN_ON(worker_thread()); bool IsReadyToSendMedia_w() const RTC_RUN_ON(worker_thread()); - rtc::Thread* signaling_thread() const { return signaling_thread_; } // NetworkInterface implementation, called by MediaEngine bool SendPacket(rtc::CopyOnWriteBuffer* packet, @@ -243,13 +227,8 @@ class BaseChannel : public ChannelInterface, void ChannelWritable_n() RTC_RUN_ON(network_thread()); void ChannelNotWritable_n() RTC_RUN_ON(network_thread()); - bool AddRecvStream_w(const StreamParams& sp) RTC_RUN_ON(worker_thread()); - bool RemoveRecvStream_w(uint32_t ssrc) RTC_RUN_ON(worker_thread()); - void ResetUnsignaledRecvStream_w() RTC_RUN_ON(worker_thread()); bool SetPayloadTypeDemuxingEnabled_w(bool enabled) RTC_RUN_ON(worker_thread()); - bool AddSendStream_w(const StreamParams& sp) RTC_RUN_ON(worker_thread()); - bool RemoveSendStream_w(uint32_t ssrc) RTC_RUN_ON(worker_thread()); // Should be called whenever the conditions for // IsReadyToReceiveMedia/IsReadyToSendMedia are satisfied (or unsatisfied). @@ -258,19 +237,19 @@ class BaseChannel : public ChannelInterface, bool UpdateLocalStreams_w(const std::vector& streams, webrtc::SdpType type, - std::string* error_desc) + std::string& error_desc) RTC_RUN_ON(worker_thread()); - bool UpdateRemoteStreams_w(const std::vector& streams, + bool UpdateRemoteStreams_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) + std::string& error_desc) RTC_RUN_ON(worker_thread()); virtual bool SetLocalContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) + std::string& error_desc) RTC_RUN_ON(worker_thread()) = 0; virtual bool SetRemoteContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) + std::string& error_desc) RTC_RUN_ON(worker_thread()) = 0; // Returns a list of RTP header extensions where any extension URI is unique. @@ -281,12 +260,26 @@ class BaseChannel : public ChannelInterface, // Add `payload_type` to `demuxer_criteria_` if payload type demuxing is // enabled. - void MaybeAddHandledPayloadType(int payload_type) RTC_RUN_ON(worker_thread()); + // Returns true if the demuxer payload type changed and a re-registration + // is needed. + bool MaybeAddHandledPayloadType(int payload_type) RTC_RUN_ON(worker_thread()); - void ClearHandledPayloadTypes() RTC_RUN_ON(worker_thread()); + // Returns true if the demuxer payload type criteria was non-empty before + // clearing. + bool ClearHandledPayloadTypes() RTC_RUN_ON(worker_thread()); - void UpdateRtpHeaderExtensionMap( - const RtpHeaderExtensions& header_extensions); + // Hops to the network thread to update the transport if an update is + // requested. If `update_demuxer` is false and `extensions` is not set, the + // function simply returns. If either of these is set, the function updates + // the transport with either or both of the demuxer criteria and the supplied + // rtp header extensions. + // Returns `true` if either an update wasn't needed or one was successfully + // applied. If the return value is `false`, then updating the demuxer criteria + // failed, which needs to be treated as an error. + bool MaybeUpdateDemuxerAndRtpExtensions_w( + bool update_demuxer, + absl::optional extensions, + std::string& error_desc) RTC_RUN_ON(worker_thread()); bool RegisterRtpDemuxerSink_w() RTC_RUN_ON(worker_thread()); @@ -294,8 +287,8 @@ class BaseChannel : public ChannelInterface, std::string ToString() const; private: - bool ConnectToRtpTransport() RTC_RUN_ON(network_thread()); - void DisconnectFromRtpTransport() RTC_RUN_ON(network_thread()); + bool ConnectToRtpTransport_n() RTC_RUN_ON(network_thread()); + void DisconnectFromRtpTransport_n() RTC_RUN_ON(network_thread()); void SignalSentPacket_n(const rtc::SentPacket& sent_packet); rtc::Thread* const worker_thread_; @@ -303,19 +296,9 @@ class BaseChannel : public ChannelInterface, rtc::Thread* const signaling_thread_; rtc::scoped_refptr alive_; - const std::string content_name_; - std::function on_first_packet_received_ RTC_GUARDED_BY(network_thread()); - // Won't be set when using raw packet transports. SDP-specific thing. - // TODO(bugs.webrtc.org/12230): Written on network thread, read on - // worker thread (at least). - // TODO(tommi): Remove this variable and instead use rtp_transport_ to - // return the transport name. This variable is currently required for - // "for_test" methods. - std::string transport_name_; - webrtc::RtpTransportInternal* rtp_transport_ RTC_GUARDED_BY(network_thread()) = nullptr; @@ -328,24 +311,9 @@ class BaseChannel : public ChannelInterface, bool was_ever_writable_ RTC_GUARDED_BY(worker_thread()) = false; const bool srtp_required_ = true; - // TODO(tommi): This field shouldn't be necessary. It's a copy of - // PeerConnection::GetCryptoOptions(), which is const state. It's also only - // used to filter header extensions when calling - // `rtp_transport_->UpdateRtpHeaderExtensionMap()` when the local/remote - // content description is updated. Since the transport is actually owned - // by the transport controller that also gets updated whenever the content - // description changes, it seems we have two paths into the transports, along - // with several thread hops via various classes (such as the Channel classes) - // that only serve as additional layers and store duplicate state. The Jsep* - // family of classes already apply session description updates on the network - // thread every time it changes. - // For the Channel classes, we should be able to get rid of: - // * crypto_options (and fewer construction parameters)_ - // * UpdateRtpHeaderExtensionMap - // * GetFilteredRtpHeaderExtensions - // * Blocking thread hop to the network thread for every call to set - // local/remote content is updated. - const webrtc::CryptoOptions crypto_options_; + // Set to either kPreferEncryptedExtension or kDiscardEncryptedExtension + // based on the supplied CryptoOptions. + const webrtc::RtpExtension::Filter extensions_filter_; // MediaChannel related members that should be accessed from the worker // thread. @@ -358,22 +326,18 @@ class BaseChannel : public ChannelInterface, bool payload_type_demuxing_enabled_ RTC_GUARDED_BY(worker_thread()) = true; std::vector local_streams_ RTC_GUARDED_BY(worker_thread()); std::vector remote_streams_ RTC_GUARDED_BY(worker_thread()); - // TODO(bugs.webrtc.org/12230): local_content_direction and - // remote_content_direction are set on the worker thread, but accessed on the - // network thread. - webrtc::RtpTransceiverDirection local_content_direction_ = - webrtc::RtpTransceiverDirection::kInactive; - webrtc::RtpTransceiverDirection remote_content_direction_ = - webrtc::RtpTransceiverDirection::kInactive; + webrtc::RtpTransceiverDirection local_content_direction_ RTC_GUARDED_BY( + worker_thread()) = webrtc::RtpTransceiverDirection::kInactive; + webrtc::RtpTransceiverDirection remote_content_direction_ RTC_GUARDED_BY( + worker_thread()) = webrtc::RtpTransceiverDirection::kInactive; // Cached list of payload types, used if payload type demuxing is re-enabled. - std::set payload_types_ RTC_GUARDED_BY(worker_thread()); + webrtc::flat_set payload_types_ RTC_GUARDED_BY(worker_thread()); + // A stored copy of the rtp header extensions as applied to the transport. + RtpHeaderExtensions rtp_header_extensions_ RTC_GUARDED_BY(worker_thread()); // TODO(bugs.webrtc.org/12239): Modified on worker thread, accessed // on network thread in RegisterRtpDemuxerSink_n (called from Init_w) webrtc::RtpDemuxerCriteria demuxer_criteria_; - // Accessed on the worker thread, modified on the network thread from - // RegisterRtpDemuxerSink_w's Invoke. - webrtc::RtpDemuxerCriteria previous_demuxer_criteria_; // This generator is used to generate SSRCs for local streams. // This is needed in cases where SSRCs are not negotiated or set explicitly // like in Simulcast. @@ -389,7 +353,7 @@ class VoiceChannel : public BaseChannel { rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); @@ -416,20 +380,22 @@ class VoiceChannel : public BaseChannel { private: // overrides from BaseChannel - void UpdateMediaSendRecvState_w() override; + void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override; bool SetLocalContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) + RTC_RUN_ON(worker_thread()) override; bool SetRemoteContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) + RTC_RUN_ON(worker_thread()) override; // Last AudioSendParameters sent down to the media_channel() via // SetSendParameters. - AudioSendParameters last_send_params_; + AudioSendParameters last_send_params_ RTC_GUARDED_BY(worker_thread()); // Last AudioRecvParameters sent down to the media_channel() via // SetRecvParameters. - AudioRecvParameters last_recv_params_; + AudioRecvParameters last_recv_params_ RTC_GUARDED_BY(worker_thread()); }; // VideoChannel is a specialization for video. @@ -439,7 +405,7 @@ class VideoChannel : public BaseChannel { rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& content_name, + const std::string& mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); @@ -450,28 +416,30 @@ class VideoChannel : public BaseChannel { return static_cast(BaseChannel::media_channel()); } - void FillBitrateInfo(BandwidthEstimationInfo* bwe_info); - cricket::MediaType media_type() const override { return cricket::MEDIA_TYPE_VIDEO; } + void FillBitrateInfo(BandwidthEstimationInfo* bwe_info); + private: // overrides from BaseChannel - void UpdateMediaSendRecvState_w() override; + void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override; bool SetLocalContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) + RTC_RUN_ON(worker_thread()) override; bool SetRemoteContent_w(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) override; + std::string& error_desc) + RTC_RUN_ON(worker_thread()) override; // Last VideoSendParameters sent down to the media_channel() via // SetSendParameters. - VideoSendParameters last_send_params_; + VideoSendParameters last_send_params_ RTC_GUARDED_BY(worker_thread()); // Last VideoRecvParameters sent down to the media_channel() via // SetRecvParameters. - VideoRecvParameters last_recv_params_; + VideoRecvParameters last_recv_params_ RTC_GUARDED_BY(worker_thread()); }; } // namespace cricket diff --git a/pc/channel_interface.h b/pc/channel_interface.h index 3b71f0f8b5..a16a9b753d 100644 --- a/pc/channel_interface.h +++ b/pc/channel_interface.h @@ -14,16 +14,32 @@ #include #include +#include "absl/strings/string_view.h" #include "api/jsep.h" #include "api/media_types.h" #include "media/base/media_channel.h" #include "pc/rtp_transport_internal.h" +namespace webrtc { +class Call; +class VideoBitrateAllocatorFactory; +} // namespace webrtc + namespace cricket { class MediaContentDescription; +class VideoChannel; +class VoiceChannel; +struct MediaConfig; -// ChannelInterface contains methods common to voice, video and data channels. +// A Channel is a construct that groups media streams of the same type +// (audio or video), both outgoing and incoming. +// When the PeerConnection API is used, a Channel corresponds one to one +// to an RtpTransceiver. +// When Unified Plan is used, there can only be at most one outgoing and +// one incoming stream. With Plan B, there can be more than one. + +// ChannelInterface contains methods common to voice and video channels. // As more methods are added to BaseChannel, they should be included in the // interface as well. class ChannelInterface { @@ -32,10 +48,14 @@ class ChannelInterface { virtual MediaChannel* media_channel() const = 0; + // Returns a string view for the transport name. Fetching the transport name + // must be done on the network thread only and note that the lifetime of + // the returned object should be assumed to only be the calling scope. // TODO(deadbeef): This is redundant; remove this. - virtual const std::string& transport_name() const = 0; + virtual absl::string_view transport_name() const = 0; - virtual const std::string& content_name() const = 0; + // TODO(tommi): Change return type to string_view. + virtual const std::string& mid() const = 0; // Enables or disables this channel virtual void Enable(bool enable) = 0; @@ -47,10 +67,10 @@ class ChannelInterface { // Channel control virtual bool SetLocalContent(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) = 0; + std::string& error_desc) = 0; virtual bool SetRemoteContent(const MediaContentDescription* content, webrtc::SdpType type, - std::string* error_desc) = 0; + std::string& error_desc) = 0; virtual bool SetPayloadTypeDemuxingEnabled(bool enabled) = 0; // Access to the local and remote streams that were set on the channel. @@ -68,6 +88,32 @@ class ChannelInterface { virtual ~ChannelInterface() = default; }; +class ChannelFactoryInterface { + public: + virtual VideoChannel* CreateVideoChannel( + webrtc::Call* call, + const MediaConfig& media_config, + const std::string& mid, + bool srtp_required, + const webrtc::CryptoOptions& crypto_options, + const VideoOptions& options, + webrtc::VideoBitrateAllocatorFactory* + video_bitrate_allocator_factory) = 0; + + virtual VoiceChannel* CreateVoiceChannel( + webrtc::Call* call, + const MediaConfig& media_config, + const std::string& mid, + bool srtp_required, + const webrtc::CryptoOptions& crypto_options, + const AudioOptions& options) = 0; + + virtual void DestroyChannel(ChannelInterface* channel) = 0; + + protected: + virtual ~ChannelFactoryInterface() = default; +}; + } // namespace cricket #endif // PC_CHANNEL_INTERFACE_H_ diff --git a/pc/channel_manager.cc b/pc/channel_manager.cc index b58830b215..1482d7f862 100644 --- a/pc/channel_manager.cc +++ b/pc/channel_manager.cc @@ -10,17 +10,16 @@ #include "pc/channel_manager.h" -#include #include #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "api/media_types.h" #include "api/sequence_checker.h" #include "media/base/media_constants.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" -#include "rtc_base/logging.h" #include "rtc_base/trace_event.h" namespace cricket { @@ -31,13 +30,9 @@ std::unique_ptr ChannelManager::Create( bool enable_rtx, rtc::Thread* worker_thread, rtc::Thread* network_thread) { - RTC_DCHECK_RUN_ON(worker_thread); RTC_DCHECK(network_thread); RTC_DCHECK(worker_thread); - if (media_engine) - media_engine->Init(); - return absl::WrapUnique(new ChannelManager( std::move(media_engine), enable_rtx, worker_thread, network_thread)); } @@ -48,16 +43,33 @@ ChannelManager::ChannelManager( rtc::Thread* worker_thread, rtc::Thread* network_thread) : media_engine_(std::move(media_engine)), + signaling_thread_(rtc::Thread::Current()), worker_thread_(worker_thread), network_thread_(network_thread), enable_rtx_(enable_rtx) { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(worker_thread_); RTC_DCHECK(network_thread_); - RTC_DCHECK_RUN_ON(worker_thread_); + + if (media_engine_) { + // TODO(tommi): Change VoiceEngine to do ctor time initialization so that + // this isn't necessary. + worker_thread_->Invoke(RTC_FROM_HERE, [&] { media_engine_->Init(); }); + } } ChannelManager::~ChannelManager() { - RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK(voice_channels_.empty()); + RTC_DCHECK(video_channels_.empty()); + // While `media_engine_` is const throughout the ChannelManager's lifetime, + // it requires destruction to happen on the worker thread. Instead of + // marking the pointer as non-const, we live with this const_cast<> in the + // destructor. + const_cast&>(media_engine_).reset(); + }); } void ChannelManager::GetSupportedAudioSendCodecs( @@ -141,12 +153,9 @@ ChannelManager::GetSupportedVideoRtpHeaderExtensions() const { VoiceChannel* ChannelManager::CreateVoiceChannel( webrtc::Call* call, const MediaConfig& media_config, - webrtc::RtpTransportInternal* rtp_transport, - rtc::Thread* signaling_thread, - const std::string& content_name, + const std::string& mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, - rtc::UniqueRandomIdGenerator* ssrc_generator, const AudioOptions& options) { RTC_DCHECK(call); RTC_DCHECK(media_engine_); @@ -155,9 +164,8 @@ VoiceChannel* ChannelManager::CreateVoiceChannel( // thread. if (!worker_thread_->IsCurrent()) { return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return CreateVoiceChannel(call, media_config, rtp_transport, - signaling_thread, content_name, srtp_required, - crypto_options, ssrc_generator, options); + return CreateVoiceChannel(call, media_config, mid, srtp_required, + crypto_options, options); }); } @@ -170,44 +178,28 @@ VoiceChannel* ChannelManager::CreateVoiceChannel( } auto voice_channel = std::make_unique( - worker_thread_, network_thread_, signaling_thread, - absl::WrapUnique(media_channel), content_name, srtp_required, - crypto_options, ssrc_generator); - - voice_channel->Init_w(rtp_transport); + worker_thread_, network_thread_, signaling_thread_, + absl::WrapUnique(media_channel), mid, srtp_required, crypto_options, + &ssrc_generator_); VoiceChannel* voice_channel_ptr = voice_channel.get(); voice_channels_.push_back(std::move(voice_channel)); return voice_channel_ptr; } -void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) { +void ChannelManager::DestroyVoiceChannel(VoiceChannel* channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel"); - RTC_DCHECK(voice_channel); - - if (!worker_thread_->IsCurrent()) { - worker_thread_->Invoke(RTC_FROM_HERE, - [&] { DestroyVoiceChannel(voice_channel); }); - return; - } - RTC_DCHECK_RUN_ON(worker_thread_); - voice_channels_.erase(absl::c_find_if( - voice_channels_, [&](const std::unique_ptr& p) { - return p.get() == voice_channel; - })); + voice_channels_, [&](const auto& p) { return p.get() == channel; })); } VideoChannel* ChannelManager::CreateVideoChannel( webrtc::Call* call, const MediaConfig& media_config, - webrtc::RtpTransportInternal* rtp_transport, - rtc::Thread* signaling_thread, - const std::string& content_name, + const std::string& mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, - rtc::UniqueRandomIdGenerator* ssrc_generator, const VideoOptions& options, webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) { RTC_DCHECK(call); @@ -217,9 +209,8 @@ VideoChannel* ChannelManager::CreateVideoChannel( // thread. if (!worker_thread_->IsCurrent()) { return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return CreateVideoChannel(call, media_config, rtp_transport, - signaling_thread, content_name, srtp_required, - crypto_options, ssrc_generator, options, + return CreateVideoChannel(call, media_config, mid, srtp_required, + crypto_options, options, video_bitrate_allocator_factory); }); } @@ -234,32 +225,41 @@ VideoChannel* ChannelManager::CreateVideoChannel( } auto video_channel = std::make_unique( - worker_thread_, network_thread_, signaling_thread, - absl::WrapUnique(media_channel), content_name, srtp_required, - crypto_options, ssrc_generator); - - video_channel->Init_w(rtp_transport); + worker_thread_, network_thread_, signaling_thread_, + absl::WrapUnique(media_channel), mid, srtp_required, crypto_options, + &ssrc_generator_); VideoChannel* video_channel_ptr = video_channel.get(); video_channels_.push_back(std::move(video_channel)); return video_channel_ptr; } -void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) { +void ChannelManager::DestroyVideoChannel(VideoChannel* channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel"); - RTC_DCHECK(video_channel); - - if (!worker_thread_->IsCurrent()) { - worker_thread_->Invoke(RTC_FROM_HERE, - [&] { DestroyVideoChannel(video_channel); }); - return; - } RTC_DCHECK_RUN_ON(worker_thread_); video_channels_.erase(absl::c_find_if( - video_channels_, [&](const std::unique_ptr& p) { - return p.get() == video_channel; - })); + video_channels_, [&](const auto& p) { return p.get() == channel; })); +} + +void ChannelManager::DestroyChannel(ChannelInterface* channel) { + RTC_DCHECK(channel); + + if (!worker_thread_->IsCurrent()) { + // TODO(tommi): Do this asynchronously when we have a way to make sure that + // the call to DestroyChannel runs before ~Call() runs, which today happens + // inside an Invoke from the signaling thread in PeerConnectin::Close(). + worker_thread_->Invoke(RTC_FROM_HERE, + [&] { DestroyChannel(channel); }); + return; + } + + if (channel->media_type() == MEDIA_TYPE_AUDIO) { + DestroyVoiceChannel(static_cast(channel)); + } else { + RTC_DCHECK_EQ(channel->media_type(), MEDIA_TYPE_VIDEO); + DestroyVideoChannel(static_cast(channel)); + } } bool ChannelManager::StartAecDump(webrtc::FileWrapper file, diff --git a/pc/channel_manager.h b/pc/channel_manager.h index 43fa27935f..0e95c96c18 100644 --- a/pc/channel_manager.h +++ b/pc/channel_manager.h @@ -27,10 +27,11 @@ #include "media/base/media_config.h" #include "media/base/media_engine.h" #include "pc/channel.h" -#include "pc/rtp_transport_internal.h" +#include "pc/channel_interface.h" #include "pc/session_description.h" #include "rtc_base/system/file_wrapper.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/unique_id_generator.h" namespace cricket { @@ -43,7 +44,7 @@ namespace cricket { // voice or just video channels. // ChannelManager also allows the application to discover what devices it has // using device manager. -class ChannelManager final { +class ChannelManager : public ChannelFactoryInterface { public: // Returns an initialized instance of ChannelManager. // If media_engine is non-nullptr, then the returned ChannelManager instance @@ -55,11 +56,12 @@ class ChannelManager final { rtc::Thread* network_thread); ChannelManager() = delete; - ~ChannelManager(); + ~ChannelManager() override; rtc::Thread* worker_thread() const { return worker_thread_; } rtc::Thread* network_thread() const { return network_thread_; } MediaEngineInterface* media_engine() { return media_engine_.get(); } + rtc::UniqueRandomIdGenerator& ssrc_generator() { return ssrc_generator_; } // Retrieves the list of supported audio & video codec types. // Can be called before starting the media engine. @@ -81,15 +83,10 @@ class ChannelManager final { // Creates a voice channel, to be associated with the specified session. VoiceChannel* CreateVoiceChannel(webrtc::Call* call, const MediaConfig& media_config, - webrtc::RtpTransportInternal* rtp_transport, - rtc::Thread* signaling_thread, - const std::string& content_name, + const std::string& mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, - rtc::UniqueRandomIdGenerator* ssrc_generator, - const AudioOptions& options); - // Destroys a voice channel created by CreateVoiceChannel. - void DestroyVoiceChannel(VoiceChannel* voice_channel); + const AudioOptions& options) override; // Creates a video channel, synced with the specified voice channel, and // associated with the specified session. @@ -97,16 +94,14 @@ class ChannelManager final { VideoChannel* CreateVideoChannel( webrtc::Call* call, const MediaConfig& media_config, - webrtc::RtpTransportInternal* rtp_transport, - rtc::Thread* signaling_thread, - const std::string& content_name, + const std::string& mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, - rtc::UniqueRandomIdGenerator* ssrc_generator, const VideoOptions& options, - webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory); - // Destroys a video channel created by CreateVideoChannel. - void DestroyVideoChannel(VideoChannel* video_channel); + webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) + override; + + void DestroyChannel(ChannelInterface* channel) override; // Starts AEC dump using existing file, with a specified maximum file size in // bytes. When the limit is reached, logging will stop and the file will be @@ -116,16 +111,30 @@ class ChannelManager final { // Stops recording AEC dump. void StopAecDump(); - private: + protected: ChannelManager(std::unique_ptr media_engine, bool enable_rtx, rtc::Thread* worker_thread, rtc::Thread* network_thread); + // Destroys a voice channel created by CreateVoiceChannel. + void DestroyVoiceChannel(VoiceChannel* voice_channel); + + // Destroys a video channel created by CreateVideoChannel. + void DestroyVideoChannel(VideoChannel* video_channel); + + private: const std::unique_ptr media_engine_; // Nullable. + rtc::Thread* const signaling_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const network_thread_; + // This object should be used to generate any SSRC that is not explicitly + // specified by the user (or by the remote party). + // TODO(bugs.webrtc.org/12666): This variable is used from both the signaling + // and worker threads. See if we can't restrict usage to a single thread. + rtc::UniqueRandomIdGenerator ssrc_generator_; + // Vector contents are non-null. std::vector> voice_channels_ RTC_GUARDED_BY(worker_thread_); diff --git a/pc/channel_manager_unittest.cc b/pc/channel_manager_unittest.cc index 88de1f6a48..7b0146c976 100644 --- a/pc/channel_manager_unittest.cc +++ b/pc/channel_manager_unittest.cc @@ -10,21 +10,21 @@ #include "pc/channel_manager.h" -#include - -#include "api/rtc_error.h" +#include "api/sequence_checker.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "media/base/fake_media_engine.h" #include "media/base/test_utils.h" #include "media/engine/fake_webrtc_call.h" -#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/p2p_constants.h" -#include "p2p/base/packet_transport_internal.h" #include "pc/dtls_srtp_transport.h" +#include "pc/rtp_transport_internal.h" +#include "rtc_base/arraysize.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace cricket { namespace { @@ -69,18 +69,18 @@ class ChannelManagerTest : public ::testing::Test { void TestCreateDestroyChannels(webrtc::RtpTransportInternal* rtp_transport) { RTC_DCHECK_RUN_ON(worker_); cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( - &fake_call_, cricket::MediaConfig(), rtp_transport, - rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired, - webrtc::CryptoOptions(), &ssrc_generator_, AudioOptions()); - EXPECT_TRUE(voice_channel != nullptr); + &fake_call_, cricket::MediaConfig(), cricket::CN_AUDIO, + kDefaultSrtpRequired, webrtc::CryptoOptions(), AudioOptions()); + ASSERT_TRUE(voice_channel != nullptr); + cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( - &fake_call_, cricket::MediaConfig(), rtp_transport, - rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired, - webrtc::CryptoOptions(), &ssrc_generator_, VideoOptions(), + &fake_call_, cricket::MediaConfig(), cricket::CN_VIDEO, + kDefaultSrtpRequired, webrtc::CryptoOptions(), VideoOptions(), video_bitrate_allocator_factory_.get()); - EXPECT_TRUE(video_channel != nullptr); - cm_->DestroyVideoChannel(video_channel); - cm_->DestroyVoiceChannel(voice_channel); + ASSERT_TRUE(video_channel != nullptr); + + cm_->DestroyChannel(video_channel); + cm_->DestroyChannel(voice_channel); } std::unique_ptr network_; @@ -89,7 +89,7 @@ class ChannelManagerTest : public ::testing::Test { video_bitrate_allocator_factory_; std::unique_ptr cm_; cricket::FakeCall fake_call_; - rtc::UniqueRandomIdGenerator ssrc_generator_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; TEST_F(ChannelManagerTest, SetVideoRtxEnabled) { @@ -99,25 +99,25 @@ TEST_F(ChannelManagerTest, SetVideoRtxEnabled) { // By default RTX is disabled. cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); // Enable and check. cm_ = cricket::ChannelManager::Create(CreateFakeMediaEngine(), true, worker_, network_.get()); cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_TRUE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_TRUE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_TRUE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_TRUE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); // Disable and check. cm_ = cricket::ChannelManager::Create(CreateFakeMediaEngine(), false, worker_, network_.get()); cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); } TEST_F(ChannelManagerTest, CreateDestroyChannels) { @@ -125,7 +125,8 @@ TEST_F(ChannelManagerTest, CreateDestroyChannels) { "fake_dtls_transport", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_.get()); auto dtls_srtp_transport = std::make_unique( - /*rtcp_mux_required=*/true); + /*rtcp_mux_required=*/true, field_trials_); + network_->Invoke( RTC_FROM_HERE, [&rtp_dtls_transport, &dtls_srtp_transport] { dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport.get(), diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc index b38ab94549..725ee34918 100644 --- a/pc/channel_unittest.cc +++ b/pc/channel_unittest.cc @@ -10,9 +10,11 @@ #include "pc/channel.h" +#include + #include -#include -#include +#include +#include #include "api/array_view.h" #include "api/audio_options.h" @@ -21,11 +23,15 @@ #include "media/base/fake_media_engine.h" #include "media/base/fake_rtp.h" #include "media/base/media_channel.h" +#include "media/base/media_constants.h" +#include "media/base/rid_description.h" #include "p2p/base/candidate_pair_interface.h" +#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_packet_transport.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" #include "pc/dtls_srtp_transport.h" #include "pc/jsep_transport.h" #include "pc/rtp_transport.h" @@ -33,12 +39,14 @@ #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::DtlsTransportInternal; using cricket::FakeVoiceMediaChannel; @@ -126,8 +134,10 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { ~ChannelTest() { if (network_thread_) { - network_thread_->Invoke( - RTC_FROM_HERE, [this]() { network_thread_safety_->SetNotAlive(); }); + network_thread_->Invoke(RTC_FROM_HERE, [this]() { + network_thread_safety_->SetNotAlive(); + DeinitChannels(); + }); } } @@ -156,30 +166,22 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { // channels. RTC_DCHECK_EQ(flags1 & RAW_PACKET_TRANSPORT, flags2 & RAW_PACKET_TRANSPORT); rtc::Thread* worker_thread = rtc::Thread::Current(); - rtc::PacketTransportInternal* rtp1 = nullptr; - rtc::PacketTransportInternal* rtcp1 = nullptr; - rtc::PacketTransportInternal* rtp2 = nullptr; - rtc::PacketTransportInternal* rtcp2 = nullptr; // Based on flags, create fake DTLS or raw packet transports. if (flags1 & RAW_PACKET_TRANSPORT) { fake_rtp_packet_transport1_.reset( new rtc::FakePacketTransport("channel1_rtp")); - rtp1 = fake_rtp_packet_transport1_.get(); if (!(flags1 & RTCP_MUX)) { fake_rtcp_packet_transport1_.reset( new rtc::FakePacketTransport("channel1_rtcp")); - rtcp1 = fake_rtcp_packet_transport1_.get(); } } else { // Confirmed to work with KT_RSA and KT_ECDSA. fake_rtp_dtls_transport1_.reset(new cricket::FakeDtlsTransport( "channel1", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); - rtp1 = fake_rtp_dtls_transport1_.get(); if (!(flags1 & RTCP_MUX)) { fake_rtcp_dtls_transport1_.reset(new cricket::FakeDtlsTransport( "channel1", cricket::ICE_CANDIDATE_COMPONENT_RTCP, network_thread_)); - rtcp1 = fake_rtcp_dtls_transport1_.get(); } if (flags1 & DTLS) { auto cert1 = rtc::RTCCertificate::Create( @@ -194,22 +196,18 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { if (flags2 & RAW_PACKET_TRANSPORT) { fake_rtp_packet_transport2_.reset( new rtc::FakePacketTransport("channel2_rtp")); - rtp2 = fake_rtp_packet_transport2_.get(); if (!(flags2 & RTCP_MUX)) { fake_rtcp_packet_transport2_.reset( new rtc::FakePacketTransport("channel2_rtcp")); - rtcp2 = fake_rtcp_packet_transport2_.get(); } } else { // Confirmed to work with KT_RSA and KT_ECDSA. fake_rtp_dtls_transport2_.reset(new cricket::FakeDtlsTransport( "channel2", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); - rtp2 = fake_rtp_dtls_transport2_.get(); if (!(flags2 & RTCP_MUX)) { fake_rtcp_dtls_transport2_.reset(new cricket::FakeDtlsTransport( "channel2", cricket::ICE_CANDIDATE_COMPONENT_RTCP, network_thread_)); - rtcp2 = fake_rtcp_dtls_transport2_.get(); } if (flags2 & DTLS) { auto cert2 = rtc::RTCCertificate::Create( @@ -283,6 +281,22 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { } } + // Unininitializes the channels on the network thread. + void DeinitChannels() { + if (!channel1_ && !channel2_) + return; + network_thread_->Invoke(RTC_FROM_HERE, [this]() { + if (channel1_) { + RTC_DCHECK_RUN_ON(channel1_->network_thread()); + channel1_->SetRtpTransport(nullptr); + } + if (channel2_) { + RTC_DCHECK_RUN_ON(channel2_->network_thread()); + channel2_->SetRtpTransport(nullptr); + } + }); + } + std::unique_ptr CreateUnencryptedTransport( rtc::PacketTransportInternal* rtp_packet_transport, rtc::PacketTransportInternal* rtcp_packet_transport) { @@ -304,7 +318,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, field_trials_); network_thread_->Invoke( RTC_FROM_HERE, @@ -343,17 +357,18 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { } bool SendInitiate() { + std::string err; bool result = channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, NULL); + SdpType::kOffer, err); if (result) { channel1_->Enable(true); FlushCurrentThread(); result = channel2_->SetRemoteContent(&remote_media_content1_, - SdpType::kOffer, NULL); + SdpType::kOffer, err); if (result) { ConnectFakeTransports(); result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, NULL); + SdpType::kAnswer, err); } } return result; @@ -362,39 +377,44 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { bool SendAccept() { channel2_->Enable(true); FlushCurrentThread(); + std::string err; return channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kAnswer, NULL); + SdpType::kAnswer, err); } bool SendOffer() { + std::string err; bool result = channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, NULL); + SdpType::kOffer, err); if (result) { channel1_->Enable(true); result = channel2_->SetRemoteContent(&remote_media_content1_, - SdpType::kOffer, NULL); + SdpType::kOffer, err); } return result; } bool SendProvisionalAnswer() { + std::string err; bool result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kPrAnswer, NULL); + SdpType::kPrAnswer, err); if (result) { channel2_->Enable(true); result = channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kPrAnswer, NULL); + SdpType::kPrAnswer, err); ConnectFakeTransports(); } return result; } bool SendFinalAnswer() { + std::string err; bool result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, NULL); - if (result) + SdpType::kAnswer, err); + if (result) { result = channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kAnswer, NULL); + SdpType::kAnswer, err); + } return result; } @@ -487,7 +507,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { explicit ScopedCallThread(FunctorT&& functor) : thread_(rtc::Thread::Create()) { thread_->Start(); - thread_->PostTask(RTC_FROM_HERE, std::forward(functor)); + thread_->PostTask(std::forward(functor)); } ~ScopedCallThread() { thread_->Stop(); } @@ -552,9 +572,10 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); typename T::Content content; CreateContent(0, kPcmuCodec, kH264Codec, &content); - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); EXPECT_EQ(0U, media_channel1()->codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); ASSERT_EQ(1U, media_channel1()->codecs().size()); EXPECT_TRUE( CodecMatches(content.codecs()[0], media_channel1()->codecs()[0])); @@ -571,9 +592,10 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo); content.set_extmap_allow_mixed_enum(offer_enum); - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); content.set_extmap_allow_mixed_enum(answer_enum); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); EXPECT_EQ(answer, media_channel1()->ExtmapAllowMixed()); } void TestSetContentsExtmapAllowMixedCallee(bool offer, bool answer) { @@ -585,9 +607,10 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo); content.set_extmap_allow_mixed_enum(offer_enum); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer, err)); content.set_extmap_allow_mixed_enum(answer_enum); - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer, err)); EXPECT_EQ(answer, media_channel1()->ExtmapAllowMixed()); } @@ -596,10 +619,11 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { void TestSetContentsNullOffer() { CreateChannels(0, 0); typename T::Content content; - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); CreateContent(0, kPcmuCodec, kH264Codec, &content); EXPECT_EQ(0U, media_channel1()->codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); ASSERT_EQ(1U, media_channel1()->codecs().size()); EXPECT_TRUE( CodecMatches(content.codecs()[0], media_channel1()->codecs()[0])); @@ -613,12 +637,13 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on mux. Should no longer be a separate RTCP channel. content.set_rtcp_mux(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, NULL)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); // Only initiator supports mux. Should still have a separate RTCP channel. - EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer, NULL)); + EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer, err)); content.set_rtcp_mux(false); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer, err)); } // Test that SetLocalContent and SetRemoteContent properly @@ -626,13 +651,11 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { // SdpType::kOffer / SdpType::kAnswer. void TestChangeStreamParamsInContent() { cricket::StreamParams stream1; - stream1.groupid = "group1"; stream1.id = "stream1"; stream1.ssrcs.push_back(kSsrc1); stream1.cname = "stream1_cname"; cricket::StreamParams stream2; - stream2.groupid = "group1"; stream2.id = "stream2"; stream2.ssrcs.push_back(kSsrc2); stream2.cname = "stream2_cname"; @@ -642,20 +665,21 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { typename T::Content content1; CreateContent(0, kPcmuCodec, kH264Codec, &content1); content1.AddStream(stream1); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); channel1_->Enable(true); EXPECT_EQ(1u, media_channel1()->send_streams().size()); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, NULL)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); EXPECT_EQ(1u, media_channel2()->recv_streams().size()); ConnectFakeTransports(); // Channel 2 do not send anything. typename T::Content content2; CreateContent(0, kPcmuCodec, kH264Codec, &content2); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); EXPECT_EQ(0u, media_channel1()->recv_streams().size()); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); channel2_->Enable(true); EXPECT_EQ(0u, media_channel2()->send_streams().size()); @@ -667,21 +691,21 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { typename T::Content content3; CreateContent(0, kPcmuCodec, kH264Codec, &content3); content3.AddStream(stream2); - EXPECT_TRUE(channel2_->SetLocalContent(&content3, SdpType::kOffer, NULL)); + EXPECT_TRUE(channel2_->SetLocalContent(&content3, SdpType::kOffer, err)); ASSERT_EQ(1u, media_channel2()->send_streams().size()); EXPECT_EQ(stream2, media_channel2()->send_streams()[0]); - EXPECT_TRUE(channel1_->SetRemoteContent(&content3, SdpType::kOffer, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content3, SdpType::kOffer, err)); ASSERT_EQ(1u, media_channel1()->recv_streams().size()); EXPECT_EQ(stream2, media_channel1()->recv_streams()[0]); // Channel 1 replies but stop sending stream1. typename T::Content content4; CreateContent(0, kPcmuCodec, kH264Codec, &content4); - EXPECT_TRUE(channel1_->SetLocalContent(&content4, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel1_->SetLocalContent(&content4, SdpType::kAnswer, err)); EXPECT_EQ(0u, media_channel1()->send_streams().size()); - EXPECT_TRUE(channel2_->SetRemoteContent(&content4, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content4, SdpType::kAnswer, err)); EXPECT_EQ(0u, media_channel2()->recv_streams().size()); SendCustomRtp2(kSsrc2, 0); @@ -706,20 +730,21 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { EXPECT_FALSE(media_channel1()->playout()); } EXPECT_FALSE(media_channel1()->sending()); + std::string err; EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, NULL)); + SdpType::kOffer, err)); if (verify_playout_) { EXPECT_TRUE(media_channel1()->playout()); } EXPECT_FALSE(media_channel1()->sending()); EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_, - SdpType::kOffer, NULL)); + SdpType::kOffer, err)); if (verify_playout_) { EXPECT_FALSE(media_channel2()->playout()); } EXPECT_FALSE(media_channel2()->sending()); EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, NULL)); + SdpType::kAnswer, err)); if (verify_playout_) { EXPECT_FALSE(media_channel2()->playout()); } @@ -740,7 +765,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { } EXPECT_TRUE(media_channel2()->sending()); EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_, - SdpType::kAnswer, NULL)); + SdpType::kAnswer, err)); if (verify_playout_) { EXPECT_TRUE(media_channel1()->playout()); } @@ -770,12 +795,12 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { } EXPECT_FALSE(media_channel2()->sending()); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, NULL)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, err)); EXPECT_TRUE( - channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, NULL)); - EXPECT_TRUE( - channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, NULL)); + channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, err)); ConnectFakeTransports(); if (verify_playout_) { @@ -789,10 +814,9 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { // Update `content2` to be RecvOnly. content2.set_direction(RtpTransceiverDirection::kRecvOnly); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, err)); EXPECT_TRUE( - channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, NULL)); - EXPECT_TRUE( - channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, NULL)); + channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, err)); if (verify_playout_) { EXPECT_TRUE(media_channel1()->playout()); @@ -805,8 +829,8 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { // Update `content2` to be SendRecv. content2.set_direction(RtpTransceiverDirection::kSendRecv); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, NULL)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, NULL)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); if (verify_playout_) { EXPECT_TRUE(media_channel1()->playout()); @@ -920,6 +944,9 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(SendAccept()); SendRtp1(); SendRtp2(); + + DeinitChannels(); + // Do not wait, destroy channels. channel1_.reset(nullptr); channel2_.reset(nullptr); @@ -1106,17 +1133,17 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { media_channel1()->set_fail_set_recv_codecs(true); EXPECT_FALSE( - channel1_->SetLocalContent(content.get(), SdpType::kOffer, &err)); + channel1_->SetLocalContent(content.get(), SdpType::kOffer, err)); EXPECT_FALSE( - channel1_->SetLocalContent(content.get(), SdpType::kAnswer, &err)); + channel1_->SetLocalContent(content.get(), SdpType::kAnswer, err)); media_channel1()->set_fail_set_send_codecs(true); EXPECT_FALSE( - channel1_->SetRemoteContent(content.get(), SdpType::kOffer, &err)); + channel1_->SetRemoteContent(content.get(), SdpType::kOffer, err)); media_channel1()->set_fail_set_send_codecs(true); EXPECT_FALSE( - channel1_->SetRemoteContent(content.get(), SdpType::kAnswer, &err)); + channel1_->SetRemoteContent(content.get(), SdpType::kAnswer, err)); } void TestSendTwoOffers() { @@ -1126,13 +1153,13 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetLocalContent(content1.get(), SdpType::kOffer, &err)); + channel1_->SetLocalContent(content1.get(), SdpType::kOffer, err)); EXPECT_TRUE(media_channel1()->HasSendStream(1)); std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetLocalContent(content2.get(), SdpType::kOffer, &err)); + channel1_->SetLocalContent(content2.get(), SdpType::kOffer, err)); EXPECT_FALSE(media_channel1()->HasSendStream(1)); EXPECT_TRUE(media_channel1()->HasSendStream(2)); } @@ -1144,13 +1171,13 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, &err)); + channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, err)); EXPECT_TRUE(media_channel1()->HasRecvStream(1)); std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetRemoteContent(content2.get(), SdpType::kOffer, &err)); + channel1_->SetRemoteContent(content2.get(), SdpType::kOffer, err)); EXPECT_FALSE(media_channel1()->HasRecvStream(1)); EXPECT_TRUE(media_channel1()->HasRecvStream(2)); } @@ -1163,14 +1190,14 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, &err)); + channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, err)); EXPECT_TRUE(media_channel1()->HasRecvStream(1)); // Send PR answer std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetLocalContent(content2.get(), SdpType::kPrAnswer, &err)); + channel1_->SetLocalContent(content2.get(), SdpType::kPrAnswer, err)); EXPECT_TRUE(media_channel1()->HasRecvStream(1)); EXPECT_TRUE(media_channel1()->HasSendStream(2)); @@ -1178,7 +1205,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content3( CreateMediaContentWithStream(3)); EXPECT_TRUE( - channel1_->SetLocalContent(content3.get(), SdpType::kAnswer, &err)); + channel1_->SetLocalContent(content3.get(), SdpType::kAnswer, err)); EXPECT_TRUE(media_channel1()->HasRecvStream(1)); EXPECT_FALSE(media_channel1()->HasSendStream(2)); EXPECT_TRUE(media_channel1()->HasSendStream(3)); @@ -1192,14 +1219,14 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetLocalContent(content1.get(), SdpType::kOffer, &err)); + channel1_->SetLocalContent(content1.get(), SdpType::kOffer, err)); EXPECT_TRUE(media_channel1()->HasSendStream(1)); // Receive PR answer std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetRemoteContent(content2.get(), SdpType::kPrAnswer, &err)); + channel1_->SetRemoteContent(content2.get(), SdpType::kPrAnswer, err)); EXPECT_TRUE(media_channel1()->HasSendStream(1)); EXPECT_TRUE(media_channel1()->HasRecvStream(2)); @@ -1207,7 +1234,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr content3( CreateMediaContentWithStream(3)); EXPECT_TRUE( - channel1_->SetRemoteContent(content3.get(), SdpType::kAnswer, &err)); + channel1_->SetRemoteContent(content3.get(), SdpType::kAnswer, err)); EXPECT_TRUE(media_channel1()->HasSendStream(1)); EXPECT_FALSE(media_channel1()->HasRecvStream(2)); EXPECT_TRUE(media_channel1()->HasRecvStream(3)); @@ -1218,12 +1245,12 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { EXPECT_FALSE(media_channel1()->ready_to_send()); network_thread_->PostTask( - RTC_FROM_HERE, [this] { channel1_->OnTransportReadyToSend(true); }); + [this] { channel1_->OnTransportReadyToSend(true); }); WaitForThreads(); EXPECT_TRUE(media_channel1()->ready_to_send()); network_thread_->PostTask( - RTC_FROM_HERE, [this] { channel1_->OnTransportReadyToSend(false); }); + [this] { channel1_->OnTransportReadyToSend(false); }); WaitForThreads(); EXPECT_FALSE(media_channel1()->ready_to_send()); } @@ -1251,8 +1278,9 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { void DefaultMaxBitrateIsUnlimited() { CreateChannels(0, 0); + std::string err; EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, NULL)); + SdpType::kOffer, err)); EXPECT_EQ(media_channel1()->max_bps(), -1); VerifyMaxBitrate(media_channel1()->GetRtpSendParameters(kSsrc1), absl::nullopt); @@ -1326,15 +1354,14 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); typename T::Content content1, content2, content3; CreateSimulcastContent({"f", "h", "q"}, &content1); - EXPECT_TRUE( - channel1_->SetLocalContent(&content1, SdpType::kOffer, nullptr)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); VerifySimulcastStreamParams(content1.streams()[0], channel1_.get()); StreamParams stream1 = channel1_->local_streams()[0]; // Create a similar offer. SetLocalContent should not remove and add. CreateSimulcastContent({"f", "h", "q"}, &content2); - EXPECT_TRUE( - channel1_->SetLocalContent(&content2, SdpType::kOffer, nullptr)); + EXPECT_TRUE(channel1_->SetLocalContent(&content2, SdpType::kOffer, err)); VerifySimulcastStreamParams(content2.streams()[0], channel1_.get()); StreamParams stream2 = channel1_->local_streams()[0]; // Check that the streams are identical (SSRCs didn't change). @@ -1342,8 +1369,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { // Create third offer that has same RIDs in different order. CreateSimulcastContent({"f", "q", "h"}, &content3); - EXPECT_TRUE( - channel1_->SetLocalContent(&content3, SdpType::kOffer, nullptr)); + EXPECT_TRUE(channel1_->SetLocalContent(&content3, SdpType::kOffer, err)); VerifySimulcastStreamParams(content3.streams()[0], channel1_.get()); } @@ -1416,6 +1442,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { rtc::Buffer rtcp_packet_; cricket::CandidatePairInterface* last_selected_candidate_pair_; rtc::UniqueRandomIdGenerator ssrc_generator_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; template <> @@ -1430,7 +1457,10 @@ std::unique_ptr ChannelTest::CreateChannel( worker_thread, network_thread, signaling_thread, std::move(ch), cricket::CN_AUDIO, (flags & DTLS) != 0, webrtc::CryptoOptions(), &ssrc_generator_); - channel->Init_w(rtp_transport); + network_thread->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(channel->network_thread()); + channel->SetRtpTransport(rtp_transport); + }); return channel; } @@ -1513,7 +1543,10 @@ std::unique_ptr ChannelTest::CreateChannel( worker_thread, network_thread, signaling_thread, std::move(ch), cricket::CN_VIDEO, (flags & DTLS) != 0, webrtc::CryptoOptions(), &ssrc_generator_); - channel->Init_w(rtp_transport); + network_thread->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(channel->network_thread()); + channel->SetRtpTransport(rtp_transport); + }); return channel; } @@ -1983,12 +2016,15 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalOfferWithPacketization) { CreateChannels(0, 0); - EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err)); EXPECT_THAT(media_channel1()->send_codecs(), testing::IsEmpty()); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->recv_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->recv_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[1].packetization, cricket::kPacketizationParamRaw); } @@ -2002,12 +2038,16 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteOfferWithPacketization) { CreateChannels(0, 0); - EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kOffer, err)); + EXPECT_TRUE(err.empty()); EXPECT_THAT(media_channel1()->recv_codecs(), testing::IsEmpty()); ASSERT_THAT(media_channel1()->send_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->send_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->send_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[1].packetization, cricket::kPacketizationParamRaw); } @@ -2021,18 +2061,25 @@ TEST_F(VideoChannelSingleThreadTest, TestSetAnswerWithPacketization) { CreateChannels(0, 0); - EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, NULL)); - EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err)); + EXPECT_TRUE(err.empty()); + EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer, err)); + EXPECT_TRUE(err.empty()); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->recv_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->recv_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[1].packetization, cricket::kPacketizationParamRaw); EXPECT_THAT(media_channel1()->send_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->send_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->send_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[1].packetization, cricket::kPacketizationParamRaw); } @@ -2048,9 +2095,9 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalAnswerWithoutPacketization) { CreateChannels(0, 0); - EXPECT_TRUE( - channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, NULL)); - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer, err)); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); ASSERT_THAT(media_channel1()->send_codecs(), testing::SizeIs(1)); @@ -2068,9 +2115,10 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteAnswerWithoutPacketization) { CreateChannels(0, 0); - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, err)); EXPECT_TRUE( - channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, NULL)); + channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, err)); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); ASSERT_THAT(media_channel1()->send_codecs(), testing::SizeIs(1)); @@ -2090,9 +2138,12 @@ TEST_F(VideoChannelSingleThreadTest, CreateChannels(0, 0); - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, err)); + EXPECT_TRUE(err.empty()); EXPECT_FALSE( - channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, NULL)); + channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, err)); + EXPECT_FALSE(err.empty()); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, cricket::kPacketizationParamRaw); @@ -2111,10 +2162,11 @@ TEST_F(VideoChannelSingleThreadTest, CreateChannels(0, 0); - EXPECT_TRUE( - channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, NULL)); - EXPECT_FALSE( - channel1_->SetLocalContent(&local_video, SdpType::kAnswer, NULL)); + std::string err; + EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, err)); + EXPECT_TRUE(err.empty()); + EXPECT_FALSE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer, err)); + EXPECT_FALSE(err.empty()); EXPECT_THAT(media_channel1()->recv_codecs(), testing::IsEmpty()); ASSERT_THAT(media_channel1()->send_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_channel1()->send_codecs()[0].packetization, absl::nullopt); diff --git a/pc/connection_context.cc b/pc/connection_context.cc index 6fdcac3113..8583e7ba04 100644 --- a/pc/connection_context.cc +++ b/pc/connection_context.cc @@ -10,13 +10,15 @@ #include "pc/connection_context.h" -#include #include #include #include "api/transport/field_trial_based_config.h" +#include "media/base/media_engine.h" #include "media/sctp/sctp_transport_factory.h" #include "rtc_base/helpers.h" +#include "rtc_base/internal/default_socket_server.h" +#include "rtc_base/socket_server.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/time_utils.h" @@ -24,19 +26,31 @@ namespace webrtc { namespace { -rtc::Thread* MaybeStartThread(rtc::Thread* old_thread, - const std::string& thread_name, - bool with_socket_server, - std::unique_ptr& thread_holder) { +rtc::Thread* MaybeStartNetworkThread( + rtc::Thread* old_thread, + std::unique_ptr& socket_factory_holder, + std::unique_ptr& thread_holder) { if (old_thread) { return old_thread; } - if (with_socket_server) { - thread_holder = rtc::Thread::CreateWithSocketServer(); - } else { - thread_holder = rtc::Thread::Create(); + std::unique_ptr socket_server = + rtc::CreateDefaultSocketServer(); + thread_holder = std::make_unique(socket_server.get()); + socket_factory_holder = std::move(socket_server); + + thread_holder->SetName("pc_network_thread", nullptr); + thread_holder->Start(); + return thread_holder.get(); +} + +rtc::Thread* MaybeStartWorkerThread( + rtc::Thread* old_thread, + std::unique_ptr& thread_holder) { + if (old_thread) { + return old_thread; } - thread_holder->SetName(thread_name, nullptr); + thread_holder = rtc::Thread::Create(); + thread_holder->SetName("pc_worker_thread", nullptr); thread_holder->Start(); return thread_holder.get(); } @@ -59,12 +73,14 @@ rtc::Thread* MaybeWrapThread(rtc::Thread* signaling_thread, std::unique_ptr MaybeCreateSctpFactory( std::unique_ptr factory, - rtc::Thread* network_thread) { + rtc::Thread* network_thread, + const WebRtcKeyValueConfig& field_trials) { if (factory) { return factory; } #ifdef WEBRTC_HAVE_SCTP - return std::make_unique(network_thread); + return std::make_unique(network_thread, + field_trials); #else return nullptr; #endif @@ -75,30 +91,28 @@ std::unique_ptr MaybeCreateSctpFactory( // Static rtc::scoped_refptr ConnectionContext::Create( PeerConnectionFactoryDependencies* dependencies) { - return new ConnectionContext(dependencies); + return rtc::scoped_refptr( + new ConnectionContext(dependencies)); } ConnectionContext::ConnectionContext( PeerConnectionFactoryDependencies* dependencies) - : network_thread_(MaybeStartThread(dependencies->network_thread, - "pc_network_thread", - true, - owned_network_thread_)), - worker_thread_(MaybeStartThread(dependencies->worker_thread, - "pc_worker_thread", - false, - owned_worker_thread_)), + : network_thread_(MaybeStartNetworkThread(dependencies->network_thread, + owned_socket_factory_, + owned_network_thread_)), + worker_thread_(MaybeStartWorkerThread(dependencies->worker_thread, + owned_worker_thread_)), signaling_thread_(MaybeWrapThread(dependencies->signaling_thread, wraps_current_thread_)), + trials_(dependencies->trials ? std::move(dependencies->trials) + : std::make_unique()), network_monitor_factory_( std::move(dependencies->network_monitor_factory)), call_factory_(std::move(dependencies->call_factory)), sctp_factory_( MaybeCreateSctpFactory(std::move(dependencies->sctp_factory), - network_thread())), - trials_(dependencies->trials - ? std::move(dependencies->trials) - : std::make_unique()) { + network_thread(), + *trials_.get())) { signaling_thread_->AllowInvokesToThread(worker_thread_); signaling_thread_->AllowInvokesToThread(network_thread_); worker_thread_->AllowInvokesToThread(network_thread_); @@ -116,19 +130,30 @@ ConnectionContext::ConnectionContext( RTC_DCHECK_RUN_ON(signaling_thread_); rtc::InitRandom(rtc::Time32()); + rtc::SocketFactory* socket_factory = dependencies->socket_factory; + if (socket_factory == nullptr) { + if (owned_socket_factory_) { + socket_factory = owned_socket_factory_.get(); + } else { + // TODO(bugs.webrtc.org/13145): This case should be deleted. Either + // require that a PacketSocketFactory and NetworkManager always are + // injected (with no need to construct these default objects), or require + // that if a network_thread is injected, an approprite rtc::SocketServer + // should be injected too. + socket_factory = network_thread()->socketserver(); + } + } // If network_monitor_factory_ is non-null, it will be used to create a // network monitor while on the network thread. default_network_manager_ = std::make_unique( - network_monitor_factory_.get()); + network_monitor_factory_.get(), socket_factory); default_socket_factory_ = - std::make_unique(network_thread()); + std::make_unique(socket_factory); - worker_thread_->Invoke(RTC_FROM_HERE, [&]() { - channel_manager_ = cricket::ChannelManager::Create( - std::move(dependencies->media_engine), - /*enable_rtx=*/true, worker_thread(), network_thread()); - }); + channel_manager_ = cricket::ChannelManager::Create( + std::move(dependencies->media_engine), + /*enable_rtx=*/true, worker_thread(), network_thread()); // Set warning levels on the threads, to give warnings when response // may be slower than is expected of the thread. @@ -142,8 +167,7 @@ ConnectionContext::ConnectionContext( ConnectionContext::~ConnectionContext() { RTC_DCHECK_RUN_ON(signaling_thread_); - worker_thread_->Invoke(RTC_FROM_HERE, - [&]() { channel_manager_.reset(nullptr); }); + channel_manager_.reset(nullptr); // Make sure `worker_thread()` and `signaling_thread()` outlive // `default_socket_factory_` and `default_network_manager_`. diff --git a/pc/connection_context.h b/pc/connection_context.h index 8fad13c10c..05f838fac4 100644 --- a/pc/connection_context.h +++ b/pc/connection_context.h @@ -29,6 +29,7 @@ #include "rtc_base/network.h" #include "rtc_base/network_monitor_factory.h" #include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_factory.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -103,6 +104,7 @@ class ConnectionContext final // Note: Since owned_network_thread_ and owned_worker_thread_ are used // in the initialization of network_thread_ and worker_thread_, they // must be declared before them, so that they are initialized first. + std::unique_ptr owned_socket_factory_; std::unique_ptr owned_network_thread_ RTC_GUARDED_BY(signaling_thread_); std::unique_ptr owned_worker_thread_ @@ -110,6 +112,10 @@ class ConnectionContext final rtc::Thread* const network_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const signaling_thread_; + + // Accessed both on signaling thread and worker thread. + std::unique_ptr const trials_; + // channel_manager is accessed both on signaling thread and worker thread. std::unique_ptr channel_manager_; std::unique_ptr const network_monitor_factory_ @@ -122,8 +128,6 @@ class ConnectionContext final std::unique_ptr default_socket_factory_ RTC_GUARDED_BY(signaling_thread_); std::unique_ptr const sctp_factory_; - // Accessed both on signaling thread and worker thread. - std::unique_ptr const trials_; }; } // namespace webrtc diff --git a/pc/data_channel_controller.cc b/pc/data_channel_controller.cc index e11647f2ca..832eb03f79 100644 --- a/pc/data_channel_controller.cc +++ b/pc/data_channel_controller.cc @@ -10,14 +10,11 @@ #include "pc/data_channel_controller.h" -#include #include -#include "absl/algorithm/container.h" -#include "absl/types/optional.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" -#include "pc/peer_connection.h" +#include "pc/peer_connection_internal.h" #include "pc/sctp_utils.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" @@ -298,7 +295,8 @@ DataChannelController::InternalCreateSctpDataChannel( return nullptr; } sctp_data_channels_.push_back(channel); - channel->SignalClosed.connect(pc_, &PeerConnection::OnSctpDataChannelClosed); + channel->SignalClosed.connect( + pc_, &PeerConnectionInternal::OnSctpDataChannelClosed); SignalSctpDataChannelCreated_(channel.get()); return channel; } diff --git a/pc/data_channel_controller.h b/pc/data_channel_controller.h index af0e06353f..fa10b745c6 100644 --- a/pc/data_channel_controller.h +++ b/pc/data_channel_controller.h @@ -19,6 +19,7 @@ #include #include "api/data_channel_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/transport/data_channel_transport_interface.h" @@ -38,12 +39,12 @@ namespace webrtc { -class PeerConnection; +class PeerConnectionInternal; class DataChannelController : public SctpDataChannelProviderInterface, public DataChannelSink { public: - explicit DataChannelController(PeerConnection* pc) : pc_(pc) {} + explicit DataChannelController(PeerConnectionInternal* pc) : pc_(pc) {} // Not copyable or movable. DataChannelController(DataChannelController&) = delete; @@ -180,7 +181,7 @@ class DataChannelController : public SctpDataChannelProviderInterface, RTC_GUARDED_BY(signaling_thread()); // Owning PeerConnection. - PeerConnection* const pc_; + PeerConnectionInternal* const pc_; // The weak pointers must be dereferenced and invalidated on the signalling // thread only. rtc::WeakPtrFactory weak_factory_{this}; diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc index 47ea74a4b2..6d96251ac1 100644 --- a/pc/data_channel_integrationtest.cc +++ b/pc/data_channel_integrationtest.cc @@ -10,24 +10,36 @@ #include -#include -#include +#include +#include #include +#include #include +#include "absl/algorithm/container.h" #include "absl/types/optional.h" #include "api/data_channel_interface.h" -#include "api/dtmf_sender_interface.h" +#include "api/dtls_transport_interface.h" #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" +#include "api/stats/rtc_stats_report.h" +#include "api/stats/rtcstats_objects.h" #include "api/units/time_delta.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_session.h" +#include "pc/session_description.h" #include "pc/test/integration_test_helpers.h" #include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/fake_clock.h" #include "rtc_base/gunit.h" -#include "rtc_base/ref_counted_object.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/virtual_socket_server.h" -#include "system_wrappers/include/field_trial.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { @@ -37,6 +49,13 @@ namespace { // All tests in this file require SCTP support. #ifdef WEBRTC_HAVE_SCTP +#if defined(WEBRTC_ANDROID) +// Disable heavy tests running on low-end Android devices. +#define DISABLED_ON_ANDROID(t) DISABLED_##t +#else +#define DISABLED_ON_ANDROID(t) t +#endif + class DataChannelIntegrationTest : public PeerConnectionIntegrationBaseTest, public ::testing::WithParamInterface< std::tuple> { @@ -79,6 +98,13 @@ class DataChannelIntegrationTestUnifiedPlan : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} }; +void MakeActiveSctpOffer(cricket::SessionDescription* desc) { + auto& transport_infos = desc->transport_infos(); + for (auto& transport_info : transport_infos) { + transport_info.description.connection_role = cricket::CONNECTIONROLE_ACTIVE; + } +} + // This test causes a PeerConnection to enter Disconnected state, and // sends data on a DataChannel while disconnected. // The data should be surfaced when the connection reestablishes. @@ -570,6 +596,134 @@ TEST_P(DataChannelIntegrationTest, ClosingConnectionStopsPacketFlow) { EXPECT_EQ(sent_packets_a, sent_packets_b); } +TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetNormally) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->CreateDataChannel(); + ASSERT_FALSE(caller()->pc()->GetSctpTransport()); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + ASSERT_TRUE(caller()->pc()->GetSctpTransport()); + ASSERT_TRUE( + caller()->pc()->GetSctpTransport()->Information().dtls_transport()); + EXPECT_TRUE(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role()); + EXPECT_EQ(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kServer); + EXPECT_EQ(callee() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kClient); + // ID should be assigned according to the odd/even rule based on role; client + // gets even numbers, server gets odd ones. + // RFC 8832 section 6. + // TODO(hta): Test multiple channels. + EXPECT_EQ(caller()->data_channel()->id(), 1); +} + +TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetWhenReversed) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->CreateDataChannel(); + callee()->SetReceivedSdpMunger(MakeActiveSctpOffer); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + EXPECT_TRUE(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role()); + EXPECT_EQ(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kClient); + EXPECT_EQ(callee() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kServer); + // ID should be assigned according to the odd/even rule based on role; client + // gets even numbers, server gets odd ones. + // RFC 8832 section 6. + // TODO(hta): Test multiple channels. + EXPECT_EQ(caller()->data_channel()->id(), 0); +} + +TEST_P(DataChannelIntegrationTest, + DtlsRoleIsSetWhenReversedWithChannelCollision) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->CreateDataChannel(); + + callee()->SetReceivedSdpMunger([this](cricket::SessionDescription* desc) { + MakeActiveSctpOffer(desc); + callee()->CreateDataChannel(); + }); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + ASSERT_EQ_WAIT(callee()->data_channels().size(), 2U, kDefaultTimeout); + ASSERT_EQ_WAIT(caller()->data_channels().size(), 2U, kDefaultTimeout); + EXPECT_TRUE(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role()); + EXPECT_EQ(caller() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kClient); + EXPECT_EQ(callee() + ->pc() + ->GetSctpTransport() + ->Information() + .dtls_transport() + ->Information() + .role(), + DtlsTransportTlsRole::kServer); + // ID should be assigned according to the odd/even rule based on role; client + // gets even numbers, server gets odd ones. + // RFC 8832 section 6. + ASSERT_EQ(caller()->data_channels().size(), 2U); + ASSERT_EQ(callee()->data_channels().size(), 2U); + EXPECT_EQ(caller()->data_channels()[0]->id(), 0); + EXPECT_EQ(caller()->data_channels()[1]->id(), 1); + EXPECT_EQ(callee()->data_channels()[0]->id(), 1); + EXPECT_EQ(callee()->data_channels()[1]->id(), 0); +} + // Test that transport stats are generated by the RTCStatsCollector for a // connection that only involves data channels. This is a regression test for // crbug.com/826972. @@ -690,7 +844,7 @@ TEST_P(DataChannelIntegrationTest, EXPECT_GT(202u, callee()->data_observer()->received_message_count()); EXPECT_LE(2u, callee()->data_observer()->received_message_count()); // Then, check that observed behavior (lose some messages) has not changed - if (webrtc::field_trial::IsEnabled("WebRTC-DataChannel-Dcsctp")) { + if (!trials().IsDisabled("WebRTC-DataChannel-Dcsctp")) { // DcSctp loses all messages. This is correct. EXPECT_EQ(2u, callee()->data_observer()->received_message_count()); } else { @@ -704,7 +858,7 @@ TEST_P(DataChannelIntegrationTest, } TEST_P(DataChannelIntegrationTest, - SomeQueuedPacketsGetDroppedInMaxRetransmitsMode) { + DISABLED_ON_ANDROID(SomeQueuedPacketsGetDroppedInMaxRetransmitsMode)) { CreatePeerConnectionWrappers(); ConnectFakeSignaling(); DataChannelInit init; diff --git a/pc/data_channel_unittest.cc b/pc/data_channel_unittest.cc index 770892cbe1..5797d1da44 100644 --- a/pc/data_channel_unittest.cc +++ b/pc/data_channel_unittest.cc @@ -8,17 +8,27 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include +#include #include +#include "api/data_channel_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "api/transport/data_channel_transport_interface.h" +#include "media/base/media_channel.h" #include "media/sctp/sctp_transport_internal.h" #include "pc/sctp_data_channel.h" #include "pc/sctp_utils.h" #include "pc/test/fake_data_channel_provider.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" -#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" #include "test/gtest.h" using webrtc::DataChannelInterface; @@ -544,23 +554,29 @@ TEST_F(SctpDataChannelTest, OpenAckRoleInitialization) { EXPECT_EQ(webrtc::InternalDataChannelInit::kNone, init2.open_handshake_role); } -// Tests that the DataChannel is closed if the sending buffer is full. -TEST_F(SctpDataChannelTest, ClosedWhenSendBufferFull) { +// Tests that that Send() returns false if the sending buffer is full +// and the channel stays open. +TEST_F(SctpDataChannelTest, OpenWhenSendBufferFull) { SetChannelReady(); - rtc::CopyOnWriteBuffer buffer(1024); + const size_t packetSize = 1024; + + rtc::CopyOnWriteBuffer buffer(packetSize); memset(buffer.MutableData(), 0, buffer.size()); webrtc::DataBuffer packet(buffer, true); provider_->set_send_blocked(true); - for (size_t i = 0; i < 16 * 1024 + 1; ++i) { + for (size_t i = 0; + i < webrtc::DataChannelInterface::MaxSendQueueSize() / packetSize; ++i) { EXPECT_TRUE(webrtc_data_channel_->Send(packet)); } - EXPECT_TRUE( - webrtc::DataChannelInterface::kClosed == webrtc_data_channel_->state() || - webrtc::DataChannelInterface::kClosing == webrtc_data_channel_->state()); + // The sending buffer shoul be full, send returns false. + EXPECT_FALSE(webrtc_data_channel_->Send(packet)); + + EXPECT_TRUE(webrtc::DataChannelInterface::kOpen == + webrtc_data_channel_->state()); } // Tests that the DataChannel is closed on transport errors. diff --git a/pc/dtls_srtp_transport.cc b/pc/dtls_srtp_transport.cc index 1b9d1a05dd..9ec14f530b 100644 --- a/pc/dtls_srtp_transport.cc +++ b/pc/dtls_srtp_transport.cc @@ -27,8 +27,9 @@ static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp"; namespace webrtc { -DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled) - : SrtpTransport(rtcp_mux_enabled) {} +DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled, + const WebRtcKeyValueConfig& field_trials) + : SrtpTransport(rtcp_mux_enabled, field_trials) {} void DtlsSrtpTransport::SetDtlsTransports( cricket::DtlsTransportInternal* rtp_dtls_transport, @@ -235,7 +236,7 @@ bool DtlsSrtpTransport::ExtractParams( false, &dtls_buffer[0], dtls_buffer.size())) { RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed"; - RTC_NOTREACHED(); // This should never happen + RTC_DCHECK_NOTREACHED(); // This should never happen return false; } diff --git a/pc/dtls_srtp_transport.h b/pc/dtls_srtp_transport.h index da068c9b8a..2ee6e02e30 100644 --- a/pc/dtls_srtp_transport.h +++ b/pc/dtls_srtp_transport.h @@ -11,6 +11,7 @@ #ifndef PC_DTLS_SRTP_TRANSPORT_H_ #define PC_DTLS_SRTP_TRANSPORT_H_ +#include #include #include @@ -31,7 +32,8 @@ namespace webrtc { // configures the SrtpSessions in the base class. class DtlsSrtpTransport : public SrtpTransport { public: - explicit DtlsSrtpTransport(bool rtcp_mux_enabled); + DtlsSrtpTransport(bool rtcp_mux_enabled, + const WebRtcKeyValueConfig& field_trials); // Set P2P layer RTP/RTCP DtlsTransports. When using RTCP-muxing, // `rtcp_dtls_transport` is null. diff --git a/pc/dtls_srtp_transport_unittest.cc b/pc/dtls_srtp_transport_unittest.cc index b2ae14f76b..30f09e740c 100644 --- a/pc/dtls_srtp_transport_unittest.cc +++ b/pc/dtls_srtp_transport_unittest.cc @@ -14,7 +14,6 @@ #include #include -#include #include "call/rtp_demuxer.h" #include "media/base/fake_rtp.h" @@ -26,10 +25,13 @@ #include "pc/test/rtp_transport_test_util.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" +#include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::FakeDtlsTransport; using cricket::FakeIceTransport; @@ -58,7 +60,7 @@ class DtlsSrtpTransportTest : public ::testing::Test, FakeDtlsTransport* rtcp_dtls, bool rtcp_mux_enabled) { auto dtls_srtp_transport = - std::make_unique(rtcp_mux_enabled); + std::make_unique(rtcp_mux_enabled, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls); @@ -88,7 +90,7 @@ class DtlsSrtpTransportTest : public ::testing::Test, &transport_observer2_, &webrtc::TransportObserver::OnReadyToSend); webrtc::RtpDemuxerCriteria demuxer_criteria; // 0x00 is the payload type used in kPcmuFrame. - demuxer_criteria.payload_types = {0x00}; + demuxer_criteria.payload_types() = {0x00}; dtls_srtp_transport1_->RegisterRtpDemuxerSink(demuxer_criteria, &transport_observer1_); dtls_srtp_transport2_->RegisterRtpDemuxerSink(demuxer_criteria, @@ -255,6 +257,7 @@ class DtlsSrtpTransportTest : public ::testing::Test, webrtc::TransportObserver transport_observer2_; int sequence_number_ = 0; + webrtc::test::ScopedKeyValueConfig field_trials_; }; // Tests that if RTCP muxing is enabled and transports are set after RTP diff --git a/pc/dtls_transport.cc b/pc/dtls_transport.cc index 074f44e22b..c9f3279fbc 100644 --- a/pc/dtls_transport.cc +++ b/pc/dtls_transport.cc @@ -19,7 +19,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" -#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" namespace webrtc { @@ -105,22 +105,35 @@ void DtlsTransport::UpdateInformation() { if (internal_dtls_transport_->dtls_state() == DtlsTransportState::kConnected) { bool success = true; + rtc::SSLRole internal_role; + absl::optional role; int ssl_cipher_suite; int tls_version; int srtp_cipher; + success &= internal_dtls_transport_->GetDtlsRole(&internal_role); + if (success) { + switch (internal_role) { + case rtc::SSL_CLIENT: + role = DtlsTransportTlsRole::kClient; + break; + case rtc::SSL_SERVER: + role = DtlsTransportTlsRole::kServer; + break; + } + } success &= internal_dtls_transport_->GetSslVersionBytes(&tls_version); success &= internal_dtls_transport_->GetSslCipherSuite(&ssl_cipher_suite); success &= internal_dtls_transport_->GetSrtpCryptoSuite(&srtp_cipher); if (success) { info_ = DtlsTransportInformation( - internal_dtls_transport_->dtls_state(), tls_version, + internal_dtls_transport_->dtls_state(), role, tls_version, ssl_cipher_suite, srtp_cipher, internal_dtls_transport_->GetRemoteSSLCertChain()); } else { RTC_LOG(LS_ERROR) << "DtlsTransport in connected state has incomplete " "TLS information"; info_ = DtlsTransportInformation( - internal_dtls_transport_->dtls_state(), absl::nullopt, + internal_dtls_transport_->dtls_state(), role, absl::nullopt, absl::nullopt, absl::nullopt, internal_dtls_transport_->GetRemoteSSLCertChain()); } diff --git a/pc/dtls_transport_unittest.cc b/pc/dtls_transport_unittest.cc index f80d99b05e..a9ac73c9b8 100644 --- a/pc/dtls_transport_unittest.cc +++ b/pc/dtls_transport_unittest.cc @@ -13,9 +13,15 @@ #include #include -#include "absl/memory/memory.h" +#include "absl/types/optional.h" +#include "api/rtc_error.h" #include "p2p/base/fake_dtls_transport.h" +#include "p2p/base/p2p_constants.h" +#include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_identity.h" #include "test/gmock.h" #include "test/gtest.h" @@ -120,6 +126,19 @@ TEST_F(DtlsTransportTest, CloseWhenClearing) { kDefaultTimeout); } +TEST_F(DtlsTransportTest, RoleAppearsOnConnect) { + rtc::FakeSSLCertificate fake_certificate("fake data"); + CreateTransport(&fake_certificate); + transport()->RegisterObserver(observer()); + EXPECT_FALSE(transport()->Information().role()); + CompleteDtlsHandshake(); + ASSERT_TRUE_WAIT(observer_.state() == DtlsTransportState::kConnected, + kDefaultTimeout); + EXPECT_TRUE(observer_.info_.role()); + EXPECT_TRUE(transport()->Information().role()); + EXPECT_EQ(transport()->Information().role(), DtlsTransportTlsRole::kClient); +} + TEST_F(DtlsTransportTest, CertificateAppearsOnConnect) { rtc::FakeSSLCertificate fake_certificate("fake data"); CreateTransport(&fake_certificate); diff --git a/pc/dtmf_sender.cc b/pc/dtmf_sender.cc index 69ef2fb8df..8b82c31aa9 100644 --- a/pc/dtmf_sender.cc +++ b/pc/dtmf_sender.cc @@ -13,8 +13,6 @@ #include #include -#include - #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" @@ -167,7 +165,7 @@ int DtmfSender::comma_delay() const { void DtmfSender::QueueInsertDtmf(const rtc::Location& posted_from, uint32_t delay_ms) { - signaling_thread_->PostDelayedTask( + signaling_thread_->PostDelayedHighPrecisionTask( ToQueuedTask(safety_flag_, [this] { RTC_DCHECK_RUN_ON(signaling_thread_); @@ -194,7 +192,7 @@ void DtmfSender::DoInsertDtmf() { if (!GetDtmfCode(tone, &code)) { // The find_first_of(kDtmfValidTones) should have guarantee `tone` is // a valid DTMF tone. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } diff --git a/pc/dtmf_sender.h b/pc/dtmf_sender.h index a208b100d4..ae213b3bf4 100644 --- a/pc/dtmf_sender.h +++ b/pc/dtmf_sender.h @@ -17,13 +17,14 @@ #include "api/dtmf_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" #include "pc/proxy.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/location.h" #include "rtc_base/ref_count.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" // DtmfSender is the native implementation of the RTCDTMFSender defined by // the WebRTC W3C Editor's Draft. @@ -72,6 +73,9 @@ class DtmfSender : public DtmfSenderInterface, public sigslot::has_slots<> { DtmfSender(rtc::Thread* signaling_thread, DtmfProviderInterface* provider); virtual ~DtmfSender(); + DtmfSender(const DtmfSender&) = delete; + DtmfSender& operator=(const DtmfSender&) = delete; + private: DtmfSender(); @@ -96,12 +100,11 @@ class DtmfSender : public DtmfSenderInterface, public sigslot::has_slots<> { // For cancelling the tasks which feed the DTMF provider one tone at a time. rtc::scoped_refptr safety_flag_ RTC_GUARDED_BY( signaling_thread_) RTC_PT_GUARDED_BY(signaling_thread_) = nullptr; - - RTC_DISALLOW_COPY_AND_ASSIGN(DtmfSender); }; // Define proxy for DtmfSenderInterface. BEGIN_PRIMARY_PROXY_MAP(DtmfSender) + PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_METHOD1(void, RegisterObserver, DtmfSenderObserverInterface*) PROXY_METHOD0(void, UnregisterObserver) diff --git a/pc/external_hmac.h b/pc/external_hmac.h index 3319beaed4..c5071fc192 100644 --- a/pc/external_hmac.h +++ b/pc/external_hmac.h @@ -30,9 +30,9 @@ #include -#include "third_party/libsrtp/crypto/include/auth.h" #include "third_party/libsrtp/crypto/include/crypto_types.h" #include "third_party/libsrtp/include/srtp.h" +#include "third_party/libsrtp/include/srtp_priv.h" #define EXTERNAL_HMAC_SHA1 SRTP_HMAC_SHA1 + 1 #define HMAC_KEY_LENGTH 20 diff --git a/pc/ice_server_parsing.cc b/pc/ice_server_parsing.cc index d9b4c39943..cb4145be1a 100644 --- a/pc/ice_server_parsing.cc +++ b/pc/ice_server_parsing.cc @@ -12,9 +12,7 @@ #include -#include #include // For std::isdigit. -#include #include #include "p2p/base/port_interface.h" @@ -211,19 +209,19 @@ static RTCErrorType ParseIceServerUrl( } if (hoststring.find('@') != std::string::npos) { - RTC_LOG(WARNING) << "Invalid url: " << uri_without_transport; - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "Invalid url: " << uri_without_transport; + RTC_LOG(LS_WARNING) << "Note that user-info@ in turn:-urls is long-deprecated."; return RTCErrorType::SYNTAX_ERROR; } std::string address; if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { - RTC_LOG(WARNING) << "Invalid hostname format: " << uri_without_transport; + RTC_LOG(LS_WARNING) << "Invalid hostname format: " << uri_without_transport; return RTCErrorType::SYNTAX_ERROR; } if (port <= 0 || port > 0xffff) { - RTC_LOG(WARNING) << "Invalid port: " << port; + RTC_LOG(LS_WARNING) << "Invalid port: " << port; return RTCErrorType::SYNTAX_ERROR; } @@ -237,7 +235,7 @@ static RTCErrorType ParseIceServerUrl( if (server.username.empty() || server.password.empty()) { // The WebRTC spec requires throwing an InvalidAccessError when username // or credential are ommitted; this is the native equivalent. - RTC_LOG(LS_ERROR) << "TURN server with empty username or password"; + RTC_LOG(LS_WARNING) << "TURN server with empty username or password"; return RTCErrorType::INVALID_PARAMETER; } // If the hostname field is not empty, then the server address must be @@ -251,7 +249,7 @@ static RTCErrorType ParseIceServerUrl( if (!IPFromString(address, &ip)) { // When hostname is set, the server address must be a // resolved ip address. - RTC_LOG(LS_ERROR) + RTC_LOG(LS_WARNING) << "IceServer has hostname field set, but URI does not " "contain an IP address."; return RTCErrorType::INVALID_PARAMETER; @@ -275,7 +273,7 @@ static RTCErrorType ParseIceServerUrl( default: // We shouldn't get to this point with an invalid service_type, we should // have returned an error already. - RTC_NOTREACHED() << "Unexpected service type"; + RTC_DCHECK_NOTREACHED() << "Unexpected service type"; return RTCErrorType::INTERNAL_ERROR; } return RTCErrorType::NONE; @@ -289,7 +287,7 @@ RTCErrorType ParseIceServers( if (!server.urls.empty()) { for (const std::string& url : server.urls) { if (url.empty()) { - RTC_LOG(LS_ERROR) << "Empty uri."; + RTC_LOG(LS_WARNING) << "Empty uri."; return RTCErrorType::SYNTAX_ERROR; } RTCErrorType err = @@ -306,7 +304,7 @@ RTCErrorType ParseIceServers( return err; } } else { - RTC_LOG(LS_ERROR) << "Empty uri."; + RTC_LOG(LS_WARNING) << "Empty uri."; return RTCErrorType::SYNTAX_ERROR; } } diff --git a/pc/ice_transport.h b/pc/ice_transport.h index 11f3de5d27..e31ec546b2 100644 --- a/pc/ice_transport.h +++ b/pc/ice_transport.h @@ -12,7 +12,6 @@ #define PC_ICE_TRANSPORT_H_ #include "api/ice_transport_interface.h" -#include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" diff --git a/pc/ice_transport_unittest.cc b/pc/ice_transport_unittest.cc index ebb46cb5d5..95af2cd552 100644 --- a/pc/ice_transport_unittest.cc +++ b/pc/ice_transport_unittest.cc @@ -12,13 +12,12 @@ #include #include -#include #include "api/ice_transport_factory.h" +#include "api/scoped_refptr.h" #include "p2p/base/fake_ice_transport.h" #include "p2p/base/fake_port_allocator.h" -#include "rtc_base/gunit.h" -#include "test/gmock.h" +#include "rtc_base/ref_counted_object.h" #include "test/gtest.h" namespace webrtc { diff --git a/pc/jitter_buffer_delay.h b/pc/jitter_buffer_delay.h index dc10e3d2ba..a6bec01ce7 100644 --- a/pc/jitter_buffer_delay.h +++ b/pc/jitter_buffer_delay.h @@ -16,6 +16,7 @@ #include "absl/types/optional.h" #include "api/sequence_checker.h" #include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { diff --git a/pc/jitter_buffer_delay_unittest.cc b/pc/jitter_buffer_delay_unittest.cc index b00075ceb5..79c39fffb8 100644 --- a/pc/jitter_buffer_delay_unittest.cc +++ b/pc/jitter_buffer_delay_unittest.cc @@ -10,9 +10,6 @@ #include "pc/jitter_buffer_delay.h" -#include - -#include "absl/types/optional.h" #include "test/gtest.h" namespace webrtc { diff --git a/pc/jsep_ice_candidate.cc b/pc/jsep_ice_candidate.cc index 6dacde629c..1e97ad42d8 100644 --- a/pc/jsep_ice_candidate.cc +++ b/pc/jsep_ice_candidate.cc @@ -10,8 +10,6 @@ #include "api/jsep_ice_candidate.h" -#include - #include "pc/webrtc_sdp.h" // This file contains JsepIceCandidate-related functions that are not diff --git a/pc/jsep_session_description.cc b/pc/jsep_session_description.cc index 57ccf7ca6e..4c57396f08 100644 --- a/pc/jsep_session_description.cc +++ b/pc/jsep_session_description.cc @@ -11,11 +11,20 @@ #include "api/jsep_session_description.h" #include +#include +#include "absl/types/optional.h" +#include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" -#include "pc/media_session.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_session.h" // IWYU pragma: keep #include "pc/webrtc_sdp.h" -#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/socket_address.h" using cricket::SessionDescription; diff --git a/pc/jsep_session_description_unittest.cc b/pc/jsep_session_description_unittest.cc index 2202aa81d0..ee446cbbda 100644 --- a/pc/jsep_session_description_unittest.cc +++ b/pc/jsep_session_description_unittest.cc @@ -13,8 +13,6 @@ #include #include -#include -#include #include #include @@ -29,6 +27,7 @@ #include "pc/session_description.h" #include "pc/webrtc_sdp.h" #include "rtc_base/helpers.h" +#include "rtc_base/net_helper.h" #include "rtc_base/socket_address.h" #include "rtc_base/string_encode.h" #include "test/gtest.h" diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc index d490819257..00447b088d 100644 --- a/pc/jsep_transport.cc +++ b/pc/jsep_transport.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include "api/array_view.h" @@ -207,17 +208,15 @@ webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription( return error; } } - RTC_DCHECK(rtp_dtls_transport_->internal()); - rtp_dtls_transport_->internal()->ice_transport()->SetIceParameters( - ice_parameters); + RTC_DCHECK(rtp_dtls_transport_->internal()); + rtp_dtls_transport_->internal()->ice_transport()->SetIceParameters( + ice_parameters); - { - if (rtcp_dtls_transport_) { - RTC_DCHECK(rtcp_dtls_transport_->internal()); - rtcp_dtls_transport_->internal()->ice_transport()->SetIceParameters( - ice_parameters); - } - } + if (rtcp_dtls_transport_) { + RTC_DCHECK(rtcp_dtls_transport_->internal()); + rtcp_dtls_transport_->internal()->ice_transport()->SetIceParameters( + ice_parameters); + } // If PRANSWER/ANSWER is set, we should decide transport protocol type. if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { error = NegotiateAndSetDtlsParameters(type); @@ -399,7 +398,7 @@ webrtc::RTCError JsepTransport::VerifyCertificateFingerprint( void JsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) { RTC_DCHECK_RUN_ON(network_thread_); if (dtls_srtp_transport_) { - RTC_LOG(INFO) + RTC_LOG(LS_INFO) << "Setting active_reset_srtp_params of DtlsSrtpTransport to: " << active_reset_srtp_params; dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params); @@ -460,7 +459,7 @@ bool JsepTransport::SetRtcpMux(bool enable, } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (!ret) { @@ -670,7 +669,7 @@ webrtc::RTCError JsepTransport::NegotiateDtlsRole( } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } else { diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h index e3e929bfd2..93604a179f 100644 --- a/pc/jsep_transport.h +++ b/pc/jsep_transport.h @@ -44,7 +44,6 @@ #include "pc/srtp_transport.h" #include "pc/transport_stats.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_fingerprint.h" #include "rtc_base/ssl_stream_adapter.h" @@ -106,6 +105,9 @@ class JsepTransport { ~JsepTransport(); + JsepTransport(const JsepTransport&) = delete; + JsepTransport& operator=(const JsepTransport&) = delete; + // Returns the MID of this transport. This is only used for logging. const std::string& mid() const { return mid_; } @@ -326,8 +328,6 @@ class JsepTransport { // `rtcp_dtls_transport_` is destroyed. The JsepTransportController will // receive the callback and update the aggregate transport states. std::function rtcp_mux_active_callback_; - - RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransport); }; } // namespace cricket diff --git a/pc/jsep_transport_collection.h b/pc/jsep_transport_collection.h index aa5293475e..099e24a457 100644 --- a/pc/jsep_transport_collection.h +++ b/pc/jsep_transport_collection.h @@ -18,6 +18,7 @@ #include #include +#include "api/jsep.h" #include "api/peer_connection_interface.h" #include "api/sequence_checker.h" #include "pc/jsep_transport.h" diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index c98640cf0a..73bb9f6300 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -12,9 +12,9 @@ #include -#include #include #include +#include #include #include @@ -62,6 +62,7 @@ JsepTransportController::JsepTransportController( RTC_DCHECK(config_.rtcp_handler); RTC_DCHECK(config_.ice_transport_factory); RTC_DCHECK(config_.on_dtls_handshake_error_); + RTC_DCHECK(config_.field_trials); } JsepTransportController::~JsepTransportController() { @@ -400,7 +401,7 @@ void JsepTransportController::SetActiveResetSrtpParams( return; } RTC_DCHECK_RUN_ON(network_thread_); - RTC_LOG(INFO) + RTC_LOG(LS_INFO) << "Updating the active_reset_srtp_params for JsepTransportController: " << active_reset_srtp_params; active_reset_srtp_params_ = active_reset_srtp_params; @@ -433,6 +434,7 @@ JsepTransportController::CreateIceTransport(const std::string& transport_name, init.set_port_allocator(port_allocator_); init.set_async_dns_resolver_factory(async_dns_resolver_factory_); init.set_event_log(config_.event_log); + init.set_field_trials(config_.field_trials); return config_.ice_transport_factory->CreateIceTransport( transport_name, component, std::move(init)); } @@ -511,8 +513,8 @@ JsepTransportController::CreateSdesTransport( cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { RTC_DCHECK_RUN_ON(network_thread_); - auto srtp_transport = - std::make_unique(rtcp_dtls_transport == nullptr); + auto srtp_transport = std::make_unique( + rtcp_dtls_transport == nullptr, *config_.field_trials); RTC_DCHECK(rtp_dtls_transport); srtp_transport->SetRtpPacketTransport(rtp_dtls_transport); if (rtcp_dtls_transport) { @@ -531,7 +533,7 @@ JsepTransportController::CreateDtlsSrtpTransport( cricket::DtlsTransportInternal* rtcp_dtls_transport) { RTC_DCHECK_RUN_ON(network_thread_); auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, *config_.field_trials); if (config_.enable_external_auth) { dtls_srtp_transport->EnableExternalAuth(); } @@ -1201,7 +1203,7 @@ void JsepTransportController::OnTransportCandidateGathered_n( const cricket::Candidate& candidate) { // We should never signal peer-reflexive candidates. if (candidate.type() == cricket::PRFLX_PORT_TYPE) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return; } @@ -1345,7 +1347,7 @@ void JsepTransportController::UpdateAggregateStates_n() { // "connected", "completed" or "closed" state. new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (standardized_ice_connection_state_ != new_ice_connection_state) { @@ -1403,7 +1405,7 @@ void JsepTransportController::UpdateAggregateStates_n() { new_combined_state = PeerConnectionInterface::PeerConnectionState::kConnected; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } if (combined_connection_state_ != new_combined_state) { diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index bbd870ec2d..0913cd455d 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,6 @@ #include "pc/transport_stats.h" #include "rtc_base/callback_list.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/helpers.h" #include "rtc_base/ref_counted_object.h" @@ -136,7 +136,10 @@ class JsepTransportController : public sigslot::has_slots<> { // Factory for SCTP transports. SctpTransportFactoryInterface* sctp_factory = nullptr; - std::function on_dtls_handshake_error_; + std::function on_dtls_handshake_error_; + + // Field trials. + const webrtc::WebRtcKeyValueConfig* field_trials; }; // The ICE related events are fired on the `network_thread`. @@ -150,6 +153,9 @@ class JsepTransportController : public sigslot::has_slots<> { Config config); virtual ~JsepTransportController(); + JsepTransportController(const JsepTransportController&) = delete; + JsepTransportController& operator=(const JsepTransportController&) = delete; + // The main method to be called; applies a description at the transport // level, creating/destroying transport objects as needed and updating their // properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not @@ -498,8 +504,6 @@ class JsepTransportController : public sigslot::has_slots<> { rtc::scoped_refptr certificate_; BundleManager bundles_; - - RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransportController); }; } // namespace webrtc diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index 4c2ee2a2ae..ee306f093a 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -11,16 +11,29 @@ #include "pc/jsep_transport_controller.h" #include -#include +#include +#include #include "api/dtls_transport_interface.h" +#include "api/transport/enums.h" +#include "p2p/base/candidate_pair_interface.h" #include "p2p/base/dtls_transport_factory.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_ice_transport.h" +#include "p2p/base/p2p_constants.h" #include "p2p/base/transport_info.h" +#include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" +#include "rtc_base/location.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/ssl_identity.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::Candidate; using cricket::Candidates; @@ -82,10 +95,13 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, cricket::PortAllocator* port_allocator = nullptr) { config.transport_observer = this; config.rtcp_handler = [](const rtc::CopyOnWriteBuffer& packet, - int64_t packet_time_us) { RTC_NOTREACHED(); }; + int64_t packet_time_us) { + RTC_DCHECK_NOTREACHED(); + }; config.ice_transport_factory = fake_ice_transport_factory_.get(); config.dtls_transport_factory = fake_dtls_transport_factory_.get(); config.on_dtls_handshake_error_ = [](rtc::SSLHandshakeError s) {}; + config.field_trials = &field_trials_; transport_controller_ = std::make_unique( network_thread, port_allocator, nullptr /* async_resolver_factory */, config); @@ -364,6 +380,7 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, // Transport controller needs to be destroyed first, because it may issue // callbacks that modify the changed_*_by_mid in the destructor. std::unique_ptr transport_controller_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; TEST_F(JsepTransportControllerTest, GetRtpTransport) { diff --git a/pc/jsep_transport_unittest.cc b/pc/jsep_transport_unittest.cc index 41c9baba6f..c63dc496db 100644 --- a/pc/jsep_transport_unittest.cc +++ b/pc/jsep_transport_unittest.cc @@ -10,15 +10,34 @@ #include "pc/jsep_transport.h" -#include +#include +#include + +#include +#include #include #include -#include "api/ice_transport_factory.h" +#include "api/candidate.h" #include "media/base/fake_rtp.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_ice_transport.h" -#include "rtc_base/gunit.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/buffer.h" +#include "rtc_base/byte_order.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace cricket { namespace { @@ -71,7 +90,7 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { rtc::PacketTransportInternal* rtp_packet_transport, rtc::PacketTransportInternal* rtcp_packet_transport) { auto srtp_transport = std::make_unique( - rtcp_packet_transport == nullptr); + rtcp_packet_transport == nullptr, field_trials_); srtp_transport->SetRtpPacketTransport(rtp_packet_transport); if (rtcp_packet_transport) { @@ -84,7 +103,7 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport, rtcp_dtls_transport); return dtls_srtp_transport; @@ -124,7 +143,7 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { rtp_dtls_transport.get(), rtcp_dtls_transport.get()); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } auto jsep_transport = std::make_unique( @@ -174,6 +193,8 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { // The SrtpTransport is owned by `jsep_transport_`. Keep a raw pointer here // for testing. webrtc::SrtpTransport* sdes_transport_ = nullptr; + + webrtc::test::ScopedKeyValueConfig field_trials_; }; // The parameterized tests cover both cases when RTCP mux is enable and diff --git a/pc/media_protocol_names.cc b/pc/media_protocol_names.cc index ae4fcf3391..667535bcbd 100644 --- a/pc/media_protocol_names.cc +++ b/pc/media_protocol_names.cc @@ -13,30 +13,62 @@ #include #include +#include + namespace cricket { +// The official registry of RTP parameters is at +// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml +// The UDP/DTLS and TCP/DTLS prefixes are not registered there. + // There are multiple variants of the RTP protocol stack, including // UDP/TLS/RTP/SAVPF (WebRTC default), RTP/AVP, RTP/AVPF, RTP/SAVPF, // TCP/DTLS/RTP/SAVPF and so on. We accept anything that has RTP/ // embedded in it somewhere as being an RTP protocol. const char kMediaProtocolRtpPrefix[] = "RTP/"; +// Protocol names generated by WebRTC const char kMediaProtocolSctp[] = "SCTP"; -const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP"; const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP"; +const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP"; const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP"; +// RFC5124 +const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF"; +const char kMediaProtocolSavpf[] = "RTP/SAVPF"; +const char kMediaProtocolAvpf[] = "RTP/AVPF"; -bool IsDtlsSctp(const std::string& protocol) { +namespace { + +// Protocol names that we tolerate, but do not generate. +// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP, +// but we tolerate "RTP/SAVPF" and "RTP/SAVP" and the "UDP/TLS" and "TCP/TLS" +// prefixes in offers we receive, for compatibility. +// RFC4585 +const char kMediaProtocolSavp[] = "RTP/SAVP"; +const char kMediaProtocolAvp[] = "RTP/AVP"; + +const char kMediaProtocolTcpTlsSavpf[] = "TCP/TLS/RTP/SAVPF"; +const char kMediaProtocolUdpTlsSavpf[] = "UDP/TLS/RTP/SAVPF"; +const char kMediaProtocolTcpTlsSavp[] = "TCP/TLS/RTP/SAVP"; +const char kMediaProtocolUdpTlsSavp[] = "UDP/TLS/RTP/SAVP"; + +} // namespace + +bool IsDtlsSctp(absl::string_view protocol) { return protocol == kMediaProtocolDtlsSctp || protocol == kMediaProtocolUdpDtlsSctp || protocol == kMediaProtocolTcpDtlsSctp; } -bool IsPlainSctp(const std::string& protocol) { +bool IsPlainSctp(absl::string_view protocol) { return protocol == kMediaProtocolSctp; } -bool IsRtpProtocol(const std::string& protocol) { +bool IsSctpProtocol(absl::string_view protocol) { + return IsPlainSctp(protocol) || IsDtlsSctp(protocol); +} + +bool IsRtpProtocol(absl::string_view protocol) { if (protocol.empty()) { return true; } @@ -51,8 +83,23 @@ bool IsRtpProtocol(const std::string& protocol) { return false; } -bool IsSctpProtocol(const std::string& protocol) { - return IsPlainSctp(protocol) || IsDtlsSctp(protocol); +// Note that the below functions support some protocol strings purely for +// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names +// and Interoperability. + +bool IsDtlsRtp(absl::string_view protocol) { + // Most-likely values first. + return protocol == kMediaProtocolDtlsSavpf || + protocol == kMediaProtocolTcpTlsSavpf || + protocol == kMediaProtocolUdpTlsSavpf || + protocol == kMediaProtocolUdpTlsSavp || + protocol == kMediaProtocolTcpTlsSavp; +} + +bool IsPlainRtp(absl::string_view protocol) { + // Most-likely values first. + return protocol == kMediaProtocolSavpf || protocol == kMediaProtocolAvpf || + protocol == kMediaProtocolSavp || protocol == kMediaProtocolAvp; } } // namespace cricket diff --git a/pc/media_protocol_names.h b/pc/media_protocol_names.h index 88f1c4659d..989c1dab6c 100644 --- a/pc/media_protocol_names.h +++ b/pc/media_protocol_names.h @@ -11,24 +11,36 @@ #ifndef PC_MEDIA_PROTOCOL_NAMES_H_ #define PC_MEDIA_PROTOCOL_NAMES_H_ -#include +#include "absl/strings/string_view.h" namespace cricket { -// Names or name prefixes of protocols as defined by SDP specifications. -extern const char kMediaProtocolRtpPrefix[]; +// Names or name prefixes of protocols as defined by SDP specifications, +// and generated in SDP produced by WebRTC. extern const char kMediaProtocolSctp[]; -extern const char kMediaProtocolDtlsSctp[]; extern const char kMediaProtocolUdpDtlsSctp[]; -extern const char kMediaProtocolTcpDtlsSctp[]; +extern const char kMediaProtocolDtlsSavpf[]; +extern const char kMediaProtocolSavpf[]; +extern const char kMediaProtocolAvpf[]; -bool IsDtlsSctp(const std::string& protocol); -bool IsPlainSctp(const std::string& protocol); +// Exported for testing only +extern const char kMediaProtocolTcpDtlsSctp[]; +extern const char kMediaProtocolDtlsSctp[]; // Returns true if the given media section protocol indicates use of RTP. -bool IsRtpProtocol(const std::string& protocol); +bool IsRtpProtocol(absl::string_view protocol); // Returns true if the given media section protocol indicates use of SCTP. -bool IsSctpProtocol(const std::string& protocol); +bool IsSctpProtocol(absl::string_view protocol); + +// Returns true if the given media protocol is unencrypted SCTP +bool IsPlainSctp(absl::string_view protocol); +// Returns true if the given media protocol is encrypted SCTP +bool IsDtlsSctp(absl::string_view protocol); + +// Returns true if the given media protocol is unencrypted RTP +bool IsPlainRtp(absl::string_view protocol); +// Returns true if the given media protocol is encrypted RTP +bool IsDtlsRtp(absl::string_view protocol); } // namespace cricket diff --git a/pc/media_session.cc b/pc/media_session.cc index c81d64913f..ec60326459 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -14,8 +14,7 @@ #include #include -#include -#include +#include #include #include @@ -24,7 +23,6 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/crypto_params.h" -#include "api/video_codecs/h264_profile_level_id.h" #include "media/base/codec.h" #include "media/base/media_constants.h" #include "media/base/sdp_video_format_utils.h" @@ -41,7 +39,6 @@ #include "rtc_base/string_encode.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/unique_id_generator.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -116,33 +113,6 @@ cricket::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions( namespace cricket { -// RTP Profile names -// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml -// RFC4585 -const char kMediaProtocolAvpf[] = "RTP/AVPF"; -// RFC5124 -const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF"; - -// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP, -// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility. -const char kMediaProtocolSavpf[] = "RTP/SAVPF"; - -// Note that the below functions support some protocol strings purely for -// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names -// and Interoperability. - -static bool IsDtlsRtp(const std::string& protocol) { - // Most-likely values first. - return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" || - protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP"; -} - -static bool IsPlainRtp(const std::string& protocol) { - // Most-likely values first. - return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" || - protocol == "RTP/SAVP" || protocol == "RTP/AVP"; -} - static RtpTransceiverDirection NegotiateRtpTransceiverDirection( RtpTransceiverDirection offer, RtpTransceiverDirection wants) { @@ -330,7 +300,8 @@ static StreamParams CreateStreamParamsForNewSenderWithSsrcs( const std::string& rtcp_cname, bool include_rtx_streams, bool include_flexfec_stream, - UniqueRandomIdGenerator* ssrc_generator) { + UniqueRandomIdGenerator* ssrc_generator, + const webrtc::WebRtcKeyValueConfig& field_trials) { StreamParams result; result.id = sender.track_id; @@ -342,8 +313,7 @@ static StreamParams CreateStreamParamsForNewSenderWithSsrcs( "a single media streams. This session has multiple " "media streams however, so no FlexFEC SSRC will be generated."; } - if (include_flexfec_stream && - !webrtc::field_trial::IsEnabled("WebRTC-FlexFEC-03")) { + if (include_flexfec_stream && !field_trials.IsEnabled("WebRTC-FlexFEC-03")) { include_flexfec_stream = false; RTC_LOG(LS_WARNING) << "WebRTC-FlexFEC trial is not enabled, not sending FlexFEC"; @@ -425,12 +395,12 @@ static void AddSimulcastToMediaDescription( // content_description. // `current_params` - All currently known StreamParams of any media type. template -static bool AddStreamParams( - const std::vector& sender_options, - const std::string& rtcp_cname, - UniqueRandomIdGenerator* ssrc_generator, - StreamParamsVec* current_streams, - MediaContentDescriptionImpl* content_description) { +static bool AddStreamParams(const std::vector& sender_options, + const std::string& rtcp_cname, + UniqueRandomIdGenerator* ssrc_generator, + StreamParamsVec* current_streams, + MediaContentDescriptionImpl* content_description, + const webrtc::WebRtcKeyValueConfig& field_trials) { // SCTP streams are not negotiated using SDP/ContentDescriptions. if (IsSctpProtocol(content_description->protocol())) { return true; @@ -443,10 +413,7 @@ static bool AddStreamParams( ContainsFlexfecCodec(content_description->codecs()); for (const SenderOptions& sender : sender_options) { - // groupid is empty for StreamParams generated using - // MediaSessionDescriptionFactory. - StreamParams* param = - GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id); + StreamParams* param = GetStreamByIds(*current_streams, sender.track_id); if (!param) { // This is a new sender. StreamParams stream_param = @@ -455,7 +422,7 @@ static bool AddStreamParams( // Signal SSRCs and legacy simulcast (if requested). CreateStreamParamsForNewSenderWithSsrcs( sender, rtcp_cname, include_rtx_streams, - include_flexfec_stream, ssrc_generator) + include_flexfec_stream, ssrc_generator, field_trials) : // Signal RIDs and spec-compliant simulcast (if requested). CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname); @@ -653,6 +620,11 @@ static bool ContainsRtxCodec(const std::vector& codecs) { return false; } +template +static bool IsRedCodec(const C& codec) { + return absl::EqualsIgnoreCase(codec.name, kRedCodecName); +} + template static bool IsRtxCodec(const C& codec) { return absl::EqualsIgnoreCase(codec.name, kRtxCodecName); @@ -738,11 +710,12 @@ static bool CreateMediaContentOffer( const RtpHeaderExtensions& rtp_extensions, UniqueRandomIdGenerator* ssrc_generator, StreamParamsVec* current_streams, - MediaContentDescriptionImpl* offer) { + MediaContentDescriptionImpl* offer, + const webrtc::WebRtcKeyValueConfig& field_trials) { offer->AddCodecs(codecs); if (!AddStreamParams(media_description_options.sender_options, session_options.rtcp_cname, ssrc_generator, - current_streams, offer)) { + current_streams, offer, field_trials)) { return false; } @@ -753,13 +726,16 @@ static bool CreateMediaContentOffer( } template -static bool ReferencedCodecsMatch(const std::vector& codecs1, - const int codec1_id, - const std::vector& codecs2, - const int codec2_id) { +static bool ReferencedCodecsMatch( + const std::vector& codecs1, + const int codec1_id, + const std::vector& codecs2, + const int codec2_id, + const webrtc::WebRtcKeyValueConfig* field_trials) { const C* codec1 = FindCodecById(codecs1, codec1_id); const C* codec2 = FindCodecById(codecs2, codec2_id); - return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2); + return codec1 != nullptr && codec2 != nullptr && + codec1->Matches(*codec2, field_trials); } template @@ -779,12 +755,14 @@ template static void NegotiateCodecs(const std::vector& local_codecs, const std::vector& offered_codecs, std::vector* negotiated_codecs, - bool keep_offer_order) { + bool keep_offer_order, + const webrtc::WebRtcKeyValueConfig* field_trials) { for (const C& ours : local_codecs) { C theirs; // Note that we intentionally only find one matching codec for each of our // local codecs, in case the remote offer contains duplicate codecs. - if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) { + if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs, + field_trials)) { C negotiated = ours; NegotiatePacketization(ours, theirs, &negotiated); negotiated.IntersectFeedbackParams(theirs); @@ -800,6 +778,11 @@ static void NegotiateCodecs(const std::vector& local_codecs, if (rtx_time_it != theirs.params.end()) { negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second); } + } else if (IsRedCodec(negotiated)) { + const auto red_it = theirs.params.find(kCodecParamNotInNameValueFormat); + if (red_it != theirs.params.end()) { + negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second); + } } if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) { webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs.params, @@ -829,20 +812,23 @@ static void NegotiateCodecs(const std::vector& local_codecs, } // Finds a codec in `codecs2` that matches `codec_to_match`, which is -// a member of `codecs1`. If `codec_to_match` is an RTX codec, both +// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both // the codecs themselves and their associated codecs must match. template -static bool FindMatchingCodec(const std::vector& codecs1, - const std::vector& codecs2, - const C& codec_to_match, - C* found_codec) { - // `codec_to_match` should be a member of `codecs1`, in order to look up RTX - // codecs' associated codecs correctly. If not, that's a programming error. +static bool FindMatchingCodec( + const std::vector& codecs1, + const std::vector& codecs2, + const C& codec_to_match, + C* found_codec, + const webrtc::WebRtcKeyValueConfig* field_trials) { + // `codec_to_match` should be a member of `codecs1`, in order to look up + // RED/RTX codecs' associated codecs correctly. If not, that's a programming + // error. RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) { return &codec == &codec_to_match; })); for (const C& potential_match : codecs2) { - if (potential_match.Matches(codec_to_match)) { + if (potential_match.Matches(codec_to_match, field_trials)) { if (IsRtxCodec(codec_to_match)) { int apt_value_1 = 0; int apt_value_2 = 0; @@ -853,8 +839,56 @@ static bool FindMatchingCodec(const std::vector& codecs1, RTC_LOG(LS_WARNING) << "RTX missing associated payload type."; continue; } - if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, - apt_value_2)) { + if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, apt_value_2, + field_trials)) { + continue; + } + } else if (IsRedCodec(codec_to_match)) { + auto red_parameters_1 = + codec_to_match.params.find(kCodecParamNotInNameValueFormat); + auto red_parameters_2 = + potential_match.params.find(kCodecParamNotInNameValueFormat); + bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end(); + bool has_parameters_2 = + red_parameters_2 != potential_match.params.end(); + if (has_parameters_1 && has_parameters_2) { + // Mixed reference codecs (i.e. 111/112) are not supported. + // Different levels of redundancy between offer and answer are + // since RED is considered to be declarative. + std::vector redundant_payloads_1; + std::vector redundant_payloads_2; + rtc::split(red_parameters_1->second, '/', &redundant_payloads_1); + rtc::split(red_parameters_2->second, '/', &redundant_payloads_2); + if (redundant_payloads_1.size() > 0 && + redundant_payloads_2.size() > 0) { + bool consistent = true; + for (size_t i = 1; i < redundant_payloads_1.size(); i++) { + if (redundant_payloads_1[i] != redundant_payloads_1[0]) { + consistent = false; + break; + } + } + for (size_t i = 1; i < redundant_payloads_2.size(); i++) { + if (redundant_payloads_2[i] != redundant_payloads_2[0]) { + consistent = false; + break; + } + } + if (!consistent) { + continue; + } + + int red_value_1; + int red_value_2; + if (rtc::FromString(redundant_payloads_1[0], &red_value_1) && + rtc::FromString(redundant_payloads_2[0], &red_value_2)) { + if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2, + red_value_2, field_trials)) { + continue; + } + } + } + } else if (has_parameters_1 != has_parameters_2) { continue; } } @@ -869,8 +903,8 @@ static bool FindMatchingCodec(const std::vector& codecs1, // Find the codec in `codec_list` that `rtx_codec` is associated with. template -static const C* GetAssociatedCodec(const std::vector& codec_list, - const C& rtx_codec) { +static const C* GetAssociatedCodecForRtx(const std::vector& codec_list, + const C& rtx_codec) { std::string associated_pt_str; if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType, &associated_pt_str)) { @@ -887,7 +921,7 @@ static const C* GetAssociatedCodec(const std::vector& codec_list, return nullptr; } - // Find the associated reference codec for the reference RTX codec. + // Find the associated codec for the RTX codec. const C* associated_codec = FindCodecById(codec_list, associated_pt); if (!associated_codec) { RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " @@ -897,32 +931,72 @@ static const C* GetAssociatedCodec(const std::vector& codec_list, return associated_codec; } +// Find the codec in `codec_list` that `red_codec` is associated with. +template +static const C* GetAssociatedCodecForRed(const std::vector& codec_list, + const C& red_codec) { + std::string fmtp; + if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) { + // Normal for video/RED. + RTC_LOG(LS_WARNING) << "RED codec " << red_codec.name + << " is missing an associated payload type."; + return nullptr; + } + + std::vector redundant_payloads; + rtc::split(fmtp, '/', &redundant_payloads); + if (redundant_payloads.size() < 2) { + return nullptr; + } + + std::string associated_pt_str = redundant_payloads[0]; + int associated_pt; + if (!rtc::FromString(associated_pt_str, &associated_pt)) { + RTC_LOG(LS_WARNING) << "Couldn't convert first payload type " + << associated_pt_str << " of RED codec " + << red_codec.name << " to an integer."; + return nullptr; + } + + // Find the associated codec for the RED codec. + const C* associated_codec = FindCodecById(codec_list, associated_pt); + if (!associated_codec) { + RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " + << associated_pt << " for RED codec " << red_codec.name + << "."; + } + return associated_codec; +} + // Adds all codecs from `reference_codecs` to `offered_codecs` that don't // already exist in `offered_codecs` and ensure the payload types don't // collide. template static void MergeCodecs(const std::vector& reference_codecs, std::vector* offered_codecs, - UsedPayloadTypes* used_pltypes) { - // Add all new codecs that are not RTX codecs. + UsedPayloadTypes* used_pltypes, + const webrtc::WebRtcKeyValueConfig* field_trials) { + // Add all new codecs that are not RTX/RED codecs. + // The two-pass splitting of the loops means preferring payload types + // of actual codecs with respect to collisions. for (const C& reference_codec : reference_codecs) { - if (!IsRtxCodec(reference_codec) && + if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) && !FindMatchingCodec(reference_codecs, *offered_codecs, - reference_codec, nullptr)) { + reference_codec, nullptr, field_trials)) { C codec = reference_codec; used_pltypes->FindAndSetIdUsed(&codec); offered_codecs->push_back(codec); } } - // Add all new RTX codecs. + // Add all new RTX or RED codecs. for (const C& reference_codec : reference_codecs) { if (IsRtxCodec(reference_codec) && !FindMatchingCodec(reference_codecs, *offered_codecs, - reference_codec, nullptr)) { + reference_codec, nullptr, field_trials)) { C rtx_codec = reference_codec; const C* associated_codec = - GetAssociatedCodec(reference_codecs, rtx_codec); + GetAssociatedCodecForRtx(reference_codecs, rtx_codec); if (!associated_codec) { continue; } @@ -930,7 +1004,8 @@ static void MergeCodecs(const std::vector& reference_codecs, // Its payload type may be different than the reference codec. C matching_codec; if (!FindMatchingCodec(reference_codecs, *offered_codecs, - *associated_codec, &matching_codec)) { + *associated_codec, &matching_codec, + field_trials)) { RTC_LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name << " codec."; continue; @@ -940,21 +1015,57 @@ static void MergeCodecs(const std::vector& reference_codecs, rtc::ToString(matching_codec.id); used_pltypes->FindAndSetIdUsed(&rtx_codec); offered_codecs->push_back(rtx_codec); + } else if (IsRedCodec(reference_codec) && + !FindMatchingCodec(reference_codecs, *offered_codecs, + reference_codec, nullptr, field_trials)) { + C red_codec = reference_codec; + const C* associated_codec = + GetAssociatedCodecForRed(reference_codecs, red_codec); + if (associated_codec) { + C matching_codec; + if (!FindMatchingCodec(reference_codecs, *offered_codecs, + *associated_codec, &matching_codec, + field_trials)) { + RTC_LOG(LS_WARNING) << "Couldn't find matching " + << associated_codec->name << " codec."; + continue; + } + + red_codec.params[kCodecParamNotInNameValueFormat] = + rtc::ToString(matching_codec.id) + "/" + + rtc::ToString(matching_codec.id); + } + used_pltypes->FindAndSetIdUsed(&red_codec); + offered_codecs->push_back(red_codec); } } } +// `codecs` is a full list of codecs with correct payload type mappings, which +// don't conflict with mappings of the other media type; `supported_codecs` is +// a list filtered for the media section`s direction but with default payload +// types. template static Codecs MatchCodecPreference( const std::vector& codec_preferences, - const Codecs& codecs) { + const Codecs& codecs, + const Codecs& supported_codecs, + const webrtc::WebRtcKeyValueConfig* field_trials) { Codecs filtered_codecs; - std::set kept_codecs_ids; bool want_rtx = false; + bool want_red = false; + for (const auto& codec_preference : codec_preferences) { + if (IsRtxCodec(codec_preference)) { + want_rtx = true; + } else if (IsRedCodec(codec_preference)) { + want_red = true; + } + } for (const auto& codec_preference : codec_preferences) { auto found_codec = absl::c_find_if( - codecs, [&codec_preference](const typename Codecs::value_type& codec) { + supported_codecs, + [&codec_preference](const typename Codecs::value_type& codec) { webrtc::RtpCodecParameters codec_parameters = codec.ToCodecParameters(); return codec_parameters.name == codec_preference.name && @@ -965,22 +1076,42 @@ static Codecs MatchCodecPreference( codec_parameters.parameters == codec_preference.parameters; }); - if (found_codec != codecs.end()) { - filtered_codecs.push_back(*found_codec); - kept_codecs_ids.insert(std::to_string(found_codec->id)); - } else if (IsRtxCodec(codec_preference)) { - want_rtx = true; - } - } - - if (want_rtx) { - for (const auto& codec : codecs) { - if (IsRtxCodec(codec)) { - const auto apt = - codec.params.find(cricket::kCodecParamAssociatedPayloadType); - if (apt != codec.params.end() && - kept_codecs_ids.count(apt->second) > 0) { - filtered_codecs.push_back(codec); + if (found_codec != supported_codecs.end()) { + typename Codecs::value_type found_codec_with_correct_pt; + if (FindMatchingCodec(supported_codecs, codecs, *found_codec, + &found_codec_with_correct_pt, field_trials)) { + filtered_codecs.push_back(found_codec_with_correct_pt); + std::string id = rtc::ToString(found_codec_with_correct_pt.id); + // Search for the matching rtx or red codec. + if (want_red || want_rtx) { + for (const auto& codec : codecs) { + if (IsRtxCodec(codec)) { + const auto apt = + codec.params.find(cricket::kCodecParamAssociatedPayloadType); + if (apt != codec.params.end() && apt->second == id) { + filtered_codecs.push_back(codec); + break; + } + } else if (IsRedCodec(codec)) { + // For RED, do not insert the codec again if it was already + // inserted. audio/red for opus gets enabled by having RED before + // the primary codec. + const auto fmtp = + codec.params.find(cricket::kCodecParamNotInNameValueFormat); + if (fmtp != codec.params.end()) { + std::vector redundant_payloads; + rtc::split(fmtp->second, '/', &redundant_payloads); + if (redundant_payloads.size() > 0 && + redundant_payloads[0] == id) { + if (std::find(filtered_codecs.begin(), filtered_codecs.end(), + codec) == filtered_codecs.end()) { + filtered_codecs.push_back(codec); + } + break; + } + } + } + } } } } @@ -989,6 +1120,27 @@ static Codecs MatchCodecPreference( return filtered_codecs; } +// Compute the union of `codecs1` and `codecs2`. +template +std::vector ComputeCodecsUnion( + const std::vector& codecs1, + const std::vector& codecs2, + const webrtc::WebRtcKeyValueConfig* field_trials) { + std::vector all_codecs; + UsedPayloadTypes used_payload_types; + for (const C& codec : codecs1) { + C codec_mutable = codec; + used_payload_types.FindAndSetIdUsed(&codec_mutable); + all_codecs.push_back(codec_mutable); + } + + // Use MergeCodecs to merge the second half of our list as it already checks + // and fixes problems with duplicate payload types. + MergeCodecs(codecs2, &all_codecs, &used_payload_types, field_trials); + + return all_codecs; +} + // Adds all extensions from `reference_extensions` to `offered_extensions` that // don't already exist in `offered_extensions` and ensure the IDs don't // collide. If an extension is added, it's also added to `regular_extensions` or @@ -1212,15 +1364,17 @@ static bool SetCodecsInAnswer( const MediaSessionOptions& session_options, UniqueRandomIdGenerator* ssrc_generator, StreamParamsVec* current_streams, - MediaContentDescriptionImpl* answer) { + MediaContentDescriptionImpl* answer, + const webrtc::WebRtcKeyValueConfig& field_trials) { std::vector negotiated_codecs; NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs, - media_description_options.codec_preferences.empty()); + media_description_options.codec_preferences.empty(), + &field_trials); answer->AddCodecs(negotiated_codecs); answer->set_protocol(offer->protocol()); if (!AddStreamParams(media_description_options.sender_options, session_options.rtcp_cname, ssrc_generator, - current_streams, answer)) { + current_streams, answer, field_trials)) { return false; // Something went seriously wrong. } return true; @@ -1296,14 +1450,12 @@ static bool IsMediaProtocolSupported(MediaType type, } if (type == MEDIA_TYPE_DATA) { - // Check for SCTP, but also for RTP for RTP-based data channels. - // TODO(pthatcher): Remove RTP once RTP-based data channels are gone. + // Check for SCTP if (secure_transport) { // Most likely scenarios first. - return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) || - IsPlainRtp(protocol); + return IsDtlsSctp(protocol); } else { - return IsPlainSctp(protocol) || IsPlainRtp(protocol); + return IsPlainSctp(protocol); } } @@ -1414,9 +1566,9 @@ MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( ChannelManager* channel_manager, - const TransportDescriptionFactory* transport_desc_factory, - rtc::UniqueRandomIdGenerator* ssrc_generator) - : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) { + const TransportDescriptionFactory* transport_desc_factory) + : MediaSessionDescriptionFactory(transport_desc_factory, + &channel_manager->ssrc_generator()) { channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_); channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_); channel_manager->GetSupportedVideoSendCodecs(&video_send_codecs_); @@ -1571,7 +1723,7 @@ std::unique_ptr MediaSessionDescriptionFactory::CreateOffer( } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } ++msection_index; } @@ -1747,7 +1899,7 @@ MediaSessionDescriptionFactory::CreateAnswer( } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } ++msection_index; // See if we can add the newly generated m= section to the BUNDLE group in @@ -1901,16 +2053,19 @@ void MergeCodecsFromDescription( const std::vector& current_active_contents, AudioCodecs* audio_codecs, VideoCodecs* video_codecs, - UsedPayloadTypes* used_pltypes) { + UsedPayloadTypes* used_pltypes, + const webrtc::WebRtcKeyValueConfig* field_trials) { for (const ContentInfo* content : current_active_contents) { if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) { const AudioContentDescription* audio = content->media_description()->as_audio(); - MergeCodecs(audio->codecs(), audio_codecs, used_pltypes); + MergeCodecs(audio->codecs(), audio_codecs, used_pltypes, + field_trials); } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) { const VideoContentDescription* video = content->media_description()->as_video(); - MergeCodecs(video->codecs(), video_codecs, used_pltypes); + MergeCodecs(video->codecs(), video_codecs, used_pltypes, + field_trials); } } } @@ -1925,16 +2080,20 @@ void MediaSessionDescriptionFactory::GetCodecsForOffer( const std::vector& current_active_contents, AudioCodecs* audio_codecs, VideoCodecs* video_codecs) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); // First - get all codecs from the current description if the media type // is used. Add them to `used_pltypes` so the payload type is not reused if a // new media type is added. UsedPayloadTypes used_pltypes; MergeCodecsFromDescription(current_active_contents, audio_codecs, - video_codecs, &used_pltypes); + video_codecs, &used_pltypes, field_trials); // Add our codecs that are not in the current description. - MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes); - MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes); + MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes, + field_trials); + MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes, + field_trials); } // Getting codecs for an answer involves these steps: @@ -1949,12 +2108,14 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const SessionDescription& remote_offer, AudioCodecs* audio_codecs, VideoCodecs* video_codecs) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); // First - get all codecs from the current description if the media type // is used. Add them to `used_pltypes` so the payload type is not reused if a // new media type is added. UsedPayloadTypes used_pltypes; MergeCodecsFromDescription(current_active_contents, audio_codecs, - video_codecs, &used_pltypes); + video_codecs, &used_pltypes, field_trials); // Second - filter out codecs that we don't support at all and should ignore. AudioCodecs filtered_offered_audio_codecs; @@ -1964,11 +2125,12 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const AudioContentDescription* audio = content.media_description()->as_audio(); for (const AudioCodec& offered_audio_codec : audio->codecs()) { - if (!FindMatchingCodec(audio->codecs(), - filtered_offered_audio_codecs, - offered_audio_codec, nullptr) && + if (!FindMatchingCodec( + audio->codecs(), filtered_offered_audio_codecs, + offered_audio_codec, nullptr, field_trials) && FindMatchingCodec(audio->codecs(), all_audio_codecs_, - offered_audio_codec, nullptr)) { + offered_audio_codec, nullptr, + field_trials)) { filtered_offered_audio_codecs.push_back(offered_audio_codec); } } @@ -1976,11 +2138,12 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const VideoContentDescription* video = content.media_description()->as_video(); for (const VideoCodec& offered_video_codec : video->codecs()) { - if (!FindMatchingCodec(video->codecs(), - filtered_offered_video_codecs, - offered_video_codec, nullptr) && + if (!FindMatchingCodec( + video->codecs(), filtered_offered_video_codecs, + offered_video_codec, nullptr, field_trials) && FindMatchingCodec(video->codecs(), all_video_codecs_, - offered_video_codec, nullptr)) { + offered_video_codec, nullptr, + field_trials)) { filtered_offered_video_codecs.push_back(offered_video_codec); } } @@ -1990,9 +2153,9 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( // Add codecs that are not in the current description but were in // `remote_offer`. MergeCodecs(filtered_offered_audio_codecs, audio_codecs, - &used_pltypes); + &used_pltypes, field_trials); MergeCodecs(filtered_offered_video_codecs, video_codecs, - &used_pltypes); + &used_pltypes, field_trials); } MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions @@ -2134,6 +2297,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( StreamParamsVec* current_streams, SessionDescription* desc, IceCredentialsIterator* ice_credentials) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); // Filter audio_codecs (which includes all codecs, with correctly remapped // payload types) based on transceiver direction. const AudioCodecs& supported_audio_codecs = @@ -2145,7 +2310,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( // Add the codecs from the current transceiver's codec preferences. // They override any existing codecs from previous negotiations. filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, supported_audio_codecs); + media_description_options.codec_preferences, audio_codecs, + supported_audio_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2156,7 +2322,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( current_content->media_description()->as_audio(); for (const AudioCodec& codec : acd->codecs()) { if (FindMatchingCodec(acd->codecs(), audio_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2165,9 +2331,10 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( AudioCodec found_codec; for (const AudioCodec& codec : supported_audio_codecs) { if (FindMatchingCodec(supported_audio_codecs, audio_codecs, - codec, &found_codec) && + codec, &found_codec, field_trials) && !FindMatchingCodec(supported_audio_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // Use the `found_codec` from `audio_codecs` because it has the // correctly mapped payload type. filtered_codecs.push_back(found_codec); @@ -2187,11 +2354,11 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( std::vector crypto_suites; GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options, &crypto_suites); - if (!CreateMediaContentOffer(media_description_options, session_options, - filtered_codecs, sdes_policy, - GetCryptos(current_content), crypto_suites, - audio_rtp_extensions, ssrc_generator_, - current_streams, audio.get())) { + if (!CreateMediaContentOffer( + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + audio_rtp_extensions, ssrc_generator_, current_streams, audio.get(), + transport_desc_factory_->trials())) { return false; } @@ -2223,6 +2390,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( StreamParamsVec* current_streams, SessionDescription* desc, IceCredentialsIterator* ice_credentials) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); // Filter video_codecs (which includes all codecs, with correctly remapped // payload types) based on transceiver direction. const VideoCodecs& supported_video_codecs = @@ -2234,7 +2403,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( // Add the codecs from the current transceiver's codec preferences. // They override any existing codecs from previous negotiations. filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, supported_video_codecs); + media_description_options.codec_preferences, video_codecs, + supported_video_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2245,7 +2415,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( current_content->media_description()->as_video(); for (const VideoCodec& codec : vcd->codecs()) { if (FindMatchingCodec(vcd->codecs(), video_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2254,11 +2424,28 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( VideoCodec found_codec; for (const VideoCodec& codec : supported_video_codecs) { if (FindMatchingCodec(supported_video_codecs, video_codecs, - codec, &found_codec) && + codec, &found_codec, field_trials) && !FindMatchingCodec(supported_video_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // Use the `found_codec` from `video_codecs` because it has the // correctly mapped payload type. + if (IsRtxCodec(codec)) { + // For RTX we might need to adjust the apt parameter if we got a + // remote offer without RTX for a codec for which we support RTX. + auto referenced_codec = + GetAssociatedCodecForRtx(supported_video_codecs, codec); + RTC_DCHECK(referenced_codec); + + // Find the codec we should be referencing and point to it. + VideoCodec changed_referenced_codec; + if (FindMatchingCodec( + supported_video_codecs, filtered_codecs, *referenced_codec, + &changed_referenced_codec, field_trials)) { + found_codec.SetParam(kCodecParamAssociatedPayloadType, + changed_referenced_codec.id); + } + } filtered_codecs.push_back(found_codec); } } @@ -2279,11 +2466,11 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( std::vector crypto_suites; GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options, &crypto_suites); - if (!CreateMediaContentOffer(media_description_options, session_options, - filtered_codecs, sdes_policy, - GetCryptos(current_content), crypto_suites, - video_rtp_extensions, ssrc_generator_, - current_streams, video.get())) { + if (!CreateMediaContentOffer( + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + video_rtp_extensions, ssrc_generator_, current_streams, video.get(), + transport_desc_factory_->trials())) { return false; } @@ -2400,6 +2587,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( StreamParamsVec* current_streams, SessionDescription* answer, IceCredentialsIterator* ice_credentials) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO)); const AudioContentDescription* offer_audio_description = offer_content->media_description()->as_audio(); @@ -2425,7 +2614,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( if (!media_description_options.codec_preferences.empty()) { filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, supported_audio_codecs); + media_description_options.codec_preferences, audio_codecs, + supported_audio_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2436,7 +2626,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( current_content->media_description()->as_audio(); for (const AudioCodec& codec : acd->codecs()) { if (FindMatchingCodec(acd->codecs(), audio_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2444,9 +2634,10 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( // Add other supported audio codecs. for (const AudioCodec& codec : supported_audio_codecs) { if (FindMatchingCodec(supported_audio_codecs, audio_codecs, - codec, nullptr) && + codec, nullptr, field_trials) && !FindMatchingCodec(supported_audio_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // We should use the local codec with local parameters and the codec id // would be correctly mapped in `NegotiateCodecs`. filtered_codecs.push_back(codec); @@ -2466,8 +2657,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( audio_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, - audio_answer.get())) { + ssrc_generator_, current_streams, audio_answer.get(), + transport_desc_factory_->trials())) { return false; } if (!CreateMediaContentAnswer( @@ -2515,6 +2706,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( StreamParamsVec* current_streams, SessionDescription* answer, IceCredentialsIterator* ice_credentials) const { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO)); const VideoContentDescription* offer_video_description = offer_content->media_description()->as_video(); @@ -2540,7 +2733,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( if (!media_description_options.codec_preferences.empty()) { filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, supported_video_codecs); + media_description_options.codec_preferences, video_codecs, + supported_video_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2551,22 +2745,29 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( current_content->media_description()->as_video(); for (const VideoCodec& codec : vcd->codecs()) { if (FindMatchingCodec(vcd->codecs(), video_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } } + // Add other supported video codecs. + VideoCodecs other_video_codecs; for (const VideoCodec& codec : supported_video_codecs) { if (FindMatchingCodec(supported_video_codecs, video_codecs, - codec, nullptr) && + codec, nullptr, field_trials) && !FindMatchingCodec(supported_video_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // We should use the local codec with local parameters and the codec id // would be correctly mapped in `NegotiateCodecs`. - filtered_codecs.push_back(codec); + other_video_codecs.push_back(codec); } } + + // Use ComputeCodecsUnion to avoid having duplicate payload IDs + filtered_codecs = ComputeCodecsUnion( + filtered_codecs, other_video_codecs, field_trials); } if (session_options.raw_packetization_for_video) { @@ -2585,8 +2786,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( video_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_video_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, - video_answer.get())) { + ssrc_generator_, current_streams, video_answer.get(), + transport_desc_factory_->trials())) { return false; } if (!CreateMediaContentAnswer( @@ -2674,7 +2875,7 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer( bool offer_uses_sctpmap = offer_data_description->use_sctpmap(); data_answer->as_sctp()->set_use_sctpmap(offer_uses_sctpmap); } else { - RTC_NOTREACHED() << "Non-SCTP data content found"; + RTC_DCHECK_NOTREACHED() << "Non-SCTP data content found"; } bool secure = bundle_transport ? bundle_transport->description.secure() @@ -2731,13 +2932,15 @@ bool MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer( } void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); audio_sendrecv_codecs_.clear(); all_audio_codecs_.clear(); // Compute the audio codecs union. for (const AudioCodec& send : audio_send_codecs_) { all_audio_codecs_.push_back(send); if (!FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_, - send, nullptr)) { + send, nullptr, field_trials)) { // It doesn't make sense to have an RTX codec we support sending but not // receiving. RTC_DCHECK(!IsRtxCodec(send)); @@ -2745,7 +2948,7 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { } for (const AudioCodec& recv : audio_recv_codecs_) { if (!FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_, - recv, nullptr)) { + recv, nullptr, field_trials)) { all_audio_codecs_.push_back(recv); } } @@ -2755,39 +2958,25 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { // expensive than decoding, and prioritizing a codec in the send list probably // means it's a codec we can handle efficiently. NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_, - &audio_sendrecv_codecs_, true); + &audio_sendrecv_codecs_, true, field_trials); } void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() { + const webrtc::WebRtcKeyValueConfig* field_trials = + &transport_desc_factory_->trials(); video_sendrecv_codecs_.clear(); - all_video_codecs_.clear(); - // Compute the video codecs union. - for (const VideoCodec& send : video_send_codecs_) { - all_video_codecs_.push_back(send); - if (!FindMatchingCodec(video_send_codecs_, video_recv_codecs_, - send, nullptr)) { - // TODO(kron): This check is violated by the unit test: - // MediaSessionDescriptionFactoryTest.RtxWithoutApt - // Remove either the test or the check. - // It doesn't make sense to have an RTX codec we support sending but not - // receiving. - // RTC_DCHECK(!IsRtxCodec(send)); - } - } - for (const VideoCodec& recv : video_recv_codecs_) { - if (!FindMatchingCodec(video_recv_codecs_, video_send_codecs_, - recv, nullptr)) { - all_video_codecs_.push_back(recv); - } - } + // Use ComputeCodecsUnion to avoid having duplicate payload IDs + all_video_codecs_ = + ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_, field_trials); + // Use NegotiateCodecs to merge our codec lists, since the operation is // essentially the same. Put send_codecs as the offered_codecs, which is the // order we'd like to follow. The reasoning is that encoding is usually more // expensive than decoding, and prioritizing a codec in the send list probably // means it's a codec we can handle efficiently. NegotiateCodecs(video_recv_codecs_, video_send_codecs_, - &video_sendrecv_codecs_, true); + &video_sendrecv_codecs_, true, field_trials); } bool IsMediaContent(const ContentInfo* content) { diff --git a/pc/media_session.h b/pc/media_session.h index 3a726a5c5a..3ff0de0bcd 100644 --- a/pc/media_session.h +++ b/pc/media_session.h @@ -22,6 +22,7 @@ #include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" +#include "api/webrtc_key_value_config.h" #include "media/base/media_constants.h" #include "media/base/rid_description.h" #include "media/base/stream_params.h" @@ -35,6 +36,13 @@ #include "pc/simulcast_description.h" #include "rtc_base/unique_id_generator.h" +namespace webrtc { + +// Forward declaration due to circular dependecy. +class ConnectionContext; + +} // namespace webrtc + namespace cricket { class ChannelManager; @@ -142,8 +150,7 @@ class MediaSessionDescriptionFactory { // This helper automatically sets up the factory to get its configuration // from the specified ChannelManager. MediaSessionDescriptionFactory(ChannelManager* cmanager, - const TransportDescriptionFactory* factory, - rtc::UniqueRandomIdGenerator* ssrc_generator); + const TransportDescriptionFactory* factory); const AudioCodecs& audio_sendrecv_codecs() const; const AudioCodecs& audio_send_codecs() const; diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc index a02b4c1415..9d01e0772f 100644 --- a/pc/media_session_unittest.cc +++ b/pc/media_session_unittest.cc @@ -10,32 +10,42 @@ #include "pc/media_session.h" +#include + #include +#include +#include #include #include -#include +#include #include #include "absl/algorithm/container.h" -#include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "api/candidate.h" +#include "api/crypto_params.h" #include "media/base/codec.h" +#include "media/base/media_constants.h" #include "media/base/test_utils.h" #include "media/sctp/sctp_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/rtp_media_utils.h" -#include "pc/srtp_filter.h" +#include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/fake_ssl_identity.h" -#include "rtc_base/gunit.h" -#include "rtc_base/message_digest.h" -#include "rtc_base/ssl_adapter.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/unique_id_generator.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" #define ASSERT_CRYPTO(cd, s, cs) \ ASSERT_EQ(s, cd->cryptos().size()); \ @@ -363,7 +373,7 @@ static void AttachSenderToMediaDescriptionOptions( num_sim_layer); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -421,7 +431,10 @@ void PreferGcmCryptoParameters(CryptoParamsVec* cryptos) { class MediaSessionDescriptionFactoryTest : public ::testing::Test { public: MediaSessionDescriptionFactoryTest() - : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) { + : tdf1_(field_trials), + tdf2_(field_trials), + f1_(&tdf1_, &ssrc_generator1), + f2_(&tdf2_, &ssrc_generator2) { f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), MAKE_VECTOR(kAudioCodecs1)); f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), @@ -781,12 +794,13 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test { } protected: + webrtc::test::ScopedKeyValueConfig field_trials; UniqueRandomIdGenerator ssrc_generator1; UniqueRandomIdGenerator ssrc_generator2; - MediaSessionDescriptionFactory f1_; - MediaSessionDescriptionFactory f2_; TransportDescriptionFactory tdf1_; TransportDescriptionFactory tdf2_; + MediaSessionDescriptionFactory f1_; + MediaSessionDescriptionFactory f2_; }; // Create a typical audio offer, and ensure it matches what we expect. @@ -3250,8 +3264,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) { // Test that, when the FlexFEC codec is added, a FlexFEC ssrc is created // together with a FEC-FR grouping. Guarded by WebRTC-FlexFEC-03 trial. TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FlexFEC-03/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials, "WebRTC-FlexFEC-03/Enabled/"); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, kActive, @@ -3293,8 +3307,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { // TODO(brandtr): Remove this test when we support simulcast, either through // multiple FlexfecSenders, or through multistream protection. TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FlexFEC-03/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials, "WebRTC-FlexFEC-03/Enabled/"); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, kActive, @@ -4320,7 +4334,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, class MediaProtocolTest : public ::testing::TestWithParam { public: MediaProtocolTest() - : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) { + : tdf1_(field_trials_), + tdf2_(field_trials_), + f1_(&tdf1_, &ssrc_generator1), + f2_(&tdf2_, &ssrc_generator2) { f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), MAKE_VECTOR(kAudioCodecs1)); f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), @@ -4340,10 +4357,11 @@ class MediaProtocolTest : public ::testing::TestWithParam { } protected: - MediaSessionDescriptionFactory f1_; - MediaSessionDescriptionFactory f2_; + webrtc::test::ScopedKeyValueConfig field_trials_; TransportDescriptionFactory tdf1_; TransportDescriptionFactory tdf2_; + MediaSessionDescriptionFactory f1_; + MediaSessionDescriptionFactory f2_; UniqueRandomIdGenerator ssrc_generator1; UniqueRandomIdGenerator ssrc_generator2; }; @@ -4379,14 +4397,15 @@ INSTANTIATE_TEST_SUITE_P(MediaProtocolDtlsPatternTest, ::testing::ValuesIn(kMediaProtocolsDtls)); TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) { - TransportDescriptionFactory tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory tdf(field_trials); UniqueRandomIdGenerator ssrc_generator; MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator); std::vector send_codecs = MAKE_VECTOR(kAudioCodecs1); std::vector recv_codecs = MAKE_VECTOR(kAudioCodecs2); // The merged list of codecs should contain any send codecs that are also - // nominally in the recieve codecs list. Payload types should be picked from + // nominally in the receive codecs list. Payload types should be picked from // the send codecs and a number-of-channels of 0 and 1 should be equivalent // (set to 1). This equals what happens when the send codecs are used in an // offer and the receive codecs are used in the following answer. @@ -4435,13 +4454,14 @@ namespace { // Compare the two vectors of codecs ignoring the payload type. template bool CodecsMatch(const std::vector& codecs1, - const std::vector& codecs2) { + const std::vector& codecs2, + const webrtc::WebRtcKeyValueConfig* field_trials) { if (codecs1.size() != codecs2.size()) { return false; } for (size_t i = 0; i < codecs1.size(); ++i) { - if (!codecs1[i].Matches(codecs2[i])) { + if (!codecs1[i].Matches(codecs2[i], field_trials)) { return false; } } @@ -4449,7 +4469,8 @@ bool CodecsMatch(const std::vector& codecs1, } void TestAudioCodecsOffer(RtpTransceiverDirection direction) { - TransportDescriptionFactory tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory tdf(field_trials); UniqueRandomIdGenerator ssrc_generator; MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator); const std::vector send_codecs = MAKE_VECTOR(kAudioCodecs1); @@ -4483,11 +4504,14 @@ void TestAudioCodecsOffer(RtpTransceiverDirection direction) { // might eventually be used anything, but we don't know more at this // moment. if (acd->direction() == RtpTransceiverDirection::kSendOnly) { - EXPECT_TRUE(CodecsMatch(send_codecs, acd->codecs())); + EXPECT_TRUE( + CodecsMatch(send_codecs, acd->codecs(), &field_trials)); } else if (acd->direction() == RtpTransceiverDirection::kRecvOnly) { - EXPECT_TRUE(CodecsMatch(recv_codecs, acd->codecs())); + EXPECT_TRUE( + CodecsMatch(recv_codecs, acd->codecs(), &field_trials)); } else { - EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs())); + EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs(), + &field_trials)); } } } @@ -4546,11 +4570,13 @@ std::vector VectorFromIndices(const T* array, const int (&indices)[IDXS]) { void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, RtpTransceiverDirection answer_direction, bool add_legacy_stream) { - TransportDescriptionFactory offer_tdf; - TransportDescriptionFactory answer_tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory offer_tdf(field_trials); + TransportDescriptionFactory answer_tdf(field_trials); UniqueRandomIdGenerator ssrc_generator1, ssrc_generator2; MediaSessionDescriptionFactory offer_factory(&offer_tdf, &ssrc_generator1); MediaSessionDescriptionFactory answer_factory(&answer_tdf, &ssrc_generator2); + offer_factory.set_audio_codecs( VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs), VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs)); @@ -4623,7 +4649,7 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, break; case RtpTransceiverDirection::kStopped: // This does not happen in any current test. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } auto format_codecs = [](const std::vector& codecs) { diff --git a/pc/media_stream.cc b/pc/media_stream.cc index 08a2a723d0..011d8abf55 100644 --- a/pc/media_stream.cc +++ b/pc/media_stream.cc @@ -12,7 +12,7 @@ #include -#include +#include #include "rtc_base/checks.h" #include "rtc_base/ref_counted_object.h" @@ -37,11 +37,13 @@ rtc::scoped_refptr MediaStream::Create(const std::string& id) { MediaStream::MediaStream(const std::string& id) : id_(id) {} bool MediaStream::AddTrack(AudioTrackInterface* track) { - return AddTrack(&audio_tracks_, track); + return AddTrack( + &audio_tracks_, rtc::scoped_refptr(track)); } bool MediaStream::AddTrack(VideoTrackInterface* track) { - return AddTrack(&video_tracks_, track); + return AddTrack( + &video_tracks_, rtc::scoped_refptr(track)); } bool MediaStream::RemoveTrack(AudioTrackInterface* track) { @@ -56,7 +58,7 @@ rtc::scoped_refptr MediaStream::FindAudioTrack( const std::string& track_id) { AudioTrackVector::iterator it = FindTrack(&audio_tracks_, track_id); if (it == audio_tracks_.end()) - return NULL; + return nullptr; return *it; } @@ -64,16 +66,17 @@ rtc::scoped_refptr MediaStream::FindVideoTrack( const std::string& track_id) { VideoTrackVector::iterator it = FindTrack(&video_tracks_, track_id); if (it == video_tracks_.end()) - return NULL; + return nullptr; return *it; } template -bool MediaStream::AddTrack(TrackVector* tracks, Track* track) { +bool MediaStream::AddTrack(TrackVector* tracks, + rtc::scoped_refptr track) { typename TrackVector::iterator it = FindTrack(tracks, track->id()); if (it != tracks->end()) return false; - tracks->push_back(track); + tracks->emplace_back(std::move((track))); FireOnChanged(); return true; } diff --git a/pc/media_stream.h b/pc/media_stream.h index 6f16bea1d9..70e58f9760 100644 --- a/pc/media_stream.h +++ b/pc/media_stream.h @@ -44,7 +44,7 @@ class MediaStream : public Notifier { private: template - bool AddTrack(TrackVector* Tracks, Track* track); + bool AddTrack(TrackVector* Tracks, rtc::scoped_refptr track); template bool RemoveTrack(TrackVector* Tracks, MediaStreamTrackInterface* track); diff --git a/pc/media_stream_track_proxy.h b/pc/media_stream_track_proxy.h index f563137c77..2af3aedb22 100644 --- a/pc/media_stream_track_proxy.h +++ b/pc/media_stream_track_proxy.h @@ -44,15 +44,16 @@ PROXY_PRIMARY_THREAD_DESTRUCTOR() BYPASS_PROXY_CONSTMETHOD0(std::string, kind) BYPASS_PROXY_CONSTMETHOD0(std::string, id) PROXY_SECONDARY_CONSTMETHOD0(TrackState, state) -PROXY_SECONDARY_CONSTMETHOD0(bool, enabled) -PROXY_SECONDARY_METHOD1(bool, set_enabled, bool) -PROXY_SECONDARY_CONSTMETHOD0(ContentHint, content_hint) -PROXY_SECONDARY_METHOD1(void, set_content_hint, ContentHint) +PROXY_CONSTMETHOD0(bool, enabled) +PROXY_METHOD1(bool, set_enabled, bool) +PROXY_CONSTMETHOD0(ContentHint, content_hint) +PROXY_METHOD1(void, set_content_hint, ContentHint) PROXY_SECONDARY_METHOD2(void, AddOrUpdateSink, rtc::VideoSinkInterface*, const rtc::VideoSinkWants&) PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface*) +PROXY_SECONDARY_METHOD0(void, RequestRefreshFrame) BYPASS_PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) diff --git a/pc/media_stream_unittest.cc b/pc/media_stream_unittest.cc index 55226992e0..1d6935690e 100644 --- a/pc/media_stream_unittest.cc +++ b/pc/media_stream_unittest.cc @@ -12,8 +12,6 @@ #include -#include - #include "pc/audio_track.h" #include "pc/test/fake_video_track_source.h" #include "pc/video_track.h" @@ -63,7 +61,7 @@ class MediaStreamTest : public ::testing::Test { ASSERT_TRUE(video_track_.get() != NULL); EXPECT_EQ(MediaStreamTrackInterface::kLive, video_track_->state()); - audio_track_ = AudioTrack::Create(kAudioTrackId, NULL); + audio_track_ = AudioTrack::Create(kAudioTrackId, nullptr); ASSERT_TRUE(audio_track_.get() != NULL); EXPECT_EQ(MediaStreamTrackInterface::kLive, audio_track_->state()); diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 0c6b175721..1cc9607d01 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -16,21 +16,22 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "api/jsep_ice_candidate.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" -#include "api/task_queue/queued_task.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/uma_metrics.h" #include "api/video/video_codec_constants.h" #include "call/audio_state.h" #include "call/packet_receiver.h" #include "media/base/media_channel.h" #include "media/base/media_config.h" +#include "media/base/media_engine.h" #include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -45,7 +46,9 @@ #include "pc/channel.h" #include "pc/ice_server_parsing.h" #include "pc/rtp_receiver.h" +#include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" +#include "pc/rtp_sender_proxy.h" #include "pc/sctp_transport.h" #include "pc/simulcast_description.h" #include "pc/webrtc_session_description_factory.h" @@ -54,6 +57,7 @@ #include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/net_helper.h" +#include "rtc_base/network.h" #include "rtc_base/network_constants.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/socket_address.h" @@ -103,7 +107,7 @@ uint32_t ConvertIceTransportTypeToCandidateFilter( case PeerConnectionInterface::kAll: return cricket::CF_ALL; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return cricket::CF_NONE; } @@ -221,7 +225,7 @@ cricket::IceConfig ParseIceConfig( gathering_policy = cricket::GATHER_CONTINUALLY; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); gathering_policy = cricket::GATHER_ONCE; } @@ -270,15 +274,8 @@ bool HasRtcpMuxEnabled(const cricket::ContentInfo* content) { bool DtlsEnabled(const PeerConnectionInterface::RTCConfiguration& configuration, const PeerConnectionFactoryInterface::Options& options, const PeerConnectionDependencies& dependencies) { - if (options.disable_encryption) - return false; - - // Enable DTLS by default if we have an identity store or a certificate. - bool default_enabled = - (dependencies.cert_generator || !configuration.certificates.empty()); - - // The `configuration` can override the default value. - return configuration.enable_dtls_srtp.value_or(default_enabled); + // RingRTC change to always disable DTLS. + return false; } } // namespace @@ -298,7 +295,6 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( bool disable_ipv6_on_wifi; int max_ipv6_networks; bool disable_link_local_networks; - bool enable_rtp_data_channel; absl::optional screencast_min_bitrate; absl::optional combined_audio_video_bwe; absl::optional enable_dtls_srtp; @@ -339,6 +335,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( absl::optional stable_writable_connection_ping_interval_ms; webrtc::VpnPreference vpn_preference; std::vector vpn_list; + PortAllocatorConfig port_allocator_config; }; static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), "Did you add something to RTCConfiguration and forget to " @@ -401,7 +398,10 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( report_usage_pattern_delay_ms == o.report_usage_pattern_delay_ms && stable_writable_connection_ping_interval_ms == o.stable_writable_connection_ping_interval_ms && - vpn_preference == o.vpn_preference && vpn_list == o.vpn_list; + vpn_preference == o.vpn_preference && vpn_list == o.vpn_list && + port_allocator_config.min_port == o.port_allocator_config.min_port && + port_allocator_config.max_port == o.port_allocator_config.max_port && + port_allocator_config.flags == o.port_allocator_config.flags; } bool PeerConnectionInterface::RTCConfiguration::operator!=( @@ -416,6 +416,12 @@ RTCErrorOr> PeerConnection::Create( std::unique_ptr call, const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies) { + // TODO(https://crbug.com/webrtc/13528): Remove support for kPlanB. + if (configuration.sdp_semantics == SdpSemantics::kPlanB_DEPRECATED) { + RTC_LOG(LS_WARNING) + << "PeerConnection constructed with legacy SDP semantics!"; + } + RTCError config_error = cricket::P2PTransportChannel::ValidateIceConfig( ParseIceConfig(configuration)); if (!config_error.ok()) { @@ -553,6 +559,7 @@ PeerConnection::~PeerConnection() { // port_allocator_ and transport_controller_ live on the network thread and // should be destroyed there. + transport_controller_copy_ = nullptr; network_thread()->Invoke(RTC_FROM_HERE, [this] { RTC_DCHECK_RUN_ON(network_thread()); TeardownDataChannelTransport_n(); @@ -601,28 +608,28 @@ RTCError PeerConnection::Initialize( } // Network thread initialization. - network_thread()->Invoke(RTC_FROM_HERE, [this, &stun_servers, - &turn_servers, &configuration, - &dependencies] { - RTC_DCHECK_RUN_ON(network_thread()); - network_thread_safety_ = PendingTaskSafetyFlag::Create(); - InitializePortAllocatorResult pa_result = - InitializePortAllocator_n(stun_servers, turn_servers, configuration); - // Send information about IPv4/IPv6 status. - PeerConnectionAddressFamilyCounter address_family = - pa_result.enable_ipv6 ? kPeerConnection_IPv6 : kPeerConnection_IPv4; - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", address_family, - kPeerConnectionAddressFamilyCounter_Max); - InitializeTransportController_n(configuration, dependencies); - }); + transport_controller_copy_ = + network_thread()->Invoke(RTC_FROM_HERE, [&] { + RTC_DCHECK_RUN_ON(network_thread()); + network_thread_safety_ = PendingTaskSafetyFlag::Create(); + InitializePortAllocatorResult pa_result = InitializePortAllocator_n( + stun_servers, turn_servers, configuration); + // Send information about IPv4/IPv6 status. + PeerConnectionAddressFamilyCounter address_family = + pa_result.enable_ipv6 ? kPeerConnection_IPv6 : kPeerConnection_IPv4; + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", + address_family, + kPeerConnectionAddressFamilyCounter_Max); + return InitializeTransportController_n(configuration, dependencies); + }); configuration_ = configuration; stats_ = std::make_unique(this); stats_collector_ = RTCStatsCollector::Create(this); - sdp_handler_ = - SdpOfferAnswerHandler::Create(this, configuration, dependencies); + sdp_handler_ = SdpOfferAnswerHandler::Create(this, configuration, + dependencies, context_); rtp_manager_ = std::make_unique( IsUnifiedPlan(), signaling_thread(), worker_thread(), channel_manager(), @@ -636,11 +643,13 @@ RTCError PeerConnection::Initialize( rtp_manager()->transceivers()->Add( RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO, channel_manager()))); + rtc::make_ref_counted(cricket::MEDIA_TYPE_AUDIO, + channel_manager()))); rtp_manager()->transceivers()->Add( RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO, channel_manager()))); + rtc::make_ref_counted(cricket::MEDIA_TYPE_VIDEO, + channel_manager()))); } int delay_ms = configuration.report_usage_pattern_delay_ms @@ -653,10 +662,14 @@ RTCError PeerConnection::Initialize( }, delay_ms); + // Record the number of configured ICE servers for all connections. + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.IceServers.Configured", + configuration_.servers.size(), 0, 31, 32); + return RTCError::OK(); } -void PeerConnection::InitializeTransportController_n( +JsepTransportController* PeerConnection::InitializeTransportController_n( const RTCConfiguration& configuration, const PeerConnectionDependencies& dependencies) { JsepTransportController::Config config; @@ -692,6 +705,8 @@ void PeerConnection::InitializeTransportController_n( } }; + config.field_trials = &context_->trials(); + transport_controller_.reset( new JsepTransportController(network_thread(), port_allocator_.get(), async_dns_resolver_factory_.get(), config)); @@ -775,6 +790,7 @@ void PeerConnection::InitializeTransportController_n( }); transport_controller_->SetIceConfig(ParseIceConfig(configuration)); + return transport_controller_.get(); } rtc::scoped_refptr PeerConnection::local_streams() { @@ -840,12 +856,7 @@ RTCErrorOr> PeerConnection::AddTrack( return sender_or_error; } -bool PeerConnection::RemoveTrack(RtpSenderInterface* sender) { - TRACE_EVENT0("webrtc", "PeerConnection::RemoveTrack"); - return RemoveTrackNew(sender).ok(); -} - -RTCError PeerConnection::RemoveTrackNew( +RTCError PeerConnection::RemoveTrackOrError( rtc::scoped_refptr sender) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!sender) { @@ -901,9 +912,12 @@ PeerConnection::AddTransceiver( } RtpTransportInternal* PeerConnection::GetRtpTransport(const std::string& mid) { + // TODO(bugs.webrtc.org/9987): Avoid the thread jump. + // This might be done by caching the value on the signaling thread. RTC_DCHECK_RUN_ON(signaling_thread()); return network_thread()->Invoke( RTC_FROM_HERE, [this, &mid] { + RTC_DCHECK_RUN_ON(network_thread()); auto rtp_transport = transport_controller_->GetRtpTransport(mid); RTC_DCHECK(rtp_transport); return rtp_transport; @@ -1156,6 +1170,9 @@ bool PeerConnection::GetStats(StatsObserver* observer, RTC_LOG_THREAD_BLOCK_COUNT(); stats_->UpdateStats(level); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(4); + // The StatsCollector is used to tell if a track is valid because it may // remember tracks that the PeerConnection previously removed. if (track && !stats_->IsValidTrack(track->id())) { @@ -1174,7 +1191,8 @@ void PeerConnection::GetStats(RTCStatsCollectorCallback* callback) { RTC_DCHECK(stats_collector_); RTC_DCHECK(callback); RTC_LOG_THREAD_BLOCK_COUNT(); - stats_collector_->GetStatsReport(callback); + stats_collector_->GetStatsReport( + rtc::scoped_refptr(callback)); } void PeerConnection::GetStats( @@ -1457,7 +1475,7 @@ RTCError PeerConnection::SetConfiguration( RTCErrorType parse_error = ParseIceServers(configuration.servers, &stun_servers, &turn_servers); if (parse_error != RTCErrorType::NONE) { - return RTCError(parse_error); + return RTCError(parse_error, "ICE server parse failed"); } // Add the turn logging id to all turn servers for (cricket::RelayServerConfig& turn_server : turn_servers) { @@ -1489,6 +1507,7 @@ RTCError PeerConnection::SetConfiguration( RTC_FROM_HERE, [this, needs_ice_restart, &ice_config, &stun_servers, &turn_servers, &modified_config, has_local_description] { + RTC_DCHECK_RUN_ON(network_thread()); // As described in JSEP, calling setConfiguration with new ICE // servers or candidate policy must set a "needs-ice-restart" bit so // that the next offer triggers an ICE restart which will pick up @@ -1511,9 +1530,12 @@ RTCError PeerConnection::SetConfiguration( if (configuration_.active_reset_srtp_params != modified_config.active_reset_srtp_params) { - // TODO(tommi): move to the network thread - this hides an invoke. - transport_controller_->SetActiveResetSrtpParams( - modified_config.active_reset_srtp_params); + // TODO(tommi): merge invokes + network_thread()->Invoke(RTC_FROM_HERE, [this, &modified_config] { + RTC_DCHECK_RUN_ON(network_thread()); + transport_controller_->SetActiveResetSrtpParams( + modified_config.active_reset_srtp_params); + }); } if (modified_config.allow_codec_switching.has_value()) { @@ -1640,6 +1662,10 @@ void PeerConnection::AddAdaptationResource( call_->AddAdaptationResource(resource); } +cricket::ChannelManager* PeerConnection::channel_manager() { + return context_->channel_manager(); +} + bool PeerConnection::StartRtcEventLog(std::unique_ptr output, int64_t output_period_ms) { return worker_thread()->Invoke( @@ -1672,7 +1698,13 @@ PeerConnection::LookupDtlsTransportByMid(const std::string& mid) { rtc::scoped_refptr PeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_->LookupDtlsTransportByMid(mid); + // TODO(bugs.webrtc.org/9987): Avoid the thread jump. + // This might be done by caching the value on the signaling thread. + return network_thread()->Invoke>( + RTC_FROM_HERE, [this, mid]() { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_->LookupDtlsTransportByMid(mid); + }); } rtc::scoped_refptr PeerConnection::GetSctpTransport() @@ -1772,6 +1804,7 @@ void PeerConnection::Close() { // TODO(tommi): ^^ That's not exactly optimal since this is yet another // blocking hop to the network thread during Close(). Further still, the // voice/video/data channels will be cleared on the worker thread. + RTC_DCHECK_RUN_ON(network_thread()); transport_controller_.reset(); port_allocator_->DiscardCandidatePool(); if (network_thread_safety_) { @@ -1897,6 +1930,10 @@ void PeerConnection::SetConnectionState( } RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.ProvisionalAnswer", pranswer, kProvisionalAnswerMax); + + // Record the number of configured ICE servers for connected connections. + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.IceServers.Connected", + configuration_.servers.size(), 0, 31, 32); } } @@ -1927,8 +1964,6 @@ void PeerConnection::OnIceCandidateError(const std::string& address, return; } Observer()->OnIceCandidateError(address, port, url, error_code, error_text); - // Leftover not to break wpt test during migration to the new API. - Observer()->OnIceCandidateError(address + ":", url, error_code, error_text); } void PeerConnection::OnIceCandidatesRemoved( @@ -2078,10 +2113,6 @@ bool PeerConnection::ReconfigurePortAllocator_n( stun_candidate_keepalive_interval); } -cricket::ChannelManager* PeerConnection::channel_manager() const { - return context_->channel_manager(); -} - bool PeerConnection::StartRtcEventLog_w( std::unique_ptr output, int64_t output_period_ms) { @@ -2099,11 +2130,10 @@ void PeerConnection::StopRtcEventLog_w() { } } -cricket::ChannelInterface* PeerConnection::GetChannel( - const std::string& content_name) { +cricket::ChannelInterface* PeerConnection::GetChannel(const std::string& mid) { for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->content_name() == content_name) { + if (channel && channel->mid() == mid) { return channel; } } @@ -2126,13 +2156,26 @@ bool PeerConnection::GetSctpSslRole(rtc::SSLRole* role) { absl::optional dtls_role; if (sctp_mid_s_) { - dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_s_); + dtls_role = network_thread()->Invoke>( + RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_->GetDtlsRole(*sctp_mid_n_); + }); if (!dtls_role && sdp_handler_->is_caller().has_value()) { + // This works fine if we are the offerer, but can be a mistake if + // we are the answerer and the remote offer is ACTIVE. In that + // case, we will guess the role wrong. + // TODO(bugs.webrtc.org/13668): Check if this actually happens. + RTC_LOG(LS_ERROR) + << "Possible risk: DTLS role guesser is active, is_caller is " + << *sdp_handler_->is_caller(); dtls_role = *sdp_handler_->is_caller() ? rtc::SSL_SERVER : rtc::SSL_CLIENT; } - *role = *dtls_role; - return true; + if (dtls_role) { + *role = *dtls_role; + return true; + } } return false; } @@ -2147,7 +2190,11 @@ bool PeerConnection::GetSslRole(const std::string& content_name, return false; } - auto dtls_role = transport_controller_->GetDtlsRole(content_name); + auto dtls_role = network_thread()->Invoke>( + RTC_FROM_HERE, [this, content_name]() { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_->GetDtlsRole(content_name); + }); if (dtls_role) { *role = *dtls_role; return true; @@ -2178,7 +2225,7 @@ std::vector PeerConnection::GetDataChannelStats() const { absl::optional PeerConnection::sctp_transport_name() const { RTC_DCHECK_RUN_ON(signaling_thread()); - if (sctp_mid_s_ && transport_controller_) + if (sctp_mid_s_ && transport_controller_copy_) return sctp_transport_name_s_; return absl::optional(); } @@ -2291,7 +2338,7 @@ void PeerConnection::OnTransportControllerConnectionState( NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -2527,7 +2574,7 @@ void PeerConnection::ReportSdpBundleUsage( } else { usage = kBundleUsageEmpty; } - } else if (configuration_.sdp_semantics == SdpSemantics::kPlanB) { + } else if (configuration_.sdp_semantics == SdpSemantics::kPlanB_DEPRECATED) { // In plan-b, simple/complex usage will not show up in the number of // m-lines or BUNDLE. usage = using_bundle ? kBundleUsageBundlePlanB : kBundleUsageNoBundlePlanB; @@ -2643,7 +2690,7 @@ void PeerConnection::OnTransportControllerGatheringState( OnIceGatheringChange(PeerConnectionInterface::kIceGatheringNew); } else { RTC_LOG(LS_ERROR) << "Unknown state received: " << state; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -2655,8 +2702,8 @@ void PeerConnection::ReportTransportStats() { media_types_by_transport_name; for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { if (transceiver->internal()->channel()) { - const std::string& transport_name = - transceiver->internal()->channel()->transport_name(); + std::string transport_name( + transceiver->internal()->channel()->transport_name()); media_types_by_transport_name[transport_name].insert( transceiver->media_type()); } @@ -2767,7 +2814,7 @@ void PeerConnection::ReportNegotiatedCiphers( rtc::kSrtpCryptoSuiteMaxValue); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); continue; } } @@ -2792,7 +2839,7 @@ void PeerConnection::ReportNegotiatedCiphers( rtc::kSslCipherSuiteMaxValue); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); continue; } } @@ -2816,7 +2863,8 @@ bool PeerConnection::OnTransportChanged( if (dtls_transport) { signaling_thread()->PostTask(ToQueuedTask( signaling_thread_safety_.flag(), - [this, name = dtls_transport->internal()->transport_name()] { + [this, + name = std::string(dtls_transport->internal()->transport_name())] { RTC_DCHECK_RUN_ON(signaling_thread()); sctp_transport_name_s_ = std::move(name); })); @@ -2843,7 +2891,7 @@ void PeerConnection::StartSctpTransport(int local_port, network_thread_safety_, [this, mid = *sctp_mid_s_, local_port, remote_port, max_message_size] { rtc::scoped_refptr sctp_transport = - transport_controller()->GetSctpTransport(mid); + transport_controller_n()->GetSctpTransport(mid); if (sctp_transport) sctp_transport->Start(local_port, remote_port, max_message_size); })); @@ -2871,6 +2919,7 @@ bool PeerConnection::ShouldFireNegotiationNeededEvent(uint32_t event_id) { } void PeerConnection::RequestUsagePatternReportForTesting() { + RTC_DCHECK_RUN_ON(signaling_thread()); message_handler_.RequestUsagePatternReport( [this]() { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -2909,16 +2958,19 @@ bool PeerConnection::UseSharedIceGatherer( } bool PeerConnection::SetIncomingRtpEnabled(bool enabled) { - return transport_controller_->SetIncomingRtpEnabled(enabled); + return network_thread()->Invoke(RTC_FROM_HERE, [this, enabled] { + JsepTransportController* transport_controller = this->transport_controller_n(); + return transport_controller->SetIncomingRtpEnabled(enabled); + }); } bool PeerConnection::SendRtp(std::unique_ptr rtp_packet) { RTC_DCHECK_RUN_ON(signaling_thread()); - JsepTransportController* transport_controller = this->transport_controller(); // Is there a better way to std::move the unique_ptr? RtpPacket* raw_rtp_packet = rtp_packet.release(); - return network_thread()->Invoke(RTC_FROM_HERE, [transport_controller, raw_rtp_packet] { + return network_thread()->Invoke(RTC_FROM_HERE, [this, raw_rtp_packet] { + JsepTransportController* transport_controller = this->transport_controller_n(); RtpTransportInternal* rtp_transport = transport_controller->GetBundledRtpTransport(); if (!rtp_transport) { return false; @@ -2936,13 +2988,11 @@ bool PeerConnection::SendRtp(std::unique_ptr rtp_packet) { } bool PeerConnection::ReceiveRtp(uint8_t pt) { - RTC_DCHECK_RUN_ON(signaling_thread()); - - JsepTransportController* transport_controller = this->transport_controller(); RtpDemuxerCriteria demux_criteria; - demux_criteria.payload_types.insert(pt); + demux_criteria.payload_types().insert(pt); RtpPacketSinkInterface* sink = Observer(); - return network_thread()->Invoke(RTC_FROM_HERE, [transport_controller, demux_criteria, sink] { + return network_thread()->Invoke(RTC_FROM_HERE, [this, demux_criteria, sink] { + JsepTransportController* transport_controller = this->transport_controller_n(); RtpTransportInternal* rtp_transport = transport_controller->GetBundledRtpTransport(); if (!rtp_transport) { return false; diff --git a/pc/peer_connection.h b/pc/peer_connection.h index 1e07388964..afc6bf1668 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -18,14 +18,11 @@ #include #include #include -#include #include #include "absl/types/optional.h" #include "api/adaptation/resource.h" #include "api/async_dns_resolver.h" -#include "api/async_resolver_factory.h" -#include "api/audio_options.h" #include "api/candidate.h" #include "api/crypto/crypto_options.h" #include "api/data_channel_interface.h" @@ -34,12 +31,10 @@ #include "api/jsep.h" #include "api/media_stream_interface.h" #include "api/media_types.h" -#include "api/packet_socket_factory.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log_output.h" -#include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" #include "api/rtp_transceiver_interface.h" @@ -53,15 +48,12 @@ #include "api/transport/data_channel_transport_interface.h" #include "api/transport/enums.h" #include "api/turn_customizer.h" -#include "api/video/video_bitrate_allocator_factory.h" +#include "api/webrtc_key_value_config.h" #include "call/call.h" -#include "media/base/media_channel.h" -#include "media/base/media_engine.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/transport_description.h" -#include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/channel_manager.h" #include "pc/connection_context.h" @@ -72,23 +64,18 @@ #include "pc/peer_connection_internal.h" #include "pc/peer_connection_message_handler.h" #include "pc/rtc_stats_collector.h" -#include "pc/rtp_receiver.h" -#include "pc/rtp_sender.h" #include "pc/rtp_transceiver.h" #include "pc/rtp_transmission_manager.h" #include "pc/rtp_transport_internal.h" #include "pc/sctp_data_channel.h" -#include "pc/sctp_transport.h" #include "pc/sdp_offer_answer.h" #include "pc/session_description.h" #include "pc/stats_collector.h" -#include "pc/stream_collection.h" #include "pc/transceiver_list.h" #include "pc/transport_stats.h" #include "pc/usage_pattern.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/network/sent_packet.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" @@ -96,7 +83,7 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" -#include "rtc_base/unique_id_generator.h" +#include "rtc_base/weak_ptr.h" namespace webrtc { @@ -115,8 +102,7 @@ namespace webrtc { // - The ICE state machine. // - Generating stats. class PeerConnection : public PeerConnectionInternal, - public JsepTransportController::Observer, - public sigslot::has_slots<> { + public JsepTransportController::Observer { public: // Creates a PeerConnection and initializes it with the given values. // If the initialization fails, the function releases the PeerConnection @@ -140,8 +126,7 @@ class PeerConnection : public PeerConnectionInternal, RTCErrorOr> AddTrack( rtc::scoped_refptr track, const std::vector& stream_ids) override; - bool RemoveTrack(RtpSenderInterface* sender) override; - RTCError RemoveTrackNew( + RTCError RemoveTrackOrError( rtc::scoped_refptr sender) override; RTCErrorOr> AddTransceiver( @@ -186,6 +171,9 @@ class PeerConnection : public PeerConnectionInternal, SignalingState signaling_state() override; IceConnectionState ice_connection_state() override; + IceConnectionState ice_connection_state_internal() override { + return ice_connection_state(); + } IceConnectionState standardized_ice_connection_state() override; PeerConnectionState peer_connection_state() override; IceGatheringState ice_gathering_state() override; @@ -281,7 +269,6 @@ class PeerConnection : public PeerConnectionInternal, return context_->signaling_thread(); } - // PeerConnectionInternal implementation. rtc::Thread* network_thread() const final { return context_->network_thread(); } @@ -293,7 +280,7 @@ class PeerConnection : public PeerConnectionInternal, bool initial_offerer() const override { RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_ && transport_controller_->initial_offerer(); + return sdp_handler_->initial_offerer(); } std::vector< @@ -304,6 +291,7 @@ class PeerConnection : public PeerConnectionInternal, } sigslot::signal1& SignalSctpDataChannelCreated() override { + RTC_DCHECK_RUN_ON(signaling_thread()); return data_channel_controller_.SignalSctpDataChannelCreated(); } @@ -327,73 +315,81 @@ class PeerConnection : public PeerConnectionInternal, bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) override; // Functions needed by DataChannelController - void NoteDataAddedEvent() { NoteUsageEvent(UsageEvent::DATA_ADDED); } + void NoteDataAddedEvent() override { NoteUsageEvent(UsageEvent::DATA_ADDED); } // Returns the observer. Will crash on CHECK if the observer is removed. - PeerConnectionObserver* Observer() const; - bool IsClosed() const { + PeerConnectionObserver* Observer() const override; + bool IsClosed() const override { RTC_DCHECK_RUN_ON(signaling_thread()); return !sdp_handler_ || sdp_handler_->signaling_state() == PeerConnectionInterface::kClosed; } // Get current SSL role used by SCTP's underlying transport. - bool GetSctpSslRole(rtc::SSLRole* role); + bool GetSctpSslRole(rtc::SSLRole* role) override; // Handler for the "channel closed" signal - void OnSctpDataChannelClosed(DataChannelInterface* channel); + void OnSctpDataChannelClosed(DataChannelInterface* channel) override; bool ShouldFireNegotiationNeededEvent(uint32_t event_id) override; // Functions needed by SdpOfferAnswerHandler - StatsCollector* stats() { + StatsCollector* stats() override { RTC_DCHECK_RUN_ON(signaling_thread()); return stats_.get(); } - DataChannelController* data_channel_controller() { + DataChannelController* data_channel_controller() override { RTC_DCHECK_RUN_ON(signaling_thread()); return &data_channel_controller_; } - bool dtls_enabled() const { + bool dtls_enabled() const override { RTC_DCHECK_RUN_ON(signaling_thread()); return dtls_enabled_; } - const PeerConnectionInterface::RTCConfiguration* configuration() const { + const PeerConnectionInterface::RTCConfiguration* configuration() + const override { RTC_DCHECK_RUN_ON(signaling_thread()); return &configuration_; } - PeerConnectionMessageHandler* message_handler() { + PeerConnectionMessageHandler* message_handler() override { RTC_DCHECK_RUN_ON(signaling_thread()); return &message_handler_; } - RtpTransmissionManager* rtp_manager() { return rtp_manager_.get(); } - const RtpTransmissionManager* rtp_manager() const { + RtpTransmissionManager* rtp_manager() override { return rtp_manager_.get(); } + const RtpTransmissionManager* rtp_manager() const override { return rtp_manager_.get(); } - cricket::ChannelManager* channel_manager() const; + cricket::ChannelManager* channel_manager(); - JsepTransportController* transport_controller() { + JsepTransportController* transport_controller_s() override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_copy_; + } + JsepTransportController* transport_controller_n() override { + RTC_DCHECK_RUN_ON(network_thread()); return transport_controller_.get(); } - cricket::PortAllocator* port_allocator() { return port_allocator_.get(); } - Call* call_ptr() { return call_ptr_; } + cricket::PortAllocator* port_allocator() override { + return port_allocator_.get(); + } + Call* call_ptr() override { return call_ptr_; } ConnectionContext* context() { return context_.get(); } - const PeerConnectionFactoryInterface::Options* options() const { + const PeerConnectionFactoryInterface::Options* options() const override { return &options_; } - void SetIceConnectionState(IceConnectionState new_state); - void NoteUsageEvent(UsageEvent event); + void SetIceConnectionState(IceConnectionState new_state) override; + void NoteUsageEvent(UsageEvent event) override; // Asynchronously adds a remote candidate on the network thread. void AddRemoteCandidate(const std::string& mid, - const cricket::Candidate& candidate); + const cricket::Candidate& candidate) override; // Report the UMA metric SdpFormatReceived for the given remote description. void ReportSdpFormatReceived( - const SessionDescriptionInterface& remote_description); + const SessionDescriptionInterface& remote_description) override; // Report the UMA metric BundleUsage for the given remote description. void ReportSdpBundleUsage( - const SessionDescriptionInterface& remote_description); + const SessionDescriptionInterface& remote_description) override; // Returns true if the PeerConnection is configured to use Unified Plan // semantics for creating offers/answers and setting local/remote @@ -401,34 +397,34 @@ class PeerConnection : public PeerConnectionInternal, // to the user. If this is false, Plan B semantics are assumed. // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once // sufficient time has passed. - bool IsUnifiedPlan() const { + bool IsUnifiedPlan() const override { RTC_DCHECK_RUN_ON(signaling_thread()); return is_unified_plan_; } bool ValidateBundleSettings( const cricket::SessionDescription* desc, const std::map& - bundle_groups_by_mid); + bundle_groups_by_mid) override; // Returns the MID for the data section associated with the // SCTP data channel, if it has been set. If no data // channels are configured this will return nullopt. - absl::optional GetDataMid() const; + absl::optional GetDataMid() const override; - void SetSctpDataMid(const std::string& mid); + void SetSctpDataMid(const std::string& mid) override; - void ResetSctpDataMid(); + void ResetSctpDataMid() override; // Asynchronously calls SctpTransport::Start() on the network thread for // `sctp_mid()` if set. Called as part of setting the local description. void StartSctpTransport(int local_port, int remote_port, - int max_message_size); + int max_message_size) override; // Returns the CryptoOptions for this PeerConnection. This will always // return the RTCConfiguration.crypto_options if set and will only default // back to the PeerConnectionFactory settings if nothing was set. - CryptoOptions GetCryptoOptions(); + CryptoOptions GetCryptoOptions() override; // Internal implementation for AddTransceiver family of methods. If // `fire_callback` is set, fires OnRenegotiationNeeded callback if successful. @@ -436,19 +432,19 @@ class PeerConnection : public PeerConnectionInternal, cricket::MediaType media_type, rtc::scoped_refptr track, const RtpTransceiverInit& init, - bool fire_callback = true); + bool fire_callback = true) override; // Returns rtp transport, result can not be nullptr. RtpTransportInternal* GetRtpTransport(const std::string& mid); // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by // this session. - bool SrtpRequired() const; + bool SrtpRequired() const override; - bool SetupDataChannelTransport_n(const std::string& mid) + bool SetupDataChannelTransport_n(const std::string& mid) override RTC_RUN_ON(network_thread()); - void TeardownDataChannelTransport_n() RTC_RUN_ON(network_thread()); - cricket::ChannelInterface* GetChannel(const std::string& content_name) + void TeardownDataChannelTransport_n() override RTC_RUN_ON(network_thread()); + cricket::ChannelInterface* GetChannel(const std::string& mid) RTC_RUN_ON(network_thread()); // Functions made public for testing. @@ -458,7 +454,9 @@ class PeerConnection : public PeerConnectionInternal, } void RequestUsagePatternReportForTesting(); - rtc::scoped_refptr shared_ice_gatherer() { + const WebRtcKeyValueConfig& trials() override { return context_->trials(); } + + rtc::scoped_refptr shared_ice_gatherer() override { return shared_ice_gatherer_; } @@ -478,7 +476,7 @@ class PeerConnection : public PeerConnectionInternal, RTCError Initialize( const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies); - void InitializeTransportController_n( + JsepTransportController* InitializeTransportController_n( const RTCConfiguration& configuration, const PeerConnectionDependencies& dependencies) RTC_RUN_ON(network_thread()); @@ -679,9 +677,14 @@ class PeerConnection : public PeerConnectionInternal, const std::string session_id_; - std::unique_ptr - transport_controller_; // TODO(bugs.webrtc.org/9987): Accessed on both - // signaling and network thread. + // The transport controller is set and used on the network thread. + // Some functions pass the value of the transport_controller_ pointer + // around as arguments while running on the signaling thread; these + // use the transport_controller_copy. + std::unique_ptr transport_controller_ + RTC_GUARDED_BY(network_thread()); + JsepTransportController* transport_controller_copy_ + RTC_GUARDED_BY(signaling_thread()) = nullptr; // `sctp_mid_` is the content name (MID) in SDP. // Note: this is used as the data channel MID by both SCTP and data channel @@ -696,7 +699,7 @@ class PeerConnection : public PeerConnectionInternal, // The machinery for handling offers and answers. Const after initialization. std::unique_ptr sdp_handler_ - RTC_GUARDED_BY(signaling_thread()); + RTC_GUARDED_BY(signaling_thread()) RTC_PT_GUARDED_BY(signaling_thread()); const bool dtls_enabled_; @@ -704,21 +707,25 @@ class PeerConnection : public PeerConnectionInternal, bool return_histogram_very_quickly_ RTC_GUARDED_BY(signaling_thread()) = false; + // The DataChannelController is accessed from both the signaling thread + // and networking thread. It is a thread-aware object. DataChannelController data_channel_controller_; // Machinery for handling messages posted to oneself - PeerConnectionMessageHandler message_handler_; + PeerConnectionMessageHandler message_handler_ + RTC_GUARDED_BY(signaling_thread()); // Administration of senders, receivers and transceivers // Accessed on both signaling and network thread. Const after Initialize(). std::unique_ptr rtp_manager_; - rtc::WeakPtrFactory weak_factory_; - // Did the connectionState ever change to `connected`? // Used to gather metrics only the first such state change. bool was_ever_connected_ RTC_GUARDED_BY(signaling_thread()) = false; + // This variable needs to be the last one in the class. + rtc::WeakPtrFactory weak_factory_; + // RingRTC change to add ICE forking rtc::scoped_refptr shared_ice_gatherer_; }; diff --git a/pc/peer_connection_adaptation_integrationtest.cc b/pc/peer_connection_adaptation_integrationtest.cc index dfb12971b4..b5a5f5231d 100644 --- a/pc/peer_connection_adaptation_integrationtest.cc +++ b/pc/peer_connection_adaptation_integrationtest.cc @@ -8,12 +8,22 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/adaptation/resource.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/video/video_source_interface.h" #include "call/adaptation/test/fake_resource.h" #include "pc/test/fake_periodic_video_source.h" #include "pc/test/fake_periodic_video_track_source.h" @@ -22,6 +32,7 @@ #include "rtc_base/gunit.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/thread.h" +#include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "test/gtest.h" diff --git a/pc/peer_connection_bundle_unittest.cc b/pc/peer_connection_bundle_unittest.cc index be05af920c..4a5d373310 100644 --- a/pc/peer_connection_bundle_unittest.cc +++ b/pc/peer_connection_bundle_unittest.cc @@ -8,21 +8,59 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/candidate.h" #include "api/create_peerconnection_factory.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/stats/rtc_stats.h" +#include "api/stats/rtc_stats_report.h" +#include "api/stats/rtcstats_objects.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "p2p/base/fake_port_allocator.h" -#include "p2p/base/test_stun_server.h" +#include "media/base/stream_params.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" #include "p2p/client/basic_port_allocator.h" -#include "pc/media_session.h" +#include "pc/channel.h" #include "pc/peer_connection.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" +#include "pc/rtp_transceiver.h" +#include "pc/rtp_transport_internal.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/network.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -80,7 +118,7 @@ class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper { return pc()->AddIceCandidate(jsep_candidate.get()); } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } diff --git a/pc/peer_connection_crypto_unittest.cc b/pc/peer_connection_crypto_unittest.cc index 394203cb02..1741b99289 100644 --- a/pc/peer_connection_crypto_unittest.cc +++ b/pc/peer_connection_crypto_unittest.cc @@ -8,17 +8,47 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" +#include "api/crypto/crypto_options.h" +#include "api/crypto_params.h" +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif diff --git a/pc/peer_connection_data_channel_unittest.cc b/pc/peer_connection_data_channel_unittest.cc index 2544473536..0fb1c638eb 100644 --- a/pc/peer_connection_data_channel_unittest.cc +++ b/pc/peer_connection_data_channel_unittest.cc @@ -20,24 +20,24 @@ #include "api/media_types.h" #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" #include "api/task_queue/default_task_queue_factory.h" -#include "media/base/codec.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/sctp_transport_factory_interface.h" #include "media/base/fake_media_engine.h" -#include "media/base/media_constants.h" #include "media/base/media_engine.h" -#include "media/sctp/sctp_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port_allocator.h" #include "pc/media_session.h" #include "pc/peer_connection.h" -#include "pc/peer_connection_factory.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" +#include "pc/sctp_transport.h" #include "pc/sdp_utils.h" #include "pc/session_description.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/checks.h" -#include "rtc_base/ref_counted_object.h" +#include "rtc_base/logging.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/thread.h" #include "test/gmock.h" @@ -280,15 +280,6 @@ TEST_P(PeerConnectionDataChannelTest, answer->description()->GetTransportInfoByName(data_content->name)); } -TEST_P(PeerConnectionDataChannelTest, - CreateDataChannelWithDtlsDisabledSucceeds) { - RTCConfiguration config; - config.enable_dtls_srtp.emplace(false); - auto caller = CreatePeerConnection(); - - EXPECT_TRUE(caller->pc()->CreateDataChannel("dc", nullptr)); -} - TEST_P(PeerConnectionDataChannelTest, SctpPortPropagatedFromSdpToTransport) { constexpr int kNewSendPort = 9998; constexpr int kNewRecvPort = 7775; diff --git a/pc/peer_connection_end_to_end_unittest.cc b/pc/peer_connection_end_to_end_unittest.cc index 4ef4c832bb..78dcda3202 100644 --- a/pc/peer_connection_end_to_end_unittest.cc +++ b/pc/peer_connection_end_to_end_unittest.cc @@ -8,19 +8,46 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + +#include +#include #include +#include +#include +#include +#include #include "absl/strings/match.h" +#include "absl/types/optional.h" #include "api/audio_codecs/L16/audio_decoder_L16.h" #include "api/audio_codecs/L16/audio_encoder_L16.h" #include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus_audio_decoder_factory.h" #include "api/audio_codecs/opus_audio_encoder_factory.h" +#include "api/audio_options.h" +#include "api/data_channel_interface.h" +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" #include "media/sctp/sctp_transport_internal.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" -#include "rtc_base/logging.h" +#include "rtc_base/physical_socket_server.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" +#include "test/gmock.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -54,14 +81,14 @@ class PeerConnectionEndToEndBaseTest : public sigslot::has_slots<>, public: typedef std::vector> DataChannelList; - explicit PeerConnectionEndToEndBaseTest(SdpSemantics sdp_semantics) { - network_thread_ = rtc::Thread::CreateWithSocketServer(); - worker_thread_ = rtc::Thread::Create(); + explicit PeerConnectionEndToEndBaseTest(SdpSemantics sdp_semantics) + : network_thread_(std::make_unique(&pss_)), + worker_thread_(rtc::Thread::Create()) { RTC_CHECK(network_thread_->Start()); RTC_CHECK(worker_thread_->Start()); - caller_ = new rtc::RefCountedObject( + caller_ = rtc::make_ref_counted( "caller", network_thread_.get(), worker_thread_.get()); - callee_ = new rtc::RefCountedObject( + callee_ = rtc::make_ref_counted( "callee", network_thread_.get(), worker_thread_.get()); webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = "stun:stun.l.google.com:19302"; @@ -125,11 +152,13 @@ class PeerConnectionEndToEndBaseTest : public sigslot::has_slots<>, } void OnCallerAddedDataChanel(DataChannelInterface* dc) { - caller_signaled_data_channels_.push_back(dc); + caller_signaled_data_channels_.push_back( + rtc::scoped_refptr(dc)); } void OnCalleeAddedDataChannel(DataChannelInterface* dc) { - callee_signaled_data_channels_.push_back(dc); + callee_signaled_data_channels_.push_back( + rtc::scoped_refptr(dc)); } // Tests that `dc1` and `dc2` can send to and receive from each other. @@ -191,6 +220,7 @@ class PeerConnectionEndToEndBaseTest : public sigslot::has_slots<>, } protected: + rtc::PhysicalSocketServer pss_; std::unique_ptr network_thread_; std::unique_ptr worker_thread_; rtc::scoped_refptr caller_; @@ -256,7 +286,7 @@ rtc::scoped_refptr CreateForwardingMockDecoderFactory( webrtc::AudioDecoderFactory* real_decoder_factory) { rtc::scoped_refptr mock_decoder_factory = - new rtc::RefCountedObject>; + rtc::make_ref_counted>(); EXPECT_CALL(*mock_decoder_factory, GetSupportedDecoders()) .Times(AtLeast(1)) .WillRepeatedly(Invoke([real_decoder_factory] { @@ -430,26 +460,22 @@ TEST_P(PeerConnectionEndToEndTest, CallWithCustomCodec) { std::vector encoder_id1, encoder_id2, decoder_id1, decoder_id2; - CreatePcs(rtc::scoped_refptr( - new rtc::RefCountedObject( - webrtc::CreateAudioEncoderFactory< - AudioEncoderUnicornSparklesRainbow>(), - &encoder_id1)), - rtc::scoped_refptr( - new rtc::RefCountedObject( - webrtc::CreateAudioDecoderFactory< - AudioDecoderUnicornSparklesRainbow>(), - &decoder_id1)), - rtc::scoped_refptr( - new rtc::RefCountedObject( - webrtc::CreateAudioEncoderFactory< - AudioEncoderUnicornSparklesRainbow>(), - &encoder_id2)), - rtc::scoped_refptr( - new rtc::RefCountedObject( - webrtc::CreateAudioDecoderFactory< - AudioDecoderUnicornSparklesRainbow>(), - &decoder_id2))); + CreatePcs(rtc::make_ref_counted( + webrtc::CreateAudioEncoderFactory< + AudioEncoderUnicornSparklesRainbow>(), + &encoder_id1), + rtc::make_ref_counted( + webrtc::CreateAudioDecoderFactory< + AudioDecoderUnicornSparklesRainbow>(), + &decoder_id1), + rtc::make_ref_counted( + webrtc::CreateAudioEncoderFactory< + AudioEncoderUnicornSparklesRainbow>(), + &encoder_id2), + rtc::make_ref_counted( + webrtc::CreateAudioDecoderFactory< + AudioDecoderUnicornSparklesRainbow>(), + &decoder_id2)); GetAndAddUserMedia(); Negotiate(); WaitForCallEstablished(); diff --git a/pc/peer_connection_factory.cc b/pc/peer_connection_factory.cc index 35bed7536e..bec6f49ae9 100644 --- a/pc/peer_connection_factory.cc +++ b/pc/peer_connection_factory.cc @@ -10,7 +10,7 @@ #include "pc/peer_connection_factory.h" -#include +#include #include #include "absl/strings/match.h" @@ -105,7 +105,8 @@ PeerConnectionFactory::PeerConnectionFactory( transport_controller_send_factory_( (dependencies->transport_controller_send_factory) ? std::move(dependencies->transport_controller_send_factory) - : std::make_unique()) {} + : std::make_unique()), + metronome_(std::move(dependencies->metronome)) {} PeerConnectionFactory::PeerConnectionFactory( PeerConnectionFactoryDependencies dependencies) @@ -218,6 +219,11 @@ PeerConnectionFactory::CreatePeerConnectionOrError( dependencies.allocator = std::make_unique( context_->default_network_manager(), packet_socket_factory, configuration.turn_customizer); + dependencies.allocator->SetPortRange( + configuration.port_allocator_config.min_port, + configuration.port_allocator_config.max_port); + dependencies.allocator->set_flags( + configuration.port_allocator_config.flags); } if (!dependencies.async_resolver_factory) { @@ -279,7 +285,8 @@ rtc::scoped_refptr PeerConnectionFactory::CreateAudioTrack( const std::string& id, AudioSourceInterface* source) { RTC_DCHECK(signaling_thread()->IsCurrent()); - rtc::scoped_refptr track(AudioTrack::Create(id, source)); + rtc::scoped_refptr track( + AudioTrack::Create(id, rtc::scoped_refptr(source))); return AudioTrackProxy::Create(signaling_thread(), track); } @@ -343,6 +350,7 @@ std::unique_ptr PeerConnectionFactory::CreateCall_w( call_config.trials = &trials(); call_config.rtp_transport_controller_send_factory = transport_controller_send_factory_.get(); + call_config.metronome = metronome_.get(); return std::unique_ptr( context_->call_factory()->CreateCall(call_config)); } diff --git a/pc/peer_connection_factory.h b/pc/peer_connection_factory.h index 4946ec6ea2..f09ca66e6e 100644 --- a/pc/peer_connection_factory.h +++ b/pc/peer_connection_factory.h @@ -23,6 +23,7 @@ #include "api/fec_controller.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/metronome/metronome.h" #include "api/neteq/neteq_factory.h" #include "api/network_state_predictor.h" #include "api/peer_connection_interface.h" @@ -152,6 +153,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { std::unique_ptr neteq_factory_; const std::unique_ptr transport_controller_send_factory_; + std::unique_ptr metronome_; }; } // namespace webrtc diff --git a/pc/peer_connection_factory_unittest.cc b/pc/peer_connection_factory_unittest.cc index dd392c5ad2..4e97053fea 100644 --- a/pc/peer_connection_factory_unittest.cc +++ b/pc/peer_connection_factory_unittest.cc @@ -10,16 +10,10 @@ #include "pc/peer_connection_factory.h" -#include - -#include -#include #include #include #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" @@ -28,17 +22,18 @@ #include "api/media_stream_interface.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/fake_frame_source.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" #include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/fake_video_track_source.h" +#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" +#include "rtc_base/time_utils.h" #include "test/gtest.h" #ifdef WEBRTC_ANDROID @@ -190,6 +185,7 @@ TEST(PeerConnectionFactoryTestInternal, DISABLED_CreatePCUsingInternalModules) { NullPeerConnectionObserver observer; webrtc::PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; std::unique_ptr cert_generator( new FakeRTCCertificateGenerator()); @@ -269,6 +265,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverDataCapabilities) { // configuration. Also verifies the URL's parsed correctly as expected. TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServer; config.servers.push_back(ice_server); @@ -304,6 +301,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { // configuration. Also verifies the list of URL's parsed correctly as expected. TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.urls.push_back(kStunIceServer); ice_server.urls.push_back(kTurnIceServer); @@ -333,6 +331,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) { TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServer; config.servers.push_back(ice_server); @@ -357,6 +356,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) { // has transport parameter in it. TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kTurnIceServerWithTransport; ice_server.username = kTurnUsername; @@ -377,6 +377,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kSecureTurnIceServer; ice_server.username = kTurnUsername; @@ -414,6 +415,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServerWithIPv4Address; config.servers.push_back(ice_server); diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc index 8bf6c7ab44..d7d160b631 100644 --- a/pc/peer_connection_header_extension_unittest.cc +++ b/pc/peer_connection_header_extension_unittest.cc @@ -9,17 +9,38 @@ */ #include +#include #include +#include +#include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/call/call_factory_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" #include "p2p/base/fake_port_allocator.h" -#include "pc/media_session.h" +#include "p2p/base/port_allocator.h" #include "pc/peer_connection_wrapper.h" -#include "rtc_base/gunit.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread.h" #include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { diff --git a/pc/peer_connection_histogram_unittest.cc b/pc/peer_connection_histogram_unittest.cc index 8a1aa60700..ae88b65213 100644 --- a/pc/peer_connection_histogram_unittest.cc +++ b/pc/peer_connection_histogram_unittest.cc @@ -15,6 +15,7 @@ #include #include "absl/types/optional.h" +#include "api/async_resolver_factory.h" #include "api/call/call_factory_interface.h" #include "api/jsep.h" #include "api/jsep_session_description.h" @@ -22,7 +23,9 @@ #include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" #include "p2p/base/mock_async_resolver.h" #include "p2p/base/port_allocator.h" #include "p2p/client/basic_port_allocator.h" @@ -39,13 +42,14 @@ #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" #include "rtc_base/gunit.h" +#include "rtc_base/mdns_responder_interface.h" #include "rtc_base/ref_counted_object.h" -#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { @@ -243,8 +247,10 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { } WrapperPtr CreatePeerConnection() { + RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; return CreatePeerConnection( - RTCConfiguration(), PeerConnectionFactoryInterface::Options(), nullptr); + config, PeerConnectionFactoryInterface::Options(), nullptr); } WrapperPtr CreatePeerConnection(const RTCConfiguration& config) { @@ -275,6 +281,7 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { WrapperPtr CreatePeerConnectionWithImmediateReport() { RTCConfiguration configuration; + configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; configuration.report_usage_pattern_delay_ms = 0; return CreatePeerConnection( configuration, PeerConnectionFactoryInterface::Options(), nullptr); @@ -287,7 +294,9 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { auto port_allocator = std::make_unique(fake_network); - return CreatePeerConnection(RTCConfiguration(), + RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + return CreatePeerConnection(config, PeerConnectionFactoryInterface::Options(), std::move(port_allocator)); } @@ -300,7 +309,9 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { auto port_allocator = std::make_unique(fake_network); - return CreatePeerConnection(RTCConfiguration(), + RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + return CreatePeerConnection(config, PeerConnectionFactoryInterface::Options(), std::move(port_allocator)); } @@ -420,6 +431,7 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintAudioVideo) { // candidate. TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCaller) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; // Enable hostname candidates with mDNS names. auto caller = CreatePeerConnectionWithMdns(config); @@ -461,6 +473,7 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCaller) { // candidate. TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCallee) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; // Enable hostname candidates with mDNS names. auto caller = CreatePeerConnection(config); @@ -526,6 +539,7 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintDataOnly) { TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) { RTCConfiguration configuration; + configuration.sdp_semantics = SdpSemantics::kUnifiedPlan; PeerConnection::IceServer server; server.urls = {"stun:dummy.stun.server"}; configuration.servers.push_back(server); @@ -546,6 +560,7 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) { TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) { RTCConfiguration configuration; + configuration.sdp_semantics = SdpSemantics::kUnifiedPlan; PeerConnection::IceServer server; server.urls = {"stun:dummy.stun.server"}; configuration.servers.push_back(server); @@ -641,7 +656,9 @@ TEST_F(PeerConnectionUsageHistogramTest, // signaled and we expect a connection with prflx remote candidates at the // caller side. auto caller = CreatePeerConnectionWithPrivateIpv6LocalAddresses(); - auto callee = CreatePeerConnectionWithMdns(RTCConfiguration()); + RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + auto callee = CreatePeerConnectionWithMdns(config); caller->CreateDataChannel("test_channel"); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); // Wait until the gathering completes so that the session description would diff --git a/pc/peer_connection_ice_unittest.cc b/pc/peer_connection_ice_unittest.cc index a27d174c98..ed64aa24ea 100644 --- a/pc/peer_connection_ice_unittest.cc +++ b/pc/peer_connection_ice_unittest.cc @@ -8,15 +8,52 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" +#include "api/candidate.h" +#include "api/ice_transport_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" -#include "p2p/base/test_stun_server.h" +#include "p2p/base/ice_transport_internal.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" #include "p2p/client/basic_port_allocator.h" +#include "pc/channel_interface.h" +#include "pc/dtls_transport.h" #include "pc/media_session.h" #include "pc/peer_connection.h" #include "pc/peer_connection_wrapper.h" +#include "pc/rtp_transceiver.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -224,11 +261,11 @@ class PeerConnectionIceBaseTest : public ::testing::Test { for (const auto& transceiver : pc->GetTransceiversInternal()) { if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { auto dtls_transport = pc->LookupDtlsTransportByMidInternal( - transceiver->internal()->channel()->content_name()); + transceiver->internal()->channel()->mid()); return dtls_transport->ice_transport()->internal()->GetIceRole(); } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return cricket::ICEROLE_UNKNOWN; } @@ -800,7 +837,7 @@ TEST_P(PeerConnectionIceTest, std::move(jsep_candidate), [&operation_completed](RTCError result) { EXPECT_FALSE(result.ok()); EXPECT_EQ(result.message(), - std::string("Error processing ICE candidate")); + std::string("The remote description was null")); operation_completed = true; }); EXPECT_TRUE_WAIT(operation_completed, kWaitTimeout); @@ -844,6 +881,7 @@ TEST_P(PeerConnectionIceTest, LocalDescriptionUpdatedWhenContinualGathering) { const SocketAddress kLocalAddress("1.1.1.1", 0); RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.continual_gathering_policy = PeerConnectionInterface::GATHER_CONTINUALLY; auto caller = CreatePeerConnectionWithAudioVideo(config); @@ -866,6 +904,7 @@ TEST_P(PeerConnectionIceTest, const SocketAddress kLocalAddress("1.1.1.1", 0); RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.continual_gathering_policy = PeerConnectionInterface::GATHER_CONTINUALLY; auto caller = CreatePeerConnectionWithAudioVideo(config); @@ -892,6 +931,7 @@ TEST_P(PeerConnectionIceTest, const SocketAddress kLocalAddress("1.1.1.1", 0); RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.continual_gathering_policy = PeerConnectionInterface::GATHER_ONCE; auto caller = CreatePeerConnectionWithAudioVideo(config); caller->network()->AddInterface(kLocalAddress); @@ -1392,6 +1432,7 @@ class PeerConnectionIceConfigTest : public ::testing::Test { TEST_F(PeerConnectionIceConfigTest, SetStunCandidateKeepaliveInterval) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.stun_candidate_keepalive_interval = 123; config.ice_candidate_pool_size = 1; CreatePeerConnection(config); @@ -1408,6 +1449,7 @@ TEST_F(PeerConnectionIceConfigTest, SetStunCandidateKeepaliveInterval) { TEST_F(PeerConnectionIceConfigTest, SetStableWritableConnectionInterval) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.stable_writable_connection_ping_interval_ms = 3500; CreatePeerConnection(config); EXPECT_TRUE(pc_->SetConfiguration(config).ok()); @@ -1418,6 +1460,7 @@ TEST_F(PeerConnectionIceConfigTest, SetStableWritableConnectionInterval) { TEST_F(PeerConnectionIceConfigTest, SetStableWritableConnectionInterval_FailsValidation) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; CreatePeerConnection(config); ASSERT_TRUE(pc_->SetConfiguration(config).ok()); config.stable_writable_connection_ping_interval_ms = 5000; @@ -1428,6 +1471,7 @@ TEST_F(PeerConnectionIceConfigTest, TEST_F(PeerConnectionIceConfigTest, SetStableWritableConnectionInterval_DefaultValue_FailsValidation) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; CreatePeerConnection(config); ASSERT_TRUE(pc_->SetConfiguration(config).ok()); config.ice_check_interval_strong_connectivity = 2500; @@ -1438,6 +1482,7 @@ TEST_F(PeerConnectionIceConfigTest, TEST_P(PeerConnectionIceTest, IceCredentialsCreateOffer) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.ice_candidate_pool_size = 1; auto pc = CreatePeerConnectionWithAudioVideo(config); ASSERT_NE(pc->port_allocator_, nullptr); @@ -1455,6 +1500,7 @@ TEST_P(PeerConnectionIceTest, IceCredentialsCreateOffer) { TEST_P(PeerConnectionIceTest, IceCredentialsCreateAnswer) { RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.ice_candidate_pool_size = 1; auto pc = CreatePeerConnectionWithAudioVideo(config); ASSERT_NE(pc->port_allocator_, nullptr); diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc index fc094161af..1e17fa1869 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,8 @@ #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" +#include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { @@ -93,10 +96,12 @@ namespace { class PeerConnectionIntegrationTest : public PeerConnectionIntegrationBaseTest, - public ::testing::WithParamInterface { + public ::testing::WithParamInterface< + std::tuple> { protected: PeerConnectionIntegrationTest() - : PeerConnectionIntegrationBaseTest(GetParam()) {} + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam()), + std::get<1>(GetParam())) {} }; // Fake clock must be set before threads are started to prevent race on @@ -473,7 +478,7 @@ TEST_P(PeerConnectionIntegrationTest, ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Remove receive video (i.e., callee sender track). - callee()->pc()->RemoveTrack(callee_sender); + callee()->pc()->RemoveTrackOrError(callee_sender); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); @@ -505,7 +510,7 @@ TEST_P(PeerConnectionIntegrationTest, ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Remove send video (i.e., caller sender track). - caller()->pc()->RemoveTrack(caller_sender); + caller()->pc()->RemoveTrackOrError(caller_sender); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); @@ -963,7 +968,7 @@ TEST_F(PeerConnectionIntegrationTestPlanB, EnableAudioAfterRejecting) { // Remove audio track, and set offer_to_receive_audio to false to cause the // m= section to be completely disabled, not just "recvonly". - caller()->pc()->RemoveTrack(sender); + caller()->pc()->RemoveTrackOrError(sender); PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_audio = 0; caller()->SetOfferAnswerOptions(options); @@ -2956,7 +2961,7 @@ TEST_F(PeerConnectionIntegrationTestPlanB, RemoveAndAddTrackWithNewStreamId) { ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Remove the sender, and create a new one with the new stream. - caller()->pc()->RemoveTrack(sender); + caller()->pc()->RemoveTrackOrError(sender); sender = caller()->AddTrack(track, {"stream_2"}); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); @@ -3513,15 +3518,21 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, } } -INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest, - PeerConnectionIntegrationTest, - Values(SdpSemantics::kPlanB, - SdpSemantics::kUnifiedPlan)); +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationTest, + Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", + "WebRTC-FrameBuffer3/arm:SyncDecoding/"))); -INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest, - PeerConnectionIntegrationTestWithFakeClock, - Values(SdpSemantics::kPlanB, - SdpSemantics::kUnifiedPlan)); +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationTestWithFakeClock, + Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", + "WebRTC-FrameBuffer3/arm:SyncDecoding/"))); // Tests that verify interoperability between Plan B and Unified Plan // PeerConnections. diff --git a/pc/peer_connection_interface_unittest.cc b/pc/peer_connection_interface_unittest.cc index 2105c784a4..7dd3b31a0c 100644 --- a/pc/peer_connection_interface_unittest.cc +++ b/pc/peer_connection_interface_unittest.cc @@ -12,9 +12,7 @@ #include #include -#include -#include #include #include #include @@ -22,32 +20,26 @@ #include "absl/strings/str_replace.h" #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/call/call_factory_interface.h" #include "api/create_peerconnection_factory.h" #include "api/data_channel_interface.h" #include "api/jsep.h" -#include "api/jsep_session_description.h" #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log/rtc_event_log_factory.h" #include "api/rtc_event_log_output.h" -#include "api/rtc_event_log_output_file.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" -#include "api/rtp_transceiver_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" #include "api/transport/field_trial_based_config.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/codec.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" @@ -68,8 +60,8 @@ #include "pc/media_stream.h" #include "pc/peer_connection.h" #include "pc/peer_connection_factory.h" -#include "pc/rtc_stats_collector.h" #include "pc/rtp_sender.h" +#include "pc/rtp_sender_proxy.h" #include "pc/session_description.h" #include "pc/stream_collection.h" #include "pc/test/fake_audio_capture_module.h" @@ -79,17 +71,14 @@ #include "pc/test/test_sdp_strings.h" #include "pc/video_track.h" #include "rtc_base/checks.h" -#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" #include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" -#include "test/testsupport/file_utils.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -710,13 +699,16 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { CreatePeerConnection(PeerConnectionInterface::RTCConfiguration()); } - // DTLS does not work in a loopback call, so is disabled for most of the + // DTLS does not work in a loopback call, so is disabled for many // tests in this file. void CreatePeerConnectionWithoutDtls() { RTCConfiguration config; - config.enable_dtls_srtp = false; - + PeerConnectionFactoryInterface::Options options; + options.disable_encryption = true; + pc_factory_->SetOptions(options); CreatePeerConnection(config); + options.disable_encryption = false; + pc_factory_->SetOptions(options); } void CreatePeerConnectionWithIceTransportsType( @@ -751,10 +743,10 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { // false. std::unique_ptr cert_generator; - if (config.enable_dtls_srtp.value_or(true)) { - fake_certificate_generator_ = new FakeRTCCertificateGenerator(); - cert_generator.reset(fake_certificate_generator_); - } + // These won't be used if encryption is turned off, but that's harmless. + fake_certificate_generator_ = new FakeRTCCertificateGenerator(); + cert_generator.reset(fake_certificate_generator_); + RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; pc_ = pc_factory_->CreatePeerConnection( @@ -1329,6 +1321,7 @@ TEST_P(PeerConnectionInterfaceTest, // in the RTCConfiguration. TEST_P(PeerConnectionInterfaceTest, CreatePeerConnectionWithPooledCandidates) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; PeerConnectionInterface::IceServer server; server.uri = kStunAddressOnly; config.servers.push_back(server); @@ -1372,6 +1365,7 @@ TEST_P(PeerConnectionInterfaceTest, // Create RTCConfiguration with some network-related fields relevant to // PortAllocator populated. PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.disable_ipv6_on_wifi = true; config.max_ipv6_networks = 10; config.tcp_candidate_policy = @@ -1412,6 +1406,7 @@ TEST_P(PeerConnectionInterfaceTest, // constructed with, before SetConfiguration is called. TEST_P(PeerConnectionInterfaceTest, GetConfigurationAfterCreatePeerConnection) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.type = PeerConnectionInterface::kRelay; CreatePeerConnection(config); @@ -1424,6 +1419,7 @@ TEST_P(PeerConnectionInterfaceTest, GetConfigurationAfterCreatePeerConnection) { // SetConfiguration. TEST_P(PeerConnectionInterfaceTest, GetConfigurationAfterSetConfiguration) { PeerConnectionInterface::RTCConfiguration starting_config; + starting_config.sdp_semantics = sdp_semantics_; starting_config.bundle_policy = webrtc::PeerConnection::kBundlePolicyMaxBundle; CreatePeerConnection(starting_config); @@ -1554,8 +1550,8 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AddTrackRemoveTrack) { EXPECT_TRUE(DoSetLocalDescription(std::move(offer))); // Now try removing the tracks. - EXPECT_TRUE(pc_->RemoveTrack(audio_sender)); - EXPECT_TRUE(pc_->RemoveTrack(video_sender)); + EXPECT_TRUE(pc_->RemoveTrackOrError(audio_sender).ok()); + EXPECT_TRUE(pc_->RemoveTrackOrError(video_sender).ok()); // Create a new offer and ensure it doesn't contain the removed senders. ASSERT_TRUE(DoCreateOffer(&offer, nullptr)); @@ -1572,8 +1568,8 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AddTrackRemoveTrack) { // Calling RemoveTrack on a sender no longer attached to a PeerConnection // should return false. - EXPECT_FALSE(pc_->RemoveTrack(audio_sender)); - EXPECT_FALSE(pc_->RemoveTrack(video_sender)); + EXPECT_FALSE(pc_->RemoveTrackOrError(audio_sender).ok()); + EXPECT_FALSE(pc_->RemoveTrackOrError(video_sender).ok()); } // Test creating senders without a stream specified, @@ -1860,7 +1856,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { // Remove the stream. Since we are sending to our selves the local // and the remote stream is the same. - pc_->RemoveTrack(pc_->GetSenders()[0]); + pc_->RemoveTrackOrError(pc_->GetSenders()[0]); // Do a re-negotiation. CreateOfferReceiveAnswer(); @@ -1903,7 +1899,6 @@ TEST_P(PeerConnectionInterfaceTest, GetRTCStatsBeforeAndAfterCalling) { // DataChannelInit configurations. TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannel) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); webrtc::DataChannelInit config; @@ -1939,7 +1934,6 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannel) { // and maxRetransmitTime by setting them to -1 to get what they want. TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWithMinusOne) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); webrtc::DataChannelInit config; @@ -1955,7 +1949,6 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWithMinusOne) { TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelShouldFailForInvalidConfig) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); std::string label = "test"; @@ -1973,7 +1966,6 @@ TEST_P(PeerConnectionInterfaceTest, TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWithInvalidIdShouldFail) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); webrtc::DataChannelInit config; @@ -2003,7 +1995,6 @@ TEST_P(PeerConnectionInterfaceTest, // Verifies that duplicated label is allowed for SCTP data channel. TEST_P(PeerConnectionInterfaceTest, SctpDuplicatedLabelAllowed) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); std::string label = "test"; @@ -2051,7 +2042,6 @@ TEST_P(PeerConnectionInterfaceTest, DISABLED_TestRejectSctpDataChannelInAnswer) // the answer as a local description. TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); AddAudioTrack("audio_label"); AddVideoTrack("video_label"); @@ -2085,7 +2075,6 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) { // and because it's non-standard. TEST_P(PeerConnectionInterfaceTest, DtlsSdesFallbackNotSupported) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); // Wait for fake certificate to be generated. Previously, this is what caused // the "a=crypto" lines to be rejected. @@ -2129,7 +2118,6 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveUpdatedAudioOfferWithBadCodecs) { // will have m-lines with a=recvonly. TEST_P(PeerConnectionInterfaceTest, CreateSubsequentRecvOnlyOffer) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); CreateAnswerAsLocalDescription(); @@ -2155,7 +2143,6 @@ TEST_P(PeerConnectionInterfaceTest, CreateSubsequentRecvOnlyOffer) { // false, the generated m-lines will be a=inactive. TEST_P(PeerConnectionInterfaceTest, CreateSubsequentInactiveOffer) { RTCConfiguration rtc_config; - rtc_config.enable_dtls_srtp = true; CreatePeerConnection(rtc_config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); CreateAnswerAsLocalDescription(); @@ -2462,8 +2449,8 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CloseAndTestMethods) { CreateAnswerAsLocalDescription(); ASSERT_EQ(1u, pc_->local_streams()->count()); - rtc::scoped_refptr local_stream = - pc_->local_streams()->at(0); + rtc::scoped_refptr local_stream( + pc_->local_streams()->at(0)); pc_->Close(); @@ -2508,7 +2495,6 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndGetStats) { // signaled. TEST_P(PeerConnectionInterfaceTest, UpdateRemoteStreams) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); @@ -2534,7 +2520,6 @@ TEST_P(PeerConnectionInterfaceTest, UpdateRemoteStreams) { TEST_F(PeerConnectionInterfaceTestPlanB, AddRemoveTrackFromExistingRemoteMediaStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); std::unique_ptr desc_ms1 = CreateSessionDescriptionAndReference(1, 1); @@ -2577,7 +2562,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // that rejects the media content type. TEST_P(PeerConnectionInterfaceTest, RejectMediaContent) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); // First create and set a remote offer, then reject its video content in our // answer. @@ -2626,7 +2610,6 @@ TEST_P(PeerConnectionInterfaceTest, RejectMediaContent) { // Don't run under Unified Plan since the stream API is not available. TEST_F(PeerConnectionInterfaceTestPlanB, RemoveTrackThenRejectMediaContent) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); MediaStreamInterface* remote_stream = observer_.remote_streams()->at(0); @@ -2652,7 +2635,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, RemoveTrackThenRejectMediaContent) { // See: https://code.google.com/p/webrtc/issues/detail?id=5054 TEST_P(PeerConnectionInterfaceTest, RecvonlyDescriptionDoesntCreateStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); std::string recvonly_offer = GetSdpStringWithStream1(); @@ -2669,7 +2651,6 @@ TEST_P(PeerConnectionInterfaceTest, RecvonlyDescriptionDoesntCreateStream) { // Don't run under Unified Plan since this behavior is Plan B specific. TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithoutMsidCreatesDefaultStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringWithoutStreamsAudioOnly); @@ -2698,7 +2679,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithoutMsidCreatesDefaultStream) { TEST_F(PeerConnectionInterfaceTestPlanB, SendOnlySdpWithoutMsidCreatesDefaultStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringSendOnlyWithoutStreams); @@ -2715,7 +2695,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // Don't run under Unified Plan since this behavior is Plan B specific. TEST_F(PeerConnectionInterfaceTestPlanB, RemoveAlreadyGoneRemoteStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); MediaStreamInterface* remote_stream = observer_.remote_streams()->at(0); @@ -2734,7 +2713,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, RemoveAlreadyGoneRemoteStream) { TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithoutMsidAndStreamsCreatesDefaultStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringWithoutStreams); @@ -2749,7 +2727,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // Don't run under Unified Plan since this behavior is Plan B specific. TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithMsidDontCreatesDefaultStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringWithMsidWithoutStreams); EXPECT_EQ(0u, observer_.remote_streams()->count()); @@ -2762,7 +2739,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithMsidDontCreatesDefaultStream) { TEST_F(PeerConnectionInterfaceTestPlanB, DefaultTracksNotDestroyedAndRecreated) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringWithoutStreamsAudioOnly); @@ -2783,7 +2759,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // Don't run under Unified Plan since this behavior is Plan B specific. TEST_F(PeerConnectionInterfaceTestPlanB, VerifyDefaultStreamIsNotCreated) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(GetSdpStringWithStream1()); rtc::scoped_refptr reference(CreateStreamCollection(1, 1)); @@ -2799,7 +2774,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, VerifyDefaultStreamIsNotCreated) { TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithMsidWithoutSsrcCreatesDefaultStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); std::string sdp_string = kSdpStringWithoutStreamsAudioOnly; // Add a=msid lines to simulate a Unified Plan endpoint that only @@ -2822,7 +2796,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, TEST_F(PeerConnectionInterfaceTestPlanB, SdpWithEmptyMsidAndSsrcCreatesDefaultStreamId) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); // Add a a=msid line to the SDP. This is prioritized when parsing the SDP, so // the sender's stream ID will be interpreted as no stream IDs. @@ -2860,7 +2833,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // Don't run under Unified Plan since this behavior is Plan B specific. TEST_F(PeerConnectionInterfaceTestPlanB, LocalDescriptionChanged) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); // Create an offer with 1 stream with 2 tracks of each type. @@ -2899,7 +2871,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, LocalDescriptionChanged) { TEST_F(PeerConnectionInterfaceTestPlanB, AddLocalStreamAfterLocalDescriptionChanged) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); rtc::scoped_refptr stream_collection = @@ -2928,7 +2899,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, TEST_P(PeerConnectionInterfaceTest, ChangeSsrcOnTrackInLocalSessionDescription) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); AddAudioTrack(kAudioTracks[0]); @@ -2981,7 +2951,6 @@ TEST_P(PeerConnectionInterfaceTest, TEST_F(PeerConnectionInterfaceTestPlanB, SignalSameTracksInSeparateMediaStream) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); rtc::scoped_refptr stream_collection = @@ -3018,7 +2987,6 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // This tests that PeerConnectionObserver::OnAddTrack is correctly called. TEST_P(PeerConnectionInterfaceTest, OnAddTrackCallback) { RTCConfiguration config; - config.enable_dtls_srtp = true; CreatePeerConnection(config); CreateAndSetRemoteOffer(kSdpStringWithStream1AudioTrackOnly); EXPECT_EQ(observer_.num_added_tracks_, 1); @@ -3034,6 +3002,7 @@ TEST_P(PeerConnectionInterfaceTest, OnAddTrackCallback) { // changing, the next offer causes an ICE restart. TEST_P(PeerConnectionInterfaceTest, SetConfigurationCausingIceRestart) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.type = PeerConnectionInterface::kRelay; CreatePeerConnection(config); config = pc_->GetConfiguration(); @@ -3068,6 +3037,7 @@ TEST_P(PeerConnectionInterfaceTest, SetConfigurationCausingIceRestart) { // changing, the next offer does *not* cause an ICE restart. TEST_P(PeerConnectionInterfaceTest, SetConfigurationNotCausingIceRestart) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.type = PeerConnectionInterface::kRelay; CreatePeerConnection(config); config = pc_->GetConfiguration(); @@ -3102,6 +3072,7 @@ TEST_P(PeerConnectionInterfaceTest, SetConfigurationNotCausingIceRestart) { // that was already restarted. TEST_P(PeerConnectionInterfaceTest, SetConfigurationCausingPartialIceRestart) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.type = PeerConnectionInterface::kRelay; CreatePeerConnection(config); config = pc_->GetConfiguration(); @@ -3262,6 +3233,7 @@ TEST_P(PeerConnectionInterfaceTest, OffersAndAnswersHaveTrickleIceOption) { // RTCConfiguration. TEST_P(PeerConnectionInterfaceTest, IceRenominationNotOffered) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.enable_ice_renomination = false; CreatePeerConnection(config); AddAudioTrack("foo"); @@ -3278,6 +3250,7 @@ TEST_P(PeerConnectionInterfaceTest, IceRenominationNotOffered) { // if it's enabled in the PC's RTCConfiguration. TEST_P(PeerConnectionInterfaceTest, IceRenominationOptionInOfferAndAnswer) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.enable_ice_renomination = true; CreatePeerConnection(config); AddAudioTrack("foo"); @@ -3362,6 +3335,7 @@ TEST_P(PeerConnectionInterfaceTest, DISABLED_DataChannelOnlyOfferWithMaxBundlePolicy) { #endif // WEBRTC_HAVE_SCTP PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = sdp_semantics_; config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle; CreatePeerConnection(config); @@ -3701,6 +3675,7 @@ class PeerConnectionMediaConfigTest : public ::testing::Test { // This sanity check validates the test infrastructure itself. TEST_F(PeerConnectionMediaConfigTest, TestCreateAndClose) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; rtc::scoped_refptr pc( pcf_->CreatePeerConnection(config, nullptr, nullptr, &observer_)); EXPECT_TRUE(pc.get()); @@ -3713,6 +3688,7 @@ TEST_F(PeerConnectionMediaConfigTest, TestCreateAndClose) { // default RTCConfiguration. TEST_F(PeerConnectionMediaConfigTest, TestDefaults) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; const cricket::MediaConfig& media_config = TestCreatePeerConnection(config); @@ -3727,6 +3703,7 @@ TEST_F(PeerConnectionMediaConfigTest, TestDefaults) { // propagated from RTCConfiguration to the PeerConnection. TEST_F(PeerConnectionMediaConfigTest, TestDisablePrerendererSmoothingTrue) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.set_prerenderer_smoothing(false); const cricket::MediaConfig& media_config = TestCreatePeerConnection(config); @@ -3738,6 +3715,7 @@ TEST_F(PeerConnectionMediaConfigTest, TestDisablePrerendererSmoothingTrue) { // propagated from RTCConfiguration to the PeerConnection. TEST_F(PeerConnectionMediaConfigTest, TestEnableExperimentCpuLoadEstimator) { PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.set_experiment_cpu_load_estimator(true); const cricket::MediaConfig& media_config = TestCreatePeerConnection(config); diff --git a/pc/peer_connection_internal.h b/pc/peer_connection_internal.h index 6f97612914..0d4463ad0a 100644 --- a/pc/peer_connection_internal.h +++ b/pc/peer_connection_internal.h @@ -19,20 +19,125 @@ #include "api/peer_connection_interface.h" #include "call/call.h" +#include "pc/jsep_transport_controller.h" +#include "pc/peer_connection_message_handler.h" #include "pc/rtp_transceiver.h" +#include "pc/rtp_transmission_manager.h" #include "pc/sctp_data_channel.h" namespace webrtc { -// Internal interface for extra PeerConnection methods. -class PeerConnectionInternal : public PeerConnectionInterface { +class DataChannelController; +class StatsCollector; + +// This interface defines the functions that are needed for +// SdpOfferAnswerHandler to access PeerConnection internal state. +class PeerConnectionSdpMethods { public: - virtual rtc::Thread* network_thread() const = 0; - virtual rtc::Thread* worker_thread() const = 0; + virtual ~PeerConnectionSdpMethods() = default; // The SDP session ID as defined by RFC 3264. virtual std::string session_id() const = 0; + // Returns true if the ICE restart flag above was set, and no ICE restart has + // occurred yet for this transport (by applying a local description with + // changed ufrag/password). If the transport has been deleted as a result of + // bundling, returns false. + virtual bool NeedsIceRestart(const std::string& content_name) const = 0; + + virtual absl::optional sctp_mid() const = 0; + + // Functions below this comment are known to only be accessed + // from SdpOfferAnswerHandler. + // Return a pointer to the active configuration. + virtual const PeerConnectionInterface::RTCConfiguration* configuration() + const = 0; + + // Report the UMA metric SdpFormatReceived for the given remote description. + virtual void ReportSdpFormatReceived( + const SessionDescriptionInterface& remote_description) = 0; + + // Report the UMA metric BundleUsage for the given remote description. + virtual void ReportSdpBundleUsage( + const SessionDescriptionInterface& remote_description) = 0; + + virtual PeerConnectionMessageHandler* message_handler() = 0; + virtual RtpTransmissionManager* rtp_manager() = 0; + virtual const RtpTransmissionManager* rtp_manager() const = 0; + virtual bool dtls_enabled() const = 0; + virtual const PeerConnectionFactoryInterface::Options* options() const = 0; + + // Returns the CryptoOptions for this PeerConnection. This will always + // return the RTCConfiguration.crypto_options if set and will only default + // back to the PeerConnectionFactory settings if nothing was set. + virtual CryptoOptions GetCryptoOptions() = 0; + virtual JsepTransportController* transport_controller_s() = 0; + virtual JsepTransportController* transport_controller_n() = 0; + virtual DataChannelController* data_channel_controller() = 0; + virtual cricket::PortAllocator* port_allocator() = 0; + virtual StatsCollector* stats() = 0; + // Returns the observer. Will crash on CHECK if the observer is removed. + virtual PeerConnectionObserver* Observer() const = 0; + virtual bool GetSctpSslRole(rtc::SSLRole* role) = 0; + virtual PeerConnectionInterface::IceConnectionState + ice_connection_state_internal() = 0; + virtual void SetIceConnectionState( + PeerConnectionInterface::IceConnectionState new_state) = 0; + virtual void NoteUsageEvent(UsageEvent event) = 0; + virtual bool IsClosed() const = 0; + // Returns true if the PeerConnection is configured to use Unified Plan + // semantics for creating offers/answers and setting local/remote + // descriptions. If this is true the RtpTransceiver API will also be available + // to the user. If this is false, Plan B semantics are assumed. + // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once + // sufficient time has passed. + virtual bool IsUnifiedPlan() const = 0; + virtual bool ValidateBundleSettings( + const cricket::SessionDescription* desc, + const std::map& + bundle_groups_by_mid) = 0; + + virtual absl::optional GetDataMid() const = 0; + // Internal implementation for AddTransceiver family of methods. If + // `fire_callback` is set, fires OnRenegotiationNeeded callback if successful. + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool fire_callback = true) = 0; + // Asynchronously calls SctpTransport::Start() on the network thread for + // `sctp_mid()` if set. Called as part of setting the local description. + virtual void StartSctpTransport(int local_port, + int remote_port, + int max_message_size) = 0; + + // Asynchronously adds a remote candidate on the network thread. + virtual void AddRemoteCandidate(const std::string& mid, + const cricket::Candidate& candidate) = 0; + + virtual Call* call_ptr() = 0; + // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by + // this session. + virtual bool SrtpRequired() const = 0; + virtual bool SetupDataChannelTransport_n(const std::string& mid) = 0; + virtual void TeardownDataChannelTransport_n() = 0; + virtual void SetSctpDataMid(const std::string& mid) = 0; + virtual void ResetSctpDataMid() = 0; + // RingRTC change for ICE forking. + virtual rtc::scoped_refptr shared_ice_gatherer() { + return nullptr; + } +}; + +// Functions defined in this class are called by other objects, +// but not by SdpOfferAnswerHandler. +class PeerConnectionInternal : public PeerConnectionInterface, + public PeerConnectionSdpMethods, + public sigslot::has_slots<> { + public: + virtual rtc::Thread* network_thread() const = 0; + virtual rtc::Thread* worker_thread() const = 0; + // Returns true if we were the initial offerer. virtual bool initial_offerer() const = 0; @@ -50,7 +155,6 @@ class PeerConnectionInternal : public PeerConnectionInterface { } virtual absl::optional sctp_transport_name() const = 0; - virtual absl::optional sctp_mid() const = 0; virtual cricket::CandidateStatsList GetPooledCandidateStats() const = 0; @@ -71,15 +175,15 @@ class PeerConnectionInternal : public PeerConnectionInterface { // Returns true if there was an ICE restart initiated by the remote offer. virtual bool IceRestartPending(const std::string& content_name) const = 0; - // Returns true if the ICE restart flag above was set, and no ICE restart has - // occurred yet for this transport (by applying a local description with - // changed ufrag/password). If the transport has been deleted as a result of - // bundling, returns false. - virtual bool NeedsIceRestart(const std::string& content_name) const = 0; - // Get SSL role for an arbitrary m= section (handles bundling correctly). virtual bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) = 0; + // Functions needed by DataChannelController + virtual void NoteDataAddedEvent() {} + // Handler for the "channel closed" signal + virtual void OnSctpDataChannelClosed(DataChannelInterface* channel) {} + + virtual const WebRtcKeyValueConfig& trials() = 0; }; } // namespace webrtc diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc index 4713068a15..590fa90102 100644 --- a/pc/peer_connection_jsep_unittest.cc +++ b/pc/peer_connection_jsep_unittest.cc @@ -8,21 +8,56 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/call/call_factory_interface.h" +#include "api/jsep.h" +#include "api/media_stream_interface.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "api/transport/field_trial_based_config.h" +#include "api/transport/sctp_transport_factory_interface.h" +#include "api/transport/webrtc_key_value_config.h" +#include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "media/engine/webrtc_media_engine.h" #include "media/engine/webrtc_media_engine_defaults.h" +#include "modules/audio_device/include/audio_device.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" +#include "pc/channel_interface.h" #include "pc/media_session.h" -#include "pc/peer_connection_factory.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif #include "pc/test/fake_audio_capture_module.h" -#include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/pc/sctp/fake_sctp_transport.h" @@ -505,6 +540,42 @@ TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) { transceivers[0]->current_direction()); } +TEST_F(PeerConnectionJsepTest, + ChangeDirectionFromRecvOnlyToSendRecvDoesNotBreakVideoNegotiation) { + auto caller = CreatePeerConnection(); + auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + auto callee = CreatePeerConnection(); + caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); + + caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); +} + +TEST_F(PeerConnectionJsepTest, + ChangeDirectionFromRecvOnlyToSendRecvDoesNotBreakAudioNegotiation) { + auto caller = CreatePeerConnection(); + auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + auto callee = CreatePeerConnection(); + caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); + + caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); +} + // Tests for multiple round trips. // Test that setting a transceiver with the inactive direction does not stop it diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index d5d0b926b7..2a3a0aff99 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc @@ -12,24 +12,56 @@ // PeerConnection and the underlying media engine, as well as tests that check // the media-related aspects of SDP. +#include +#include +#include +#include #include +#include +#include #include +#include +#include +#include #include "absl/algorithm/container.h" #include "absl/types/optional.h" +#include "api/audio_options.h" #include "api/call/call_factory_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" +#include "media/base/codec.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_constants.h" +#include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "p2p/base/fake_port_allocator.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" #include "pc/rtp_media_utils.h" -#include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif -#include "pc/test/fake_rtc_certificate_generator.h" #include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" @@ -311,8 +343,8 @@ TEST_F(PeerConnectionMediaTestPlanB, EmptyRemoteOfferRemovesRecvStreams) { ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); // Remove both tracks from caller. - caller->pc()->RemoveTrack(caller_audio_track); - caller->pc()->RemoveTrack(caller_video_track); + caller->pc()->RemoveTrackOrError(caller_audio_track); + caller->pc()->RemoveTrackOrError(caller_video_track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); @@ -409,8 +441,8 @@ TEST_F(PeerConnectionMediaTestPlanB, EmptyLocalAnswerRemovesSendStreams) { ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); // Remove both tracks from callee. - callee->pc()->RemoveTrack(callee_audio_track); - callee->pc()->RemoveTrack(callee_video_track); + callee->pc()->RemoveTrackOrError(callee_audio_track); + callee->pc()->RemoveTrackOrError(callee_video_track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); @@ -846,6 +878,29 @@ bool HasAnyComfortNoiseCodecs(const cricket::SessionDescription* desc) { return false; } +bool HasPayloadTypeConflict(const cricket::SessionDescription* desc) { + std::set payload_types; + const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc); + if (audio_desc) { + for (const auto& codec : audio_desc->codecs()) { + if (payload_types.count(codec.id) > 0) { + return true; + } + payload_types.insert(codec.id); + } + } + const auto* video_desc = cricket::GetFirstVideoContentDescription(desc); + if (video_desc) { + for (const auto& codec : video_desc->codecs()) { + if (payload_types.count(codec.id) > 0) { + return true; + } + payload_types.insert(codec.id); + } + } + return false; +} + TEST_P(PeerConnectionMediaTest, CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) { auto fake_engine = std::make_unique(); @@ -1059,7 +1114,7 @@ TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) { EXPECT_EQ(1u, callee_video->send_streams().size()); // Callee removes video but keeps audio and rejects the video once again. - callee->pc()->RemoveTrack(callee_video_track); + callee->pc()->RemoveTrackOrError(callee_video_track); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_TRUE( callee->SetLocalDescription(callee->CreateAnswer(options_reject_video))); @@ -1129,7 +1184,7 @@ TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) { EXPECT_EQ(1u, callee_video->send_streams().size()); // Callee removes audio but keeps video and rejects the audio once again. - callee->pc()->RemoveTrack(callee_audio_track); + callee->pc()->RemoveTrackOrError(callee_audio_track); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_TRUE( callee->SetLocalDescription(callee->CreateAnswer(options_reject_audio))); @@ -1279,6 +1334,258 @@ TEST_P(PeerConnectionMediaTest, audio_options.combined_audio_video_bwe); } +// Test that if a RED codec refers to another codec in its fmtp line, but that +// codec's payload type was reassigned for some reason (either the remote +// endpoint selected a different payload type or there was a conflict), the RED +// fmtp line is modified to refer to the correct payload type. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeReassigned) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "120/120"); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + ASSERT_EQ(1u, answer_description->codecs().size()); + + // Offer from the callee should respect the established payload type, and + // attempt to add RED, which should refer to the correct payload type. + offer = callee->CreateOfferAndSetAsLocal(); + auto* offer_description = + cricket::GetFirstAudioContentDescription(offer->description()); + ASSERT_EQ(2u, offer_description->codecs().size()); + for (const auto& codec : offer_description->codecs()) { + if (codec.name == "foo") { + ASSERT_EQ(100, codec.id); + } else if (codec.name == cricket::kRedCodecName) { + std::string fmtp; + ASSERT_TRUE(codec.GetParam("", &fmtp)); + EXPECT_EQ("100/100", fmtp); + } + } +} + +// Test that RED without fmtp does match RED without fmtp. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpMatchNoFmtp) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + caller_fake_codecs.push_back( + cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1)); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + // Red (without fmtp) is negotiated. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + ASSERT_EQ(2u, answer_description->codecs().size()); + + // Offer from the callee should respect the established payload type, and + // attempt to add RED. + offer = callee->CreateOfferAndSetAsLocal(); + auto* offer_description = + cricket::GetFirstAudioContentDescription(offer->description()); + ASSERT_EQ(2u, offer_description->codecs().size()); + for (const auto& codec : offer_description->codecs()) { + if (codec.name == "foo") { + ASSERT_EQ(100, codec.id); + } else if (codec.name == cricket::kRedCodecName) { + ASSERT_EQ(101, codec.id); + } + } +} + +// Test that RED without fmtp does not match RED with fmtp. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpNoMatchFmtp) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + caller_fake_codecs.push_back( + cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1)); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "120/120"); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + // It should not negotiate RED. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + ASSERT_EQ(1u, answer_description->codecs().size()); + + // Offer from the callee should respect the established payload type, and + // attempt to add RED, which should refer to the correct payload type. + offer = callee->CreateOfferAndSetAsLocal(); + auto* offer_description = + cricket::GetFirstAudioContentDescription(offer->description()); + ASSERT_EQ(2u, offer_description->codecs().size()); + for (const auto& codec : offer_description->codecs()) { + if (codec.name == "foo") { + ASSERT_EQ(100, codec.id); + } else if (codec.name == cricket::kRedCodecName) { + std::string fmtp; + ASSERT_TRUE( + codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp)); + EXPECT_EQ("100/100", fmtp); + } + } +} + +// Test that RED with fmtp must match base codecs. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeMustMatchBaseCodecs) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + caller_fake_codecs.push_back( + cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1)); + caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "100/100"); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + callee_fake_codecs.push_back(cricket::AudioCodec(122, "bar", 0, 0, 1)); + callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "122/122"); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + // It should not negotiate RED since RED is associated with foo, not bar. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + ASSERT_EQ(1u, answer_description->codecs().size()); +} + +// Test behaviour when the RED fmtp attempts to specify different codecs +// which is not supported. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadMixed) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + caller_fake_codecs.push_back(cricket::AudioCodec(102, "bar", 0, 0, 1)); + caller_fake_codecs.push_back( + cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1)); + caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "100/102"); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "120/120"); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + // RED is not negotiated. + ASSERT_EQ(1u, answer_description->codecs().size()); +} + +// Test behaviour when the RED fmtp attempts to negotiate different levels of +// redundancy. +TEST_P(PeerConnectionMediaTest, RedFmtpPayloadDifferentRedundancy) { + std::vector caller_fake_codecs; + caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1)); + caller_fake_codecs.push_back( + cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1)); + caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "100/100"); + auto caller_fake_engine = std::make_unique(); + caller_fake_engine->SetAudioCodecs(caller_fake_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine)); + + std::vector callee_fake_codecs; + callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1)); + callee_fake_codecs.push_back( + cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1)); + callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat, + "120/120/120"); + auto callee_fake_engine = std::make_unique(); + callee_fake_engine->SetAudioCodecs(callee_fake_codecs); + auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); + + // Offer from the caller establishes 100 as the "foo" payload type. + auto offer = caller->CreateOfferAndSetAsLocal(); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswerAndSetAsLocal(); + auto answer_description = + cricket::GetFirstAudioContentDescription(answer->description()); + // RED is negotiated. + ASSERT_EQ(2u, answer_description->codecs().size()); + + // Offer from the callee should respect the established payload type, and + // attempt to add RED, which should refer to the correct payload type. + offer = callee->CreateOfferAndSetAsLocal(); + auto* offer_description = + cricket::GetFirstAudioContentDescription(offer->description()); + ASSERT_EQ(2u, offer_description->codecs().size()); + for (const auto& codec : offer_description->codecs()) { + if (codec.name == "foo") { + ASSERT_EQ(100, codec.id); + } else if (codec.name == cricket::kRedCodecName) { + std::string fmtp; + ASSERT_TRUE( + codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp)); + EXPECT_EQ("100/100", fmtp); + } + } +} + template bool CompareCodecs(const std::vector& capabilities, const std::vector& codecs) { @@ -1793,6 +2100,147 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description())); } +// If the "default" payload types of audio/video codecs are the same, and +// audio/video are bundled (as is the default), payload types should be +// remapped to avoid conflict, as normally happens without using +// SetCodecPreferences. +TEST_F(PeerConnectionMediaTestUnifiedPlan, + SetCodecPreferencesAvoidsPayloadTypeConflictInOffer) { + auto fake_engine = std::make_unique(); + + std::vector audio_codecs; + audio_codecs.emplace_back(100, "foo", 0, 0, 1); + audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1); + audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetAudioCodecs(audio_codecs); + + std::vector video_codecs; + video_codecs.emplace_back(100, "bar"); + video_codecs.emplace_back(101, cricket::kRtxCodecName); + video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetVideoCodecs(video_codecs); + + auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine)); + auto transceivers = caller->pc()->GetTransceivers(); + ASSERT_EQ(2u, transceivers.size()); + + auto audio_transceiver = caller->pc()->GetTransceivers()[0]; + auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_AUDIO); + EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + auto video_transceiver = caller->pc()->GetTransceivers()[1]; + capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_VIDEO); + EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + RTCOfferAnswerOptions options; + auto offer = caller->CreateOffer(options); + EXPECT_FALSE(HasPayloadTypeConflict(offer->description())); + // Sanity check that we got the primary codec and RTX. + EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(offer->description()) + ->codecs() + .size()); + EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(offer->description()) + ->codecs() + .size()); +} + +// Same as above, but preferences set for the answer. +TEST_F(PeerConnectionMediaTestUnifiedPlan, + SetCodecPreferencesAvoidsPayloadTypeConflictInAnswer) { + auto fake_engine = std::make_unique(); + + std::vector audio_codecs; + audio_codecs.emplace_back(100, "foo", 0, 0, 1); + audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1); + audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetAudioCodecs(audio_codecs); + + std::vector video_codecs; + video_codecs.emplace_back(100, "bar"); + video_codecs.emplace_back(101, cricket::kRtxCodecName); + video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetVideoCodecs(video_codecs); + + auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine)); + + RTCOfferAnswerOptions options; + caller->SetRemoteDescription(caller->CreateOffer(options)); + + auto transceivers = caller->pc()->GetTransceivers(); + ASSERT_EQ(2u, transceivers.size()); + + auto audio_transceiver = caller->pc()->GetTransceivers()[0]; + auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_AUDIO); + EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + auto video_transceiver = caller->pc()->GetTransceivers()[1]; + capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_VIDEO); + EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + auto answer = caller->CreateAnswer(options); + + EXPECT_FALSE(HasPayloadTypeConflict(answer->description())); + // Sanity check that we got the primary codec and RTX. + EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(answer->description()) + ->codecs() + .size()); + EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(answer->description()) + ->codecs() + .size()); +} + +// Same as above, but preferences set for a subsequent offer. +TEST_F(PeerConnectionMediaTestUnifiedPlan, + SetCodecPreferencesAvoidsPayloadTypeConflictInSubsequentOffer) { + auto fake_engine = std::make_unique(); + + std::vector audio_codecs; + audio_codecs.emplace_back(100, "foo", 0, 0, 1); + audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1); + audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetAudioCodecs(audio_codecs); + + std::vector video_codecs; + video_codecs.emplace_back(100, "bar"); + video_codecs.emplace_back(101, cricket::kRtxCodecName); + video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100"; + fake_engine->SetVideoCodecs(video_codecs); + + auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine)); + + RTCOfferAnswerOptions options; + caller->SetRemoteDescription(caller->CreateOffer(options)); + caller->SetLocalDescription(caller->CreateAnswer(options)); + + auto transceivers = caller->pc()->GetTransceivers(); + ASSERT_EQ(2u, transceivers.size()); + + auto audio_transceiver = caller->pc()->GetTransceivers()[0]; + auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_AUDIO); + EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + auto video_transceiver = caller->pc()->GetTransceivers()[1]; + capabilities = caller->pc_factory()->GetRtpSenderCapabilities( + cricket::MediaType::MEDIA_TYPE_VIDEO); + EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok()); + + auto reoffer = caller->CreateOffer(options); + + EXPECT_FALSE(HasPayloadTypeConflict(reoffer->description())); + // Sanity check that we got the primary codec and RTX. + EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(reoffer->description()) + ->codecs() + .size()); + EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(reoffer->description()) + ->codecs() + .size()); +} + INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest, PeerConnectionMediaTest, Values(SdpSemantics::kPlanB, diff --git a/pc/peer_connection_message_handler.cc b/pc/peer_connection_message_handler.cc index 4b7913d678..77db3e45e2 100644 --- a/pc/peer_connection_message_handler.cc +++ b/pc/peer_connection_message_handler.cc @@ -10,6 +10,7 @@ #include "pc/peer_connection_message_handler.h" +#include #include #include "api/jsep.h" @@ -129,7 +130,7 @@ void PeerConnectionMessageHandler::OnMessage(rtc::Message* msg) { break; } default: - RTC_NOTREACHED() << "Not implemented"; + RTC_DCHECK_NOTREACHED() << "Not implemented"; break; } } diff --git a/pc/peer_connection_proxy.h b/pc/peer_connection_proxy.h index e9f1ee86fe..84ce41e033 100644 --- a/pc/peer_connection_proxy.h +++ b/pc/peer_connection_proxy.h @@ -35,8 +35,9 @@ PROXY_METHOD2(RTCErrorOr>, AddTrack, rtc::scoped_refptr, const std::vector&) -PROXY_METHOD1(bool, RemoveTrack, RtpSenderInterface*) -PROXY_METHOD1(RTCError, RemoveTrackNew, rtc::scoped_refptr) +PROXY_METHOD1(RTCError, + RemoveTrackOrError, + rtc::scoped_refptr) PROXY_METHOD1(RTCErrorOr>, AddTransceiver, rtc::scoped_refptr) diff --git a/pc/peer_connection_rampup_tests.cc b/pc/peer_connection_rampup_tests.cc index 5cf30d83a7..dc5a11f58a 100644 --- a/pc/peer_connection_rampup_tests.cc +++ b/pc/peer_connection_rampup_tests.cc @@ -15,8 +15,6 @@ #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/audio_options.h" @@ -24,14 +22,13 @@ #include "api/jsep.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/port_allocator.h" @@ -51,6 +48,7 @@ #include "rtc_base/location.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/test_certificate_verifier.h" #include "rtc_base/thread.h" @@ -233,15 +231,16 @@ class PeerConnectionRampUpTest : public ::testing::Test { void CreateTurnServer(cricket::ProtocolType type, const std::string& common_name = "test turn server") { rtc::Thread* thread = network_thread(); + rtc::SocketFactory* factory = firewall_socket_server_.get(); std::unique_ptr turn_server = network_thread_->Invoke>( - RTC_FROM_HERE, [thread, type, common_name] { + RTC_FROM_HERE, [thread, factory, type, common_name] { static const rtc::SocketAddress turn_server_internal_address{ kTurnInternalAddress, kTurnInternalPort}; static const rtc::SocketAddress turn_server_external_address{ kTurnExternalAddress, kTurnExternalPort}; return std::make_unique( - thread, turn_server_internal_address, + thread, factory, turn_server_internal_address, turn_server_external_address, type, true /*ignore_bad_certs=*/, common_name); }); @@ -343,9 +342,11 @@ TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverTCP) { ice_server.username = "test"; ice_server.password = "test"; PeerConnectionInterface::RTCConfiguration client_1_config; + client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_1_config.servers.push_back(ice_server); client_1_config.type = PeerConnectionInterface::kRelay; PeerConnectionInterface::RTCConfiguration client_2_config; + client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_2_config.servers.push_back(ice_server); client_2_config.type = PeerConnectionInterface::kRelay; ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config)); @@ -364,9 +365,11 @@ TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverUDP) { ice_server.username = "test"; ice_server.password = "test"; PeerConnectionInterface::RTCConfiguration client_1_config; + client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_1_config.servers.push_back(ice_server); client_1_config.type = PeerConnectionInterface::kRelay; PeerConnectionInterface::RTCConfiguration client_2_config; + client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_2_config.servers.push_back(ice_server); client_2_config.type = PeerConnectionInterface::kRelay; ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config)); @@ -385,9 +388,11 @@ TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverTLS) { ice_server.username = "test"; ice_server.password = "test"; PeerConnectionInterface::RTCConfiguration client_1_config; + client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_1_config.servers.push_back(ice_server); client_1_config.type = PeerConnectionInterface::kRelay; PeerConnectionInterface::RTCConfiguration client_2_config; + client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_2_config.servers.push_back(ice_server); client_2_config.type = PeerConnectionInterface::kRelay; @@ -399,9 +404,11 @@ TEST_F(PeerConnectionRampUpTest, Bwe_After_TurnOverTLS) { TEST_F(PeerConnectionRampUpTest, Bwe_After_UDPPeerToPeer) { PeerConnectionInterface::RTCConfiguration client_1_config; + client_1_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_1_config.tcp_candidate_policy = PeerConnection::kTcpCandidatePolicyDisabled; PeerConnectionInterface::RTCConfiguration client_2_config; + client_2_config.sdp_semantics = SdpSemantics::kUnifiedPlan; client_2_config.tcp_candidate_policy = PeerConnection::kTcpCandidatePolicyDisabled; ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config)); @@ -412,9 +419,9 @@ TEST_F(PeerConnectionRampUpTest, Bwe_After_UDPPeerToPeer) { TEST_F(PeerConnectionRampUpTest, Bwe_After_TCPPeerToPeer) { firewall_socket_server()->set_udp_sockets_enabled(false); - ASSERT_TRUE(CreatePeerConnectionWrappers( - PeerConnectionInterface::RTCConfiguration(), - PeerConnectionInterface::RTCConfiguration())); + PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + ASSERT_TRUE(CreatePeerConnectionWrappers(config, config)); SetupOneWayCall(); RunTest("tcp_peer_to_peer"); diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc index 715546b2ea..6c08ba47bc 100644 --- a/pc/peer_connection_rtp_unittest.cc +++ b/pc/peer_connection_rtp_unittest.cc @@ -8,8 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include #include #include #include @@ -17,8 +18,6 @@ #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" @@ -30,14 +29,13 @@ #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" #include "api/set_remote_description_observer_interface.h" #include "api/uma_metrics.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/stream_params.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" @@ -237,7 +235,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithoutStreamFiresOnRemoveTrack) { ASSERT_TRUE( caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); - EXPECT_TRUE(caller->pc()->RemoveTrack(sender)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_EQ(callee->observer()->add_track_events_.size(), 1u); @@ -255,7 +253,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithStreamFiresOnRemoveTrack) { ASSERT_TRUE( caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); - EXPECT_TRUE(caller->pc()->RemoveTrack(sender)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_EQ(callee->observer()->add_track_events_.size(), 1u); @@ -277,7 +275,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamFiresOnRemoveTrack) { caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); // Remove "audio_track1". - EXPECT_TRUE(caller->pc()->RemoveTrack(sender1)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender1).ok()); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_EQ(callee->observer()->add_track_events_.size(), 2u); EXPECT_EQ( @@ -289,7 +287,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamFiresOnRemoveTrack) { caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); // Remove "audio_track2". - EXPECT_TRUE(caller->pc()->RemoveTrack(sender2)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender2).ok()); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_EQ(callee->observer()->add_track_events_.size(), 2u); EXPECT_EQ(callee->observer()->GetAddTrackReceivers(), @@ -480,7 +478,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, ASSERT_EQ(1u, callee->observer()->add_track_events_.size()); EXPECT_EQ(0u, callee->observer()->remove_track_events_.size()); - caller->pc()->RemoveTrack(sender); + caller->pc()->RemoveTrackOrError(sender); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); EXPECT_EQ(1u, callee->observer()->add_track_events_.size()); @@ -580,7 +578,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithoutStreamRemovesReceiver) { ASSERT_EQ(callee->pc()->GetReceivers().size(), 1u); auto receiver = callee->pc()->GetReceivers()[0]; - ASSERT_TRUE(caller->pc()->RemoveTrack(sender)); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { @@ -604,7 +602,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithStreamRemovesReceiver) { ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); ASSERT_EQ(callee->pc()->GetReceivers().size(), 1u); auto receiver = callee->pc()->GetReceivers()[0]; - ASSERT_TRUE(caller->pc()->RemoveTrack(sender)); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { @@ -630,7 +628,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamRemovesReceiver) { ASSERT_EQ(2u, callee->pc()->GetReceivers().size()); // Remove "audio_track1". - EXPECT_TRUE(caller->pc()->RemoveTrack(sender1)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender1).ok()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { @@ -648,7 +646,7 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamRemovesReceiver) { } // Remove "audio_track2". - EXPECT_TRUE(caller->pc()->RemoveTrack(sender2)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender2).ok()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { @@ -706,7 +704,7 @@ TEST_F(PeerConnectionRtpTestPlanB, // first and second SetRemoteDescription() calls. auto sender = caller->AddAudioTrack("audio_track", {}); auto srd1_sdp = caller->CreateOfferAndSetAsLocal(); - EXPECT_TRUE(caller->pc()->RemoveTrack(sender)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); auto srd2_sdp = caller->CreateOfferAndSetAsLocal(); // In the first SetRemoteDescription() callback, check that we have a @@ -738,10 +736,12 @@ TEST_F(PeerConnectionRtpTestPlanB, // when the first callback is invoked. callee->pc()->SetRemoteDescription( std::move(srd1_sdp), - new OnSuccessObserver(srd1_callback)); + rtc::make_ref_counted>( + srd1_callback)); callee->pc()->SetRemoteDescription( std::move(srd2_sdp), - new OnSuccessObserver(srd2_callback)); + rtc::make_ref_counted>( + srd2_callback)); EXPECT_TRUE_WAIT(srd1_callback_called, kDefaultTimeout); EXPECT_TRUE_WAIT(srd2_callback_called, kDefaultTimeout); } @@ -1302,7 +1302,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, RemoveTrackClearsSenderTrack) { auto caller = CreatePeerConnection(); auto sender = caller->AddAudioTrack("a"); - ASSERT_TRUE(caller->pc()->RemoveTrack(sender)); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); EXPECT_FALSE(sender->track()); } @@ -1325,7 +1325,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, caller->observer()->clear_legacy_renegotiation_needed(); caller->observer()->clear_latest_negotiation_needed_event(); - ASSERT_TRUE(caller->pc()->RemoveTrack(transceiver->sender())); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(transceiver->sender()).ok()); EXPECT_TRUE(caller->observer()->legacy_renegotiation_needed()); EXPECT_TRUE(caller->observer()->has_negotiation_needed_event()); @@ -1350,7 +1350,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, caller->observer()->clear_legacy_renegotiation_needed(); caller->observer()->clear_latest_negotiation_needed_event(); - ASSERT_TRUE(caller->pc()->RemoveTrack(transceiver->sender())); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(transceiver->sender()).ok()); EXPECT_TRUE(caller->observer()->legacy_renegotiation_needed()); EXPECT_TRUE(caller->observer()->has_negotiation_needed_event()); @@ -1368,7 +1368,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, RemoveTrackWithNullSenderTrackIsNoOp) { caller->observer()->clear_legacy_renegotiation_needed(); caller->observer()->clear_latest_negotiation_needed_event(); - ASSERT_TRUE(caller->pc()->RemoveTrack(sender)); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); EXPECT_FALSE(caller->observer()->legacy_renegotiation_needed()); EXPECT_FALSE(caller->observer()->has_negotiation_needed_event()); @@ -1385,7 +1385,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, RemoveTrackErrorIfClosed) { caller->observer()->clear_legacy_renegotiation_needed(); caller->observer()->clear_latest_negotiation_needed_event(); - EXPECT_FALSE(caller->pc()->RemoveTrack(sender)); + EXPECT_FALSE(caller->pc()->RemoveTrackOrError(sender).ok()); EXPECT_FALSE(caller->observer()->legacy_renegotiation_needed()); EXPECT_FALSE(caller->observer()->has_negotiation_needed_event()); } @@ -1395,11 +1395,11 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, auto caller = CreatePeerConnection(); auto sender = caller->AddAudioTrack("a"); - ASSERT_TRUE(caller->pc()->RemoveTrack(sender)); + ASSERT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); caller->observer()->clear_legacy_renegotiation_needed(); caller->observer()->clear_latest_negotiation_needed_event(); - EXPECT_TRUE(caller->pc()->RemoveTrack(sender)); + EXPECT_TRUE(caller->pc()->RemoveTrackOrError(sender).ok()); EXPECT_FALSE(caller->observer()->legacy_renegotiation_needed()); EXPECT_FALSE(caller->observer()->has_negotiation_needed_event()); } @@ -1413,7 +1413,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, AddRemoveAddTrackOffersWorksAudio) { auto sender1 = caller->AddAudioTrack("audio1"); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); - caller->pc()->RemoveTrack(sender1); + caller->pc()->RemoveTrackOrError(sender1); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); // This will re-use the transceiver created by the first AddTrack. @@ -1429,7 +1429,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, AddRemoveAddTrackOffersWorksVideo) { auto sender1 = caller->AddVideoTrack("video1"); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); - caller->pc()->RemoveTrack(sender1); + caller->pc()->RemoveTrackOrError(sender1); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); // This will re-use the transceiver created by the first AddTrack. @@ -1486,7 +1486,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, auto sender1 = caller->AddTrack(track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); - caller->pc()->RemoveTrack(sender1); + caller->pc()->RemoveTrackOrError(sender1); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); auto sender2 = caller->AddTrack(track); @@ -1654,11 +1654,11 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, caller->AddTransceiver(video_track); auto transceiver = caller->AddTransceiver(track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); - caller->pc()->RemoveTrack(transceiver->sender()); + caller->pc()->RemoveTrackOrError(transceiver->sender()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); caller->AddTrack(track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); - caller->pc()->RemoveTrack(transceiver->sender()); + caller->pc()->RemoveTrackOrError(transceiver->sender()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); } @@ -1672,12 +1672,12 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, caller->AddTransceiver(video_track); auto transceiver = caller->AddTransceiver(track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); - caller->pc()->RemoveTrack(transceiver->sender()); + caller->pc()->RemoveTrackOrError(transceiver->sender()); ExchangeOfferAnswerWhereRemoteStopsTransceiver(caller.get(), callee.get(), 1); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); caller->AddTrack(track); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); - caller->pc()->RemoveTrack(transceiver->sender()); + caller->pc()->RemoveTrackOrError(transceiver->sender()); ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); } diff --git a/pc/peer_connection_signaling_unittest.cc b/pc/peer_connection_signaling_unittest.cc index d20dc706cb..5923d2c47f 100644 --- a/pc/peer_connection_signaling_unittest.cc +++ b/pc/peer_connection_signaling_unittest.cc @@ -12,20 +12,52 @@ // machine, as well as tests that check basic, media-agnostic aspects of SDP. #include +#include +#include +#include #include +#include +#include #include +#include +#include +#include +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" -#include "api/jsep_session_description.h" +#include "api/dtls_transport_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" +#include "media/base/codec.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "p2p/base/port_allocator.h" #include "pc/peer_connection.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" -#include "pc/webrtc_sdp.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -33,7 +65,6 @@ #include "pc/test/fake_rtc_certificate_generator.h" #include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" -#include "test/gmock.h" namespace webrtc { @@ -82,7 +113,7 @@ class ExecuteFunctionOnCreateSessionDescriptionObserver function_(desc); } - void OnFailure(RTCError error) override { RTC_NOTREACHED(); } + void OnFailure(RTCError error) override { RTC_DCHECK_NOTREACHED(); } private: bool was_called_ = false; @@ -273,9 +304,10 @@ class PeerConnectionSignalingStateTest break; } case SignalingState::kClosed: { - RTC_NOTREACHED() << "Set the second member of the tuple to true to " - "achieve a closed state from an existing, valid " - "state."; + RTC_DCHECK_NOTREACHED() + << "Set the second member of the tuple to true to " + "achieve a closed state from an existing, valid " + "state."; } } @@ -658,7 +690,7 @@ TEST_P(PeerConnectionSignalingTest, // Process all currently pending messages by waiting for a posted task to run. bool checkpoint_reached = false; rtc::Thread::Current()->PostTask( - RTC_FROM_HERE, [&checkpoint_reached] { checkpoint_reached = true; }); + [&checkpoint_reached] { checkpoint_reached = true; }); EXPECT_TRUE_WAIT(checkpoint_reached, kWaitTimeout); // If resolving the observer was pending, it must now have been called. EXPECT_TRUE(observer->called()); @@ -673,8 +705,9 @@ TEST_P(PeerConnectionSignalingTest, SetRemoteDescriptionExecutesImmediately) { // By not waiting for the observer's callback we can verify that the operation // executed immediately. - callee->pc()->SetRemoteDescription(std::move(offer), - new FakeSetRemoteDescriptionObserver()); + callee->pc()->SetRemoteDescription( + std::move(offer), + rtc::make_ref_counted()); EXPECT_EQ(2u, callee->pc()->GetReceivers().size()); } @@ -692,8 +725,9 @@ TEST_P(PeerConnectionSignalingTest, CreateOfferBlocksSetRemoteDescription) { // SetRemoteDescription() operation should be chained to be executed // asynchronously, when CreateOffer() completes. callee->pc()->CreateOffer(offer_observer, RTCOfferAnswerOptions()); - callee->pc()->SetRemoteDescription(std::move(offer), - new FakeSetRemoteDescriptionObserver()); + callee->pc()->SetRemoteDescription( + std::move(offer), + rtc::make_ref_counted()); // CreateOffer() is asynchronous; without message processing this operation // should not have completed. EXPECT_FALSE(offer_observer->called()); @@ -1025,12 +1059,49 @@ TEST_P(PeerConnectionSignalingTest, ReceiveFlexFecReoffer) { ASSERT_EQ(flexfec_it->id, 35); auto av1_it = std::find_if( offer_codecs.begin(), offer_codecs.end(), - [](const cricket::Codec& codec) { return codec.name == "AV1X"; }); + [](const cricket::Codec& codec) { return codec.name == "AV1"; }); if (av1_it != offer_codecs.end()) { ASSERT_NE(av1_it->id, 35); } } +TEST_P(PeerConnectionSignalingTest, MidAttributeMaxLength) { + auto caller = CreatePeerConnection(); + + std::string sdp = + "v=0\r\n" + "o=- 8403615332048243445 2 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 102\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:IZeV\r\n" + "a=ice-pwd:uaZhQD4rYM/Tta2qWBT1Bbt4\r\n" + "a=ice-options:trickle\r\n" + "a=fingerprint:sha-256 " + "D8:6C:3D:FA:23:E2:2C:63:11:2D:D0:86:BE:C4:D0:65:F9:42:F7:1C:06:04:27:E6:" + "1C:2C:74:01:8D:50:67:23\r\n" + "a=setup:actpass\r\n" + // Too long mid attribute. + "a=mid:0123456789012345678901234567890123\r\n" + "a=sendrecv\r\n" + "a=msid:stream track\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=rtpmap:102 VP8/90000\r\n" + "a=rtcp-fb:102 goog-remb\r\n" + "a=rtcp-fb:102 transport-cc\r\n" + "a=rtcp-fb:102 ccm fir\r\n" + "a=rtcp-fb:102 nack\r\n" + "a=rtcp-fb:102 nack pli\r\n" + "a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n"; + std::unique_ptr remote_description = + webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr); + + EXPECT_FALSE(caller->SetRemoteDescription(std::move(remote_description))); +} + INSTANTIATE_TEST_SUITE_P(PeerConnectionSignalingTest, PeerConnectionSignalingTest, Values(SdpSemantics::kPlanB, @@ -1075,8 +1146,9 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, // the new observer should also be invoked synchronously - as is ensured by // other tests.) RTC_DCHECK(!caller->pc()->GetTransceivers()[0]->mid().has_value()); - caller->pc()->SetLocalDescription(std::move(offer), - new FakeSetLocalDescriptionObserver()); + caller->pc()->SetLocalDescription( + std::move(offer), + rtc::make_ref_counted()); EXPECT_TRUE(caller->pc()->GetTransceivers()[0]->mid().has_value()); } @@ -1094,7 +1166,7 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, // operation executed immediately. RTC_DCHECK(!pc->GetTransceivers()[0]->mid().has_value()); pc->SetLocalDescription( - new rtc::RefCountedObject(), + rtc::make_ref_counted(), desc); EXPECT_TRUE(pc->GetTransceivers()[0]->mid().has_value()); }); @@ -1228,4 +1300,60 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, callee->observer()->latest_negotiation_needed_event())); } +TEST_F(PeerConnectionSignalingUnifiedPlanTest, RtxReofferApt) { + auto callee = CreatePeerConnection(); + + std::string sdp = + "v=0\r\n" + "o=- 8403615332048243445 2 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 102\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:IZeV\r\n" + "a=ice-pwd:uaZhQD4rYM/Tta2qWBT1Bbt4\r\n" + "a=ice-options:trickle\r\n" + "a=fingerprint:sha-256 " + "D8:6C:3D:FA:23:E2:2C:63:11:2D:D0:86:BE:C4:D0:65:F9:42:F7:1C:06:04:27:E6:" + "1C:2C:74:01:8D:50:67:23\r\n" + "a=setup:actpass\r\n" + "a=mid:0\r\n" + "a=sendrecv\r\n" + "a=msid:stream track\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=rtpmap:102 VP8/90000\r\n" + "a=rtcp-fb:102 goog-remb\r\n" + "a=rtcp-fb:102 transport-cc\r\n" + "a=rtcp-fb:102 ccm fir\r\n" + "a=rtcp-fb:102 nack\r\n" + "a=rtcp-fb:102 nack pli\r\n" + "a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n"; + std::unique_ptr remote_description = + webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr); + + EXPECT_TRUE(callee->SetRemoteDescription(std::move(remote_description))); + + auto answer = callee->CreateAnswer(RTCOfferAnswerOptions()); + EXPECT_TRUE( + callee->SetLocalDescription(CloneSessionDescription(answer.get()))); + + callee->pc()->GetTransceivers()[0]->Stop(); + auto reoffer = callee->CreateOffer(RTCOfferAnswerOptions()); + auto codecs = reoffer->description() + ->contents()[0] + .media_description() + ->as_video() + ->codecs(); + ASSERT_GT(codecs.size(), 2u); + EXPECT_EQ(codecs[0].name, "VP8"); + EXPECT_EQ(codecs[1].name, "rtx"); + auto apt_it = codecs[1].params.find("apt"); + ASSERT_NE(apt_it, codecs[1].params.end()); + // The apt should match the id from the remote offer. + EXPECT_EQ(apt_it->second, rtc::ToString(codecs[0].id)); + EXPECT_EQ(apt_it->second, "102"); +} + } // namespace webrtc diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index 31385754b7..10c4f39703 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -8,26 +8,51 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include +#include +#include #include #include // no-presubmit-check TODO(webrtc:8982) +#include +#include +#include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" +#include "api/jsep.h" #include "api/media_types.h" +#include "api/peer_connection_interface.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/uma_metrics.h" +#include "api/video/video_codec_constants.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "pc/peer_connection.h" +#include "media/base/rid_description.h" +#include "media/base/stream_params.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "pc/channel_interface.h" #include "pc/peer_connection_wrapper.h" +#include "pc/session_description.h" +#include "pc/simulcast_description.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/mock_peer_connection_observers.h" -#include "rtc_base/gunit.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread.h" +#include "rtc_base/unique_id_generator.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::Contains; using ::testing::Each; diff --git a/pc/peer_connection_wrapper.cc b/pc/peer_connection_wrapper.cc index e9ed5c3efb..b304fe8b5f 100644 --- a/pc/peer_connection_wrapper.cc +++ b/pc/peer_connection_wrapper.cc @@ -12,8 +12,6 @@ #include -#include -#include #include #include @@ -168,8 +166,7 @@ bool PeerConnectionWrapper::SetRemoteDescription( bool PeerConnectionWrapper::SetRemoteDescription( std::unique_ptr desc, RTCError* error_out) { - rtc::scoped_refptr observer = - new FakeSetRemoteDescriptionObserver(); + auto observer = rtc::make_ref_counted(); pc()->SetRemoteDescription(std::move(desc), observer); EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout); bool ok = observer->error().ok(); diff --git a/pc/proxy.h b/pc/proxy.h index 565ae80175..2c347d6249 100644 --- a/pc/proxy.h +++ b/pc/proxy.h @@ -53,12 +53,12 @@ // The variant defined with BEGIN_PRIMARY_PROXY_MAP is unaware of // the secondary thread, and invokes all methods on the primary thread. // -// The variant defined with BEGIN_OWNED_PROXY_MAP does not use -// refcounting, and instead just takes ownership of the object being proxied. #ifndef PC_PROXY_H_ #define PC_PROXY_H_ +#include + #include #include #include @@ -69,6 +69,7 @@ #include "api/task_queue/queued_task.h" #include "api/task_queue/task_queue_base.h" #include "rtc_base/event.h" +#include "rtc_base/location.h" #include "rtc_base/message_handler.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/string_utils.h" @@ -202,61 +203,66 @@ class ConstMethodCall : public QueuedTask { #define PROXY_STRINGIZE(x) PROXY_STRINGIZE_IMPL(x) // Helper macros to reduce code duplication. -#define PROXY_MAP_BOILERPLATE(c) \ - template \ - class c##ProxyWithInternal; \ - typedef c##ProxyWithInternal c##Proxy; \ - template \ - class c##ProxyWithInternal : public c##Interface { \ - protected: \ - static constexpr char proxy_name_[] = #c "Proxy"; \ - typedef c##Interface C; \ - \ - public: \ - const INTERNAL_CLASS* internal() const { return c_; } \ - INTERNAL_CLASS* internal() { return c_; } +#define PROXY_MAP_BOILERPLATE(class_name) \ + template \ + class class_name##ProxyWithInternal; \ + typedef class_name##ProxyWithInternal \ + class_name##Proxy; \ + template \ + class class_name##ProxyWithInternal : public class_name##Interface { \ + protected: \ + static constexpr char proxy_name_[] = #class_name "Proxy"; \ + typedef class_name##Interface C; \ + \ + public: \ + const INTERNAL_CLASS* internal() const { return c(); } \ + INTERNAL_CLASS* internal() { return c(); } // clang-format off // clang-format would put the semicolon alone, // leading to a presubmit error (cpplint.py) -#define END_PROXY_MAP(c) \ - }; \ - template \ - constexpr char c##ProxyWithInternal::proxy_name_[]; +#define END_PROXY_MAP(class_name) \ + }; \ + template \ + constexpr char class_name##ProxyWithInternal::proxy_name_[]; // clang-format on -#define PRIMARY_PROXY_MAP_BOILERPLATE(c) \ - protected: \ - c##ProxyWithInternal(rtc::Thread* primary_thread, INTERNAL_CLASS* c) \ - : primary_thread_(primary_thread), c_(c) {} \ - \ - private: \ +#define PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ + protected: \ + class_name##ProxyWithInternal(rtc::Thread* primary_thread, \ + rtc::scoped_refptr c) \ + : primary_thread_(primary_thread), c_(std::move(c)) {} \ + \ + private: \ mutable rtc::Thread* primary_thread_; -#define SECONDARY_PROXY_MAP_BOILERPLATE(c) \ - protected: \ - c##ProxyWithInternal(rtc::Thread* primary_thread, \ - rtc::Thread* secondary_thread, INTERNAL_CLASS* c) \ - : primary_thread_(primary_thread), \ - secondary_thread_(secondary_thread), \ - c_(c) {} \ - \ - private: \ - mutable rtc::Thread* primary_thread_; \ +#define SECONDARY_PROXY_MAP_BOILERPLATE(class_name) \ + protected: \ + class_name##ProxyWithInternal(rtc::Thread* primary_thread, \ + rtc::Thread* secondary_thread, \ + rtc::scoped_refptr c) \ + : primary_thread_(primary_thread), \ + secondary_thread_(secondary_thread), \ + c_(std::move(c)) {} \ + \ + private: \ + mutable rtc::Thread* primary_thread_; \ mutable rtc::Thread* secondary_thread_; // Note that the destructor is protected so that the proxy can only be // destroyed via RefCountInterface. -#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \ - protected: \ - ~c##ProxyWithInternal() { \ - MethodCall call( \ - this, &c##ProxyWithInternal::DestroyInternal); \ - call.Marshal(RTC_FROM_HERE, destructor_thread()); \ - } \ - \ - private: \ - void DestroyInternal() { c_ = nullptr; } \ +#define REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name) \ + protected: \ + ~class_name##ProxyWithInternal() { \ + MethodCall call( \ + this, &class_name##ProxyWithInternal::DestroyInternal); \ + call.Marshal(RTC_FROM_HERE, destructor_thread()); \ + } \ + \ + private: \ + const INTERNAL_CLASS* c() const { return c_.get(); } \ + INTERNAL_CLASS* c() { return c_.get(); } \ + void DestroyInternal() { c_ = nullptr; } \ rtc::scoped_refptr c_; // Note: This doesn't use a unique_ptr, because it intends to handle a corner @@ -264,50 +270,41 @@ class ConstMethodCall : public QueuedTask { // this proxy object. If relying on a unique_ptr to delete the object, its // inner pointer would be set to null before this reentrant callback would have // a chance to run, resulting in a segfault. -#define OWNED_PROXY_MAP_BOILERPLATE(c) \ - public: \ - ~c##ProxyWithInternal() { \ - MethodCall call( \ - this, &c##ProxyWithInternal::DestroyInternal); \ - call.Marshal(RTC_FROM_HERE, destructor_thread()); \ - } \ - \ - private: \ - void DestroyInternal() { delete c_; } \ +#define OWNED_PROXY_MAP_BOILERPLATE(class_name) \ + public: \ + ~class_name##ProxyWithInternal() { \ + MethodCall call( \ + this, &class_name##ProxyWithInternal::DestroyInternal); \ + call.Marshal(RTC_FROM_HERE, destructor_thread()); \ + } \ + \ + private: \ + const INTERNAL_CLASS* c() const { return c_; } \ + INTERNAL_CLASS* c() { return c_; } \ + void DestroyInternal() { delete c_; } \ INTERNAL_CLASS* c_; -#define BEGIN_PRIMARY_PROXY_MAP(c) \ - PROXY_MAP_BOILERPLATE(c) \ - PRIMARY_PROXY_MAP_BOILERPLATE(c) \ - REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \ +#define BEGIN_PRIMARY_PROXY_MAP(class_name) \ + PROXY_MAP_BOILERPLATE(class_name) \ + PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ + REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name) \ public: \ - static rtc::scoped_refptr Create( \ - rtc::Thread* primary_thread, INTERNAL_CLASS* c) { \ - return rtc::make_ref_counted(primary_thread, c); \ + static rtc::scoped_refptr Create( \ + rtc::Thread* primary_thread, rtc::scoped_refptr c) { \ + return rtc::make_ref_counted( \ + primary_thread, std::move(c)); \ } -#define BEGIN_PROXY_MAP(c) \ - PROXY_MAP_BOILERPLATE(c) \ - SECONDARY_PROXY_MAP_BOILERPLATE(c) \ - REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \ - public: \ - static rtc::scoped_refptr Create( \ - rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \ - INTERNAL_CLASS* c) { \ - return rtc::make_ref_counted(primary_thread, \ - secondary_thread, c); \ - } - -#define BEGIN_OWNED_PROXY_MAP(c) \ - PROXY_MAP_BOILERPLATE(c) \ - SECONDARY_PROXY_MAP_BOILERPLATE(c) \ - OWNED_PROXY_MAP_BOILERPLATE(c) \ +#define BEGIN_PROXY_MAP(class_name) \ + PROXY_MAP_BOILERPLATE(class_name) \ + SECONDARY_PROXY_MAP_BOILERPLATE(class_name) \ + REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name) \ public: \ - static std::unique_ptr Create( \ + static rtc::scoped_refptr Create( \ rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \ - std::unique_ptr c) { \ - return std::unique_ptr(new c##ProxyWithInternal( \ - primary_thread, secondary_thread, c.release())); \ + rtc::scoped_refptr c) { \ + return rtc::make_ref_counted( \ + primary_thread, secondary_thread, std::move(c)); \ } #define PROXY_PRIMARY_THREAD_DESTRUCTOR() \ @@ -339,124 +336,124 @@ class ConstMethodCall : public QueuedTask { #define PROXY_METHOD0(r, method) \ r method() override { \ TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method); \ + MethodCall call(c(), &C::method); \ return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } #define PROXY_CONSTMETHOD0(r, method) \ r method() const override { \ TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method); \ + ConstMethodCall call(c(), &C::method); \ return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD1(r, method, t1) \ - r method(t1 a1) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD1(r, method, t1) \ + r method(t1 a1) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_CONSTMETHOD1(r, method, t1) \ - r method(t1 a1) const override { \ - TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_CONSTMETHOD1(r, method, t1) \ + r method(t1 a1) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c(), &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD3(r, method, t1, t2, t3) \ - r method(t1 a1, t2 a2, t3 a3) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD3(r, method, t1, t2, t3) \ + r method(t1 a1, t2 a2, t3 a3) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2), std::move(a3)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD4(r, method, t1, t2, t3, t4) \ - r method(t1 a1, t2 a2, t3 a3, t4 a4) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3), \ - std::move(a4)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD4(r, method, t1, t2, t3, t4) \ + r method(t1 a1, t2 a2, t3 a3, t4 a4) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2), std::move(a3), \ + std::move(a4)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \ - r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3), \ - std::move(a4), std::move(a5)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \ + r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2), std::move(a3), \ + std::move(a4), std::move(a5)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } // Define methods which should be invoked on the secondary thread. #define PROXY_SECONDARY_METHOD0(r, method) \ r method() override { \ TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method); \ + MethodCall call(c(), &C::method); \ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } #define PROXY_SECONDARY_CONSTMETHOD0(r, method) \ r method() const override { \ TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method); \ + ConstMethodCall call(c(), &C::method); \ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD1(r, method, t1) \ - r method(t1 a1) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD1(r, method, t1) \ + r method(t1 a1) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1) \ - r method(t1 a1) const override { \ - TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1) \ + r method(t1 a1) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c(), &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) const override { \ +#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ + } + +#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3) \ + r method(t1 a1, t2 a2, t3 a3) override { \ TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ + MethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2), std::move(a3)); \ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3) \ - r method(t1 a1, t2 a2, t3 a3) override { \ - TRACE_BOILERPLATE(method); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ - } - -#define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2) \ - r method(t1 a1, t2 a2, t3 a3) const override { \ - TRACE_BOILERPLATE(method); \ - ConstMethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2) \ + r method(t1 a1, t2 a2, t3 a3) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c(), &C::method, std::move(a1), \ + std::move(a2), std::move(a3)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } // For use when returning purely const state (set during construction). diff --git a/pc/proxy_unittest.cc b/pc/proxy_unittest.cc index ef3d97eddc..48c087f690 100644 --- a/pc/proxy_unittest.cc +++ b/pc/proxy_unittest.cc @@ -98,7 +98,7 @@ class SignalingProxyTest : public ::testing::Test { ASSERT_TRUE(signaling_thread_->Start()); fake_ = Fake::Create(); fake_signaling_proxy_ = - FakeSignalingProxy::Create(signaling_thread_.get(), fake_.get()); + FakeSignalingProxy::Create(signaling_thread_.get(), fake_); } protected: @@ -186,8 +186,8 @@ class ProxyTest : public ::testing::Test { ASSERT_TRUE(signaling_thread_->Start()); ASSERT_TRUE(worker_thread_->Start()); fake_ = Fake::Create(); - fake_proxy_ = FakeProxy::Create(signaling_thread_.get(), - worker_thread_.get(), fake_.get()); + fake_proxy_ = + FakeProxy::Create(signaling_thread_.get(), worker_thread_.get(), fake_); } protected: @@ -256,54 +256,4 @@ TEST_F(ProxyTest, WorkerMethod2) { EXPECT_EQ("Method2", fake_proxy_->Method2(arg1, arg2)); } -// Interface for testing OWNED_PROXY_MAP. -class FooInterface { - public: - virtual ~FooInterface() {} - virtual void Bar() = 0; -}; - -class Foo : public FooInterface { - public: - Foo() {} - MOCK_METHOD(void, Bar, (), (override)); -}; - -BEGIN_OWNED_PROXY_MAP(Foo) -PROXY_PRIMARY_THREAD_DESTRUCTOR() -PROXY_METHOD0(void, Bar) -END_PROXY_MAP(Foo) - -class OwnedProxyTest : public ::testing::Test { - public: - OwnedProxyTest() - : signaling_thread_(rtc::Thread::Create()), - worker_thread_(rtc::Thread::Create()), - foo_(new Foo()), - foo_proxy_(FooProxy::Create(signaling_thread_.get(), - worker_thread_.get(), - std::unique_ptr(foo_))) { - signaling_thread_->Start(); - worker_thread_->Start(); - } - - void CheckSignalingThread() { EXPECT_TRUE(signaling_thread_->IsCurrent()); } - void CheckWorkerThread() { EXPECT_TRUE(worker_thread_->IsCurrent()); } - - protected: - std::unique_ptr signaling_thread_; - std::unique_ptr worker_thread_; - Foo* foo_; // Owned by foo_proxy_, not this class. - std::unique_ptr foo_proxy_; -}; - -// Just tests that a method can be invoked using an "owned proxy" (as opposed -// to normal ref-counted version). -TEST_F(OwnedProxyTest, BasicTest) { - EXPECT_CALL(*foo_, Bar()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &OwnedProxyTest::CheckSignalingThread)); - foo_proxy_->Bar(); -} - } // namespace webrtc diff --git a/pc/remote_audio_source.cc b/pc/remote_audio_source.cc index dc890e737c..781e4512be 100644 --- a/pc/remote_audio_source.cc +++ b/pc/remote_audio_source.cc @@ -13,6 +13,7 @@ #include #include +#include #include "absl/algorithm/container.h" #include "api/scoped_refptr.h" @@ -55,7 +56,7 @@ RemoteAudioSource::RemoteAudioSource( : main_thread_(rtc::Thread::Current()), worker_thread_(worker_thread), on_audio_channel_gone_action_(on_audio_channel_gone_action), - state_(MediaSourceInterface::kLive) { + state_(MediaSourceInterface::kInitializing) { RTC_DCHECK(main_thread_); RTC_DCHECK(worker_thread_); } @@ -134,11 +135,6 @@ void RemoteAudioSource::AddSink(AudioTrackSinkInterface* sink) { RTC_DCHECK_RUN_ON(main_thread_); RTC_DCHECK(sink); - if (state_ != MediaSourceInterface::kLive) { - RTC_LOG(LS_ERROR) << "Can't register sink as the source isn't live."; - return; - } - MutexLock lock(&sink_lock_); RTC_DCHECK(!absl::c_linear_search(sinks_, sink)); sinks_.push_back(sink); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 1f8b28df62..c965161b0e 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -10,26 +10,28 @@ #include "pc/rtc_stats_collector.h" +#include #include -#include #include #include #include #include +#include #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/candidate.h" +#include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" #include "api/rtp_parameters.h" -#include "api/rtp_receiver_interface.h" -#include "api/rtp_sender_interface.h" #include "api/sequence_checker.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtcstats_objects.h" #include "api/task_queue/queued_task.h" +#include "api/units/time_delta.h" #include "api/video/video_content_type.h" #include "common_video/include/quality_limitation_reason.h" #include "media/base/media_channel.h" @@ -37,7 +39,6 @@ #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "p2p/base/connection_info.h" -#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" @@ -45,6 +46,8 @@ #include "pc/channel_interface.h" #include "pc/data_channel_utils.h" #include "pc/rtc_stats_traversal.h" +#include "pc/rtp_receiver_proxy.h" +#include "pc/rtp_sender_proxy.h" #include "pc/webrtc_sdp.h" #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" @@ -170,7 +173,7 @@ const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { return RTCIceCandidateType::kPrflx; if (type == cricket::RELAY_PORT_TYPE) return RTCIceCandidateType::kRelay; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -186,7 +189,7 @@ const char* DataStateToRTCDataChannelState( case DataChannelInterface::kClosed: return RTCDataChannelState::kClosed; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } } @@ -203,7 +206,7 @@ const char* IceCandidatePairStateToRTCStatsIceCandidatePairState( case cricket::IceCandidatePairState::FAILED: return RTCStatsIceCandidatePairState::kFailed; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } } @@ -227,7 +230,7 @@ const char* DtlsTransportStateToRTCDtlsTransportState( } } -const char* NetworkAdapterTypeToStatsType(rtc::AdapterType type) { +const char* NetworkTypeToStatsType(rtc::AdapterType type) { switch (type) { case rtc::ADAPTER_TYPE_CELLULAR: case rtc::ADAPTER_TYPE_CELLULAR_2G: @@ -246,7 +249,37 @@ const char* NetworkAdapterTypeToStatsType(rtc::AdapterType type) { case rtc::ADAPTER_TYPE_ANY: return RTCNetworkType::kUnknown; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); + return nullptr; +} + +absl::string_view NetworkTypeToStatsNetworkAdapterType(rtc::AdapterType type) { + switch (type) { + case rtc::ADAPTER_TYPE_CELLULAR: + return RTCNetworkAdapterType::kCellular; + case rtc::ADAPTER_TYPE_CELLULAR_2G: + return RTCNetworkAdapterType::kCellular2g; + case rtc::ADAPTER_TYPE_CELLULAR_3G: + return RTCNetworkAdapterType::kCellular3g; + case rtc::ADAPTER_TYPE_CELLULAR_4G: + return RTCNetworkAdapterType::kCellular4g; + case rtc::ADAPTER_TYPE_CELLULAR_5G: + return RTCNetworkAdapterType::kCellular5g; + case rtc::ADAPTER_TYPE_ETHERNET: + return RTCNetworkAdapterType::kEthernet; + case rtc::ADAPTER_TYPE_WIFI: + return RTCNetworkAdapterType::kWifi; + case rtc::ADAPTER_TYPE_UNKNOWN: + return RTCNetworkAdapterType::kUnknown; + case rtc::ADAPTER_TYPE_LOOPBACK: + return RTCNetworkAdapterType::kLoopback; + case rtc::ADAPTER_TYPE_ANY: + return RTCNetworkAdapterType::kAny; + case rtc::ADAPTER_TYPE_VPN: + /* should not be handled here. Vpn is modelled as a bool */ + break; + } + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -269,9 +302,12 @@ std::map QualityLimitationDurationToRTCQualityLimitationDuration( std::map durations_ms) { std::map result; + // The internal duration is defined in milliseconds while the spec defines + // the value in seconds: + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations for (const auto& elem : durations_ms) { result[QualityLimitationReasonToRTCQualityLimitationReason(elem.first)] = - elem.second; + elem.second / static_cast(rtc::kNumMillisecsPerSec); } return result; } @@ -536,6 +572,9 @@ void SetOutboundRTPStreamStatsFromVoiceSenderInfo( outbound_audio); outbound_audio->media_type = "audio"; outbound_audio->kind = "audio"; + if (voice_sender_info.target_bitrate > 0) { + outbound_audio->target_bitrate = voice_sender_info.target_bitrate; + } if (voice_sender_info.codec_payload_type) { outbound_audio->codec_id = RTCCodecStatsIDFromMidDirectionAndPayload( mid, /*inbound=*/false, *voice_sender_info.codec_payload_type); @@ -724,13 +763,34 @@ const std::string& ProduceIceCandidateStats(int64_t timestamp_us, candidate_stats->transport_id = transport_id; if (is_local) { candidate_stats->network_type = - NetworkAdapterTypeToStatsType(candidate.network_type()); - if (candidate.type() == cricket::RELAY_PORT_TYPE) { - std::string relay_protocol = candidate.relay_protocol(); + NetworkTypeToStatsType(candidate.network_type()); + const std::string& candidate_type = candidate.type(); + const std::string& relay_protocol = candidate.relay_protocol(); + const std::string& url = candidate.url(); + if (candidate_type == cricket::RELAY_PORT_TYPE || + (candidate_type == cricket::PRFLX_PORT_TYPE && + !relay_protocol.empty())) { RTC_DCHECK(relay_protocol.compare("udp") == 0 || relay_protocol.compare("tcp") == 0 || relay_protocol.compare("tls") == 0); candidate_stats->relay_protocol = relay_protocol; + if (!url.empty()) { + candidate_stats->url = url; + } + } else if (candidate_type == cricket::STUN_PORT_TYPE) { + if (!url.empty()) { + candidate_stats->url = url; + } + } + if (candidate.network_type() == rtc::ADAPTER_TYPE_VPN) { + candidate_stats->vpn = true; + candidate_stats->network_adapter_type = + std::string(NetworkTypeToStatsNetworkAdapterType( + candidate.underlying_type_for_vpn())); + } else { + candidate_stats->vpn = false; + candidate_stats->network_adapter_type = std::string( + NetworkTypeToStatsNetworkAdapterType(candidate.network_type())); } } else { // We don't expect to know the adapter type of remote candidates. @@ -965,7 +1025,7 @@ void ProduceSenderMediaTrackStats( if (sender_info) { voice_sender_info = sender_info; } else { - RTC_LOG(LS_INFO) + RTC_DLOG(LS_INFO) << "RTCStatsCollector: No voice sender info for sender with ssrc " << sender->ssrc(); } @@ -993,8 +1053,8 @@ void ProduceSenderMediaTrackStats( if (sender_info) { video_sender_info = sender_info; } else { - RTC_LOG(LS_INFO) << "No video sender info for sender with ssrc " - << sender->ssrc(); + RTC_DLOG(LS_INFO) + << "No video sender info for sender with ssrc " << sender->ssrc(); } } std::unique_ptr video_track_stats = @@ -1002,7 +1062,7 @@ void ProduceSenderMediaTrackStats( timestamp_us, *track, *video_sender_info, sender->AttachmentId()); report->AddStats(std::move(video_track_stats)); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } } @@ -1041,7 +1101,7 @@ void ProduceReceiverMediaTrackStats( receiver->AttachmentId()); report->AddStats(std::move(video_track_stats)); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } } @@ -1218,7 +1278,8 @@ void RTCStatsCollector::GetStatsReportInternal( std::vector requests_; }; signaling_thread_->PostTask(std::make_unique( - this, cached_report_, std::move(requests))); + rtc::scoped_refptr(this), cached_report_, + std::move(requests))); } else if (!num_pending_partial_reports_) { // Only start gathering stats if we're not already gathering stats. In the // case of already gathering stats, `callback_` will be invoked when there @@ -1242,7 +1303,6 @@ void RTCStatsCollector::GetStatsReportInternal( network_report_event_.Reset(); rtc::scoped_refptr collector(this); network_thread_->PostTask( - RTC_FROM_HERE, [collector, sctp_transport_name = pc_->sctp_transport_name(), timestamp_us]() mutable { collector->ProducePartialResultsOnNetworkThread( @@ -1331,7 +1391,7 @@ void RTCStatsCollector::ProducePartialResultsOnNetworkThread( network_report_event_.Set(); rtc::scoped_refptr collector(this); signaling_thread_->PostTask( - RTC_FROM_HERE, [collector] { collector->MergeNetworkReport_s(); }); + [collector] { collector->MergeNetworkReport_s(); }); } void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl( @@ -1552,8 +1612,18 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n( // false after a certain amount of time without a response passes. // https://crbug.com/633550 candidate_pair_stats->writable = info.writable; + // Note that sent_total_packets includes discarded packets but + // sent_total_bytes does not. + candidate_pair_stats->packets_sent = static_cast( + info.sent_total_packets - info.sent_discarded_packets); + candidate_pair_stats->packets_discarded_on_send = + static_cast(info.sent_discarded_packets); + candidate_pair_stats->packets_received = + static_cast(info.packets_received); candidate_pair_stats->bytes_sent = static_cast(info.sent_total_bytes); + candidate_pair_stats->bytes_discarded_on_send = + static_cast(info.sent_discarded_bytes); candidate_pair_stats->bytes_received = static_cast(info.recv_total_bytes); candidate_pair_stats->total_round_trip_time = @@ -1644,14 +1714,16 @@ void RTCStatsCollector::ProduceMediaStreamTrackStats_s( for (const RtpTransceiverStatsInfo& stats : transceiver_stats_infos_) { std::vector> senders; for (const auto& sender : stats.transceiver->senders()) { - senders.push_back(sender->internal()); + senders.push_back( + rtc::scoped_refptr(sender->internal())); } ProduceSenderMediaTrackStats(timestamp_us, *stats.track_media_info_map, senders, report); std::vector> receivers; for (const auto& receiver : stats.transceiver->receivers()) { - receivers.push_back(receiver->internal()); + receivers.push_back( + rtc::scoped_refptr(receiver->internal())); } ProduceReceiverMediaTrackStats(timestamp_us, *stats.track_media_info_map, receivers, report); @@ -1783,7 +1855,7 @@ void RTCStatsCollector::ProduceRTPStreamStats_n( } else if (stats.media_type == cricket::MEDIA_TYPE_VIDEO) { ProduceVideoRTPStreamStats_n(timestamp_us, stats, report); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } } @@ -2010,21 +2082,20 @@ void RTCStatsCollector::ProduceTransportStats_n( new RTCTransportStats(RTCTransportStatsIDFromTransportChannel( transport_name, channel_stats.component), timestamp_us)); - transport_stats->bytes_sent = 0; - transport_stats->packets_sent = 0; - transport_stats->bytes_received = 0; - transport_stats->packets_received = 0; + transport_stats->packets_sent = + channel_stats.ice_transport_stats.packets_sent; + transport_stats->packets_received = + channel_stats.ice_transport_stats.packets_received; + transport_stats->bytes_sent = + channel_stats.ice_transport_stats.bytes_sent; + transport_stats->bytes_received = + channel_stats.ice_transport_stats.bytes_received; transport_stats->dtls_state = DtlsTransportStateToRTCDtlsTransportState(channel_stats.dtls_state); transport_stats->selected_candidate_pair_changes = channel_stats.ice_transport_stats.selected_candidate_pair_changes; for (const cricket::ConnectionInfo& info : channel_stats.ice_transport_stats.connection_infos) { - *transport_stats->bytes_sent += info.sent_total_bytes; - *transport_stats->packets_sent += - info.sent_total_packets - info.sent_discarded_packets; - *transport_stats->bytes_received += info.recv_total_bytes; - *transport_stats->packets_received += info.packets_received; if (info.best_connection) { transport_stats->selected_candidate_pair_id = RTCIceCandidatePairStatsIDFromConnectionInfo(info); @@ -2131,8 +2202,8 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { continue; } - stats.mid = channel->content_name(); - stats.transport_name = channel->transport_name(); + stats.mid = channel->mid(); + stats.transport_name = std::string(channel->transport_name()); if (media_type == cricket::MEDIA_TYPE_AUDIO) { auto* voice_channel = static_cast(channel); @@ -2147,7 +2218,7 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { video_stats[video_channel->media_channel()] = std::make_unique(); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } }); @@ -2194,11 +2265,13 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { } std::vector> senders; for (const auto& sender : transceiver->senders()) { - senders.push_back(sender->internal()); + senders.push_back( + rtc::scoped_refptr(sender->internal())); } std::vector> receivers; for (const auto& receiver : transceiver->receivers()) { - receivers.push_back(receiver->internal()); + receivers.push_back( + rtc::scoped_refptr(receiver->internal())); } stats.track_media_info_map = std::make_unique( std::move(voice_media_info), std::move(video_media_info), senders, diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index c84e6d3fef..e6d9d184fe 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h @@ -12,6 +12,8 @@ #define PC_RTC_STATS_COLLECTOR_H_ #include + +#include #include #include #include diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 8c4f0a6623..1438615ee6 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -11,28 +11,45 @@ #include "pc/rtc_stats_collector.h" #include +#include +#include #include #include #include #include #include +#include #include #include -#include "absl/memory/memory.h" #include "absl/strings/str_replace.h" +#include "api/candidate.h" #include "api/dtls_transport_interface.h" +#include "api/media_stream_interface.h" #include "api/media_stream_track.h" #include "api/rtp_parameters.h" +#include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "api/video/recordable_encoded_frame.h" +#include "api/video/video_content_type.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" +#include "common_video/include/quality_limitation_reason.h" +#include "media/base/media_channel.h" +#include "modules/audio_processing/include/audio_processing_statistics.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "p2p/base/connection_info.h" +#include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" #include "pc/media_stream.h" +#include "pc/stream_collection.h" #include "pc/test/fake_data_channel_provider.h" #include "pc/test/fake_peer_connection_for_stats.h" #include "pc/test/mock_data_channel.h" @@ -43,10 +60,19 @@ #include "rtc_base/fake_clock.h" #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" -#include "rtc_base/logging.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_encode.h" #include "rtc_base/strings/json.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/time_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" using ::testing::_; using ::testing::AtLeast; @@ -190,11 +216,14 @@ std::unique_ptr CreateFakeCandidate( const std::string& protocol, const rtc::AdapterType adapter_type, const std::string& candidate_type, - uint32_t priority) { + uint32_t priority, + const rtc::AdapterType underlying_type_for_vpn = + rtc::ADAPTER_TYPE_UNKNOWN) { std::unique_ptr candidate(new cricket::Candidate()); candidate->set_address(rtc::SocketAddress(hostname, port)); candidate->set_protocol(protocol); candidate->set_network_type(adapter_type); + candidate->set_underlying_type_for_vpn(underlying_type_for_vpn); candidate->set_type(candidate_type); candidate->set_priority(priority); return candidate; @@ -221,8 +250,7 @@ class FakeAudioTrackForStats : public MediaStreamTrack { const std::string& id, MediaStreamTrackInterface::TrackState state, bool create_fake_audio_processor) { - rtc::scoped_refptr audio_track_stats( - new rtc::RefCountedObject(id)); + auto audio_track_stats = rtc::make_ref_counted(id); audio_track_stats->set_state(state); if (create_fake_audio_processor) { audio_track_stats->processor_ = @@ -254,9 +282,8 @@ class FakeVideoTrackSourceForStats : public VideoTrackSourceInterface { static rtc::scoped_refptr Create( int input_width, int input_height) { - return rtc::scoped_refptr( - new rtc::RefCountedObject(input_width, - input_height)); + return rtc::make_ref_counted(input_width, + input_height); } FakeVideoTrackSourceForStats(int input_width, int input_height) @@ -301,9 +328,8 @@ class FakeVideoTrackForStats : public MediaStreamTrack { const std::string& id, MediaStreamTrackInterface::TrackState state, rtc::scoped_refptr source) { - rtc::scoped_refptr video_track( - new rtc::RefCountedObject(id, - std::move(source))); + auto video_track = + rtc::make_ref_counted(id, std::move(source)); video_track->set_state(state); return video_track; } @@ -353,8 +379,7 @@ rtc::scoped_refptr CreateMockSender( media_type == cricket::MEDIA_TYPE_AUDIO) || (track->kind() == MediaStreamTrackInterface::kVideoKind && media_type == cricket::MEDIA_TYPE_VIDEO)); - rtc::scoped_refptr sender( - new rtc::RefCountedObject()); + auto sender = rtc::make_ref_counted(); EXPECT_CALL(*sender, track()).WillRepeatedly(Return(track)); EXPECT_CALL(*sender, ssrc()).WillRepeatedly(Return(ssrc)); EXPECT_CALL(*sender, media_type()).WillRepeatedly(Return(media_type)); @@ -367,7 +392,6 @@ rtc::scoped_refptr CreateMockSender( EXPECT_CALL(*sender, AttachmentId()).WillRepeatedly(Return(attachment_id)); EXPECT_CALL(*sender, stream_ids()).WillRepeatedly(Return(local_stream_ids)); EXPECT_CALL(*sender, SetTransceiverAsStopped()); - EXPECT_CALL(*sender, Stop()); return sender; } @@ -375,8 +399,7 @@ rtc::scoped_refptr CreateMockReceiver( const rtc::scoped_refptr& track, uint32_t ssrc, int attachment_id) { - rtc::scoped_refptr receiver( - new rtc::RefCountedObject()); + auto receiver = rtc::make_ref_counted(); EXPECT_CALL(*receiver, track()).WillRepeatedly(Return(track)); EXPECT_CALL(*receiver, streams()) .WillRepeatedly( @@ -394,7 +417,7 @@ rtc::scoped_refptr CreateMockReceiver( return params; })); EXPECT_CALL(*receiver, AttachmentId()).WillRepeatedly(Return(attachment_id)); - EXPECT_CALL(*receiver, StopAndEndTrack()); + EXPECT_CALL(*receiver, Stop()).WillRepeatedly(Return()); return receiver; } @@ -465,6 +488,8 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr sender = CreateMockSender(media_type, track, ssrc, attachment_id, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); return sender; } @@ -495,6 +520,7 @@ class RTCStatsCollectorWrapper { .WillRepeatedly( Return(std::vector>( {remote_stream}))); + EXPECT_CALL(*receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(receiver); return receiver; } @@ -536,7 +562,8 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr(local_audio_track), voice_sender_info.local_stats[0].ssrc, voice_sender_info.local_stats[0].ssrc + 10, local_stream_ids); - EXPECT_CALL(*rtp_sender, SetMediaChannel(_)); + EXPECT_CALL(*rtp_sender, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*rtp_sender, Stop()); pc_->AddSender(rtp_sender); } @@ -555,7 +582,7 @@ class RTCStatsCollectorWrapper { voice_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); - EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)); + EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(rtp_receiver); } @@ -573,7 +600,8 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr(local_video_track), video_sender_info.local_stats[0].ssrc, video_sender_info.local_stats[0].ssrc + 10, local_stream_ids); - EXPECT_CALL(*rtp_sender, SetMediaChannel(_)); + EXPECT_CALL(*rtp_sender, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*rtp_sender, Stop()); pc_->AddSender(rtp_sender); } @@ -592,15 +620,12 @@ class RTCStatsCollectorWrapper { video_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); - EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)); + EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(rtp_receiver); } - auto* voice_media_channel = pc_->AddVoiceChannel("audio", "transport"); - voice_media_channel->SetStats(voice_media_info); - - auto* video_media_channel = pc_->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_media_info); + pc_->AddVoiceChannel("audio", "transport", voice_media_info); + pc_->AddVideoChannel("video", "transport", video_media_info); } private: @@ -626,7 +651,7 @@ class RTCStatsCollectorWrapper { class RTCStatsCollectorTest : public ::testing::Test { public: RTCStatsCollectorTest() - : pc_(new rtc::RefCountedObject()), + : pc_(rtc::make_ref_counted()), stats_(new RTCStatsCollectorWrapper(pc_)) {} void ExpectReportContainsCertificateInfo( @@ -708,9 +733,7 @@ class RTCStatsCollectorTest : public ::testing::Test { video_media_info.receivers[0].codec_payload_type = recv_codec.payload_type; // transport graph.transport_id = "RTCTransport_TransportName_1"; - auto* video_media_channel = - pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); // track (sender) graph.sender = stats_->SetupLocalTrackAndSender( cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 3, false, 50); @@ -823,9 +846,7 @@ class RTCStatsCollectorTest : public ::testing::Test { // transport graph.transport_id = "RTCTransport_TransportName_1"; - auto* video_media_channel = - pc_->AddVoiceChannel("VoiceMid", "TransportName"); - video_media_channel->SetStats(media_info); + pc_->AddVoiceChannel("VoiceMid", "TransportName", media_info); // track (sender) graph.sender = stats_->SetupLocalTrackAndSender( cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", kLocalSsrc, false, 50); @@ -1009,8 +1030,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) { voice_media_info.send_codecs.insert( std::make_pair(outbound_audio_codec.payload_type, outbound_audio_codec)); - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); // Video cricket::VideoMediaInfo video_media_info; @@ -1034,8 +1054,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) { video_media_info.send_codecs.insert( std::make_pair(outbound_video_codec.payload_type, outbound_video_codec)); - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -1098,6 +1117,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { const char kVideoTransport[] = "video"; pc_->AddVoiceChannel("audio", kAudioTransport); + std::unique_ptr audio_local_certinfo = CreateFakeCertificateAndInfoFromDers( std::vector({"(local) audio"})); @@ -1152,10 +1172,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { } TEST_F(RTCStatsCollectorTest, CollectTwoRTCDataChannelStatsWithPendingId) { - pc_->AddSctpDataChannel( - new MockSctpDataChannel(/*id=*/-1, DataChannelInterface::kConnecting)); - pc_->AddSctpDataChannel( - new MockSctpDataChannel(/*id=*/-1, DataChannelInterface::kConnecting)); + pc_->AddSctpDataChannel(rtc::make_ref_counted( + /*id=*/-1, DataChannelInterface::kConnecting)); + pc_->AddSctpDataChannel(rtc::make_ref_counted( + /*id=*/-1, DataChannelInterface::kConnecting)); rtc::scoped_refptr report = stats_->GetStatsReport(); } @@ -1165,7 +1185,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { // This is not a safe assumption, but in order to make it work for // the test, we reset the ID allocator at test start. SctpDataChannel::ResetInternalIdAllocatorForTesting(-1); - pc_->AddSctpDataChannel(new MockSctpDataChannel( + pc_->AddSctpDataChannel(rtc::make_ref_counted( 0, "MockSctpDataChannel0", DataChannelInterface::kConnecting, "udp", 1, 2, 3, 4)); RTCDataChannelStats expected_data_channel0("RTCDataChannel_0", 0); @@ -1178,9 +1198,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { expected_data_channel0.messages_received = 3; expected_data_channel0.bytes_received = 4; - pc_->AddSctpDataChannel(new MockSctpDataChannel(1, "MockSctpDataChannel1", - DataChannelInterface::kOpen, - "tcp", 5, 6, 7, 8)); + pc_->AddSctpDataChannel(rtc::make_ref_counted( + 1, "MockSctpDataChannel1", DataChannelInterface::kOpen, "tcp", 5, 6, 7, + 8)); RTCDataChannelStats expected_data_channel1("RTCDataChannel_1", 0); expected_data_channel1.label = "MockSctpDataChannel1"; expected_data_channel1.protocol = "tcp"; @@ -1191,7 +1211,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { expected_data_channel1.messages_received = 7; expected_data_channel1.bytes_received = 8; - pc_->AddSctpDataChannel(new MockSctpDataChannel( + pc_->AddSctpDataChannel(rtc::make_ref_counted( 2, "MockSctpDataChannel2", DataChannelInterface::kClosing, "udp", 9, 10, 11, 12)); RTCDataChannelStats expected_data_channel2("RTCDataChannel_2", 0); @@ -1204,9 +1224,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { expected_data_channel2.messages_received = 11; expected_data_channel2.bytes_received = 12; - pc_->AddSctpDataChannel(new MockSctpDataChannel(3, "MockSctpDataChannel3", - DataChannelInterface::kClosed, - "tcp", 13, 14, 15, 16)); + pc_->AddSctpDataChannel(rtc::make_ref_counted( + 3, "MockSctpDataChannel3", DataChannelInterface::kClosed, "tcp", 13, 14, + 15, 16)); RTCDataChannelStats expected_data_channel3("RTCDataChannel_3", 0); expected_data_channel3.label = "MockSctpDataChannel3"; expected_data_channel3.protocol = "tcp"; @@ -1239,9 +1259,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { // Candidates in the first transport stats. - std::unique_ptr a_local_host = - CreateFakeCandidate("1.2.3.4", 5, "a_local_host's protocol", - rtc::ADAPTER_TYPE_VPN, cricket::LOCAL_PORT_TYPE, 0); + std::unique_ptr a_local_host = CreateFakeCandidate( + "1.2.3.4", 5, "a_local_host's protocol", rtc::ADAPTER_TYPE_VPN, + cricket::LOCAL_PORT_TYPE, 0, rtc::ADAPTER_TYPE_ETHERNET); RTCLocalIceCandidateStats expected_a_local_host( "RTCIceCandidate_" + a_local_host->id(), 0); expected_a_local_host.transport_id = "RTCTransport_a_0"; @@ -1252,7 +1272,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_host.protocol = "a_local_host's protocol"; expected_a_local_host.candidate_type = "host"; expected_a_local_host.priority = 0; - EXPECT_FALSE(*expected_a_local_host.is_remote); + expected_a_local_host.vpn = true; + expected_a_local_host.network_adapter_type = RTCNetworkAdapterType::kEthernet; std::unique_ptr a_remote_srflx = CreateFakeCandidate( "6.7.8.9", 10, "remote_srflx's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1266,11 +1287,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_remote_srflx.protocol = "remote_srflx's protocol"; expected_a_remote_srflx.candidate_type = "srflx"; expected_a_remote_srflx.priority = 1; - EXPECT_TRUE(*expected_a_remote_srflx.is_remote); std::unique_ptr a_local_prflx = CreateFakeCandidate( - "11.12.13.14", 15, "a_local_prflx's protocol", rtc::ADAPTER_TYPE_CELLULAR, - cricket::PRFLX_PORT_TYPE, 2); + "11.12.13.14", 15, "a_local_prflx's protocol", + rtc::ADAPTER_TYPE_CELLULAR_2G, cricket::PRFLX_PORT_TYPE, 2); RTCLocalIceCandidateStats expected_a_local_prflx( "RTCIceCandidate_" + a_local_prflx->id(), 0); expected_a_local_prflx.transport_id = "RTCTransport_a_0"; @@ -1281,7 +1301,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_prflx.protocol = "a_local_prflx's protocol"; expected_a_local_prflx.candidate_type = "prflx"; expected_a_local_prflx.priority = 2; - EXPECT_FALSE(*expected_a_local_prflx.is_remote); + expected_a_local_prflx.vpn = false; + expected_a_local_prflx.network_adapter_type = + RTCNetworkAdapterType::kCellular2g; std::unique_ptr a_remote_relay = CreateFakeCandidate( "16.17.18.19", 20, "a_remote_relay's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1295,16 +1317,17 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_remote_relay.protocol = "a_remote_relay's protocol"; expected_a_remote_relay.candidate_type = "relay"; expected_a_remote_relay.priority = 3; - EXPECT_TRUE(*expected_a_remote_relay.is_remote); std::unique_ptr a_local_relay = CreateFakeCandidate( "16.17.18.19", 21, "a_local_relay's protocol", rtc::ADAPTER_TYPE_UNKNOWN, cricket::RELAY_PORT_TYPE, 1); a_local_relay->set_relay_protocol("tcp"); + a_local_relay->set_url("turn:url1"); - RTCRemoteIceCandidateStats expected_a_local_relay( + RTCLocalIceCandidateStats expected_a_local_relay( "RTCIceCandidate_" + a_local_relay->id(), 0); expected_a_local_relay.transport_id = "RTCTransport_a_0"; + expected_a_local_relay.network_type = "unknown"; expected_a_local_relay.ip = "16.17.18.19"; expected_a_local_relay.address = "16.17.18.19"; expected_a_local_relay.port = 21; @@ -1312,7 +1335,29 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_relay.relay_protocol = "tcp"; expected_a_local_relay.candidate_type = "relay"; expected_a_local_relay.priority = 1; - EXPECT_TRUE(*expected_a_local_relay.is_remote); + expected_a_local_relay.url = "turn:url1"; + expected_a_local_relay.vpn = false; + expected_a_local_relay.network_adapter_type = RTCNetworkAdapterType::kUnknown; + + std::unique_ptr a_local_relay_prflx = CreateFakeCandidate( + "11.12.13.20", 22, "a_local_relay_prflx's protocol", + rtc::ADAPTER_TYPE_UNKNOWN, cricket::PRFLX_PORT_TYPE, 1); + a_local_relay_prflx->set_relay_protocol("udp"); + + RTCLocalIceCandidateStats expected_a_local_relay_prflx( + "RTCIceCandidate_" + a_local_relay_prflx->id(), 0); + expected_a_local_relay_prflx.transport_id = "RTCTransport_a_0"; + expected_a_local_relay_prflx.network_type = "unknown"; + expected_a_local_relay_prflx.ip = "11.12.13.20"; + expected_a_local_relay_prflx.address = "11.12.13.20"; + expected_a_local_relay_prflx.port = 22; + expected_a_local_relay_prflx.protocol = "a_local_relay_prflx's protocol"; + expected_a_local_relay_prflx.relay_protocol = "udp"; + expected_a_local_relay_prflx.candidate_type = "prflx"; + expected_a_local_relay_prflx.priority = 1; + expected_a_local_relay_prflx.vpn = false; + expected_a_local_relay_prflx.network_adapter_type = + RTCNetworkAdapterType::kUnknown; // Candidates in the second transport stats. std::unique_ptr b_local = @@ -1328,7 +1373,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_b_local.protocol = "b_local's protocol"; expected_b_local.candidate_type = "host"; expected_b_local.priority = 42; - EXPECT_FALSE(*expected_b_local.is_remote); + expected_b_local.vpn = false; + expected_b_local.network_adapter_type = RTCNetworkAdapterType::kWifi; std::unique_ptr b_remote = CreateFakeCandidate( "42.42.42.42", 42, "b_remote's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1342,7 +1388,6 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_b_remote.protocol = "b_remote's protocol"; expected_b_remote.candidate_type = "host"; expected_b_remote.priority = 42; - EXPECT_TRUE(*expected_b_remote.is_remote); // Add candidate pairs to connection. cricket::TransportChannelStats a_transport_channel_stats; @@ -1364,6 +1409,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { .local_candidate = *a_local_relay.get(); a_transport_channel_stats.ice_transport_stats.connection_infos[2] .remote_candidate = *a_remote_relay.get(); + a_transport_channel_stats.ice_transport_stats.connection_infos.push_back( + cricket::ConnectionInfo()); + a_transport_channel_stats.ice_transport_stats.connection_infos[3] + .local_candidate = *a_local_relay_prflx.get(); + a_transport_channel_stats.ice_transport_stats.connection_infos[3] + .remote_candidate = *a_remote_relay.get(); pc_->AddVoiceChannel("audio", "a"); pc_->SetTransportStats("a", a_transport_channel_stats); @@ -1395,6 +1446,13 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { EXPECT_EQ(expected_a_remote_relay, report->Get(expected_a_remote_relay.id()) ->cast_to()); + ASSERT_TRUE(report->Get(expected_a_local_relay.id())); + EXPECT_EQ(expected_a_local_relay, report->Get(expected_a_local_relay.id()) + ->cast_to()); + ASSERT_TRUE(report->Get(expected_a_local_relay_prflx.id())); + EXPECT_EQ(expected_a_local_relay_prflx, + report->Get(expected_a_local_relay_prflx.id()) + ->cast_to()); ASSERT_TRUE(report->Get(expected_b_local.id())); EXPECT_EQ( expected_b_local, @@ -1421,6 +1479,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { connection_info.local_candidate = *local_candidate.get(); connection_info.remote_candidate = *remote_candidate.get(); connection_info.writable = true; + connection_info.sent_discarded_packets = 3; + connection_info.sent_total_packets = 10; + connection_info.packets_received = 51; + connection_info.sent_discarded_bytes = 7; connection_info.sent_total_bytes = 42; connection_info.recv_total_bytes = 1234; connection_info.total_round_trip_time_ms = 0; @@ -1458,8 +1520,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { expected_pair.priority = 5555; expected_pair.nominated = false; expected_pair.writable = true; + expected_pair.packets_sent = 7; + expected_pair.packets_received = 51; + expected_pair.packets_discarded_on_send = 3; expected_pair.bytes_sent = 42; expected_pair.bytes_received = 1234; + expected_pair.bytes_discarded_on_send = 7; expected_pair.total_round_trip_time = 0.0; expected_pair.requests_received = 2020; expected_pair.requests_sent = 2000; @@ -1543,7 +1609,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { expected_local_candidate.protocol = "protocol"; expected_local_candidate.candidate_type = "host"; expected_local_candidate.priority = 42; - EXPECT_FALSE(*expected_local_candidate.is_remote); + expected_local_candidate.vpn = false; + expected_local_candidate.network_adapter_type = RTCNetworkAdapterType::kWifi; ASSERT_TRUE(report->Get(expected_local_candidate.id())); EXPECT_EQ(expected_local_candidate, report->Get(expected_local_candidate.id()) @@ -1558,7 +1625,6 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { expected_remote_candidate.protocol = "protocol"; expected_remote_candidate.candidate_type = "host"; expected_remote_candidate.priority = 42; - EXPECT_TRUE(*expected_remote_candidate.is_remote); ASSERT_TRUE(report->Get(expected_remote_candidate.id())); EXPECT_EQ(expected_remote_candidate, report->Get(expected_remote_candidate.id()) @@ -1985,8 +2051,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { voice_media_info.receive_codecs.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + auto* voice_media_channel = + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); stats_->SetupRemoteTrackAndReceiver( cricket::MEDIA_TYPE_AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); @@ -2090,8 +2156,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { video_media_info.receive_codecs.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + auto* video_media_channel = + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); stats_->SetupRemoteTrackAndReceiver( cricket::MEDIA_TYPE_VIDEO, "RemoteVideoTrackID", "RemoteStreamId", 1); @@ -2168,6 +2234,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { voice_media_info.senders[0].header_and_padding_bytes_sent = 12; voice_media_info.senders[0].retransmitted_bytes_sent = 30; voice_media_info.senders[0].nacks_rcvd = 31; + voice_media_info.senders[0].target_bitrate = 32000; voice_media_info.senders[0].codec_payload_type = 42; RtpCodecParameters codec_parameters; @@ -2178,8 +2245,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { voice_media_info.send_codecs.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", 1, true, /*attachment_id=*/50); @@ -2202,6 +2268,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { expected_audio.header_bytes_sent = 12; expected_audio.retransmitted_bytes_sent = 30; expected_audio.nack_count = 31; + expected_audio.target_bitrate = 32000; ASSERT_TRUE(report->Get(expected_audio.id())); EXPECT_EQ( @@ -2259,8 +2326,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.send_codecs.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + auto* video_media_channel = + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 1, true, /*attachment_id=*/50); @@ -2297,7 +2364,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { expected_video.total_packet_send_delay = 10.0; expected_video.quality_limitation_reason = "bandwidth"; expected_video.quality_limitation_durations = std::map{ - std::pair{"bandwidth", 300.0}, + std::pair{"bandwidth", 0.3}, }; expected_video.quality_limitation_resolution_changes = 56u; expected_video.frame_width = 200u; @@ -2369,6 +2436,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( rtp_connection_info); rtp_transport_channel_stats.dtls_state = DtlsTransportState::kNew; + rtp_transport_channel_stats.ice_transport_stats.bytes_sent = 42; + rtp_transport_channel_stats.ice_transport_stats.packets_sent = 1; + rtp_transport_channel_stats.ice_transport_stats.bytes_received = 1337; + rtp_transport_channel_stats.ice_transport_stats.packets_received = 4; rtp_transport_channel_stats.ice_transport_stats .selected_candidate_pair_changes = 1; pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats}); @@ -2407,6 +2478,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { rtcp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( rtcp_connection_info); rtcp_transport_channel_stats.dtls_state = DtlsTransportState::kConnecting; + rtcp_transport_channel_stats.ice_transport_stats.bytes_sent = 1337; + rtcp_transport_channel_stats.ice_transport_stats.packets_sent = 1; + rtcp_transport_channel_stats.ice_transport_stats.bytes_received = 42; + rtcp_transport_channel_stats.ice_transport_stats.packets_received = 4; pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats, rtcp_transport_channel_stats}); @@ -2512,11 +2587,6 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { rtp_connection_info.best_connection = false; rtp_connection_info.local_candidate = *rtp_local_candidate.get(); rtp_connection_info.remote_candidate = *rtp_remote_candidate.get(); - rtp_connection_info.sent_total_bytes = 42; - rtp_connection_info.recv_total_bytes = 1337; - rtp_connection_info.sent_total_packets = 3; - rtp_connection_info.sent_discarded_packets = 2; - rtp_connection_info.packets_received = 4; cricket::TransportChannelStats rtp_transport_channel_stats; rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( @@ -2538,12 +2608,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { "RTCTransport_transport_" + rtc::ToString(cricket::ICE_CANDIDATE_COMPONENT_RTP), report->timestamp_us()); - expected_rtp_transport.bytes_sent = 42; - expected_rtp_transport.packets_sent = 1; - expected_rtp_transport.bytes_received = 1337; - expected_rtp_transport.packets_received = 4; expected_rtp_transport.dtls_state = RTCDtlsTransportState::kConnected; expected_rtp_transport.selected_candidate_pair_changes = 1; + expected_rtp_transport.bytes_sent = 0; + expected_rtp_transport.bytes_received = 0; + expected_rtp_transport.packets_sent = 0; + expected_rtp_transport.packets_received = 0; // Crypto parameters expected_rtp_transport.tls_version = "0203"; expected_rtp_transport.dtls_cipher = "TLS_RSA_WITH_AES_128_CBC_SHA"; @@ -2578,8 +2648,7 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) { std::make_pair(codec_parameters.payload_type, codec_parameters)); // Emulates the case where AddTrack is used without an associated MediaStream - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", 1, false, /*attachment_id=*/50); @@ -2625,8 +2694,7 @@ TEST_F(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { voice_media_info.senders[0].apm_statistics.echo_return_loss = 42.0; voice_media_info.senders[0].apm_statistics.echo_return_loss_enhancement = 52.0; - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", kSsrc, false, kAttachmentId); @@ -2659,14 +2727,13 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { video_media_info.senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; - video_media_info.senders[0].framerate_input = 29; + video_media_info.senders[0].framerate_input = 29.0; video_media_info.aggregated_senders[0].local_stats.push_back( cricket::SsrcSenderInfo()); video_media_info.aggregated_senders[0].local_stats[0].ssrc = kSsrc; - video_media_info.aggregated_senders[0].framerate_input = 29; + video_media_info.aggregated_senders[0].framerate_input = 29.0; video_media_info.aggregated_senders[0].frames = 10001; - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, kVideoSourceHeight); @@ -2674,6 +2741,8 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2684,7 +2753,7 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { expected_video.kind = "video"; expected_video.width = kVideoSourceWidth; expected_video.height = kVideoSourceHeight; - expected_video.frames_per_second = 29; + expected_video.frames_per_second = 29.0; expected_video.frames = 10001; ASSERT_TRUE(report->Get(expected_video.id())); @@ -2708,9 +2777,8 @@ TEST_F(RTCStatsCollectorTest, cricket::VideoMediaInfo video_media_info; video_media_info.senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); - video_media_info.senders[0].framerate_input = 29; - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + video_media_info.senders[0].framerate_input = 29.0; + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, kVideoSourceHeight); @@ -2718,6 +2786,8 @@ TEST_F(RTCStatsCollectorTest, "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kNoSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2739,15 +2809,16 @@ TEST_F(RTCStatsCollectorTest, video_media_info.senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; - video_media_info.senders[0].framerate_input = 29; - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + video_media_info.senders[0].framerate_input = 29.0; + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); auto video_track = FakeVideoTrackForStats::Create( "LocalVideoTrackID", MediaStreamTrackInterface::kLive, /*source=*/nullptr); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2767,10 +2838,11 @@ TEST_F(RTCStatsCollectorTest, voice_media_info.senders.push_back(cricket::VoiceSenderInfo()); voice_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); voice_media_info.senders[0].local_stats[0].ssrc = kSsrc; - auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_AUDIO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2795,7 +2867,7 @@ class RTCStatsCollectorTestWithParamKind return "Video"; case cricket::MEDIA_TYPE_DATA: case cricket::MEDIA_TYPE_UNSUPPORTED: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } } @@ -2828,8 +2900,7 @@ class RTCStatsCollectorTestWithParamKind sender.report_block_datas.push_back(report_block_data); voice_media_info.senders.push_back(sender); } - auto* voice_media_channel = pc_->AddVoiceChannel("mid", transport_name); - voice_media_channel->SetStats(voice_media_info); + pc_->AddVoiceChannel("mid", transport_name, voice_media_info); return; } case cricket::MEDIA_TYPE_VIDEO: { @@ -2848,13 +2919,12 @@ class RTCStatsCollectorTestWithParamKind video_media_info.aggregated_senders.push_back(sender); video_media_info.senders.push_back(sender); } - auto* video_media_channel = pc_->AddVideoChannel("mid", transport_name); - video_media_channel->SetStats(video_media_info); + pc_->AddVideoChannel("mid", transport_name, video_media_info); return; } case cricket::MEDIA_TYPE_DATA: case cricket::MEDIA_TYPE_UNSUPPORTED: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -3091,12 +3161,13 @@ TEST_F(RTCStatsCollectorTest, video_media_info.senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; - video_media_info.senders[0].framerate_input = 29; - auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); - video_media_channel->SetStats(video_media_info); + video_media_info.senders[0].framerate_input = 29.0; + pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -3246,6 +3317,7 @@ TEST_F(RTCStatsCollectorTest, StatsReportedOnZeroSsrc) { MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 0, 49, {}); + EXPECT_CALL(*sender, Stop()); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -3265,6 +3337,7 @@ TEST_F(RTCStatsCollectorTest, DoNotCrashOnSsrcChange) { MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 4711, 49, {}); + EXPECT_CALL(*sender, Stop()); pc_->AddSender(sender); // We do not generate any matching voice_sender_info stats. @@ -3296,10 +3369,8 @@ class RecursiveCallback : public RTCStatsCollectorCallback { // Test that nothing bad happens if a callback causes GetStatsReport to be // called again recursively. Regression test for crbug.com/webrtc/8973. TEST_F(RTCStatsCollectorTest, DoNotCrashWhenGetStatsCalledDuringCallback) { - rtc::scoped_refptr callback1( - new rtc::RefCountedObject(stats_.get())); - rtc::scoped_refptr callback2( - new rtc::RefCountedObject(stats_.get())); + auto callback1 = rtc::make_ref_counted(stats_.get()); + auto callback2 = rtc::make_ref_counted(stats_.get()); stats_->stats_collector()->GetStatsReport(callback1); stats_->stats_collector()->GetStatsReport(callback2); EXPECT_TRUE_WAIT(callback1->called(), kGetStatsReportTimeoutMs); @@ -3326,8 +3397,9 @@ class FakeRTCStatsCollector : public RTCStatsCollector, static rtc::scoped_refptr Create( PeerConnectionInternal* pc, int64_t cache_lifetime_us) { - return new rtc::RefCountedObject(pc, - cache_lifetime_us); + return rtc::scoped_refptr( + new rtc::RefCountedObject(pc, + cache_lifetime_us)); } // Since FakeRTCStatsCollector inherits twice from RefCountInterface, once via @@ -3419,8 +3491,7 @@ class FakeRTCStatsCollector : public RTCStatsCollector, }; TEST(RTCStatsCollectorTestWithFakeCollector, ThreadUsageAndResultsMerging) { - rtc::scoped_refptr pc( - new rtc::RefCountedObject()); + auto pc = rtc::make_ref_counted(); rtc::scoped_refptr stats_collector( FakeRTCStatsCollector::Create(pc, 50 * rtc::kNumMicrosecsPerMillisec)); stats_collector->VerifyThreadUsageAndResultsMerging(); diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index afa50d84df..085f0636a4 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -19,8 +18,6 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/audio_options.h" @@ -123,6 +120,7 @@ class RTCStatsIntegrationTest : public ::testing::Test { void StartCall() { // Create PeerConnections and "connect" sigslots PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; PeerConnectionInterface::IceServer ice_server; ice_server.uri = "stun:1.1.1.1:3478"; config.servers.push_back(ice_server); @@ -489,7 +487,13 @@ class RTCStatsReportVerifier { verifier.TestMemberIsDefined(candidate_pair.nominated); verifier.TestMemberIsDefined(candidate_pair.writable); verifier.TestMemberIsUndefined(candidate_pair.readable); + verifier.TestMemberIsNonNegative(candidate_pair.packets_sent); + verifier.TestMemberIsNonNegative( + candidate_pair.packets_discarded_on_send); + verifier.TestMemberIsNonNegative(candidate_pair.packets_received); verifier.TestMemberIsNonNegative(candidate_pair.bytes_sent); + verifier.TestMemberIsNonNegative( + candidate_pair.bytes_discarded_on_send); verifier.TestMemberIsNonNegative(candidate_pair.bytes_received); verifier.TestMemberIsNonNegative( candidate_pair.total_round_trip_time); @@ -517,6 +521,7 @@ class RTCStatsReportVerifier { candidate_pair.consent_requests_sent); verifier.TestMemberIsUndefined(candidate_pair.consent_responses_received); verifier.TestMemberIsUndefined(candidate_pair.consent_responses_sent); + return verifier.ExpectAllMembersSuccessfullyTested(); } @@ -527,8 +532,12 @@ class RTCStatsReportVerifier { verifier.TestMemberIsDefined(candidate.is_remote); if (*candidate.is_remote) { verifier.TestMemberIsUndefined(candidate.network_type); + verifier.TestMemberIsUndefined(candidate.network_adapter_type); + verifier.TestMemberIsUndefined(candidate.vpn); } else { verifier.TestMemberIsDefined(candidate.network_type); + verifier.TestMemberIsDefined(candidate.network_adapter_type); + verifier.TestMemberIsDefined(candidate.vpn); } verifier.TestMemberIsDefined(candidate.ip); verifier.TestMemberIsDefined(candidate.address); @@ -958,7 +967,6 @@ class RTCStatsReportVerifier { outbound_stream.header_bytes_sent); verifier.TestMemberIsNonNegative( outbound_stream.retransmitted_bytes_sent); - verifier.TestMemberIsUndefined(outbound_stream.target_bitrate); if (outbound_stream.media_type.is_defined() && *outbound_stream.media_type == "video") { verifier.TestMemberIsDefined(outbound_stream.frames_encoded); @@ -994,6 +1002,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsNonNegative(outbound_stream.frames_sent); verifier.TestMemberIsNonNegative( outbound_stream.huge_frames_sent); + verifier.TestMemberIsUndefined(outbound_stream.target_bitrate); verifier.MarkMemberTested(outbound_stream.rid, true); } else { verifier.TestMemberIsUndefined(outbound_stream.frames_encoded); @@ -1017,6 +1026,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(outbound_stream.frame_width); verifier.TestMemberIsUndefined(outbound_stream.frames_sent); verifier.TestMemberIsUndefined(outbound_stream.huge_frames_sent); + verifier.TestMemberIsNonNegative(outbound_stream.target_bitrate); } return verifier.ExpectAllMembersSuccessfullyTested(); } @@ -1105,7 +1115,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(video_source.width); verifier.TestMemberIsUndefined(video_source.height); verifier.TestMemberIsNonNegative(video_source.frames); - verifier.TestMemberIsNonNegative(video_source.frames_per_second); + verifier.TestMemberIsNonNegative(video_source.frames_per_second); return verifier.ExpectAllMembersSuccessfullyTested(); } diff --git a/pc/rtc_stats_traversal.cc b/pc/rtc_stats_traversal.cc index 49e79fec52..6d886f5099 100644 --- a/pc/rtc_stats_traversal.cc +++ b/pc/rtc_stats_traversal.cc @@ -140,7 +140,7 @@ std::vector GetStatsReferencedIds(const RTCStats& stats) { AddIdIfDefined(transport.local_certificate_id, &neighbor_ids); AddIdIfDefined(transport.remote_certificate_id, &neighbor_ids); } else { - RTC_NOTREACHED() << "Unrecognized type: " << type; + RTC_DCHECK_NOTREACHED() << "Unrecognized type: " << type; } return neighbor_ids; } diff --git a/pc/rtc_stats_traversal_unittest.cc b/pc/rtc_stats_traversal_unittest.cc index c7d097117e..6e9b784313 100644 --- a/pc/rtc_stats_traversal_unittest.cc +++ b/pc/rtc_stats_traversal_unittest.cc @@ -11,7 +11,6 @@ #include "pc/rtc_stats_traversal.h" #include -#include #include #include "api/stats/rtcstats_objects.h" diff --git a/pc/rtp_media_utils.cc b/pc/rtp_media_utils.cc index c5d642b685..52c5bb0eac 100644 --- a/pc/rtp_media_utils.cc +++ b/pc/rtp_media_utils.cc @@ -49,7 +49,7 @@ RtpTransceiverDirection RtpTransceiverDirectionReversed( case RtpTransceiverDirection::kRecvOnly: return RtpTransceiverDirection::kSendOnly; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return direction; } } @@ -81,7 +81,7 @@ const char* RtpTransceiverDirectionToString(RtpTransceiverDirection direction) { case RtpTransceiverDirection::kStopped: return "kStopped"; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } diff --git a/pc/rtp_media_utils.h b/pc/rtp_media_utils.h index 6f7986f096..240274fe05 100644 --- a/pc/rtp_media_utils.h +++ b/pc/rtp_media_utils.h @@ -11,8 +11,9 @@ #ifndef PC_RTP_MEDIA_UTILS_H_ #define PC_RTP_MEDIA_UTILS_H_ +#include // no-presubmit-check TODO(webrtc:8982) + #include "api/rtp_transceiver_direction.h" -#include "api/rtp_transceiver_interface.h" namespace webrtc { diff --git a/pc/rtp_parameters_conversion.cc b/pc/rtp_parameters_conversion.cc index 8d3064ed93..afba4bc94f 100644 --- a/pc/rtp_parameters_conversion.cc +++ b/pc/rtp_parameters_conversion.cc @@ -10,10 +10,10 @@ #include "pc/rtp_parameters_conversion.h" -#include #include #include #include +#include #include #include "api/array_view.h" diff --git a/pc/rtp_parameters_conversion.h b/pc/rtp_parameters_conversion.h index 62e4685722..959f3fde47 100644 --- a/pc/rtp_parameters_conversion.h +++ b/pc/rtp_parameters_conversion.h @@ -11,7 +11,6 @@ #ifndef PC_RTP_PARAMETERS_CONVERSION_H_ #define PC_RTP_PARAMETERS_CONVERSION_H_ -#include #include #include "absl/types/optional.h" diff --git a/pc/rtp_parameters_conversion_unittest.cc b/pc/rtp_parameters_conversion_unittest.cc index 99d976abcd..50d90e1c30 100644 --- a/pc/rtp_parameters_conversion_unittest.cc +++ b/pc/rtp_parameters_conversion_unittest.cc @@ -10,10 +10,13 @@ #include "pc/rtp_parameters_conversion.h" -#include +#include +#include +#include -#include "rtc_base/gunit.h" +#include "api/media_types.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::UnorderedElementsAre; diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc index 2444c9b60d..a2b3353c0e 100644 --- a/pc/rtp_receiver.cc +++ b/pc/rtp_receiver.cc @@ -17,7 +17,7 @@ #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" -#include "rtc_base/location.h" +#include "rtc_base/thread.h" namespace webrtc { diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 73fc5b9858..7d124dfd69 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h @@ -42,16 +42,27 @@ namespace webrtc { // Internal class used by PeerConnection. class RtpReceiverInternal : public RtpReceiverInterface { public: - // Stops receiving. The track may be reactivated. + // Call on the signaling thread, to let the receiver know that the the + // embedded source object should enter a stopped/ended state and the track's + // state set to `kEnded`, a final state that cannot be reversed. virtual void Stop() = 0; - // Stops the receiver permanently. - // Causes the associated track to enter kEnded state. Cannot be reversed. - virtual void StopAndEndTrack() = 0; + + // Call on the signaling thread to set the source's state to `ended` before + // clearing the media channel (`SetMediaChannel(nullptr)`) on the worker + // thread. + // The difference between `Stop()` and `SetSourceEnded()` is that the latter + // does not change the state of the associated track. + // NOTE: Calling this function should be followed with a call to + // `SetMediaChannel(nullptr)` on the worker thread, to complete the operation. + virtual void SetSourceEnded() = 0; // Sets the underlying MediaEngine channel associated with this RtpSender. // A VoiceMediaChannel should be used for audio RtpSenders and // a VideoMediaChannel should be used for video RtpSenders. - // Must call SetMediaChannel(nullptr) before the media channel is destroyed. + // NOTE: + // * SetMediaChannel(nullptr) must be called before the media channel is + // destroyed. + // * This method must be invoked on the worker thread. virtual void SetMediaChannel(cricket::MediaChannel* media_channel) = 0; // Configures the RtpReceiver with the underlying media channel, with the diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 2e36f4f99a..852e84fed2 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -111,7 +112,8 @@ bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) { RtpSenderBase::RtpSenderBase(rtc::Thread* worker_thread, const std::string& id, SetStreamsObserver* set_streams_observer) - : worker_thread_(worker_thread), + : signaling_thread_(rtc::Thread::Current()), + worker_thread_(worker_thread), id_(id), set_streams_observer_(set_streams_observer) { RTC_DCHECK(worker_thread); @@ -120,6 +122,7 @@ RtpSenderBase::RtpSenderBase(rtc::Thread* worker_thread, void RtpSenderBase::SetFrameEncryptor( rtc::scoped_refptr frame_encryptor) { + RTC_DCHECK_RUN_ON(signaling_thread_); frame_encryptor_ = std::move(frame_encryptor); // Special Case: Set the frame encryptor to any value on any existing channel. if (media_channel_ && ssrc_ && !stopped_) { @@ -136,6 +139,7 @@ void RtpSenderBase::SetMediaChannel(cricket::MediaChannel* media_channel) { } RtpParameters RtpSenderBase::GetParametersInternal() const { + RTC_DCHECK_RUN_ON(signaling_thread_); if (stopped_) { return RtpParameters(); } @@ -150,6 +154,7 @@ RtpParameters RtpSenderBase::GetParametersInternal() const { } RtpParameters RtpSenderBase::GetParameters() const { + RTC_DCHECK_RUN_ON(signaling_thread_); RtpParameters result = GetParametersInternal(); last_transaction_id_ = rtc::CreateRandomUuid(); result.transaction_id = last_transaction_id_.value(); @@ -157,6 +162,7 @@ RtpParameters RtpSenderBase::GetParameters() const { } RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(!stopped_); if (UnimplementedRtpParameterHasValue(parameters)) { @@ -186,6 +192,7 @@ RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { } RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters"); if (is_transceiver_stopped_) { LOG_AND_RETURN_ERROR( @@ -225,6 +232,7 @@ void RtpSenderBase::SetStreams(const std::vector& stream_ids) { } bool RtpSenderBase::SetTrack(MediaStreamTrackInterface* track) { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::SetTrack"); if (stopped_) { RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender."; @@ -266,6 +274,7 @@ bool RtpSenderBase::SetTrack(MediaStreamTrackInterface* track) { } void RtpSenderBase::SetSsrc(uint32_t ssrc) { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::SetSsrc"); if (stopped_ || ssrc == ssrc_) { return; @@ -315,6 +324,7 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) { } void RtpSenderBase::Stop() { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::Stop"); // TODO(deadbeef): Need to do more here to fully stop sending packets. if (stopped_) { @@ -335,6 +345,7 @@ void RtpSenderBase::Stop() { RTCError RtpSenderBase::DisableEncodingLayers( const std::vector& rids) { + RTC_DCHECK_RUN_ON(signaling_thread_); if (stopped_) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, "Cannot disable encodings on a stopped sender."); @@ -381,6 +392,7 @@ RTCError RtpSenderBase::DisableEncodingLayers( void RtpSenderBase::SetEncoderToPacketizerFrameTransformer( rtc::scoped_refptr frame_transformer) { + RTC_DCHECK_RUN_ON(signaling_thread_); frame_transformer_ = std::move(frame_transformer); if (media_channel_ && ssrc_ && !stopped_) { worker_thread_->Invoke(RTC_FROM_HERE, [&] { @@ -483,6 +495,7 @@ sigslot::signal0<>* AudioRtpSender::GetOnDestroyedSignal() { } void AudioRtpSender::OnChanged() { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "AudioRtpSender::OnChanged"); RTC_DCHECK(!stopped_); if (cached_track_enabled_ != track_->enabled()) { @@ -517,10 +530,12 @@ void AudioRtpSender::RemoveTrackFromStats() { } rtc::scoped_refptr AudioRtpSender::GetDtmfSender() const { + RTC_DCHECK_RUN_ON(signaling_thread_); return dtmf_sender_proxy_; } void AudioRtpSender::SetSend() { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(!stopped_); RTC_DCHECK(can_send_track()); if (!media_channel_) { @@ -551,6 +566,7 @@ void AudioRtpSender::SetSend() { } void AudioRtpSender::ClearSend() { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(ssrc_ != 0); RTC_DCHECK(!stopped_); if (!media_channel_) { @@ -584,10 +600,13 @@ VideoRtpSender::~VideoRtpSender() { } void VideoRtpSender::OnChanged() { + RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "VideoRtpSender::OnChanged"); RTC_DCHECK(!stopped_); - if (cached_track_content_hint_ != video_track()->content_hint()) { - cached_track_content_hint_ = video_track()->content_hint(); + + auto content_hint = video_track()->content_hint(); + if (cached_track_content_hint_ != content_hint) { + cached_track_content_hint_ = content_hint; if (can_send_track()) { SetSend(); } @@ -600,11 +619,13 @@ void VideoRtpSender::AttachTrack() { } rtc::scoped_refptr VideoRtpSender::GetDtmfSender() const { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_LOG(LS_INFO) << "Tried to get DTMF sender from video sender."; return nullptr; } void VideoRtpSender::SetSend() { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(!stopped_); RTC_DCHECK(can_send_track()); if (!media_channel_) { @@ -636,6 +657,7 @@ void VideoRtpSender::SetSend() { } void VideoRtpSender::ClearSend() { + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(ssrc_ != 0); RTC_DCHECK(!stopped_); if (!media_channel_) { diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 4bc16c796f..569a6007d3 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -17,6 +17,7 @@ #include #include + #include #include #include @@ -32,13 +33,16 @@ #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" #include "media/base/audio_source.h" #include "media/base/media_channel.h" #include "pc/dtmf_sender.h" #include "pc/stats_collector_interface.h" +#include "rtc_base/checks.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -104,6 +108,9 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { bool SetTrack(MediaStreamTrackInterface* track) override; rtc::scoped_refptr track() const override { + // This method is currently called from the worker thread by + // RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n. + // RTC_DCHECK_RUN_ON(signaling_thread_); return track_; } @@ -120,9 +127,17 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { // underlying transport (this occurs if the sender isn't seen in a local // description). void SetSsrc(uint32_t ssrc) override; - uint32_t ssrc() const override { return ssrc_; } + uint32_t ssrc() const override { + // This method is currently called from the worker thread by + // RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n. + // RTC_DCHECK_RUN_ON(signaling_thread_); + return ssrc_; + } - std::vector stream_ids() const override { return stream_ids_; } + std::vector stream_ids() const override { + RTC_DCHECK_RUN_ON(signaling_thread_); + return stream_ids_; + } void set_stream_ids(const std::vector& stream_ids) override { stream_ids_ = stream_ids; } @@ -135,6 +150,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { init_parameters_.encodings = init_send_encodings; } std::vector init_send_encodings() const override { + RTC_DCHECK_RUN_ON(signaling_thread_); return init_parameters_.encodings; } @@ -143,6 +159,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { dtls_transport_ = dtls_transport; } rtc::scoped_refptr dtls_transport() const override { + RTC_DCHECK_RUN_ON(signaling_thread_); return dtls_transport_; } @@ -168,7 +185,10 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { void SetEncoderToPacketizerFrameTransformer( rtc::scoped_refptr frame_transformer) override; - void SetTransceiverAsStopped() override { is_transceiver_stopped_ = true; } + void SetTransceiverAsStopped() override { + RTC_DCHECK_RUN_ON(signaling_thread_); + is_transceiver_stopped_ = true; + } protected: // If `set_streams_observer` is not null, it is invoked when SetStreams() @@ -195,16 +215,22 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { virtual void AddTrackToStats() {} virtual void RemoveTrackFromStats() {} - rtc::Thread* worker_thread_; + rtc::Thread* const signaling_thread_; + rtc::Thread* const worker_thread_; uint32_t ssrc_ = 0; - bool stopped_ = false; - bool is_transceiver_stopped_ = false; + bool stopped_ RTC_GUARDED_BY(signaling_thread_) = false; + bool is_transceiver_stopped_ RTC_GUARDED_BY(signaling_thread_) = false; int attachment_id_ = 0; const std::string id_; std::vector stream_ids_; RtpParameters init_parameters_; + // TODO(tommi): `media_channel_` and several other member variables in this + // class (ssrc_, stopped_, etc) are accessed from more than one thread without + // a guard or lock. Internally there are also several Invoke()s that we could + // remove since the upstream code may already be performing several operations + // on the worker thread. cricket::MediaChannel* media_channel_ = nullptr; rtc::scoped_refptr track_; diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc index f36183366a..a7b9c9ab68 100644 --- a/pc/rtp_sender_receiver_unittest.cc +++ b/pc/rtp_sender_receiver_unittest.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -28,15 +29,20 @@ #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" #include "api/test/fake_frame_decryptor.h" #include "api/test/fake_frame_encryptor.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "api/video/video_bitrate_allocator_factory.h" +#include "api/video/video_codec_constants.h" #include "media/base/codec.h" +#include "media/base/delayable.h" #include "media/base/fake_media_engine.h" #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" +#include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "media/base/test_utils.h" #include "media/engine/fake_webrtc_call.h" @@ -50,8 +56,6 @@ #include "pc/dtls_srtp_transport.h" #include "pc/local_audio_source.h" #include "pc/media_stream.h" -#include "pc/remote_audio_source.h" -#include "pc/rtp_receiver.h" #include "pc/rtp_sender.h" #include "pc/rtp_transport_internal.h" #include "pc/test/fake_video_track_source.h" @@ -59,11 +63,14 @@ #include "pc/video_track.h" #include "rtc_base/checks.h" #include "rtc_base/gunit.h" +#include "rtc_base/location.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/run_loop.h" +#include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::ContainerEq; @@ -122,14 +129,16 @@ class RtpSenderReceiverTest rtp_transport_ = CreateDtlsSrtpTransport(); voice_channel_ = channel_manager_->CreateVoiceChannel( - &fake_call_, cricket::MediaConfig(), rtp_transport_.get(), - rtc::Thread::Current(), cricket::CN_AUDIO, srtp_required, - webrtc::CryptoOptions(), &ssrc_generator_, cricket::AudioOptions()); + &fake_call_, cricket::MediaConfig(), cricket::CN_AUDIO, srtp_required, + webrtc::CryptoOptions(), cricket::AudioOptions()); video_channel_ = channel_manager_->CreateVideoChannel( - &fake_call_, cricket::MediaConfig(), rtp_transport_.get(), - rtc::Thread::Current(), cricket::CN_VIDEO, srtp_required, - webrtc::CryptoOptions(), &ssrc_generator_, cricket::VideoOptions(), + &fake_call_, cricket::MediaConfig(), cricket::CN_VIDEO, srtp_required, + webrtc::CryptoOptions(), cricket::VideoOptions(), video_bitrate_allocator_factory_.get()); + + voice_channel_->SetRtpTransport(rtp_transport_.get()); + video_channel_->SetRtpTransport(rtp_transport_.get()); + voice_channel_->Enable(true); video_channel_->Enable(true); voice_media_channel_ = media_engine_->GetVoiceChannel(0); @@ -169,13 +178,17 @@ class RtpSenderReceiverTest local_stream_ = nullptr; video_track_ = nullptr; audio_track_ = nullptr; - worker_thread_->Invoke(RTC_FROM_HERE, - [&]() { channel_manager_.reset(); }); + + voice_channel_->SetRtpTransport(nullptr); + video_channel_->SetRtpTransport(nullptr); + + channel_manager_->DestroyChannel(voice_channel_); + channel_manager_->DestroyChannel(video_channel_); } std::unique_ptr CreateDtlsSrtpTransport() { auto dtls_srtp_transport = std::make_unique( - /*rtcp_mux_required=*/true); + /*rtcp_mux_required=*/true, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport_.get(), /*rtcp_dtls_transport=*/nullptr); return dtls_srtp_transport; @@ -341,7 +354,7 @@ class RtpSenderReceiverTest void DestroyAudioRtpReceiver() { if (!audio_rtp_receiver_) return; - audio_rtp_receiver_->Stop(); + audio_rtp_receiver_->SetMediaChannel(nullptr); audio_rtp_receiver_ = nullptr; VerifyVoiceChannelNoOutput(); } @@ -350,6 +363,7 @@ class RtpSenderReceiverTest if (!video_rtp_receiver_) return; video_rtp_receiver_->Stop(); + video_rtp_receiver_->SetMediaChannel(nullptr); video_rtp_receiver_ = nullptr; VerifyVideoChannelNoOutput(); } @@ -531,7 +545,7 @@ class RtpSenderReceiverTest rtc::scoped_refptr video_track_; rtc::scoped_refptr audio_track_; bool audio_sender_destroyed_signal_fired_ = false; - rtc::UniqueRandomIdGenerator ssrc_generator_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; // Test that `voice_channel_` is updated when an audio track is associated @@ -577,7 +591,7 @@ TEST_F(RtpSenderReceiverTest, LocalAudioSourceOptionsApplied) { cricket::AudioOptions options; options.echo_cancellation = true; auto source = LocalAudioSource::Create(&options); - CreateAudioRtpSender(source.get()); + CreateAudioRtpSender(source); EXPECT_EQ(true, voice_media_channel_->options().echo_cancellation); @@ -1635,7 +1649,7 @@ TEST_F(RtpSenderReceiverTest, AudioReceiverCannotSetFrameDecryptorAfterStop) { rtc::scoped_refptr fake_frame_decryptor( new FakeFrameDecryptor()); EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); - audio_rtp_receiver_->Stop(); + audio_rtp_receiver_->SetMediaChannel(nullptr); audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyAudioRtpReceiver(); @@ -1682,7 +1696,7 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCannotSetFrameDecryptorAfterStop) { rtc::scoped_refptr fake_frame_decryptor( new FakeFrameDecryptor()); EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); - video_rtp_receiver_->Stop(); + video_rtp_receiver_->SetMediaChannel(nullptr); video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyVideoRtpReceiver(); diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index a78b9d6be6..5e3a084d2d 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -10,6 +10,7 @@ #include "pc/rtp_transceiver.h" +#include #include #include #include @@ -24,6 +25,7 @@ #include "pc/rtp_media_utils.h" #include "pc/session_description.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread.h" @@ -154,16 +156,21 @@ RtpTransceiver::~RtpTransceiver() { RTC_DCHECK_RUN_ON(thread_); StopInternal(); } + + RTC_CHECK(!channel_) << "Missing call to SetChannel(nullptr)?"; } -void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) { +void RtpTransceiver::SetChannel( + cricket::ChannelInterface* channel, + std::function transport_lookup) { RTC_DCHECK_RUN_ON(thread_); // Cannot set a non-null channel on a stopped transceiver. - if (stopped_ && channel) { + if ((stopped_ && channel) || channel == channel_) { return; } RTC_DCHECK(channel || channel_); + RTC_DCHECK(!channel || transport_lookup) << "lookup function not supplied"; RTC_LOG_THREAD_BLOCK_COUNT(); @@ -177,6 +184,8 @@ void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) { signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); } + cricket::ChannelInterface* channel_to_delete = nullptr; + // An alternative to this, could be to require SetChannel to be called // on the network thread. The channel object operates for the most part // on the network thread, as part of its initialization being on the network @@ -189,11 +198,14 @@ void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) { channel_manager_->network_thread()->Invoke(RTC_FROM_HERE, [&]() { if (channel_) { channel_->SetFirstPacketReceivedCallback(nullptr); + channel_->SetRtpTransport(nullptr); + channel_to_delete = channel_; } channel_ = channel; if (channel_) { + channel_->SetRtpTransport(transport_lookup(channel_->mid())); channel_->SetFirstPacketReceivedCallback( [thread = thread_, flag = signaling_thread_safety_, this]() mutable { thread->PostTask(ToQueuedTask( @@ -202,20 +214,34 @@ void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) { } }); - for (const auto& sender : senders_) { - sender->internal()->SetMediaChannel(channel_ ? channel_->media_channel() - : nullptr); - } - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); - for (const auto& receiver : receivers_) { - if (!channel_) { - receiver->internal()->Stop(); - } else { - receiver->internal()->SetMediaChannel(channel_->media_channel()); - } + if (!channel_) { + for (const auto& receiver : receivers_) + receiver->internal()->SetSourceEnded(); + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); // There should not be an invoke. } + + if (channel_to_delete || !senders_.empty() || !receivers_.empty()) { + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + auto* media_channel = channel_ ? channel_->media_channel() : nullptr; + for (const auto& sender : senders_) { + sender->internal()->SetMediaChannel(media_channel); + } + + for (const auto& receiver : receivers_) { + receiver->internal()->SetMediaChannel(media_channel); + } + + // Destroy the channel, if we had one, now _after_ updating the receivers + // who might have had references to the previous channel. + if (channel_to_delete) { + channel_manager_->DestroyChannel(channel_to_delete); + } + }); + } + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); } void RtpTransceiver::AddSender( @@ -256,6 +282,7 @@ void RtpTransceiver::AddReceiver( } bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { + RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!unified_plan_); if (receiver) { RTC_DCHECK_EQ(media_type(), receiver->media_type()); @@ -264,8 +291,13 @@ bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { if (it == receivers_.end()) { return false; } - // `Stop()` will clear the internally cached pointer to the media channel. + (*it)->internal()->Stop(); + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + // `Stop()` will clear the receiver's pointer to the media channel. + (*it)->internal()->SetMediaChannel(nullptr); + }); + receivers_.erase(it); return true; } @@ -273,14 +305,14 @@ bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { rtc::scoped_refptr RtpTransceiver::sender_internal() const { RTC_DCHECK(unified_plan_); RTC_CHECK_EQ(1u, senders_.size()); - return senders_[0]->internal(); + return rtc::scoped_refptr(senders_[0]->internal()); } rtc::scoped_refptr RtpTransceiver::receiver_internal() const { RTC_DCHECK(unified_plan_); RTC_CHECK_EQ(1u, receivers_.size()); - return receivers_[0]->internal(); + return rtc::scoped_refptr(receivers_[0]->internal()); } cricket::MediaType RtpTransceiver::media_type() const { @@ -383,15 +415,22 @@ void RtpTransceiver::StopSendingAndReceiving() { // // 3. Stop sending media with sender. // + RTC_DCHECK_RUN_ON(thread_); + // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as // specified in [RFC3550]. - RTC_DCHECK_RUN_ON(thread_); for (const auto& sender : senders_) sender->internal()->Stop(); - // 5. Stop receiving media with receiver. + // Signal to receiver sources that we're stopping. for (const auto& receiver : receivers_) - receiver->internal()->StopAndEndTrack(); + receiver->internal()->Stop(); + + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + // 5 Stop receiving media with receiver. + for (const auto& receiver : receivers_) + receiver->internal()->SetMediaChannel(nullptr); + }); stopping_ = true; direction_ = webrtc::RtpTransceiverDirection::kInactive; diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h index c995329273..e71fdc1695 100644 --- a/pc/rtp_transceiver.h +++ b/pc/rtp_transceiver.h @@ -20,9 +20,12 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/jsep.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" #include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" @@ -34,7 +37,8 @@ #include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" #include "pc/rtp_sender_proxy.h" -#include "rtc_base/ref_counted_object.h" +#include "pc/rtp_transport_internal.h" +#include "pc/session_description.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread_annotations.h" @@ -71,9 +75,8 @@ namespace webrtc { // MediaType specified in the constructor. Audio RtpTransceivers will have // AudioRtpSenders, AudioRtpReceivers, and a VoiceChannel. Video RtpTransceivers // will have VideoRtpSenders, VideoRtpReceivers, and a VideoChannel. -class RtpTransceiver final - : public rtc::RefCountedObject, - public sigslot::has_slots<> { +class RtpTransceiver : public RtpTransceiverInterface, + public sigslot::has_slots<> { public: // Construct a Plan B-style RtpTransceiver with no senders, receivers, or // channel set. @@ -100,8 +103,36 @@ class RtpTransceiver final cricket::ChannelInterface* channel() const { return channel_; } // Sets the Voice/VideoChannel. The caller must pass in the correct channel - // implementation based on the type of the transceiver. - void SetChannel(cricket::ChannelInterface* channel); + // implementation based on the type of the transceiver. The call must + // furthermore be made on the signaling thread. + // + // `channel`: The channel instance to be associated with the transceiver. + // When a valid pointer is passed for `channel`, the state of the object + // is expected to be newly constructed and not initalized for network + // activity (see next parameter for more). + // + // NOTE: For all practical purposes, the ownership of the channel + // object should be considered to lie with the transceiver until + // `SetChannel()` is called again with nullptr set as the new channel. + // Moving forward, this parameter will change to either be a + // std::unique_ptr<> or the full construction of the channel object will + // be moved to happen within the context of the transceiver class. + // + // `transport_lookup`: When `channel` points to a valid channel object, this + // callback function will be used to look up the `RtpTransport` object + // to associate with the channel via `BaseChannel::SetRtpTransport`. + // The lookup function will be called on the network thread, synchronously + // during the call to `SetChannel`. This means that the caller of + // `SetChannel()` may provide a callback function that references state + // that exists within the calling scope of SetChannel (e.g. a variable + // on the stack). + // The reason for this design is to limit the number of times we jump + // synchronously to the network thread from the signaling thread. + // The callback allows us to combine the transport lookup with network + // state initialization of the channel object. + void SetChannel(cricket::ChannelInterface* channel, + std::function + transport_lookup); // Adds an RtpSender of the appropriate type to be owned by this transceiver. // Must not be null. diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc index 35d9265e03..9c2a0461b3 100644 --- a/pc/rtp_transceiver_unittest.cc +++ b/pc/rtp_transceiver_unittest.cc @@ -14,12 +14,15 @@ #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtp_parameters.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" #include "pc/test/mock_channel_interface.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" +#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" @@ -32,73 +35,101 @@ using ::testing::ReturnRef; namespace webrtc { +namespace { +class ChannelManagerForTest : public cricket::ChannelManager { + public: + ChannelManagerForTest() + : cricket::ChannelManager(std::make_unique(), + true, + rtc::Thread::Current(), + rtc::Thread::Current()) {} + + MOCK_METHOD(void, DestroyChannel, (cricket::ChannelInterface*), (override)); +}; +} // namespace + // Checks that a channel cannot be set on a stopped `RtpTransceiver`. TEST(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) { - auto cm = cricket::ChannelManager::Create( - nullptr, true, rtc::Thread::Current(), rtc::Thread::Current()); - RtpTransceiver transceiver(cricket::MediaType::MEDIA_TYPE_AUDIO, cm.get()); + ChannelManagerForTest cm; + const std::string content_name("my_mid"); + auto transceiver = rtc::make_ref_counted( + cricket::MediaType::MEDIA_TYPE_AUDIO, &cm); cricket::MockChannelInterface channel1; EXPECT_CALL(channel1, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); + EXPECT_CALL(channel1, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(channel1, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(channel1, SetRtpTransport(_)).WillRepeatedly(Return(true)); - transceiver.SetChannel(&channel1); - EXPECT_EQ(&channel1, transceiver.channel()); + transceiver->SetChannel(&channel1, [&](const std::string& mid) { + EXPECT_EQ(mid, content_name); + return nullptr; + }); + EXPECT_EQ(&channel1, transceiver->channel()); // Stop the transceiver. - transceiver.StopInternal(); - EXPECT_EQ(&channel1, transceiver.channel()); + transceiver->StopInternal(); + EXPECT_EQ(&channel1, transceiver->channel()); cricket::MockChannelInterface channel2; EXPECT_CALL(channel2, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); // Channel can no longer be set, so this call should be a no-op. - transceiver.SetChannel(&channel2); - EXPECT_EQ(&channel1, transceiver.channel()); + transceiver->SetChannel(&channel2, + [](const std::string&) { return nullptr; }); + EXPECT_EQ(&channel1, transceiver->channel()); + + // Clear the current channel before `transceiver` goes out of scope. + EXPECT_CALL(channel1, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(cm, DestroyChannel(&channel1)).WillRepeatedly(testing::Return()); + transceiver->SetChannel(nullptr, nullptr); } // Checks that a channel can be unset on a stopped `RtpTransceiver` TEST(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) { - auto cm = cricket::ChannelManager::Create( - nullptr, true, rtc::Thread::Current(), rtc::Thread::Current()); - RtpTransceiver transceiver(cricket::MediaType::MEDIA_TYPE_VIDEO, cm.get()); + ChannelManagerForTest cm; + const std::string content_name("my_mid"); + auto transceiver = rtc::make_ref_counted( + cricket::MediaType::MEDIA_TYPE_VIDEO, &cm); cricket::MockChannelInterface channel; EXPECT_CALL(channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_VIDEO)); + EXPECT_CALL(channel, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(channel, SetFirstPacketReceivedCallback(_)) .WillRepeatedly(testing::Return()); + EXPECT_CALL(channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(cm, DestroyChannel(&channel)).WillRepeatedly(testing::Return()); - transceiver.SetChannel(&channel); - EXPECT_EQ(&channel, transceiver.channel()); + transceiver->SetChannel(&channel, [&](const std::string& mid) { + EXPECT_EQ(mid, content_name); + return nullptr; + }); + EXPECT_EQ(&channel, transceiver->channel()); // Stop the transceiver. - transceiver.StopInternal(); - EXPECT_EQ(&channel, transceiver.channel()); + transceiver->StopInternal(); + EXPECT_EQ(&channel, transceiver->channel()); // Set the channel to `nullptr`. - transceiver.SetChannel(nullptr); - EXPECT_EQ(nullptr, transceiver.channel()); + transceiver->SetChannel(nullptr, nullptr); + EXPECT_EQ(nullptr, transceiver->channel()); } class RtpTransceiverUnifiedPlanTest : public ::testing::Test { public: RtpTransceiverUnifiedPlanTest() - : channel_manager_(cricket::ChannelManager::Create( - std::make_unique(), - false, - rtc::Thread::Current(), - rtc::Thread::Current())), - transceiver_(RtpSenderProxyWithInternal::Create( - rtc::Thread::Current(), - sender_), - RtpReceiverProxyWithInternal::Create( - rtc::Thread::Current(), - rtc::Thread::Current(), - receiver_), - channel_manager_.get(), - channel_manager_->GetSupportedAudioRtpHeaderExtensions(), - /* on_negotiation_needed= */ [] {}) {} + : transceiver_(rtc::make_ref_counted( + RtpSenderProxyWithInternal::Create( + rtc::Thread::Current(), + sender_), + RtpReceiverProxyWithInternal::Create( + rtc::Thread::Current(), + rtc::Thread::Current(), + receiver_), + &channel_manager_, + channel_manager_.GetSupportedAudioRtpHeaderExtensions(), + /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); @@ -116,37 +147,33 @@ class RtpTransceiverUnifiedPlanTest : public ::testing::Test { rtc::scoped_refptr receiver_ = MockReceiver(); rtc::scoped_refptr sender_ = MockSender(); - std::unique_ptr channel_manager_; - RtpTransceiver transceiver_; + ChannelManagerForTest channel_manager_; + rtc::scoped_refptr transceiver_; }; // Basic tests for Stop() TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver_.direction()); - EXPECT_FALSE(transceiver_.current_direction()); - transceiver_.StopStandard(); - EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_.direction()); - EXPECT_FALSE(transceiver_.current_direction()); - transceiver_.StopTransceiverProcedure(); - EXPECT_TRUE(transceiver_.current_direction()); - EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_.direction()); + EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver_->direction()); + EXPECT_FALSE(transceiver_->current_direction()); + transceiver_->StopStandard(); + EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); + EXPECT_FALSE(transceiver_->current_direction()); + transceiver_->StopTransceiverProcedure(); + EXPECT_TRUE(transceiver_->current_direction()); + EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, - *transceiver_.current_direction()); + *transceiver_->current_direction()); } class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { public: RtpTransceiverTestForHeaderExtensions() - : channel_manager_(cricket::ChannelManager::Create( - std::make_unique(), - false, - rtc::Thread::Current(), - rtc::Thread::Current())), - extensions_( + : extensions_( {RtpHeaderExtensionCapability("uri1", 1, RtpTransceiverDirection::kSendOnly), @@ -159,16 +186,17 @@ class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { RtpHeaderExtensionCapability(RtpExtension::kVideoRotationUri, 4, RtpTransceiverDirection::kSendRecv)}), - transceiver_(RtpSenderProxyWithInternal::Create( - rtc::Thread::Current(), - sender_), - RtpReceiverProxyWithInternal::Create( - rtc::Thread::Current(), - rtc::Thread::Current(), - receiver_), - channel_manager_.get(), - extensions_, - /* on_negotiation_needed= */ [] {}) {} + transceiver_(rtc::make_ref_counted( + RtpSenderProxyWithInternal::Create( + rtc::Thread::Current(), + sender_), + RtpReceiverProxyWithInternal::Create( + rtc::Thread::Current(), + rtc::Thread::Current(), + receiver_), + &channel_manager_, + extensions_, + /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); @@ -184,103 +212,118 @@ class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { return sender; } + void ClearChannel(cricket::MockChannelInterface& mock_channel) { + EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); + EXPECT_CALL(mock_channel, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(channel_manager_, DestroyChannel(&mock_channel)) + .WillRepeatedly(testing::Return()); + transceiver_->SetChannel(nullptr, nullptr); + } + rtc::scoped_refptr receiver_ = MockReceiver(); rtc::scoped_refptr sender_ = MockSender(); - std::unique_ptr channel_manager_; + ChannelManagerForTest channel_manager_; std::vector extensions_; - RtpTransceiver transceiver_; + rtc::scoped_refptr transceiver_; }; TEST_F(RtpTransceiverTestForHeaderExtensions, OffersChannelManagerList) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, ModifiesDirection) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kSendOnly; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kRecvOnly; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kInactive; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, AcceptsStoppedExtension) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kStopped; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsUnsupportedExtension) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); std::vector modified_extensions( {RtpHeaderExtensionCapability("uri3", 1, RtpTransceiverDirection::kSendRecv)}); - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::UNSUPPORTED_PARAMETER)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsStoppedMandatoryExtensions) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); std::vector modified_extensions = extensions_; // Attempting to stop the mandatory MID extension. modified_extensions[2].direction = RtpTransceiverDirection::kStopped; - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); modified_extensions = extensions_; // Attempting to stop the mandatory video orientation extension. modified_extensions[3].direction = RtpTransceiverDirection::kStopped; - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithoutChannel) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre()); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithChannelWithoutNegotiation) { - EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + const std::string content_name("my_mid"); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); @@ -289,13 +332,19 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, EXPECT_CALL(mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); EXPECT_CALL(mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); - transceiver_.SetChannel(&mock_channel); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); + EXPECT_CALL(mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); + transceiver_->SetChannel(&mock_channel, + [](const std::string&) { return nullptr; }); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre()); + + ClearChannel(mock_channel); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) { - EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + const std::string content_name("my_mid"); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); @@ -305,24 +354,30 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) { EXPECT_CALL(mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); EXPECT_CALL(mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1), webrtc::RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - transceiver_.SetChannel(&mock_channel); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + transceiver_->SetChannel(&mock_channel, + [](const std::string&) { return nullptr; }); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri1", 1, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( "uri2", 2, RtpTransceiverDirection::kSendRecv))); + + ClearChannel(mock_channel); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExtsSecondTime) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); @@ -330,9 +385,9 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, webrtc::RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri1", 1, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( @@ -341,9 +396,9 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, extensions = {webrtc::RtpExtension("uri3", 4), webrtc::RtpExtension("uri5", 6)}; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri3", 4, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( diff --git a/pc/rtp_transmission_manager.cc b/pc/rtp_transmission_manager.cc index 9040a69699..538fa62fe7 100644 --- a/pc/rtp_transmission_manager.cc +++ b/pc/rtp_transmission_manager.cc @@ -10,7 +10,7 @@ #include "pc/rtp_transmission_manager.h" -#include +#include #include #include "absl/types/optional.h" @@ -23,6 +23,7 @@ #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -270,7 +271,7 @@ RtpTransmissionManager::CreateAndAddTransceiver( RTC_DCHECK(!FindSenderById(sender->id())); auto transceiver = RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver( + rtc::make_ref_counted( sender, receiver, channel_manager(), sender->media_type() == cricket::MEDIA_TYPE_AUDIO ? channel_manager()->GetSupportedAudioRtpHeaderExtensions() @@ -345,7 +346,7 @@ RtpTransmissionManager::GetAudioTransceiver() const { return transceiver; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -360,7 +361,7 @@ RtpTransmissionManager::GetVideoTransceiver() const { return transceiver; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } @@ -378,7 +379,8 @@ void RtpTransmissionManager::AddAudioTrack(AudioTrackInterface* track, } // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track, + auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), + rtc::scoped_refptr(track), {stream->id()}, {}); new_sender->internal()->SetMediaChannel(voice_media_channel()); GetAudioTransceiver()->internal()->AddSender(new_sender); @@ -424,7 +426,8 @@ void RtpTransmissionManager::AddVideoTrack(VideoTrackInterface* track, } // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track, + auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), + rtc::scoped_refptr(track), {stream->id()}, {}); new_sender->internal()->SetMediaChannel(video_media_channel()); GetVideoTransceiver()->internal()->AddSender(new_sender); @@ -457,13 +460,14 @@ void RtpTransmissionManager::CreateAudioReceiver( // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use // the constructor taking stream IDs instead. auto audio_receiver = rtc::make_ref_counted( - worker_thread(), remote_sender_info.sender_id, streams, IsUnifiedPlan()); - audio_receiver->SetMediaChannel(voice_media_channel()); + worker_thread(), remote_sender_info.sender_id, streams, IsUnifiedPlan(), + voice_media_channel()); if (remote_sender_info.sender_id == kDefaultAudioSenderId) { audio_receiver->SetupUnsignaledMediaChannel(); } else { audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); } + auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(audio_receiver)); GetAudioTransceiver()->internal()->AddReceiver(receiver); @@ -481,12 +485,13 @@ void RtpTransmissionManager::CreateVideoReceiver( // the constructor taking stream IDs instead. auto video_receiver = rtc::make_ref_counted( worker_thread(), remote_sender_info.sender_id, streams); - video_receiver->SetMediaChannel(video_media_channel()); - if (remote_sender_info.sender_id == kDefaultVideoSenderId) { - video_receiver->SetupUnsignaledMediaChannel(); - } else { - video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } + + video_receiver->SetupMediaChannel( + remote_sender_info.sender_id == kDefaultVideoSenderId + ? absl::nullopt + : absl::optional(remote_sender_info.first_ssrc), + video_media_channel()); + auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(video_receiver)); GetVideoTransceiver()->internal()->AddReceiver(receiver); @@ -527,7 +532,7 @@ void RtpTransmissionManager::OnRemoteSenderAdded( } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { CreateVideoReceiver(stream, sender_info); } else { - RTC_NOTREACHED() << "Invalid media type"; + RTC_DCHECK_NOTREACHED() << "Invalid media type"; } } @@ -562,7 +567,7 @@ void RtpTransmissionManager::OnRemoteSenderRemoved( stream->RemoveTrack(video_track); } } else { - RTC_NOTREACHED() << "Invalid media type"; + RTC_DCHECK_NOTREACHED() << "Invalid media type"; } if (receiver) { RTC_DCHECK(!closed_); @@ -636,7 +641,7 @@ std::vector* RtpTransmissionManager::GetLocalSenderInfos( const RtpSenderInfo* RtpTransmissionManager::FindSenderInfo( const std::vector& infos, const std::string& stream_id, - const std::string sender_id) const { + const std::string& sender_id) const { for (const RtpSenderInfo& sender_info : infos) { if (sender_info.stream_id == stream_id && sender_info.sender_id == sender_id) { diff --git a/pc/rtp_transmission_manager.h b/pc/rtp_transmission_manager.h index f616d9d0f6..3496bfa54d 100644 --- a/pc/rtp_transmission_manager.h +++ b/pc/rtp_transmission_manager.h @@ -29,7 +29,9 @@ #include "media/base/media_channel.h" #include "pc/channel_manager.h" #include "pc/rtp_receiver.h" +#include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" +#include "pc/rtp_sender_proxy.h" #include "pc/rtp_transceiver.h" #include "pc/stats_collector_interface.h" #include "pc/transceiver_list.h" @@ -50,7 +52,7 @@ namespace webrtc { struct RtpSenderInfo { RtpSenderInfo() : first_ssrc(0) {} RtpSenderInfo(const std::string& stream_id, - const std::string sender_id, + const std::string& sender_id, uint32_t ssrc) : stream_id(stream_id), sender_id(sender_id), first_ssrc(ssrc) {} bool operator==(const RtpSenderInfo& other) { @@ -184,7 +186,7 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { cricket::MediaType media_type); const RtpSenderInfo* FindSenderInfo(const std::vector& infos, const std::string& stream_id, - const std::string sender_id) const; + const std::string& sender_id) const; // Return the RtpSender with the given track attached. rtc::scoped_refptr> diff --git a/pc/rtp_transport.cc b/pc/rtp_transport.cc index 4b87aae76b..1f37aecd84 100644 --- a/pc/rtp_transport.cc +++ b/pc/rtp_transport.cc @@ -11,11 +11,13 @@ #include "pc/rtp_transport.h" #include -#include + +#include #include #include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/units/timestamp.h" #include "media/base/rtp_utils.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" diff --git a/pc/rtp_transport.h b/pc/rtp_transport.h index bd2ec88beb..77826810b1 100644 --- a/pc/rtp_transport.h +++ b/pc/rtp_transport.h @@ -18,6 +18,7 @@ #include "absl/types/optional.h" #include "call/rtp_demuxer.h" +#include "call/video_receive_stream.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "p2p/base/packet_transport_internal.h" #include "pc/rtp_transport_internal.h" diff --git a/pc/rtp_transport_unittest.cc b/pc/rtp_transport_unittest.cc index b3bd1db2e5..0e6af734f3 100644 --- a/pc/rtp_transport_unittest.cc +++ b/pc/rtp_transport_unittest.cc @@ -10,16 +10,10 @@ #include "pc/rtp_transport.h" -#include -#include -#include -#include - -#include "api/rtp_headers.h" -#include "api/rtp_parameters.h" #include "p2p/base/fake_packet_transport.h" #include "pc/test/rtp_transport_test_util.h" #include "rtc_base/buffer.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" @@ -285,7 +279,7 @@ TEST(RtpTransportTest, SignalHandledRtpPayloadType) { TransportObserver observer(&transport); RtpDemuxerCriteria demuxer_criteria; // Add a handled payload type. - demuxer_criteria.payload_types = {0x11}; + demuxer_criteria.payload_types().insert(0x11); transport.RegisterRtpDemuxerSink(demuxer_criteria, &observer); // An rtp packet. @@ -309,7 +303,7 @@ TEST(RtpTransportTest, DontSignalUnhandledRtpPayloadType) { TransportObserver observer(&transport); RtpDemuxerCriteria demuxer_criteria; // Add an unhandled payload type. - demuxer_criteria.payload_types = {0x12}; + demuxer_criteria.payload_types().insert(0x12); transport.RegisterRtpDemuxerSink(demuxer_criteria, &observer); const rtc::PacketOptions options; diff --git a/pc/scenario_tests/goog_cc_test.cc b/pc/scenario_tests/goog_cc_test.cc index d9e27e2edf..f0a30dfd86 100644 --- a/pc/scenario_tests/goog_cc_test.cc +++ b/pc/scenario_tests/goog_cc_test.cc @@ -93,13 +93,16 @@ TEST(GoogCcPeerScenarioTest, MAYBE_NoBweChangeFromVideoUnmute) { // Resume video but stop audio. Bandwidth should not drop. video.capturer->Start(); - RTCError status = caller->pc()->RemoveTrackNew(audio.sender); + RTCError status = caller->pc()->RemoveTrackOrError(audio.sender); ASSERT_TRUE(status.ok()); audio.track->set_enabled(false); for (int i = 0; i < 10; i++) { s.ProcessMessages(TimeDelta::Seconds(1)); EXPECT_GE(get_bwe(), initial_bwe); } + + caller->pc()->Close(); + callee->pc()->Close(); } } // namespace test diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc index 0e4ef7de88..356493658a 100644 --- a/pc/sctp_data_channel.cc +++ b/pc/sctp_data_channel.cc @@ -31,7 +31,6 @@ namespace webrtc { namespace { static size_t kMaxQueuedReceivedDataBytes = 16 * 1024 * 1024; -static size_t kMaxQueuedSendDataBytes = 16 * 1024 * 1024; static std::atomic g_unique_id{0}; @@ -159,11 +158,8 @@ rtc::scoped_refptr SctpDataChannel::Create( rtc::scoped_refptr SctpDataChannel::CreateProxy( rtc::scoped_refptr channel) { // TODO(bugs.webrtc.org/11547): incorporate the network thread in the proxy. - // Also, consider allowing the proxy object to own the reference (std::move). - // As is, the proxy has a raw pointer and no reference to the channel object - // and trusting that the lifetime management aligns with the - // sctp_data_channels_ array in SctpDataChannelController. - return DataChannelProxy::Create(channel->signaling_thread_, channel.get()); + auto* signaling_thread = channel->signaling_thread_; + return DataChannelProxy::Create(signaling_thread, std::move(channel)); } SctpDataChannel::SctpDataChannel(const InternalDataChannelInit& config, @@ -254,7 +250,7 @@ bool SctpDataChannel::reliable() const { uint64_t SctpDataChannel::buffered_amount() const { RTC_DCHECK_RUN_ON(signaling_thread_); - return buffered_amount_; + return queued_send_data_.byte_count(); } void SctpDataChannel::Close() { @@ -306,18 +302,12 @@ bool SctpDataChannel::Send(const DataBuffer& buffer) { return false; } - buffered_amount_ += buffer.size(); - // If the queue is non-empty, we're waiting for SignalReadyToSend, // so just add to the end of the queue and keep waiting. if (!queued_send_data_.Empty()) { if (!QueueSendDataMessage(buffer)) { - RTC_LOG(LS_ERROR) << "Closing the DataChannel due to a failure to queue " - "additional data."; - // https://w3c.github.io/webrtc-pc/#dom-rtcdatachannel-send step 5 - // Note that the spec doesn't explicitly say to close in this situation. - CloseAbruptlyWithError(RTCError(RTCErrorType::RESOURCE_EXHAUSTED, - "Unable to queue data for sending")); + // Queue is full + return false; } return true; } @@ -487,8 +477,6 @@ void SctpDataChannel::CloseAbruptlyWithError(RTCError error) { } // Closing abruptly means any queued data gets thrown away. - buffered_amount_ = 0; - queued_send_data_.Clear(); queued_control_data_.Clear(); @@ -644,8 +632,6 @@ bool SctpDataChannel::SendDataMessage(const DataBuffer& buffer, ++messages_sent_; bytes_sent_ += buffer.size(); - RTC_DCHECK(buffered_amount_ >= buffer.size()); - buffered_amount_ -= buffer.size(); if (observer_ && buffer.size() > 0) { observer_->OnBufferedAmountChange(buffer.size()); } @@ -671,7 +657,8 @@ bool SctpDataChannel::SendDataMessage(const DataBuffer& buffer, bool SctpDataChannel::QueueSendDataMessage(const DataBuffer& buffer) { RTC_DCHECK_RUN_ON(signaling_thread_); size_t start_buffered_amount = queued_send_data_.byte_count(); - if (start_buffered_amount + buffer.size() > kMaxQueuedSendDataBytes) { + if (start_buffered_amount + buffer.size() > + DataChannelInterface::MaxSendQueueSize()) { RTC_LOG(LS_ERROR) << "Can't buffer any more data for the data channel."; return false; } diff --git a/pc/sctp_data_channel.h b/pc/sctp_data_channel.h index 0c3b95a225..56f99df3e5 100644 --- a/pc/sctp_data_channel.h +++ b/pc/sctp_data_channel.h @@ -266,9 +266,6 @@ class SctpDataChannel : public DataChannelInterface, uint64_t bytes_sent_ RTC_GUARDED_BY(signaling_thread_) = 0; uint32_t messages_received_ RTC_GUARDED_BY(signaling_thread_) = 0; uint64_t bytes_received_ RTC_GUARDED_BY(signaling_thread_) = 0; - // Number of bytes of data that have been queued using Send(). Increased - // before each transport send and decreased after each successful send. - uint64_t buffered_amount_ RTC_GUARDED_BY(signaling_thread_) = 0; SctpDataChannelProviderInterface* const provider_ RTC_GUARDED_BY(signaling_thread_); HandshakeState handshake_state_ RTC_GUARDED_BY(signaling_thread_) = diff --git a/pc/sctp_data_channel_transport.cc b/pc/sctp_data_channel_transport.cc index f01f86ebd8..626d1757b7 100644 --- a/pc/sctp_data_channel_transport.cc +++ b/pc/sctp_data_channel_transport.cc @@ -10,9 +10,6 @@ #include "pc/sctp_data_channel_transport.h" -#include "absl/types/optional.h" -#include "pc/sctp_utils.h" - namespace webrtc { SctpDataChannelTransport::SctpDataChannelTransport( diff --git a/pc/sctp_transport.h b/pc/sctp_transport.h index 16b98407b6..4981db4ede 100644 --- a/pc/sctp_transport.h +++ b/pc/sctp_transport.h @@ -16,9 +16,11 @@ #include "api/dtls_transport_interface.h" #include "api/scoped_refptr.h" #include "api/sctp_transport_interface.h" +#include "api/sequence_checker.h" #include "media/sctp/sctp_transport_internal.h" #include "p2p/base/dtls_transport_internal.h" #include "pc/dtls_transport.h" +#include "rtc_base/checks.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" diff --git a/pc/sctp_transport_unittest.cc b/pc/sctp_transport_unittest.cc index 679b481f4c..e3168d8b99 100644 --- a/pc/sctp_transport_unittest.cc +++ b/pc/sctp_transport_unittest.cc @@ -14,10 +14,17 @@ #include #include "absl/memory/memory.h" +#include "absl/types/optional.h" #include "api/dtls_transport_interface.h" +#include "api/transport/data_channel_transport_interface.h" +#include "media/base/media_channel.h" #include "p2p/base/fake_dtls_transport.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" #include "pc/dtls_transport.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/pc/sctp_utils.cc b/pc/sctp_utils.cc index f7458405ea..c60e339b08 100644 --- a/pc/sctp_utils.cc +++ b/pc/sctp_utils.cc @@ -11,12 +11,12 @@ #include "pc/sctp_utils.h" #include -#include + +#include #include "absl/types/optional.h" #include "api/priority.h" #include "rtc_base/byte_buffer.h" -#include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/logging.h" diff --git a/pc/sctp_utils_unittest.cc b/pc/sctp_utils_unittest.cc index af14fe4f6b..146886b8cb 100644 --- a/pc/sctp_utils_unittest.cc +++ b/pc/sctp_utils_unittest.cc @@ -12,6 +12,8 @@ #include +#include "absl/types/optional.h" +#include "api/priority.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/copy_on_write_buffer.h" #include "test/gtest.h" diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc index 52655d1d0d..013e9d2416 100644 --- a/pc/sdp_offer_answer.cc +++ b/pc/sdp_offer_answer.cc @@ -11,15 +11,18 @@ #include "pc/sdp_offer_answer.h" #include +#include #include #include #include #include +#include #include #include #include "absl/algorithm/container.h" #include "absl/memory/memory.h" +#include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/crypto/crypto_options.h" @@ -29,7 +32,6 @@ #include "api/rtp_sender_interface.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "media/base/codec.h" -#include "media/base/media_engine.h" #include "media/base/rid_description.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" @@ -38,15 +40,16 @@ #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" #include "p2p/base/transport_info.h" -#include "pc/data_channel_utils.h" +#include "pc/channel_interface.h" #include "pc/dtls_transport.h" #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" -#include "pc/peer_connection.h" +#include "pc/peer_connection_internal.h" #include "pc/peer_connection_message_handler.h" #include "pc/rtp_media_utils.h" +#include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" -#include "pc/rtp_transport_internal.h" +#include "pc/rtp_sender_proxy.h" #include "pc/simulcast_description.h" #include "pc/stats_collector.h" #include "pc/usage_pattern.h" @@ -59,9 +62,7 @@ #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" -#include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" using cricket::ContentInfo; @@ -89,9 +90,6 @@ namespace { typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions; -constexpr const char* kAlwaysAllowPayloadTypeDemuxingFieldTrialName = - "WebRTC-AlwaysAllowPayloadTypeDemuxing"; - // Error messages const char kInvalidSdp[] = "Invalid session description."; const char kInvalidCandidates[] = "Description contains invalid candidates."; @@ -123,6 +121,10 @@ const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled"; // The length of RTCP CNAMEs. static const int kRtcpCnameLength = 16; +// The maximum length of the MID attribute. +// TODO(bugs.webrtc.org/12517) - reduce to 16 again. +static constexpr size_t kMidMaxSize = 32; + const char kDefaultStreamId[] = "default"; // NOTE: Duplicated in peer_connection.cc: static const char kDefaultAudioSenderId[] = "defaulta0"; @@ -133,40 +135,9 @@ void NoteAddIceCandidateResult(int result) { kAddIceCandidateMax); } -void NoteKeyProtocolAndMedia(KeyExchangeProtocolType protocol_type, - cricket::MediaType media_type) { - // Array of structs needed to map {KeyExchangeProtocolType, - // cricket::MediaType} to KeyExchangeProtocolMedia without using std::map in - // order to avoid -Wglobal-constructors and -Wexit-time-destructors. - static constexpr struct { - KeyExchangeProtocolType protocol_type; - cricket::MediaType media_type; - KeyExchangeProtocolMedia protocol_media; - } kEnumCounterKeyProtocolMediaMap[] = { - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeDtlsAudio}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeDtlsVideo}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeDtlsData}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeSdesAudio}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeSdesVideo}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeSdesData}, - }; - +void NoteKeyProtocol(KeyExchangeProtocolType protocol_type) { RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type, kEnumCounterKeyProtocolMax); - - for (const auto& i : kEnumCounterKeyProtocolMediaMap) { - if (i.protocol_type == protocol_type && i.media_type == media_type) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocolByMedia", - i.protocol_media, - kEnumCounterKeyProtocolMediaTypeMax); - } - } } std::map GetBundleGroupsByMid( @@ -221,7 +192,9 @@ std::string GetSetDescriptionErrorMessage(cricket::ContentSource source, const RTCError& error) { rtc::StringBuilder oss; oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote") - << " " << SdpTypeToString(type) << " sdp: " << error.message(); + << " " << SdpTypeToString(type) << " sdp: "; + RTC_DCHECK(!absl::StartsWith(error.message(), oss.str())) << error.message(); + oss << error.message(); return oss.Release(); } @@ -361,9 +334,8 @@ RTCError VerifyCrypto(const SessionDescription* desc, continue; } // Note what media is used with each crypto protocol, for all sections. - NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls - : webrtc::kEnumCounterKeyProtocolSdes, - content_info.media_description()->type()); + NoteKeyProtocol(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls + : webrtc::kEnumCounterKeyProtocolSdes); const std::string& mid = content_info.name; auto it = bundle_groups_by_mid.find(mid); const cricket::ContentGroup* bundle = @@ -443,16 +415,25 @@ bool VerifyIceUfragPwdPresent( RTCError ValidateMids(const cricket::SessionDescription& description) { std::set mids; + size_t max_length = 0; for (const cricket::ContentInfo& content : description.contents()) { if (content.name.empty()) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "A media section is missing a MID attribute."); } + max_length = std::max(max_length, content.name.size()); + if (content.name.size() > kMidMaxSize) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "The MID attribute exceeds the maximum supported " + "length of 32 characters."); + } if (!mids.insert(content.name).second) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Duplicate a=mid value '" + content.name + "'."); } } + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.Mid.Size", max_length, 0, + 31, 32); return RTCError::OK(); } @@ -468,28 +449,6 @@ bool ValidateOfferAnswerOptions( IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); } -// Map internal signaling state name to spec name: -// https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum -std::string GetSignalingStateString( - PeerConnectionInterface::SignalingState state) { - switch (state) { - case PeerConnectionInterface::kStable: - return "stable"; - case PeerConnectionInterface::kHaveLocalOffer: - return "have-local-offer"; - case PeerConnectionInterface::kHaveLocalPrAnswer: - return "have-local-pranswer"; - case PeerConnectionInterface::kHaveRemoteOffer: - return "have-remote-offer"; - case PeerConnectionInterface::kHaveRemotePrAnswer: - return "have-remote-pranswer"; - case PeerConnectionInterface::kClosed: - return "closed"; - } - RTC_NOTREACHED(); - return ""; -} - // This method will extract any send encodings that were sent by the remote // connection. This is currently only relevant for Simulcast scenario (where // the number of layers may be communicated by the server). @@ -592,7 +551,7 @@ absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) { case cricket::MEDIA_TYPE_UNSUPPORTED: return "not supported"; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } @@ -709,7 +668,7 @@ std::string GenerateRtcpCname() { std::string cname; if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return cname; } @@ -756,6 +715,250 @@ bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info, } // namespace +// This class stores state related to a SetRemoteDescription operation, captures +// and reports potential errors that migth occur and makes sure to notify the +// observer of the operation and the operations chain of completion. +class SdpOfferAnswerHandler::RemoteDescriptionOperation { + public: + RemoteDescriptionOperation( + SdpOfferAnswerHandler* handler, + std::unique_ptr desc, + rtc::scoped_refptr observer, + std::function operations_chain_callback) + : handler_(handler), + desc_(std::move(desc)), + observer_(std::move(observer)), + operations_chain_callback_(std::move(operations_chain_callback)), + unified_plan_(handler_->IsUnifiedPlan()) { + if (!desc_) { + type_ = static_cast(-1); + InvalidParam("SessionDescription is NULL."); + } else { + type_ = desc_->GetType(); + } + } + + ~RemoteDescriptionOperation() { + RTC_DCHECK_RUN_ON(handler_->signaling_thread()); + SignalCompletion(); + operations_chain_callback_(); + } + + bool ok() const { return error_.ok(); } + + // Notifies the observer that the operation is complete and releases the + // reference to the observer. + void SignalCompletion() { + if (!observer_) + return; + + if (!error_.ok() && type_ != static_cast(-1)) { + std::string error_message = + GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type_, error_); + RTC_LOG(LS_ERROR) << error_message; + error_.set_message(std::move(error_message)); + } + + observer_->OnSetRemoteDescriptionComplete(error_); + observer_ = nullptr; // Only fire the notification once. + } + + // If a session error has occurred the PeerConnection is in a possibly + // inconsistent state so fail right away. + bool HaveSessionError() { + RTC_DCHECK(ok()); + if (handler_->session_error() != SessionError::kNone) + InternalError(handler_->GetSessionErrorMsg()); + return !ok(); + } + + // Returns true if the operation was a rollback operation. If this function + // returns true, the caller should consider the operation complete. Otherwise + // proceed to the next step. + bool MaybeRollback() { + RTC_DCHECK_RUN_ON(handler_->signaling_thread()); + RTC_DCHECK(ok()); + if (type_ != SdpType::kRollback) { + // Check if we can do an implicit rollback. + if (type_ == SdpType::kOffer && unified_plan_ && + handler_->pc_->configuration()->enable_implicit_rollback && + handler_->signaling_state() == + PeerConnectionInterface::kHaveLocalOffer) { + handler_->Rollback(type_); + } + return false; + } + + if (unified_plan_) { + error_ = handler_->Rollback(type_); + } else if (type_ == SdpType::kRollback) { + Unsupported("Rollback not supported in Plan B"); + } + + return true; + } + + // Report to UMA the format of the received offer or answer. + void ReportOfferAnswerUma() { + RTC_DCHECK(ok()); + if (type_ == SdpType::kOffer || type_ == SdpType::kAnswer) { + handler_->pc_->ReportSdpFormatReceived(*desc_.get()); + handler_->pc_->ReportSdpBundleUsage(*desc_.get()); + } + } + + // Checks if the session description for the operation is valid. If not, the + // function captures error information and returns false. Note that if the + // return value is false, the operation should be considered done. + bool IsDescriptionValid() { + RTC_DCHECK_RUN_ON(handler_->signaling_thread()); + RTC_DCHECK(ok()); + RTC_DCHECK(bundle_groups_by_mid_.empty()) << "Already called?"; + bundle_groups_by_mid_ = GetBundleGroupsByMid(description()); + error_ = handler_->ValidateSessionDescription( + desc_.get(), cricket::CS_REMOTE, bundle_groups_by_mid_); + return ok(); + } + + // Transfers ownership of the session description object over to `handler_`. + bool ReplaceRemoteDescriptionAndCheckEror() { + RTC_DCHECK_RUN_ON(handler_->signaling_thread()); + RTC_DCHECK(ok()); + RTC_DCHECK(desc_); + RTC_DCHECK(!replaced_remote_description_); +#if RTC_DCHECK_IS_ON + const auto* existing_remote_description = handler_->remote_description(); +#endif + + error_ = handler_->ReplaceRemoteDescription(std::move(desc_), type_, + &replaced_remote_description_); + + if (ok()) { +#if RTC_DCHECK_IS_ON + // Sanity check that our `old_remote_description()` method always returns + // the same value as `remote_description()` did before the call to + // ReplaceRemoteDescription. + RTC_DCHECK_EQ(existing_remote_description, old_remote_description()); +#endif + } else { + SetAsSessionError(); + } + + return ok(); + } + + bool UpdateChannels() { + RTC_DCHECK(ok()); + RTC_DCHECK(!desc_) << "ReplaceRemoteDescription hasn't been called"; + + const auto* remote_description = handler_->remote_description(); + + const cricket::SessionDescription* session_desc = + remote_description->description(); + + // Transport and Media channels will be created only when offer is set. + if (unified_plan_) { + error_ = handler_->UpdateTransceiversAndDataChannels( + cricket::CS_REMOTE, *remote_description, + handler_->local_description(), old_remote_description(), + bundle_groups_by_mid_); + } else { + // Media channels will be created only when offer is set. These may use + // new transports just created by PushdownTransportDescription. + if (type_ == SdpType::kOffer) { + // TODO(mallinath) - Handle CreateChannel failure, as new local + // description is applied. Restore back to old description. + error_ = handler_->CreateChannels(*session_desc); + } + // Remove unused channels if MediaContentDescription is rejected. + handler_->RemoveUnusedChannels(session_desc); + } + + return ok(); + } + + bool UpdateSessionState() { + RTC_DCHECK(ok()); + error_ = handler_->UpdateSessionState( + type_, cricket::CS_REMOTE, + handler_->remote_description()->description(), bundle_groups_by_mid_); + if (!ok()) + SetAsSessionError(); + return ok(); + } + + bool UseCandidatesInRemoteDescription() { + RTC_DCHECK(ok()); + if (handler_->local_description() && + !handler_->UseCandidatesInRemoteDescription()) { + InvalidParam(kInvalidCandidates); + } + return ok(); + } + + // Convenience getter for desc_->GetType(). + SdpType type() const { return type_; } + bool unified_plan() const { return unified_plan_; } + cricket::SessionDescription* description() { return desc_->description(); } + + const SessionDescriptionInterface* old_remote_description() const { + RTC_DCHECK(!desc_) << "Called before replacing the remote description"; + if (type_ == SdpType::kAnswer) + return replaced_remote_description_.get(); + return replaced_remote_description_ + ? replaced_remote_description_.get() + : handler_->current_remote_description(); + } + + // Returns a reference to a cached map of bundle groups ordered by mid. + // Note that this will only be valid after a successful call to + // `IsDescriptionValid`. + const std::map& + bundle_groups_by_mid() const { + RTC_DCHECK(ok()); + return bundle_groups_by_mid_; + } + + private: + // Convenience methods for populating the embedded `error_` object. + void Unsupported(std::string message) { + SetError(RTCErrorType::UNSUPPORTED_OPERATION, std::move(message)); + } + + void InvalidParam(std::string message) { + SetError(RTCErrorType::INVALID_PARAMETER, std::move(message)); + } + + void InternalError(std::string message) { + SetError(RTCErrorType::INTERNAL_ERROR, std::move(message)); + } + + void SetError(RTCErrorType type, std::string message) { + RTC_DCHECK(ok()) << "Overwriting an existing error?"; + error_ = RTCError(type, std::move(message)); + } + + // Called when the PeerConnection could be in an inconsistent state and we set + // the session error so that future calls to + // SetLocalDescription/SetRemoteDescription fail. + void SetAsSessionError() { + RTC_DCHECK(!ok()); + handler_->SetSessionError(SessionError::kContent, error_.message()); + } + + SdpOfferAnswerHandler* const handler_; + std::unique_ptr desc_; + // Keeps the replaced session description object alive while the operation + // is taking place since methods that depend on `old_remote_description()` + // for updating the state, need it. + std::unique_ptr replaced_remote_description_; + rtc::scoped_refptr observer_; + std::function operations_chain_callback_; + RTCError error_ = RTCError::OK(); + std::map bundle_groups_by_mid_; + SdpType type_; + const bool unified_plan_; +}; // Used by parameterless SetLocalDescription() to create an offer or answer. // Upon completion of creating the session description, SetLocalDescription() is // invoked with the result. @@ -955,8 +1158,10 @@ class SdpOfferAnswerHandler::LocalIceCredentialsToReplace { std::set> ice_credentials_; }; -SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnection* pc) +SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc, + ConnectionContext* context) : pc_(pc), + context_(context), local_streams_(StreamCollection::Create()), remote_streams_(StreamCollection::Create()), operations_chain_(rtc::OperationsChain::Create()), @@ -975,17 +1180,19 @@ SdpOfferAnswerHandler::~SdpOfferAnswerHandler() {} // Static std::unique_ptr SdpOfferAnswerHandler::Create( - PeerConnection* pc, + PeerConnectionSdpMethods* pc, const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies) { - auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc)); - handler->Initialize(configuration, dependencies); + PeerConnectionDependencies& dependencies, + ConnectionContext* context) { + auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context)); + handler->Initialize(configuration, dependencies, context); return handler; } void SdpOfferAnswerHandler::Initialize( const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies) { + PeerConnectionDependencies& dependencies, + ConnectionContext* context) { RTC_DCHECK_RUN_ON(signaling_thread()); video_options_.screencast_min_bitrate_kbps = configuration.screencast_min_bitrate; @@ -1015,11 +1222,11 @@ void SdpOfferAnswerHandler::Initialize( webrtc_session_desc_factory_ = std::make_unique( - signaling_thread(), channel_manager(), this, pc_->session_id(), - pc_->dtls_enabled(), std::move(dependencies.cert_generator), - certificate, &ssrc_generator_, + context, this, pc_->session_id(), pc_->dtls_enabled(), + std::move(dependencies.cert_generator), certificate, [this](const rtc::scoped_refptr& certificate) { - transport_controller()->SetLocalCertificate(certificate); + RTC_DCHECK_RUN_ON(signaling_thread()); + transport_controller_s()->SetLocalCertificate(certificate); }); if (pc_->options()->disable_encryption) { @@ -1042,7 +1249,7 @@ void SdpOfferAnswerHandler::Initialize( // ================================================================== // Access to pc_ variables cricket::ChannelManager* SdpOfferAnswerHandler::channel_manager() const { - return pc_->channel_manager(); + return context_->channel_manager(); } TransceiverList* SdpOfferAnswerHandler::transceivers() { if (!pc_->rtp_manager()) { @@ -1056,12 +1263,19 @@ const TransceiverList* SdpOfferAnswerHandler::transceivers() const { } return pc_->rtp_manager()->transceivers(); } -JsepTransportController* SdpOfferAnswerHandler::transport_controller() { - return pc_->transport_controller(); +JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() { + return pc_->transport_controller_s(); } -const JsepTransportController* SdpOfferAnswerHandler::transport_controller() +JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() { + return pc_->transport_controller_n(); +} +const JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() const { - return pc_->transport_controller(); + return pc_->transport_controller_s(); +} +const JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() + const { + return pc_->transport_controller_n(); } DataChannelController* SdpOfferAnswerHandler::data_channel_controller() { return pc_->data_channel_controller(); @@ -1102,7 +1316,11 @@ void SdpOfferAnswerHandler::RestartIce() { } rtc::Thread* SdpOfferAnswerHandler::signaling_thread() const { - return pc_->signaling_thread(); + return context_->signaling_thread(); +} + +rtc::Thread* SdpOfferAnswerHandler::network_thread() const { + return context_->network_thread(); } void SdpOfferAnswerHandler::CreateOffer( @@ -1201,7 +1419,8 @@ void SdpOfferAnswerHandler::SetLocalDescription( RTC_DCHECK_RUN_ON(signaling_thread()); SetLocalDescription( rtc::make_ref_counted( - weak_ptr_factory_.GetWeakPtr(), observer)); + weak_ptr_factory_.GetWeakPtr(), + rtc::scoped_refptr(observer))); } void SdpOfferAnswerHandler::SetLocalDescription( @@ -1267,9 +1486,10 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(desc); - // Update stats here so that we have the most recent stats for tracks and - // streams that might be removed by updating the session description. - pc_->stats()->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + // Invalidate the [legacy] stats cache to make sure that it gets updated next + // time getStats() gets called, as updating the session description affects + // the stats. + pc_->stats()->InvalidateCache(); // Take a reference to the old local description since it's used below to // compare against the new local description. When setting the new local @@ -1291,6 +1511,9 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( replaced_local_description = std::move(pending_local_description_); pending_local_description_ = std::move(desc); } + if (!initial_offerer_) { + initial_offerer_.emplace(type == SdpType::kOffer); + } // The session description to apply now must be accessed by // `local_description()`. RTC_DCHECK(local_description()); @@ -1315,10 +1538,12 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( } if (IsUnifiedPlan()) { - RTCError error = UpdateTransceiversAndDataChannels( + error = UpdateTransceiversAndDataChannels( cricket::CS_LOCAL, *local_description(), old_local_description, remote_description(), bundle_groups_by_mid); if (!error.ok()) { + RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type) + << ")"; return error; } std::vector> remove_list; @@ -1334,7 +1559,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( // information about DTLS transports. if (transceiver->mid()) { auto dtls_transport = LookupDtlsTransportByMid( - pc_->network_thread(), transport_controller(), *transceiver->mid()); + context_->network_thread(), transport_controller_s(), + *transceiver->mid()); transceiver->sender_internal()->set_transport(dtls_transport); transceiver->receiver_internal()->set_transport(dtls_transport); } @@ -1379,6 +1605,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( // description is applied. Restore back to old description. RTCError error = CreateChannels(*local_description()->description()); if (!error.ok()) { + RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type) + << ")"; return error; } } @@ -1390,13 +1618,13 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( local_description()->description(), bundle_groups_by_mid); if (!error.ok()) { + RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type) + << ")"; return error; } - if (remote_description()) { - // Now that we have a local description, we can push down remote candidates. - UseCandidatesInSessionDescription(remote_description()); - } + // Now that we have a local description, we can push down remote candidates. + UseCandidatesInRemoteDescription(); pending_ice_restarts_.clear(); if (session_error() != SessionError::kNone) { @@ -1508,15 +1736,11 @@ void SdpOfferAnswerHandler::SetRemoteDescription( // SetSessionDescriptionObserverAdapter takes care of making sure the // `observer_refptr` is invoked in a posted message. this_weak_ptr->DoSetRemoteDescription( - std::move(desc), - rtc::make_ref_counted( - this_weak_ptr, observer_refptr)); - // For backwards-compatability reasons, we declare the operation as - // completed here (rather than in a post), so that the operation chain - // is not blocked by this operation when the observer is invoked. This - // allows the observer to trigger subsequent offer/answer operations - // synchronously if the operation chain is now empty. - operations_chain_callback(); + std::make_unique( + this_weak_ptr.get(), std::move(desc), + rtc::make_ref_counted( + this_weak_ptr, observer_refptr), + std::move(operations_chain_callback))); }); } @@ -1531,6 +1755,12 @@ void SdpOfferAnswerHandler::SetRemoteDescription( [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer, desc = std::move(desc)]( std::function operations_chain_callback) mutable { + if (!observer) { + RTC_DLOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; + operations_chain_callback(); + return; + } + // Abort early if `this_weak_ptr` is no longer valid. if (!this_weak_ptr) { observer->OnSetRemoteDescriptionComplete(RTCError( @@ -1539,107 +1769,80 @@ void SdpOfferAnswerHandler::SetRemoteDescription( operations_chain_callback(); return; } - this_weak_ptr->DoSetRemoteDescription(std::move(desc), - std::move(observer)); - // DoSetRemoteDescription() is implemented as a synchronous operation. - // The `observer` will already have been informed that it completed, and - // we can mark this operation as complete without any loose ends. - operations_chain_callback(); + + this_weak_ptr->DoSetRemoteDescription( + std::make_unique( + this_weak_ptr.get(), std::move(desc), std::move(observer), + std::move(operations_chain_callback))); }); } -RTCError SdpOfferAnswerHandler::ApplyRemoteDescription( +RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription( std::unique_ptr desc, - const std::map& - bundle_groups_by_mid) { - TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription"); - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc); - - // Update stats here so that we have the most recent stats for tracks and - // streams that might be removed by updating the session description. - pc_->stats()->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); - - // Take a reference to the old remote description since it's used below to - // compare against the new remote description. When setting the new remote - // description, grab ownership of the replaced session description in case it - // is the same as `old_remote_description`, to keep it alive for the duration - // of the method. - const SessionDescriptionInterface* old_remote_description = - remote_description(); - std::unique_ptr replaced_remote_description; - SdpType type = desc->GetType(); - if (type == SdpType::kAnswer) { - replaced_remote_description = pending_remote_description_ - ? std::move(pending_remote_description_) - : std::move(current_remote_description_); + SdpType sdp_type, + std::unique_ptr* replaced_description) { + RTC_DCHECK(replaced_description); + if (sdp_type == SdpType::kAnswer) { + *replaced_description = pending_remote_description_ + ? std::move(pending_remote_description_) + : std::move(current_remote_description_); current_remote_description_ = std::move(desc); pending_remote_description_ = nullptr; current_local_description_ = std::move(pending_local_description_); } else { - replaced_remote_description = std::move(pending_remote_description_); + *replaced_description = std::move(pending_remote_description_); pending_remote_description_ = std::move(desc); } + // The session description to apply now must be accessed by // `remote_description()`. - RTC_DCHECK(remote_description()); + const cricket::SessionDescription* session_desc = + remote_description()->description(); // Report statistics about any use of simulcast. ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription, - *remote_description()->description()); + *session_desc); - RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type); - if (!error.ok()) { - return error; - } - // Transport and Media channels will be created only when offer is set. - if (IsUnifiedPlan()) { - RTCError error = UpdateTransceiversAndDataChannels( - cricket::CS_REMOTE, *remote_description(), local_description(), - old_remote_description, bundle_groups_by_mid); - if (!error.ok()) { - return error; - } - } else { - // Media channels will be created only when offer is set. These may use new - // transports just created by PushdownTransportDescription. - if (type == SdpType::kOffer) { - // TODO(mallinath) - Handle CreateChannel failure, as new local - // description is applied. Restore back to old description. - RTCError error = CreateChannels(*remote_description()->description()); - if (!error.ok()) { - return error; - } - } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(remote_description()->description()); - } + // NOTE: This will perform an Invoke() to the network thread. + return transport_controller_s()->SetRemoteDescription(sdp_type, session_desc); +} + +void SdpOfferAnswerHandler::ApplyRemoteDescription( + std::unique_ptr operation) { + TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription"); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(operation->description()); + + // Invalidate the [legacy] stats cache to make sure that it gets updated next + // time getStats() gets called, as updating the session description affects + // the stats. + pc_->stats()->InvalidateCache(); + + if (!operation->ReplaceRemoteDescriptionAndCheckEror()) + return; + + if (!operation->UpdateChannels()) + return; // NOTE: Candidates allocation will be initiated only when // SetLocalDescription is called. - error = UpdateSessionState(type, cricket::CS_REMOTE, - remote_description()->description(), - bundle_groups_by_mid); - if (!error.ok()) { - return error; - } + if (!operation->UpdateSessionState()) + return; - if (local_description() && - !UseCandidatesInSessionDescription(remote_description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidCandidates); - } + if (!operation->UseCandidatesInRemoteDescription()) + return; - if (old_remote_description) { + if (operation->old_remote_description()) { for (const cricket::ContentInfo& content : - old_remote_description->description()->contents()) { + operation->old_remote_description()->description()->contents()) { // Check if this new SessionDescription contains new ICE ufrag and // password that indicates the remote peer requests an ICE restart. // TODO(deadbeef): When we start storing both the current and pending // remote description, this should reset pending_ice_restarts and compare // against the current description. - if (CheckForRemoteIceRestart(old_remote_description, remote_description(), - content.name)) { - if (type == SdpType::kOffer) { + if (CheckForRemoteIceRestart(operation->old_remote_description(), + remote_description(), content.name)) { + if (operation->type() == SdpType::kOffer) { pending_ice_restarts_.insert(content.name); } } else { @@ -1651,14 +1854,14 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription( // description plus any candidates added since then. We should remove // this once we're sure it won't break anything. WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( - old_remote_description, content.name, mutable_remote_description()); + operation->old_remote_description(), content.name, + mutable_remote_description()); } } } - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } + if (operation->HaveSessionError()) + return; // Set the the ICE connection state to connecting since the connection may // become writable with peer reflexive candidates before any remote candidate @@ -1670,7 +1873,7 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription( // actually means "gathering candidates", so cannot be be used here. if (remote_description()->GetType() != SdpType::kOffer && remote_description()->number_of_mediasections() > 0u && - pc_->ice_connection_state() == + pc_->ice_connection_state_internal() == PeerConnectionInterface::kIceConnectionNew) { pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); } @@ -1682,120 +1885,10 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription( data_channel_controller()->AllocateSctpSids(role); } - if (IsUnifiedPlan()) { - std::vector> - now_receiving_transceivers; - std::vector> remove_list; - std::vector> added_streams; - std::vector> removed_streams; - for (const auto& transceiver_ext : transceivers()->List()) { - const auto transceiver = transceiver_ext->internal(); - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, remote_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - RtpTransceiverDirection local_direction = - RtpTransceiverDirectionReversed(media_desc->direction()); - // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the - // RTCSessionDescription: Set the associated remote streams given - // transceiver.[[Receiver]], msids, addList, and removeList". - // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription - if (RtpTransceiverDirectionHasRecv(local_direction)) { - std::vector stream_ids; - if (!media_desc->streams().empty()) { - // The remote description has signaled the stream IDs. - stream_ids = media_desc->streams()[0].stream_ids(); - } - transceivers() - ->StableState(transceiver_ext) - ->SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); - - RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name - << " (" << GetStreamIdsString(stream_ids) << ")."; - SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids, - &added_streams, &removed_streams); - // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 - // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, - // and transceiver's current direction is neither sendrecv nor recvonly, - // process the addition of a remote track for the media description. - if (!transceiver->fired_direction() || - !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { - RTC_LOG(LS_INFO) - << "Processing the addition of a remote track for MID=" - << content->name << "."; - now_receiving_transceivers.push_back(transceiver); - } - } - // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's - // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the - // removal of a remote track for the media description, given transceiver, - // removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(local_direction) && - (transceiver->fired_direction() && - RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list, - &removed_streams); - } - // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. - transceiver->set_fired_direction(local_direction); - // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to - // direction. - transceiver->set_current_direction(local_direction); - // 2.2.8.1.11.[3-6]: Set the transport internal slots. - if (transceiver->mid()) { - auto dtls_transport = LookupDtlsTransportByMid(pc_->network_thread(), - transport_controller(), - *transceiver->mid()); - transceiver->sender_internal()->set_transport(dtls_transport); - transceiver->receiver_internal()->set_transport(dtls_transport); - } - } - // 2.2.8.1.12: If the media description is rejected, and transceiver is - // not already stopped, stop the RTCRtpTransceiver transceiver. - if (content->rejected && !transceiver->stopped()) { - RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name - << " since the media section was rejected."; - transceiver->StopTransceiverProcedure(); - } - if (!content->rejected && - RtpTransceiverDirectionHasRecv(local_direction)) { - if (!media_desc->streams().empty() && - media_desc->streams()[0].has_ssrcs()) { - uint32_t ssrc = media_desc->streams()[0].first_ssrc(); - transceiver->receiver_internal()->SetupMediaChannel(ssrc); - } else { - transceiver->receiver_internal()->SetupUnsignaledMediaChannel(); - } - } - } - // Once all processing has finished, fire off callbacks. - auto observer = pc_->Observer(); - for (const auto& transceiver : now_receiving_transceivers) { - pc_->stats()->AddTrack(transceiver->receiver()->track()); - observer->OnTrack(transceiver); - observer->OnAddTrack(transceiver->receiver(), - transceiver->receiver()->streams()); - } - for (const auto& stream : added_streams) { - observer->OnAddStream(stream); - } - for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); - } - for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); - } + if (operation->unified_plan()) { + ApplyRemoteDescriptionUpdateTransceiverState(operation->type()); } - const cricket::ContentInfo* audio_content = - GetFirstAudioContent(remote_description()->description()); - const cricket::ContentInfo* video_content = - GetFirstVideoContent(remote_description()->description()); const cricket::AudioContentDescription* audio_desc = GetFirstAudioContentDescription(remote_description()->description()); const cricket::VideoContentDescription* video_desc = @@ -1809,68 +1902,199 @@ RTCError SdpOfferAnswerHandler::ApplyRemoteDescription( remote_peer_supports_msid_ = true; } + if (!operation->unified_plan()) { + PlanBUpdateSendersAndReceivers( + GetFirstAudioContent(remote_description()->description()), audio_desc, + GetFirstVideoContent(remote_description()->description()), video_desc); + } + + if (operation->type() == SdpType::kAnswer) { + if (local_ice_credentials_to_replace_->SatisfiesIceRestart( + *current_local_description_)) { + local_ice_credentials_to_replace_->ClearIceCredentials(); + } + + RemoveStoppedTransceivers(); + } + + // Consider the operation complete at this point. + operation->SignalCompletion(); + + SetRemoteDescriptionPostProcess(operation->type() == SdpType::kAnswer); +} + +void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( + SdpType sdp_type) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(IsUnifiedPlan()); + std::vector> + now_receiving_transceivers; + std::vector> remove_list; + std::vector> added_streams; + std::vector> removed_streams; + for (const auto& transceiver_ext : transceivers()->List()) { + const auto transceiver = transceiver_ext->internal(); + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, remote_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = content->media_description(); + RtpTransceiverDirection local_direction = + RtpTransceiverDirectionReversed(media_desc->direction()); + // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the + // RTCSessionDescription: Set the associated remote streams given + // transceiver.[[Receiver]], msids, addList, and removeList". + // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription + if (RtpTransceiverDirectionHasRecv(local_direction)) { + std::vector stream_ids; + if (!media_desc->streams().empty()) { + // The remote description has signaled the stream IDs. + stream_ids = media_desc->streams()[0].stream_ids(); + } + transceivers() + ->StableState(transceiver_ext) + ->SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); + + RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name + << " (" << GetStreamIdsString(stream_ids) << ")."; + SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids, + &added_streams, &removed_streams); + // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 + // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, + // and transceiver's current direction is neither sendrecv nor recvonly, + // process the addition of a remote track for the media description. + if (!transceiver->fired_direction() || + !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { + RTC_LOG(LS_INFO) << "Processing the addition of a remote track for MID=" + << content->name << "."; + // Since the transceiver is passed to the user in an + // OnTrack event, we must use the proxied transceiver. + now_receiving_transceivers.push_back(transceiver_ext); + } + } + // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's + // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the + // removal of a remote track for the media description, given transceiver, + // removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(local_direction) && + (transceiver->fired_direction() && + RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list, + &removed_streams); + } + // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. + transceiver->set_fired_direction(local_direction); + // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run + // the following steps: + if (sdp_type == SdpType::kPrAnswer || sdp_type == SdpType::kAnswer) { + // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to + // direction. + transceiver->set_current_direction(local_direction); + // 2.2.8.1.11.[3-6]: Set the transport internal slots. + if (transceiver->mid()) { + auto dtls_transport = LookupDtlsTransportByMid( + context_->network_thread(), transport_controller_s(), + *transceiver->mid()); + transceiver->sender_internal()->set_transport(dtls_transport); + transceiver->receiver_internal()->set_transport(dtls_transport); + } + } + // 2.2.8.1.12: If the media description is rejected, and transceiver is + // not already stopped, stop the RTCRtpTransceiver transceiver. + if (content->rejected && !transceiver->stopped()) { + RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name + << " since the media section was rejected."; + transceiver->StopTransceiverProcedure(); + } + if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) { + if (!media_desc->streams().empty() && + media_desc->streams()[0].has_ssrcs()) { + uint32_t ssrc = media_desc->streams()[0].first_ssrc(); + transceiver->receiver_internal()->SetupMediaChannel(ssrc); + } else { + transceiver->receiver_internal()->SetupUnsignaledMediaChannel(); + } + } + } + // Once all processing has finished, fire off callbacks. + auto observer = pc_->Observer(); + for (const auto& transceiver : now_receiving_transceivers) { + pc_->stats()->AddTrack(transceiver->receiver()->track()); + observer->OnTrack(transceiver); + observer->OnAddTrack(transceiver->receiver(), + transceiver->receiver()->streams()); + } + for (const auto& stream : added_streams) { + observer->OnAddStream(stream); + } + for (const auto& transceiver : remove_list) { + observer->OnRemoveTrack(transceiver->receiver()); + } + for (const auto& stream : removed_streams) { + observer->OnRemoveStream(stream); + } +} + +void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers( + const cricket::ContentInfo* audio_content, + const cricket::AudioContentDescription* audio_desc, + const cricket::ContentInfo* video_content, + const cricket::VideoContentDescription* video_desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(!IsUnifiedPlan()); + // We wait to signal new streams until we finish processing the description, // since only at that point will new streams have all their tracks. rtc::scoped_refptr new_streams(StreamCollection::Create()); - if (!IsUnifiedPlan()) { - // TODO(steveanton): When removing RTP senders/receivers in response to a - // rejected media section, there is some cleanup logic that expects the - // voice/ video channel to still be set. But in this method the voice/video - // channel would have been destroyed by the SetRemoteDescription caller - // above so the cleanup that relies on them fails to run. The RemoveSenders - // calls should be moved to right before the DestroyChannel calls to fix - // this. + // TODO(steveanton): When removing RTP senders/receivers in response to a + // rejected media section, there is some cleanup logic that expects the + // voice/ video channel to still be set. But in this method the voice/video + // channel would have been destroyed by the SetRemoteDescription caller + // above so the cleanup that relies on them fails to run. The RemoveSenders + // calls should be moved to right before the DestroyChannel calls to fix + // this. - // Find all audio rtp streams and create corresponding remote AudioTracks - // and MediaStreams. - if (audio_content) { - if (audio_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_AUDIO); - } else { - bool default_audio_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(audio_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(audio_desc), - default_audio_track_needed, audio_desc->type(), - new_streams); - } + // Find all audio rtp streams and create corresponding remote AudioTracks + // and MediaStreams. + if (audio_content) { + if (audio_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_AUDIO); + } else { + bool default_audio_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(audio_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(audio_desc), + default_audio_track_needed, audio_desc->type(), + new_streams); } - - // Find all video rtp streams and create corresponding remote VideoTracks - // and MediaStreams. - if (video_content) { - if (video_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_VIDEO); - } else { - bool default_video_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(video_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(video_desc), - default_video_track_needed, video_desc->type(), - new_streams); - } - } - - // Iterate new_streams and notify the observer about new MediaStreams. - auto observer = pc_->Observer(); - for (size_t i = 0; i < new_streams->count(); ++i) { - MediaStreamInterface* new_stream = new_streams->at(i); - pc_->stats()->AddStream(new_stream); - observer->OnAddStream( - rtc::scoped_refptr(new_stream)); - } - - UpdateEndedRemoteMediaStreams(); } - if (type == SdpType::kAnswer && - local_ice_credentials_to_replace_->SatisfiesIceRestart( - *current_local_description_)) { - local_ice_credentials_to_replace_->ClearIceCredentials(); + // Find all video rtp streams and create corresponding remote VideoTracks + // and MediaStreams. + if (video_content) { + if (video_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_VIDEO); + } else { + bool default_video_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(video_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(video_desc), + default_video_track_needed, video_desc->type(), + new_streams); + } } - return RTCError::OK(); + // Iterate new_streams and notify the observer about new MediaStreams. + auto observer = pc_->Observer(); + for (size_t i = 0; i < new_streams->count(); ++i) { + MediaStreamInterface* new_stream = new_streams->at(i); + pc_->stats()->AddStream(new_stream); + observer->OnAddStream(rtc::scoped_refptr(new_stream)); + } + + UpdateEndedRemoteMediaStreams(); } void SdpOfferAnswerHandler::DoSetLocalDescription( @@ -1951,7 +2175,7 @@ void SdpOfferAnswerHandler::DoSetLocalDescription( // TODO(deadbeef): We already had to hop to the network thread for // MaybeStartGathering... - pc_->network_thread()->Invoke( + context_->network_thread()->Invoke( RTC_FROM_HERE, [this] { port_allocator()->DiscardCandidatePool(); }); // Make UMA notes about what was agreed to. ReportNegotiatedSdpSemantics(*local_description()); @@ -1980,9 +2204,9 @@ void SdpOfferAnswerHandler::DoSetLocalDescription( // completed. // RingRTC change to add ICE forking if (pc_->shared_ice_gatherer()) { - transport_controller()->StartGatheringWithSharedIceGatherer(pc_->shared_ice_gatherer()); + transport_controller_s()->StartGatheringWithSharedIceGatherer(pc_->shared_ice_gatherer()); } else { - transport_controller()->MaybeStartGathering(); + transport_controller_s()->MaybeStartGathering(); } } @@ -2125,110 +2349,48 @@ void SdpOfferAnswerHandler::DoCreateAnswer( } void SdpOfferAnswerHandler::DoSetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { + std::unique_ptr operation) { RTC_DCHECK_RUN_ON(signaling_thread()); TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetRemoteDescription"); - if (!observer) { - RTC_LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; + if (!operation->ok()) return; - } - if (!desc) { - observer->OnSetRemoteDescriptionComplete(RTCError( - RTCErrorType::INVALID_PARAMETER, "SessionDescription is NULL.")); + if (operation->HaveSessionError()) return; - } - // If a session error has occurred the PeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + if (operation->MaybeRollback()) return; - } - if (IsUnifiedPlan()) { - if (pc_->configuration()->enable_implicit_rollback) { - if (desc->GetType() == SdpType::kOffer && - signaling_state() == PeerConnectionInterface::kHaveLocalOffer) { - Rollback(desc->GetType()); - } - } - // Explicit rollback. - if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType())); - return; - } - } else if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Rollback not supported in Plan B")); + + operation->ReportOfferAnswerUma(); + + // Handle remote descriptions missing a=mid lines for interop with legacy + // end points. + FillInMissingRemoteMids(operation->description()); + if (!operation->IsDescriptionValid()) return; - } - if (desc->GetType() == SdpType::kOffer || - desc->GetType() == SdpType::kAnswer) { - // Report to UMA the format of the received offer or answer. - pc_->ReportSdpFormatReceived(*desc); - pc_->ReportSdpBundleUsage(*desc); - } - // Handle remote descriptions missing a=mid lines for interop with legacy end - // points. - FillInMissingRemoteMids(desc->description()); + ApplyRemoteDescription(std::move(operation)); +} - std::map bundle_groups_by_mid = - GetBundleGroupsByMid(desc->description()); - RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE, - bundle_groups_by_mid); - if (!error.ok()) { - std::string error_message = GetSetDescriptionErrorMessage( - cricket::CS_REMOTE, desc->GetType(), error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } - - // Grab the description type before moving ownership to - // ApplyRemoteDescription, which may destroy it before returning. - const SdpType type = desc->GetType(); - - error = ApplyRemoteDescription(std::move(desc), bundle_groups_by_mid); - // `desc` may be destroyed at this point. - - if (!error.ok()) { - // If ApplyRemoteDescription fails, the PeerConnection could be in an - // inconsistent state, so act conservatively here and set the session error - // so that future calls to SetLocalDescription/SetRemoteDescription fail. - SetSessionError(SessionError::kContent, error.message()); - std::string error_message = - GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type, error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } +// Called after a DoSetRemoteDescription operation completes. +void SdpOfferAnswerHandler::SetRemoteDescriptionPostProcess(bool was_answer) { RTC_DCHECK(remote_description()); - if (type == SdpType::kAnswer) { - RemoveStoppedTransceivers(); + if (was_answer) { // TODO(deadbeef): We already had to hop to the network thread for // MaybeStartGathering... - pc_->network_thread()->Invoke( + context_->network_thread()->Invoke( RTC_FROM_HERE, [this] { port_allocator()->DiscardCandidatePool(); }); // Make UMA notes about what was agreed to. ReportNegotiatedSdpSemantics(*remote_description()); } - observer->OnSetRemoteDescriptionComplete(RTCError::OK()); pc_->NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED); // Check if negotiation is needed. We must do this after informing the - // observer that SetRemoteDescription() has completed to ensure negotiation is - // not needed prior to the promise resolving. + // observer that SetRemoteDescription() has completed to ensure negotiation + // is not needed prior to the promise resolving. if (IsUnifiedPlan()) { bool was_negotiation_needed = is_negotiation_needed_; UpdateNegotiationNeeded(); @@ -2250,8 +2412,8 @@ void SdpOfferAnswerHandler::SetAssociatedRemoteStreams( RTC_DCHECK_RUN_ON(signaling_thread()); std::vector> media_streams; for (const std::string& stream_id : stream_ids) { - rtc::scoped_refptr stream = - remote_streams_->find(stream_id); + rtc::scoped_refptr stream( + remote_streams_->find(stream_id)); if (!stream) { stream = MediaStreamProxy::Create(rtc::Thread::Current(), MediaStream::Create(stream_id)); @@ -2273,10 +2435,10 @@ void SdpOfferAnswerHandler::SetAssociatedRemoteStreams( } std::vector> previous_streams = receiver->streams(); - // SetStreams() will add/remove the receiver's track to/from the streams. This - // differs from the spec - the spec uses an "addList" and "removeList" to - // update the stream-track relationships in a later step. We do this earlier, - // changing the order of things, but the end-result is the same. + // SetStreams() will add/remove the receiver's track to/from the streams. + // This differs from the spec - the spec uses an "addList" and "removeList" + // to update the stream-track relationships in a later step. We do this + // earlier, changing the order of things, but the end-result is the same. // TODO(hbos): When we remove remote_streams(), use set_stream_ids() // instead. https://crbug.com/webrtc/9480 receiver->SetStreams(media_streams); @@ -2287,8 +2449,8 @@ bool SdpOfferAnswerHandler::AddIceCandidate( const IceCandidateInterface* ice_candidate) { const AddIceCandidateResult result = AddIceCandidateInternal(ice_candidate); NoteAddIceCandidateResult(result); - // If the return value is kAddIceCandidateFailNotReady, the candidate has been - // added, although not 'ready', but that's a success. + // If the return value is kAddIceCandidateFailNotReady, the candidate has + // been added, although not 'ready', but that's a success. return result == kAddIceCandidateSuccess || result == kAddIceCandidateFailNotReady; } @@ -2344,9 +2506,9 @@ void SdpOfferAnswerHandler::AddIceCandidate( std::function callback) { TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate"); RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. + // Chain this operation. If asynchronous operations are pending on the + // chain, this operation will be queued to be invoked, otherwise the + // contents of the lambda will execute immediately. operations_chain_->ChainOperation( [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), candidate = std::move(candidate), callback = std::move(callback)]( @@ -2357,18 +2519,42 @@ void SdpOfferAnswerHandler::AddIceCandidate( : kAddIceCandidateFailClosed; NoteAddIceCandidateResult(result); operations_chain_callback(); - if (result == kAddIceCandidateFailClosed) { - callback(RTCError( - RTCErrorType::INVALID_STATE, - "AddIceCandidate failed because the session was shut down")); - } else if (result != kAddIceCandidateSuccess && - result != kAddIceCandidateFailNotReady) { - // Fail with an error type and message consistent with Chromium. - // TODO(hbos): Fail with error types according to spec. - callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Error processing ICE candidate")); - } else { - callback(RTCError::OK()); + switch (result) { + case AddIceCandidateResult::kAddIceCandidateSuccess: + case AddIceCandidateResult::kAddIceCandidateFailNotReady: + // Success! + callback(RTCError::OK()); + break; + case AddIceCandidateResult::kAddIceCandidateFailClosed: + // Note that the spec says to just abort without resolving the + // promise in this case, but this layer must return an RTCError. + callback(RTCError( + RTCErrorType::INVALID_STATE, + "AddIceCandidate failed because the session was shut down")); + break; + case AddIceCandidateResult::kAddIceCandidateFailNoRemoteDescription: + // Spec: "If remoteDescription is null return a promise rejected + // with a newly created InvalidStateError." + callback(RTCError(RTCErrorType::INVALID_STATE, + "The remote description was null")); + break; + case AddIceCandidateResult::kAddIceCandidateFailNullCandidate: + // TODO(https://crbug.com/935898): Handle end-of-candidates instead + // of treating null candidate as an error. + callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Error processing ICE candidate")); + break; + case AddIceCandidateResult::kAddIceCandidateFailNotValid: + case AddIceCandidateResult::kAddIceCandidateFailInAddition: + case AddIceCandidateResult::kAddIceCandidateFailNotUsable: + // Spec: "If candidate could not be successfully added [...] Reject + // p with a newly created OperationError and abort these steps." + // UNSUPPORTED_OPERATION maps to OperationError. + callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Error processing ICE candidate")); + break; + default: + RTC_DCHECK_NOTREACHED(); } }); } @@ -2403,7 +2589,7 @@ bool SdpOfferAnswerHandler::RemoveIceCandidates( } // Remove the candidates from the transport controller. - RTCError error = transport_controller()->RemoveRemoteCandidates(candidates); + RTCError error = transport_controller_s()->RemoveRemoteCandidates(candidates); if (!error.ok()) { RTC_LOG(LS_ERROR) << "RemoveIceCandidates: Error when removing remote candidates: " @@ -2480,9 +2666,9 @@ void SdpOfferAnswerHandler::ChangeSignalingState( return; } RTC_LOG(LS_INFO) << "Session: " << pc_->session_id() << " Old state: " - << GetSignalingStateString(signaling_state_) + << PeerConnectionInterface::AsString(signaling_state_) << " New state: " - << GetSignalingStateString(signaling_state); + << PeerConnectionInterface::AsString(signaling_state); signaling_state_ = signaling_state; pc_->Observer()->OnSignalingChange(signaling_state_); } @@ -2495,8 +2681,8 @@ RTCError SdpOfferAnswerHandler::UpdateSessionState( bundle_groups_by_mid) { RTC_DCHECK_RUN_ON(signaling_thread()); - // If there's already a pending error then no state transition should happen. - // But all call-sites should be verifying this before calling us! + // If there's already a pending error then no state transition should + // happen. But all call-sites should be verifying this before calling us! RTC_DCHECK(session_error() == SessionError::kNone); // If this is answer-ish we're ready to let media flow. @@ -2542,10 +2728,10 @@ bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent( // one obsolete. if (!operations_chain_->IsEmpty()) { // Since we just suppressed an event that would have been fired, if - // negotiation is still needed by the time the chain becomes empty again, we - // must make sure to generate another event if negotiation is needed then. - // This happens when `is_negotiation_needed_` goes from false to true, so we - // set it to false until UpdateNegotiationNeeded() is called. + // negotiation is still needed by the time the chain becomes empty again, + // we must make sure to generate another event if negotiation is needed + // then. This happens when `is_negotiation_needed_` goes from false to + // true, so we set it to false until UpdateNegotiationNeeded() is called. is_negotiation_needed_ = false; update_negotiation_needed_on_empty_chain_ = true; return false; @@ -2589,7 +2775,8 @@ bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) { return false; } - local_streams_->AddStream(local_stream); + local_streams_->AddStream( + rtc::scoped_refptr(local_stream)); auto observer = std::make_unique( local_stream, [this](AudioTrackInterface* audio_track, @@ -2697,8 +2884,9 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { if (state != PeerConnectionInterface::kHaveLocalOffer && state != PeerConnectionInterface::kHaveRemoteOffer) { return RTCError(RTCErrorType::INVALID_STATE, - "Called in wrong signalingState: " + - GetSignalingStateString(signaling_state())); + (rtc::StringBuilder("Called in wrong signalingState: ") + << (PeerConnectionInterface::AsString(signaling_state()))) + .Release()); } RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(IsUnifiedPlan()); @@ -2727,7 +2915,7 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { } RTC_DCHECK(transceiver->internal()->mid().has_value()); - DestroyTransceiverChannel(transceiver); + transceiver->internal()->SetChannel(nullptr, nullptr); if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && transceiver->receiver()) { @@ -2749,7 +2937,7 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { transceiver->internal()->set_mid(state.mid()); transceiver->internal()->set_mline_index(state.mline_index()); } - RTCError e = transport_controller()->RollbackTransports(); + RTCError e = transport_controller_s()->RollbackTransports(); if (!e.ok()) { return e; } @@ -2769,8 +2957,8 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { pc_->Observer()->OnRemoveStream(stream); } - // The assumption is that in case of implicit rollback UpdateNegotiationNeeded - // gets called in SetRemoteDescription. + // The assumption is that in case of implicit rollback + // UpdateNegotiationNeeded gets called in SetRemoteDescription. if (desc_type == SdpType::kRollback) { UpdateNegotiationNeeded(); if (is_negotiation_needed_) { @@ -2792,9 +2980,9 @@ void SdpOfferAnswerHandler::OnOperationsChainEmpty() { if (pc_->IsClosed() || !update_negotiation_needed_on_empty_chain_) return; update_negotiation_needed_on_empty_chain_ = false; - // Firing when chain is empty is only supported in Unified Plan to avoid Plan - // B regressions. (In Plan B, onnegotiationneeded is already broken anyway, so - // firing it even more might just be confusing.) + // Firing when chain is empty is only supported in Unified Plan to avoid + // Plan B regressions. (In Plan B, onnegotiationneeded is already broken + // anyway, so firing it even more might just be confusing.) if (IsUnifiedPlan()) { UpdateNegotiationNeeded(); } @@ -2824,7 +3012,8 @@ bool SdpOfferAnswerHandler::NeedsIceRestart( absl::optional SdpOfferAnswerHandler::GetDtlsRole( const std::string& mid) const { - return transport_controller()->GetDtlsRole(mid); + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_s()->GetDtlsRole(mid); } void SdpOfferAnswerHandler::UpdateNegotiationNeeded() { @@ -2836,8 +3025,8 @@ void SdpOfferAnswerHandler::UpdateNegotiationNeeded() { } // In the spec, a task is queued here to run the following steps - this is - // meant to ensure we do not fire onnegotiationneeded prematurely if multiple - // changes are being made at once. In order to support Chromium's + // meant to ensure we do not fire onnegotiationneeded prematurely if + // multiple changes are being made at once. In order to support Chromium's // implementation where the JavaScript representation of the PeerConnection // lives on a separate thread though, the queuing of a task is instead // performed by the PeerConnectionObserver posting from the signaling thread @@ -2860,8 +3049,8 @@ void SdpOfferAnswerHandler::UpdateNegotiationNeeded() { // "stable", as part of the steps for setting an RTCSessionDescription. // If the result of checking if negotiation is needed is false, clear the - // negotiation-needed flag by setting connection's [[NegotiationNeeded]] slot - // to false, and abort these steps. + // negotiation-needed flag by setting connection's [[NegotiationNeeded]] + // slot to false, and abort these steps. bool is_negotiation_needed = CheckIfNegotiationIsNeeded(); if (!is_negotiation_needed) { is_negotiation_needed_ = false; @@ -2884,16 +3073,16 @@ void SdpOfferAnswerHandler::UpdateNegotiationNeeded() { // If connection's [[NegotiationNeeded]] slot is false, abort these steps. // Fire an event named negotiationneeded at connection. pc_->Observer()->OnRenegotiationNeeded(); - // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent() is - // used in the task queued by the observer, this event will only fire when the - // chain is empty. + // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent() + // is used in the task queued by the observer, this event will only fire + // when the chain is empty. GenerateNegotiationNeededEvent(); } bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() { RTC_DCHECK_RUN_ON(signaling_thread()); - // 1. If any implementation-specific negotiation is required, as described at - // the start of this section, return true. + // 1. If any implementation-specific negotiation is required, as described + // at the start of this section, return true. // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return // true. @@ -3040,20 +3229,20 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( cricket::ContentSource source, const std::map& bundle_groups_by_mid) { - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } + // An assumption is that a check for session error is done at a higher level. + RTC_DCHECK_EQ(SessionError::kNone, session_error()); if (!sdesc || !sdesc->description()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + return RTCError(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); } SdpType type = sdesc->GetType(); if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) || (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - "Called in wrong state: " + GetSignalingStateString(signaling_state())); + return RTCError(RTCErrorType::INVALID_STATE, + (rtc::StringBuilder("Called in wrong state: ") + << PeerConnectionInterface::AsString(signaling_state())) + .Release()); } RTCError error = ValidateMids(*sdesc->description()); @@ -3074,14 +3263,12 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( // Verify ice-ufrag and ice-pwd. if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutIceUfragPwd); + return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutIceUfragPwd); } if (!pc_->ValidateBundleSettings(sdesc->description(), bundle_groups_by_mid)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kBundleWithoutRtcpMux); + return RTCError(RTCErrorType::INVALID_PARAMETER, kBundleWithoutRtcpMux); } // TODO(skvlad): When the local rtcp-mux policy is Require, reject any @@ -3089,25 +3276,25 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( // Verify m-lines in Answer when compared against Offer. if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // With an answer we want to compare the new answer session description with - // the offer's session description from the current negotiation. + // With an answer we want to compare the new answer session description + // with the offer's session description from the current negotiation. const cricket::SessionDescription* offer_desc = (source == cricket::CS_LOCAL) ? remote_description()->description() : local_description()->description(); if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInAnswer); + return RTCError(RTCErrorType::INVALID_PARAMETER, kMlineMismatchInAnswer); } } else { // The re-offers should respect the order of m= sections in current // description. See RFC3264 Section 8 paragraph 4 for more details. - // With a re-offer, either the current local or current remote descriptions - // could be the most up to date, so we would like to check against both of - // them if they exist. It could be the case that one of them has a 0 port - // for a media section, but the other does not. This is important to check - // against in the case that we are recycling an m= section. + // With a re-offer, either the current local or current remote + // descriptions could be the most up to date, so we would like to check + // against both of them if they exist. It could be the case that one of + // them has a 0 port for a media section, but the other does not. This is + // important to check against in the case that we are recycling an m= + // section. const cricket::SessionDescription* current_desc = nullptr; const cricket::SessionDescription* secondary_current_desc = nullptr; if (local_description()) { @@ -3121,8 +3308,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if (current_desc && !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInSubsequentOffer); + return RTCError(RTCErrorType::INVALID_PARAMETER, + kMlineMismatchInSubsequentOffer); } } @@ -3137,10 +3324,10 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if ((desc.type() == cricket::MEDIA_TYPE_AUDIO || desc.type() == cricket::MEDIA_TYPE_VIDEO) && desc.streams().size() > 1u) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Media section has more than one track specified " - "with a=ssrc lines which is not supported with " - "Unified Plan."); + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "Media section has more than one track specified with a=ssrc lines " + "which is not supported with Unified Plan."); } } } @@ -3162,14 +3349,15 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( if (new_session.GetType() == SdpType::kOffer) { // If the BUNDLE policy is max-bundle, then we know for sure that all - // transports will be bundled from the start. Return an error if max-bundle - // is specified but the session description does not have a BUNDLE group. + // transports will be bundled from the start. Return an error if + // max-bundle is specified but the session description does not have a + // BUNDLE group. if (pc_->configuration()->bundle_policy == PeerConnectionInterface::kBundlePolicyMaxBundle && bundle_groups_by_mid.empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max-bundle configured but session description " - "has no BUNDLE group"); + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "max-bundle configured but session description has no BUNDLE group"); } } @@ -3227,8 +3415,7 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) { RTC_LOG(LS_INFO) << "Ignoring unsupported media type"; } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Unknown section type."); + return RTCError(RTCErrorType::INTERNAL_ERROR, "Unknown section type."); } } @@ -3273,8 +3460,8 @@ SdpOfferAnswerHandler::AssociateTransceiver( } if (!transceiver) { // This may happen normally when media sections are rejected. - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Transceiver not found based on m-line index"); + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Transceiver not found based on m-line index"); } } else { RTC_DCHECK_EQ(source, cricket::CS_REMOTE); @@ -3334,9 +3521,8 @@ SdpOfferAnswerHandler::AssociateTransceiver( } if (transceiver->media_type() != media_desc->type()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Transceiver type does not match media description type."); + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Transceiver type does not match media description type."); } if (media_desc->HasSimulcast()) { @@ -3383,8 +3569,7 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel( cricket::ChannelInterface* channel = transceiver->internal()->channel(); if (content.rejected) { if (channel) { - transceiver->internal()->SetChannel(nullptr); - DestroyChannelInterface(channel); + transceiver->internal()->SetChannel(nullptr, nullptr); } } else { if (!channel) { @@ -3395,11 +3580,15 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel( channel = CreateVideoChannel(content.name); } if (!channel) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create channel for mid=" + content.name); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create channel for mid=" + content.name); } - transceiver->internal()->SetChannel(channel); + // Note: this is a thread hop; the lambda will be executed + // on the network thread. + transceiver->internal()->SetChannel(channel, [&](const std::string& mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); } } return RTCError::OK(); @@ -3422,8 +3611,8 @@ RTCError SdpOfferAnswerHandler::UpdateDataChannel( if (!data_channel_controller()->data_channel_transport()) { RTC_LOG(LS_INFO) << "Creating data channel, mid=" << content.mid(); if (!CreateDataChannel(content.name)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create data channel."); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create data channel."); } } } @@ -3568,7 +3757,7 @@ void SdpOfferAnswerHandler::GetOptionsForOffer( pc_->configuration()->enable_ice_renomination)); } else { session_options->ice_credentials = - pc_->network_thread()->Invoke>( + context_->network_thread()->Invoke>( RTC_FROM_HERE, [this] { return port_allocator()->GetPooledIceCredentials(); }); } @@ -3831,7 +4020,7 @@ void SdpOfferAnswerHandler::GetOptionsForAnswer( pc_->configuration()->enable_ice_renomination)); } else { session_options->ice_credentials = - pc_->network_thread()->Invoke>( + context_->network_thread()->Invoke>( RTC_FROM_HERE, [this] { return port_allocator()->GetPooledIceCredentials(); }); } @@ -3945,7 +4134,7 @@ const char* SdpOfferAnswerHandler::SessionErrorToString( case SessionError::kTransport: return "ERROR_TRANSPORT"; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } @@ -4182,8 +4371,8 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList( const std::string& sender_id = params.id; uint32_t ssrc = params.first_ssrc(); - rtc::scoped_refptr stream = - remote_streams_->find(stream_id); + rtc::scoped_refptr stream( + remote_streams_->find(stream_id)); if (!stream) { // This is a new MediaStream. Create a new remote MediaStream. stream = MediaStreamProxy::Create(rtc::Thread::Current(), @@ -4203,8 +4392,8 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList( // Add default sender if necessary. if (default_sender_needed) { - rtc::scoped_refptr default_stream = - remote_streams_->find(kDefaultStreamId); + rtc::scoped_refptr default_stream( + remote_streams_->find(kDefaultStreamId)); if (!default_stream) { // Create the new default MediaStream. default_stream = MediaStreamProxy::Create( @@ -4249,12 +4438,14 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(sdesc); + // Note: This will perform an Invoke over to the worker thread, which we'll + // also do in a loop below. if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) { // Note that this is never expected to fail, since RtpDemuxer doesn't return // an error when changing payload type demux criteria, which is all this // does. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to update payload type demuxing state."); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to update payload type demuxing state."); } // Push down the new SDP media section for each audio/video transceiver. @@ -4289,20 +4480,15 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( // - crbug.com/1157227 // - crbug.com/1187289 for (const auto& entry : channels) { - RTCError error = - pc_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { - std::string error; - bool success = - (source == cricket::CS_LOCAL) - ? entry.first->SetLocalContent(entry.second, type, &error) - : entry.first->SetRemoteContent(entry.second, type, &error); - if (!success) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); - } - return RTCError::OK(); + std::string error; + bool success = + context_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + return (source == cricket::CS_LOCAL) + ? entry.first->SetLocalContent(entry.second, type, error) + : entry.first->SetRemoteContent(entry.second, type, error); }); - if (!error.ok()) { - return error; + if (!success) { + return RTCError(RTCErrorType::INVALID_PARAMETER, error); } } @@ -4342,13 +4528,13 @@ RTCError SdpOfferAnswerHandler::PushdownTransportDescription( if (source == cricket::CS_LOCAL) { const SessionDescriptionInterface* sdesc = local_description(); RTC_DCHECK(sdesc); - return transport_controller()->SetLocalDescription(type, - sdesc->description()); + return transport_controller_s()->SetLocalDescription(type, + sdesc->description()); } else { const SessionDescriptionInterface* sdesc = remote_description(); RTC_DCHECK(sdesc); - return transport_controller()->SetRemoteDescription(type, - sdesc->description()); + return transport_controller_s()->SetRemoteDescription(type, + sdesc->description()); } } @@ -4397,12 +4583,14 @@ void SdpOfferAnswerHandler::RemoveUnusedChannels( // voice channel. const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); if (!video_info || video_info->rejected) { - DestroyTransceiverChannel(rtp_manager()->GetVideoTransceiver()); + rtp_manager()->GetVideoTransceiver()->internal()->SetChannel(nullptr, + nullptr); } const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); if (!audio_info || audio_info->rejected) { - DestroyTransceiverChannel(rtp_manager()->GetAudioTransceiver()); + rtp_manager()->GetAudioTransceiver()->internal()->SetChannel(nullptr, + nullptr); } const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); @@ -4439,7 +4627,7 @@ void SdpOfferAnswerHandler::ReportNegotiatedSdpSemantics( semantics_negotiated = kSdpSemanticNegotiatedMixed; break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated", semantics_negotiated, kSdpSemanticNegotiatedMax); @@ -4451,7 +4639,8 @@ void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() { for (size_t i = 0; i < remote_streams_->count(); ++i) { MediaStreamInterface* stream = remote_streams_->at(i); if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { - streams_to_remove.push_back(stream); + streams_to_remove.push_back( + rtc::scoped_refptr(stream)); } } @@ -4461,9 +4650,9 @@ void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() { } } -bool SdpOfferAnswerHandler::UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc) { +bool SdpOfferAnswerHandler::UseCandidatesInRemoteDescription() { RTC_DCHECK_RUN_ON(signaling_thread()); + auto* remote_desc = remote_description(); if (!remote_desc) { return true; } @@ -4477,7 +4666,7 @@ bool SdpOfferAnswerHandler::UseCandidatesInSessionDescription( if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) { if (valid) { RTC_LOG(LS_INFO) - << "UseCandidatesInSessionDescription: Not ready to use " + << "UseCandidatesInRemoteDescription: Not ready to use " "candidate."; } continue; @@ -4592,10 +4781,14 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { !rtp_manager()->GetAudioTransceiver()->internal()->channel()) { cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); if (!voice_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create voice channel."); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create voice channel."); } - rtp_manager()->GetAudioTransceiver()->internal()->SetChannel(voice_channel); + rtp_manager()->GetAudioTransceiver()->internal()->SetChannel( + voice_channel, [&](const std::string& mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); } const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); @@ -4603,18 +4796,22 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { !rtp_manager()->GetVideoTransceiver()->internal()->channel()) { cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); if (!video_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create video channel."); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create video channel."); } - rtp_manager()->GetVideoTransceiver()->internal()->SetChannel(video_channel); + rtp_manager()->GetVideoTransceiver()->internal()->SetChannel( + video_channel, [&](const std::string& mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); } const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc); if (data && !data->rejected && !data_channel_controller()->data_channel_transport()) { if (!CreateDataChannel(data->name)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create data channel."); + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create data channel."); } } @@ -4629,15 +4826,12 @@ cricket::VoiceChannel* SdpOfferAnswerHandler::CreateVoiceChannel( if (!channel_manager()->media_engine()) return nullptr; - RtpTransportInternal* rtp_transport = pc_->GetRtpTransport(mid); - // TODO(bugs.webrtc.org/11992): CreateVoiceChannel internally switches to the // worker thread. We shouldn't be using the `call_ptr_` hack here but simply // be on the worker thread and use `call_` (update upstream code). return channel_manager()->CreateVoiceChannel( - pc_->call_ptr(), pc_->configuration()->media_config, rtp_transport, - signaling_thread(), mid, pc_->SrtpRequired(), pc_->GetCryptoOptions(), - &ssrc_generator_, audio_options()); + pc_->call_ptr(), pc_->configuration()->media_config, mid, + pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options()); } // TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. @@ -4648,23 +4842,19 @@ cricket::VideoChannel* SdpOfferAnswerHandler::CreateVideoChannel( if (!channel_manager()->media_engine()) return nullptr; - // NOTE: This involves a non-ideal hop (Invoke) over to the network thread. - RtpTransportInternal* rtp_transport = pc_->GetRtpTransport(mid); - // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to the // worker thread. We shouldn't be using the `call_ptr_` hack here but simply // be on the worker thread and use `call_` (update upstream code). return channel_manager()->CreateVideoChannel( - pc_->call_ptr(), pc_->configuration()->media_config, rtp_transport, - signaling_thread(), mid, pc_->SrtpRequired(), pc_->GetCryptoOptions(), - &ssrc_generator_, video_options(), + pc_->call_ptr(), pc_->configuration()->media_config, mid, + pc_->SrtpRequired(), pc_->GetCryptoOptions(), video_options(), video_bitrate_allocator_factory_.get()); } bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) { RTC_DCHECK_RUN_ON(signaling_thread()); - if (!pc_->network_thread()->Invoke(RTC_FROM_HERE, [this, &mid] { - RTC_DCHECK_RUN_ON(pc_->network_thread()); + if (!context_->network_thread()->Invoke(RTC_FROM_HERE, [this, &mid] { + RTC_DCHECK_RUN_ON(context_->network_thread()); return pc_->SetupDataChannelTransport_n(mid); })) { return false; @@ -4678,40 +4868,6 @@ bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) { return true; } -void SdpOfferAnswerHandler::DestroyTransceiverChannel( - rtc::scoped_refptr> - transceiver) { - TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DestroyTransceiverChannel"); - RTC_DCHECK(transceiver); - RTC_LOG_THREAD_BLOCK_COUNT(); - - // TODO(tommi): We're currently on the signaling thread. - // There are multiple hops to the worker ahead. - // Consider if we can make the call to SetChannel() on the worker thread - // (and require that to be the context it's always called in) and also - // call DestroyChannelInterface there, since it also needs to hop to the - // worker. - - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); - if (channel) { - // TODO(tommi): VideoRtpReceiver::SetMediaChannel blocks and jumps to the - // worker thread. When being set to nullptr, there are additional - // blocking calls to e.g. ClearRecordableEncodedFrameCallback which triggers - // another blocking call or Stop() for video channels. - // The channel object also needs to be de-initialized on the network thread - // so if ownership of the channel object lies with the transceiver, we could - // un-set the channel pointer and uninitialize/destruct the channel object - // at the same time, rather than in separate steps. - transceiver->internal()->SetChannel(nullptr); - // TODO(tommi): All channel objects end up getting deleted on the - // worker thread (ideally should be on the network thread but the - // MediaChannel objects are tied to the worker. Can the teardown be done - // asynchronously across the threads rather than blocking? - DestroyChannelInterface(channel); - } -} - void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) { RTC_DCHECK_RUN_ON(signaling_thread()); const bool has_sctp = pc_->sctp_mid().has_value(); @@ -4719,8 +4875,8 @@ void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) { if (has_sctp) data_channel_controller()->OnTransportChannelClosed(error); - pc_->network_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(pc_->network_thread()); + context_->network_thread()->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(context_->network_thread()); pc_->TeardownDataChannelTransport_n(); }); @@ -4728,44 +4884,6 @@ void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) { pc_->ResetSctpDataMid(); } -void SdpOfferAnswerHandler::DestroyChannelInterface( - cricket::ChannelInterface* channel) { - TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DestroyChannelInterface"); - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(channel_manager()->media_engine()); - RTC_DCHECK(channel); - - // TODO(bugs.webrtc.org/11992): All the below methods should be called on the - // worker thread. (they switch internally anyway). Change - // DestroyChannelInterface to either be called on the worker thread, or do - // this asynchronously on the worker. - RTC_LOG_THREAD_BLOCK_COUNT(); - - switch (channel->media_type()) { - case cricket::MEDIA_TYPE_AUDIO: - channel_manager()->DestroyVoiceChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_VIDEO: - channel_manager()->DestroyVideoChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_DATA: - RTC_NOTREACHED() - << "Trying to destroy datachannel through DestroyChannelInterface"; - break; - default: - RTC_NOTREACHED() << "Unknown media type: " << channel->media_type(); - break; - } - - // TODO(tommi): Figure out why we can get 2 blocking calls when running - // PeerConnectionCryptoTest.CreateAnswerWithDifferentSslRoles. - // and 3 when running - // PeerConnectionCryptoTest.CreateAnswerWithDifferentSslRoles - // RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); -} - void SdpOfferAnswerHandler::DestroyAllChannels() { RTC_DCHECK_RUN_ON(signaling_thread()); if (!transceivers()) { @@ -4781,12 +4899,12 @@ void SdpOfferAnswerHandler::DestroyAllChannels() { for (const auto& transceiver : list) { if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - DestroyTransceiverChannel(transceiver); + transceiver->internal()->SetChannel(nullptr, nullptr); } } for (const auto& transceiver : list) { if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - DestroyTransceiverChannel(transceiver); + transceiver->internal()->SetChannel(nullptr, nullptr); } } @@ -4971,29 +5089,6 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( } } - // Gather all updates ahead of time so that all channels can be updated in a - // single Invoke; necessary due to thread guards. - std::vector> - channels_to_update; - for (const auto& transceiver : transceivers()->ListInternal()) { - cricket::ChannelInterface* channel = transceiver->channel(); - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, sdesc); - if (!channel || !content) { - continue; - } - RtpTransceiverDirection local_direction = - content->media_description()->direction(); - if (source == cricket::CS_REMOTE) { - local_direction = RtpTransceiverDirectionReversed(local_direction); - } - channels_to_update.emplace_back(local_direction, transceiver->channel()); - } - - if (channels_to_update.empty()) { - return true; - } - // In Unified Plan, payload type demuxing is useful for legacy endpoints that // don't support the MID header extension, but it can also cause incorrrect // forwarding of packets when going from one m= section to multiple m= @@ -5012,52 +5107,72 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() || mid_header_extension_missing_video || pt_demuxing_has_been_used_video_; - // Kill switch for the above change. - if (field_trial::IsEnabled(kAlwaysAllowPayloadTypeDemuxingFieldTrialName)) { - // TODO(https://crbug.com/webrtc/12814): If disabling PT-based demux does - // not trigger regressions, remove this kill switch. - bundled_pt_demux_allowed_audio = true; - bundled_pt_demux_allowed_video = true; + + // Gather all updates ahead of time so that all channels can be updated in a + // single Invoke; necessary due to thread guards. + std::vector> channels_to_update; + for (const auto& transceiver : transceivers()->ListInternal()) { + cricket::ChannelInterface* channel = transceiver->channel(); + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, sdesc); + if (!channel || !content) { + continue; + } + + const cricket::MediaType media_type = channel->media_type(); + if (media_type != cricket::MediaType::MEDIA_TYPE_AUDIO && + media_type != cricket::MediaType::MEDIA_TYPE_VIDEO) { + continue; + } + + RtpTransceiverDirection local_direction = + content->media_description()->direction(); + if (source == cricket::CS_REMOTE) { + local_direction = RtpTransceiverDirectionReversed(local_direction); + } + + auto bundle_it = bundle_groups_by_mid.find(channel->mid()); + const cricket::ContentGroup* bundle_group = + bundle_it != bundle_groups_by_mid.end() ? bundle_it->second : nullptr; + bool pt_demux_enabled = RtpTransceiverDirectionHasRecv(local_direction); + if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) { + pt_demux_enabled &= + !bundle_group || + (bundled_pt_demux_allowed_audio && + payload_types_by_bundle[bundle_group].pt_demuxing_possible_audio); + if (pt_demux_enabled) { + pt_demuxing_has_been_used_audio_ = true; + } + } else { + RTC_DCHECK_EQ(media_type, cricket::MediaType::MEDIA_TYPE_VIDEO); + pt_demux_enabled &= + !bundle_group || + (bundled_pt_demux_allowed_video && + payload_types_by_bundle[bundle_group].pt_demuxing_possible_video); + if (pt_demux_enabled) { + pt_demuxing_has_been_used_video_ = true; + } + } + + channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel()); } - return pc_->worker_thread()->Invoke( - RTC_FROM_HERE, - [&channels_to_update, &bundle_groups_by_mid, &payload_types_by_bundle, - bundled_pt_demux_allowed_audio, bundled_pt_demux_allowed_video, - pt_demuxing_has_been_used_audio = &pt_demuxing_has_been_used_audio_, - pt_demuxing_has_been_used_video = &pt_demuxing_has_been_used_video_]() { + if (channels_to_update.empty()) { + return true; + } + + // TODO(bugs.webrtc.org/11993): This Invoke() will also invoke on the network + // thread for every demuxer sink that needs to be updated. The demuxer state + // needs to be fully (and only) managed on the network thread and once that's + // the case, there's no need to stop by on the worker. Ideally we could also + // do this without blocking. + return context_->worker_thread()->Invoke( + RTC_FROM_HERE, [&channels_to_update]() { for (const auto& it : channels_to_update) { - RtpTransceiverDirection local_direction = it.first; - cricket::ChannelInterface* channel = it.second; - cricket::MediaType media_type = channel->media_type(); - auto bundle_it = bundle_groups_by_mid.find(channel->content_name()); - const cricket::ContentGroup* bundle_group = - bundle_it != bundle_groups_by_mid.end() ? bundle_it->second - : nullptr; - if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) { - bool pt_demux_enabled = - RtpTransceiverDirectionHasRecv(local_direction) && - (!bundle_group || (bundled_pt_demux_allowed_audio && - payload_types_by_bundle[bundle_group] - .pt_demuxing_possible_audio)); - if (pt_demux_enabled) { - *pt_demuxing_has_been_used_audio = true; - } - if (!channel->SetPayloadTypeDemuxingEnabled(pt_demux_enabled)) { - return false; - } - } else if (media_type == cricket::MediaType::MEDIA_TYPE_VIDEO) { - bool pt_demux_enabled = - RtpTransceiverDirectionHasRecv(local_direction) && - (!bundle_group || (bundled_pt_demux_allowed_video && - payload_types_by_bundle[bundle_group] - .pt_demuxing_possible_video)); - if (pt_demux_enabled) { - *pt_demuxing_has_been_used_video = true; - } - if (!channel->SetPayloadTypeDemuxingEnabled(pt_demux_enabled)) { - return false; - } + if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) { + // Note that the state has already been irrevocably changed at this + // point. Is it useful to stop the loop? + return false; } } return true; diff --git a/pc/sdp_offer_answer.h b/pc/sdp_offer_answer.h index c89ffd2e6d..15c092723e 100644 --- a/pc/sdp_offer_answer.h +++ b/pc/sdp_offer_answer.h @@ -47,12 +47,12 @@ #include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/channel_manager.h" +#include "pc/connection_context.h" #include "pc/data_channel_controller.h" #include "pc/ice_server_parsing.h" #include "pc/jsep_transport_controller.h" #include "pc/media_session.h" #include "pc/media_stream_observer.h" -#include "pc/peer_connection_factory.h" #include "pc/peer_connection_internal.h" #include "pc/rtc_stats_collector.h" #include "pc/rtp_receiver.h" @@ -94,9 +94,10 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // Creates an SdpOfferAnswerHandler. Modifies dependencies. static std::unique_ptr Create( - PeerConnection* pc, + PeerConnectionSdpMethods* pc, const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies); + PeerConnectionDependencies& dependencies, + ConnectionContext* context); void ResetSessionDescFactory() { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -181,7 +182,16 @@ class SdpOfferAnswerHandler : public SdpStateProvider, rtc::scoped_refptr local_streams(); rtc::scoped_refptr remote_streams(); + bool initial_offerer() { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (initial_offerer_) { + return *initial_offerer_; + } + return false; + } + private: + class RemoteDescriptionOperation; class ImplicitCreateSessionDescriptionObserver; friend class ImplicitCreateSessionDescriptionObserver; @@ -204,14 +214,17 @@ class SdpOfferAnswerHandler : public SdpStateProvider, class LocalIceCredentialsToReplace; // Only called by the Create() function. - explicit SdpOfferAnswerHandler(PeerConnection* pc); + explicit SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc, + ConnectionContext* context); // Called from the `Create()` function. Can only be called // once. Modifies dependencies. void Initialize( const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies); + PeerConnectionDependencies& dependencies, + ConnectionContext* context); rtc::Thread* signaling_thread() const; + rtc::Thread* network_thread() const; // Non-const versions of local_description()/remote_description(), for use // internally. SessionDescriptionInterface* mutable_local_description() @@ -231,10 +244,24 @@ class SdpOfferAnswerHandler : public SdpStateProvider, std::unique_ptr desc, const std::map& bundle_groups_by_mid); - RTCError ApplyRemoteDescription( + void ApplyRemoteDescription( + std::unique_ptr operation); + + RTCError ReplaceRemoteDescription( std::unique_ptr desc, - const std::map& - bundle_groups_by_mid); + SdpType sdp_type, + std::unique_ptr* replaced_description) + RTC_RUN_ON(signaling_thread()); + + // Part of ApplyRemoteDescription steps specific to Unified Plan. + void ApplyRemoteDescriptionUpdateTransceiverState(SdpType sdp_type); + + // Part of ApplyRemoteDescription steps specific to plan b. + void PlanBUpdateSendersAndReceivers( + const cricket::ContentInfo* audio_content, + const cricket::AudioContentDescription* audio_desc, + const cricket::ContentInfo* video_content, + const cricket::VideoContentDescription* video_desc); // Implementation of the offer/answer exchange operations. These are chained // onto the `operations_chain_` when the public CreateOffer(), CreateAnswer(), @@ -249,8 +276,11 @@ class SdpOfferAnswerHandler : public SdpStateProvider, std::unique_ptr desc, rtc::scoped_refptr observer); void DoSetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer); + std::unique_ptr operation); + + // Called after a DoSetRemoteDescription operation completes. + void SetRemoteDescriptionPostProcess(bool was_answer) + RTC_RUN_ON(signaling_thread()); // Update the state, signaling if necessary. void ChangeSignalingState( @@ -356,7 +386,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // to the SDP semantics. void FillInMissingRemoteMids(cricket::SessionDescription* remote_description); - // Returns an RtpTransciever, if available, that can be used to receive the + // Returns an RtpTransceiver, if available, that can be used to receive the // given media type according to JSEP rules. rtc::scoped_refptr> FindAvailableTransceiverToReceive(cricket::MediaType media_type) const; @@ -482,9 +512,11 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // exist. void UpdateEndedRemoteMediaStreams(); - // Uses all remote candidates in `remote_desc` in this session. - bool UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc); + // Uses all remote candidates in the currently set remote_description(). + // If no remote description is currently set (nullptr), the return value will + // be true. If `UseCandidate()` fails for any candidate in the remote + // description, the return value will be false. + bool UseCandidatesInRemoteDescription(); // Uses `candidate` in this session. bool UseCandidate(const IceCandidateInterface* candidate); // Returns true if we are ready to push down the remote candidate. @@ -513,22 +545,12 @@ class SdpOfferAnswerHandler : public SdpStateProvider, cricket::VideoChannel* CreateVideoChannel(const std::string& mid); bool CreateDataChannel(const std::string& mid); - // Destroys and clears the BaseChannel associated with the given transceiver, - // if such channel is set. - void DestroyTransceiverChannel( - rtc::scoped_refptr> - transceiver); - // Destroys the RTP data channel transport and/or the SCTP data channel // transport and clears it. void DestroyDataChannelTransport(RTCError error); - // Destroys the given ChannelInterface. - // The channel cannot be accessed after this method is called. - void DestroyChannelInterface(cricket::ChannelInterface* channel); // Generates MediaDescriptionOptions for the `session_opts` based on existing // local description or remote description. - void GenerateMediaDescriptionOptions( const SessionDescriptionInterface* session_desc, RtpTransceiverDirection audio_direction, @@ -555,6 +577,15 @@ class SdpOfferAnswerHandler : public SdpStateProvider, const std::map& bundle_groups_by_mid); + // Updates the error state, signaling if necessary. + void SetSessionError(SessionError error, const std::string& error_desc); + + // Implements AddIceCandidate without reporting usage, but returns the + // particular success/error value that should be reported (and can be utilized + // for other purposes). + AddIceCandidateResult AddIceCandidateInternal( + const IceCandidateInterface* candidate); + // ================================================================== // Access to pc_ variables cricket::ChannelManager* channel_manager() const; @@ -566,13 +597,20 @@ class SdpOfferAnswerHandler : public SdpStateProvider, const cricket::PortAllocator* port_allocator() const; RtpTransmissionManager* rtp_manager(); const RtpTransmissionManager* rtp_manager() const; - JsepTransportController* transport_controller(); - const JsepTransportController* transport_controller() const; + JsepTransportController* transport_controller_s() + RTC_RUN_ON(signaling_thread()); + const JsepTransportController* transport_controller_s() const + RTC_RUN_ON(signaling_thread()); + JsepTransportController* transport_controller_n() + RTC_RUN_ON(network_thread()); + const JsepTransportController* transport_controller_n() const + RTC_RUN_ON(network_thread()); // =================================================================== const cricket::AudioOptions& audio_options() { return audio_options_; } const cricket::VideoOptions& video_options() { return video_options_; } - PeerConnection* const pc_; + PeerConnectionSdpMethods* const pc_; + ConnectionContext* const context_; std::unique_ptr webrtc_session_desc_factory_ RTC_GUARDED_BY(signaling_thread()); @@ -627,14 +665,16 @@ class SdpOfferAnswerHandler : public SdpStateProvider, bool remote_peer_supports_msid_ RTC_GUARDED_BY(signaling_thread()) = false; bool is_negotiation_needed_ RTC_GUARDED_BY(signaling_thread()) = false; - uint32_t negotiation_needed_event_id_ = 0; + uint32_t negotiation_needed_event_id_ RTC_GUARDED_BY(signaling_thread()) = 0; bool update_negotiation_needed_on_empty_chain_ RTC_GUARDED_BY(signaling_thread()) = false; // If PT demuxing is successfully negotiated one time we will allow PT // demuxing for the rest of the session so that PT-based apps default to PT // demuxing in follow-up O/A exchanges. - bool pt_demuxing_has_been_used_audio_ = false; - bool pt_demuxing_has_been_used_video_ = false; + bool pt_demuxing_has_been_used_audio_ RTC_GUARDED_BY(signaling_thread()) = + false; + bool pt_demuxing_has_been_used_video_ RTC_GUARDED_BY(signaling_thread()) = + false; // In Unified Plan, if we encounter remote SDP that does not contain an a=msid // line we create and use a stream with a random ID for our receivers. This is @@ -643,15 +683,6 @@ class SdpOfferAnswerHandler : public SdpStateProvider, rtc::scoped_refptr missing_msid_default_stream_ RTC_GUARDED_BY(signaling_thread()); - // Updates the error state, signaling if necessary. - void SetSessionError(SessionError error, const std::string& error_desc); - - // Implements AddIceCandidate without reporting usage, but returns the - // particular success/error value that should be reported (and can be utilized - // for other purposes). - AddIceCandidateResult AddIceCandidateInternal( - const IceCandidateInterface* candidate); - SessionError session_error_ RTC_GUARDED_BY(signaling_thread()) = SessionError::kNone; std::string session_error_desc_ RTC_GUARDED_BY(signaling_thread()); @@ -660,21 +691,17 @@ class SdpOfferAnswerHandler : public SdpStateProvider, cricket::AudioOptions audio_options_ RTC_GUARDED_BY(signaling_thread()); cricket::VideoOptions video_options_ RTC_GUARDED_BY(signaling_thread()); - // This object should be used to generate any SSRC that is not explicitly - // specified by the user (or by the remote party). - // The generator is not used directly, instead it is passed on to the - // channel manager and the session description factory. - // TODO(bugs.webrtc.org/12666): This variable is used from both the signaling - // and worker threads. See if we can't restrict usage to a single thread. - rtc::UniqueRandomIdGenerator ssrc_generator_; - // A video bitrate allocator factory. // This can be injected using the PeerConnectionDependencies, // or else the CreateBuiltinVideoBitrateAllocatorFactory() will be called. // Note that one can still choose to override this in a MediaEngine // if one wants too. std::unique_ptr - video_bitrate_allocator_factory_; + video_bitrate_allocator_factory_ RTC_GUARDED_BY(signaling_thread()); + + // Whether we are the initial offerer on the association. This + // determines the SSL role. + absl::optional initial_offerer_ RTC_GUARDED_BY(signaling_thread()); rtc::WeakPtrFactory weak_ptr_factory_ RTC_GUARDED_BY(signaling_thread()); diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc new file mode 100644 index 0000000000..b992c34906 --- /dev/null +++ b/pc/sdp_offer_answer_unittest.cc @@ -0,0 +1,116 @@ +/* + * 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 +#include +#include + +#include "api/audio/audio_mixer.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/create_peerconnection_factory.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/video_codecs/builtin_video_decoder_factory.h" +#include "api/video_codecs/builtin_video_encoder_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "p2p/base/port_allocator.h" +#include "pc/peer_connection_wrapper.h" +#include "pc/test/fake_audio_capture_module.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" +#include "system_wrappers/include/metrics.h" +#include "test/gtest.h" + +// This file contains unit tests that relate to the behavior of the +// SdpOfferAnswer module. +// Tests are writen as integration tests with PeerConnection, since the +// behaviors are still linked so closely that it is hard to test them in +// isolation. + +namespace webrtc { + +using RTCConfiguration = PeerConnectionInterface::RTCConfiguration; + +namespace { + +std::unique_ptr CreateAndStartThread() { + auto thread = rtc::Thread::Create(); + thread->Start(); + return thread; +} + +} // namespace + +class SdpOfferAnswerTest : public ::testing::Test { + public: + SdpOfferAnswerTest() + // Note: We use a PeerConnectionFactory with a distinct + // signaling thread, so that thread handling can be tested. + : signaling_thread_(CreateAndStartThread()), + pc_factory_( + CreatePeerConnectionFactory(nullptr, + nullptr, + signaling_thread_.get(), + FakeAudioCaptureModule::Create(), + CreateBuiltinAudioEncoderFactory(), + CreateBuiltinAudioDecoderFactory(), + CreateBuiltinVideoEncoderFactory(), + CreateBuiltinVideoDecoderFactory(), + nullptr /* audio_mixer */, + nullptr /* audio_processing */)) { + webrtc::metrics::Reset(); + } + + std::unique_ptr CreatePeerConnection() { + RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + return CreatePeerConnection(config); + } + + std::unique_ptr CreatePeerConnection( + const RTCConfiguration& config) { + auto observer = std::make_unique(); + auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr, + observer.get()); + EXPECT_TRUE(pc.get()); + observer->SetPeerConnectionInterface(pc.get()); + return std::make_unique(pc_factory_, pc, + std::move(observer)); + } + + protected: + std::unique_ptr signaling_thread_; + rtc::scoped_refptr pc_factory_; + + private: +}; + +TEST_F(SdpOfferAnswerTest, OnTrackReturnsProxiedObject) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + // Verify that caller->observer->OnTrack() has been called with a + // proxied transceiver object. + ASSERT_EQ(callee->observer()->on_track_transceivers_.size(), 1u); + auto transceiver = callee->observer()->on_track_transceivers_[0]; + // Since the signaling thread is not the current thread, + // this will DCHECK if the transceiver is not proxied. + transceiver->stopped(); +} + +} // namespace webrtc diff --git a/pc/sdp_serializer.cc b/pc/sdp_serializer.cc index 3f46db5bb0..6d405d07a9 100644 --- a/pc/sdp_serializer.cc +++ b/pc/sdp_serializer.cc @@ -10,9 +10,9 @@ #include "pc/sdp_serializer.h" -#include #include #include +#include #include #include @@ -101,33 +101,33 @@ rtc::StringBuilder& operator<<(rtc::StringBuilder& builder, // sc-id = [sc-id-paused] rid-id // rid-id = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid RTCErrorOr ParseSimulcastLayerList(const std::string& str) { - std::vector tokens; - rtc::split(str, kDelimiterSemicolonChar, &tokens); + std::vector tokens = + rtc::split(str, kDelimiterSemicolonChar); if (tokens.empty()) { return ParseError("Layer list cannot be empty."); } SimulcastLayerList result; - for (const std::string& token : tokens) { + for (const absl::string_view& token : tokens) { if (token.empty()) { return ParseError("Simulcast alternative layer list is empty."); } - std::vector rid_tokens; - rtc::split(token, kDelimiterCommaChar, &rid_tokens); + std::vector rid_tokens = + rtc::split(token, kDelimiterCommaChar); if (rid_tokens.empty()) { return ParseError("Simulcast alternative layer list is malformed."); } std::vector layers; - for (const std::string& rid_token : rid_tokens) { + for (const absl::string_view& rid_token : rid_tokens) { if (rid_token.empty() || rid_token == kSimulcastPausedStream) { return ParseError("Rid must not be empty."); } bool paused = rid_token[0] == kSimulcastPausedStreamChar; - std::string rid = paused ? rid_token.substr(1) : rid_token; + absl::string_view rid = paused ? rid_token.substr(1) : rid_token; layers.push_back(SimulcastLayer(rid, paused)); } diff --git a/pc/sdp_serializer_unittest.cc b/pc/sdp_serializer_unittest.cc index 68d4c2acef..0c31750df4 100644 --- a/pc/sdp_serializer_unittest.cc +++ b/pc/sdp_serializer_unittest.cc @@ -10,12 +10,14 @@ #include "pc/sdp_serializer.h" +#include + #include #include #include #include -#include "rtc_base/gunit.h" +#include "test/gtest.h" using cricket::RidDescription; using cricket::RidDirection; diff --git a/pc/sdp_utils.cc b/pc/sdp_utils.cc index b750b04a46..ca61f0013f 100644 --- a/pc/sdp_utils.cc +++ b/pc/sdp_utils.cc @@ -10,8 +10,8 @@ #include "pc/sdp_utils.h" -#include #include +#include #include "api/jsep_session_description.h" #include "rtc_base/checks.h" diff --git a/pc/session_description.cc b/pc/session_description.cc index 7b878cbf7b..c1feedbf53 100644 --- a/pc/session_description.cc +++ b/pc/session_description.cc @@ -10,11 +10,10 @@ #include "pc/session_description.h" -#include - #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" namespace cricket { namespace { diff --git a/pc/session_description.h b/pc/session_description.h index fed083987d..a68c312f42 100644 --- a/pc/session_description.h +++ b/pc/session_description.h @@ -15,9 +15,9 @@ #include #include -#include #include #include +#include #include #include @@ -47,13 +47,6 @@ typedef std::vector VideoCodecs; typedef std::vector CryptoParamsVec; typedef std::vector RtpHeaderExtensions; -// RTC4585 RTP/AVPF -extern const char kMediaProtocolAvpf[]; -// RFC5124 RTP/SAVPF -extern const char kMediaProtocolSavpf[]; - -extern const char kMediaProtocolDtlsSavpf[]; - // Options to control how session descriptions are generated. const int kAutoBandwidth = -1; diff --git a/pc/session_description_unittest.cc b/pc/session_description_unittest.cc index 00ce538398..4d0913bad2 100644 --- a/pc/session_description_unittest.cc +++ b/pc/session_description_unittest.cc @@ -9,8 +9,6 @@ */ #include "pc/session_description.h" -#include - #include "test/gtest.h" namespace cricket { diff --git a/pc/simulcast_description.cc b/pc/simulcast_description.cc index 0ae3e2074e..ec87415677 100644 --- a/pc/simulcast_description.cc +++ b/pc/simulcast_description.cc @@ -14,7 +14,7 @@ namespace cricket { -SimulcastLayer::SimulcastLayer(const std::string& rid, bool is_paused) +SimulcastLayer::SimulcastLayer(absl::string_view rid, bool is_paused) : rid{rid}, is_paused{is_paused} { RTC_DCHECK(!rid.empty()); } diff --git a/pc/simulcast_description.h b/pc/simulcast_description.h index f7ae28837e..7caf164de5 100644 --- a/pc/simulcast_description.h +++ b/pc/simulcast_description.h @@ -16,6 +16,8 @@ #include #include +#include "absl/strings/string_view.h" + namespace cricket { // Describes a Simulcast Layer. @@ -23,7 +25,7 @@ namespace cricket { // See also: https://tools.ietf.org/html/draft-ietf-mmusic-rid-15 for // an explanation about rids. struct SimulcastLayer final { - SimulcastLayer(const std::string& rid, bool is_paused); + SimulcastLayer(absl::string_view rid, bool is_paused); SimulcastLayer(const SimulcastLayer& other) = default; SimulcastLayer& operator=(const SimulcastLayer& other) = default; diff --git a/pc/srtp_filter.cc b/pc/srtp_filter.cc index c48dfdb4cd..9d7f39a7a3 100644 --- a/pc/srtp_filter.cc +++ b/pc/srtp_filter.cc @@ -11,8 +11,8 @@ #include "pc/srtp_filter.h" #include -#include -#include + +#include #include "absl/strings/match.h" #include "rtc_base/logging.h" diff --git a/pc/srtp_filter.h b/pc/srtp_filter.h index f1e164936c..e2848a1090 100644 --- a/pc/srtp_filter.h +++ b/pc/srtp_filter.h @@ -27,7 +27,6 @@ #include "api/sequence_checker.h" #include "pc/session_description.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_stream_adapter.h" // Forward declaration to avoid pulling in libsrtp headers here diff --git a/pc/srtp_session.cc b/pc/srtp_session.cc index 76ab3a8fe8..b5b244265c 100644 --- a/pc/srtp_session.cc +++ b/pc/srtp_session.cc @@ -10,17 +10,23 @@ #include "pc/srtp_session.h" +#include + #include +#include #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "api/array_view.h" +#include "api/webrtc_key_value_config.h" #include "modules/rtp_rtcp/source/rtp_util.h" #include "pc/external_hmac.h" +#include "rtc_base/byte_order.h" +#include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" #include "rtc_base/time_utils.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" #include "third_party/libsrtp/include/srtp.h" #include "third_party/libsrtp/include/srtp_priv.h" @@ -34,8 +40,10 @@ using ::webrtc::ParseRtpSequenceNumber; // in srtp.h. constexpr int kSrtpErrorCodeBoundary = 28; -SrtpSession::SrtpSession() { - dump_plain_rtp_ = webrtc::field_trial::IsEnabled("WebRTC-Debugging-RtpDump"); +SrtpSession::SrtpSession() {} + +SrtpSession::SrtpSession(const webrtc::WebRtcKeyValueConfig& field_trials) { + dump_plain_rtp_ = field_trials.IsEnabled("WebRTC-Debugging-RtpDump"); } SrtpSession::~SrtpSession() { diff --git a/pc/srtp_session.h b/pc/srtp_session.h index 89fab0daf2..c3979aae7a 100644 --- a/pc/srtp_session.h +++ b/pc/srtp_session.h @@ -11,11 +11,14 @@ #ifndef PC_SRTP_SESSION_H_ #define PC_SRTP_SESSION_H_ +#include +#include + #include #include "api/scoped_refptr.h" #include "api/sequence_checker.h" -#include "rtc_base/constructor_magic.h" +#include "api/webrtc_key_value_config.h" #include "rtc_base/synchronization/mutex.h" // Forward declaration to avoid pulling in libsrtp headers here @@ -33,8 +36,12 @@ void ProhibitLibsrtpInitialization(); class SrtpSession { public: SrtpSession(); + explicit SrtpSession(const webrtc::WebRtcKeyValueConfig& field_trials); ~SrtpSession(); + SrtpSession(const SrtpSession&) = delete; + SrtpSession& operator=(const SrtpSession&) = delete; + // Configures the session for sending data using the specified // cipher-suite and key. Receiving must be done by a separate session. bool SetSend(int cs, @@ -141,7 +148,6 @@ class SrtpSession { bool external_auth_enabled_ = false; int decryption_failure_count_ = 0; bool dump_plain_rtp_ = false; - RTC_DISALLOW_COPY_AND_ASSIGN(SrtpSession); }; } // namespace cricket diff --git a/pc/srtp_session_unittest.cc b/pc/srtp_session_unittest.cc index dc08c2e908..16a840a307 100644 --- a/pc/srtp_session_unittest.cc +++ b/pc/srtp_session_unittest.cc @@ -21,6 +21,7 @@ #include "system_wrappers/include/metrics.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "third_party/libsrtp/include/srtp.h" using ::testing::ElementsAre; @@ -32,7 +33,9 @@ std::vector kEncryptedHeaderExtensionIds; class SrtpSessionTest : public ::testing::Test { public: - SrtpSessionTest() { webrtc::metrics::Reset(); } + SrtpSessionTest() : s1_(field_trials_), s2_(field_trials_) { + webrtc::metrics::Reset(); + } protected: virtual void SetUp() { @@ -69,6 +72,7 @@ class SrtpSessionTest : public ::testing::Test { EXPECT_EQ(expected_len, out_len); EXPECT_EQ(0, memcmp(rtcp_packet_, kRtcpReport, out_len)); } + webrtc::test::ScopedKeyValueConfig field_trials_; cricket::SrtpSession s1_; cricket::SrtpSession s2_; char rtp_packet_[sizeof(kPcmuFrame) + 10]; diff --git a/pc/srtp_transport.cc b/pc/srtp_transport.cc index 230c1a347b..b59fc1d1d2 100644 --- a/pc/srtp_transport.cc +++ b/pc/srtp_transport.cc @@ -34,8 +34,9 @@ namespace webrtc { -SrtpTransport::SrtpTransport(bool rtcp_mux_enabled) - : RtpTransport(rtcp_mux_enabled) {} +SrtpTransport::SrtpTransport(bool rtcp_mux_enabled, + const WebRtcKeyValueConfig& field_trials) + : RtpTransport(rtcp_mux_enabled), field_trials_(field_trials) {} RTCError SrtpTransport::SetSrtpSendKey(const cricket::CryptoParams& params) { if (send_params_) { @@ -324,13 +325,13 @@ bool SrtpTransport::SetRtcpParams(int send_cs, return false; } - send_rtcp_session_.reset(new cricket::SrtpSession()); + send_rtcp_session_.reset(new cricket::SrtpSession(field_trials_)); if (!send_rtcp_session_->SetSend(send_cs, send_key, send_key_len, send_extension_ids)) { return false; } - recv_rtcp_session_.reset(new cricket::SrtpSession()); + recv_rtcp_session_.reset(new cricket::SrtpSession(field_trials_)); if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len, recv_extension_ids)) { return false; @@ -361,8 +362,8 @@ void SrtpTransport::ResetParams() { } void SrtpTransport::CreateSrtpSessions() { - send_session_.reset(new cricket::SrtpSession()); - recv_session_.reset(new cricket::SrtpSession()); + send_session_.reset(new cricket::SrtpSession(field_trials_)); + recv_session_.reset(new cricket::SrtpSession(field_trials_)); if (external_auth_enabled_) { send_session_->EnableExternalAuth(); } diff --git a/pc/srtp_transport.h b/pc/srtp_transport.h index 4bc028d68e..2fec29eb01 100644 --- a/pc/srtp_transport.h +++ b/pc/srtp_transport.h @@ -21,6 +21,7 @@ #include "absl/types/optional.h" #include "api/crypto_params.h" #include "api/rtc_error.h" +#include "api/webrtc_key_value_config.h" #include "p2p/base/packet_transport_internal.h" #include "pc/rtp_transport.h" #include "pc/srtp_session.h" @@ -36,7 +37,8 @@ namespace webrtc { // parameters for the SrtpSession underneath. class SrtpTransport : public RtpTransport { public: - explicit SrtpTransport(bool rtcp_mux_enabled); + SrtpTransport(bool rtcp_mux_enabled, + const WebRtcKeyValueConfig& field_trials); virtual ~SrtpTransport() = default; @@ -167,6 +169,8 @@ class SrtpTransport : public RtpTransport { int rtp_abs_sendtime_extn_id_ = -1; int decryption_failure_count_ = 0; + + const WebRtcKeyValueConfig& field_trials_; }; } // namespace webrtc diff --git a/pc/srtp_transport_unittest.cc b/pc/srtp_transport_unittest.cc index 46e7397e36..4e643935a9 100644 --- a/pc/srtp_transport_unittest.cc +++ b/pc/srtp_transport_unittest.cc @@ -12,8 +12,6 @@ #include -#include -#include #include #include "call/rtp_demuxer.h" @@ -25,9 +23,11 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::kSrtpAeadAes128Gcm; using rtc::kTestKey1; @@ -58,8 +58,10 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { rtp_packet_transport1_->SetDestination(rtp_packet_transport2_.get(), asymmetric); - srtp_transport1_ = std::make_unique(rtcp_mux_enabled); - srtp_transport2_ = std::make_unique(rtcp_mux_enabled); + srtp_transport1_ = + std::make_unique(rtcp_mux_enabled, field_trials_); + srtp_transport2_ = + std::make_unique(rtcp_mux_enabled, field_trials_); srtp_transport1_->SetRtpPacketTransport(rtp_packet_transport1_.get()); srtp_transport2_->SetRtpPacketTransport(rtp_packet_transport2_.get()); @@ -71,7 +73,7 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { RtpDemuxerCriteria demuxer_criteria; // 0x00 is the payload type used in kPcmuFrame. - demuxer_criteria.payload_types = {0x00}; + demuxer_criteria.payload_types().insert(0x00); srtp_transport1_->RegisterRtpDemuxerSink(demuxer_criteria, &rtp_sink1_); srtp_transport2_->RegisterRtpDemuxerSink(demuxer_criteria, &rtp_sink2_); @@ -101,7 +103,7 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { EXPECT_EQ(80 / 8, overhead); // 80-bit tag. break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } @@ -334,6 +336,7 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { TransportObserver rtp_sink2_; int sequence_number_ = 0; + webrtc::test::ScopedKeyValueConfig field_trials_; }; class SrtpTransportTestWithExternalAuth diff --git a/pc/stats_collector.cc b/pc/stats_collector.cc index cad9cf664c..927e993203 100644 --- a/pc/stats_collector.cc +++ b/pc/stats_collector.cc @@ -13,22 +13,25 @@ #include #include -#include +#include +#include +#include #include #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/audio_codecs/audio_encoder.h" #include "api/candidate.h" #include "api/data_channel_interface.h" #include "api/media_types.h" -#include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/video/video_content_type.h" #include "api/video/video_timing.h" +#include "api/webrtc_key_value_config.h" #include "call/call.h" #include "media/base/media_channel.h" #include "modules/audio_processing/include/audio_processing_statistics.h" @@ -38,6 +41,8 @@ #include "pc/channel_interface.h" #include "pc/data_channel_utils.h" #include "pc/rtp_receiver.h" +#include "pc/rtp_receiver_proxy.h" +#include "pc/rtp_sender_proxy.h" #include "pc/rtp_transceiver.h" #include "pc/transport_stats.h" #include "rtc_base/checks.h" @@ -51,7 +56,6 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -385,7 +389,7 @@ void ExtractStats(const cricket::VideoSenderInfo& info, info.encode_usage_percent}, {StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd}, {StatsReport::kStatsValueNameFrameHeightSent, info.send_frame_height}, - {StatsReport::kStatsValueNameFrameRateInput, info.framerate_input}, + {StatsReport::kStatsValueNameFrameRateInput, round(info.framerate_input)}, {StatsReport::kStatsValueNameFrameRateSent, info.framerate_sent}, {StatsReport::kStatsValueNameFrameWidthSent, info.send_frame_width}, {StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd}, @@ -505,7 +509,7 @@ const char* IceCandidateTypeToStatsType(const std::string& candidate_type) { if (candidate_type == cricket::RELAY_PORT_TYPE) { return STATSREPORT_RELAY_PORT_TYPE; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return "unknown"; } @@ -530,7 +534,7 @@ const char* AdapterTypeToStatsType(rtc::AdapterType type) { case rtc::ADAPTER_TYPE_ANY: return STATSREPORT_ADAPTER_TYPE_WILDCARD; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } } @@ -539,7 +543,7 @@ StatsCollector::StatsCollector(PeerConnectionInternal* pc) : pc_(pc), stats_gathering_started_(0), use_standard_bytes_stats_( - webrtc::field_trial::IsEnabled(kUseStandardBytesStats)) { + pc->trials().IsEnabled(kUseStandardBytesStats)) { RTC_DCHECK(pc_); } @@ -572,7 +576,7 @@ void StatsCollector::AddTrack(MediaStreamTrackInterface* track) { CreateTrackReport(static_cast(track), &reports_, &track_ids_); } else { - RTC_NOTREACHED() << "Illegal track kind"; + RTC_DCHECK_NOTREACHED() << "Illegal track kind"; } } @@ -807,6 +811,8 @@ StatsReport* StatsCollector::AddConnectionInfoReport( info.remote_candidate.type()); report->AddString(StatsReport::kStatsValueNameTransportType, info.local_candidate.protocol()); + report->AddString(StatsReport::kStatsValueNameLocalCandidateRelayProtocol, + info.local_candidate.relay_protocol()); return report; } @@ -883,8 +889,8 @@ StatsCollector::SessionStats StatsCollector::ExtractSessionInfo_n( for (auto& transceiver : transceivers) { cricket::ChannelInterface* channel = transceiver->internal()->channel(); if (channel) { - stats.transport_names_by_mid[channel->content_name()] = - channel->transport_name(); + stats.transport_names_by_mid[channel->mid()] = + std::string(channel->transport_name()); } } @@ -962,9 +968,9 @@ void StatsCollector::ExtractSessionInfo_s(SessionStats& session_stats) { } for (const auto& channel_iter : transport.stats.channel_stats) { - StatsReport::Id id( + StatsReport::Id channel_stats_id( StatsReport::NewComponentId(transport.name, channel_iter.component)); - StatsReport* channel_report = reports_.ReplaceOrAddNew(id); + StatsReport* channel_report = reports_.ReplaceOrAddNew(channel_stats_id); channel_report->set_timestamp(stats_gathering_started_); channel_report->AddInt(StatsReport::kStatsValueNameComponent, channel_iter.component); @@ -1179,7 +1185,7 @@ void StatsCollector::ExtractMediaInfo( } std::unique_ptr gatherer = CreateMediaChannelStatsGatherer(channel->media_channel()); - gatherer->mid = channel->content_name(); + gatherer->mid = channel->mid(); gatherer->transport_name = transport_names_by_mid.at(gatherer->mid); for (const auto& sender : transceiver->internal()->senders()) { @@ -1206,7 +1212,7 @@ void StatsCollector::ExtractMediaInfo( if (!channel) continue; MediaChannelStatsGatherer* gatherer = gatherers[i++].get(); - RTC_DCHECK_EQ(gatherer->mid, channel->content_name()); + RTC_DCHECK_EQ(gatherer->mid, channel->mid()); for (const auto& receiver : transceiver->internal()->receivers()) { gatherer->receiver_track_id_by_ssrc.insert(std::make_pair( @@ -1364,7 +1370,8 @@ void StatsCollector::UpdateTrackReports() { } } -void StatsCollector::ClearUpdateStatsCacheForTest() { +void StatsCollector::InvalidateCache() { + RTC_DCHECK_RUN_ON(pc_->signaling_thread()); cache_timestamp_ms_ = 0; } diff --git a/pc/stats_collector.h b/pc/stats_collector.h index 2fd5d9d8f8..794437e9eb 100644 --- a/pc/stats_collector.h +++ b/pc/stats_collector.h @@ -21,18 +21,25 @@ #include #include #include +#include #include #include +#include "absl/types/optional.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" #include "api/stats_types.h" +#include "api/webrtc_key_value_config.h" #include "p2p/base/connection_info.h" #include "p2p/base/port.h" #include "pc/peer_connection_internal.h" +#include "pc/rtp_transceiver.h" #include "pc/stats_collector_interface.h" +#include "pc/transport_stats.h" #include "rtc_base/network_constants.h" #include "rtc_base/ssl_certificate.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -96,10 +103,11 @@ class StatsCollector : public StatsCollectorInterface { // A track is invalid if there is no report data for it. bool IsValidTrack(const std::string& track_id); - // Method used by the unittest to force a update of stats since UpdateStats() - // that occur less than kMinGatherStatsPeriod number of ms apart will be - // ignored. - void ClearUpdateStatsCacheForTest(); + // Reset the internal cache timestamp to force an update of the stats next + // time UpdateStats() is called. This call needs to be made on the signaling + // thread and should be made every time configuration changes that affect + // stats have been made. + void InvalidateCache(); bool UseStandardBytesStats() const { return use_standard_bytes_stats_; } @@ -192,7 +200,7 @@ class StatsCollector : public StatsCollectorInterface { TrackIdMap track_ids_; // Raw pointer to the peer connection the statistics are gathered from. PeerConnectionInternal* const pc_; - int64_t cache_timestamp_ms_ = 0; + int64_t cache_timestamp_ms_ RTC_GUARDED_BY(pc_->signaling_thread()) = 0; double stats_gathering_started_; const bool use_standard_bytes_stats_; diff --git a/pc/stats_collector_unittest.cc b/pc/stats_collector_unittest.cc index 07df5a8438..144ca34b55 100644 --- a/pc/stats_collector_unittest.cc +++ b/pc/stats_collector_unittest.cc @@ -12,7 +12,7 @@ #include -#include +#include #include "absl/algorithm/container.h" #include "absl/types/optional.h" @@ -20,11 +20,16 @@ #include "api/candidate.h" #include "api/data_channel_interface.h" #include "api/media_stream_track.h" +#include "api/media_types.h" +#include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "call/call.h" #include "media/base/media_channel.h" #include "modules/audio_processing/include/audio_processing_statistics.h" +#include "p2p/base/ice_transport_internal.h" #include "pc/media_stream.h" +#include "pc/rtp_receiver.h" +#include "pc/rtp_sender.h" #include "pc/sctp_data_channel.h" #include "pc/test/fake_peer_connection_for_stats.h" #include "pc/test/fake_video_track_source.h" @@ -43,6 +48,7 @@ #include "rtc_base/string_encode.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/thread.h" +#include "test/gmock.h" #include "test/gtest.h" using cricket::ConnectionInfo; @@ -54,6 +60,8 @@ using cricket::VideoSenderInfo; using cricket::VoiceMediaInfo; using cricket::VoiceReceiverInfo; using cricket::VoiceSenderInfo; +using ::testing::_; +using ::testing::AtMost; using ::testing::Return; using ::testing::UnorderedElementsAre; @@ -612,7 +620,7 @@ class StatsCollectorTest : public ::testing::Test { const VoiceMediaInfo& voice_info, StatsReports* reports) { stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); - stats->ClearUpdateStatsCacheForTest(); + stats->InvalidateCache(); stats->GetStats(nullptr, reports); // Verify the existence of the track report. @@ -745,6 +753,9 @@ static rtc::scoped_refptr CreateMockSender( Return(track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO)); + EXPECT_CALL(*sender, SetMediaChannel(_)).Times(AtMost(2)); + EXPECT_CALL(*sender, SetTransceiverAsStopped()).Times(AtMost(1)); + EXPECT_CALL(*sender, Stop()); return sender; } @@ -759,6 +770,8 @@ static rtc::scoped_refptr CreateMockReceiver( Return(track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO)); + EXPECT_CALL(*receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver, Stop()).WillRepeatedly(Return()); return receiver; } @@ -911,8 +924,7 @@ TEST_P(StatsCollectorTrackTest, BytesCounterHandles64Bits) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddOutgoingVideoTrack(pc, stats.get()); @@ -994,8 +1006,7 @@ TEST_P(StatsCollectorTrackTest, VideoBandwidthEstimationInfoIsReported) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddOutgoingVideoTrack(pc, stats.get()); @@ -1092,8 +1103,7 @@ TEST_P(StatsCollectorTrackTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddOutgoingVideoTrack(pc, stats.get()); @@ -1147,8 +1157,7 @@ TEST_P(StatsCollectorTrackTest, TransportObjectLinkedFromSsrcObject) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddOutgoingVideoTrack(pc, stats.get()); @@ -1210,8 +1219,7 @@ TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsPresent) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddOutgoingVideoTrack(pc, stats.get()); @@ -1239,8 +1247,7 @@ TEST_P(StatsCollectorTrackTest, ReportsFromRemoteTrack) { VideoMediaInfo video_info; video_info.receivers.push_back(video_receiver_info); - auto* video_media_info = pc->AddVideoChannel("video", "transport"); - video_media_info->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); AddIncomingVideoTrack(pc, stats.get()); @@ -1756,7 +1763,7 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { stream_->AddTrack(new_audio_track); stats->AddLocalAudioTrack(new_audio_track, kSsrcOfTrack); - stats->ClearUpdateStatsCacheForTest(); + stats->InvalidateCache(); VoiceSenderInfo new_voice_sender_info; InitVoiceSenderInfo(&new_voice_sender_info); @@ -1808,7 +1815,6 @@ TEST_P(StatsCollectorTrackTest, TwoLocalSendersWithSameTrack) { StatsReports reports; stats->GetStats(local_track.get(), &reports); - RTC_LOG(LS_INFO) << reports.size(); // Both SSRC reports have the same track ID. EXPECT_EQ(kLocalTrackId, GetValueInNthReportByType( @@ -1850,8 +1856,7 @@ TEST_P(StatsCollectorTrackTest, VerifyVideoSendSsrcStats) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1878,8 +1883,7 @@ TEST_P(StatsCollectorTrackTest, VerifyVideoReceiveSsrcStatsNew) { VideoMediaInfo video_info; video_info.receivers.push_back(video_receiver_info); - auto* video_media_channel = pc->AddVideoChannel("video", "transport"); - video_media_channel->SetStats(video_info); + pc->AddVideoChannel("video", "transport", video_info); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; diff --git a/pc/stream_collection.h b/pc/stream_collection.h index 9bbf957efd..ed85947bf5 100644 --- a/pc/stream_collection.h +++ b/pc/stream_collection.h @@ -12,6 +12,7 @@ #define PC_STREAM_COLLECTION_H_ #include +#include #include #include "api/peer_connection_interface.h" @@ -66,13 +67,13 @@ class StreamCollection : public StreamCollectionInterface { return NULL; } - void AddStream(MediaStreamInterface* stream) { + void AddStream(rtc::scoped_refptr stream) { for (StreamVector::iterator it = media_streams_.begin(); it != media_streams_.end(); ++it) { if ((*it)->id().compare(stream->id()) == 0) return; } - media_streams_.push_back(stream); + media_streams_.push_back(std::move(stream)); } void RemoveStream(MediaStreamInterface* remove_stream) { diff --git a/pc/test/fake_audio_capture_module.cc b/pc/test/fake_audio_capture_module.cc index 214ed6b523..41611d12a3 100644 --- a/pc/test/fake_audio_capture_module.cc +++ b/pc/test/fake_audio_capture_module.cc @@ -72,7 +72,7 @@ int FakeAudioCaptureModule::frames_received() const { int32_t FakeAudioCaptureModule::ActiveAudioLayer( AudioLayer* /*audio_layer*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -94,17 +94,17 @@ int32_t FakeAudioCaptureModule::Terminate() { } bool FakeAudioCaptureModule::Initialized() const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int16_t FakeAudioCaptureModule::PlayoutDevices() { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int16_t FakeAudioCaptureModule::RecordingDevices() { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -112,7 +112,7 @@ int32_t FakeAudioCaptureModule::PlayoutDeviceName( uint16_t /*index*/, char /*name*/[webrtc::kAdmMaxDeviceNameSize], char /*guid*/[webrtc::kAdmMaxGuidSize]) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -120,7 +120,7 @@ int32_t FakeAudioCaptureModule::RecordingDeviceName( uint16_t /*index*/, char /*name*/[webrtc::kAdmMaxDeviceNameSize], char /*guid*/[webrtc::kAdmMaxGuidSize]) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -150,7 +150,7 @@ int32_t FakeAudioCaptureModule::SetRecordingDevice( } int32_t FakeAudioCaptureModule::PlayoutIsAvailable(bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -164,7 +164,7 @@ bool FakeAudioCaptureModule::PlayoutIsInitialized() const { } int32_t FakeAudioCaptureModule::RecordingIsAvailable(bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -241,7 +241,7 @@ int32_t FakeAudioCaptureModule::InitSpeaker() { } bool FakeAudioCaptureModule::SpeakerIsInitialized() const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -251,40 +251,40 @@ int32_t FakeAudioCaptureModule::InitMicrophone() { } bool FakeAudioCaptureModule::MicrophoneIsInitialized() const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SpeakerVolumeIsAvailable(bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SetSpeakerVolume(uint32_t /*volume*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SpeakerVolume(uint32_t* /*volume*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::MaxSpeakerVolume( uint32_t* /*max_volume*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::MinSpeakerVolume( uint32_t* /*min_volume*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::MicrophoneVolumeIsAvailable( bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -308,37 +308,37 @@ int32_t FakeAudioCaptureModule::MaxMicrophoneVolume( int32_t FakeAudioCaptureModule::MinMicrophoneVolume( uint32_t* /*min_volume*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SpeakerMuteIsAvailable(bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SetSpeakerMute(bool /*enable*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SpeakerMute(bool* /*enabled*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::MicrophoneMuteIsAvailable(bool* /*available*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::SetMicrophoneMute(bool /*enable*/) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } int32_t FakeAudioCaptureModule::MicrophoneMute(bool* /*enabled*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -357,7 +357,7 @@ int32_t FakeAudioCaptureModule::SetStereoPlayout(bool /*enable*/) { } int32_t FakeAudioCaptureModule::StereoPlayout(bool* /*enabled*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -376,7 +376,7 @@ int32_t FakeAudioCaptureModule::SetStereoRecording(bool enable) { } int32_t FakeAudioCaptureModule::StereoRecording(bool* /*enabled*/) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } @@ -397,7 +397,7 @@ void FakeAudioCaptureModule::OnMessage(rtc::Message* msg) { default: // All existing messages should be caught. Getting here should never // happen. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -506,7 +506,7 @@ void FakeAudioCaptureModule::ReceiveFrameP() { kNumberOfChannels, kSamplesPerSecond, rec_buffer_, nSamplesOut, &elapsed_time_ms, &ntp_time_ms) != 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } RTC_CHECK(nSamplesOut == kNumberSamples); @@ -532,7 +532,7 @@ void FakeAudioCaptureModule::SendFrameP() { send_buffer_, kNumberSamples, kNumberBytesPerSample, kNumberOfChannels, kSamplesPerSecond, kTotalDelayMs, kClockDriftMs, current_mic_level, key_pressed, current_mic_level) != 0) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } current_mic_level_ = current_mic_level; } diff --git a/pc/test/fake_peer_connection_base.h b/pc/test/fake_peer_connection_base.h index 610afc3d90..7ab2a39e64 100644 --- a/pc/test/fake_peer_connection_base.h +++ b/pc/test/fake_peer_connection_base.h @@ -18,7 +18,9 @@ #include #include "api/sctp_transport_interface.h" +#include "api/webrtc_key_value_config.h" #include "pc/peer_connection_internal.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -50,9 +52,7 @@ class FakePeerConnectionBase : public PeerConnectionInternal { return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); } - bool RemoveTrack(RtpSenderInterface* sender) override { return false; } - - RTCError RemoveTrackNew( + RTCError RemoveTrackOrError( rtc::scoped_refptr sender) override { return RTCError(RTCErrorType::UNSUPPORTED_OPERATION); } @@ -314,8 +314,79 @@ class FakePeerConnectionBase : public PeerConnectionInternal { rtc::SSLRole* role) override { return false; } + const PeerConnectionInterface::RTCConfiguration* configuration() + const override { + return nullptr; + } + + void ReportSdpFormatReceived( + const SessionDescriptionInterface& remote_description) override {} + + void ReportSdpBundleUsage( + const SessionDescriptionInterface& remote_description) override {} + + PeerConnectionMessageHandler* message_handler() override { return nullptr; } + RtpTransmissionManager* rtp_manager() override { return nullptr; } + const RtpTransmissionManager* rtp_manager() const override { return nullptr; } + bool dtls_enabled() const override { return false; } + const PeerConnectionFactoryInterface::Options* options() const override { + return nullptr; + } + + CryptoOptions GetCryptoOptions() override { return CryptoOptions(); } + JsepTransportController* transport_controller_s() override { return nullptr; } + JsepTransportController* transport_controller_n() override { return nullptr; } + DataChannelController* data_channel_controller() override { return nullptr; } + cricket::PortAllocator* port_allocator() override { return nullptr; } + StatsCollector* stats() override { return nullptr; } + PeerConnectionObserver* Observer() const override { return nullptr; } + bool GetSctpSslRole(rtc::SSLRole* role) override { return false; } + PeerConnectionInterface::IceConnectionState ice_connection_state_internal() + override { + return PeerConnectionInterface::IceConnectionState::kIceConnectionNew; + } + void SetIceConnectionState( + PeerConnectionInterface::IceConnectionState new_state) override {} + void NoteUsageEvent(UsageEvent event) override {} + bool IsClosed() const override { return false; } + bool IsUnifiedPlan() const override { return true; } + bool ValidateBundleSettings( + const cricket::SessionDescription* desc, + const std::map& + bundle_groups_by_mid) override { + return false; + } + + absl::optional GetDataMid() const override { + return absl::nullopt; + } + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool fire_callback = true) override { + return RTCError(RTCErrorType::INTERNAL_ERROR, ""); + } + void StartSctpTransport(int local_port, + int remote_port, + int max_message_size) override {} + + void AddRemoteCandidate(const std::string& mid, + const cricket::Candidate& candidate) override {} + + Call* call_ptr() override { return nullptr; } + bool SrtpRequired() const override { return false; } + bool SetupDataChannelTransport_n(const std::string& mid) override { + return false; + } + void TeardownDataChannelTransport_n() override {} + void SetSctpDataMid(const std::string& mid) override {} + void ResetSctpDataMid() override {} + + const WebRtcKeyValueConfig& trials() override { return field_trials_; } protected: + webrtc::test::ScopedKeyValueConfig field_trials_; sigslot::signal1 SignalSctpDataChannelCreated_; }; diff --git a/pc/test/fake_peer_connection_for_stats.h b/pc/test/fake_peer_connection_for_stats.h index 4cdbd82162..7f8559d4de 100644 --- a/pc/test/fake_peer_connection_for_stats.h +++ b/pc/test/fake_peer_connection_for_stats.h @@ -101,7 +101,7 @@ class VoiceChannelForTesting : public cricket::VoiceChannel { test_transport_name_(std::move(transport_name)) {} private: - const std::string& transport_name() const override { + absl::string_view transport_name() const override { return test_transport_name_; } @@ -130,7 +130,7 @@ class VideoChannelForTesting : public cricket::VideoChannel { test_transport_name_(std::move(transport_name)) {} private: - const std::string& transport_name() const override { + absl::string_view transport_name() const override { return test_transport_name_; } @@ -152,6 +152,12 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { local_streams_(StreamCollection::Create()), remote_streams_(StreamCollection::Create()) {} + ~FakePeerConnectionForStats() { + for (auto transceiver : transceivers_) { + transceiver->internal()->SetChannel(nullptr, nullptr); + } + } + rtc::scoped_refptr mutable_local_streams() { return local_streams_; } @@ -197,7 +203,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { FakeVoiceMediaChannelForStats* AddVoiceChannel( const std::string& mid, - const std::string& transport_name) { + const std::string& transport_name, + cricket::VoiceMediaInfo initial_stats = cricket::VoiceMediaInfo()) { RTC_DCHECK(!voice_channel_); auto voice_media_channel = std::make_unique(network_thread_); @@ -205,16 +212,20 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { voice_channel_ = std::make_unique( worker_thread_, network_thread_, signaling_thread_, std::move(voice_media_channel), mid, kDefaultSrtpRequired, - webrtc::CryptoOptions(), &ssrc_generator_, transport_name); + webrtc::CryptoOptions(), &channel_manager_.ssrc_generator(), + transport_name); GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO) ->internal() - ->SetChannel(voice_channel_.get()); + ->SetChannel(voice_channel_.get(), + [](const std::string&) { return nullptr; }); + voice_media_channel_ptr->SetStats(initial_stats); return voice_media_channel_ptr; } FakeVideoMediaChannelForStats* AddVideoChannel( const std::string& mid, - const std::string& transport_name) { + const std::string& transport_name, + cricket::VideoMediaInfo initial_stats = cricket::VideoMediaInfo()) { RTC_DCHECK(!video_channel_); auto video_media_channel = std::make_unique(network_thread_); @@ -222,10 +233,13 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { video_channel_ = std::make_unique( worker_thread_, network_thread_, signaling_thread_, std::move(video_media_channel), mid, kDefaultSrtpRequired, - webrtc::CryptoOptions(), &ssrc_generator_, transport_name); + webrtc::CryptoOptions(), &channel_manager_.ssrc_generator(), + transport_name); GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO) ->internal() - ->SetChannel(video_channel_.get()); + ->SetChannel(video_channel_.get(), + [](const std::string&) { return nullptr; }); + video_media_channel_ptr->SetStats(initial_stats); return video_media_channel_ptr; } @@ -393,20 +407,26 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { } auto transceiver = RtpTransceiverProxyWithInternal::Create( signaling_thread_, - new RtpTransceiver(media_type, channel_manager_.get())); + rtc::make_ref_counted(media_type, &channel_manager_)); transceivers_.push_back(transceiver); return transceiver; } + class TestChannelManager : public cricket::ChannelManager { + public: + TestChannelManager(rtc::Thread* worker, rtc::Thread* network) + : cricket::ChannelManager(nullptr, true, worker, network) {} + + // Override DestroyChannel so that calls from the transceiver won't go to + // the default ChannelManager implementation. + void DestroyChannel(cricket::ChannelInterface*) override {} + }; + rtc::Thread* const network_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const signaling_thread_; - std::unique_ptr channel_manager_ = - cricket::ChannelManager::Create(nullptr /* MediaEngineInterface */, - true, - worker_thread_, - network_thread_); + TestChannelManager channel_manager_{worker_thread_, network_thread_}; rtc::scoped_refptr local_streams_; rtc::scoped_refptr remote_streams_; @@ -430,8 +450,6 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { local_certificates_by_transport_; std::map> remote_cert_chains_by_transport_; - - rtc::UniqueRandomIdGenerator ssrc_generator_; }; } // namespace webrtc diff --git a/pc/test/fake_rtc_certificate_generator.h b/pc/test/fake_rtc_certificate_generator.h index fc931ad661..dadd5bb1fa 100644 --- a/pc/test/fake_rtc_certificate_generator.h +++ b/pc/test/fake_rtc_certificate_generator.h @@ -171,7 +171,7 @@ class FakeRTCCertificateGenerator case rtc::KT_ECDSA: return rtc::RTCCertificate::FromPEM(kEcdsaPems[0]); default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } } @@ -190,7 +190,7 @@ class FakeRTCCertificateGenerator case rtc::KT_ECDSA: return kEcdsaPems[key_index_]; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kEcdsaPems[key_index_]; } } diff --git a/pc/test/integration_test_helpers.cc b/pc/test/integration_test_helpers.cc index 10e4f455ba..3f07f361fc 100644 --- a/pc/test/integration_test_helpers.cc +++ b/pc/test/integration_test_helpers.cc @@ -56,4 +56,46 @@ int FindFirstMediaStatsIndexByKind( return -1; } +TaskQueueMetronome::TaskQueueMetronome(TaskQueueFactory* factory, + TimeDelta tick_period) + : tick_period_(tick_period), + queue_(factory->CreateTaskQueue("MetronomeQueue", + TaskQueueFactory::Priority::HIGH)) { + tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] { + MutexLock lock(&mutex_); + for (auto* listener : listeners_) { + listener->OnTickTaskQueue()->PostTask( + ToQueuedTask([listener] { listener->OnTick(); })); + } + return tick_period_; + }); +} + +TaskQueueMetronome::~TaskQueueMetronome() { + RTC_DCHECK(listeners_.empty()); + rtc::Event stop_event; + queue_.PostTask([this, &stop_event] { + tick_task_.Stop(); + stop_event.Set(); + }); + stop_event.Wait(1000); +} + +void TaskQueueMetronome::AddListener(TickListener* listener) { + MutexLock lock(&mutex_); + auto [it, inserted] = listeners_.insert(listener); + RTC_DCHECK(inserted); +} + +void TaskQueueMetronome::RemoveListener(TickListener* listener) { + MutexLock lock(&mutex_); + auto it = listeners_.find(listener); + RTC_DCHECK(it != listeners_.end()); + listeners_.erase(it); +} + +TimeDelta TaskQueueMetronome::TickPeriod() const { + return tick_period_; +} + } // namespace webrtc diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h index a978eda4ef..00417d96a5 100644 --- a/pc/test/integration_test_helpers.h +++ b/pc/test/integration_test_helpers.h @@ -95,6 +95,7 @@ #include "pc/test/mock_peer_connection_observers.h" #include "pc/video_track_source.h" #include "rtc_base/checks.h" +#include "rtc_base/event.h" #include "rtc_base/fake_clock.h" #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" @@ -111,14 +112,16 @@ #include "rtc_base/socket_address.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" +#include "rtc_base/task_utils/repeating_task.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/test_certificate_verifier.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -172,6 +175,24 @@ int FindFirstMediaStatsIndexByKind( const std::vector& media_stats_vec); +class TaskQueueMetronome : public webrtc::Metronome { + public: + TaskQueueMetronome(TaskQueueFactory* factory, TimeDelta tick_period); + ~TaskQueueMetronome() override; + + // webrtc::Metronome implementation. + void AddListener(TickListener* listener) override; + void RemoveListener(TickListener* listener) override; + TimeDelta TickPeriod() const override; + + private: + Mutex mutex_; + const TimeDelta tick_period_; + std::set listeners_ RTC_GUARDED_BY(mutex_); + RepeatingTaskHandle tick_task_; + rtc::TaskQueue queue_; +}; + class SignalingMessageReceiver { public: virtual void ReceiveSdpMessage(SdpType type, const std::string& msg) = 0; @@ -389,14 +410,29 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, void CreateDataChannel(const std::string& label, const webrtc::DataChannelInit* init) { - data_channel_ = pc()->CreateDataChannel(label, init); - ASSERT_TRUE(data_channel_.get() != nullptr); - data_observer_.reset(new MockDataChannelObserver(data_channel_)); + data_channels_.push_back(pc()->CreateDataChannel(label, init)); + ASSERT_TRUE(data_channels_.back().get() != nullptr); + data_observers_.push_back( + std::make_unique(data_channels_.back())); + } + + // Return the last observed data channel. + DataChannelInterface* data_channel() { + if (data_channels_.size() == 0) { + return nullptr; + } + return data_channels_.back(); + } + // Return all data channels. + const std::vector>& data_channels() { + return data_channels_; } - DataChannelInterface* data_channel() { return data_channel_; } const MockDataChannelObserver* data_observer() const { - return data_observer_.get(); + if (data_observers_.size() == 0) { + return nullptr; + } + return data_observers_.back().get(); } int audio_frames_received() const { @@ -659,15 +695,15 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, // Concealing more than 20% of samples during a renegotiation is // unacceptable. // Worst bots: - // linux_more_configs bot at conceal rate 0.516 - // linux_x86_dbg bot at conceal rate 0.854 + // Nondebug: Linux32 Release at conceal rate 0.606597 (CI run) + // Debug: linux_x86_dbg bot at conceal rate 0.854 if (delta_samples > 0) { #if !defined(NDEBUG) - EXPECT_GT(0.95, 1.0 * delta_concealed / delta_samples) + EXPECT_LT(1.0 * delta_concealed / delta_samples, 0.95) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #else - EXPECT_GT(0.6, 1.0 * delta_concealed / delta_samples) + EXPECT_LT(1.0 * delta_concealed / delta_samples, 0.7) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #endif @@ -720,6 +756,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, pc_factory_dependencies.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); pc_factory_dependencies.trials = std::make_unique(); + pc_factory_dependencies.metronome = std::make_unique( + pc_factory_dependencies.task_queue_factory.get(), TimeDelta::Millis(8)); cricket::MediaEngineDependencies media_deps; media_deps.task_queue_factory = pc_factory_dependencies.task_queue_factory.get(); @@ -774,6 +812,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, const PeerConnectionInterface::RTCConfiguration* config, webrtc::PeerConnectionDependencies dependencies) { PeerConnectionInterface::RTCConfiguration modified_config; + modified_config.sdp_semantics = sdp_semantics_; // If `config` is null, this will result in a default configuration being // used. if (config) { @@ -1093,8 +1132,9 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, void OnDataChannel( rtc::scoped_refptr data_channel) override { RTC_LOG(LS_INFO) << debug_name_ << ": OnDataChannel"; - data_channel_ = data_channel; - data_observer_.reset(new MockDataChannelObserver(data_channel)); + data_channels_.push_back(data_channel); + data_observers_.push_back( + std::make_unique(data_channel)); } std::string debug_name_; @@ -1137,8 +1177,9 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, std::function generated_sdp_munger_; std::function remote_offer_handler_; rtc::MockAsyncResolver* remote_async_resolver_ = nullptr; - rtc::scoped_refptr data_channel_; - std::unique_ptr data_observer_; + // All data channels either created or observed on this peerconnection + std::vector> data_channels_; + std::vector> data_observers_; std::vector> rtp_receiver_observers_; @@ -1325,9 +1366,9 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { fss_(new rtc::FirewallSocketServer(ss_.get())), network_thread_(new rtc::Thread(fss_.get())), worker_thread_(rtc::Thread::Create()), - field_trials_(field_trials.has_value() - ? new test::ScopedFieldTrials(*field_trials) - : nullptr) { + // TODO(bugs.webrtc.org/10335): Pass optional ScopedKeyValueConfig. + field_trials_(new test::ScopedKeyValueConfig( + field_trials.has_value() ? *field_trials : "")) { network_thread_->SetName("PCNetworkThread", this); worker_thread_->SetName("PCWorkerThread", this); RTC_CHECK(network_thread_->Start()); @@ -1548,12 +1589,14 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { cricket::ProtocolType type = cricket::ProtocolType::PROTO_UDP, const std::string& common_name = "test turn server") { rtc::Thread* thread = network_thread(); + rtc::SocketFactory* socket_factory = fss_.get(); std::unique_ptr turn_server = network_thread()->Invoke>( - RTC_FROM_HERE, - [thread, internal_address, external_address, type, common_name] { + RTC_FROM_HERE, [thread, socket_factory, internal_address, + external_address, type, common_name] { return std::make_unique( - thread, internal_address, external_address, type, + thread, socket_factory, internal_address, external_address, + type, /*ignore_bad_certs=*/true, common_name); }); turn_servers_.push_back(std::move(turn_server)); @@ -1812,6 +1855,8 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { expected_cipher_suite); } + const WebRtcKeyValueConfig& trials() const { return *field_trials_.get(); } + protected: SdpSemantics sdp_semantics_; @@ -1831,7 +1876,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { std::vector> turn_customizers_; std::unique_ptr caller_; std::unique_ptr callee_; - std::unique_ptr field_trials_; + std::unique_ptr field_trials_; }; } // namespace webrtc diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h index 6faba5c8fc..844a36e2d0 100644 --- a/pc/test/mock_channel_interface.h +++ b/pc/test/mock_channel_interface.h @@ -26,8 +26,8 @@ class MockChannelInterface : public cricket::ChannelInterface { public: MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); MOCK_METHOD(MediaChannel*, media_channel, (), (const, override)); - MOCK_METHOD(const std::string&, transport_name, (), (const, override)); - MOCK_METHOD(const std::string&, content_name, (), (const, override)); + MOCK_METHOD(absl::string_view, transport_name, (), (const, override)); + MOCK_METHOD(const std::string&, mid, (), (const, override)); MOCK_METHOD(void, Enable, (bool), (override)); MOCK_METHOD(void, SetFirstPacketReceivedCallback, @@ -37,13 +37,13 @@ class MockChannelInterface : public cricket::ChannelInterface { SetLocalContent, (const cricket::MediaContentDescription*, webrtc::SdpType, - std::string*), + std::string&), (override)); MOCK_METHOD(bool, SetRemoteContent, (const cricket::MediaContentDescription*, webrtc::SdpType, - std::string*), + std::string&), (override)); MOCK_METHOD(bool, SetPayloadTypeDemuxingEnabled, (bool), (override)); MOCK_METHOD(const std::vector&, diff --git a/pc/test/mock_peer_connection_observers.h b/pc/test/mock_peer_connection_observers.h index 8054e08333..2698d956af 100644 --- a/pc/test/mock_peer_connection_observers.h +++ b/pc/test/mock_peer_connection_observers.h @@ -259,25 +259,38 @@ class MockCreateSessionDescriptionObserver error_("MockCreateSessionDescriptionObserver not called") {} virtual ~MockCreateSessionDescriptionObserver() {} void OnSuccess(SessionDescriptionInterface* desc) override { + MutexLock lock(&mutex_); called_ = true; error_ = ""; desc_.reset(desc); } void OnFailure(webrtc::RTCError error) override { + MutexLock lock(&mutex_); called_ = true; error_ = error.message(); } - bool called() const { return called_; } - bool result() const { return error_.empty(); } - const std::string& error() const { return error_; } + bool called() const { + MutexLock lock(&mutex_); + return called_; + } + bool result() const { + MutexLock lock(&mutex_); + return error_.empty(); + } + const std::string& error() const { + MutexLock lock(&mutex_); + return error_; + } std::unique_ptr MoveDescription() { + MutexLock lock(&mutex_); return std::move(desc_); } private: - bool called_; - std::string error_; - std::unique_ptr desc_; + mutable Mutex mutex_; + bool called_ RTC_GUARDED_BY(mutex_); + std::string error_ RTC_GUARDED_BY(mutex_); + std::unique_ptr desc_ RTC_GUARDED_BY(mutex_); }; class MockSetSessionDescriptionObserver @@ -292,19 +305,32 @@ class MockSetSessionDescriptionObserver error_("MockSetSessionDescriptionObserver not called") {} ~MockSetSessionDescriptionObserver() override {} void OnSuccess() override { + MutexLock lock(&mutex_); + called_ = true; error_ = ""; } void OnFailure(webrtc::RTCError error) override { + MutexLock lock(&mutex_); called_ = true; error_ = error.message(); } - bool called() const { return called_; } - bool result() const { return error_.empty(); } - const std::string& error() const { return error_; } + bool called() const { + MutexLock lock(&mutex_); + return called_; + } + bool result() const { + MutexLock lock(&mutex_); + return error_.empty(); + } + const std::string& error() const { + MutexLock lock(&mutex_); + return error_; + } private: + mutable Mutex mutex_; bool called_; std::string error_; }; diff --git a/pc/test/mock_rtp_receiver_internal.h b/pc/test/mock_rtp_receiver_internal.h index ba244039af..c222a04e2f 100644 --- a/pc/test/mock_rtp_receiver_internal.h +++ b/pc/test/mock_rtp_receiver_internal.h @@ -57,7 +57,7 @@ class MockRtpReceiverInternal : public RtpReceiverInternal { // RtpReceiverInternal methods. MOCK_METHOD(void, Stop, (), (override)); - MOCK_METHOD(void, StopAndEndTrack, (), (override)); + MOCK_METHOD(void, SetSourceEnded, (), (override)); MOCK_METHOD(void, SetMediaChannel, (cricket::MediaChannel*), (override)); MOCK_METHOD(void, SetupMediaChannel, (uint32_t), (override)); MOCK_METHOD(void, SetupUnsignaledMediaChannel, (), (override)); diff --git a/pc/test/mock_voice_media_channel.h b/pc/test/mock_voice_media_channel.h new file mode 100644 index 0000000000..daa5b1a4a0 --- /dev/null +++ b/pc/test/mock_voice_media_channel.h @@ -0,0 +1,151 @@ +/* + * 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 PC_TEST_MOCK_VOICE_MEDIA_CHANNEL_H_ +#define PC_TEST_MOCK_VOICE_MEDIA_CHANNEL_H_ + +#include +#include +#include + +#include "media/base/media_channel.h" +#include "rtc_base/gunit.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::InvokeWithoutArgs; +using ::testing::Mock; + +namespace cricket { +class MockVoiceMediaChannel : public VoiceMediaChannel { + public: + explicit MockVoiceMediaChannel(webrtc::TaskQueueBase* network_thread) + : VoiceMediaChannel(network_thread) {} + + MOCK_METHOD(void, SetInterface, (NetworkInterface * iface), (override)); + MOCK_METHOD(void, + OnPacketReceived, + (rtc::CopyOnWriteBuffer packet, int64_t packet_time_us), + (override)); + MOCK_METHOD(void, + OnPacketSent, + (const rtc::SentPacket& sent_packet), + (override)); + MOCK_METHOD(void, OnReadyToSend, (bool ready), (override)); + MOCK_METHOD(void, + OnNetworkRouteChanged, + (absl::string_view transport_name, + const rtc::NetworkRoute& network_route), + (override)); + MOCK_METHOD(bool, AddSendStream, (const StreamParams& sp), (override)); + MOCK_METHOD(bool, RemoveSendStream, (uint32_t ssrc), (override)); + MOCK_METHOD(bool, AddRecvStream, (const StreamParams& sp), (override)); + MOCK_METHOD(bool, RemoveRecvStream, (uint32_t ssrc), (override)); + MOCK_METHOD(void, ResetUnsignaledRecvStream, (), (override)); + MOCK_METHOD(void, OnDemuxerCriteriaUpdatePending, (), (override)); + MOCK_METHOD(void, OnDemuxerCriteriaUpdateComplete, (), (override)); + MOCK_METHOD(int, GetRtpSendTimeExtnId, (), (const, override)); + MOCK_METHOD( + void, + SetFrameEncryptor, + (uint32_t ssrc, + rtc::scoped_refptr frame_encryptor), + (override)); + MOCK_METHOD( + void, + SetFrameDecryptor, + (uint32_t ssrc, + rtc::scoped_refptr frame_decryptor), + (override)); + MOCK_METHOD(void, SetVideoCodecSwitchingEnabled, (bool enabled), (override)); + MOCK_METHOD(webrtc::RtpParameters, + GetRtpSendParameters, + (uint32_t ssrc), + (const, override)); + MOCK_METHOD(webrtc::RTCError, + SetRtpSendParameters, + (uint32_t ssrc, const webrtc::RtpParameters& parameters), + (override)); + MOCK_METHOD( + void, + SetEncoderToPacketizerFrameTransformer, + (uint32_t ssrc, + rtc::scoped_refptr frame_transformer), + (override)); + MOCK_METHOD( + void, + SetDepacketizerToDecoderFrameTransformer, + (uint32_t ssrc, + rtc::scoped_refptr frame_transformer), + (override)); + + MOCK_METHOD(bool, + SetSendParameters, + (const AudioSendParameters& params), + (override)); + MOCK_METHOD(bool, + SetRecvParameters, + (const AudioRecvParameters& params), + (override)); + MOCK_METHOD(webrtc::RtpParameters, + GetRtpReceiveParameters, + (uint32_t ssrc), + (const, override)); + MOCK_METHOD(webrtc::RtpParameters, + GetDefaultRtpReceiveParameters, + (), + (const, override)); + MOCK_METHOD(void, SetPlayout, (bool playout), (override)); + MOCK_METHOD(void, SetSend, (bool send), (override)); + MOCK_METHOD(bool, + SetAudioSend, + (uint32_t ssrc, + bool enable, + const AudioOptions* options, + AudioSource* source), + (override)); + MOCK_METHOD(bool, + SetOutputVolume, + (uint32_t ssrc, double volume), + (override)); + MOCK_METHOD(bool, SetDefaultOutputVolume, (double volume), (override)); + MOCK_METHOD(bool, CanInsertDtmf, (), (override)); + MOCK_METHOD(bool, + InsertDtmf, + (uint32_t ssrc, int event, int duration), + (override)); + MOCK_METHOD(bool, + GetStats, + (VoiceMediaInfo * info, bool get_and_clear_legacy_stats), + (override)); + MOCK_METHOD(void, + SetRawAudioSink, + (uint32_t ssrc, std::unique_ptr sink), + (override)); + MOCK_METHOD(void, + SetDefaultRawAudioSink, + (std::unique_ptr sink), + (override)); + MOCK_METHOD(std::vector, + GetSources, + (uint32_t ssrc), + (const, override)); + + MOCK_METHOD(bool, + SetBaseMinimumPlayoutDelayMs, + (uint32_t ssrc, int delay_ms), + (override)); + MOCK_METHOD(absl::optional, + GetBaseMinimumPlayoutDelayMs, + (uint32_t ssrc), + (const, override)); +}; +} // namespace cricket + +#endif // PC_TEST_MOCK_VOICE_MEDIA_CHANNEL_H_ diff --git a/pc/track_media_info_map.cc b/pc/track_media_info_map.cc index e68f2f7a52..12670dda28 100644 --- a/pc/track_media_info_map.cc +++ b/pc/track_media_info_map.cc @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include "api/media_types.h" diff --git a/pc/track_media_info_map_unittest.cc b/pc/track_media_info_map_unittest.cc index 42962daa2d..8cf3360c6f 100644 --- a/pc/track_media_info_map_unittest.cc +++ b/pc/track_media_info_map_unittest.cc @@ -10,22 +10,28 @@ #include "pc/track_media_info_map.h" +#include + +#include #include -#include #include +#include #include #include -#include "api/rtp_sender_interface.h" +#include "api/media_types.h" +#include "api/rtp_parameters.h" #include "api/test/mock_video_track.h" -#include "api/transport/rtp/rtp_source.h" #include "media/base/media_channel.h" #include "pc/audio_track.h" #include "pc/test/fake_video_track_source.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" #include "pc/video_track.h" -#include "rtc_base/ref_count.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { @@ -125,7 +131,7 @@ class TrackMediaInfoMapTest : public ::testing::Test { local_track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO, - ssrcs, local_track); + ssrcs, rtc::scoped_refptr(local_track)); rtp_senders_.push_back(rtp_sender); if (local_track->kind() == MediaStreamTrackInterface::kAudioKind) { @@ -154,7 +160,7 @@ class TrackMediaInfoMapTest : public ::testing::Test { remote_track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO, - ssrcs, remote_track); + ssrcs, rtc::scoped_refptr(remote_track)); rtp_receivers_.push_back(rtp_receiver); if (remote_track->kind() == MediaStreamTrackInterface::kAudioKind) { diff --git a/pc/transceiver_list.cc b/pc/transceiver_list.cc index 235c9af036..139c498634 100644 --- a/pc/transceiver_list.cc +++ b/pc/transceiver_list.cc @@ -10,6 +10,8 @@ #include "pc/transceiver_list.h" +#include + #include "rtc_base/checks.h" namespace webrtc { diff --git a/pc/used_ids.h b/pc/used_ids.h index 0b06dbac56..1236a786df 100644 --- a/pc/used_ids.h +++ b/pc/used_ids.h @@ -52,8 +52,7 @@ class UsedIds { if (IsIdUsed(original_id)) { new_id = FindUnusedId(); - RTC_LOG(LS_INFO) << "Duplicate id found. Reassigning from " - << original_id << " to " << new_id; + // Duplicate id found. Reassign from the original id to the new. idstruct->id = new_id; } SetIdUsed(new_id); diff --git a/pc/used_ids_unittest.cc b/pc/used_ids_unittest.cc index af66898450..6362f2773a 100644 --- a/pc/used_ids_unittest.cc +++ b/pc/used_ids_unittest.cc @@ -10,6 +10,7 @@ #include "pc/used_ids.h" +#include "absl/strings/string_view.h" #include "test/gtest.h" using cricket::UsedIds; diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc index 8db4d9f02f..7659d7c2f9 100644 --- a/pc/video_rtp_receiver.cc +++ b/pc/video_rtp_receiver.cc @@ -12,15 +12,16 @@ #include +#include #include #include #include "api/video/recordable_encoded_frame.h" -#include "api/video_track_source_proxy_factory.h" #include "pc/video_track.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -41,20 +42,15 @@ VideoRtpReceiver::VideoRtpReceiver( track_(VideoTrackProxyWithInternal::Create( rtc::Thread::Current(), worker_thread, - VideoTrack::Create(receiver_id, - CreateVideoTrackSourceProxy(rtc::Thread::Current(), - worker_thread, - source_), - worker_thread))), + VideoTrack::Create(receiver_id, source_, worker_thread))), attachment_id_(GenerateUniqueId()) { RTC_DCHECK(worker_thread_); SetStreams(streams); - RTC_DCHECK_EQ(source_->state(), MediaSourceInterface::kLive); + RTC_DCHECK_EQ(source_->state(), MediaSourceInterface::kInitializing); } VideoRtpReceiver::~VideoRtpReceiver() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RTC_DCHECK(stopped_); RTC_DCHECK(!media_channel_); } @@ -114,87 +110,66 @@ void VideoRtpReceiver::SetDepacketizerToDecoderFrameTransformer( void VideoRtpReceiver::Stop() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - // TODO(deadbeef): Need to do more here to fully stop receiving packets. - - if (!stopped_) { - source_->SetState(MediaSourceInterface::kEnded); - stopped_ = true; - } - - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - if (media_channel_) { - SetSink(nullptr); - SetMediaChannel_w(nullptr); - } - source_->ClearCallback(); - }); -} - -void VideoRtpReceiver::StopAndEndTrack() { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - Stop(); + source_->SetState(MediaSourceInterface::kEnded); track_->internal()->set_ended(); } -void VideoRtpReceiver::RestartMediaChannel(absl::optional ssrc) { +void VideoRtpReceiver::SetSourceEnded() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + source_->SetState(MediaSourceInterface::kEnded); +} - // `stopped_` will be `true` on construction. RestartMediaChannel - // can in this case function like "ensure started" and flip `stopped_` - // to false. - +// RTC_RUN_ON(&signaling_thread_checker_) +void VideoRtpReceiver::RestartMediaChannel(absl::optional ssrc) { + MediaSourceInterface::SourceState state = source_->state(); // TODO(tommi): Can we restart the media channel without blocking? - bool ok = worker_thread_->Invoke(RTC_FROM_HERE, [&, was_stopped = - stopped_] { + worker_thread_->Invoke(RTC_FROM_HERE, [&] { RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - // Ignore further negotiations if we've already been stopped and don't - // have an associated media channel. - RTC_DCHECK(was_stopped); - return false; // Can't restart. - } - - if (!was_stopped && ssrc_ == ssrc) { - // Already running with that ssrc. - return true; - } - - // Disconnect from the previous ssrc. - if (!was_stopped) { - SetSink(nullptr); - } - - bool encoded_sink_enabled = saved_encoded_sink_enabled_; - SetEncodedSinkEnabled(false); - - // Set up the new ssrc. - ssrc_ = std::move(ssrc); - SetSink(source_->sink()); - if (encoded_sink_enabled) { - SetEncodedSinkEnabled(true); - } - - if (frame_transformer_ && media_channel_) { - media_channel_->SetDepacketizerToDecoderFrameTransformer( - ssrc_.value_or(0), frame_transformer_); - } - - if (media_channel_ && ssrc_) { - if (frame_decryptor_) { - media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_); - } - - media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); - } - - return true; + RestartMediaChannel_w(std::move(ssrc), state); }); + source_->SetState(MediaSourceInterface::kLive); +} - if (!ok) - return; +// RTC_RUN_ON(worker_thread_) +void VideoRtpReceiver::RestartMediaChannel_w( + absl::optional ssrc, + MediaSourceInterface::SourceState state) { + if (!media_channel_) { + return; // Can't restart. + } - stopped_ = false; + const bool encoded_sink_enabled = saved_encoded_sink_enabled_; + + if (state != MediaSourceInterface::kInitializing) { + if (ssrc == ssrc_) + return; + + // Disconnect from a previous ssrc. + SetSink(nullptr); + + if (encoded_sink_enabled) + SetEncodedSinkEnabled(false); + } + + // Set up the new ssrc. + ssrc_ = std::move(ssrc); + SetSink(source_->sink()); + if (encoded_sink_enabled) { + SetEncodedSinkEnabled(true); + } + + if (frame_transformer_ && media_channel_) { + media_channel_->SetDepacketizerToDecoderFrameTransformer( + ssrc_.value_or(0), frame_transformer_); + } + + if (media_channel_ && ssrc_) { + if (frame_decryptor_) { + media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_); + } + + media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); + } } // RTC_RUN_ON(worker_thread_) @@ -284,17 +259,11 @@ void VideoRtpReceiver::SetJitterBufferMinimumDelay( } void VideoRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK_RUN_ON(worker_thread_); RTC_DCHECK(media_channel == nullptr || media_channel->media_type() == media_type()); - if (stopped_ && !media_channel) - return; - - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - SetMediaChannel_w(media_channel); - }); + SetMediaChannel_w(media_channel); } // RTC_RUN_ON(worker_thread_) @@ -302,6 +271,10 @@ void VideoRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { if (media_channel == media_channel_) return; + if (!media_channel) { + SetSink(nullptr); + } + bool encoded_sink_enabled = saved_encoded_sink_enabled_; if (encoded_sink_enabled && media_channel_) { // Turn off the old sink, if any. @@ -324,6 +297,9 @@ void VideoRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { ssrc_.value_or(0), frame_transformer_); } } + + if (!media_channel) + source_->ClearCallback(); } void VideoRtpReceiver::NotifyFirstPacketReceived() { @@ -341,6 +317,19 @@ std::vector VideoRtpReceiver::GetSources() const { return media_channel_->GetSources(*ssrc_); } +void VideoRtpReceiver::SetupMediaChannel(absl::optional ssrc, + cricket::MediaChannel* media_channel) { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK(media_channel); + MediaSourceInterface::SourceState state = source_->state(); + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RTC_DCHECK_RUN_ON(worker_thread_); + SetMediaChannel_w(media_channel); + RestartMediaChannel_w(std::move(ssrc), state); + }); + source_->SetState(MediaSourceInterface::kLive); +} + void VideoRtpReceiver::OnGenerateKeyFrame() { RTC_DCHECK_RUN_ON(worker_thread_); if (!media_channel_) { diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h index b5381860b3..4261e417d2 100644 --- a/pc/video_rtp_receiver.h +++ b/pc/video_rtp_receiver.h @@ -88,7 +88,7 @@ class VideoRtpReceiver : public RtpReceiverInternal { // RtpReceiverInternal implementation. void Stop() override; - void StopAndEndTrack() override; + void SetSourceEnded() override; void SetupMediaChannel(uint32_t ssrc) override; void SetupUnsignaledMediaChannel() override; uint32_t ssrc() const override; @@ -110,8 +110,17 @@ class VideoRtpReceiver : public RtpReceiverInternal { std::vector GetSources() const override; + // Combines SetMediaChannel, SetupMediaChannel and + // SetupUnsignaledMediaChannel. + void SetupMediaChannel(absl::optional ssrc, + cricket::MediaChannel* media_channel); + private: - void RestartMediaChannel(absl::optional ssrc); + void RestartMediaChannel(absl::optional ssrc) + RTC_RUN_ON(&signaling_thread_checker_); + void RestartMediaChannel_w(absl::optional ssrc, + MediaSourceInterface::SourceState state) + RTC_RUN_ON(worker_thread_); void SetSink(rtc::VideoSinkInterface* sink) RTC_RUN_ON(worker_thread_); void SetMediaChannel_w(cricket::MediaChannel* media_channel) @@ -141,8 +150,6 @@ class VideoRtpReceiver : public RtpReceiverInternal { rtc::Thread* const worker_thread_; const std::string id_; - // See documentation for `stopped_` below for when a valid media channel - // has been assigned and when this pointer will be null. cricket::VideoMediaChannel* media_channel_ RTC_GUARDED_BY(worker_thread_) = nullptr; absl::optional ssrc_ RTC_GUARDED_BY(worker_thread_); @@ -152,15 +159,6 @@ class VideoRtpReceiver : public RtpReceiverInternal { const rtc::scoped_refptr> track_; std::vector> streams_ RTC_GUARDED_BY(&signaling_thread_checker_); - // `stopped` is state that's used on the signaling thread to indicate whether - // a valid `media_channel_` has been assigned and configured. When an instance - // of VideoRtpReceiver is initially created, `stopped_` is true and will - // remain true until either `SetupMediaChannel` or - // `SetupUnsignaledMediaChannel` is called after assigning a media channel. - // After that, `stopped_` will remain false until `Stop()` is called. - // Note, for checking the state of the class on the worker thread, - // check `media_channel_` instead, as that's the main worker thread state. - bool stopped_ RTC_GUARDED_BY(&signaling_thread_checker_) = true; RtpReceiverObserverInterface* observer_ RTC_GUARDED_BY(&signaling_thread_checker_) = nullptr; bool received_first_packet_ RTC_GUARDED_BY(&signaling_thread_checker_) = diff --git a/pc/video_rtp_receiver_unittest.cc b/pc/video_rtp_receiver_unittest.cc index 3a8099d30f..c13214fcbb 100644 --- a/pc/video_rtp_receiver_unittest.cc +++ b/pc/video_rtp_receiver_unittest.cc @@ -10,11 +10,17 @@ #include "pc/video_rtp_receiver.h" +#include #include +#include "api/task_queue/task_queue_base.h" +#include "api/video/recordable_encoded_frame.h" #include "api/video/test/mock_recordable_encoded_frame.h" #include "media/base/fake_media_engine.h" +#include "rtc_base/location.h" +#include "rtc_base/ref_counted_object.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::_; using ::testing::AnyNumber; @@ -60,13 +66,20 @@ class VideoRtpReceiverTest : public testing::Test { std::string("receiver"), std::vector({"stream"}))) { worker_thread_->Start(); - receiver_->SetMediaChannel(&channel_); + SetMediaChannel(&channel_); } ~VideoRtpReceiverTest() override { - // Clear expectations that tests may have set up before calling Stop(). + // Clear expectations that tests may have set up before calling + // SetMediaChannel(nullptr). Mock::VerifyAndClearExpectations(&channel_); receiver_->Stop(); + SetMediaChannel(nullptr); + } + + void SetMediaChannel(cricket::MediaChannel* media_channel) { + worker_thread_->Invoke( + RTC_FROM_HERE, [&]() { receiver_->SetMediaChannel(media_channel); }); } webrtc::VideoTrackSourceInterface* Source() { @@ -94,23 +107,24 @@ TEST_F(VideoRtpReceiverTest, MockVideoMediaChannel channel2(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel_, GenerateKeyFrame).Times(0); EXPECT_CALL(channel2, GenerateKeyFrame).Times(0); - receiver_->SetMediaChannel(&channel2); + SetMediaChannel(&channel2); Mock::VerifyAndClearExpectations(&channel2); // Generate a key frame. When we switch channel next time, we will have to // re-generate it as we don't know if it was eventually received + EXPECT_CALL(channel2, GenerateKeyFrame).Times(1); Source()->GenerateKeyFrame(); MockVideoMediaChannel channel3(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel3, GenerateKeyFrame); - receiver_->SetMediaChannel(&channel3); + SetMediaChannel(&channel3); // Switching to a new channel should now not cause calls to GenerateKeyFrame. StrictMock channel4(nullptr, cricket::VideoOptions()); - receiver_->SetMediaChannel(&channel4); + SetMediaChannel(&channel4); - // We must call Stop() here since the mock media channels live on the stack - // and `receiver_` still has a pointer to those objects. - receiver_->Stop(); + // We must call SetMediaChannel(nullptr) here since the mock media channels + // live on the stack and `receiver_` still has a pointer to those objects. + SetMediaChannel(nullptr); } TEST_F(VideoRtpReceiverTest, EnablesEncodedOutput) { @@ -135,7 +149,7 @@ TEST_F(VideoRtpReceiverTest, DisablesEnablesEncodedOutputOnChannelSwitch) { Source()->AddEncodedSink(&sink); MockVideoMediaChannel channel2(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel2, SetRecordableEncodedFrameCallback); - receiver_->SetMediaChannel(&channel2); + SetMediaChannel(&channel2); Mock::VerifyAndClearExpectations(&channel2); // When clearing encoded frame buffer function, we need channel switches @@ -143,11 +157,11 @@ TEST_F(VideoRtpReceiverTest, DisablesEnablesEncodedOutputOnChannelSwitch) { EXPECT_CALL(channel2, ClearRecordableEncodedFrameCallback); Source()->RemoveEncodedSink(&sink); StrictMock channel3(nullptr, cricket::VideoOptions()); - receiver_->SetMediaChannel(&channel3); + SetMediaChannel(&channel3); - // We must call Stop() here since the mock media channels live on the stack - // and `receiver_` still has a pointer to those objects. - receiver_->Stop(); + // We must call SetMediaChannel(nullptr) here since the mock media channels + // live on the stack and `receiver_` still has a pointer to those objects. + SetMediaChannel(nullptr); } TEST_F(VideoRtpReceiverTest, BroadcastsEncodedFramesWhenEnabled) { @@ -169,7 +183,6 @@ TEST_F(VideoRtpReceiverTest, BroadcastsEncodedFramesWhenEnabled) { TEST_F(VideoRtpReceiverTest, EnablesEncodedOutputOnChannelRestart) { InSequence s; - EXPECT_CALL(channel_, ClearRecordableEncodedFrameCallback(0)); MockVideoSink sink; Source()->AddEncodedSink(&sink); EXPECT_CALL(channel_, SetRecordableEncodedFrameCallback(4711, _)); diff --git a/pc/video_rtp_track_source.h b/pc/video_rtp_track_source.h index 23a7cd224f..a9e43f6667 100644 --- a/pc/video_rtp_track_source.h +++ b/pc/video_rtp_track_source.h @@ -20,7 +20,6 @@ #include "api/video/video_source_interface.h" #include "media/base/video_broadcaster.h" #include "pc/video_track_source.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" @@ -45,6 +44,9 @@ class VideoRtpTrackSource : public VideoTrackSource { explicit VideoRtpTrackSource(Callback* callback); + VideoRtpTrackSource(const VideoRtpTrackSource&) = delete; + VideoRtpTrackSource& operator=(const VideoRtpTrackSource&) = delete; + // Call before the object implementing Callback finishes it's destructor. No // more callbacks will be fired after completion. Must be called on the // worker thread @@ -83,8 +85,6 @@ class VideoRtpTrackSource : public VideoTrackSource { std::vector*> encoded_sinks_ RTC_GUARDED_BY(mu_); Callback* callback_ RTC_GUARDED_BY(worker_sequence_checker_); - - RTC_DISALLOW_COPY_AND_ASSIGN(VideoRtpTrackSource); }; } // namespace webrtc diff --git a/pc/video_rtp_track_source_unittest.cc b/pc/video_rtp_track_source_unittest.cc index 5666b77d5f..bb1dc193de 100644 --- a/pc/video_rtp_track_source_unittest.cc +++ b/pc/video_rtp_track_source_unittest.cc @@ -10,6 +10,12 @@ #include "pc/video_rtp_track_source.h" +#include "absl/types/optional.h" +#include "api/scoped_refptr.h" +#include "api/units/timestamp.h" +#include "api/video/color_space.h" +#include "api/video/encoded_image.h" +#include "api/video/video_codec_type.h" #include "rtc_base/ref_counted_object.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/pc/video_track.cc b/pc/video_track.cc index d0246faa87..aa8e0df922 100644 --- a/pc/video_track.cc +++ b/pc/video_track.cc @@ -10,7 +10,6 @@ #include "pc/video_track.h" -#include #include #include @@ -22,12 +21,14 @@ namespace webrtc { -VideoTrack::VideoTrack(const std::string& label, - VideoTrackSourceInterface* video_source, - rtc::Thread* worker_thread) +VideoTrack::VideoTrack( + const std::string& label, + rtc::scoped_refptr< + VideoTrackSourceProxyWithInternal> source, + rtc::Thread* worker_thread) : MediaStreamTrack(label), worker_thread_(worker_thread), - video_source_(video_source), + video_source_(std::move(source)), content_hint_(ContentHint::kNone) { RTC_DCHECK_RUN_ON(&signaling_thread_); // Detach the thread checker for VideoSourceBaseGuarded since we'll make calls @@ -53,14 +54,19 @@ void VideoTrack::AddOrUpdateSink(rtc::VideoSinkInterface* sink, RTC_DCHECK_RUN_ON(worker_thread_); VideoSourceBaseGuarded::AddOrUpdateSink(sink, wants); rtc::VideoSinkWants modified_wants = wants; - modified_wants.black_frames = !enabled(); - video_source_->AddOrUpdateSink(sink, modified_wants); + modified_wants.black_frames = !enabled_w_; + video_source_->internal()->AddOrUpdateSink(sink, modified_wants); } void VideoTrack::RemoveSink(rtc::VideoSinkInterface* sink) { RTC_DCHECK_RUN_ON(worker_thread_); VideoSourceBaseGuarded::RemoveSink(sink); - video_source_->RemoveSink(sink); + video_source_->internal()->RemoveSink(sink); +} + +void VideoTrack::RequestRefreshFrame() { + RTC_DCHECK_RUN_ON(worker_thread_); + video_source_->internal()->RequestRefreshFrame(); } VideoTrackSourceInterface* VideoTrack::GetSource() const { @@ -68,13 +74,17 @@ VideoTrackSourceInterface* VideoTrack::GetSource() const { return video_source_.get(); } +VideoTrackSourceInterface* VideoTrack::GetSourceInternal() const { + return video_source_->internal(); +} + VideoTrackInterface::ContentHint VideoTrack::content_hint() const { - RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK_RUN_ON(&signaling_thread_); return content_hint_; } void VideoTrack::set_content_hint(ContentHint hint) { - RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK_RUN_ON(&signaling_thread_); if (content_hint_ == hint) return; content_hint_ = hint; @@ -82,17 +92,29 @@ void VideoTrack::set_content_hint(ContentHint hint) { } bool VideoTrack::set_enabled(bool enable) { - RTC_DCHECK_RUN_ON(worker_thread_); - for (auto& sink_pair : sink_pairs()) { - rtc::VideoSinkWants modified_wants = sink_pair.wants; - modified_wants.black_frames = !enable; - video_source_->AddOrUpdateSink(sink_pair.sink, modified_wants); - } - return MediaStreamTrack::set_enabled(enable); + RTC_DCHECK_RUN_ON(&signaling_thread_); + + bool ret = MediaStreamTrack::set_enabled(enable); + + worker_thread_->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(worker_thread_); + enabled_w_ = enable; + for (auto& sink_pair : sink_pairs()) { + rtc::VideoSinkWants modified_wants = sink_pair.wants; + modified_wants.black_frames = !enable; + video_source_->AddOrUpdateSink(sink_pair.sink, modified_wants); + } + }); + + return ret; } bool VideoTrack::enabled() const { - RTC_DCHECK_RUN_ON(worker_thread_); + if (worker_thread_->IsCurrent()) { + RTC_DCHECK_RUN_ON(worker_thread_); + return enabled_w_; + } + RTC_DCHECK_RUN_ON(&signaling_thread_); return MediaStreamTrack::enabled(); } @@ -103,22 +125,23 @@ MediaStreamTrackInterface::TrackState VideoTrack::state() const { void VideoTrack::OnChanged() { RTC_DCHECK_RUN_ON(&signaling_thread_); - worker_thread_->Invoke( - RTC_FROM_HERE, [this, state = video_source_->state()]() { - // TODO(tommi): Calling set_state() this way isn't ideal since we're - // currently blocking the signaling thread and set_state() may - // internally fire notifications via `FireOnChanged()` which may further - // amplify the blocking effect on the signaling thread. - rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; - set_state(state == MediaSourceInterface::kEnded ? kEnded : kLive); - }); + rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; + MediaSourceInterface::SourceState state = video_source_->state(); + set_state(state == MediaSourceInterface::kEnded ? kEnded : kLive); } rtc::scoped_refptr VideoTrack::Create( const std::string& id, VideoTrackSourceInterface* source, rtc::Thread* worker_thread) { - return rtc::make_ref_counted(id, source, worker_thread); + rtc::scoped_refptr< + VideoTrackSourceProxyWithInternal> + source_proxy = VideoTrackSourceProxy::Create( + rtc::Thread::Current(), worker_thread, + rtc::scoped_refptr(source)); + + return rtc::make_ref_counted(id, std::move(source_proxy), + worker_thread); } } // namespace webrtc diff --git a/pc/video_track.h b/pc/video_track.h index 49deaee76a..f938b3362c 100644 --- a/pc/video_track.h +++ b/pc/video_track.h @@ -13,6 +13,7 @@ #include +#include "absl/types/optional.h" #include "api/media_stream_interface.h" #include "api/media_stream_track.h" #include "api/scoped_refptr.h" @@ -21,11 +22,17 @@ #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" #include "media/base/video_source_base.h" +#include "pc/video_track_source_proxy.h" +#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" namespace webrtc { +// TODO(tommi): Instead of inheriting from `MediaStreamTrack<>`, implement the +// properties directly in this class. `MediaStreamTrack` doesn't guard against +// conflicting access, so we'd need to override those methods anyway in this +// class in order to make sure things are correctly checked. class VideoTrack : public MediaStreamTrack, public rtc::VideoSourceBaseGuarded, public ObserverInterface { @@ -38,6 +45,7 @@ class VideoTrack : public MediaStreamTrack, void AddOrUpdateSink(rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) override; void RemoveSink(rtc::VideoSinkInterface* sink) override; + void RequestRefreshFrame() override; VideoTrackSourceInterface* GetSource() const override; ContentHint content_hint() const override; @@ -47,10 +55,15 @@ class VideoTrack : public MediaStreamTrack, MediaStreamTrackInterface::TrackState state() const override; std::string kind() const override; + // Direct access to the non-proxied source object for internal implementation. + VideoTrackSourceInterface* GetSourceInternal() const; + protected: - VideoTrack(const std::string& id, - VideoTrackSourceInterface* video_source, - rtc::Thread* worker_thread); + VideoTrack( + const std::string& id, + rtc::scoped_refptr< + VideoTrackSourceProxyWithInternal> source, + rtc::Thread* worker_thread); ~VideoTrack(); private: @@ -59,8 +72,15 @@ class VideoTrack : public MediaStreamTrack, RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker signaling_thread_; rtc::Thread* const worker_thread_; - const rtc::scoped_refptr video_source_; - ContentHint content_hint_ RTC_GUARDED_BY(worker_thread_); + const rtc::scoped_refptr< + VideoTrackSourceProxyWithInternal> + video_source_; + ContentHint content_hint_ RTC_GUARDED_BY(&signaling_thread_); + // Cached `enabled` state for the worker thread. This is kept in sync with + // the state maintained on the signaling thread via set_enabled() but can + // be queried without blocking on the worker thread by callers that don't + // use an api proxy to call the `enabled()` method. + bool enabled_w_ RTC_GUARDED_BY(worker_thread_) = true; }; } // namespace webrtc diff --git a/pc/video_track_source.cc b/pc/video_track_source.cc index d15eaaf43c..64e99cc064 100644 --- a/pc/video_track_source.cc +++ b/pc/video_track_source.cc @@ -15,11 +15,12 @@ namespace webrtc { VideoTrackSource::VideoTrackSource(bool remote) - : state_(kLive), remote_(remote) { + : state_(kInitializing), remote_(remote) { worker_thread_checker_.Detach(); } void VideoTrackSource::SetState(SourceState new_state) { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (state_ != new_state) { state_ = new_state; FireOnChanged(); diff --git a/pc/video_track_source.h b/pc/video_track_source.h index 4a29381c4c..723b10d8f3 100644 --- a/pc/video_track_source.h +++ b/pc/video_track_source.h @@ -20,7 +20,10 @@ #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" #include "media/base/media_channel.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -31,7 +34,10 @@ class RTC_EXPORT VideoTrackSource : public Notifier { explicit VideoTrackSource(bool remote); void SetState(SourceState new_state); - SourceState state() const override { return state_; } + SourceState state() const override { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + return state_; + } bool remote() const override { return remote_; } bool is_screencast() const override { return false; } @@ -56,8 +62,9 @@ class RTC_EXPORT VideoTrackSource : public Notifier { virtual rtc::VideoSourceInterface* source() = 0; private: - SequenceChecker worker_thread_checker_; - SourceState state_; + RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_thread_checker_; + RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; + SourceState state_ RTC_GUARDED_BY(&signaling_thread_checker_); const bool remote_; }; diff --git a/pc/video_track_source_proxy.cc b/pc/video_track_source_proxy.cc index 309c1f20f8..c3e95e23cc 100644 --- a/pc/video_track_source_proxy.cc +++ b/pc/video_track_source_proxy.cc @@ -11,7 +11,9 @@ #include "pc/video_track_source_proxy.h" #include "api/media_stream_interface.h" +#include "api/scoped_refptr.h" #include "api/video_track_source_proxy_factory.h" +#include "rtc_base/thread.h" namespace webrtc { @@ -19,7 +21,9 @@ rtc::scoped_refptr CreateVideoTrackSourceProxy( rtc::Thread* signaling_thread, rtc::Thread* worker_thread, VideoTrackSourceInterface* source) { - return VideoTrackSourceProxy::Create(signaling_thread, worker_thread, source); + return VideoTrackSourceProxy::Create( + signaling_thread, worker_thread, + rtc::scoped_refptr(source)); } } // namespace webrtc diff --git a/pc/video_track_source_proxy.h b/pc/video_track_source_proxy.h index 8914dd0525..8500a98766 100644 --- a/pc/video_track_source_proxy.h +++ b/pc/video_track_source_proxy.h @@ -11,7 +11,13 @@ #ifndef PC_VIDEO_TRACK_SOURCE_PROXY_H_ #define PC_VIDEO_TRACK_SOURCE_PROXY_H_ +#include "absl/types/optional.h" #include "api/media_stream_interface.h" +#include "api/video/recordable_encoded_frame.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" +#include "api/video_track_source_constraints.h" #include "pc/proxy.h" namespace webrtc { @@ -21,6 +27,7 @@ namespace webrtc { // TODO(deadbeef): Move this to .cc file. What threads methods are called on is // an implementation detail. BEGIN_PROXY_MAP(VideoTrackSource) + PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_CONSTMETHOD0(SourceState, state) BYPASS_PROXY_CONSTMETHOD0(bool, remote) @@ -32,6 +39,7 @@ PROXY_SECONDARY_METHOD2(void, rtc::VideoSinkInterface*, const rtc::VideoSinkWants&) PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface*) +PROXY_SECONDARY_METHOD0(void, RequestRefreshFrame) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*) PROXY_CONSTMETHOD0(bool, SupportsEncodedOutput) @@ -42,6 +50,9 @@ PROXY_SECONDARY_METHOD1(void, PROXY_SECONDARY_METHOD1(void, RemoveEncodedSink, rtc::VideoSinkInterface*) +PROXY_SECONDARY_METHOD1(void, + ProcessConstraints, + const webrtc::VideoTrackSourceConstraints&) END_PROXY_MAP(VideoTrackSource) } // namespace webrtc diff --git a/pc/video_track_unittest.cc b/pc/video_track_unittest.cc index 6342b608f1..2a10c93f7b 100644 --- a/pc/video_track_unittest.cc +++ b/pc/video_track_unittest.cc @@ -13,11 +13,11 @@ #include #include "media/base/fake_frame_source.h" -#include "media/base/video_common.h" #include "pc/test/fake_video_track_renderer.h" #include "pc/test/fake_video_track_source.h" #include "pc/video_track_source.h" #include "rtc_base/ref_counted_object.h" +#include "rtc_base/time_utils.h" #include "test/gtest.h" using webrtc::FakeVideoTrackRenderer; @@ -40,10 +40,19 @@ class VideoTrackTest : public ::testing::Test { protected: rtc::scoped_refptr video_track_source_; - rtc::scoped_refptr video_track_; + rtc::scoped_refptr video_track_; cricket::FakeFrameSource frame_source_; }; +// VideoTrack::Create will create an API proxy around the source object. +// The `GetSource` method provides access to the proxy object intented for API +// use while the GetSourceInternal() provides direct access to the source object +// as provided to the `VideoTrack::Create` factory function. +TEST_F(VideoTrackTest, CheckApiProxyAndInternalSource) { + EXPECT_NE(video_track_->GetSource(), video_track_source_.get()); + EXPECT_EQ(video_track_->GetSourceInternal(), video_track_source_.get()); +} + // Test changing the source state also changes the track state. TEST_F(VideoTrackTest, SourceStateChangeTrackState) { EXPECT_EQ(MediaStreamTrackInterface::kLive, video_track_->state()); diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index d5fcddba3a..12e9dd8f68 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -12,9 +12,9 @@ #include #include -#include #include +#include #include #include #include @@ -25,12 +25,14 @@ #include #include "absl/algorithm/container.h" +#include "absl/strings/ascii.h" #include "api/candidate.h" #include "api/crypto_params.h" #include "api/jsep_ice_candidate.h" #include "api/jsep_session_description.h" #include "api/media_types.h" // for RtpExtension +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" @@ -107,14 +109,15 @@ namespace webrtc { // = // where MUST be exactly one case-significant character. -// Legal characters in a value (RFC 4566 section 9): +// Check if passed character is a "token-char" from RFC 4566. +// https://datatracker.ietf.org/doc/html/rfc4566#section-9 // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 // / %x41-5A / %x5E-7E -static const char kLegalTokenCharacters[] = - "!#$%&'*+-." // %x21, %x23-27, %x2A-2B, %x2D-2E - "0123456789" // %x30-39 - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // %x41-5A - "^_`abcdefghijklmnopqrstuvwxyz{|}~"; // %x5E-7E +bool IsTokenChar(char ch) { + return ch == 0x21 || (ch >= 0x23 && ch <= 0x27) || ch == 0x2a || ch == 0x2b || + ch == 0x2d || ch == 0x2e || (ch >= 0x30 && ch <= 0x39) || + (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x5e && ch <= 0x7e); +} static const int kLinePrefixLength = 2; // Length of = static const char kLineTypeVersion = 'v'; static const char kLineTypeOrigin = 'o'; @@ -256,6 +259,9 @@ static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel"; // types. const int kWildcardPayloadType = -1; +// Maximum number of channels allowed. +static const size_t kMaxNumberOfChannels = 24; + struct SsrcInfo { uint32_t ssrc_id; std::string cname; @@ -344,7 +350,7 @@ static bool ParseFmtpAttributes(const std::string& line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseFmtpParam(const std::string& line, +static bool ParseFmtpParam(absl::string_view line, std::string* parameter, std::string* value, SdpParseError* error); @@ -415,7 +421,9 @@ static bool ParseFailed(absl::string_view message, RTC_LOG(LS_ERROR) << "Failed to parse: \"" << first_line << "\". Reason: " << description; if (error) { - error->line = std::string(first_line); + // TODO(bugs.webrtc.org/13220): In C++17, we can use plain assignment, with + // a string_view on the right hand side. + error->line.assign(first_line.data(), first_line.size()); error->description = std::move(description); } return false; @@ -635,7 +643,7 @@ static bool GetSingleTokenValue(const std::string& message, if (!GetValue(message, attribute, value, error)) { return false; } - if (strspn(value->c_str(), kLegalTokenCharacters) != value->size()) { + if (!absl::c_all_of(absl::string_view(*value), IsTokenChar)) { rtc::StringBuilder description; description << "Illegal character found in the value of " << attribute; return ParseFailed(message, description.Release(), error); @@ -779,7 +787,7 @@ static int GetCandidatePreferenceFromType(const std::string& type) { } else if (type == cricket::RELAY_PORT_TYPE) { preference = kPreferenceRelayed; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return preference; } @@ -1414,14 +1422,14 @@ void BuildMediaDescription(const ContentInfo* content_info, fmt.append(kDefaultSctpmapProtocol); } } else { - RTC_NOTREACHED() << "Data description without SCTP"; + RTC_DCHECK_NOTREACHED() << "Data description without SCTP"; } } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) { const UnsupportedContentDescription* unsupported_desc = media_desc->as_unsupported(); type = unsupported_desc->media_type(); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // The fmt must never be empty. If no codecs are found, set the fmt attribute // to 0. @@ -1617,7 +1625,7 @@ void BuildRtpContentAttributes(const MediaContentDescription* media_desc, InitAttrLine(kAttributeSendRecv, &os); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); InitAttrLine(kAttributeSendRecv, &os); break; } @@ -1992,7 +2000,7 @@ void BuildCandidate(const std::vector& candidates, type = kCandidatePrflx; // Peer reflexive candidate may be signaled for being removed. } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); // Never write out candidates if we don't know the type. continue; } @@ -3625,6 +3633,10 @@ bool ParseRtpmapAttribute(const std::string& line, return false; } } + if (channels > kMaxNumberOfChannels) { + return ParseFailed(line, "At most 24 channels are supported.", error); + } + AudioContentDescription* audio_desc = media_desc->as_audio(); UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels, audio_desc); @@ -3632,14 +3644,14 @@ bool ParseRtpmapAttribute(const std::string& line, return true; } -bool ParseFmtpParam(const std::string& line, +bool ParseFmtpParam(absl::string_view line, std::string* parameter, std::string* value, SdpParseError* error) { if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) { // Support for non-key-value lines like RFC 2198 or RFC 4733. *parameter = ""; - *value = line; + *value = std::string(line); return true; } // a=fmtp: =; =; ... @@ -3682,14 +3694,13 @@ bool ParseFmtpAttributes(const std::string& line, } // Parse out format specific parameters. - std::vector fields; - rtc::split(line_params, kSdpDelimiterSemicolonChar, &fields); - cricket::CodecParameterMap codec_params; - for (auto& iter : fields) { + for (absl::string_view param : + rtc::split(line_params, kSdpDelimiterSemicolonChar)) { std::string name; std::string value; - if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) { + if (!ParseFmtpParam(absl::StripAsciiWhitespace(param), &name, &value, + error)) { return false; } if (codec_params.find(name) != codec_params.end()) { diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc index a2a884a460..ea1b07d06a 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -11,10 +11,10 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -22,21 +22,26 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "api/array_view.h" #include "api/crypto_params.h" #include "api/jsep_session_description.h" #include "api/media_types.h" #include "api/rtp_parameters.h" -#include "api/rtp_transceiver_interface.h" +#include "api/rtp_transceiver_direction.h" #include "media/base/codec.h" #include "media/base/media_constants.h" +#include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/media_session.h" #include "pc/session_description.h" +#include "pc/simulcast_description.h" #include "rtc_base/checks.h" #include "rtc_base/message_digest.h" #include "rtc_base/socket_address.h" @@ -966,7 +971,7 @@ static void ReplaceDirection(RtpTransceiverDirection direction, break; case RtpTransceiverDirection::kStopped: default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); new_direction = "a=sendrecv"; break; } @@ -1602,7 +1607,7 @@ class WebRtcSdpTest : public ::testing::Test { } else if (mline_index == 1) { content_name = kVideoContentName; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } TransportInfo transport_info(content_name, TransportDescription(ufrag, pwd)); @@ -4694,3 +4699,15 @@ TEST_F(WebRtcSdpTest, IllegalMidCharacterValue) { Replace("a=mid:", "a=mid:[]", &sdp); ExpectParseFailure(std::string(sdp), "a=mid:[]"); } + +TEST_F(WebRtcSdpTest, MaxChannels) { + std::string sdp = + "v=0\r\n" + "o=- 11 22 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=audio 49232 RTP/AVP 108\r\n" + "a=rtpmap:108 ISAC/16000/512\r\n"; + + ExpectParseFailure(sdp, "a=rtpmap:108 ISAC/16000/512"); +} diff --git a/pc/webrtc_session_description_factory.cc b/pc/webrtc_session_description_factory.cc index 995ef5e780..09f2ee2f00 100644 --- a/pc/webrtc_session_description_factory.cc +++ b/pc/webrtc_session_description_factory.cc @@ -11,8 +11,8 @@ #include "pc/webrtc_session_description_factory.h" #include -#include -#include + +#include #include #include #include @@ -23,6 +23,8 @@ #include "api/jsep.h" #include "api/jsep_session_description.h" #include "api/rtc_error.h" +#include "api/sequence_checker.h" +#include "pc/connection_context.h" #include "pc/sdp_state_provider.h" #include "pc/session_description.h" #include "rtc_base/checks.h" @@ -32,6 +34,7 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" +#include "rtc_base/unique_id_generator.h" using cricket::MediaSessionOptions; using rtc::UniqueRandomIdGenerator; @@ -125,20 +128,18 @@ void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( } WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, + ConnectionContext* context, const SdpStateProvider* sdp_info, const std::string& session_id, bool dtls_enabled, std::unique_ptr cert_generator, const rtc::scoped_refptr& certificate, - UniqueRandomIdGenerator* ssrc_generator, std::function&)> on_certificate_ready) - : signaling_thread_(signaling_thread), - session_desc_factory_(channel_manager, - &transport_desc_factory_, - ssrc_generator), + : signaling_thread_(context->signaling_thread()), + transport_desc_factory_(context->trials()), + session_desc_factory_(context->channel_manager(), + &transport_desc_factory_), // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp // as the session id and session version. To simplify, it should be fine // to just use a random number as session id and start version from @@ -324,7 +325,7 @@ void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) { break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } diff --git a/pc/webrtc_session_description_factory.h b/pc/webrtc_session_description_factory.h index 8e80fb556d..79171f797b 100644 --- a/pc/webrtc_session_description_factory.h +++ b/pc/webrtc_session_description_factory.h @@ -26,7 +26,6 @@ #include "pc/channel_manager.h" #include "pc/media_session.h" #include "pc/sdp_state_provider.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/message_handler.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/rtc_certificate_generator.h" @@ -80,18 +79,21 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler, // asynchronously. If a certificate is given, will use that for identifying // over DTLS. If neither is specified, DTLS is disabled. WebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, + ConnectionContext* context, const SdpStateProvider* sdp_info, const std::string& session_id, bool dtls_enabled, std::unique_ptr cert_generator, const rtc::scoped_refptr& certificate, - rtc::UniqueRandomIdGenerator* ssrc_generator, std::function&)> on_certificate_ready); virtual ~WebRtcSessionDescriptionFactory(); + WebRtcSessionDescriptionFactory(const WebRtcSessionDescriptionFactory&) = + delete; + WebRtcSessionDescriptionFactory& operator=( + const WebRtcSessionDescriptionFactory&) = delete; + static void CopyCandidatesFromSessionDescription( const SessionDescriptionInterface* source_desc, const std::string& content_name, @@ -159,8 +161,6 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler, std::function&)> on_certificate_ready_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSessionDescriptionFactory); }; } // namespace webrtc diff --git a/presubmit_test.py b/presubmit_test.py index e7879f99f7..b6951bf92f 100755 --- a/presubmit_test.py +++ b/presubmit_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env vpython3 # Copyright 2017 The WebRTC project authors. All Rights Reserved. # @@ -8,6 +8,7 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. +from __future__ import absolute_import import os import shutil import tempfile @@ -20,145 +21,145 @@ from presubmit_test_mocks import MockInputApi, MockOutputApi, MockFile, MockChan class CheckBugEntryFieldTest(unittest.TestCase): - def testCommitMessageBugEntryWithNoError(self): - mock_input_api = MockInputApi() - mock_output_api = MockOutputApi() - mock_input_api.change = MockChange([], ['webrtc:1234']) - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(0, len(errors)) + def testCommitMessageBugEntryWithNoError(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + mock_input_api.change = MockChange([], ['webrtc:1234']) + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(0, len(errors)) - def testCommitMessageBugEntryReturnError(self): - mock_input_api = MockInputApi() - mock_output_api = MockOutputApi() - mock_input_api.change = MockChange([], ['webrtc:1234', 'webrtc=4321']) - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(1, len(errors)) - self.assertEqual(('Bogus Bug entry: webrtc=4321. Please specify' - ' the issue tracker prefix and the issue number,' - ' separated by a colon, e.g. webrtc:123 or' - ' chromium:12345.'), str(errors[0])) + def testCommitMessageBugEntryReturnError(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + mock_input_api.change = MockChange([], ['webrtc:1234', 'webrtc=4321']) + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(1, len(errors)) + self.assertEqual(('Bogus Bug entry: webrtc=4321. Please specify' + ' the issue tracker prefix and the issue number,' + ' separated by a colon, e.g. webrtc:123 or' + ' chromium:12345.'), str(errors[0])) - def testCommitMessageBugEntryWithoutPrefix(self): - mock_input_api = MockInputApi() - mock_output_api = MockOutputApi() - mock_input_api.change = MockChange([], ['1234']) - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(1, len(errors)) - self.assertEqual(('Bug entry requires issue tracker prefix, ' - 'e.g. webrtc:1234'), str(errors[0])) + def testCommitMessageBugEntryWithoutPrefix(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + mock_input_api.change = MockChange([], ['1234']) + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(1, len(errors)) + self.assertEqual(('Bug entry requires issue tracker prefix, ' + 'e.g. webrtc:1234'), str(errors[0])) - def testCommitMessageBugEntryIsNone(self): - mock_input_api = MockInputApi() - mock_output_api = MockOutputApi() - mock_input_api.change = MockChange([], ['None']) - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(0, len(errors)) + def testCommitMessageBugEntryIsNone(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + mock_input_api.change = MockChange([], ['None']) + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(0, len(errors)) - def testCommitMessageBugEntrySupportInternalBugReference(self): - mock_input_api = MockInputApi() - mock_output_api = MockOutputApi() - mock_input_api.change.BUG = 'b/12345' - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(0, len(errors)) - mock_input_api.change.BUG = 'b/12345, webrtc:1234' - errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, - mock_output_api) - self.assertEqual(0, len(errors)) + def testCommitMessageBugEntrySupportInternalBugReference(self): + mock_input_api = MockInputApi() + mock_output_api = MockOutputApi() + mock_input_api.change.BUG = 'b/12345' + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(0, len(errors)) + mock_input_api.change.BUG = 'b/12345, webrtc:1234' + errors = PRESUBMIT.CheckCommitMessageBugEntry(mock_input_api, + mock_output_api) + self.assertEqual(0, len(errors)) class CheckNewlineAtTheEndOfProtoFilesTest(unittest.TestCase): - def setUp(self): - self.tmp_dir = tempfile.mkdtemp() - self.proto_file_path = os.path.join(self.tmp_dir, 'foo.proto') - self.input_api = MockInputApi() - self.output_api = MockOutputApi() + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + self.proto_file_path = os.path.join(self.tmp_dir, 'foo.proto') + self.input_api = MockInputApi() + self.output_api = MockOutputApi() - def tearDown(self): - shutil.rmtree(self.tmp_dir, ignore_errors=True) + def tearDown(self): + shutil.rmtree(self.tmp_dir, ignore_errors=True) - def testErrorIfProtoFileDoesNotEndWithNewline(self): - self._GenerateProtoWithoutNewlineAtTheEnd() - self.input_api.files = [MockFile(self.proto_file_path)] - errors = PRESUBMIT.CheckNewlineAtTheEndOfProtoFiles( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(1, len(errors)) - self.assertEqual( - 'File %s must end with exactly one newline.' % - self.proto_file_path, str(errors[0])) + def testErrorIfProtoFileDoesNotEndWithNewline(self): + self._GenerateProtoWithoutNewlineAtTheEnd() + self.input_api.files = [MockFile(self.proto_file_path)] + errors = PRESUBMIT.CheckNewlineAtTheEndOfProtoFiles( + self.input_api, self.output_api, lambda x: True) + self.assertEqual(1, len(errors)) + self.assertEqual( + 'File %s must end with exactly one newline.' % self.proto_file_path, + str(errors[0])) - def testNoErrorIfProtoFileEndsWithNewline(self): - self._GenerateProtoWithNewlineAtTheEnd() - self.input_api.files = [MockFile(self.proto_file_path)] - errors = PRESUBMIT.CheckNewlineAtTheEndOfProtoFiles( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(0, len(errors)) + def testNoErrorIfProtoFileEndsWithNewline(self): + self._GenerateProtoWithNewlineAtTheEnd() + self.input_api.files = [MockFile(self.proto_file_path)] + errors = PRESUBMIT.CheckNewlineAtTheEndOfProtoFiles( + self.input_api, self.output_api, lambda x: True) + self.assertEqual(0, len(errors)) - def _GenerateProtoWithNewlineAtTheEnd(self): - with open(self.proto_file_path, 'w') as f: - f.write( - textwrap.dedent(""" + def _GenerateProtoWithNewlineAtTheEnd(self): + with open(self.proto_file_path, 'w') as f: + f.write( + textwrap.dedent(""" syntax = "proto2"; option optimize_for = LITE_RUNTIME; package webrtc.audioproc; """)) - def _GenerateProtoWithoutNewlineAtTheEnd(self): - with open(self.proto_file_path, 'w') as f: - f.write( - textwrap.dedent(""" + def _GenerateProtoWithoutNewlineAtTheEnd(self): + with open(self.proto_file_path, 'w') as f: + f.write( + textwrap.dedent(""" syntax = "proto2"; option optimize_for = LITE_RUNTIME; package webrtc.audioproc;""")) class CheckNoMixingSourcesTest(unittest.TestCase): - def setUp(self): - self.tmp_dir = tempfile.mkdtemp() - self.file_path = os.path.join(self.tmp_dir, 'BUILD.gn') - self.input_api = MockInputApi() - self.output_api = MockOutputApi() + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + self.file_path = os.path.join(self.tmp_dir, 'BUILD.gn') + self.input_api = MockInputApi() + self.output_api = MockOutputApi() - def tearDown(self): - shutil.rmtree(self.tmp_dir, ignore_errors=True) + def tearDown(self): + shutil.rmtree(self.tmp_dir, ignore_errors=True) - def testErrorIfCAndCppAreMixed(self): - self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.cc', 'bar.h']) + def testErrorIfCAndCppAreMixed(self): + self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.cc', 'bar.h']) - def testErrorIfCAndObjCAreMixed(self): - self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.m', 'bar.h']) + def testErrorIfCAndObjCAreMixed(self): + self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.m', 'bar.h']) - def testErrorIfCAndObjCppAreMixed(self): - self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.mm', 'bar.h']) + def testErrorIfCAndObjCppAreMixed(self): + self._AssertNumberOfErrorsWithSources(1, ['foo.c', 'bar.mm', 'bar.h']) - def testErrorIfCppAndObjCAreMixed(self): - self._AssertNumberOfErrorsWithSources(1, ['foo.cc', 'bar.m', 'bar.h']) + def testErrorIfCppAndObjCAreMixed(self): + self._AssertNumberOfErrorsWithSources(1, ['foo.cc', 'bar.m', 'bar.h']) - def testErrorIfCppAndObjCppAreMixed(self): - self._AssertNumberOfErrorsWithSources(1, ['foo.cc', 'bar.mm', 'bar.h']) + def testErrorIfCppAndObjCppAreMixed(self): + self._AssertNumberOfErrorsWithSources(1, ['foo.cc', 'bar.mm', 'bar.h']) - def testNoErrorIfOnlyC(self): - self._AssertNumberOfErrorsWithSources(0, ['foo.c', 'bar.c', 'bar.h']) + def testNoErrorIfOnlyC(self): + self._AssertNumberOfErrorsWithSources(0, ['foo.c', 'bar.c', 'bar.h']) - def testNoErrorIfOnlyCpp(self): - self._AssertNumberOfErrorsWithSources(0, ['foo.cc', 'bar.cc', 'bar.h']) + def testNoErrorIfOnlyCpp(self): + self._AssertNumberOfErrorsWithSources(0, ['foo.cc', 'bar.cc', 'bar.h']) - def testNoErrorIfOnlyObjC(self): - self._AssertNumberOfErrorsWithSources(0, ['foo.m', 'bar.m', 'bar.h']) + def testNoErrorIfOnlyObjC(self): + self._AssertNumberOfErrorsWithSources(0, ['foo.m', 'bar.m', 'bar.h']) - def testNoErrorIfOnlyObjCpp(self): - self._AssertNumberOfErrorsWithSources(0, ['foo.mm', 'bar.mm', 'bar.h']) + def testNoErrorIfOnlyObjCpp(self): + self._AssertNumberOfErrorsWithSources(0, ['foo.mm', 'bar.mm', 'bar.h']) - def testNoErrorIfObjCAndObjCppAreMixed(self): - self._AssertNumberOfErrorsWithSources(0, ['foo.m', 'bar.mm', 'bar.h']) + def testNoErrorIfObjCAndObjCppAreMixed(self): + self._AssertNumberOfErrorsWithSources(0, ['foo.m', 'bar.mm', 'bar.h']) - def testNoErrorIfSourcesAreInExclusiveIfBranches(self): - self._GenerateBuildFile( - textwrap.dedent(""" + def testNoErrorIfSourcesAreInExclusiveIfBranches(self): + self._GenerateBuildFile( + textwrap.dedent(""" rtc_library("bar_foo") { if (is_win) { sources = [ @@ -184,15 +185,15 @@ class CheckNoMixingSourcesTest(unittest.TestCase): } } """)) - self.input_api.files = [MockFile(self.file_path)] - errors = PRESUBMIT.CheckNoMixingSources(self.input_api, - [MockFile(self.file_path)], - self.output_api) - self.assertEqual(0, len(errors)) + self.input_api.files = [MockFile(self.file_path)] + errors = PRESUBMIT.CheckNoMixingSources(self.input_api, + [MockFile(self.file_path)], + self.output_api) + self.assertEqual(0, len(errors)) - def testErrorIfSourcesAreNotInExclusiveIfBranches(self): - self._GenerateBuildFile( - textwrap.dedent(""" + def testErrorIfSourcesAreNotInExclusiveIfBranches(self): + self._GenerateBuildFile( + textwrap.dedent(""" rtc_library("bar_foo") { if (is_win) { sources = [ @@ -224,23 +225,22 @@ class CheckNoMixingSourcesTest(unittest.TestCase): } } """)) - self.input_api.files = [MockFile(self.file_path)] - errors = PRESUBMIT.CheckNoMixingSources(self.input_api, - [MockFile(self.file_path)], - self.output_api) - self.assertEqual(1, len(errors)) - self.assertTrue('bar.cc' in str(errors[0])) - self.assertTrue('bar.mm' in str(errors[0])) - self.assertTrue('foo.cc' in str(errors[0])) - self.assertTrue('foo.mm' in str(errors[0])) - self.assertTrue('bar.m' in str(errors[0])) - self.assertTrue('bar.c' in str(errors[0])) + self.input_api.files = [MockFile(self.file_path)] + errors = PRESUBMIT.CheckNoMixingSources(self.input_api, + [MockFile(self.file_path)], + self.output_api) + self.assertEqual(1, len(errors)) + self.assertTrue('bar.cc' in str(errors[0])) + self.assertTrue('bar.mm' in str(errors[0])) + self.assertTrue('foo.cc' in str(errors[0])) + self.assertTrue('foo.mm' in str(errors[0])) + self.assertTrue('bar.m' in str(errors[0])) + self.assertTrue('bar.c' in str(errors[0])) - def _AssertNumberOfErrorsWithSources(self, number_of_errors, sources): - assert len( - sources) == 3, 'This function accepts a list of 3 source files' - self._GenerateBuildFile( - textwrap.dedent(""" + def _AssertNumberOfErrorsWithSources(self, number_of_errors, sources): + assert len(sources) == 3, 'This function accepts a list of 3 source files' + self._GenerateBuildFile( + textwrap.dedent(""" rtc_static_library("bar_foo") { sources = [ "%s", @@ -256,84 +256,76 @@ class CheckNoMixingSourcesTest(unittest.TestCase): ], } """ % (tuple(sources) * 2))) - self.input_api.files = [MockFile(self.file_path)] - errors = PRESUBMIT.CheckNoMixingSources(self.input_api, - [MockFile(self.file_path)], - self.output_api) - self.assertEqual(number_of_errors, len(errors)) - if number_of_errors == 1: - for source in sources: - if not source.endswith('.h'): - self.assertTrue(source in str(errors[0])) + self.input_api.files = [MockFile(self.file_path)] + errors = PRESUBMIT.CheckNoMixingSources(self.input_api, + [MockFile(self.file_path)], + self.output_api) + self.assertEqual(number_of_errors, len(errors)) + if number_of_errors == 1: + for source in sources: + if not source.endswith('.h'): + self.assertTrue(source in str(errors[0])) - def _GenerateBuildFile(self, content): - with open(self.file_path, 'w') as f: - f.write(content) + def _GenerateBuildFile(self, content): + with open(self.file_path, 'w') as f: + f.write(content) class CheckAssertUsageTest(unittest.TestCase): - def setUp(self): - self.input_api = MockInputApi() - self.output_api = MockOutputApi() - self._content_with_assert = [ - 'void Foo() {', - ' assert(true);', - '}' - ] - self._content_without_assert = [ - 'void Foo() {', - ' RTC_CHECK(true);', - '}' - ] + def setUp(self): + self.input_api = MockInputApi() + self.output_api = MockOutputApi() + self._content_with_assert = ['void Foo() {', ' assert(true);', '}'] + self._content_without_assert = ['void Foo() {', ' RTC_CHECK(true);', '}'] - def testDetectsAssertInCcFile(self): - self.input_api.files = [ - MockFile('with_assert.cc', self._content_with_assert), - MockFile('without_assert.cc', self._content_without_assert), - ] - errors = PRESUBMIT.CheckAssertUsage( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(1, len(errors)) - self.assertEqual('with_assert.cc', errors[0].items[0]) + def testDetectsAssertInCcFile(self): + self.input_api.files = [ + MockFile('with_assert.cc', self._content_with_assert), + MockFile('without_assert.cc', self._content_without_assert), + ] + errors = PRESUBMIT.CheckAssertUsage(self.input_api, + self.output_api, lambda x: True) + self.assertEqual(1, len(errors)) + self.assertEqual('with_assert.cc', errors[0].items[0]) - def testDetectsAssertInHeaderFile(self): - self.input_api.files = [ - MockFile('with_assert.h', self._content_with_assert), - MockFile('without_assert.h', self._content_without_assert), - ] - errors = PRESUBMIT.CheckAssertUsage( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(1, len(errors)) - self.assertEqual('with_assert.h', errors[0].items[0]) + def testDetectsAssertInHeaderFile(self): + self.input_api.files = [ + MockFile('with_assert.h', self._content_with_assert), + MockFile('without_assert.h', self._content_without_assert), + ] + errors = PRESUBMIT.CheckAssertUsage(self.input_api, + self.output_api, lambda x: True) + self.assertEqual(1, len(errors)) + self.assertEqual('with_assert.h', errors[0].items[0]) - def testDetectsAssertInObjCFile(self): - self.input_api.files = [ - MockFile('with_assert.m', self._content_with_assert), - MockFile('without_assert.m', self._content_without_assert), - ] - errors = PRESUBMIT.CheckAssertUsage( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(1, len(errors)) - self.assertEqual('with_assert.m', errors[0].items[0]) + def testDetectsAssertInObjCFile(self): + self.input_api.files = [ + MockFile('with_assert.m', self._content_with_assert), + MockFile('without_assert.m', self._content_without_assert), + ] + errors = PRESUBMIT.CheckAssertUsage(self.input_api, + self.output_api, lambda x: True) + self.assertEqual(1, len(errors)) + self.assertEqual('with_assert.m', errors[0].items[0]) - def testDetectsAssertInObjCppFile(self): - self.input_api.files = [ - MockFile('with_assert.mm', self._content_with_assert), - MockFile('without_assert.mm', self._content_without_assert), - ] - errors = PRESUBMIT.CheckAssertUsage( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(1, len(errors)) - self.assertEqual('with_assert.mm', errors[0].items[0]) + def testDetectsAssertInObjCppFile(self): + self.input_api.files = [ + MockFile('with_assert.mm', self._content_with_assert), + MockFile('without_assert.mm', self._content_without_assert), + ] + errors = PRESUBMIT.CheckAssertUsage(self.input_api, + self.output_api, lambda x: True) + self.assertEqual(1, len(errors)) + self.assertEqual('with_assert.mm', errors[0].items[0]) - def testDoesntDetectAssertInOtherFiles(self): - self.input_api.files = [ - MockFile('with_assert.cpp', self._content_with_assert), - ] - errors = PRESUBMIT.CheckAssertUsage( - self.input_api, self.output_api, lambda x: True) - self.assertEqual(0, len(errors)) + def testDoesntDetectAssertInOtherFiles(self): + self.input_api.files = [ + MockFile('with_assert.cpp', self._content_with_assert), + ] + errors = PRESUBMIT.CheckAssertUsage(self.input_api, + self.output_api, lambda x: True) + self.assertEqual(0, len(errors)) if __name__ == '__main__': - unittest.main() + unittest.main() diff --git a/presubmit_test_mocks.py b/presubmit_test_mocks.py index 4ed7947530..015f6e7e15 100644 --- a/presubmit_test_mocks.py +++ b/presubmit_test_mocks.py @@ -1,3 +1,5 @@ +#!/usr/bin/env vpython3 + # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. # # Use of this source code is governed by a BSD-style license @@ -9,135 +11,131 @@ # This file is inspired to [1]. # [1] - https://cs.chromium.org/chromium/src/PRESUBMIT_test_mocks.py +from __future__ import absolute_import import os.path import re -class MockInputApi(object): - """Mock class for the InputApi class. +class MockInputApi: + """Mock class for the InputApi class. This class can be used for unittests for presubmit by initializing the files attribute as the list of changed files. """ - def __init__(self): - self.change = MockChange([], []) - self.files = [] - self.presubmit_local_path = os.path.dirname(__file__) - self.re = re # pylint: disable=invalid-name + def __init__(self): + self.change = MockChange([], []) + self.files = [] + self.presubmit_local_path = os.path.dirname(__file__) + self.re = re # pylint: disable=invalid-name - def AffectedSourceFiles(self, file_filter=None): - return self.AffectedFiles(file_filter=file_filter) + def AffectedSourceFiles(self, file_filter=None): + return self.AffectedFiles(file_filter=file_filter) - def AffectedFiles(self, file_filter=None, include_deletes=False): - for f in self.files: - if file_filter and not file_filter(f): - continue - if not include_deletes and f.Action() == 'D': - continue - yield f + def AffectedFiles(self, file_filter=None, include_deletes=False): + for f in self.files: + if file_filter and not file_filter(f): + continue + if not include_deletes and f.Action() == 'D': + continue + yield f - @classmethod - def FilterSourceFile(cls, - affected_file, - files_to_check=(), - files_to_skip=()): - # pylint: disable=unused-argument - return True + @classmethod + def FilterSourceFile(cls, affected_file, files_to_check=(), files_to_skip=()): + # pylint: disable=unused-argument + return True - def PresubmitLocalPath(self): - return self.presubmit_local_path + def PresubmitLocalPath(self): + return self.presubmit_local_path - def ReadFile(self, affected_file, mode='rU'): - filename = affected_file.AbsoluteLocalPath() - for f in self.files: - if f.LocalPath() == filename: - with open(filename, mode) as f: - return f.read() - # Otherwise, file is not in our mock API. - raise IOError, "No such file or directory: '%s'" % filename + def ReadFile(self, affected_file, mode='r'): + filename = affected_file.AbsoluteLocalPath() + for f in self.files: + if f.LocalPath() == filename: + with open(filename, mode) as f: + return f.read() + # Otherwise, file is not in our mock API. + raise IOError("No such file or directory: '%s'" % filename) -class MockOutputApi(object): - """Mock class for the OutputApi class. +class MockOutputApi: + """Mock class for the OutputApi class. An instance of this class can be passed to presubmit unittests for outputing various types of results. """ - class PresubmitResult(object): - def __init__(self, message, items=None, long_text=''): - self.message = message - self.items = items - self.long_text = long_text + class PresubmitResult: + def __init__(self, message, items=None, long_text=''): + self.message = message + self.items = items + self.long_text = long_text - def __repr__(self): - return self.message + def __repr__(self): + return self.message - class PresubmitError(PresubmitResult): - def __init__(self, message, items=None, long_text=''): - MockOutputApi.PresubmitResult.__init__(self, message, items, - long_text) - self.type = 'error' + class PresubmitError(PresubmitResult): + def __init__(self, message, items=None, long_text=''): + MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) + self.type = 'error' -class MockChange(object): - """Mock class for Change class. +class MockChange: + """Mock class for Change class. This class can be used in presubmit unittests to mock the query of the current change. """ - def __init__(self, changed_files, bugs_from_description, tags=None): - self._changed_files = changed_files - self._bugs_from_description = bugs_from_description - self.tags = dict() if not tags else tags + def __init__(self, changed_files, bugs_from_description, tags=None): + self._changed_files = changed_files + self._bugs_from_description = bugs_from_description + self.tags = dict() if not tags else tags - def BugsFromDescription(self): - return self._bugs_from_description + def BugsFromDescription(self): + return self._bugs_from_description - def __getattr__(self, attr): - """Return tags directly as attributes on the object.""" - if not re.match(r"^[A-Z_]*$", attr): - raise AttributeError(self, attr) - return self.tags.get(attr) + def __getattr__(self, attr): + """Return tags directly as attributes on the object.""" + if not re.match(r"^[A-Z_]*$", attr): + raise AttributeError(self, attr) + return self.tags.get(attr) -class MockFile(object): - """Mock class for the File class. +class MockFile: + """Mock class for the File class. This class can be used to form the mock list of changed files in MockInputApi for presubmit unittests. """ - def __init__(self, - local_path, - new_contents=None, - old_contents=None, - action='A'): - if new_contents is None: - new_contents = ["Data"] - self._local_path = local_path - self._new_contents = new_contents - self._changed_contents = [(i + 1, l) - for i, l in enumerate(new_contents)] - self._action = action - self._old_contents = old_contents + def __init__(self, + local_path, + new_contents=None, + old_contents=None, + action='A'): + if new_contents is None: + new_contents = ["Data"] + self._local_path = local_path + self._new_contents = new_contents + self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)] + self._action = action + self._old_contents = old_contents - def Action(self): - return self._action + def Action(self): + return self._action - def ChangedContents(self): - return self._changed_contents + def ChangedContents(self): + return self._changed_contents - def NewContents(self): - return self._new_contents + def NewContents(self): + return self._new_contents - def LocalPath(self): - return self._local_path + def LocalPath(self): + return self._local_path - def AbsoluteLocalPath(self): - return self._local_path + def AbsoluteLocalPath(self): + return self._local_path - def OldContents(self): - return self._old_contents + def OldContents(self): + return self._old_contents diff --git a/pylintrc b/pylintrc index f26c84adce..852445a1ce 100644 --- a/pylintrc +++ b/pylintrc @@ -28,6 +28,7 @@ disable= exec-used, fixme, import-error, + import-outside-toplevel, missing-docstring, no-init, no-member, @@ -97,6 +98,9 @@ max-line-length=80 # Maximum number of lines in a module max-module-lines=1000 +# We use two spaces for indents, instead of the usual four spaces or tab. +indent-string=' ' + [BASIC] @@ -192,10 +196,6 @@ max-public-methods=20 [CLASSES] -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp diff --git a/resources/audio_processing/output_data_float.pb.sha1 b/resources/audio_processing/output_data_float.pb.sha1 index d3375949ac..85e628cb76 100644 --- a/resources/audio_processing/output_data_float.pb.sha1 +++ b/resources/audio_processing/output_data_float.pb.sha1 @@ -1 +1 @@ -749efdfd1e3c3ace434b3673dac9ce4938534449 \ No newline at end of file +d7dadc14736be65465a79054ce29413cd4cc1ccd \ No newline at end of file diff --git a/resources/audio_processing/output_data_float_avx2.pb.sha1 b/resources/audio_processing/output_data_float_avx2.pb.sha1 index 79a95efc0e..63f9887265 100644 --- a/resources/audio_processing/output_data_float_avx2.pb.sha1 +++ b/resources/audio_processing/output_data_float_avx2.pb.sha1 @@ -1 +1 @@ -78c1a84de332173863c997538aa19b8cdcba5020 \ No newline at end of file +40df0b4e636bb59fe258b93f8aab7fd2d3f6440d \ No newline at end of file diff --git a/resources/speech_and_misc_wb.pcm.sha1 b/resources/speech_and_misc_wb.pcm.sha1 deleted file mode 100644 index 47ae524440..0000000000 --- a/resources/speech_and_misc_wb.pcm.sha1 +++ /dev/null @@ -1 +0,0 @@ -0f0326ac7689cb62fb515402bb5e7248ab4ebdcb \ No newline at end of file diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 6cc6be1212..e09b62f936 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -6,8 +6,6 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/crypto.gni") -import("//build/config/ui.gni") import("//third_party/google_benchmark/buildconfig.gni") import("../webrtc.gni") @@ -16,10 +14,6 @@ if (is_android) { import("//build/config/android/rules.gni") } -config("threading_chromium_config") { - defines = [ "NO_MAIN_THREAD_WRAPPING" ] -} - if (!rtc_build_ssl) { config("external_ssl_library") { assert(rtc_ssl_root != "", @@ -102,6 +96,7 @@ rtc_library("rtc_base_approved") { absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/numeric:bits", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] public_deps = [] # no-presubmit-check TODO(webrtc:8603) @@ -136,6 +131,7 @@ rtc_library("rtc_base_approved") { "rate_statistics.h", "rate_tracker.cc", "rate_tracker.h", + "strong_alias.h", "swap_queue.h", "timestamp_aligner.cc", "timestamp_aligner.h", @@ -193,7 +189,6 @@ rtc_library("rtc_base_approved") { rtc_source_set("macromagic") { sources = [ "arraysize.h", - "constructor_magic.h", "format_macros.h", "thread_annotations.h", ] @@ -290,6 +285,10 @@ rtc_library("rtc_event") { } } +config("chromium_logging_config") { + defines = [ "LOGGING_INSIDE_WEBRTC" ] +} + rtc_library("logging") { visibility = [ "*" ] libs = [] @@ -314,11 +313,14 @@ rtc_library("logging") { "../../webrtc_overrides/rtc_base/logging.cc", "../../webrtc_overrides/rtc_base/logging.h", ] + + # This macro needs to be both present in all WebRTC targets (see its + # definition in //BUILD.gn but also propagated to all the targets + # depending on the Chromium component defined in + # //third_party/webrtc_overrides:webrtc_component. This public_config + # allows GN to propagate the macro accordingly. + public_configs = [ ":chromium_logging_config" ] } else { - configs += [ - "..:no_exit_time_destructors", - "..:no_global_constructors", - ] sources = [ "logging.cc", "logging.h", @@ -504,6 +506,7 @@ rtc_source_set("rtc_operations_chain") { ":checks", ":macromagic", ":refcount", + "../api:refcountedbase", "../api:scoped_refptr", "../api:sequence_checker", "system:no_unique_address", @@ -681,6 +684,7 @@ rtc_library("rtc_json") { # expected to be when building json outside of the standalone build. defines += [ "WEBRTC_EXTERNAL_JSON" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("net_helpers") { @@ -693,7 +697,10 @@ rtc_library("net_helpers") { deps += [ ":ifaddrs_android" ] } if (is_win) { - deps += [ ":win32" ] + deps += [ + ":rtc_base_approved", + ":win32", + ] } } @@ -725,6 +732,7 @@ rtc_library("ip_address") { if (is_win) { deps += [ ":win32" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("socket_address") { @@ -746,6 +754,7 @@ rtc_library("socket_address") { if (is_win) { deps += [ ":win32" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("null_socket_server") { @@ -770,10 +779,6 @@ rtc_source_set("socket_server") { rtc_library("threading") { visibility = [ "*" ] - if (build_with_chromium) { - public_configs = [ ":threading_chromium_config" ] - } - sources = [ "async_resolver.cc", "async_resolver.h", @@ -791,7 +796,11 @@ rtc_library("threading") { "thread.h", "thread_message.h", ] - absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/strings", + ] deps = [ ":async_resolver_interface", ":atomicops", @@ -884,6 +893,7 @@ if (is_android) { "log", "GLESv2", ] + absl_deps = [ "//third_party/abseil-cpp/absl/cleanup" ] } } @@ -1065,12 +1075,6 @@ rtc_library("rtc_base") { if (is_win) { sources += [ "win32_socket_init.h" ] - if (current_os != "winuwp") { - sources += [ - "win32_socket_server.cc", - "win32_socket_server.h", - ] - } } } # !build_with_chromium @@ -1234,6 +1238,7 @@ rtc_library("rtc_base_tests_utils") { absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", ] } @@ -1327,10 +1332,10 @@ if (rtc_include_tests) { "third_party/sigslot", "//testing/gtest", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] - if (is_win) { - sources += [ "win32_socket_server_unittest.cc" ] - } + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] } rtc_library("rtc_base_approved_unittests") { @@ -1371,6 +1376,7 @@ if (rtc_include_tests) { "string_utils_unittest.cc", "strings/string_builder_unittest.cc", "strings/string_format_unittest.cc", + "strong_alias_unittest.cc", "swap_queue_unittest.cc", "thread_annotations_unittest.cc", "time_utils_unittest.cc", @@ -1413,6 +1419,7 @@ if (rtc_include_tests) { "../test:fileutils", "../test:test_main", "../test:test_support", + "containers:flat_map", "containers:unittests", "memory:unittests", "synchronization:mutex", @@ -1424,6 +1431,7 @@ if (rtc_include_tests) { "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/numeric:bits", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } diff --git a/rtc_base/async_invoker.cc b/rtc_base/async_invoker.cc index 4cf0aca78d..61c153499f 100644 --- a/rtc_base/async_invoker.cc +++ b/rtc_base/async_invoker.cc @@ -46,28 +46,6 @@ void DEPRECATED_AsyncInvoker::OnMessage(Message* msg) { delete data; } -void DEPRECATED_AsyncInvoker::Flush(Thread* thread, - uint32_t id /*= MQID_ANY*/) { - // If the destructor is waiting for invocations to finish, don't start - // running even more tasks. - if (destroying_.load(std::memory_order_relaxed)) - return; - - // Run this on `thread` to reduce the number of context switches. - if (Thread::Current() != thread) { - thread->Invoke(RTC_FROM_HERE, - [this, thread, id] { Flush(thread, id); }); - return; - } - - MessageList removed; - thread->Clear(this, id, &removed); - for (MessageList::iterator it = removed.begin(); it != removed.end(); ++it) { - // This message was pending on this thread, so run it now. - thread->Send(it->posted_from, it->phandler, it->message_id, it->pdata); - } -} - void DEPRECATED_AsyncInvoker::Clear() { ThreadManager::Clear(this); } diff --git a/rtc_base/async_invoker.h b/rtc_base/async_invoker.h index 20b24f3314..c1f2060134 100644 --- a/rtc_base/async_invoker.h +++ b/rtc_base/async_invoker.h @@ -18,7 +18,6 @@ #include "absl/base/attributes.h" #include "api/scoped_refptr.h" #include "rtc_base/async_invoker_inl.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/event.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -26,6 +25,8 @@ namespace rtc { +// DEPRECATED - do not use. +// // Invokes function objects (aka functors) asynchronously on a Thread, and // owns the lifetime of calls (ie, when this object is destroyed, calls in // flight are cancelled). AsyncInvoker can optionally execute a user-specified @@ -92,6 +93,9 @@ class DEPRECATED_AsyncInvoker : public MessageHandlerAutoCleanup { DEPRECATED_AsyncInvoker(); ~DEPRECATED_AsyncInvoker() override; + DEPRECATED_AsyncInvoker(const DEPRECATED_AsyncInvoker&) = delete; + DEPRECATED_AsyncInvoker& operator=(const DEPRECATED_AsyncInvoker&) = delete; + // Call `functor` asynchronously on `thread`, with no callback upon // completion. Returns immediately. template @@ -119,13 +123,6 @@ class DEPRECATED_AsyncInvoker : public MessageHandlerAutoCleanup { DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id); } - // Synchronously execute on `thread` all outstanding calls we own - // that are pending on `thread`, and wait for calls to complete - // before returning. Optionally filter by message id. - // The destructor will not wait for outstanding calls, so if that - // behavior is desired, call Flush() before destroying this object. - void Flush(Thread* thread, uint32_t id = MQID_ANY); - // Cancels any outstanding calls we own that are pending on any thread, and // which have not yet started to execute. This does not wait for any calls // that have already started executing to complete. @@ -153,10 +150,9 @@ class DEPRECATED_AsyncInvoker : public MessageHandlerAutoCleanup { // future. std::atomic pending_invocations_; - // Reference counted so that if the AsyncInvoker destructor finishes before - // an AsyncClosure's destructor that's about to call - // "invocation_complete_->Set()", it's not dereferenced after being - // destroyed. + // Reference counted so that if the destructor finishes before an + // AsyncClosure's destructor that's about to call + // "invocation_complete_->Set()", it's not dereferenced after being destroyed. rtc::Ref::Ptr invocation_complete_; // This flag is used to ensure that if an application AsyncInvokes tasks that @@ -165,13 +161,8 @@ class DEPRECATED_AsyncInvoker : public MessageHandlerAutoCleanup { std::atomic destroying_; friend class AsyncClosure; - - RTC_DISALLOW_COPY_AND_ASSIGN(DEPRECATED_AsyncInvoker); }; -using AsyncInvoker ABSL_DEPRECATED("bugs.webrtc.org/12339") = - DEPRECATED_AsyncInvoker; - } // namespace rtc #endif // RTC_BASE_ASYNC_INVOKER_H_ diff --git a/rtc_base/async_invoker_inl.h b/rtc_base/async_invoker_inl.h index 9fb328782c..c2b6413519 100644 --- a/rtc_base/async_invoker_inl.h +++ b/rtc_base/async_invoker_inl.h @@ -23,7 +23,7 @@ namespace rtc { class DEPRECATED_AsyncInvoker; -// Helper class for AsyncInvoker. Runs a task and triggers a callback +// Helper class for DEPRECATED_AsyncInvoker. Runs a task and triggers a callback // on the calling thread if necessary. class AsyncClosure { public: diff --git a/rtc_base/async_packet_socket.h b/rtc_base/async_packet_socket.h index d47d57b692..2e334ec36d 100644 --- a/rtc_base/async_packet_socket.h +++ b/rtc_base/async_packet_socket.h @@ -13,7 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/dscp.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" @@ -69,6 +68,9 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> { AsyncPacketSocket(); ~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; @@ -117,8 +119,7 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> { // 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 and server TCP sockets) or CONNECTING (for client TCP - // sockets). + // (for UDP sockets). sigslot::signal2 SignalAddressReady; // Emitted for client TCP sockets when state is changed from @@ -128,12 +129,24 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> { // Emitted for client TCP sockets when state is changed from // CONNECTED to CLOSED. sigslot::signal2 SignalClose; +}; - // Used only for listening TCP sockets. - sigslot::signal2 SignalNewConnection; +// Listen socket, producing an AsyncPacketSocket when a peer connects. +class RTC_EXPORT AsyncListenSocket : public sigslot::has_slots<> { + public: + enum class State { + kClosed, + kBound, + }; - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncPacketSocket); + // 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, diff --git a/rtc_base/async_resolver.cc b/rtc_base/async_resolver.cc index 75efce5edd..ad1598f214 100644 --- a/rtc_base/async_resolver.cc +++ b/rtc_base/async_resolver.cc @@ -40,13 +40,37 @@ #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/third_party/sigslot/sigslot.h" // for signal_with_thread... +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +#include +#endif + namespace rtc { +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +namespace { + +void GlobalGcdRunTask(void* context) { + std::unique_ptr task( + static_cast(context)); + task->Run(); +} + +// Post a task into the system-defined global concurrent queue. +void PostTaskToGlobalQueue(std::unique_ptr task) { + dispatch_queue_global_t global_queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + webrtc::QueuedTask* context = task.release(); + dispatch_async_f(global_queue, context, &GlobalGcdRunTask); +} + +} // namespace +#endif + int ResolveHostname(const std::string& hostname, int family, std::vector* addresses) { #ifdef __native_client__ - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl"; return -1; #else // __native_client__ @@ -123,7 +147,7 @@ void AsyncResolver::Start(const SocketAddress& addr) { RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK(!destroy_called_); addr_ = addr; - PlatformThread::SpawnDetached( + auto thread_function = [this, addr, caller_task_queue = webrtc::TaskQueueBase::Current(), state = state_] { std::vector addresses; @@ -146,8 +170,12 @@ void AsyncResolver::Start(const SocketAddress& addr) { } })); } - }, - "AsyncResolver"); + }; +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + PostTaskToGlobalQueue(webrtc::ToQueuedTask(std::move(thread_function))); +#else + PlatformThread::SpawnDetached(std::move(thread_function), "AsyncResolver"); +#endif } bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const { diff --git a/rtc_base/async_tcp_socket.cc b/rtc_base/async_tcp_socket.cc index 76efb6dec1..c2480755b4 100644 --- a/rtc_base/async_tcp_socket.cc +++ b/rtc_base/async_tcp_socket.cc @@ -62,16 +62,11 @@ Socket* AsyncTCPSocketBase::ConnectSocket( } AsyncTCPSocketBase::AsyncTCPSocketBase(Socket* socket, - bool listen, size_t max_packet_size) : socket_(socket), - listen_(listen), max_insize_(max_packet_size), max_outsize_(max_packet_size) { - if (!listen_) { - // Listening sockets don't send/receive data, so they don't need buffers. - inbuf_.EnsureCapacity(kMinimumRecvSize); - } + inbuf_.EnsureCapacity(kMinimumRecvSize); RTC_DCHECK(socket_.get() != nullptr); socket_->SignalConnectEvent.connect(this, @@ -79,12 +74,6 @@ AsyncTCPSocketBase::AsyncTCPSocketBase(Socket* socket, socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent); socket_->SignalWriteEvent.connect(this, &AsyncTCPSocketBase::OnWriteEvent); socket_->SignalCloseEvent.connect(this, &AsyncTCPSocketBase::OnCloseEvent); - - if (listen_) { - if (socket_->Listen(kListenBacklog) < 0) { - RTC_LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError(); - } - } } AsyncTCPSocketBase::~AsyncTCPSocketBase() {} @@ -106,15 +95,11 @@ AsyncTCPSocket::State AsyncTCPSocketBase::GetState() const { case Socket::CS_CLOSED: return STATE_CLOSED; case Socket::CS_CONNECTING: - if (listen_) { - return STATE_BOUND; - } else { - return STATE_CONNECTING; - } + return STATE_CONNECTING; case Socket::CS_CONNECTED: return STATE_CONNECTED; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return STATE_CLOSED; } } @@ -149,7 +134,6 @@ int AsyncTCPSocketBase::SendTo(const void* pv, } int AsyncTCPSocketBase::FlushOutBuffer() { - RTC_DCHECK(!listen_); RTC_DCHECK_GT(outbuf_.size(), 0); rtc::ArrayView view = outbuf_; int res; @@ -159,7 +143,7 @@ int AsyncTCPSocketBase::FlushOutBuffer() { break; } if (static_cast(res) > view.size()) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); res = -1; break; } @@ -189,7 +173,6 @@ int AsyncTCPSocketBase::FlushOutBuffer() { void AsyncTCPSocketBase::AppendToOutBuffer(const void* pv, size_t cb) { RTC_DCHECK(outbuf_.size() + cb <= max_outsize_); - RTC_DCHECK(!listen_); outbuf_.AppendData(static_cast(pv), cb); } @@ -200,62 +183,44 @@ void AsyncTCPSocketBase::OnConnectEvent(Socket* socket) { void AsyncTCPSocketBase::OnReadEvent(Socket* socket) { RTC_DCHECK(socket_.get() == socket); - if (listen_) { - rtc::SocketAddress address; - rtc::Socket* new_socket = socket->Accept(&address); - if (!new_socket) { - // TODO(stefan): Do something better like forwarding the error - // to the user. - RTC_LOG(LS_ERROR) << "TCP accept failed with error " - << socket_->GetError(); - return; + size_t total_recv = 0; + while (true) { + size_t free_size = inbuf_.capacity() - inbuf_.size(); + if (free_size < kMinimumRecvSize && inbuf_.capacity() < max_insize_) { + inbuf_.EnsureCapacity(std::min(max_insize_, inbuf_.capacity() * 2)); + free_size = inbuf_.capacity() - inbuf_.size(); } - HandleIncomingConnection(new_socket); + int len = socket_->Recv(inbuf_.data() + inbuf_.size(), free_size, nullptr); + if (len < 0) { + // TODO(stefan): Do something better like forwarding the error to the + // user. + if (!socket_->IsBlocking()) { + RTC_LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError(); + } + break; + } - // Prime a read event in case data is waiting. - new_socket->SignalReadEvent(new_socket); + total_recv += len; + inbuf_.SetSize(inbuf_.size() + len); + if (!len || static_cast(len) < free_size) { + break; + } + } + + if (!total_recv) { + return; + } + + size_t size = inbuf_.size(); + ProcessInput(inbuf_.data(), &size); + + if (size > inbuf_.size()) { + RTC_LOG(LS_ERROR) << "input buffer overflow"; + RTC_DCHECK_NOTREACHED(); + inbuf_.Clear(); } else { - size_t total_recv = 0; - while (true) { - size_t free_size = inbuf_.capacity() - inbuf_.size(); - if (free_size < kMinimumRecvSize && inbuf_.capacity() < max_insize_) { - inbuf_.EnsureCapacity(std::min(max_insize_, inbuf_.capacity() * 2)); - free_size = inbuf_.capacity() - inbuf_.size(); - } - - int len = - socket_->Recv(inbuf_.data() + inbuf_.size(), free_size, nullptr); - if (len < 0) { - // TODO(stefan): Do something better like forwarding the error to the - // user. - if (!socket_->IsBlocking()) { - RTC_LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError(); - } - break; - } - - total_recv += len; - inbuf_.SetSize(inbuf_.size() + len); - if (!len || static_cast(len) < free_size) { - break; - } - } - - if (!total_recv) { - return; - } - - size_t size = inbuf_.size(); - ProcessInput(inbuf_.data(), &size); - - if (size > inbuf_.size()) { - RTC_LOG(LS_ERROR) << "input buffer overflow"; - RTC_NOTREACHED(); - inbuf_.Clear(); - } else { - inbuf_.SetSize(size); - } + inbuf_.SetSize(size); } } @@ -283,12 +248,11 @@ AsyncTCPSocket* AsyncTCPSocket::Create(Socket* socket, const SocketAddress& bind_address, const SocketAddress& remote_address) { return new AsyncTCPSocket( - AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address), - false); + AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address)); } -AsyncTCPSocket::AsyncTCPSocket(Socket* socket, bool listen) - : AsyncTCPSocketBase(socket, listen, kBufSize) {} +AsyncTCPSocket::AsyncTCPSocket(Socket* socket) + : AsyncTCPSocketBase(socket, kBufSize) {} int AsyncTCPSocket::Send(const void* pv, size_t cb, @@ -343,8 +307,51 @@ void AsyncTCPSocket::ProcessInput(char* data, size_t* len) { } } -void AsyncTCPSocket::HandleIncomingConnection(Socket* socket) { - SignalNewConnection(this, new AsyncTCPSocket(socket, false)); +AsyncTcpListenSocket::AsyncTcpListenSocket(std::unique_ptr socket) + : socket_(std::move(socket)) { + RTC_DCHECK(socket_.get() != nullptr); + socket_->SignalReadEvent.connect(this, &AsyncTcpListenSocket::OnReadEvent); + if (socket_->Listen(kListenBacklog) < 0) { + RTC_LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError(); + } +} + +AsyncTcpListenSocket::State AsyncTcpListenSocket::GetState() const { + switch (socket_->GetState()) { + case Socket::CS_CLOSED: + return State::kClosed; + case Socket::CS_CONNECTING: + return State::kBound; + default: + RTC_DCHECK_NOTREACHED(); + return State::kClosed; + } +} + +SocketAddress AsyncTcpListenSocket::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +void AsyncTcpListenSocket::OnReadEvent(Socket* socket) { + RTC_DCHECK(socket_.get() == socket); + + rtc::SocketAddress address; + rtc::Socket* new_socket = socket->Accept(&address); + if (!new_socket) { + // TODO(stefan): Do something better like forwarding the error + // to the user. + RTC_LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError(); + return; + } + + HandleIncomingConnection(new_socket); + + // Prime a read event in case data is waiting. + new_socket->SignalReadEvent(new_socket); +} + +void AsyncTcpListenSocket::HandleIncomingConnection(Socket* socket) { + SignalNewConnection(this, new AsyncTCPSocket(socket)); } } // namespace rtc diff --git a/rtc_base/async_tcp_socket.h b/rtc_base/async_tcp_socket.h index ddf9a436f6..541080fba7 100644 --- a/rtc_base/async_tcp_socket.h +++ b/rtc_base/async_tcp_socket.h @@ -17,7 +17,6 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" @@ -28,16 +27,17 @@ namespace rtc { // buffer them in user space. class AsyncTCPSocketBase : public AsyncPacketSocket { public: - AsyncTCPSocketBase(Socket* socket, bool listen, size_t max_packet_size); + 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 rtc::PacketOptions& options) override = 0; virtual void ProcessInput(char* data, size_t* len) = 0; - // Signals incoming connection. - virtual void HandleIncomingConnection(Socket* socket) = 0; SocketAddress GetLocalAddress() const override; SocketAddress GetRemoteAddress() const override; @@ -76,13 +76,10 @@ class AsyncTCPSocketBase : public AsyncPacketSocket { void OnCloseEvent(Socket* socket, int error); std::unique_ptr socket_; - bool listen_; Buffer inbuf_; Buffer outbuf_; size_t max_insize_; size_t max_outsize_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocketBase); }; class AsyncTCPSocket : public AsyncTCPSocketBase { @@ -93,17 +90,32 @@ class AsyncTCPSocket : public AsyncTCPSocketBase { static AsyncTCPSocket* Create(Socket* socket, const SocketAddress& bind_address, const SocketAddress& remote_address); - AsyncTCPSocket(Socket* socket, bool listen); + explicit AsyncTCPSocket(Socket* socket); ~AsyncTCPSocket() override {} + AsyncTCPSocket(const AsyncTCPSocket&) = delete; + AsyncTCPSocket& operator=(const AsyncTCPSocket&) = delete; + int Send(const void* pv, size_t cb, const rtc::PacketOptions& options) override; void ProcessInput(char* data, size_t* len) override; - void HandleIncomingConnection(Socket* socket) override; +}; + +class AsyncTcpListenSocket : public AsyncListenSocket { + public: + explicit AsyncTcpListenSocket(std::unique_ptr socket); + + State GetState() const override; + SocketAddress GetLocalAddress() const override; + + virtual void HandleIncomingConnection(rtc::Socket* socket); private: - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocket); + // Called by the underlying socket + void OnReadEvent(Socket* socket); + + std::unique_ptr socket_; }; } // namespace rtc diff --git a/rtc_base/bit_buffer.cc b/rtc_base/bit_buffer.cc index a73ef5d869..7dc7428fe9 100644 --- a/rtc_base/bit_buffer.cc +++ b/rtc_base/bit_buffer.cc @@ -18,21 +18,6 @@ namespace { -// Returns the lowest (right-most) `bit_count` bits in `byte`. -uint8_t LowestBits(uint8_t byte, size_t bit_count) { - RTC_DCHECK_LE(bit_count, 8); - return byte & ((1 << bit_count) - 1); -} - -// Returns the highest (left-most) `bit_count` bits in `byte`, shifted to the -// lowest bits (to the right). -uint8_t HighestBits(uint8_t byte, size_t bit_count) { - RTC_DCHECK_LE(bit_count, 8); - uint8_t shift = 8 - static_cast(bit_count); - uint8_t mask = 0xFF << shift; - return (byte & mask) >> shift; -} - // Returns the highest byte of `val` in a uint8_t. uint8_t HighestByte(uint64_t val) { return static_cast(val >> 56); @@ -64,121 +49,24 @@ uint8_t WritePartialByte(uint8_t source, namespace rtc { -BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count) - : bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() { +BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count) + : writable_bytes_(bytes), + byte_count_(byte_count), + byte_offset_(), + bit_offset_() { RTC_DCHECK(static_cast(byte_count_) <= std::numeric_limits::max()); } -uint64_t BitBuffer::RemainingBitCount() const { +uint64_t BitBufferWriter::RemainingBitCount() const { return (static_cast(byte_count_) - byte_offset_) * 8 - bit_offset_; } -bool BitBuffer::ReadUInt8(uint8_t& val) { - uint32_t bit_val; - if (!ReadBits(sizeof(uint8_t) * 8, bit_val)) { - return false; - } - RTC_DCHECK(bit_val <= std::numeric_limits::max()); - val = static_cast(bit_val); - return true; -} - -bool BitBuffer::ReadUInt16(uint16_t& val) { - uint32_t bit_val; - if (!ReadBits(sizeof(uint16_t) * 8, bit_val)) { - return false; - } - RTC_DCHECK(bit_val <= std::numeric_limits::max()); - val = static_cast(bit_val); - return true; -} - -bool BitBuffer::ReadUInt32(uint32_t& val) { - return ReadBits(sizeof(uint32_t) * 8, val); -} - -bool BitBuffer::PeekBits(size_t bit_count, uint32_t& val) { - // TODO(nisse): Could allow bit_count == 0 and always return success. But - // current code reads one byte beyond end of buffer in the case that - // RemainingBitCount() == 0 and bit_count == 0. - RTC_DCHECK(bit_count > 0); - if (bit_count > RemainingBitCount() || bit_count > 32) { - return false; - } - const uint8_t* bytes = bytes_ + byte_offset_; - size_t remaining_bits_in_current_byte = 8 - bit_offset_; - uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte); - // If we're reading fewer bits than what's left in the current byte, just - // return the portion of this byte that we need. - if (bit_count < remaining_bits_in_current_byte) { - val = HighestBits(bits, bit_offset_ + bit_count); - return true; - } - // Otherwise, subtract what we've read from the bit count and read as many - // full bytes as we can into bits. - bit_count -= remaining_bits_in_current_byte; - while (bit_count >= 8) { - bits = (bits << 8) | *bytes++; - bit_count -= 8; - } - // Whatever we have left is smaller than a byte, so grab just the bits we need - // and shift them into the lowest bits. - if (bit_count > 0) { - bits <<= bit_count; - bits |= HighestBits(*bytes, bit_count); - } - val = bits; - return true; -} - -bool BitBuffer::PeekBits(size_t bit_count, uint64_t& val) { - // TODO(nisse): Could allow bit_count == 0 and always return success. But - // current code reads one byte beyond end of buffer in the case that - // RemainingBitCount() == 0 and bit_count == 0. - RTC_DCHECK(bit_count > 0); - if (bit_count > RemainingBitCount() || bit_count > 64) { - return false; - } - const uint8_t* bytes = bytes_ + byte_offset_; - size_t remaining_bits_in_current_byte = 8 - bit_offset_; - uint64_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte); - // If we're reading fewer bits than what's left in the current byte, just - // return the portion of this byte that we need. - if (bit_count < remaining_bits_in_current_byte) { - val = HighestBits(bits, bit_offset_ + bit_count); - return true; - } - // Otherwise, subtract what we've read from the bit count and read as many - // full bytes as we can into bits. - bit_count -= remaining_bits_in_current_byte; - while (bit_count >= 8) { - bits = (bits << 8) | *bytes++; - bit_count -= 8; - } - // Whatever we have left is smaller than a byte, so grab just the bits we need - // and shift them into the lowest bits. - if (bit_count > 0) { - bits <<= bit_count; - bits |= HighestBits(*bytes, bit_count); - } - val = bits; - return true; -} - -bool BitBuffer::ReadBits(size_t bit_count, uint32_t& val) { - return PeekBits(bit_count, val) && ConsumeBits(bit_count); -} - -bool BitBuffer::ReadBits(size_t bit_count, uint64_t& val) { - return PeekBits(bit_count, val) && ConsumeBits(bit_count); -} - -bool BitBuffer::ConsumeBytes(size_t byte_count) { +bool BitBufferWriter::ConsumeBytes(size_t byte_count) { return ConsumeBits(byte_count * 8); } -bool BitBuffer::ConsumeBits(size_t bit_count) { +bool BitBufferWriter::ConsumeBits(size_t bit_count) { if (bit_count > RemainingBitCount()) { return false; } @@ -188,86 +76,15 @@ bool BitBuffer::ConsumeBits(size_t bit_count) { return true; } -bool BitBuffer::ReadNonSymmetric(uint32_t num_values, uint32_t& val) { - RTC_DCHECK_GT(num_values, 0); - RTC_DCHECK_LE(num_values, uint32_t{1} << 31); - if (num_values == 1) { - // When there is only one possible value, it requires zero bits to store it. - // But ReadBits doesn't support reading zero bits. - val = 0; - return true; - } - size_t count_bits = absl::bit_width(num_values); - uint32_t num_min_bits_values = (uint32_t{1} << count_bits) - num_values; - - if (!ReadBits(count_bits - 1, val)) { - return false; - } - - if (val < num_min_bits_values) { - return true; - } - - uint32_t extra_bit; - if (!ReadBits(/*bit_count=*/1, extra_bit)) { - return false; - } - - val = (val << 1) + extra_bit - num_min_bits_values; - return true; -} - -bool BitBuffer::ReadExponentialGolomb(uint32_t& val) { - // Store off the current byte/bit offset, in case we want to restore them due - // to a failed parse. - size_t original_byte_offset = byte_offset_; - size_t original_bit_offset = bit_offset_; - - // Count the number of leading 0 bits by peeking/consuming them one at a time. - size_t zero_bit_count = 0; - uint32_t peeked_bit; - while (PeekBits(1, peeked_bit) && peeked_bit == 0) { - zero_bit_count++; - ConsumeBits(1); - } - - // We should either be at the end of the stream, or the next bit should be 1. - RTC_DCHECK(!PeekBits(1, peeked_bit) || peeked_bit == 1); - - // The bit count of the value is the number of zeros + 1. Make sure that many - // bits fits in a uint32_t and that we have enough bits left for it, and then - // read the value. - size_t value_bit_count = zero_bit_count + 1; - if (value_bit_count > 32 || !ReadBits(value_bit_count, val)) { - RTC_CHECK(Seek(original_byte_offset, original_bit_offset)); - return false; - } - val -= 1; - return true; -} - -bool BitBuffer::ReadSignedExponentialGolomb(int32_t& val) { - uint32_t unsigned_val; - if (!ReadExponentialGolomb(unsigned_val)) { - return false; - } - if ((unsigned_val & 1) == 0) { - val = -static_cast(unsigned_val / 2); - } else { - val = (unsigned_val + 1) / 2; - } - return true; -} - -void BitBuffer::GetCurrentOffset(size_t* out_byte_offset, - size_t* out_bit_offset) { +void BitBufferWriter::GetCurrentOffset(size_t* out_byte_offset, + size_t* out_bit_offset) { RTC_CHECK(out_byte_offset != nullptr); RTC_CHECK(out_bit_offset != nullptr); *out_byte_offset = byte_offset_; *out_bit_offset = bit_offset_; } -bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) { +bool BitBufferWriter::Seek(size_t byte_offset, size_t bit_offset) { if (byte_offset > byte_count_ || bit_offset > 7 || (byte_offset == byte_count_ && bit_offset > 0)) { return false; @@ -277,9 +94,6 @@ bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) { return true; } -BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count) - : BitBuffer(bytes, byte_count), writable_bytes_(bytes) {} - bool BitBufferWriter::WriteUInt8(uint8_t val) { return WriteBits(val, sizeof(uint8_t) * 8); } diff --git a/rtc_base/bit_buffer.h b/rtc_base/bit_buffer.h index 57366161c9..b4991bc724 100644 --- a/rtc_base/bit_buffer.h +++ b/rtc_base/bit_buffer.h @@ -14,21 +14,19 @@ #include // For size_t. #include // For integer types. -#include "absl/base/attributes.h" -#include "rtc_base/constructor_magic.h" - namespace rtc { -// A class, similar to ByteBuffer, that can parse bit-sized data out of a set of -// bytes. Has a similar API to ByteBuffer, plus methods for reading bit-sized -// and exponential golomb encoded data. For a writable version, use -// BitBufferWriter. Unlike ByteBuffer, this class doesn't make a copy of the -// source bytes, so it can be used on read-only data. +// 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 BitBuffer { +class BitBufferWriter { public: - BitBuffer(const uint8_t* bytes, size_t byte_count); + // 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]. @@ -37,75 +35,6 @@ class BitBuffer { // The remaining bits in the byte buffer. uint64_t RemainingBitCount() const; - // Reads byte-sized values from the buffer. Returns false if there isn't - // enough data left for the specified type. - bool ReadUInt8(uint8_t& val); - bool ReadUInt16(uint16_t& val); - bool ReadUInt32(uint32_t& val); - ABSL_DEPRECATED("") bool ReadUInt8(uint8_t* val) { - return val ? ReadUInt8(*val) : false; - } - ABSL_DEPRECATED("") bool ReadUInt16(uint16_t* val) { - return val ? ReadUInt16(*val) : false; - } - ABSL_DEPRECATED("") bool ReadUInt32(uint32_t* val) { - return val ? ReadUInt32(*val) : false; - } - - // Reads bit-sized values from the buffer. Returns false if there isn't enough - // data left for the specified bit count. - bool ReadBits(size_t bit_count, uint32_t& val); - bool ReadBits(size_t bit_count, uint64_t& val); - ABSL_DEPRECATED("") bool ReadBits(uint32_t* val, size_t bit_count) { - return val ? ReadBits(bit_count, *val) : false; - } - - // Peeks bit-sized values from the buffer. Returns false if there isn't enough - // data left for the specified number of bits. Doesn't move the current - // offset. - bool PeekBits(size_t bit_count, uint32_t& val); - bool PeekBits(size_t bit_count, uint64_t& val); - ABSL_DEPRECATED("") bool PeekBits(uint32_t* val, size_t bit_count) { - return val ? PeekBits(bit_count, *val) : false; - } - - // 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 = countbits(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 - // Returns false if there isn't enough data left. - bool ReadNonSymmetric(uint32_t num_values, uint32_t& val); - ABSL_DEPRECATED("") - bool ReadNonSymmetric(uint32_t* val, uint32_t num_values) { - return val ? ReadNonSymmetric(num_values, *val) : false; - } - - // Reads the exponential golomb encoded value at the current offset. - // Exponential golomb values are encoded as: - // 1) x = source val + 1 - // 2) In binary, write [countbits(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. - // Returns false if there isn't enough data left for the specified type, or if - // the value wouldn't fit in a uint32_t. - bool ReadExponentialGolomb(uint32_t& val); - ABSL_DEPRECATED("") bool ReadExponentialGolomb(uint32_t* val) { - return val ? ReadExponentialGolomb(*val) : false; - } - - // 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. - bool ReadSignedExponentialGolomb(int32_t& val); - ABSL_DEPRECATED("") bool ReadSignedExponentialGolomb(int32_t* val) { - return val ? ReadSignedExponentialGolomb(*val) : false; - } - // 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); @@ -117,26 +46,6 @@ class BitBuffer { // offset is from the given byte, in the range [0,7]. bool Seek(size_t byte_offset, size_t bit_offset); - protected: - const uint8_t* const bytes_; - // The total size of `bytes_`. - 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_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BitBuffer); -}; - -// A BitBuffer API for write operations. Supports symmetric write APIs to the -// reading APIs of BitBuffer. Note that the read/write offset is shared with the -// BitBuffer API, so both reading and writing will consume bytes/bits. -class BitBufferWriter : public BitBuffer { - public: - // Constructs a bit buffer for the writable buffer of `bytes`. - BitBufferWriter(uint8_t* bytes, size_t byte_count); - // 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); @@ -166,8 +75,12 @@ class BitBufferWriter : public BitBuffer { private: // The buffer, as a writable array. uint8_t* const writable_bytes_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BitBufferWriter); + // 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 rtc diff --git a/rtc_base/bit_buffer_unittest.cc b/rtc_base/bit_buffer_unittest.cc index e6bb4270c7..198be50e11 100644 --- a/rtc_base/bit_buffer_unittest.cc +++ b/rtc_base/bit_buffer_unittest.cc @@ -12,7 +12,9 @@ #include +#include "api/array_view.h" #include "rtc_base/arraysize.h" +#include "rtc_base/bitstream_reader.h" #include "rtc_base/byte_buffer.h" #include "test/gmock.h" #include "test/gtest.h" @@ -20,10 +22,11 @@ namespace rtc { using ::testing::ElementsAre; +using ::webrtc::BitstreamReader; -TEST(BitBufferTest, ConsumeBits) { - const uint8_t bytes[64] = {0}; - BitBuffer buffer(bytes, 32); +TEST(BitBufferWriterTest, ConsumeBits) { + uint8_t bytes[64] = {0}; + BitBufferWriter buffer(bytes, 32); uint64_t total_bits = 32 * 8; EXPECT_EQ(total_bits, buffer.RemainingBitCount()); EXPECT_TRUE(buffer.ConsumeBits(3)); @@ -43,138 +46,7 @@ TEST(BitBufferTest, ConsumeBits) { EXPECT_EQ(total_bits, buffer.RemainingBitCount()); } -TEST(BitBufferTest, ReadBytesAligned) { - const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89}; - uint8_t val8; - uint16_t val16; - uint32_t val32; - BitBuffer buffer(bytes, 8); - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0x0Au, val8); - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0xBCu, val8); - EXPECT_TRUE(buffer.ReadUInt16(val16)); - EXPECT_EQ(0xDEF1u, val16); - EXPECT_TRUE(buffer.ReadUInt32(val32)); - EXPECT_EQ(0x23456789u, val32); -} - -TEST(BitBufferTest, ReadBytesOffset4) { - const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, - 0x45, 0x67, 0x89, 0x0A}; - uint8_t val8; - uint16_t val16; - uint32_t val32; - BitBuffer buffer(bytes, 9); - EXPECT_TRUE(buffer.ConsumeBits(4)); - - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0xABu, val8); - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0xCDu, val8); - EXPECT_TRUE(buffer.ReadUInt16(val16)); - EXPECT_EQ(0xEF12u, val16); - EXPECT_TRUE(buffer.ReadUInt32(val32)); - EXPECT_EQ(0x34567890u, val32); -} - -TEST(BitBufferTest, ReadBytesOffset3) { - // The pattern we'll check against is counting down from 0b1111. It looks - // weird here because it's all offset by 3. - // Byte pattern is: - // 56701234 - // 0b00011111, - // 0b11011011, - // 0b10010111, - // 0b01010011, - // 0b00001110, - // 0b11001010, - // 0b10000110, - // 0b01000010 - // xxxxx <-- last 5 bits unused. - - // The bytes. It almost looks like counting down by two at a time, except the - // jump at 5->3->0, since that's when the high bit is turned off. - const uint8_t bytes[] = {0x1F, 0xDB, 0x97, 0x53, 0x0E, 0xCA, 0x86, 0x42}; - - uint8_t val8; - uint16_t val16; - uint32_t val32; - BitBuffer buffer(bytes, 8); - EXPECT_TRUE(buffer.ConsumeBits(3)); - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0xFEu, val8); - EXPECT_TRUE(buffer.ReadUInt16(val16)); - EXPECT_EQ(0xDCBAu, val16); - EXPECT_TRUE(buffer.ReadUInt32(val32)); - EXPECT_EQ(0x98765432u, val32); - // 5 bits left unread. Not enough to read a uint8_t. - EXPECT_EQ(5u, buffer.RemainingBitCount()); - EXPECT_FALSE(buffer.ReadUInt8(val8)); -} - -TEST(BitBufferTest, ReadBits) { - // Bit values are: - // 0b01001101, - // 0b00110010 - const uint8_t bytes[] = {0x4D, 0x32}; - uint32_t val; - BitBuffer buffer(bytes, 2); - EXPECT_TRUE(buffer.ReadBits(3, val)); - // 0b010 - EXPECT_EQ(0x2u, val); - EXPECT_TRUE(buffer.ReadBits(2, val)); - // 0b01 - EXPECT_EQ(0x1u, val); - EXPECT_TRUE(buffer.ReadBits(7, val)); - // 0b1010011 - EXPECT_EQ(0x53u, val); - EXPECT_TRUE(buffer.ReadBits(2, val)); - // 0b00 - EXPECT_EQ(0x0u, val); - EXPECT_TRUE(buffer.ReadBits(1, val)); - // 0b1 - EXPECT_EQ(0x1u, val); - EXPECT_TRUE(buffer.ReadBits(1, val)); - // 0b0 - EXPECT_EQ(0x0u, val); - - EXPECT_FALSE(buffer.ReadBits(1, val)); -} - -TEST(BitBufferTest, ReadBits64) { - const uint8_t bytes[] = {0x4D, 0x32, 0xAB, 0x54, 0x00, 0xFF, 0xFE, 0x01, - 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89}; - BitBuffer buffer(bytes, 16); - uint64_t val; - - // Peek and read first 33 bits. - EXPECT_TRUE(buffer.PeekBits(33, val)); - EXPECT_EQ(0x4D32AB5400FFFE01ull >> (64 - 33), val); - val = 0; - EXPECT_TRUE(buffer.ReadBits(33, val)); - EXPECT_EQ(0x4D32AB5400FFFE01ull >> (64 - 33), val); - - // Peek and read next 31 bits. - constexpr uint64_t kMask31Bits = (1ull << 32) - 1; - EXPECT_TRUE(buffer.PeekBits(31, val)); - EXPECT_EQ(0x4D32AB5400FFFE01ull & kMask31Bits, val); - val = 0; - EXPECT_TRUE(buffer.ReadBits(31, val)); - EXPECT_EQ(0x4D32AB5400FFFE01ull & kMask31Bits, val); - - // Peek and read remaining 64 bits. - EXPECT_TRUE(buffer.PeekBits(64, val)); - EXPECT_EQ(0xABCDEF0123456789ull, val); - val = 0; - EXPECT_TRUE(buffer.ReadBits(64, val)); - EXPECT_EQ(0xABCDEF0123456789ull, val); - - // Nothing more to read. - EXPECT_FALSE(buffer.ReadBits(1, val)); -} - -TEST(BitBufferDeathTest, SetOffsetValues) { +TEST(BitBufferWriterDeathTest, SetOffsetValues) { uint8_t bytes[4] = {0}; BitBufferWriter buffer(bytes, 4); @@ -213,21 +85,6 @@ TEST(BitBufferDeathTest, SetOffsetValues) { #endif } -TEST(BitBufferTest, ReadNonSymmetricSameNumberOfBitsWhenNumValuesPowerOf2) { - const uint8_t bytes[2] = {0xf3, 0xa0}; - BitBuffer reader(bytes, 2); - - uint32_t values[4]; - ASSERT_EQ(reader.RemainingBitCount(), 16u); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/1 << 4, values[0])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/1 << 4, values[1])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/1 << 4, values[2])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/1 << 4, values[3])); - ASSERT_EQ(reader.RemainingBitCount(), 0u); - - EXPECT_THAT(values, ElementsAre(0xf, 0x3, 0xa, 0x0)); -} - TEST(BitBufferWriterTest, WriteNonSymmetricSameNumberOfBitsWhenNumValuesPowerOf2) { uint8_t bytes[2] = {}; @@ -274,28 +131,14 @@ TEST(BitBufferWriterTest, NonSymmetricReadsMatchesWrites) { // 00.01.100.101.110.111 = 00011001|01110111 = 0x19|77 EXPECT_THAT(bytes, ElementsAre(0x19, 0x77)); - rtc::BitBuffer reader(bytes, 2); - uint32_t values[6]; - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[0])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[1])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[2])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[3])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[4])); - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/6, values[5])); - - EXPECT_THAT(values, ElementsAre(0, 1, 2, 3, 4, 5)); -} - -TEST(BitBufferTest, ReadNonSymmetricOnlyValueConsumesNoBits) { - const uint8_t bytes[2] = {}; - BitBuffer reader(bytes, 2); - uint32_t value = 0xFFFFFFFF; - ASSERT_EQ(reader.RemainingBitCount(), 16u); - - EXPECT_TRUE(reader.ReadNonSymmetric(/*num_values=*/1, value)); - - EXPECT_EQ(value, 0u); - EXPECT_EQ(reader.RemainingBitCount(), 16u); + BitstreamReader reader(bytes); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 0u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 1u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 2u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 3u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 4u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/6), 5u); + EXPECT_TRUE(reader.Ok()); } TEST(BitBufferWriterTest, WriteNonSymmetricOnlyValueConsumesNoBits) { @@ -308,74 +151,6 @@ TEST(BitBufferWriterTest, WriteNonSymmetricOnlyValueConsumesNoBits) { EXPECT_EQ(writer.RemainingBitCount(), 16u); } -uint64_t GolombEncoded(uint32_t val) { - val++; - uint32_t bit_counter = val; - uint64_t bit_count = 0; - while (bit_counter > 0) { - bit_count++; - bit_counter >>= 1; - } - return static_cast(val) << (64 - (bit_count * 2 - 1)); -} - -TEST(BitBufferTest, GolombUint32Values) { - ByteBufferWriter byteBuffer; - byteBuffer.Resize(16); - BitBuffer buffer(reinterpret_cast(byteBuffer.Data()), - byteBuffer.Capacity()); - // Test over the uint32_t range with a large enough step that the test doesn't - // take forever. Around 20,000 iterations should do. - const int kStep = std::numeric_limits::max() / 20000; - for (uint32_t i = 0; i < std::numeric_limits::max() - kStep; - i += kStep) { - uint64_t encoded_val = GolombEncoded(i); - byteBuffer.Clear(); - byteBuffer.WriteUInt64(encoded_val); - uint32_t decoded_val; - EXPECT_TRUE(buffer.Seek(0, 0)); - EXPECT_TRUE(buffer.ReadExponentialGolomb(decoded_val)); - EXPECT_EQ(i, decoded_val); - } -} - -TEST(BitBufferTest, SignedGolombValues) { - uint8_t golomb_bits[] = { - 0x80, // 1 - 0x40, // 010 - 0x60, // 011 - 0x20, // 00100 - 0x38, // 00111 - }; - int32_t expected[] = {0, 1, -1, 2, -3}; - for (size_t i = 0; i < sizeof(golomb_bits); ++i) { - BitBuffer buffer(&golomb_bits[i], 1); - int32_t decoded_val; - ASSERT_TRUE(buffer.ReadSignedExponentialGolomb(decoded_val)); - EXPECT_EQ(expected[i], decoded_val) - << "Mismatch in expected/decoded value for golomb_bits[" << i - << "]: " << static_cast(golomb_bits[i]); - } -} - -TEST(BitBufferTest, NoGolombOverread) { - const uint8_t bytes[] = {0x00, 0xFF, 0xFF}; - // Make sure the bit buffer correctly enforces byte length on golomb reads. - // If it didn't, the above buffer would be valid at 3 bytes. - BitBuffer buffer(bytes, 1); - uint32_t decoded_val; - EXPECT_FALSE(buffer.ReadExponentialGolomb(decoded_val)); - - BitBuffer longer_buffer(bytes, 2); - EXPECT_FALSE(longer_buffer.ReadExponentialGolomb(decoded_val)); - - BitBuffer longest_buffer(bytes, 3); - EXPECT_TRUE(longest_buffer.ReadExponentialGolomb(decoded_val)); - // Golomb should have read 9 bits, so 0x01FF, and since it is golomb, the - // result is 0x01FF - 1 = 0x01FE. - EXPECT_EQ(0x01FEu, decoded_val); -} - TEST(BitBufferWriterTest, SymmetricReadWrite) { uint8_t bytes[16] = {0}; BitBufferWriter buffer(bytes, 4); @@ -390,22 +165,15 @@ TEST(BitBufferWriterTest, SymmetricReadWrite) { // That should be all that fits in the buffer. EXPECT_FALSE(buffer.WriteBits(1, 1)); - EXPECT_TRUE(buffer.Seek(0, 0)); - uint32_t val; - EXPECT_TRUE(buffer.ReadBits(3, val)); - EXPECT_EQ(0x2u, val); - EXPECT_TRUE(buffer.ReadBits(2, val)); - EXPECT_EQ(0x1u, val); - EXPECT_TRUE(buffer.ReadBits(7, val)); - EXPECT_EQ(0x53u, val); - EXPECT_TRUE(buffer.ReadBits(2, val)); - EXPECT_EQ(0x0u, val); - EXPECT_TRUE(buffer.ReadBits(1, val)); - EXPECT_EQ(0x1u, val); - EXPECT_TRUE(buffer.ReadBits(17, val)); - EXPECT_EQ(0x1ABCDu, val); + BitstreamReader reader(rtc::MakeArrayView(bytes, 4)); + EXPECT_EQ(reader.ReadBits(3), 0x2u); + EXPECT_EQ(reader.ReadBits(2), 0x1u); + EXPECT_EQ(reader.ReadBits(7), 0x53u); + EXPECT_EQ(reader.ReadBits(2), 0x0u); + EXPECT_EQ(reader.ReadBits(1), 0x1u); + EXPECT_EQ(reader.ReadBits(17), 0x1ABCDu); // And there should be nothing left. - EXPECT_FALSE(buffer.ReadBits(1, val)); + EXPECT_EQ(reader.RemainingBitCount(), 0); } TEST(BitBufferWriterTest, SymmetricBytesMisaligned) { @@ -418,16 +186,12 @@ TEST(BitBufferWriterTest, SymmetricBytesMisaligned) { EXPECT_TRUE(buffer.WriteUInt16(0x3456u)); EXPECT_TRUE(buffer.WriteUInt32(0x789ABCDEu)); - buffer.Seek(0, 3); - uint8_t val8; - uint16_t val16; - uint32_t val32; - EXPECT_TRUE(buffer.ReadUInt8(val8)); - EXPECT_EQ(0x12u, val8); - EXPECT_TRUE(buffer.ReadUInt16(val16)); - EXPECT_EQ(0x3456u, val16); - EXPECT_TRUE(buffer.ReadUInt32(val32)); - EXPECT_EQ(0x789ABCDEu, val32); + BitstreamReader reader(bytes); + reader.ConsumeBits(3); + EXPECT_EQ(reader.Read(), 0x12u); + EXPECT_EQ(reader.Read(), 0x3456u); + EXPECT_EQ(reader.Read(), 0x789ABCDEu); + EXPECT_TRUE(reader.Ok()); } TEST(BitBufferWriterTest, SymmetricGolomb) { @@ -437,13 +201,11 @@ TEST(BitBufferWriterTest, SymmetricGolomb) { for (size_t i = 0; i < arraysize(test_string); ++i) { EXPECT_TRUE(buffer.WriteExponentialGolomb(test_string[i])); } - buffer.Seek(0, 0); + BitstreamReader reader(bytes); for (size_t i = 0; i < arraysize(test_string); ++i) { - uint32_t val; - EXPECT_TRUE(buffer.ReadExponentialGolomb(val)); - EXPECT_LE(val, std::numeric_limits::max()); - EXPECT_EQ(test_string[i], static_cast(val)); + EXPECT_EQ(int64_t{reader.ReadExponentialGolomb()}, int64_t{test_string[i]}); } + EXPECT_TRUE(reader.Ok()); } TEST(BitBufferWriterTest, WriteClearsBits) { diff --git a/rtc_base/bitstream_reader.cc b/rtc_base/bitstream_reader.cc index 0bb604970b..d2c622d938 100644 --- a/rtc_base/bitstream_reader.cc +++ b/rtc_base/bitstream_reader.cc @@ -12,6 +12,8 @@ #include +#include + #include "absl/numeric/bits.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" @@ -79,14 +81,14 @@ int BitstreamReader::ReadBit() { void BitstreamReader::ConsumeBits(int bits) { RTC_DCHECK_GE(bits, 0); set_last_read_is_verified(false); + if (remaining_bits_ < bits) { + Invalidate(); + return; + } int remaining_bytes = (remaining_bits_ + 7) / 8; remaining_bits_ -= bits; int new_remaining_bytes = (remaining_bits_ + 7) / 8; - // When `remaining_bits_` is negative, `BitstreamReader` is in failure state - // and `bytes_' member no longer used, thus its value doesn't matter. - // In such case it doesn't matter that negative integer division rounds up - // instead of down and thus this byte adjustement might seem incorrect. bytes_ += (remaining_bytes - new_remaining_bytes); } diff --git a/rtc_base/bitstream_reader.h b/rtc_base/bitstream_reader.h index 8c0f66fa91..51c7914bd7 100644 --- a/rtc_base/bitstream_reader.h +++ b/rtc_base/bitstream_reader.h @@ -61,14 +61,17 @@ class BitstreamReader { // Reads unsigned integer of fixed width. template ::value && + !std::is_same::value && sizeof(T) <= 8>::type* = nullptr> ABSL_MUST_USE_RESULT T Read() { return rtc::dchecked_cast(ReadBits(sizeof(T) * 8)); } // Reads single bit as boolean. - template <> - ABSL_MUST_USE_RESULT bool Read() { + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + ABSL_MUST_USE_RESULT bool Read() { return ReadBit() != 0; } diff --git a/rtc_base/bitstream_reader_unittest.cc b/rtc_base/bitstream_reader_unittest.cc index 45c4eca8d4..997abdf573 100644 --- a/rtc_base/bitstream_reader_unittest.cc +++ b/rtc_base/bitstream_reader_unittest.cc @@ -75,6 +75,16 @@ TEST(BitstreamReaderTest, ConsumeBits) { EXPECT_LT(reader.RemainingBitCount(), 0); } +TEST(BitstreamReaderTest, ConsumeLotsOfBits) { + const uint8_t bytes[1] = {}; + BitstreamReader reader(bytes); + + reader.ConsumeBits(std::numeric_limits::max()); + reader.ConsumeBits(std::numeric_limits::max()); + EXPECT_GE(reader.ReadBit(), 0); + EXPECT_FALSE(reader.Ok()); +} + TEST(BitstreamReaderTest, ReadBit) { const uint8_t bytes[] = {0b0100'0001, 0b1011'0001}; BitstreamReader reader(bytes); diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc index e1bcd61070..a866224496 100644 --- a/rtc_base/boringssl_certificate.cc +++ b/rtc_base/boringssl_certificate.cc @@ -10,6 +10,8 @@ #include "rtc_base/boringssl_certificate.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -106,7 +108,7 @@ bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) { } break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } if (!CBB_flush(cbb)) { @@ -116,7 +118,7 @@ bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) { } // Adds an X.509 Common Name to `cbb`. -bool AddCommonName(CBB* cbb, const std::string& common_name) { +bool AddCommonName(CBB* cbb, absl::string_view common_name) { // See RFC 4519. static const uint8_t kCommonName[] = {0x55, 0x04, 0x03}; @@ -138,7 +140,7 @@ bool AddCommonName(CBB* cbb, const std::string& common_name) { !CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)) || !CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING) || !CBB_add_bytes(&value, - reinterpret_cast(common_name.c_str()), + reinterpret_cast(common_name.data()), common_name.size()) || !CBB_flush(cbb)) { return false; @@ -275,7 +277,7 @@ std::unique_ptr BoringSSLCertificate::Generate( } std::unique_ptr BoringSSLCertificate::FromPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { std::string der; if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) { return nullptr; @@ -340,7 +342,7 @@ bool BoringSSLCertificate::GetSignatureDigestAlgorithm( return false; } -bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm, +bool BoringSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { @@ -348,7 +350,7 @@ bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm, } bool BoringSSLCertificate::ComputeDigest(const CRYPTO_BUFFER* cert_buffer, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) { diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h index 40a4bd8f38..bd331686b7 100644 --- a/rtc_base/boringssl_certificate.h +++ b/rtc_base/boringssl_certificate.h @@ -18,8 +18,8 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -39,10 +39,13 @@ class BoringSSLCertificate final : public SSLCertificate { OpenSSLKeyPair* key_pair, const SSLIdentityParams& params); static std::unique_ptr FromPEMString( - const std::string& pem_string); + absl::string_view pem_string); ~BoringSSLCertificate() override; + BoringSSLCertificate(const BoringSSLCertificate&) = delete; + BoringSSLCertificate& operator=(const BoringSSLCertificate&) = delete; + std::unique_ptr Clone() const override; CRYPTO_BUFFER* cert_buffer() const { return cert_buffer_.get(); } @@ -53,14 +56,14 @@ class BoringSSLCertificate final : public SSLCertificate { bool operator!=(const BoringSSLCertificate& other) const; // Compute the digest of the certificate given `algorithm`. - bool ComputeDigest(const std::string& algorithm, + bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const override; // Compute the digest of a certificate as a CRYPTO_BUFFER. static bool ComputeDigest(const CRYPTO_BUFFER* cert_buffer, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length); @@ -72,7 +75,6 @@ class BoringSSLCertificate final : public SSLCertificate { private: // A handle to the DER encoded certificate data. bssl::UniquePtr cert_buffer_; - RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLCertificate); }; } // namespace rtc diff --git a/rtc_base/boringssl_identity.cc b/rtc_base/boringssl_identity.cc index d22c8ce529..a61524a679 100644 --- a/rtc_base/boringssl_identity.cc +++ b/rtc_base/boringssl_identity.cc @@ -22,6 +22,7 @@ #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -67,12 +68,12 @@ std::unique_ptr BoringSSLIdentity::CreateInternal( // static std::unique_ptr BoringSSLIdentity::CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime) { SSLIdentityParams params; params.key_params = key_params; - params.common_name = common_name; + params.common_name = std::string(common_name); time_t now = time(nullptr); params.not_before = now + kCertificateWindowInSeconds; params.not_after = now + certificate_lifetime; @@ -87,8 +88,8 @@ std::unique_ptr BoringSSLIdentity::CreateForTest( } std::unique_ptr BoringSSLIdentity::CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate) { + absl::string_view private_key, + absl::string_view certificate) { std::unique_ptr cert( BoringSSLCertificate::FromPEMString(certificate)); if (!cert) { @@ -108,8 +109,8 @@ std::unique_ptr BoringSSLIdentity::CreateFromPEMStrings( } std::unique_ptr BoringSSLIdentity::CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain) { + absl::string_view private_key, + absl::string_view certificate_chain) { bssl::UniquePtr bio( BIO_new_mem_buf(certificate_chain.data(), rtc::dchecked_cast(certificate_chain.size()))); diff --git a/rtc_base/boringssl_identity.h b/rtc_base/boringssl_identity.h index 71b29b486d..ffc8812af2 100644 --- a/rtc_base/boringssl_identity.h +++ b/rtc_base/boringssl_identity.h @@ -17,8 +17,8 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/boringssl_certificate.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/openssl_key_pair.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -31,19 +31,22 @@ namespace rtc { class BoringSSLIdentity final : public SSLIdentity { public: static std::unique_ptr CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime); static std::unique_ptr CreateForTest( const SSLIdentityParams& params); static std::unique_ptr CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate); + absl::string_view private_key, + absl::string_view certificate); static std::unique_ptr CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain); + absl::string_view private_key, + absl::string_view certificate_chain); ~BoringSSLIdentity() override; + BoringSSLIdentity(const BoringSSLIdentity&) = delete; + BoringSSLIdentity& operator=(const BoringSSLIdentity&) = delete; + const BoringSSLCertificate& certificate() const override; const SSLCertChain& cert_chain() const override; @@ -67,8 +70,6 @@ class BoringSSLIdentity final : public SSLIdentity { std::unique_ptr key_pair_; std::unique_ptr cert_chain_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLIdentity); }; } // namespace rtc diff --git a/rtc_base/buffer_queue.h b/rtc_base/buffer_queue.h index ee435f4694..b018e160a1 100644 --- a/rtc_base/buffer_queue.h +++ b/rtc_base/buffer_queue.h @@ -18,7 +18,6 @@ #include "api/sequence_checker.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" @@ -30,6 +29,9 @@ class BufferQueue final { 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; @@ -61,8 +63,6 @@ class BufferQueue final { const size_t default_size_; std::deque queue_ RTC_GUARDED_BY(sequence_checker_); std::vector free_list_ RTC_GUARDED_BY(sequence_checker_); - - RTC_DISALLOW_COPY_AND_ASSIGN(BufferQueue); }; } // namespace rtc diff --git a/rtc_base/byte_buffer.h b/rtc_base/byte_buffer.h index fc383f0a33..9bcbb838aa 100644 --- a/rtc_base/byte_buffer.h +++ b/rtc_base/byte_buffer.h @@ -16,9 +16,9 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" -#include "rtc_base/constructor_magic.h" // Reads/Writes from/to buffer using network byte order (big endian) namespace rtc { @@ -29,6 +29,9 @@ class ByteBufferWriterT { ByteBufferWriterT() { Construct(nullptr, kDefaultCapacity); } ByteBufferWriterT(const char* bytes, size_t len) { Construct(bytes, len); } + ByteBufferWriterT(const ByteBufferWriterT&) = delete; + ByteBufferWriterT& operator=(const ByteBufferWriterT&) = delete; + const char* Data() const { return buffer_.data(); } size_t Length() const { return buffer_.size(); } size_t Capacity() const { return buffer_.capacity(); } @@ -70,8 +73,8 @@ class ByteBufferWriterT { char last_byte = static_cast(val); WriteBytes(&last_byte, 1); } - void WriteString(const std::string& val) { - WriteBytes(val.c_str(), val.size()); + void WriteString(absl::string_view val) { + WriteBytes(val.data(), val.size()); } void WriteBytes(const char* val, size_t len) { buffer_.AppendData(val, len); } @@ -104,7 +107,6 @@ class ByteBufferWriterT { // There are sensible ways to define these, but they aren't needed in our code // base. - RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferWriterT); }; class ByteBufferWriter : public ByteBufferWriterT> { @@ -112,8 +114,8 @@ class ByteBufferWriter : public ByteBufferWriterT> { ByteBufferWriter(); ByteBufferWriter(const char* bytes, size_t len); - private: - RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferWriter); + ByteBufferWriter(const ByteBufferWriter&) = delete; + ByteBufferWriter& operator=(const ByteBufferWriter&) = delete; }; // The ByteBufferReader references the passed data, i.e. the pointer must be @@ -129,6 +131,9 @@ class ByteBufferReader { explicit ByteBufferReader(const ByteBufferWriter& buf); + ByteBufferReader(const ByteBufferReader&) = delete; + ByteBufferReader& operator=(const ByteBufferReader&) = delete; + // Returns start of unprocessed data. const char* Data() const { return bytes_ + start_; } // Returns number of unprocessed bytes. @@ -161,9 +166,6 @@ class ByteBufferReader { size_t size_; size_t start_; size_t end_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferReader); }; } // namespace rtc diff --git a/rtc_base/callback_list_unittest.cc b/rtc_base/callback_list_unittest.cc index 665d779739..e2bc6d515e 100644 --- a/rtc_base/callback_list_unittest.cc +++ b/rtc_base/callback_list_unittest.cc @@ -17,7 +17,7 @@ namespace webrtc { namespace { -TEST(CallbackList, NoRecieverSingleMessageTest) { +TEST(CallbackList, NoReceiverSingleMessageTest) { CallbackList c; c.Send("message"); @@ -128,7 +128,7 @@ struct LargeNonTrivial { LargeNonTrivial(LargeNonTrivial&& m) {} ~LargeNonTrivial() = default; - void operator()(int& a) { a = 1; } + void operator()(int& b) { b = 1; } }; TEST(CallbackList, LargeNonTrivialTest) { diff --git a/rtc_base/checks.cc b/rtc_base/checks.cc index 239ea9f0da..d6974d722f 100644 --- a/rtc_base/checks.cc +++ b/rtc_base/checks.cc @@ -15,6 +15,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_ANDROID) #define RTC_LOG_TAG_ANDROID "rtc" #include // NOLINT @@ -37,13 +39,14 @@ namespace { -RTC_NORETURN void WriteFatalLogAndAbort(const std::string& output) { - const char* output_c = output.c_str(); +RTC_NORETURN void WriteFatalLogAndAbort(absl::string_view output) { #if defined(WEBRTC_ANDROID) - __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output_c); + std::string output_str = std::string(output); + __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", + output_str.c_str()); #endif fflush(stdout); - fprintf(stderr, "%s", output_c); + fwrite(output.data(), output.size(), 1, stderr); fflush(stderr); #if defined(WEBRTC_WIN) DebugBreak(); diff --git a/rtc_base/checks.h b/rtc_base/checks.h index 4015338ae0..863e39d651 100644 --- a/rtc_base/checks.h +++ b/rtc_base/checks.h @@ -444,7 +444,7 @@ RTC_NORETURN RTC_EXPORT void UnreachableCodeReached(); #endif #define RTC_UNREACHABLE_CODE_HIT false -#define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT) +#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. diff --git a/rtc_base/constructor_magic.h b/rtc_base/constructor_magic.h deleted file mode 100644 index 8d12a7b135..0000000000 --- a/rtc_base/constructor_magic.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTC_BASE_CONSTRUCTOR_MAGIC_H_ -#define RTC_BASE_CONSTRUCTOR_MAGIC_H_ - -// A macro to disallow the copy constructor and operator= functions. This should -// be used in the declarations for a class. -#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete - -#endif // RTC_BASE_CONSTRUCTOR_MAGIC_H_ diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h index 526cbe5c5c..6837f06526 100644 --- a/rtc_base/copy_on_write_buffer.h +++ b/rtc_base/copy_on_write_buffer.h @@ -24,6 +24,7 @@ #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 rtc { @@ -70,6 +71,17 @@ class RTC_EXPORT CopyOnWriteBuffer { 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()) {} + ~CopyOnWriteBuffer(); // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, @@ -221,8 +233,14 @@ class RTC_EXPORT CopyOnWriteBuffer { AppendData(array, N); } - void AppendData(const CopyOnWriteBuffer& buf) { - AppendData(buf.data(), buf.size()); + 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 diff --git a/rtc_base/copy_on_write_buffer_unittest.cc b/rtc_base/copy_on_write_buffer_unittest.cc index d3978686a8..ad2c4e0fba 100644 --- a/rtc_base/copy_on_write_buffer_unittest.cc +++ b/rtc_base/copy_on_write_buffer_unittest.cc @@ -353,4 +353,24 @@ TEST(CopyOnWriteBufferTest, SlicesAreIndependent) { EXPECT_EQ(buf.cdata() + 3, slice.cdata()); } +TEST(CopyOnWriteBufferTest, AcceptsVectorLikeTypes) { + std::vector a = {1, 2}; + std::vector b = {3, 4}; + rtc::ArrayView c(a); + rtc::ArrayView d(b); + + CopyOnWriteBuffer a_buf(a); + CopyOnWriteBuffer b_buf(b); + CopyOnWriteBuffer c_buf(c); + CopyOnWriteBuffer d_buf(d); + + CopyOnWriteBuffer all; + all.AppendData(a); + all.AppendData(b); + all.AppendData(c); + all.AppendData(d); + + EXPECT_EQ(all.size(), 8U); +} + } // namespace rtc diff --git a/rtc_base/crc32.h b/rtc_base/crc32.h index ca8578d69c..93376a5a12 100644 --- a/rtc_base/crc32.h +++ b/rtc_base/crc32.h @@ -16,6 +16,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // Updates a CRC32 checksum with `len` bytes from `buf`. `initial` holds the @@ -26,8 +28,8 @@ uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len); inline uint32_t ComputeCrc32(const void* buf, size_t len) { return UpdateCrc32(0, buf, len); } -inline uint32_t ComputeCrc32(const std::string& str) { - return ComputeCrc32(str.c_str(), str.size()); +inline uint32_t ComputeCrc32(absl::string_view str) { + return ComputeCrc32(str.data(), str.size()); } } // namespace rtc diff --git a/rtc_base/deprecated/recursive_critical_section.h b/rtc_base/deprecated/recursive_critical_section.h index 15b1f97e9f..cc308e45b7 100644 --- a/rtc_base/deprecated/recursive_critical_section.h +++ b/rtc_base/deprecated/recursive_critical_section.h @@ -11,7 +11,6 @@ #ifndef RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_ #define RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_ -#include "rtc_base/constructor_magic.h" #include "rtc_base/platform_thread_types.h" #include "rtc_base/thread_annotations.h" @@ -94,9 +93,11 @@ class RTC_SCOPED_LOCKABLE CritScope { RTC_EXCLUSIVE_LOCK_FUNCTION(cs); ~CritScope() RTC_UNLOCK_FUNCTION(); + CritScope(const CritScope&) = delete; + CritScope& operator=(const CritScope&) = delete; + private: const RecursiveCriticalSection* const cs_; - RTC_DISALLOW_COPY_AND_ASSIGN(CritScope); }; } // namespace rtc diff --git a/rtc_base/event_tracer.h b/rtc_base/event_tracer.h index 4bbda579bc..68aaf0d8ab 100644 --- a/rtc_base/event_tracer.h +++ b/rtc_base/event_tracer.h @@ -28,6 +28,8 @@ #include +#include "rtc_base/system/rtc_export.h" + namespace webrtc { typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name); @@ -70,12 +72,12 @@ class EventTracer { namespace rtc { namespace tracing { // Set up internal event tracer. -void SetupInternalTracer(); -bool StartInternalCapture(const char* filename); -void StartInternalCaptureToFile(FILE* file); -void StopInternalCapture(); +RTC_EXPORT void SetupInternalTracer(); +RTC_EXPORT bool StartInternalCapture(const char* filename); +RTC_EXPORT void StartInternalCaptureToFile(FILE* file); +RTC_EXPORT void StopInternalCapture(); // Make sure we run this, this will tear down the internal tracing. -void ShutdownInternalTracer(); +RTC_EXPORT void ShutdownInternalTracer(); } // namespace tracing } // namespace rtc diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn index b0a729abfe..5788484025 100644 --- a/rtc_base/experiments/BUILD.gn +++ b/rtc_base/experiments/BUILD.gn @@ -78,6 +78,21 @@ rtc_library("quality_scaler_settings") { absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } +rtc_library("bandwidth_quality_scaler_settings") { + sources = [ + "bandwidth_quality_scaler_settings.cc", + "bandwidth_quality_scaler_settings.h", + ] + deps = [ + ":field_trial_parser", + "../:rtc_base_approved", + "../../api/transport:field_trial_based_config", + "../../api/transport:webrtc_key_value_config", + "../../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + rtc_library("quality_scaling_experiment") { sources = [ "quality_scaling_experiment.cc", @@ -111,6 +126,7 @@ rtc_library("balanced_degradation_settings") { deps = [ ":field_trial_parser", "../:rtc_base_approved", + "../../api:webrtc_key_value_config", "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", ] @@ -141,7 +157,10 @@ rtc_library("encoder_info_settings") { "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("rtt_mult_experiment") { @@ -237,6 +256,7 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "balanced_degradation_settings_unittest.cc", + "bandwidth_quality_scaler_settings_unittest.cc", "cpu_speed_experiment_unittest.cc", "encoder_info_settings_unittest.cc", "field_trial_list_unittest.cc", @@ -255,6 +275,7 @@ if (rtc_include_tests && !build_with_chromium) { ] deps = [ ":balanced_degradation_settings", + ":bandwidth_quality_scaler_settings", ":cpu_speed_experiment", ":encoder_info_settings", ":field_trial_parser", @@ -274,9 +295,13 @@ if (rtc_include_tests && !build_with_chromium) { "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", "../../test:field_trial", + "../../test:scoped_key_value_config", "../../test:test_main", "../../test:test_support", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } } diff --git a/rtc_base/experiments/balanced_degradation_settings.cc b/rtc_base/experiments/balanced_degradation_settings.cc index 90d44efb10..7cd1a675d4 100644 --- a/rtc_base/experiments/balanced_degradation_settings.cc +++ b/rtc_base/experiments/balanced_degradation_settings.cc @@ -15,7 +15,6 @@ #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -332,7 +331,8 @@ BalancedDegradationSettings::Config::Config(int pixels, av1(av1), generic(generic) {} -BalancedDegradationSettings::BalancedDegradationSettings() { +BalancedDegradationSettings::BalancedDegradationSettings( + const WebRtcKeyValueConfig& field_trials) { FieldTrialStructList configs( {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }), FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }), @@ -390,7 +390,7 @@ BalancedDegradationSettings::BalancedDegradationSettings() { [](Config* c) { return &c->generic.kbps_res; })}, {}); - ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial)); + ParseFieldTrial({&configs}, field_trials.Lookup(kFieldTrial)); configs_ = GetValidOrDefault(configs.Get()); RTC_DCHECK_GT(configs_.size(), 1); diff --git a/rtc_base/experiments/balanced_degradation_settings.h b/rtc_base/experiments/balanced_degradation_settings.h index 0b2f2f5993..8255547cd0 100644 --- a/rtc_base/experiments/balanced_degradation_settings.h +++ b/rtc_base/experiments/balanced_degradation_settings.h @@ -15,6 +15,7 @@ #include "absl/types/optional.h" #include "api/video_codecs/video_encoder.h" +#include "api/webrtc_key_value_config.h" namespace webrtc { @@ -22,7 +23,7 @@ class BalancedDegradationSettings { public: static constexpr int kNoFpsDiff = -100; - BalancedDegradationSettings(); + BalancedDegradationSettings(const WebRtcKeyValueConfig& field_trials); ~BalancedDegradationSettings(); struct CodecTypeSpecific { diff --git a/rtc_base/experiments/balanced_degradation_settings_unittest.cc b/rtc_base/experiments/balanced_degradation_settings_unittest.cc index 92833ee98c..a32dbb4aaa 100644 --- a/rtc_base/experiments/balanced_degradation_settings_unittest.cc +++ b/rtc_base/experiments/balanced_degradation_settings_unittest.cc @@ -13,8 +13,8 @@ #include #include "rtc_base/gunit.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace { @@ -59,8 +59,8 @@ void VerifyIsDefault( } // namespace TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) { - webrtc::test::ScopedFieldTrials field_trials(""); - BalancedDegradationSettings settings; + webrtc::test::ScopedKeyValueConfig field_trials(""); + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); EXPECT_TRUE(settings.CanAdaptUp(kVideoCodecVP8, 1, /*bitrate_bps*/ 1)); EXPECT_TRUE( @@ -75,10 +75,10 @@ TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) { } TEST(BalancedDegradationSettings, GetsConfig) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,other:4|5|6/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -117,35 +117,35 @@ TEST(BalancedDegradationSettings, GetsConfig) { } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroFpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:0|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfPixelsDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|999|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfFramerateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|4|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|8|9,vp9_fps:9|10|11," "h264_fps:11|12|13,av1_fps:1|2|3,generic_fps:13|14|15/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -184,34 +184,34 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) { } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroVp8FpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:0|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigForInvalidFpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:10|15|2000/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfVp8FramerateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:4|5|25,vp8_fps:5|4|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsMinFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1)); EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1000)); EXPECT_EQ(15, settings.MinFps(kVideoCodecVP8, 1001)); @@ -223,10 +223,10 @@ TEST(BalancedDegradationSettings, GetsMinFps) { } TEST(BalancedDegradationSettings, GetsVp8MinFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1)); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1000)); EXPECT_EQ(10, settings.MinFps(kVideoCodecVP8, 1001)); @@ -238,10 +238,10 @@ TEST(BalancedDegradationSettings, GetsVp8MinFps) { } TEST(BalancedDegradationSettings, GetsMaxFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1)); EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1000)); EXPECT_EQ(25, settings.MaxFps(kVideoCodecVP8, 1001)); @@ -251,10 +251,10 @@ TEST(BalancedDegradationSettings, GetsMaxFps) { } TEST(BalancedDegradationSettings, GetsVp8MaxFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1)); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1000)); EXPECT_EQ(12, settings.MaxFps(kVideoCodecVP8, 1001)); @@ -264,39 +264,39 @@ TEST(BalancedDegradationSettings, GetsVp8MaxFps) { } TEST(BalancedDegradationSettings, GetsVp9Fps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp9_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP9, 1000)); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP9, 1000)); } TEST(BalancedDegradationSettings, GetsH264Fps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,h264_fps:8|11|13/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(11, settings.MinFps(kVideoCodecH264, 2000)); EXPECT_EQ(13, settings.MaxFps(kVideoCodecH264, 2000)); } TEST(BalancedDegradationSettings, GetsGenericFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,generic_fps:9|12|14/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(14, settings.MinFps(kVideoCodecGeneric, 3000)); EXPECT_EQ(std::numeric_limits::max(), settings.MaxFps(kVideoCodecGeneric, 3000)); } TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|100,vp8_fps:30|100|100/"); const int kUnlimitedFps = std::numeric_limits::max(); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(15, settings.MinFps(kVideoCodecH264, 2000)); EXPECT_EQ(kUnlimitedFps, settings.MinFps(kVideoCodecH264, 2001)); EXPECT_EQ(30, settings.MinFps(kVideoCodecVP8, 1000)); @@ -304,7 +304,7 @@ TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) { } TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|88|99,kbps_res:55|111|222," "vp8_kbps:11|12|13,vp8_kbps_res:14|15|16," @@ -312,7 +312,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { "h264_kbps:31|32|33,h264_kbps_res:34|35|36," "av1_kbps:41|42|43,av1_kbps_res:44|45|46," "generic_kbps:51|52|53,generic_kbps_res:54|55|56/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -351,29 +351,29 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { } TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|43|99/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreasesWithUnsetValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|0|43/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, CanAdaptUp) { VideoCodecType vp8 = kVideoCodecVP8; - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,kbps:0|80|0|90," "vp9_kbps:40|50|60|70/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_TRUE(s.CanAdaptUp(vp8, 1000, 0)); // No bitrate provided. EXPECT_FALSE(s.CanAdaptUp(vp8, 1000, 79000)); EXPECT_TRUE(s.CanAdaptUp(vp8, 1000, 80000)); @@ -384,12 +384,12 @@ TEST(BalancedDegradationSettings, CanAdaptUp) { } TEST(BalancedDegradationSettings, CanAdaptUpWithCodecType) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,vp8_kbps:0|30|40|50," "vp9_kbps:0|60|70|80,h264_kbps:0|55|65|75,av1_kbps:0|77|88|99," "generic_kbps:0|25|35|45/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_FALSE(s.CanAdaptUp(kVideoCodecVP8, 1000, 29000)); EXPECT_TRUE(s.CanAdaptUp(kVideoCodecVP8, 1000, 30000)); EXPECT_FALSE(s.CanAdaptUp(kVideoCodecVP9, 1000, 59000)); @@ -405,11 +405,11 @@ TEST(BalancedDegradationSettings, CanAdaptUpWithCodecType) { TEST(BalancedDegradationSettings, CanAdaptUpResolution) { VideoCodecType vp8 = kVideoCodecVP8; - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,kbps_res:0|80|0|90," "vp9_kbps_res:40|50|60|70/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_TRUE(s.CanAdaptUpResolution(vp8, 1000, 0)); // No bitrate provided. EXPECT_FALSE(s.CanAdaptUpResolution(vp8, 1000, 79000)); EXPECT_TRUE(s.CanAdaptUpResolution(vp8, 1000, 80000)); @@ -420,12 +420,12 @@ TEST(BalancedDegradationSettings, CanAdaptUpResolution) { } TEST(BalancedDegradationSettings, CanAdaptUpResolutionWithCodecType) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,vp8_kbps_res:0|30|40|50," "vp9_kbps_res:0|60|70|80,h264_kbps_res:0|55|65|75," "av1_kbps_res:0|77|88|99,generic_kbps_res:0|25|35|45/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_FALSE(s.CanAdaptUpResolution(kVideoCodecVP8, 1000, 29000)); EXPECT_TRUE(s.CanAdaptUpResolution(kVideoCodecVP8, 1000, 30000)); EXPECT_FALSE(s.CanAdaptUpResolution(kVideoCodecVP9, 1000, 59000)); @@ -441,10 +441,10 @@ TEST(BalancedDegradationSettings, CanAdaptUpResolutionWithCodecType) { } TEST(BalancedDegradationSettings, GetsFpsDiff) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,fps_diff:0|-2|3/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(0, settings.MinFpsDiff(1)); EXPECT_EQ(0, settings.MinFpsDiff(1000)); EXPECT_EQ(-2, settings.MinFpsDiff(1001)); @@ -455,21 +455,21 @@ TEST(BalancedDegradationSettings, GetsFpsDiff) { } TEST(BalancedDegradationSettings, GetsNoFpsDiffIfValueBelowMinSetting) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,fps_diff:-100|-99|-101/"); // Min valid fps_diff setting: -99. - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_FALSE(settings.MinFpsDiff(1000)); EXPECT_EQ(-99, settings.MinFpsDiff(2000)); EXPECT_FALSE(settings.MinFpsDiff(3000)); } TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecH264, 1)); @@ -478,13 +478,13 @@ TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) { } TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_low:89|90|88," "vp8_qp_high:90|91|92,vp9_qp_low:27|28|29,vp9_qp_high:120|130|140," "h264_qp_low:12|13|14,h264_qp_high:20|30|40,av1_qp_low:2|3|4," "av1_qp_high:11|33|44,generic_qp_low:7|6|5,generic_qp_high:22|23|24/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -523,54 +523,54 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) { } TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasLowThreshold) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_low:89|90|88/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasHighThreshold) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfLowEqualsHigh) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|88/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfLowGreaterThanHigh) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|87/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroQpValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|0|88,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsVp8QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(89, settings.GetQpThresholds(kVideoCodecVP8, 1)->low); EXPECT_EQ(90, settings.GetQpThresholds(kVideoCodecVP8, 1)->high); EXPECT_EQ(90, settings.GetQpThresholds(kVideoCodecVP8, 1000)->high); @@ -582,11 +582,11 @@ TEST(BalancedDegradationSettings, GetsVp8QpThresholds) { } TEST(BalancedDegradationSettings, GetsVp9QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp9_qp_low:55|56|57,vp9_qp_high:155|156|157/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecVP9, 1000); EXPECT_TRUE(thresholds); EXPECT_EQ(55, thresholds->low); @@ -594,11 +594,11 @@ TEST(BalancedDegradationSettings, GetsVp9QpThresholds) { } TEST(BalancedDegradationSettings, GetsH264QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "h264_qp_low:21|22|23,h264_qp_high:41|43|42/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecH264, 2000); EXPECT_TRUE(thresholds); EXPECT_EQ(22, thresholds->low); @@ -606,11 +606,11 @@ TEST(BalancedDegradationSettings, GetsH264QpThresholds) { } TEST(BalancedDegradationSettings, GetsGenericQpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "generic_qp_low:2|3|4,generic_qp_high:22|23|24/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecGeneric, 3000); EXPECT_TRUE(thresholds); EXPECT_EQ(4, thresholds->low); diff --git a/rtc_base/experiments/bandwidth_quality_scaler_settings.cc b/rtc_base/experiments/bandwidth_quality_scaler_settings.cc new file mode 100644 index 0000000000..332ab6be4b --- /dev/null +++ b/rtc_base/experiments/bandwidth_quality_scaler_settings.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/experiments/bandwidth_quality_scaler_settings.h" + +#include "api/transport/field_trial_based_config.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +BandwidthQualityScalerSettings::BandwidthQualityScalerSettings( + const WebRtcKeyValueConfig* const key_value_config) + : bitrate_state_update_interval_s_("bitrate_state_update_interval_s_") { + ParseFieldTrial( + {&bitrate_state_update_interval_s_}, + key_value_config->Lookup("WebRTC-Video-BandwidthQualityScalerSettings")); +} + +BandwidthQualityScalerSettings +BandwidthQualityScalerSettings::ParseFromFieldTrials() { + FieldTrialBasedConfig field_trial_config; + return BandwidthQualityScalerSettings(&field_trial_config); +} + +absl::optional +BandwidthQualityScalerSettings::BitrateStateUpdateInterval() const { + if (bitrate_state_update_interval_s_ && + bitrate_state_update_interval_s_.Value() <= 0) { + RTC_LOG(LS_WARNING) + << "Unsupported bitrate_state_update_interval_s_ value, ignored."; + return absl::nullopt; + } + return bitrate_state_update_interval_s_.GetOptional(); +} + +} // namespace webrtc diff --git a/rtc_base/experiments/bandwidth_quality_scaler_settings.h b/rtc_base/experiments/bandwidth_quality_scaler_settings.h new file mode 100644 index 0000000000..959aea5bd3 --- /dev/null +++ b/rtc_base/experiments/bandwidth_quality_scaler_settings.h @@ -0,0 +1,35 @@ +/* + * 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_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_ + +#include "absl/types/optional.h" +#include "api/transport/webrtc_key_value_config.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +class BandwidthQualityScalerSettings final { + public: + static BandwidthQualityScalerSettings ParseFromFieldTrials(); + + absl::optional BitrateStateUpdateInterval() const; + + private: + explicit BandwidthQualityScalerSettings( + const WebRtcKeyValueConfig* const key_value_config); + + FieldTrialOptional bitrate_state_update_interval_s_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_ diff --git a/rtc_base/experiments/bandwidth_quality_scaler_settings_unittest.cc b/rtc_base/experiments/bandwidth_quality_scaler_settings_unittest.cc new file mode 100644 index 0000000000..fab22cede0 --- /dev/null +++ b/rtc_base/experiments/bandwidth_quality_scaler_settings_unittest.cc @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#include "rtc_base/experiments/bandwidth_quality_scaler_settings.h" + +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(BandwidthQualityScalerSettingsTest, ValuesNotSetByDefault) { + const auto settings = BandwidthQualityScalerSettings::ParseFromFieldTrials(); + EXPECT_FALSE(settings.BitrateStateUpdateInterval()); +} + +TEST(BandwidthQualityScalerSettingsTest, ParseBitrateStateUpdateInterval) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-BandwidthQualityScalerSettings/" + "bitrate_state_update_interval_s_:100/"); + EXPECT_EQ(100u, BandwidthQualityScalerSettings::ParseFromFieldTrials() + .BitrateStateUpdateInterval()); +} + +TEST(BandwidthQualityScalerSettingsTest, ParseAll) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-BandwidthQualityScalerSettings/" + "bitrate_state_update_interval_s_:100/"); + EXPECT_EQ(100u, BandwidthQualityScalerSettings::ParseFromFieldTrials() + .BitrateStateUpdateInterval()); +} + +TEST(BandwidthQualityScalerSettingsTest, DoesNotParseIncorrectValue) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-BandwidthQualityScalerSettings/" + "bitrate_state_update_interval_s_:??/"); + const auto settings = BandwidthQualityScalerSettings::ParseFromFieldTrials(); + EXPECT_FALSE(settings.BitrateStateUpdateInterval()); +} + +} // namespace +} // namespace webrtc diff --git a/rtc_base/experiments/encoder_info_settings.cc b/rtc_base/experiments/encoder_info_settings.cc index 9e1a5190a3..a74eb50a43 100644 --- a/rtc_base/experiments/encoder_info_settings.cc +++ b/rtc_base/experiments/encoder_info_settings.cc @@ -12,6 +12,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/logging.h" #include "system_wrappers/include/field_trial.h" @@ -29,7 +30,7 @@ std::vector ToResolutionBitrateLimits( } return result; } - +constexpr float kDefaultMinBitratebps = 30000; } // namespace // Default bitrate limits for simulcast with one active stream: @@ -63,7 +64,99 @@ EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution( return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels); } -EncoderInfoSettings::EncoderInfoSettings(std::string name) +// Return the suitable bitrate limits for specified resolution when qp is +// untrusted, they are experimental values. +// TODO(bugs.webrtc.org/12942): Maybe we need to add other codecs(VP8/VP9) +// experimental values. +std::vector +EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted() { + // Specific limits for H264/AVC + return {{0 * 0, 0, 0, 0}, + {320 * 180, 0, 30000, 300000}, + {480 * 270, 300000, 30000, 500000}, + {640 * 360, 500000, 30000, 800000}, + {960 * 540, 800000, 30000, 1500000}, + {1280 * 720, 1500000, 30000, 2500000}, + {1920 * 1080, 2500000, 30000, 4000000}}; +} + +// Through linear interpolation, return the bitrate limit corresponding to the +// specified |frame_size_pixels|. +absl::optional +EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( + absl::optional frame_size_pixels, + const std::vector& + resolution_bitrate_limits) { + if (!frame_size_pixels.has_value() || frame_size_pixels.value() <= 0) { + return absl::nullopt; + } + + std::vector bitrate_limits = + resolution_bitrate_limits; + + // Sort the list of bitrate limits by resolution. + sort(bitrate_limits.begin(), bitrate_limits.end(), + [](const VideoEncoder::ResolutionBitrateLimits& lhs, + const VideoEncoder::ResolutionBitrateLimits& rhs) { + return lhs.frame_size_pixels < rhs.frame_size_pixels; + }); + + if (bitrate_limits.empty()) { + return absl::nullopt; + } + + int interpolation_index = -1; + for (size_t i = 0; i < bitrate_limits.size(); ++i) { + if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels.value()) { + interpolation_index = i; + break; + } + } + + // -1 means that the maximum resolution is exceeded, we will select the + // largest data as the return result. + if (interpolation_index == -1) { + return *bitrate_limits.rbegin(); + } + + // If we have a matching resolution, return directly without interpolation. + if (bitrate_limits[interpolation_index].frame_size_pixels == + frame_size_pixels.value()) { + return bitrate_limits[interpolation_index]; + } + + // No matching resolution, do a linear interpolate. + int lower_pixel_count = + bitrate_limits[interpolation_index - 1].frame_size_pixels; + int upper_pixel_count = bitrate_limits[interpolation_index].frame_size_pixels; + float alpha = (frame_size_pixels.value() - lower_pixel_count) * 1.0 / + (upper_pixel_count - lower_pixel_count); + int min_start_bitrate_bps = static_cast( + bitrate_limits[interpolation_index].min_start_bitrate_bps * alpha + + bitrate_limits[interpolation_index - 1].min_start_bitrate_bps * + (1.0 - alpha)); + int max_bitrate_bps = static_cast( + bitrate_limits[interpolation_index].max_bitrate_bps * alpha + + bitrate_limits[interpolation_index - 1].max_bitrate_bps * (1.0 - alpha)); + + if (max_bitrate_bps >= min_start_bitrate_bps) { + return VideoEncoder::ResolutionBitrateLimits( + frame_size_pixels.value(), min_start_bitrate_bps, kDefaultMinBitratebps, + max_bitrate_bps); + } else { + RTC_LOG(LS_WARNING) + << "BitRate interpolation calculating result is abnormal. " + << " lower_pixel_count = " << lower_pixel_count + << " upper_pixel_count = " << upper_pixel_count + << " frame_size_pixels = " << frame_size_pixels.value() + << " min_start_bitrate_bps = " << min_start_bitrate_bps + << " min_bitrate_bps = " << kDefaultMinBitratebps + << " max_bitrate_bps = " << max_bitrate_bps; + return absl::nullopt; + } +} + +EncoderInfoSettings::EncoderInfoSettings(absl::string_view name) : requested_resolution_alignment_("requested_resolution_alignment"), apply_alignment_to_all_simulcast_layers_( "apply_alignment_to_all_simulcast_layers") { @@ -82,14 +175,15 @@ EncoderInfoSettings::EncoderInfoSettings(std::string name) [](BitrateLimit* b) { return &b->max_bitrate_bps; })}, {}); - if (field_trial::FindFullName(name).empty()) { + std::string name_str = std::string(name); + if (field_trial::FindFullName(name_str).empty()) { // Encoder name not found, use common string applying to all encoders. - name = "WebRTC-GetEncoderInfoOverride"; + name_str = "WebRTC-GetEncoderInfoOverride"; } ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_, &apply_alignment_to_all_simulcast_layers_}, - field_trial::FindFullName(name)); + field_trial::FindFullName(name_str)); resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get()); } diff --git a/rtc_base/experiments/encoder_info_settings.h b/rtc_base/experiments/encoder_info_settings.h index 9cbb5875bb..d450697f47 100644 --- a/rtc_base/experiments/encoder_info_settings.h +++ b/rtc_base/experiments/encoder_info_settings.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/video_codecs/video_encoder.h" #include "rtc_base/experiments/field_trial_parser.h" @@ -48,8 +49,17 @@ class EncoderInfoSettings { GetDefaultSinglecastBitrateLimitsForResolution(VideoCodecType codec_type, int frame_size_pixels); + static std::vector + GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted(); + + static absl::optional + GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( + absl::optional frame_size_pixels, + const std::vector& + resolution_bitrate_limits); + protected: - explicit EncoderInfoSettings(std::string name); + explicit EncoderInfoSettings(absl::string_view name); private: FieldTrialOptional requested_resolution_alignment_; diff --git a/rtc_base/experiments/field_trial_list.cc b/rtc_base/experiments/field_trial_list.cc index de897bfbef..72cd79f2d2 100644 --- a/rtc_base/experiments/field_trial_list.cc +++ b/rtc_base/experiments/field_trial_list.cc @@ -9,9 +9,11 @@ */ #include "rtc_base/experiments/field_trial_list.h" +#include "absl/strings/string_view.h" + namespace webrtc { -FieldTrialListBase::FieldTrialListBase(std::string key) +FieldTrialListBase::FieldTrialListBase(absl::string_view key) : FieldTrialParameterInterface(key), failed_(false), parse_got_called_(false) {} @@ -34,7 +36,7 @@ bool FieldTrialListWrapper::Used() { } bool FieldTrialStructListBase::Parse(absl::optional str_value) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return true; } diff --git a/rtc_base/experiments/field_trial_list.h b/rtc_base/experiments/field_trial_list.h index 877e29a699..7b9f0d9dfc 100644 --- a/rtc_base/experiments/field_trial_list.h +++ b/rtc_base/experiments/field_trial_list.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/string_encode.h" @@ -36,7 +37,7 @@ namespace webrtc { class FieldTrialListBase : public FieldTrialParameterInterface { protected: friend class FieldTrialListWrapper; - explicit FieldTrialListBase(std::string key); + explicit FieldTrialListBase(absl::string_view key); bool Failed() const; bool Used() const; @@ -52,13 +53,15 @@ class FieldTrialListBase : public FieldTrialParameterInterface { template class FieldTrialList : public FieldTrialListBase { public: - explicit FieldTrialList(std::string key) : FieldTrialList(key, {}) {} - FieldTrialList(std::string key, std::initializer_list default_values) + explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {} + FieldTrialList(absl::string_view key, std::initializer_list default_values) : FieldTrialListBase(key), values_(default_values) {} std::vector Get() const { return values_; } operator std::vector() const { return Get(); } - const T& operator[](size_t index) const { return values_[index]; } + typename std::vector::const_reference operator[](size_t index) const { + return values_[index]; + } const std::vector* operator->() const { return &values_; } protected: @@ -130,7 +133,7 @@ struct LambdaTypeTraits { template struct TypedFieldTrialListWrapper : FieldTrialListWrapper { public: - TypedFieldTrialListWrapper(std::string key, + TypedFieldTrialListWrapper(absl::string_view key, std::function sink) : list_(key), sink_(sink) {} @@ -149,7 +152,8 @@ struct TypedFieldTrialListWrapper : FieldTrialListWrapper { template > -FieldTrialListWrapper* FieldTrialStructMember(std::string key, F accessor) { +FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key, + F accessor) { return new field_trial_list_impl::TypedFieldTrialListWrapper< typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) { *accessor(static_cast(s)) = t; diff --git a/rtc_base/experiments/field_trial_list_unittest.cc b/rtc_base/experiments/field_trial_list_unittest.cc index a1abfe4bf8..221a3c6929 100644 --- a/rtc_base/experiments/field_trial_list_unittest.cc +++ b/rtc_base/experiments/field_trial_list_unittest.cc @@ -10,6 +10,7 @@ #include "rtc_base/experiments/field_trial_list.h" +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "test/gmock.h" @@ -21,13 +22,16 @@ namespace webrtc { struct Garment { int price = 0; std::string color = ""; + bool has_glitter = false; // Only needed for testing. Garment() = default; - Garment(int p, std::string c) : price(p), color(c) {} + Garment(int p, absl::string_view c, bool g) + : price(p), color(c), has_glitter(g) {} bool operator==(const Garment& other) const { - return price == other.price && color == other.color; + return price == other.price && color == other.color && + has_glitter == other.has_glitter; } }; @@ -60,17 +64,20 @@ TEST(FieldTrialListTest, ParsesListParameter) { TEST(FieldTrialListTest, ParsesStructList) { FieldTrialStructList my_list( {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), - FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, "blue"}, {2, "red"}}); + FieldTrialStructMember("price", [](Garment* g) { return &g->price; }), + FieldTrialStructMember("has_glitter", + [](Garment* g) { return &g->has_glitter; })}, + {{1, "blue", false}, {2, "red", true}}); ParseFieldTrial({&my_list}, "color:mauve|red|gold," "price:10|20|30," + "has_glitter:1|0|1," "other_param:asdf"); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{10, "mauve"}, Garment{20, "red"}, - Garment{30, "gold"})); + ElementsAre(Garment{10, "mauve", true}, Garment{20, "red", false}, + Garment{30, "gold", true})); } // One FieldTrialList has the wrong length, so we use the user-provided default @@ -80,7 +87,7 @@ TEST(FieldTrialListTest, StructListKeepsDefaultWithMismatchingLength) { {FieldTrialStructMember("wrong_length", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, "blue"}, {2, "red"}}); + {{1, "blue", true}, {2, "red", false}}); ParseFieldTrial({&my_list}, "wrong_length:mauve|magenta|chartreuse|indigo," @@ -88,7 +95,7 @@ TEST(FieldTrialListTest, StructListKeepsDefaultWithMismatchingLength) { "price:10|20|30"); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{1, "blue"}, Garment{2, "red"})); + ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false})); } // One list is missing. We set the values we're given, and the others remain @@ -97,12 +104,13 @@ TEST(FieldTrialListTest, StructListUsesDefaultForMissingList) { FieldTrialStructList my_list( {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, "blue"}, {2, "red"}}); + {{1, "blue", true}, {2, "red", false}}); ParseFieldTrial({&my_list}, "price:10|20|30"); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{10, ""}, Garment{20, ""}, Garment{30, ""})); + ElementsAre(Garment{10, "", false}, Garment{20, "", false}, + Garment{30, "", false})); } // The user haven't provided values for any lists, so we use the default list. @@ -110,12 +118,12 @@ TEST(FieldTrialListTest, StructListUsesDefaultListWithoutValues) { FieldTrialStructList my_list( {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, "blue"}, {2, "red"}}); + {{1, "blue", true}, {2, "red", false}}); ParseFieldTrial({&my_list}, ""); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{1, "blue"}, Garment{2, "red"})); + ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false})); } // Some lists are provided and all are empty, so we return a empty list. @@ -123,7 +131,7 @@ TEST(FieldTrialListTest, StructListHandlesEmptyLists) { FieldTrialStructList my_list( {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, "blue"}, {2, "red"}}); + {{1, "blue", true}, {2, "red", false}}); ParseFieldTrial({&my_list}, "color,price"); diff --git a/rtc_base/experiments/field_trial_parser.cc b/rtc_base/experiments/field_trial_parser.cc index 8fc89cec8f..78d5489f5e 100644 --- a/rtc_base/experiments/field_trial_parser.cc +++ b/rtc_base/experiments/field_trial_parser.cc @@ -16,21 +16,15 @@ #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 { -namespace { -int FindOrEnd(std::string str, size_t start, char delimiter) { - size_t pos = str.find(delimiter, start); - pos = (pos == std::string::npos) ? str.length() : pos; - return static_cast(pos); -} -} // namespace - -FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key) +FieldTrialParameterInterface::FieldTrialParameterInterface( + absl::string_view key) : key_(key) {} FieldTrialParameterInterface::~FieldTrialParameterInterface() { RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ @@ -39,8 +33,8 @@ FieldTrialParameterInterface::~FieldTrialParameterInterface() { void ParseFieldTrial( std::initializer_list fields, - std::string trial_string) { - std::map field_map; + absl::string_view trial_string) { + std::map field_map; FieldTrialParameterInterface* keyless_field = nullptr; for (FieldTrialParameterInterface* field : fields) { field->MarkAsUsed(); @@ -60,18 +54,29 @@ void ParseFieldTrial( field_map[field->key_] = field; } } + bool logged_unknown_key = false; - size_t i = 0; - while (i < trial_string.length()) { - int val_end = FindOrEnd(trial_string, i, ','); - int colon_pos = FindOrEnd(trial_string, i, ':'); - int key_end = std::min(val_end, colon_pos); - int val_begin = key_end + 1; - std::string key = trial_string.substr(i, key_end - i); + 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); absl::optional opt_value; - if (val_end >= val_begin) - opt_value = trial_string.substr(val_begin, val_end - val_begin); - i = val_end + 1; + 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))) { @@ -79,7 +84,7 @@ void ParseFieldTrial( << "' in trial: \"" << trial_string << "\""; } } else if (!opt_value && keyless_field && !key.empty()) { - if (!keyless_field->Parse(key)) { + if (!keyless_field->Parse(std::string(key))) { RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" << key << "' in trial: \"" << trial_string << "\""; } @@ -87,14 +92,17 @@ void ParseFieldTrial( // "_" 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 - 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 += f.first; - valid_keys += ", "; + 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; } - RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys; } } @@ -104,7 +112,7 @@ void ParseFieldTrial( } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { if (str == "true" || str == "1") { return true; } else if (str == "false" || str == "0") { @@ -114,10 +122,10 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { double value; char unit[2]{0, 0}; - if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) { + if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) { if (unit[0] == '%') return value / 100; return value; @@ -127,9 +135,9 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { int64_t value; - if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } @@ -138,9 +146,9 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { int64_t value; - if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } @@ -149,34 +157,36 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { - return std::move(str); +absl::optional ParseTypedParameter( + absl::string_view str) { + return std::string(str); } template <> absl::optional> ParseTypedParameter>( - std::string str) { + absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> ParseTypedParameter>( - std::string str) { + absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } -FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {} +FieldTrialFlag::FieldTrialFlag(absl::string_view key) + : FieldTrialFlag(key, false) {} -FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value) +FieldTrialFlag::FieldTrialFlag(absl::string_view key, bool default_value) : FieldTrialParameterInterface(key), value_(default_value) {} bool FieldTrialFlag::Get() const { @@ -201,7 +211,7 @@ bool FieldTrialFlag::Parse(absl::optional str_value) { } AbstractFieldTrialEnum::AbstractFieldTrialEnum( - std::string key, + absl::string_view key, int default_value, std::map mapping) : FieldTrialParameterInterface(key), diff --git a/rtc_base/experiments/field_trial_parser.h b/rtc_base/experiments/field_trial_parser.h index 42535ed6a4..822895e70b 100644 --- a/rtc_base/experiments/field_trial_parser.h +++ b/rtc_base/experiments/field_trial_parser.h @@ -18,6 +18,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" // Field trial parser functionality. Provides funcitonality to parse field trial @@ -45,10 +46,10 @@ class FieldTrialParameterInterface { FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default; FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) = default; - explicit FieldTrialParameterInterface(std::string key); + explicit FieldTrialParameterInterface(absl::string_view key); friend void ParseFieldTrial( std::initializer_list fields, - std::string raw_string); + absl::string_view trial_string); void MarkAsUsed() { used_ = true; } virtual bool Parse(absl::optional str_value) = 0; @@ -65,19 +66,19 @@ class FieldTrialParameterInterface { // with extracted values if available. void ParseFieldTrial( std::initializer_list fields, - std::string raw_string); + absl::string_view trial_string); // Specialize this in code file for custom types. Should return absl::nullopt if // the given string cannot be properly parsed. template -absl::optional ParseTypedParameter(std::string); +absl::optional ParseTypedParameter(absl::string_view); // This class uses the ParseTypedParameter function to implement a parameter // implementation with an enforced default value. template class FieldTrialParameter : public FieldTrialParameterInterface { public: - FieldTrialParameter(std::string key, T default_value) + FieldTrialParameter(absl::string_view key, T default_value) : FieldTrialParameterInterface(key), value_(default_value) {} T Get() const { return value_; } operator T() const { return Get(); } @@ -107,7 +108,7 @@ class FieldTrialParameter : public FieldTrialParameterInterface { template class FieldTrialConstrained : public FieldTrialParameterInterface { public: - FieldTrialConstrained(std::string key, + FieldTrialConstrained(absl::string_view key, T default_value, absl::optional lower_limit, absl::optional upper_limit) @@ -140,7 +141,7 @@ class FieldTrialConstrained : public FieldTrialParameterInterface { class AbstractFieldTrialEnum : public FieldTrialParameterInterface { public: - AbstractFieldTrialEnum(std::string key, + AbstractFieldTrialEnum(absl::string_view key, int default_value, std::map mapping); ~AbstractFieldTrialEnum() override; @@ -161,7 +162,7 @@ class AbstractFieldTrialEnum : public FieldTrialParameterInterface { template class FieldTrialEnum : public AbstractFieldTrialEnum { public: - FieldTrialEnum(std::string key, + FieldTrialEnum(absl::string_view key, T default_value, std::map mapping) : AbstractFieldTrialEnum(key, @@ -184,9 +185,9 @@ class FieldTrialEnum : public AbstractFieldTrialEnum { template class FieldTrialOptional : public FieldTrialParameterInterface { public: - explicit FieldTrialOptional(std::string key) + explicit FieldTrialOptional(absl::string_view key) : FieldTrialParameterInterface(key) {} - FieldTrialOptional(std::string key, absl::optional default_value) + FieldTrialOptional(absl::string_view key, absl::optional default_value) : FieldTrialParameterInterface(key), value_(default_value) {} absl::optional GetOptional() const { return value_; } const T& Value() const { return value_.value(); } @@ -216,10 +217,10 @@ class FieldTrialOptional : public FieldTrialParameterInterface { // explicit value is provided, the flag evaluates to true. class FieldTrialFlag : public FieldTrialParameterInterface { public: - explicit FieldTrialFlag(std::string key); - FieldTrialFlag(std::string key, bool default_value); + explicit FieldTrialFlag(absl::string_view key); + FieldTrialFlag(absl::string_view key, bool default_value); bool Get() const; - operator bool() const; + explicit operator bool() const; protected: bool Parse(absl::optional str_value) override; @@ -229,7 +230,8 @@ class FieldTrialFlag : public FieldTrialParameterInterface { }; template -absl::optional> ParseOptionalParameter(std::string str) { +absl::optional> ParseOptionalParameter( + absl::string_view str) { if (str.empty()) return absl::optional(); auto parsed = ParseTypedParameter(str); @@ -239,28 +241,29 @@ absl::optional> ParseOptionalParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter( + absl::string_view str); template <> absl::optional> ParseTypedParameter>( - std::string str); + absl::string_view str); template <> absl::optional> ParseTypedParameter>( - std::string str); + absl::string_view str); template <> absl::optional> -ParseTypedParameter>(std::string str); +ParseTypedParameter>(absl::string_view str); template <> absl::optional> -ParseTypedParameter>(std::string str); +ParseTypedParameter>(absl::string_view str); // Accepts true, false, else parsed with sscanf %i, true if != 0. extern template class FieldTrialParameter; diff --git a/rtc_base/experiments/field_trial_parser_unittest.cc b/rtc_base/experiments/field_trial_parser_unittest.cc index d36b3c7d95..9916edee97 100644 --- a/rtc_base/experiments/field_trial_parser_unittest.cc +++ b/rtc_base/experiments/field_trial_parser_unittest.cc @@ -9,6 +9,7 @@ */ #include "rtc_base/experiments/field_trial_parser.h" +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/gunit.h" #include "system_wrappers/include/field_trial.h" @@ -28,7 +29,7 @@ struct DummyExperiment { FieldTrialParameter hash = FieldTrialParameter("h", "a80"); - explicit DummyExperiment(std::string field_trial) { + explicit DummyExperiment(absl::string_view field_trial) { ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash}, field_trial); } diff --git a/rtc_base/experiments/field_trial_units.cc b/rtc_base/experiments/field_trial_units.cc index 5aceab76a0..92af46a9e3 100644 --- a/rtc_base/experiments/field_trial_units.cc +++ b/rtc_base/experiments/field_trial_units.cc @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" // Large enough to fit "seconds", the longest supported unit name. @@ -28,7 +29,7 @@ struct ValueWithUnit { std::string unit; }; -absl::optional ParseValueWithUnit(std::string str) { +absl::optional ParseValueWithUnit(absl::string_view str) { if (str == "inf") { return ValueWithUnit{std::numeric_limits::infinity(), ""}; } else if (str == "-inf") { @@ -37,8 +38,8 @@ absl::optional ParseValueWithUnit(std::string str) { double double_val; char unit_char[RTC_TRIAL_UNIT_SIZE]; unit_char[0] = 0; - if (sscanf(str.c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s", &double_val, - unit_char) >= 1) { + if (sscanf(std::string(str).c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s", + &double_val, unit_char) >= 1) { return ValueWithUnit{double_val, unit_char}; } } @@ -47,7 +48,7 @@ absl::optional ParseValueWithUnit(std::string str) { } // namespace template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit.empty() || result->unit == "kbps") { @@ -60,7 +61,7 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit.empty() || result->unit == "bytes") @@ -70,7 +71,8 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter( + absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit == "s" || result->unit == "seconds") { @@ -86,17 +88,17 @@ absl::optional ParseTypedParameter(std::string str) { template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } diff --git a/rtc_base/experiments/field_trial_units.h b/rtc_base/experiments/field_trial_units.h index d85b2f04ba..408367c031 100644 --- a/rtc_base/experiments/field_trial_units.h +++ b/rtc_base/experiments/field_trial_units.h @@ -10,6 +10,7 @@ #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ +#include "absl/strings/string_view.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" @@ -18,11 +19,11 @@ namespace webrtc { template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); extern template class FieldTrialParameter; extern template class FieldTrialParameter; diff --git a/rtc_base/experiments/field_trial_units_unittest.cc b/rtc_base/experiments/field_trial_units_unittest.cc index 1f46d6f9ee..8996663d8e 100644 --- a/rtc_base/experiments/field_trial_units_unittest.cc +++ b/rtc_base/experiments/field_trial_units_unittest.cc @@ -11,6 +11,7 @@ #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "rtc_base/experiments/field_trial_parser.h" #include "test/gtest.h" @@ -25,7 +26,7 @@ struct DummyExperiment { FieldTrialOptional max_buffer = FieldTrialOptional("b", absl::nullopt); - explicit DummyExperiment(std::string field_trial) { + explicit DummyExperiment(absl::string_view field_trial) { ParseFieldTrial({&target_rate, &max_buffer, &period}, field_trial); } }; diff --git a/rtc_base/experiments/min_video_bitrate_experiment.cc b/rtc_base/experiments/min_video_bitrate_experiment.cc index 11450d0849..f37c4e9c76 100644 --- a/rtc_base/experiments/min_video_bitrate_experiment.cc +++ b/rtc_base/experiments/min_video_bitrate_experiment.cc @@ -105,7 +105,7 @@ absl::optional GetExperimentalMinVideoBitrate(VideoCodecType type) { return absl::nullopt; } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } return absl::nullopt; diff --git a/rtc_base/experiments/quality_rampup_experiment.cc b/rtc_base/experiments/quality_rampup_experiment.cc index ee6675c924..35c83f7011 100644 --- a/rtc_base/experiments/quality_rampup_experiment.cc +++ b/rtc_base/experiments/quality_rampup_experiment.cc @@ -70,8 +70,13 @@ bool QualityRampupExperiment::BwHigh(int64_t now_ms, return (now_ms - *start_ms_) >= min_duration_ms_.Value(); } +void QualityRampupExperiment::Reset() { + start_ms_.reset(); + max_bitrate_kbps_.reset(); +} + bool QualityRampupExperiment::Enabled() const { - return min_pixels_ || min_duration_ms_ || max_bitrate_kbps_; + return min_pixels_ && min_duration_ms_; } } // namespace webrtc diff --git a/rtc_base/experiments/quality_rampup_experiment.h b/rtc_base/experiments/quality_rampup_experiment.h index 78556ebda9..719b1893f6 100644 --- a/rtc_base/experiments/quality_rampup_experiment.h +++ b/rtc_base/experiments/quality_rampup_experiment.h @@ -33,6 +33,7 @@ class QualityRampupExperiment final { // (max_bitrate_factor_) above `max_bitrate_kbps_` for `min_duration_ms_`. bool BwHigh(int64_t now_ms, uint32_t available_bw_kbps); + void Reset(); bool Enabled() const; private: diff --git a/rtc_base/experiments/struct_parameters_parser.cc b/rtc_base/experiments/struct_parameters_parser.cc index d62eb6f1ea..011df3eaba 100644 --- a/rtc_base/experiments/struct_parameters_parser.cc +++ b/rtc_base/experiments/struct_parameters_parser.cc @@ -11,13 +11,14 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/logging.h" namespace webrtc { namespace { size_t FindOrEnd(absl::string_view str, size_t start, char delimiter) { size_t pos = str.find(delimiter, start); - pos = (pos == std::string::npos) ? str.length() : pos; + pos = (pos == absl::string_view::npos) ? str.length() : pos; return pos; } } // namespace diff --git a/rtc_base/experiments/struct_parameters_parser.h b/rtc_base/experiments/struct_parameters_parser.h index 523ecfb05d..f5f8340209 100644 --- a/rtc_base/experiments/struct_parameters_parser.h +++ b/rtc_base/experiments/struct_parameters_parser.h @@ -28,7 +28,7 @@ namespace webrtc { namespace struct_parser_impl { struct TypedMemberParser { public: - bool (*parse)(const absl::string_view src, void* target); + bool (*parse)(absl::string_view src, void* target); void (*encode)(const void* src, std::string* target); }; diff --git a/rtc_base/fake_ssl_identity.cc b/rtc_base/fake_ssl_identity.cc index 0648363b2e..73c843a2e7 100644 --- a/rtc_base/fake_ssl_identity.cc +++ b/rtc_base/fake_ssl_identity.cc @@ -14,12 +14,13 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/message_digest.h" namespace rtc { -FakeSSLCertificate::FakeSSLCertificate(const std::string& pem_string) +FakeSSLCertificate::FakeSSLCertificate(absl::string_view pem_string) : pem_string_(pem_string), digest_algorithm_(DIGEST_SHA_1), expiration_time_(-1) {} @@ -51,8 +52,8 @@ void FakeSSLCertificate::SetCertificateExpirationTime(int64_t expiration_time) { expiration_time_ = expiration_time; } -void FakeSSLCertificate::set_digest_algorithm(const std::string& algorithm) { - digest_algorithm_ = algorithm; +void FakeSSLCertificate::set_digest_algorithm(absl::string_view algorithm) { + digest_algorithm_ = std::string(algorithm); } bool FakeSSLCertificate::GetSignatureDigestAlgorithm( @@ -61,7 +62,7 @@ bool FakeSSLCertificate::GetSignatureDigestAlgorithm( return true; } -bool FakeSSLCertificate::ComputeDigest(const std::string& algorithm, +bool FakeSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { @@ -70,7 +71,7 @@ bool FakeSSLCertificate::ComputeDigest(const std::string& algorithm, return (*length != 0); } -FakeSSLIdentity::FakeSSLIdentity(const std::string& pem_string) +FakeSSLIdentity::FakeSSLIdentity(absl::string_view pem_string) : FakeSSLIdentity(FakeSSLCertificate(pem_string)) {} FakeSSLIdentity::FakeSSLIdentity(const std::vector& pem_strings) { @@ -103,17 +104,17 @@ const SSLCertChain& FakeSSLIdentity::cert_chain() const { } std::string FakeSSLIdentity::PrivateKeyToPEMString() const { - RTC_NOTREACHED(); // Not implemented. + RTC_DCHECK_NOTREACHED(); // Not implemented. return ""; } std::string FakeSSLIdentity::PublicKeyToPEMString() const { - RTC_NOTREACHED(); // Not implemented. + RTC_DCHECK_NOTREACHED(); // Not implemented. return ""; } bool FakeSSLIdentity::operator==(const SSLIdentity& other) const { - RTC_NOTREACHED(); // Not implemented. + RTC_DCHECK_NOTREACHED(); // Not implemented. return false; } diff --git a/rtc_base/fake_ssl_identity.h b/rtc_base/fake_ssl_identity.h index 512baba9fb..2b4ae2e57a 100644 --- a/rtc_base/fake_ssl_identity.h +++ b/rtc_base/fake_ssl_identity.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -23,7 +24,7 @@ class FakeSSLCertificate : public SSLCertificate { public: // SHA-1 is the default digest algorithm because it is available in all build // configurations used for unit testing. - explicit FakeSSLCertificate(const std::string& pem_string); + explicit FakeSSLCertificate(absl::string_view pem_string); FakeSSLCertificate(const FakeSSLCertificate&); ~FakeSSLCertificate() override; @@ -34,14 +35,14 @@ class FakeSSLCertificate : public SSLCertificate { void ToDER(Buffer* der_buffer) const override; int64_t CertificateExpirationTime() const override; bool GetSignatureDigestAlgorithm(std::string* algorithm) const override; - bool ComputeDigest(const std::string& algorithm, + bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const override; void SetCertificateExpirationTime(int64_t expiration_time); - void set_digest_algorithm(const std::string& algorithm); + void set_digest_algorithm(absl::string_view algorithm); private: std::string pem_string_; @@ -52,7 +53,7 @@ class FakeSSLCertificate : public SSLCertificate { class FakeSSLIdentity : public SSLIdentity { public: - explicit FakeSSLIdentity(const std::string& pem_string); + explicit FakeSSLIdentity(absl::string_view pem_string); // For a certificate chain. explicit FakeSSLIdentity(const std::vector& pem_strings); explicit FakeSSLIdentity(const FakeSSLCertificate& cert); diff --git a/rtc_base/file_rotating_stream.cc b/rtc_base/file_rotating_stream.cc index 5a004a937b..c529b5b1b4 100644 --- a/rtc_base/file_rotating_stream.cc +++ b/rtc_base/file_rotating_stream.cc @@ -14,6 +14,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include @@ -29,6 +31,7 @@ #include "absl/types/optional.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" // Note: We use fprintf for logging in the write paths of this stream to avoid // infinite loops when logging. @@ -39,54 +42,58 @@ namespace { const char kCallSessionLogPrefix[] = "webrtc_log"; -std::string AddTrailingPathDelimiterIfNeeded(std::string directory); +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory); // `dir` must have a trailing delimiter. `prefix` must not include wild card // characters. -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix); -bool DeleteFile(const std::string& file); -bool MoveFile(const std::string& old_file, const std::string& new_file); -bool IsFile(const std::string& file); -bool IsFolder(const std::string& file); -absl::optional GetFileSize(const std::string& file); +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix); +bool DeleteFile(absl::string_view file); +bool MoveFile(absl::string_view old_file, absl::string_view new_file); +bool IsFile(absl::string_view file); +bool IsFolder(absl::string_view file); +absl::optional GetFileSize(absl::string_view file); #if defined(WEBRTC_WIN) -std::string AddTrailingPathDelimiterIfNeeded(std::string directory) { +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) { if (absl::EndsWith(directory, "\\")) { - return directory; + return std::string(directory); } - return directory + "\\"; + return std::string(directory) + "\\"; } -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix) { +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix) { RTC_DCHECK(absl::EndsWith(directory, "\\")); WIN32_FIND_DATAW data; HANDLE handle; - handle = ::FindFirstFileW(ToUtf16(directory + prefix + '*').c_str(), &data); + StringBuilder pattern_builder{directory}; + pattern_builder << prefix << "*"; + handle = ::FindFirstFileW(ToUtf16(pattern_builder.str()).c_str(), &data); if (handle == INVALID_HANDLE_VALUE) return {}; std::vector file_list; do { - file_list.emplace_back(directory + ToUtf8(data.cFileName)); + StringBuilder file_builder{directory}; + file_builder << ToUtf8(data.cFileName); + file_list.emplace_back(file_builder.Release()); } while (::FindNextFileW(handle, &data) == TRUE); ::FindClose(handle); return file_list; } -bool DeleteFile(const std::string& file) { +bool DeleteFile(absl::string_view file) { return ::DeleteFileW(ToUtf16(file).c_str()) != 0; } -bool MoveFile(const std::string& old_file, const std::string& new_file) { +bool MoveFile(absl::string_view old_file, absl::string_view new_file) { return ::MoveFileW(ToUtf16(old_file).c_str(), ToUtf16(new_file).c_str()) != 0; } -bool IsFile(const std::string& file) { +bool IsFile(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data)) @@ -94,7 +101,7 @@ bool IsFile(const std::string& file) { return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; } -bool IsFolder(const std::string& file) { +bool IsFolder(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data)) @@ -103,7 +110,7 @@ bool IsFolder(const std::string& file) { FILE_ATTRIBUTE_DIRECTORY; } -absl::optional GetFileSize(const std::string& file) { +absl::optional GetFileSize(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data) == 0) @@ -113,55 +120,57 @@ absl::optional GetFileSize(const std::string& file) { #else // defined(WEBRTC_WIN) -std::string AddTrailingPathDelimiterIfNeeded(std::string directory) { +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) { if (absl::EndsWith(directory, "/")) { - return directory; + return std::string(directory); } - return directory + "/"; + return std::string(directory) + "/"; } -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix) { +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix) { RTC_DCHECK(absl::EndsWith(directory, "/")); - DIR* dir = ::opendir(directory.c_str()); + std::string directory_str = std::string(directory); + DIR* dir = ::opendir(directory_str.c_str()); if (dir == nullptr) return {}; std::vector file_list; for (struct dirent* dirent = ::readdir(dir); dirent; dirent = ::readdir(dir)) { std::string name = dirent->d_name; - if (name.compare(0, prefix.size(), prefix) == 0) { - file_list.emplace_back(directory + name); + if (name.compare(0, prefix.size(), prefix.data(), prefix.size()) == 0) { + file_list.emplace_back(directory_str + name); } } ::closedir(dir); return file_list; } -bool DeleteFile(const std::string& file) { - return ::unlink(file.c_str()) == 0; +bool DeleteFile(absl::string_view file) { + return ::unlink(std::string(file).c_str()) == 0; } -bool MoveFile(const std::string& old_file, const std::string& new_file) { - return ::rename(old_file.c_str(), new_file.c_str()) == 0; +bool MoveFile(absl::string_view old_file, absl::string_view new_file) { + return ::rename(std::string(old_file).c_str(), + std::string(new_file).c_str()) == 0; } -bool IsFile(const std::string& file) { +bool IsFile(absl::string_view file) { struct stat st; - int res = ::stat(file.c_str(), &st); + int res = ::stat(std::string(file).c_str(), &st); // Treat symlinks, named pipes, etc. all as files. return res == 0 && !S_ISDIR(st.st_mode); } -bool IsFolder(const std::string& file) { +bool IsFolder(absl::string_view file) { struct stat st; - int res = ::stat(file.c_str(), &st); + int res = ::stat(std::string(file).c_str(), &st); return res == 0 && S_ISDIR(st.st_mode); } -absl::optional GetFileSize(const std::string& file) { +absl::optional GetFileSize(absl::string_view file) { struct stat st; - if (::stat(file.c_str(), &st) != 0) + if (::stat(std::string(file).c_str(), &st) != 0) return absl::nullopt; return st.st_size; } @@ -170,8 +179,8 @@ absl::optional GetFileSize(const std::string& file) { } // namespace -FileRotatingStream::FileRotatingStream(const std::string& dir_path, - const std::string& file_prefix, +FileRotatingStream::FileRotatingStream(absl::string_view dir_path, + absl::string_view file_prefix, size_t max_file_size, size_t num_files) : dir_path_(AddTrailingPathDelimiterIfNeeded(dir_path)), @@ -331,7 +340,7 @@ std::string FileRotatingStream::GetFilePath(size_t index, } CallSessionFileRotatingStream::CallSessionFileRotatingStream( - const std::string& dir_path, + absl::string_view dir_path, size_t max_total_log_size) : FileRotatingStream(dir_path, kCallSessionLogPrefix, @@ -376,8 +385,8 @@ size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles( } FileRotatingStreamReader::FileRotatingStreamReader( - const std::string& dir_path, - const std::string& file_prefix) { + absl::string_view dir_path, + absl::string_view file_prefix) { file_names_ = GetFilesWithPrefix(AddTrailingPathDelimiterIfNeeded(dir_path), file_prefix); @@ -413,7 +422,7 @@ size_t FileRotatingStreamReader::ReadAll(void* buffer, size_t size) const { } CallSessionFileRotatingStreamReader::CallSessionFileRotatingStreamReader( - const std::string& dir_path) + absl::string_view dir_path) : FileRotatingStreamReader(dir_path, kCallSessionLogPrefix) {} } // namespace rtc diff --git a/rtc_base/file_rotating_stream.h b/rtc_base/file_rotating_stream.h index beb47c83da..6ae2753098 100644 --- a/rtc_base/file_rotating_stream.h +++ b/rtc_base/file_rotating_stream.h @@ -17,7 +17,7 @@ #include #include -#include "rtc_base/constructor_magic.h" +#include "absl/strings/string_view.h" #include "rtc_base/system/file_wrapper.h" namespace rtc { @@ -30,13 +30,16 @@ class FileRotatingStream { public: // Use this constructor for writing to a directory. Files in the directory // matching the prefix will be deleted on open. - FileRotatingStream(const std::string& dir_path, - const std::string& file_prefix, + FileRotatingStream(absl::string_view dir_path, + absl::string_view file_prefix, size_t max_file_size, size_t num_files); virtual ~FileRotatingStream(); + FileRotatingStream(const FileRotatingStream&) = delete; + FileRotatingStream& operator=(const FileRotatingStream&) = delete; + bool IsOpen() const; bool Write(const void* data, size_t data_len); @@ -100,8 +103,6 @@ class FileRotatingStream { // buffering the file size read from disk might not be accurate. size_t current_bytes_written_; bool disable_buffering_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FileRotatingStream); }; // CallSessionFileRotatingStream is meant to be used in situations where we will @@ -126,10 +127,14 @@ class CallSessionFileRotatingStream : public FileRotatingStream { // Use this constructor for writing to a directory. Files in the directory // matching what's used by the stream will be deleted. `max_total_log_size` // must be at least 4. - CallSessionFileRotatingStream(const std::string& dir_path, + CallSessionFileRotatingStream(absl::string_view dir_path, size_t max_total_log_size); ~CallSessionFileRotatingStream() override {} + CallSessionFileRotatingStream(const CallSessionFileRotatingStream&) = delete; + CallSessionFileRotatingStream& operator=( + const CallSessionFileRotatingStream&) = delete; + protected: void OnRotation() override; @@ -140,8 +145,6 @@ class CallSessionFileRotatingStream : public FileRotatingStream { const size_t max_total_log_size_; size_t num_rotations_; - - RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingStream); }; // This is a convenience class, to read all files produced by a @@ -150,8 +153,8 @@ class CallSessionFileRotatingStream : public FileRotatingStream { // directory at construction time. class FileRotatingStreamReader { public: - FileRotatingStreamReader(const std::string& dir_path, - const std::string& file_prefix); + FileRotatingStreamReader(absl::string_view dir_path, + absl::string_view file_prefix); ~FileRotatingStreamReader(); size_t GetSize() const; size_t ReadAll(void* buffer, size_t size) const; @@ -162,7 +165,7 @@ class FileRotatingStreamReader { class CallSessionFileRotatingStreamReader : public FileRotatingStreamReader { public: - CallSessionFileRotatingStreamReader(const std::string& dir_path); + CallSessionFileRotatingStreamReader(absl::string_view dir_path); }; } // namespace rtc diff --git a/rtc_base/file_rotating_stream_unittest.cc b/rtc_base/file_rotating_stream_unittest.cc index 849b111148..a48dfe9a61 100644 --- a/rtc_base/file_rotating_stream_unittest.cc +++ b/rtc_base/file_rotating_stream_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -44,15 +45,15 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { static const char* kFilePrefix; static const size_t kMaxFileSize; - void Init(const std::string& dir_name, - const std::string& file_prefix, + void Init(absl::string_view dir_name, + absl::string_view file_prefix, size_t max_file_size, size_t num_log_files, bool ensure_trailing_delimiter = true) { dir_path_ = webrtc::test::OutputPath(); // Append per-test output path in order to run within gtest parallel. - dir_path_.append(dir_name); + dir_path_.append(dir_name.begin(), dir_name.end()); if (ensure_trailing_delimiter) { dir_path_.append(webrtc::test::kPathDelimiter); } @@ -80,7 +81,7 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { // end of stream result. void VerifyStreamRead(const char* expected_contents, const size_t expected_length, - const std::string& dir_path, + absl::string_view dir_path, const char* file_prefix) { FileRotatingStreamReader reader(dir_path, file_prefix); EXPECT_EQ(reader.GetSize(), expected_length); @@ -92,9 +93,10 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { void VerifyFileContents(const char* expected_contents, const size_t expected_length, - const std::string& file_path) { + absl::string_view file_path) { std::unique_ptr buffer(new uint8_t[expected_length + 1]); - webrtc::FileWrapper f = webrtc::FileWrapper::OpenReadOnly(file_path); + webrtc::FileWrapper f = + webrtc::FileWrapper::OpenReadOnly(std::string(file_path)); ASSERT_TRUE(f.is_open()); size_t size_read = f.Read(buffer.get(), expected_length + 1); EXPECT_EQ(size_read, expected_length); @@ -255,11 +257,11 @@ TEST_F(MAYBE_FileRotatingStreamTest, GetFilePath) { class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test { protected: - void Init(const std::string& dir_name, size_t max_total_log_size) { + void Init(absl::string_view dir_name, size_t max_total_log_size) { dir_path_ = webrtc::test::OutputPath(); // Append per-test output path in order to run within gtest parallel. - dir_path_.append(dir_name); + dir_path_.append(dir_name.begin(), dir_name.end()); dir_path_.append(webrtc::test::kPathDelimiter); ASSERT_TRUE(webrtc::test::CreateDir(dir_path_)); stream_.reset( @@ -285,7 +287,7 @@ class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test { // end of stream result. void VerifyStreamRead(const char* expected_contents, const size_t expected_length, - const std::string& dir_path) { + absl::string_view dir_path) { CallSessionFileRotatingStreamReader reader(dir_path); EXPECT_EQ(reader.GetSize(), expected_length); std::unique_ptr buffer(new uint8_t[expected_length]); diff --git a/rtc_base/gunit.cc b/rtc_base/gunit.cc index 83ee8075fb..7cd60fe9ee 100644 --- a/rtc_base/gunit.cc +++ b/rtc_base/gunit.cc @@ -13,6 +13,7 @@ #include #include "absl/strings/match.h" +#include "absl/strings/string_view.h" ::testing::AssertionResult AssertStartsWith(const char* text_expr, const char* prefix_expr, @@ -30,9 +31,9 @@ ::testing::AssertionResult AssertStringContains(const char* str_expr, const char* substr_expr, - const std::string& str, - const std::string& substr) { - if (str.find(substr) != std::string::npos) { + absl::string_view str, + absl::string_view substr) { + if (str.find(substr) != absl::string_view::npos) { return ::testing::AssertionSuccess(); } else { return ::testing::AssertionFailure() diff --git a/rtc_base/gunit.h b/rtc_base/gunit.h index dedf3ee067..ac99c7ac4d 100644 --- a/rtc_base/gunit.h +++ b/rtc_base/gunit.h @@ -11,6 +11,7 @@ #ifndef RTC_BASE_GUNIT_H_ #define RTC_BASE_GUNIT_H_ +#include "absl/strings/string_view.h" #include "rtc_base/fake_clock.h" #include "rtc_base/logging.h" #include "rtc_base/thread.h" @@ -162,7 +163,7 @@ testing::AssertionResult AssertStartsWith(const char* text_expr, // Usage: EXPECT_PRED_FORMAT2(AssertStringContains, str, "substring"); testing::AssertionResult AssertStringContains(const char* str_expr, const char* substr_expr, - const std::string& str, - const std::string& substr); + absl::string_view str, + absl::string_view substr); #endif // RTC_BASE_GUNIT_H_ diff --git a/rtc_base/helpers.cc b/rtc_base/helpers.cc index 64cab10335..337239894a 100644 --- a/rtc_base/helpers.cc +++ b/rtc_base/helpers.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -145,10 +146,8 @@ bool CreateRandomString(size_t len, std::string* str) { return CreateRandomString(len, kBase64, 64, str); } -bool CreateRandomString(size_t len, - const std::string& table, - std::string* str) { - return CreateRandomString(len, table.c_str(), static_cast(table.size()), +bool CreateRandomString(size_t len, absl::string_view table, std::string* str) { + return CreateRandomString(len, table.data(), static_cast(table.size()), str); } diff --git a/rtc_base/helpers.h b/rtc_base/helpers.h index 2fd2fc5218..c214f5212f 100644 --- a/rtc_base/helpers.h +++ b/rtc_base/helpers.h @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -42,7 +43,7 @@ RTC_EXPORT bool CreateRandomString(size_t length, std::string* str); // For ease of implementation, the function requires that the table // size evenly divide 256; otherwise, it returns false. RTC_EXPORT bool CreateRandomString(size_t length, - const std::string& table, + absl::string_view table, std::string* str); // Generates (cryptographically) random data of the given length. diff --git a/rtc_base/http_common.cc b/rtc_base/http_common.cc index 1dd4a202f8..88639a7c70 100644 --- a/rtc_base/http_common.cc +++ b/rtc_base/http_common.cc @@ -10,6 +10,8 @@ #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #include @@ -185,7 +187,7 @@ void HttpParseAttributes(const char* data, } bool HttpHasAttribute(const HttpAttributeList& attributes, - const std::string& name, + absl::string_view name, std::string* value) { for (HttpAttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { @@ -213,7 +215,7 @@ bool HttpHasNthAttribute(HttpAttributeList& attributes, return true; } -std::string quote(const std::string& str) { +std::string quote(absl::string_view str) { std::string result; result.push_back('"'); for (size_t i = 0; i < str.size(); ++i) { @@ -232,7 +234,7 @@ struct NegotiateAuthContext : public HttpAuthContext { size_t steps; bool specified_credentials; - NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2) + NegotiateAuthContext(absl::string_view auth, CredHandle c1, CtxtHandle c2) : HttpAuthContext(auth), cred(c1), ctx(c2), @@ -251,9 +253,9 @@ struct NegotiateAuthContext : public HttpAuthContext { HttpAuthResult HttpAuthenticate(const char* challenge, size_t len, const SocketAddress& server, - const std::string& method, - const std::string& uri, - const std::string& username, + absl::string_view method, + absl::string_view uri, + absl::string_view username, const CryptString& password, HttpAuthContext*& context, std::string& response, @@ -326,7 +328,7 @@ HttpAuthResult HttpAuthenticate(const char* challenge, pos += strcpyn(sensitive + pos, len - pos, ":"); password.CopyTo(sensitive + pos, true); - std::string A2 = method + ":" + uri; + std::string A2 = std::string(method) + ":" + std::string(uri); std::string middle; if (has_qop) { qop = "auth"; @@ -373,7 +375,7 @@ HttpAuthResult HttpAuthenticate(const char* challenge, if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), nullptr, server.port(), 0, &len, spn) != ERROR_SUCCESS) { - RTC_LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed"; + RTC_LOG_F(LS_WARNING) << "(Negotiate) - DsMakeSpn failed"; return HAR_IGNORE; } #else @@ -413,8 +415,8 @@ HttpAuthResult HttpAuthenticate(const char* challenge, if (neg) { const size_t max_steps = 10; if (++neg->steps >= max_steps) { - RTC_LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) " - "too many retries"; + RTC_LOG(LS_WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) " + "too many retries"; return HAR_ERROR; } steps = neg->steps; @@ -459,11 +461,11 @@ HttpAuthResult HttpAuthenticate(const char* challenge, size_t len = password.GetLength() + 1; char* sensitive = new char[len]; password.CopyTo(sensitive, true); - std::string::size_type pos = username.find('\\'); - if (pos == std::string::npos) { + absl::string_view::size_type pos = username.find('\\'); + if (pos == absl::string_view::npos) { auth_id.UserLength = static_cast( std::min(sizeof(userbuf) - 1, username.size())); - memcpy(userbuf, username.c_str(), auth_id.UserLength); + memcpy(userbuf, username.data(), auth_id.UserLength); userbuf[auth_id.UserLength] = 0; auth_id.DomainLength = 0; domainbuf[auth_id.DomainLength] = 0; @@ -474,11 +476,11 @@ HttpAuthResult HttpAuthenticate(const char* challenge, } else { auth_id.UserLength = static_cast( std::min(sizeof(userbuf) - 1, username.size() - pos - 1)); - memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + memcpy(userbuf, username.data() + pos + 1, auth_id.UserLength); userbuf[auth_id.UserLength] = 0; auth_id.DomainLength = static_cast(std::min(sizeof(domainbuf) - 1, pos)); - memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + memcpy(domainbuf, username.data(), auth_id.DomainLength); domainbuf[auth_id.DomainLength] = 0; auth_id.PasswordLength = static_cast( std::min(sizeof(passbuf) - 1, password.GetLength())); diff --git a/rtc_base/http_common.h b/rtc_base/http_common.h index edf161fb4c..d287bd5d7c 100644 --- a/rtc_base/http_common.h +++ b/rtc_base/http_common.h @@ -13,6 +13,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { class CryptString; @@ -24,7 +26,7 @@ class SocketAddress; struct HttpAuthContext { std::string auth_method; - HttpAuthContext(const std::string& auth) : auth_method(auth) {} + HttpAuthContext(absl::string_view auth) : auth_method(auth) {} virtual ~HttpAuthContext() {} }; @@ -37,9 +39,9 @@ enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR }; HttpAuthResult HttpAuthenticate(const char* challenge, size_t len, const SocketAddress& server, - const std::string& method, - const std::string& uri, - const std::string& username, + absl::string_view method, + absl::string_view uri, + absl::string_view username, const CryptString& password, HttpAuthContext*& context, std::string& response, diff --git a/rtc_base/ifaddrs_android.cc b/rtc_base/ifaddrs_android.cc index 1cc63fe9f3..6474fb7244 100644 --- a/rtc_base/ifaddrs_android.cc +++ b/rtc_base/ifaddrs_android.cc @@ -24,6 +24,8 @@ #include #include +#include "absl/cleanup/cleanup.h" + namespace { struct netlinkrequest { @@ -138,10 +140,12 @@ int populate_ifaddrs(struct ifaddrs* ifaddr, } int getifaddrs(struct ifaddrs** result) { + *result = nullptr; int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { return -1; } + absl::Cleanup close_file = [fd] { close(fd); }; netlinkrequest ifaddr_request; memset(&ifaddr_request, 0, sizeof(ifaddr_request)); @@ -151,10 +155,10 @@ int getifaddrs(struct ifaddrs** result) { ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); if (static_cast(count) != ifaddr_request.header.nlmsg_len) { - close(fd); return -1; } struct ifaddrs* start = nullptr; + absl::Cleanup cleanup_start = [&start] { freeifaddrs(start); }; struct ifaddrs* current = nullptr; char buf[kMaxReadSize]; ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); @@ -165,13 +169,12 @@ int getifaddrs(struct ifaddrs** result) { header = NLMSG_NEXT(header, header_size)) { switch (header->nlmsg_type) { case NLMSG_DONE: - // Success. Return. + // Success. Return `start`. Cancel `start` cleanup because it + // becomes callers responsibility. + std::move(cleanup_start).Cancel(); *result = start; - close(fd); return 0; case NLMSG_ERROR: - close(fd); - freeifaddrs(start); return -1; case RTM_NEWADDR: { ifaddrmsg* address_msg = @@ -192,8 +195,6 @@ int getifaddrs(struct ifaddrs** result) { } if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta), RTA_PAYLOAD(rta)) != 0) { - freeifaddrs(start); - *result = nullptr; return -1; } current = newest; @@ -206,8 +207,6 @@ int getifaddrs(struct ifaddrs** result) { } amount_read = recv(fd, &buf, kMaxReadSize, 0); } - close(fd); - freeifaddrs(start); return -1; } diff --git a/rtc_base/ip_address.cc b/rtc_base/ip_address.cc index 86f42e0bf9..5ebb402145 100644 --- a/rtc_base/ip_address.cc +++ b/rtc_base/ip_address.cc @@ -11,6 +11,8 @@ #if defined(WEBRTC_POSIX) #include #include + +#include "absl/strings/string_view.h" #ifdef OPENBSD #include #endif @@ -276,14 +278,15 @@ bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out) { return false; } -bool IPFromString(const std::string& str, IPAddress* out) { +bool IPFromString(absl::string_view str, IPAddress* out) { if (!out) { return false; } in_addr addr; - if (rtc::inet_pton(AF_INET, str.c_str(), &addr) == 0) { + const std::string str_copy = std::string(str); + if (rtc::inet_pton(AF_INET, str_copy.c_str(), &addr) == 0) { in6_addr addr6; - if (rtc::inet_pton(AF_INET6, str.c_str(), &addr6) == 0) { + if (rtc::inet_pton(AF_INET6, str_copy.c_str(), &addr6) == 0) { *out = IPAddress(); return false; } @@ -294,7 +297,7 @@ bool IPFromString(const std::string& str, IPAddress* out) { return true; } -bool IPFromString(const std::string& str, int flags, InterfaceAddress* out) { +bool IPFromString(absl::string_view str, int flags, InterfaceAddress* out) { IPAddress ip; if (!IPFromString(str, &ip)) { return false; diff --git a/rtc_base/ip_address.h b/rtc_base/ip_address.h index 8725417393..58ad8ba4b2 100644 --- a/rtc_base/ip_address.h +++ b/rtc_base/ip_address.h @@ -16,6 +16,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #endif #if defined(WEBRTC_WIN) #include @@ -29,8 +31,8 @@ #if defined(WEBRTC_WIN) #include "rtc_base/win32.h" #endif +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" - namespace rtc { enum IPv6AddressFlag { @@ -155,8 +157,8 @@ class RTC_EXPORT InterfaceAddress : public IPAddress { }; bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out); -RTC_EXPORT bool IPFromString(const std::string& str, IPAddress* out); -RTC_EXPORT bool IPFromString(const std::string& str, +RTC_EXPORT bool IPFromString(absl::string_view str, IPAddress* out); +RTC_EXPORT bool IPFromString(absl::string_view str, int flags, InterfaceAddress* out); bool IPIsAny(const IPAddress& ip); diff --git a/rtc_base/ip_address_unittest.cc b/rtc_base/ip_address_unittest.cc index f94649cfee..9ca05c95fe 100644 --- a/rtc_base/ip_address_unittest.cc +++ b/rtc_base/ip_address_unittest.cc @@ -10,6 +10,7 @@ #include "rtc_base/ip_address.h" +#include "absl/strings/string_view.h" #include "test/gtest.h" namespace rtc { @@ -118,7 +119,7 @@ bool AreEqual(const IPAddress& addr, const IPAddress& addr2) { return true; } -bool BrokenIPStringFails(const std::string& broken) { +bool BrokenIPStringFails(absl::string_view broken) { IPAddress addr(0); // Intentionally make it v4. if (IPFromString(kIPv4BrokenString1, &addr)) { return false; @@ -126,13 +127,13 @@ bool BrokenIPStringFails(const std::string& broken) { return addr.family() == AF_UNSPEC; } -bool CheckMaskCount(const std::string& mask, int expected_length) { +bool CheckMaskCount(absl::string_view mask, int expected_length) { IPAddress addr; return IPFromString(mask, &addr) && (expected_length == CountIPMaskBits(addr)); } -bool TryInvalidMaskCount(const std::string& mask) { +bool TryInvalidMaskCount(absl::string_view mask) { // We don't care about the result at all, but we do want to know if // CountIPMaskBits is going to crash or infinite loop or something. IPAddress addr; @@ -143,9 +144,9 @@ bool TryInvalidMaskCount(const std::string& mask) { return true; } -bool CheckTruncateIP(const std::string& initial, +bool CheckTruncateIP(absl::string_view initial, int truncate_length, - const std::string& expected_result) { + absl::string_view expected_result) { IPAddress addr, expected; IPFromString(initial, &addr); IPFromString(expected_result, &expected); diff --git a/rtc_base/log_sinks.cc b/rtc_base/log_sinks.cc index 4365142517..b237e873ee 100644 --- a/rtc_base/log_sinks.cc +++ b/rtc_base/log_sinks.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" namespace rtc { @@ -36,14 +37,18 @@ FileRotatingLogSink::FileRotatingLogSink(FileRotatingStream* stream) FileRotatingLogSink::~FileRotatingLogSink() {} void FileRotatingLogSink::OnLogMessage(const std::string& message) { + OnLogMessage(absl::string_view(message)); +} + +void FileRotatingLogSink::OnLogMessage(absl::string_view message) { if (!stream_->IsOpen()) { std::fprintf(stderr, "Init() must be called before adding this sink.\n"); return; } - stream_->Write(message.c_str(), message.size()); + stream_->Write(message.data(), message.size()); } -void FileRotatingLogSink::OnLogMessage(const std::string& message, +void FileRotatingLogSink::OnLogMessage(absl::string_view message, LoggingSeverity sev, const char* tag) { if (!stream_->IsOpen()) { @@ -52,7 +57,7 @@ void FileRotatingLogSink::OnLogMessage(const std::string& message, } stream_->Write(tag, strlen(tag)); stream_->Write(": ", 2); - stream_->Write(message.c_str(), message.size()); + stream_->Write(message.data(), message.size()); } bool FileRotatingLogSink::Init() { diff --git a/rtc_base/log_sinks.h b/rtc_base/log_sinks.h index 87bec6dba8..da339a54f1 100644 --- a/rtc_base/log_sinks.h +++ b/rtc_base/log_sinks.h @@ -16,7 +16,7 @@ #include #include -#include "rtc_base/constructor_magic.h" +#include "absl/strings/string_view.h" #include "rtc_base/file_rotating_stream.h" #include "rtc_base/logging.h" @@ -34,10 +34,14 @@ class FileRotatingLogSink : public LogSink { 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(const std::string& message, + void OnLogMessage(absl::string_view message) override; + void OnLogMessage(absl::string_view message, LoggingSeverity sev, const char* tag) override; @@ -52,8 +56,6 @@ class FileRotatingLogSink : public LogSink { private: std::unique_ptr stream_; - - RTC_DISALLOW_COPY_AND_ASSIGN(FileRotatingLogSink); }; // Log sink that uses a CallSessionFileRotatingStream to write to disk. @@ -64,8 +66,10 @@ class CallSessionFileRotatingLogSink : public FileRotatingLogSink { size_t max_total_log_size); ~CallSessionFileRotatingLogSink() override; - private: - RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingLogSink); + CallSessionFileRotatingLogSink(const CallSessionFileRotatingLogSink&) = + delete; + CallSessionFileRotatingLogSink& operator=( + const CallSessionFileRotatingLogSink&) = delete; }; } // namespace rtc diff --git a/rtc_base/logging.cc b/rtc_base/logging.cc index 321d848384..102c1d1c70 100644 --- a/rtc_base/logging.cc +++ b/rtc_base/logging.cc @@ -42,6 +42,7 @@ static const int kMaxLogLineSize = 1024 - 60; #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/platform_thread_types.h" #include "rtc_base/string_encode.h" @@ -55,13 +56,15 @@ namespace rtc { namespace { // By default, release builds don't log, debug builds at info level #if !defined(NDEBUG) -static LoggingSeverity g_min_sev = LS_INFO; -static LoggingSeverity g_dbg_sev = LS_INFO; +constexpr LoggingSeverity kDefaultLoggingSeverity = LS_INFO; #else -static LoggingSeverity g_min_sev = LS_NONE; -static LoggingSeverity g_dbg_sev = LS_NONE; +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, '/'); @@ -73,9 +76,10 @@ const char* FilenameFromPath(const char* file) { } // Global lock for log subsystem, only needed to serialize access to streams_. -// TODO(bugs.webrtc.org/11665): this is not currently constant initialized and -// trivially destructible. -webrtc::Mutex g_log_mutex_; +webrtc::Mutex& GetLoggingLock() { + static webrtc::Mutex& mutex = *new webrtc::Mutex(); + return mutex; +} } // namespace @@ -89,7 +93,7 @@ bool LogMessage::log_to_stderr_ = true; // 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(g_log_mutex_) = +ABSL_CONST_INIT LogSink* LogMessage::streams_ RTC_GUARDED_BY(GetLoggingLock()) = nullptr; ABSL_CONST_INIT std::atomic LogMessage::streams_empty_ = {true}; @@ -202,7 +206,7 @@ LogMessage::~LogMessage() { #endif } - webrtc::MutexLock lock(&g_log_mutex_); + webrtc::MutexLock lock(&GetLoggingLock()); for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { if (severity_ >= entry->min_severity_) { #if defined(WEBRTC_ANDROID) @@ -251,7 +255,7 @@ void LogMessage::LogTimestamps(bool on) { void LogMessage::LogToDebug(LoggingSeverity min_sev) { g_dbg_sev = min_sev; - webrtc::MutexLock lock(&g_log_mutex_); + webrtc::MutexLock lock(&GetLoggingLock()); UpdateMinLogSeverity(); } @@ -260,7 +264,7 @@ void LogMessage::SetLogToStderr(bool log_to_stderr) { } int LogMessage::GetLogToStream(LogSink* stream) { - webrtc::MutexLock lock(&g_log_mutex_); + webrtc::MutexLock lock(&GetLoggingLock()); LoggingSeverity sev = LS_NONE; for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { if (stream == nullptr || stream == entry) { @@ -271,7 +275,7 @@ int LogMessage::GetLogToStream(LogSink* stream) { } void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) { - webrtc::MutexLock lock(&g_log_mutex_); + webrtc::MutexLock lock(&GetLoggingLock()); stream->min_severity_ = min_sev; stream->next_ = streams_; streams_ = stream; @@ -280,7 +284,7 @@ void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) { } void LogMessage::RemoveLogToStream(LogSink* stream) { - webrtc::MutexLock lock(&g_log_mutex_); + webrtc::MutexLock lock(&GetLoggingLock()); for (LogSink** entry = &streams_; *entry != nullptr; entry = &(*entry)->next_) { if (*entry == stream) { @@ -342,7 +346,7 @@ void LogMessage::ConfigureLogging(const char* params) { } void LogMessage::UpdateMinLogSeverity() - RTC_EXCLUSIVE_LOCKS_REQUIRED(g_log_mutex_) { + 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_); @@ -481,7 +485,7 @@ void Log(const LogArgType* fmt, ...) { } #endif default: { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); va_end(args); return; } @@ -535,7 +539,7 @@ void Log(const LogArgType* fmt, ...) { reinterpret_cast(va_arg(args, const void*))); break; default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); va_end(args); return; } @@ -560,4 +564,20 @@ void LogSink::OnLogMessage(const std::string& msg, LoggingSeverity /* severity */) { OnLogMessage(msg); } + +// Inefficient default implementation, override is recommended. +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag) { + OnLogMessage(tag + (": " + std::string(msg)), severity); +} + +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity /* severity */) { + OnLogMessage(msg); +} + +void LogSink::OnLogMessage(absl::string_view msg) { + OnLogMessage(std::string(msg)); +} } // namespace rtc diff --git a/rtc_base/logging.h b/rtc_base/logging.h index 4fbbb5c6fc..ee4d410bf4 100644 --- a/rtc_base/logging.h +++ b/rtc_base/logging.h @@ -54,7 +54,6 @@ #include "absl/base/attributes.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/system/inline.h" @@ -73,9 +72,7 @@ namespace rtc { ////////////////////////////////////////////////////////////////////// - -// Note that the non-standard LoggingSeverity aliases exist because they are -// still in broad use. The meanings of the levels are: +// The meanings of the levels are: // LS_VERBOSE: This level is for data which we do not want to appear in the // normal debug log, but should appear in diagnostic logs. // LS_INFO: Chatty level used in debugging for all sorts of things, the default @@ -89,9 +86,6 @@ enum LoggingSeverity { LS_WARNING, LS_ERROR, LS_NONE, - INFO = LS_INFO, - WARNING = LS_WARNING, - LERROR = LS_ERROR }; // LogErrorContext assists in interpreting the meaning of an error value. @@ -118,6 +112,13 @@ class LogSink { LoggingSeverity severity); virtual void OnLogMessage(const std::string& message) = 0; + virtual void OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag); + virtual void OnLogMessage(absl::string_view message, + LoggingSeverity severity); + virtual void OnLogMessage(absl::string_view message); + private: friend class ::rtc::LogMessage; #if RTC_LOG_ENABLED() @@ -441,6 +442,9 @@ class LogMessage { const std::string& tag); ~LogMessage(); + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + void AddTag(const char* tag); rtc::StringBuilder& stream(); // Returns the time at which this function was called for the first time. @@ -600,8 +604,6 @@ class LogMessage { // The stringbuilder that buffers the formatted message before output rtc::StringBuilder print_stream_; - - RTC_DISALLOW_COPY_AND_ASSIGN(LogMessage); }; ////////////////////////////////////////////////////////////////////// diff --git a/rtc_base/logging_unittest.cc b/rtc_base/logging_unittest.cc index dc1208f3f6..cd8d753fb3 100644 --- a/rtc_base/logging_unittest.cc +++ b/rtc_base/logging_unittest.cc @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" @@ -34,7 +35,10 @@ class LogSinkImpl : public LogSink { private: void OnLogMessage(const std::string& message) override { - log_data_->append(message); + OnLogMessage(absl::string_view(message)); + } + void OnLogMessage(absl::string_view message) override { + log_data_->append(message.begin(), message.end()); } std::string* const log_data_; }; diff --git a/rtc_base/mdns_responder_interface.h b/rtc_base/mdns_responder_interface.h index 64fb3cebff..14ef9a202d 100644 --- a/rtc_base/mdns_responder_interface.h +++ b/rtc_base/mdns_responder_interface.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ip_address.h" namespace webrtc { @@ -23,7 +24,7 @@ namespace webrtc { class MdnsResponderInterface { public: using NameCreatedCallback = - std::function; + std::function; using NameRemovedCallback = std::function; MdnsResponderInterface() = default; diff --git a/rtc_base/memory/BUILD.gn b/rtc_base/memory/BUILD.gn index ee66ac0df8..583278ee6e 100644 --- a/rtc_base/memory/BUILD.gn +++ b/rtc_base/memory/BUILD.gn @@ -53,3 +53,7 @@ rtc_library("unittests") { "../../test:test_support", ] } + +rtc_source_set("always_valid_pointer") { + sources = [ "always_valid_pointer.h" ] +} diff --git a/rtc_base/memory/always_valid_pointer.h b/rtc_base/memory/always_valid_pointer.h new file mode 100644 index 0000000000..c73512d3c5 --- /dev/null +++ b/rtc_base/memory/always_valid_pointer.h @@ -0,0 +1,40 @@ +/* + * 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 + +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_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + private: + const std::unique_ptr owned_instance_; + Interface* const pointer_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ diff --git a/rtc_base/memory/fifo_buffer.cc b/rtc_base/memory/fifo_buffer.cc index 3fbea8dc20..116badd915 100644 --- a/rtc_base/memory/fifo_buffer.cc +++ b/rtc_base/memory/fifo_buffer.cc @@ -44,41 +44,6 @@ bool FifoBuffer::GetBuffered(size_t* size) const { return true; } -bool FifoBuffer::SetCapacity(size_t size) { - webrtc::MutexLock lock(&mutex_); - if (data_length_ > size) { - return false; - } - - if (size != buffer_length_) { - char* buffer = new char[size]; - const size_t copy = data_length_; - const size_t tail_copy = std::min(copy, buffer_length_ - read_position_); - memcpy(buffer, &buffer_[read_position_], tail_copy); - memcpy(buffer + tail_copy, &buffer_[0], copy - tail_copy); - buffer_.reset(buffer); - read_position_ = 0; - buffer_length_ = size; - } - return true; -} - -StreamResult FifoBuffer::ReadOffset(void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_read) { - webrtc::MutexLock lock(&mutex_); - return ReadOffsetLocked(buffer, bytes, offset, bytes_read); -} - -StreamResult FifoBuffer::WriteOffset(const void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_written) { - webrtc::MutexLock lock(&mutex_); - return WriteOffsetLocked(buffer, bytes, offset, bytes_written); -} - StreamState FifoBuffer::GetState() const { webrtc::MutexLock lock(&mutex_); return state_; @@ -91,7 +56,7 @@ StreamResult FifoBuffer::Read(void* buffer, webrtc::MutexLock lock(&mutex_); const bool was_writable = data_length_ < buffer_length_; size_t copy = 0; - StreamResult result = ReadOffsetLocked(buffer, bytes, 0, ©); + StreamResult result = ReadLocked(buffer, bytes, ©); if (result == SR_SUCCESS) { // If read was successful then adjust the read position and number of @@ -118,7 +83,7 @@ StreamResult FifoBuffer::Write(const void* buffer, const bool was_readable = (data_length_ > 0); size_t copy = 0; - StreamResult result = WriteOffsetLocked(buffer, bytes, 0, ©); + StreamResult result = WriteLocked(buffer, bytes, ©); if (result == SR_SUCCESS) { // If write was successful then adjust the number of readable bytes. @@ -189,22 +154,15 @@ void FifoBuffer::ConsumeWriteBuffer(size_t size) { } } -bool FifoBuffer::GetWriteRemaining(size_t* size) const { - webrtc::MutexLock lock(&mutex_); - *size = buffer_length_ - data_length_; - return true; -} - -StreamResult FifoBuffer::ReadOffsetLocked(void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_read) { - if (offset >= data_length_) { +StreamResult FifoBuffer::ReadLocked(void* buffer, + size_t bytes, + size_t* bytes_read) { + if (data_length_ == 0) { return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS; } - const size_t available = data_length_ - offset; - const size_t read_position = (read_position_ + offset) % buffer_length_; + const size_t available = data_length_; + const size_t read_position = read_position_ % buffer_length_; const size_t copy = std::min(bytes, available); const size_t tail_copy = std::min(copy, buffer_length_ - read_position); char* const p = static_cast(buffer); @@ -217,21 +175,20 @@ StreamResult FifoBuffer::ReadOffsetLocked(void* buffer, return SR_SUCCESS; } -StreamResult FifoBuffer::WriteOffsetLocked(const void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_written) { +StreamResult FifoBuffer::WriteLocked(const void* buffer, + size_t bytes, + size_t* bytes_written) { if (state_ == SS_CLOSED) { return SR_EOS; } - if (data_length_ + offset >= buffer_length_) { + if (data_length_ >= buffer_length_) { return SR_BLOCK; } - const size_t available = buffer_length_ - data_length_ - offset; + const size_t available = buffer_length_ - data_length_; const size_t write_position = - (read_position_ + data_length_ + offset) % buffer_length_; + (read_position_ + data_length_) % buffer_length_; const size_t copy = std::min(bytes, available); const size_t tail_copy = std::min(copy, buffer_length_ - write_position); const char* const p = static_cast(buffer); diff --git a/rtc_base/memory/fifo_buffer.h b/rtc_base/memory/fifo_buffer.h index de3ebcd5e8..e3d29f4b60 100644 --- a/rtc_base/memory/fifo_buffer.h +++ b/rtc_base/memory/fifo_buffer.h @@ -29,28 +29,12 @@ class FifoBuffer final : public StreamInterface { // 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; - // Resizes the buffer to the specified capacity. Fails if data_length_ > size - bool SetCapacity(size_t length); - - // Read into `buffer` with an offset from the current read position, offset - // is specified in number of bytes. - // This method doesn't adjust read position nor the number of available - // bytes, user has to call ConsumeReadData() to do this. - StreamResult ReadOffset(void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_read); - - // Write `buffer` with an offset from the current write position, offset is - // specified in number of bytes. - // This method doesn't adjust the number of buffered bytes, user has to call - // ConsumeWriteBuffer() to do this. - StreamResult WriteOffset(const void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_written); // StreamInterface methods StreamState GetState() const override; @@ -95,10 +79,6 @@ class FifoBuffer final : public StreamInterface { void* GetWriteBuffer(size_t* buf_len); void ConsumeWriteBuffer(size_t used); - // Return the number of Write()-able bytes remaining before end-of-stream. - // Returns false if not known. - bool GetWriteRemaining(size_t* size) const; - private: void PostEvent(int events, int err) { owner_->PostTask(webrtc::ToQueuedTask(task_safety_, [this, events, err]() { @@ -106,20 +86,16 @@ class FifoBuffer final : public StreamInterface { })); } - // Helper method that implements ReadOffset. Caller must acquire a lock + // Helper method that implements Read. Caller must acquire a lock // when calling this method. - StreamResult ReadOffsetLocked(void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_read) + StreamResult ReadLocked(void* buffer, size_t bytes, size_t* bytes_read) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Helper method that implements WriteOffset. Caller must acquire a lock + // Helper method that implements Write. Caller must acquire a lock // when calling this method. - StreamResult WriteOffsetLocked(const void* buffer, - size_t bytes, - size_t offset, - size_t* bytes_written) + StreamResult WriteLocked(const void* buffer, + size_t bytes, + size_t* bytes_written) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); webrtc::ScopedTaskSafety task_safety_; @@ -129,7 +105,7 @@ class FifoBuffer final : public StreamInterface { // the allocated buffer std::unique_ptr buffer_ RTC_GUARDED_BY(mutex_); // size of the allocated buffer - size_t buffer_length_ RTC_GUARDED_BY(mutex_); + const size_t buffer_length_; // amount of readable data in the buffer size_t data_length_ RTC_GUARDED_BY(mutex_); // offset to the readable data @@ -138,7 +114,6 @@ class FifoBuffer final : public StreamInterface { Thread* const owner_; // object lock mutable webrtc::Mutex mutex_; - RTC_DISALLOW_COPY_AND_ASSIGN(FifoBuffer); }; } // namespace rtc diff --git a/rtc_base/memory/fifo_buffer_unittest.cc b/rtc_base/memory/fifo_buffer_unittest.cc index 5c8ef43835..b602f0ae45 100644 --- a/rtc_base/memory/fifo_buffer_unittest.cc +++ b/rtc_base/memory/fifo_buffer_unittest.cc @@ -202,25 +202,6 @@ TEST(FifoBufferTest, TestAll) { // Check that the stream is now empty EXPECT_EQ(SR_BLOCK, buf.Read(out, kSize, &bytes, nullptr)); - // Try growing the buffer - EXPECT_EQ(SR_SUCCESS, buf.Write(in, kSize, &bytes, nullptr)); - EXPECT_EQ(kSize, bytes); - EXPECT_TRUE(buf.SetCapacity(kSize * 2)); - EXPECT_EQ(SR_SUCCESS, buf.Write(in + kSize, kSize, &bytes, nullptr)); - EXPECT_EQ(kSize, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(out, kSize * 2, &bytes, nullptr)); - EXPECT_EQ(kSize * 2, bytes); - EXPECT_EQ(0, memcmp(in, out, kSize * 2)); - - // Try shrinking the buffer - EXPECT_EQ(SR_SUCCESS, buf.Write(in, kSize, &bytes, nullptr)); - EXPECT_EQ(kSize, bytes); - EXPECT_TRUE(buf.SetCapacity(kSize)); - EXPECT_EQ(SR_BLOCK, buf.Write(in, kSize, &bytes, nullptr)); - EXPECT_EQ(SR_SUCCESS, buf.Read(out, kSize, &bytes, nullptr)); - EXPECT_EQ(kSize, bytes); - EXPECT_EQ(0, memcmp(in, out, kSize)); - // Write to the stream, close it, read the remaining bytes EXPECT_EQ(SR_SUCCESS, buf.Write(in, kSize / 2, &bytes, nullptr)); buf.Close(); @@ -240,53 +221,4 @@ TEST(FifoBufferTest, FullBufferCheck) { EXPECT_EQ(0U, free); } -TEST(FifoBufferTest, WriteOffsetAndReadOffset) { - const size_t kSize = 16; - const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; - char out[kSize * 2]; - FifoBuffer buf(kSize); - - // Write 14 bytes. - EXPECT_EQ(SR_SUCCESS, buf.Write(in, 14, nullptr, nullptr)); - - // Make sure data is in `buf`. - size_t buffered; - EXPECT_TRUE(buf.GetBuffered(&buffered)); - EXPECT_EQ(14u, buffered); - - // Read 10 bytes. - buf.ConsumeReadData(10); - - // There should be now 12 bytes of available space. - size_t remaining; - EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); - EXPECT_EQ(12u, remaining); - - // Write at offset 12, this should fail. - EXPECT_EQ(SR_BLOCK, buf.WriteOffset(in, 10, 12, nullptr)); - - // Write 8 bytes at offset 4, this wraps around the buffer. - EXPECT_EQ(SR_SUCCESS, buf.WriteOffset(in, 8, 4, nullptr)); - - // Number of available space remains the same until we call - // ConsumeWriteBuffer(). - EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); - EXPECT_EQ(12u, remaining); - buf.ConsumeWriteBuffer(12); - - // There's 4 bytes bypassed and 4 bytes no read so skip them and verify the - // 8 bytes written. - size_t read; - EXPECT_EQ(SR_SUCCESS, buf.ReadOffset(out, 8, 8, &read)); - EXPECT_EQ(8u, read); - EXPECT_EQ(0, memcmp(out, in, 8)); - - // There should still be 16 bytes available for reading. - EXPECT_TRUE(buf.GetBuffered(&buffered)); - EXPECT_EQ(16u, buffered); - - // Read at offset 16, this should fail since we don't have that much data. - EXPECT_EQ(SR_BLOCK, buf.ReadOffset(out, 10, 16, nullptr)); -} - } // namespace rtc diff --git a/rtc_base/message_digest.cc b/rtc_base/message_digest.cc index 62b4a6bc97..3b39cf9f18 100644 --- a/rtc_base/message_digest.cc +++ b/rtc_base/message_digest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/openssl_digest.h" #include "rtc_base/string_encode.h" @@ -30,7 +31,7 @@ const char DIGEST_SHA_512[] = "sha-512"; static const size_t kBlockSize = 64; // valid for SHA-256 and down -MessageDigest* MessageDigestFactory::Create(const std::string& alg) { +MessageDigest* MessageDigestFactory::Create(absl::string_view alg) { MessageDigest* digest = new OpenSSLDigest(alg); if (digest->Size() == 0) { // invalid algorithm delete digest; @@ -39,7 +40,7 @@ MessageDigest* MessageDigestFactory::Create(const std::string& alg) { return digest; } -bool IsFips180DigestAlgorithm(const std::string& alg) { +bool IsFips180DigestAlgorithm(absl::string_view alg) { // These are the FIPS 180 algorithms. According to RFC 4572 Section 5, // "Self-signed certificates (for which legacy certificates are not a // consideration) MUST use one of the FIPS 180 algorithms (SHA-1, @@ -59,7 +60,7 @@ size_t ComputeDigest(MessageDigest* digest, return digest->Finish(output, out_len); } -size_t ComputeDigest(const std::string& alg, +size_t ComputeDigest(absl::string_view alg, const void* input, size_t in_len, void* output, @@ -69,15 +70,15 @@ size_t ComputeDigest(const std::string& alg, : 0; } -std::string ComputeDigest(MessageDigest* digest, const std::string& input) { +std::string ComputeDigest(MessageDigest* digest, absl::string_view input) { std::unique_ptr output(new char[digest->Size()]); ComputeDigest(digest, input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); } -bool ComputeDigest(const std::string& alg, - const std::string& input, +bool ComputeDigest(absl::string_view alg, + absl::string_view input, std::string* output) { std::unique_ptr digest(MessageDigestFactory::Create(alg)); if (!digest) { @@ -87,7 +88,7 @@ bool ComputeDigest(const std::string& alg, return true; } -std::string ComputeDigest(const std::string& alg, const std::string& input) { +std::string ComputeDigest(absl::string_view alg, absl::string_view input) { std::string output; ComputeDigest(alg, input, &output); return output; @@ -135,7 +136,7 @@ size_t ComputeHmac(MessageDigest* digest, return digest->Finish(output, out_len); } -size_t ComputeHmac(const std::string& alg, +size_t ComputeHmac(absl::string_view alg, const void* key, size_t key_len, const void* input, @@ -151,17 +152,17 @@ size_t ComputeHmac(const std::string& alg, } std::string ComputeHmac(MessageDigest* digest, - const std::string& key, - const std::string& input) { + absl::string_view key, + absl::string_view input) { std::unique_ptr output(new char[digest->Size()]); ComputeHmac(digest, key.data(), key.size(), input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); } -bool ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input, +bool ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input, std::string* output) { std::unique_ptr digest(MessageDigestFactory::Create(alg)); if (!digest) { @@ -171,9 +172,9 @@ bool ComputeHmac(const std::string& alg, return true; } -std::string ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input) { +std::string ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input) { std::string output; ComputeHmac(alg, key, input, &output); return output; diff --git a/rtc_base/message_digest.h b/rtc_base/message_digest.h index 02e0bfd561..632b9af075 100644 --- a/rtc_base/message_digest.h +++ b/rtc_base/message_digest.h @@ -15,6 +15,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // Definitions for the digest algorithms. @@ -42,12 +44,12 @@ class MessageDigest { // A factory class for creating digest objects. class MessageDigestFactory { public: - static MessageDigest* Create(const std::string& alg); + static MessageDigest* Create(absl::string_view alg); }; // A check that an algorithm is in a list of approved digest algorithms // from RFC 4572 (FIPS 180). -bool IsFips180DigestAlgorithm(const std::string& alg); +bool IsFips180DigestAlgorithm(absl::string_view alg); // Functions to create hashes. @@ -63,25 +65,25 @@ size_t ComputeDigest(MessageDigest* digest, // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no // digest with the given name. -size_t ComputeDigest(const std::string& alg, +size_t ComputeDigest(absl::string_view alg, const void* input, size_t in_len, void* output, size_t out_len); // Computes the hash of `input` using the `digest` hash implementation, and // returns it as a hex-encoded string. -std::string ComputeDigest(MessageDigest* digest, const std::string& input); +std::string ComputeDigest(MessageDigest* digest, absl::string_view input); // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if // there is no digest with the given name. -std::string ComputeDigest(const std::string& alg, const std::string& input); +std::string ComputeDigest(absl::string_view alg, absl::string_view input); // Like the previous function, but returns an explicit result code. -bool ComputeDigest(const std::string& alg, - const std::string& input, +bool ComputeDigest(absl::string_view alg, + absl::string_view input, std::string* output); // Shorthand way to compute a hex-encoded hash using MD5. -inline std::string MD5(const std::string& input) { +inline std::string MD5(absl::string_view input) { return ComputeDigest(DIGEST_MD5, input); } @@ -102,7 +104,7 @@ size_t ComputeHmac(MessageDigest* digest, // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no // digest with the given name. -size_t ComputeHmac(const std::string& alg, +size_t ComputeHmac(absl::string_view alg, const void* key, size_t key_len, const void* input, @@ -112,18 +114,18 @@ size_t ComputeHmac(const std::string& alg, // Computes the HMAC of `input` using the `digest` hash implementation and `key` // to key the HMAC, and returns it as a hex-encoded string. std::string ComputeHmac(MessageDigest* digest, - const std::string& key, - const std::string& input); + absl::string_view key, + absl::string_view input); // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if // there is no digest with the given name. -std::string ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input); +std::string ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input); // Like the previous function, but returns an explicit result code. -bool ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input, +bool ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input, std::string* output); } // namespace rtc diff --git a/rtc_base/message_handler.h b/rtc_base/message_handler.h index 62c8344e1f..c5e05cad68 100644 --- a/rtc_base/message_handler.h +++ b/rtc_base/message_handler.h @@ -14,7 +14,6 @@ #include #include "api/function_view.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -37,11 +36,12 @@ class RTC_EXPORT MessageHandlerAutoCleanup : public MessageHandler { public: ~MessageHandlerAutoCleanup() override; + MessageHandlerAutoCleanup(const MessageHandlerAutoCleanup&) = delete; + MessageHandlerAutoCleanup& operator=(const MessageHandlerAutoCleanup&) = + delete; + protected: MessageHandlerAutoCleanup(); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(MessageHandlerAutoCleanup); }; } // namespace rtc diff --git a/rtc_base/nat_server.h b/rtc_base/nat_server.h index 5078fbb2c1..acbd62a092 100644 --- a/rtc_base/nat_server.h +++ b/rtc_base/nat_server.h @@ -15,7 +15,6 @@ #include #include "rtc_base/async_udp_socket.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/nat_types.h" #include "rtc_base/proxy_server.h" #include "rtc_base/socket_address_pair.h" @@ -69,6 +68,9 @@ class NATServer : public sigslot::has_slots<> { const SocketAddress& external_ip); ~NATServer() override; + NATServer(const NATServer&) = delete; + NATServer& operator=(const NATServer&) = delete; + SocketAddress internal_udp_address() const { return udp_server_socket_->GetLocalAddress(); } @@ -122,7 +124,6 @@ class NATServer : public sigslot::has_slots<> { ProxyServer* tcp_proxy_server_; InternalMap* int_map_; ExternalMap* ext_map_; - RTC_DISALLOW_COPY_AND_ASSIGN(NATServer); }; } // namespace rtc diff --git a/rtc_base/nat_socket_factory.h b/rtc_base/nat_socket_factory.h index 9b1d2f09e3..70cb303def 100644 --- a/rtc_base/nat_socket_factory.h +++ b/rtc_base/nat_socket_factory.h @@ -17,7 +17,6 @@ #include #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/nat_server.h" #include "rtc_base/nat_types.h" #include "rtc_base/socket.h" @@ -50,6 +49,9 @@ class NATSocketFactory : public SocketFactory, public NATInternalSocketFactory { const SocketAddress& nat_udp_addr, const SocketAddress& nat_tcp_addr); + NATSocketFactory(const NATSocketFactory&) = delete; + NATSocketFactory& operator=(const NATSocketFactory&) = delete; + // SocketFactory implementation Socket* CreateSocket(int family, int type) override; @@ -63,7 +65,6 @@ class NATSocketFactory : public SocketFactory, public NATInternalSocketFactory { SocketFactory* factory_; SocketAddress nat_udp_addr_; SocketAddress nat_tcp_addr_; - RTC_DISALLOW_COPY_AND_ASSIGN(NATSocketFactory); }; // Creates sockets that will send traffic through a NAT depending on what @@ -135,6 +136,9 @@ class NATSocketServer : public SocketServer, public NATInternalSocketFactory { explicit NATSocketServer(SocketServer* ss); + NATSocketServer(const NATSocketServer&) = delete; + NATSocketServer& operator=(const NATSocketServer&) = delete; + SocketServer* socketserver() { return server_; } Thread* queue() { return msg_queue_; } @@ -161,7 +165,6 @@ class NATSocketServer : public SocketServer, public NATInternalSocketFactory { SocketServer* server_; Thread* msg_queue_; TranslatorMap nats_; - RTC_DISALLOW_COPY_AND_ASSIGN(NATSocketServer); }; // Free-standing NAT helper functions. diff --git a/rtc_base/nat_types.cc b/rtc_base/nat_types.cc index 50d7de0dda..9ca03608e8 100644 --- a/rtc_base/nat_types.cc +++ b/rtc_base/nat_types.cc @@ -53,7 +53,7 @@ NAT* NAT::Create(NATType type) { case NAT_SYMMETRIC: return new SymmetricNAT(); default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return 0; } } diff --git a/rtc_base/nat_unittest.cc b/rtc_base/nat_unittest.cc index e757bfda97..2e41684b78 100644 --- a/rtc_base/nat_unittest.cc +++ b/rtc_base/nat_unittest.cc @@ -56,7 +56,7 @@ TestClient* CreateTestClient(SocketFactory* factory, } TestClient* CreateTCPTestClient(Socket* socket) { - return new TestClient(std::make_unique(socket, false)); + return new TestClient(std::make_unique(socket)); } // Tests that when sending from internal_addr to external_addrs through the @@ -219,7 +219,8 @@ bool TestConnectivity(const SocketAddress& src, const IPAddress& dst) { } void TestPhysicalInternal(const SocketAddress& int_addr) { - BasicNetworkManager network_manager; + PhysicalSocketServer socket_server; + BasicNetworkManager network_manager(nullptr, &socket_server); network_manager.StartUpdating(); // Process pending messages so the network list is updated. Thread::Current()->ProcessMessages(0); diff --git a/rtc_base/net_helper.cc b/rtc_base/net_helper.cc index 893b500d56..4afee7bfb0 100644 --- a/rtc_base/net_helper.cc +++ b/rtc_base/net_helper.cc @@ -10,6 +10,8 @@ #include "rtc_base/net_helper.h" +#include "absl/strings/string_view.h" + namespace cricket { const char UDP_PROTOCOL_NAME[] = "udp"; @@ -17,7 +19,7 @@ const char TCP_PROTOCOL_NAME[] = "tcp"; const char SSLTCP_PROTOCOL_NAME[] = "ssltcp"; const char TLS_PROTOCOL_NAME[] = "tls"; -int GetProtocolOverhead(const std::string& protocol) { +int GetProtocolOverhead(absl::string_view protocol) { if (protocol == TCP_PROTOCOL_NAME || protocol == SSLTCP_PROTOCOL_NAME) { return kTcpHeaderSize; } else if (protocol == UDP_PROTOCOL_NAME) { diff --git a/rtc_base/net_helper.h b/rtc_base/net_helper.h index 9abbbdefb2..758c0faad9 100644 --- a/rtc_base/net_helper.h +++ b/rtc_base/net_helper.h @@ -12,6 +12,8 @@ #include +#include "absl/strings/string_view.h" + // This header contains helper functions and constants used by different types // of transports. namespace cricket { @@ -25,7 +27,7 @@ constexpr int kTcpHeaderSize = 20; constexpr int kUdpHeaderSize = 8; // Get the transport layer overhead per packet based on the protocol. -int GetProtocolOverhead(const std::string& protocol); +int GetProtocolOverhead(absl::string_view protocol); } // namespace cricket diff --git a/rtc_base/net_helpers.cc b/rtc_base/net_helpers.cc index bec854af03..f521f0f64b 100644 --- a/rtc_base/net_helpers.cc +++ b/rtc_base/net_helpers.cc @@ -16,7 +16,7 @@ #include #include -#include "rtc_base/win32.h" +#include "rtc_base/win/windows_version.h" #endif #if defined(WEBRTC_POSIX) && !defined(__native_client__) #include @@ -70,10 +70,10 @@ bool HasIPv6Enabled() { // WinUWP always has IPv6 capability. return true; #elif defined(WEBRTC_WIN) - if (IsWindowsVistaOrLater()) { + if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_VISTA) { return true; } - if (!IsWindowsXpOrLater()) { + if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_XP) { return false; } DWORD protbuff_size = 4096; diff --git a/rtc_base/network.cc b/rtc_base/network.cc index b485077129..52026308b7 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -10,6 +10,8 @@ #include "rtc_base/network.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_POSIX) #include #endif // WEBRTC_POSIX @@ -34,14 +36,20 @@ #include "rtc_base/string_encode.h" #include "rtc_base/string_utils.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread.h" #include "system_wrappers/include/field_trial.h" namespace rtc { namespace { -const uint32_t kUpdateNetworksMessage = 1; -const uint32_t kSignalNetworksMessage = 2; +// List of MAC addresses of known VPN (for windows). +constexpr uint8_t kVpns[2][6] = { + // Cisco AnyConnect. + {0x0, 0x5, 0x9A, 0x3C, 0x7A, 0x0}, + // GlobalProtect Virtual Ethernet. + {0x2, 0x50, 0x41, 0x0, 0x0, 0x1}, +}; // Fetch list of networks every two seconds. const int kNetworksUpdateIntervalMs = 2000; @@ -130,7 +138,7 @@ uint16_t ComputeNetworkCostByType(int type, return kNetworkCostMax + vpnCost; case rtc::ADAPTER_TYPE_VPN: // The cost of a VPN should be computed using its underlying network type. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return kNetworkCostUnknown; default: return kNetworkCostUnknown + vpnCost; @@ -184,7 +192,7 @@ const char kPublicIPv4Host[] = "8.8.8.8"; const char kPublicIPv6Host[] = "2001:4860:4860::8888"; const int kPublicPort = 53; // DNS port. -std::string MakeNetworkKey(const std::string& name, +std::string MakeNetworkKey(absl::string_view name, const IPAddress& prefix, int prefix_length) { rtc::StringBuilder ost; @@ -486,17 +494,45 @@ Network* NetworkManagerBase::GetNetworkFromAddress( return nullptr; } -BasicNetworkManager::BasicNetworkManager() : BasicNetworkManager(nullptr) {} +bool NetworkManagerBase::IsVpnMacAddress( + rtc::ArrayView address) { + if (address.data() == nullptr && address.size() == 0) { + return false; + } + for (const auto& vpn : kVpns) { + if (sizeof(vpn) == address.size() && + memcmp(vpn, address.data(), address.size()) == 0) { + return true; + } + } + return false; +} + +BasicNetworkManager::BasicNetworkManager() + : BasicNetworkManager(nullptr, nullptr) {} + +BasicNetworkManager::BasicNetworkManager(SocketFactory* socket_factory) + : BasicNetworkManager(nullptr, socket_factory) {} BasicNetworkManager::BasicNetworkManager( NetworkMonitorFactory* network_monitor_factory) + : BasicNetworkManager(network_monitor_factory, nullptr) {} + +BasicNetworkManager::BasicNetworkManager( + NetworkMonitorFactory* network_monitor_factory, + SocketFactory* socket_factory) : network_monitor_factory_(network_monitor_factory), + socket_factory_(socket_factory), allow_mac_based_ipv6_( webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")), bind_using_ifname_( !webrtc::field_trial::IsDisabled("WebRTC-BindUsingInterfaceName")) {} -BasicNetworkManager::~BasicNetworkManager() {} +BasicNetworkManager::~BasicNetworkManager() { + if (task_safety_flag_) { + task_safety_flag_->SetNotAlive(); + } +} void BasicNetworkManager::OnNetworksChanged() { RTC_DCHECK_RUN_ON(thread_); @@ -508,7 +544,7 @@ void BasicNetworkManager::OnNetworksChanged() { bool BasicNetworkManager::CreateNetworks(bool include_ignored, NetworkList* networks) const { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); RTC_LOG(LS_WARNING) << "BasicNetworkManager doesn't work on NaCl yet"; return false; } @@ -623,8 +659,8 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, struct ifaddrs* interfaces; int error = getifaddrs(&interfaces); if (error != 0) { - RTC_LOG_ERR(LERROR) << "getifaddrs failed to gather interface data: " - << error; + RTC_LOG_ERR(LS_ERROR) << "getifaddrs failed to gather interface data: " + << error; return false; } @@ -781,6 +817,15 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, vpn_underlying_adapter_type = adapter_type; adapter_type = ADAPTER_TYPE_VPN; } + if (adapter_type != ADAPTER_TYPE_VPN && + IsVpnMacAddress(rtc::ArrayView( + reinterpret_cast( + adapter_addrs->PhysicalAddress), + adapter_addrs->PhysicalAddressLength))) { + vpn_underlying_adapter_type = adapter_type; + adapter_type = ADAPTER_TYPE_VPN; + } + std::unique_ptr network(new Network( name, description, prefix, prefix_length, adapter_type)); network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); @@ -856,9 +901,17 @@ void BasicNetworkManager::StartUpdating() { // we should trigger network signal immediately for the new clients // to start allocating ports. if (sent_first_update_) - thread_->Post(RTC_FROM_HERE, this, kSignalNetworksMessage); + thread_->PostTask(ToQueuedTask(task_safety_flag_, [this] { + RTC_DCHECK_RUN_ON(thread_); + SignalNetworksChanged(); + })); } else { - thread_->Post(RTC_FROM_HERE, this, kUpdateNetworksMessage); + RTC_DCHECK(task_safety_flag_ == nullptr); + task_safety_flag_ = webrtc::PendingTaskSafetyFlag::Create(); + thread_->PostTask(ToQueuedTask(task_safety_flag_, [this] { + RTC_DCHECK_RUN_ON(thread_); + UpdateNetworksContinually(); + })); StartNetworkMonitor(); } ++start_count_; @@ -871,7 +924,8 @@ void BasicNetworkManager::StopUpdating() { --start_count_; if (!start_count_) { - thread_->Clear(this); + task_safety_flag_->SetNotAlive(); + task_safety_flag_ = nullptr; sent_first_update_ = false; StopNetworkMonitor(); } @@ -915,30 +969,21 @@ void BasicNetworkManager::StopNetworkMonitor() { } } -void BasicNetworkManager::OnMessage(Message* msg) { - RTC_DCHECK_RUN_ON(thread_); - switch (msg->message_id) { - case kUpdateNetworksMessage: { - UpdateNetworksContinually(); - break; - } - case kSignalNetworksMessage: { - SignalNetworksChanged(); - break; - } - default: - RTC_NOTREACHED(); - } -} - IPAddress BasicNetworkManager::QueryDefaultLocalAddress(int family) const { - RTC_DCHECK(thread_->socketserver() != nullptr); RTC_DCHECK(family == AF_INET || family == AF_INET6); + // TODO(bugs.webrtc.org/13145): Delete support for null `socket_factory_`, + // require socket factory to be provided to constructor. + SocketFactory* socket_factory = socket_factory_; + if (!socket_factory) { + socket_factory = thread_->socketserver(); + } + RTC_DCHECK(socket_factory); + std::unique_ptr socket( - thread_->socketserver()->CreateSocket(family, SOCK_DGRAM)); + socket_factory->CreateSocket(family, SOCK_DGRAM)); if (!socket) { - RTC_LOG_ERR(LERROR) << "Socket creation failed"; + RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; return IPAddress(); } @@ -978,8 +1023,12 @@ void BasicNetworkManager::UpdateNetworksOnce() { void BasicNetworkManager::UpdateNetworksContinually() { UpdateNetworksOnce(); - thread_->PostDelayed(RTC_FROM_HERE, kNetworksUpdateIntervalMs, this, - kUpdateNetworksMessage); + thread_->PostDelayedTask(ToQueuedTask(task_safety_flag_, + [this] { + RTC_DCHECK_RUN_ON(thread_); + UpdateNetworksContinually(); + }), + kNetworksUpdateIntervalMs); } void BasicNetworkManager::DumpNetworks() { @@ -1008,8 +1057,8 @@ NetworkBindingResult BasicNetworkManager::BindSocketToNetwork( return network_monitor_->BindSocketToNetwork(socket_fd, address, if_name); } -Network::Network(const std::string& name, - const std::string& desc, +Network::Network(absl::string_view name, + absl::string_view desc, const IPAddress& prefix, int prefix_length) : name_(name), @@ -1026,8 +1075,8 @@ Network::Network(const std::string& name, add_network_cost_to_vpn_( webrtc::field_trial::IsEnabled("WebRTC-AddNetworkCostToVpn")) {} -Network::Network(const std::string& name, - const std::string& desc, +Network::Network(absl::string_view name, + absl::string_view desc, const IPAddress& prefix, int prefix_length, AdapterType type) @@ -1120,6 +1169,51 @@ uint16_t Network::GetCost() const { add_network_cost_to_vpn_); } +// This is the inverse of ComputeNetworkCostByType(). +std::pair +Network::GuessAdapterFromNetworkCost(int network_cost) { + switch (network_cost) { + case kNetworkCostMin: + return {rtc::ADAPTER_TYPE_ETHERNET, false}; + case kNetworkCostMin + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_ETHERNET, true}; + case kNetworkCostLow: + return {rtc::ADAPTER_TYPE_WIFI, false}; + case kNetworkCostLow + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_WIFI, true}; + case kNetworkCostCellular: + return {rtc::ADAPTER_TYPE_CELLULAR, false}; + case kNetworkCostCellular + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_CELLULAR, true}; + case kNetworkCostCellular2G: + return {rtc::ADAPTER_TYPE_CELLULAR_2G, false}; + case kNetworkCostCellular2G + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_CELLULAR_2G, true}; + case kNetworkCostCellular3G: + return {rtc::ADAPTER_TYPE_CELLULAR_3G, false}; + case kNetworkCostCellular3G + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_CELLULAR_3G, true}; + case kNetworkCostCellular4G: + return {rtc::ADAPTER_TYPE_CELLULAR_4G, false}; + case kNetworkCostCellular4G + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_CELLULAR_4G, true}; + case kNetworkCostCellular5G: + return {rtc::ADAPTER_TYPE_CELLULAR_5G, false}; + case kNetworkCostCellular5G + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_CELLULAR_5G, true}; + case kNetworkCostUnknown: + return {rtc::ADAPTER_TYPE_UNKNOWN, false}; + case kNetworkCostUnknown + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_UNKNOWN, true}; + case kNetworkCostMax: + return {rtc::ADAPTER_TYPE_ANY, false}; + case kNetworkCostMax + kNetworkCostVpn: + return {rtc::ADAPTER_TYPE_ANY, true}; + } + RTC_LOG(LS_VERBOSE) << "Unknown network cost: " << network_cost; + return {rtc::ADAPTER_TYPE_UNKNOWN, false}; +} + std::string Network::ToString() const { rtc::StringBuilder ss; // Print out the first space-terminated token of the network desc, plus diff --git a/rtc_base/network.h b/rtc_base/network.h index 94350e0e4d..bf14bef0e4 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -19,13 +19,16 @@ #include #include +#include "absl/strings/string_view.h" +#include "api/array_view.h" #include "api/sequence_checker.h" #include "rtc_base/ip_address.h" #include "rtc_base/mdns_responder_interface.h" -#include "rtc_base/message_handler.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/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread_annotations.h" @@ -49,7 +52,7 @@ const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK; // Makes a string key for this network. Used in the network manager's maps. // Network objects are keyed on interface name, network prefix and the // length of that prefix. -std::string MakeNetworkKey(const std::string& name, +std::string MakeNetworkKey(absl::string_view name, const IPAddress& prefix, int prefix_length); @@ -194,6 +197,10 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { 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(rtc::ArrayView address); + protected: typedef std::map NetworkMap; // Updates `networks_` with the networks listed in `list`. If @@ -245,12 +252,18 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { // Basic implementation of the NetworkManager interface that gets list // of networks using OS APIs. class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, - public MessageHandlerAutoCleanup, public NetworkBinderInterface, public sigslot::has_slots<> { public: + ABSL_DEPRECATED( + "Use the version with socket_factory, see bugs.webrtc.org/13145") BasicNetworkManager(); + explicit BasicNetworkManager(SocketFactory* socket_factory); + ABSL_DEPRECATED( + "Use the version with socket_factory, see bugs.webrtc.org/13145") explicit BasicNetworkManager(NetworkMonitorFactory* network_monitor_factory); + BasicNetworkManager(NetworkMonitorFactory* network_monitor_factory, + SocketFactory* socket_factory); ~BasicNetworkManager() override; void StartUpdating() override; @@ -258,8 +271,6 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, void DumpNetworks() override; - // MessageHandler interface. - void OnMessage(Message* msg) override; bool started() { return start_count_ > 0; } // Sets the network ignore list, which is empty by default. Any network on the @@ -326,26 +337,27 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, bool sent_first_update_ = true; int start_count_ = 0; std::vector network_ignore_list_; - NetworkMonitorFactory* network_monitor_factory_ RTC_GUARDED_BY(thread_) = - nullptr; + NetworkMonitorFactory* const network_monitor_factory_; + SocketFactory* 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_; + rtc::scoped_refptr task_safety_flag_; }; // Represents a Unix-type network interface, with a name and single address. class RTC_EXPORT Network { public: - Network(const std::string& name, - const std::string& description, + Network(absl::string_view name, + absl::string_view description, const IPAddress& prefix, int prefix_length); - Network(const std::string& name, - const std::string& description, + Network(absl::string_view name, + absl::string_view description, const IPAddress& prefix, int prefix_length, AdapterType type); @@ -513,6 +525,9 @@ class RTC_EXPORT Network { SignalNetworkPreferenceChanged(this); } + static std::pair + GuessAdapterFromNetworkCost(int network_cost); + // Debugging description of this network std::string ToString() const; diff --git a/rtc_base/network_constants.cc b/rtc_base/network_constants.cc index 905aa3646c..9e6ab520e5 100644 --- a/rtc_base/network_constants.cc +++ b/rtc_base/network_constants.cc @@ -39,7 +39,7 @@ std::string AdapterTypeToString(AdapterType type) { case ADAPTER_TYPE_LOOPBACK: return "Loopback"; default: - RTC_NOTREACHED() << "Invalid type " << type; + RTC_DCHECK_NOTREACHED() << "Invalid type " << type; return std::string(); } } diff --git a/rtc_base/network_constants.h b/rtc_base/network_constants.h index 0495afdcc9..578b9710d0 100644 --- a/rtc_base/network_constants.h +++ b/rtc_base/network_constants.h @@ -57,6 +57,16 @@ enum AdapterType { 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 rtc #endif // RTC_BASE_NETWORK_CONSTANTS_H_ diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index c0eea1ff52..72571d1497 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/network_constants.h" namespace rtc { @@ -78,12 +79,12 @@ class NetworkMonitorInterface { virtual void Start() = 0; virtual void Stop() = 0; - virtual AdapterType GetAdapterType(const std::string& interface_name) = 0; + virtual AdapterType GetAdapterType(absl::string_view interface_name) = 0; virtual AdapterType GetVpnUnderlyingAdapterType( - const std::string& interface_name) = 0; + absl::string_view interface_name) = 0; virtual NetworkPreference GetNetworkPreference( - const std::string& interface_name) = 0; + absl::string_view interface_name) = 0; // Does `this` NetworkMonitorInterface implement BindSocketToNetwork? // Only Android returns true. @@ -94,7 +95,7 @@ class NetworkMonitorInterface { virtual NetworkBindingResult BindSocketToNetwork( int socket_fd, const IPAddress& address, - const std::string& interface_name) { + absl::string_view interface_name) { return NetworkBindingResult::NOT_IMPLEMENTED; } @@ -107,7 +108,7 @@ class NetworkMonitorInterface { // These specific use case this was added for was a phone with two SIM cards, // where attempting to use all interfaces returned from getifaddrs caused the // connection to be dropped. - virtual bool IsAdapterAvailable(const std::string& interface_name) { + virtual bool IsAdapterAvailable(absl::string_view interface_name) { return true; } diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index e7b9982cc6..0b5f3e94ac 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -18,10 +18,12 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/net_helpers.h" #include "rtc_base/network_monitor.h" #include "rtc_base/network_monitor_factory.h" +#include "rtc_base/physical_socket_server.h" #if defined(WEBRTC_POSIX) #include #include @@ -44,7 +46,7 @@ namespace rtc { namespace { -IPAddress IPFromString(const std::string& str) { +IPAddress IPFromString(absl::string_view str) { IPAddress ip; RTC_CHECK(IPFromString(str, &ip)); return ip; @@ -55,7 +57,7 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { void Start() override { started_ = true; } void Stop() override { started_ = false; } bool started() { return started_; } - AdapterType GetAdapterType(const std::string& if_name) override { + AdapterType GetAdapterType(absl::string_view if_name) override { // Note that the name matching rules are different from the // GetAdapterTypeFromName in NetworkManager. if (absl::StartsWith(if_name, "wifi")) { @@ -66,14 +68,14 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { } return ADAPTER_TYPE_UNKNOWN; } - AdapterType GetVpnUnderlyingAdapterType(const std::string& if_name) override { + AdapterType GetVpnUnderlyingAdapterType(absl::string_view if_name) override { return ADAPTER_TYPE_UNKNOWN; } - NetworkPreference GetNetworkPreference(const std::string& if_name) override { + NetworkPreference GetNetworkPreference(absl::string_view if_name) override { return NetworkPreference::NEUTRAL; } - bool IsAdapterAvailable(const std::string& if_name) override { + bool IsAdapterAvailable(absl::string_view if_name) override { return absl::c_count(unavailable_adapters_, if_name) == 0; } @@ -84,16 +86,15 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { bool SupportsBindSocketToNetwork() const override { return true; } - NetworkBindingResult BindSocketToNetwork( - int socket_fd, - const IPAddress& address, - const std::string& if_name) override { + NetworkBindingResult BindSocketToNetwork(int socket_fd, + const IPAddress& address, + absl::string_view if_name) override { if (absl::c_count(addresses_, address) > 0) { return NetworkBindingResult::SUCCESS; } for (auto const& iter : adapters_) { - if (if_name.find(iter) != std::string::npos) { + if (if_name.find(iter) != absl::string_view::npos) { return NetworkBindingResult::SUCCESS; } } @@ -127,11 +128,11 @@ class FakeNetworkMonitorFactory : public NetworkMonitorFactory { bool SameNameAndPrefix(const rtc::Network& a, const rtc::Network& b) { if (a.name() != b.name()) { - RTC_LOG(INFO) << "Different interface names."; + RTC_LOG(LS_INFO) << "Different interface names."; return false; } if (a.prefix_length() != b.prefix_length() || a.prefix() != b.prefix()) { - RTC_LOG(INFO) << "Different IP prefixes."; + RTC_LOG(LS_INFO) << "Different IP prefixes."; return false; } return true; @@ -208,7 +209,7 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { include_ignored, networks); } - struct sockaddr_in6* CreateIpv6Addr(const std::string& ip_string, + struct sockaddr_in6* CreateIpv6Addr(absl::string_view ip_string, uint32_t scope_id) { struct sockaddr_in6* ipv6_addr = static_cast(malloc(sizeof(struct sockaddr_in6))); @@ -224,8 +225,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { // Pointers created here need to be released via ReleaseIfAddrs. struct ifaddrs* AddIpv6Address(struct ifaddrs* list, char* if_name, - const std::string& ipv6_address, - const std::string& ipv6_netmask, + absl::string_view ipv6_address, + absl::string_view ipv6_netmask, uint32_t scope_id) { struct ifaddrs* if_addr = new struct ifaddrs; memset(if_addr, 0, sizeof(struct ifaddrs)); @@ -240,8 +241,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { } struct ifaddrs* InstallIpv6Network(char* if_name, - const std::string& ipv6_address, - const std::string& ipv6_mask, + absl::string_view ipv6_address, + absl::string_view ipv6_mask, BasicNetworkManager& network_manager) { ifaddrs* addr_list = nullptr; addr_list = AddIpv6Address(addr_list, if_name, ipv6_address, ipv6_mask, 0); @@ -253,7 +254,7 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { return addr_list; } - struct sockaddr_in* CreateIpv4Addr(const std::string& ip_string) { + struct sockaddr_in* CreateIpv4Addr(absl::string_view ip_string) { struct sockaddr_in* ipv4_addr = static_cast(malloc(sizeof(struct sockaddr_in))); memset(ipv4_addr, 0, sizeof(struct sockaddr_in)); @@ -267,8 +268,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { // Pointers created here need to be released via ReleaseIfAddrs. struct ifaddrs* AddIpv4Address(struct ifaddrs* list, char* if_name, - const std::string& ipv4_address, - const std::string& ipv4_netmask) { + absl::string_view ipv4_address, + absl::string_view ipv4_netmask) { struct ifaddrs* if_addr = new struct ifaddrs; memset(if_addr, 0, sizeof(struct ifaddrs)); if_addr->ifa_name = if_name; @@ -282,8 +283,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { } struct ifaddrs* InstallIpv4Network(char* if_name, - const std::string& ipv4_address, - const std::string& ipv4_mask, + absl::string_view ipv4_address, + absl::string_view ipv4_mask, BasicNetworkManager& network_manager) { ifaddrs* addr_list = nullptr; addr_list = AddIpv4Address(addr_list, if_name, ipv4_address, ipv4_mask); @@ -313,8 +314,9 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { class TestBasicNetworkManager : public BasicNetworkManager { public: - TestBasicNetworkManager(NetworkMonitorFactory* network_monitor_factory) - : BasicNetworkManager(network_monitor_factory) {} + TestBasicNetworkManager(NetworkMonitorFactory* network_monitor_factory, + SocketFactory* socket_factory) + : BasicNetworkManager(network_monitor_factory, socket_factory) {} using BasicNetworkManager::QueryDefaultLocalAddress; using BasicNetworkManager::set_default_local_addresses; }; @@ -335,7 +337,8 @@ TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresIPsStartingWith0) { IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET); Network ipv4_network2("test_eth1", "Test Network Adapter 2", IPAddress(0x010000U), 24, ADAPTER_TYPE_ETHERNET); - BasicNetworkManager network_manager; + PhysicalSocketServer socket_server; + BasicNetworkManager network_manager(&socket_server); network_manager.StartUpdating(); EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1)); EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2)); @@ -347,12 +350,13 @@ TEST_F(NetworkTest, TestIgnoreList) { 24); Network include_me("include_me", "Include me please!", IPAddress(0x12345600U), 24); - BasicNetworkManager default_network_manager; + PhysicalSocketServer socket_server; + BasicNetworkManager default_network_manager(&socket_server); default_network_manager.StartUpdating(); EXPECT_FALSE(IsIgnoredNetwork(default_network_manager, ignore_me)); EXPECT_FALSE(IsIgnoredNetwork(default_network_manager, include_me)); - BasicNetworkManager ignoring_network_manager; + BasicNetworkManager ignoring_network_manager(&socket_server); std::vector ignore_list; ignore_list.push_back("ignore_me"); ignoring_network_manager.set_network_ignore_list(ignore_list); @@ -363,7 +367,8 @@ TEST_F(NetworkTest, TestIgnoreList) { // Test is failing on Windows opt: b/11288214 TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); NetworkManager::NetworkList result = GetNetworks(manager, true); // We should be able to bind to any addresses we find. NetworkManager::NetworkList::iterator it; @@ -398,7 +403,8 @@ TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { // Test StartUpdating() and StopUpdating(). network_permission_state starts with // ALLOWED. TEST_F(NetworkTest, TestUpdateNetworks) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(nullptr, &socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED, @@ -437,7 +443,8 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { IPAddress(0x00010000U), 16); ipv4_network1.AddIP(IPAddress(0x12345678)); ipv4_network2.AddIP(IPAddress(0x00010004)); - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); // Add ipv4_network1 to the list of networks. NetworkManager::NetworkList list; @@ -546,7 +553,8 @@ void SetupNetworks(NetworkManager::NetworkList* list) { // Test that the basic network merging case works. TEST_F(NetworkTest, TestIPv6MergeNetworkList) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); NetworkManager::NetworkList original_list; @@ -567,7 +575,8 @@ TEST_F(NetworkTest, TestIPv6MergeNetworkList) { // merged, that the changed callback is not called, and that the original // objects remain in the result list. TEST_F(NetworkTest, TestNoChangeMerge) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); NetworkManager::NetworkList original_list; @@ -595,7 +604,8 @@ TEST_F(NetworkTest, TestNoChangeMerge) { // a different IP. The original network should remain in the list, but have its // IP changed. TEST_F(NetworkTest, MergeWithChangedIP) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); NetworkManager::NetworkList original_list; @@ -628,10 +638,12 @@ TEST_F(NetworkTest, MergeWithChangedIP) { EXPECT_EQ(changed_ip, network_to_change->GetIPs().at(0)); } +// TODO(bugs.webrtc.org/13846): Re-enable when the ASan issue is fixed. // Testing a similar case to above, but checking that a network can be updated // with additional IPs (not just a replacement). -TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) { - BasicNetworkManager manager; +TEST_F(NetworkTest, DISABLED_TestMultipleIPMergeNetworkList) { + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); NetworkManager::NetworkList original_list; @@ -680,7 +692,8 @@ TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) { // Test that merge correctly distinguishes multiple networks on an interface. TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); NetworkManager::NetworkList original_list; @@ -721,7 +734,8 @@ TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) { // Test that DumpNetworks does not crash. TEST_F(NetworkTest, TestCreateAndDumpNetworks) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); NetworkManager::NetworkList list = GetNetworks(manager, true); bool changed; @@ -730,7 +744,8 @@ TEST_F(NetworkTest, TestCreateAndDumpNetworks) { } TEST_F(NetworkTest, TestIPv6Toggle) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); bool ipv6_found = false; NetworkManager::NetworkList list; @@ -752,7 +767,8 @@ TEST_F(NetworkTest, TestIPv6Toggle) { // Test that when network interfaces are sorted and given preference values, // IPv6 comes first. TEST_F(NetworkTest, IPv6NetworksPreferredOverIPv4) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); Network ipv4_network1("test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); ipv4_network1.AddIP(IPAddress(0x12345600U)); @@ -781,7 +797,8 @@ TEST_F(NetworkTest, IPv6NetworksPreferredOverIPv4) { // When two interfaces are equivalent in everything but name, they're expected // to be preference-ordered by name. For example, "eth0" before "eth1". TEST_F(NetworkTest, NetworksSortedByInterfaceName) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); Network* eth0 = new Network("test_eth0", "Test Network Adapter 1", IPAddress(0x65432100U), 24); eth0->AddIP(IPAddress(0x65432100U)); @@ -827,7 +844,8 @@ TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { list.ifa_name = const_cast("test_iface"); NetworkManager::NetworkList result; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); CallConvertIfAddrs(manager, &list, true, &result); EXPECT_TRUE(result.empty()); @@ -843,7 +861,8 @@ TEST_F(NetworkTest, TestConvertIfAddrsMultiAddressesOnOneInterface) { list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:2", "FFFF:FFFF:FFFF:FFFF::", 0); NetworkManager::NetworkList result; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); CallConvertIfAddrs(manager, list, true, &result); EXPECT_EQ(1U, result.size()); @@ -863,7 +882,8 @@ TEST_F(NetworkTest, TestConvertIfAddrsNotRunning) { list.ifa_netmask = &ifa_netmask; NetworkManager::NetworkList result; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); CallConvertIfAddrs(manager, &list, true, &result); EXPECT_TRUE(result.empty()); @@ -875,7 +895,8 @@ TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) { char if_name[20] = "wifi0"; std::string ipv6_address = "1000:2000:3000:4000:0:0:0:1"; std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::"; - BasicNetworkManager manager_without_monitor; + PhysicalSocketServer socket_server; + BasicNetworkManager manager_without_monitor(nullptr, &socket_server); manager_without_monitor.StartUpdating(); // A network created without a network monitor will get UNKNOWN type. ifaddrs* addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask, @@ -885,7 +906,7 @@ TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) { // With the fake network monitor the type should be correctly determined. FakeNetworkMonitorFactory factory; - BasicNetworkManager manager_with_monitor(&factory); + BasicNetworkManager manager_with_monitor(&factory, &socket_server); manager_with_monitor.StartUpdating(); // Add the same ipv6 address as before but it has the right network type // detected by the network monitor now. @@ -904,7 +925,8 @@ TEST_F(NetworkTest, TestGetAdapterTypeFromNameMatching) { std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1"; std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1"; std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::"; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); // IPSec interface; name is in form "ipsec". @@ -981,7 +1003,8 @@ TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) { // Sanity check that both interfaces are included by default. FakeNetworkMonitorFactory factory; - BasicNetworkManager manager(&factory); + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&factory, &socket_server); manager.StartUpdating(); CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result); EXPECT_EQ(2u, result.size()); @@ -1006,7 +1029,8 @@ TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) { // Test MergeNetworkList successfully combines all IPs for the same // prefix/length into a single Network. TEST_F(NetworkTest, TestMergeNetworkList) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); NetworkManager::NetworkList list; // Create 2 IPAddress classes with only last digit different. @@ -1035,14 +1059,15 @@ TEST_F(NetworkTest, TestMergeNetworkList) { // IPAddresses. EXPECT_EQ(list2.size(), 1uL); EXPECT_EQ(list2[0]->GetIPs().size(), 2uL); - EXPECT_EQ(list2[0]->GetIPs()[0], InterfaceAddress(ip1)); - EXPECT_EQ(list2[0]->GetIPs()[1], InterfaceAddress(ip2)); + EXPECT_THAT(list2[0]->GetIPs(), UnorderedElementsAre(InterfaceAddress(ip1), + InterfaceAddress(ip2))); } // Test that MergeNetworkList successfully detects the change if // a network becomes inactive and then active again. TEST_F(NetworkTest, TestMergeNetworkListWithInactiveNetworks) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); Network network1("test_wifi", "Test Network Adapter 1", IPAddress(0x12345600U), 24); Network network2("test_eth0", "Test Network Adapter 2", @@ -1125,7 +1150,8 @@ TEST_F(NetworkTest, TestIPv6Selection) { TEST_F(NetworkTest, TestNetworkMonitoring) { FakeNetworkMonitorFactory factory; - BasicNetworkManager manager(&factory); + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&factory, &socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); manager.StartUpdating(); @@ -1155,7 +1181,8 @@ TEST_F(NetworkTest, TestNetworkMonitoring) { TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) { IPAddress ip; FakeNetworkMonitorFactory factory; - TestBasicNetworkManager manager(&factory); + PhysicalSocketServer socket_server; + TestBasicNetworkManager manager(&factory, &socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); manager.StartUpdating(); @@ -1219,7 +1246,8 @@ TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) { // Test that MergeNetworkList does not set change = true // when changing from cellular_X to cellular_Y. TEST_F(NetworkTest, TestWhenNetworkListChangeReturnsChangedFlag) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); IPAddress ip1; EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); @@ -1281,7 +1309,8 @@ TEST_F(NetworkTest, TestWhenNetworkListChangeReturnsChangedFlag) { TEST_F(NetworkTest, IgnoresMACBasedIPv6Address) { std::string ipv6_address = "2607:fc20:f340:1dc8:214:22ff:fe01:2345"; std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); // IPSec interface; name is in form "ipsec". @@ -1300,7 +1329,8 @@ TEST_F(NetworkTest, WebRTC_AllowMACBasedIPv6Address) { "WebRTC-AllowMACBasedIPv6/Enabled/"); std::string ipv6_address = "2607:fc20:f340:1dc8:214:22ff:fe01:2345"; std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.StartUpdating(); // IPSec interface; name is in form "ipsec". @@ -1327,7 +1357,8 @@ TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) { // Sanity check that both interfaces are included by default. FakeNetworkMonitorFactory factory; - BasicNetworkManager manager(&factory); + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&factory, &socket_server); manager.StartUpdating(); CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result); EXPECT_EQ(2u, result.size()); @@ -1391,9 +1422,49 @@ TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) { delete net2; } +TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-AddNetworkCostToVpn/Enabled/" + "WebRTC-UseDifferentiatedCellularCosts/Enabled/"); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + for (auto type : kAllAdapterTypes) { + if (type == rtc::ADAPTER_TYPE_VPN) + continue; + Network net1("em1", "em1", TruncateIP(ip1, 64), 64); + net1.set_type(type); + auto [guess, vpn] = Network::GuessAdapterFromNetworkCost(net1.GetCost()); + EXPECT_FALSE(vpn); + if (type == rtc::ADAPTER_TYPE_LOOPBACK) { + EXPECT_EQ(guess, rtc::ADAPTER_TYPE_ETHERNET); + } else { + EXPECT_EQ(type, guess); + } + } + + // VPN + for (auto type : kAllAdapterTypes) { + if (type == rtc::ADAPTER_TYPE_VPN) + continue; + Network net1("em1", "em1", TruncateIP(ip1, 64), 64); + net1.set_type(rtc::ADAPTER_TYPE_VPN); + net1.set_underlying_type_for_vpn(type); + auto [guess, vpn] = Network::GuessAdapterFromNetworkCost(net1.GetCost()); + EXPECT_TRUE(vpn); + if (type == rtc::ADAPTER_TYPE_LOOPBACK) { + EXPECT_EQ(guess, rtc::ADAPTER_TYPE_ETHERNET); + } else { + EXPECT_EQ(type, guess); + } + } +} + TEST_F(NetworkTest, VpnList) { + PhysicalSocketServer socket_server; { - BasicNetworkManager manager; + BasicNetworkManager manager(&socket_server); manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)}); manager.StartUpdating(); EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32)); @@ -1405,7 +1476,7 @@ TEST_F(NetworkTest, VpnList) { EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 15)); } { - BasicNetworkManager manager; + BasicNetworkManager manager(&socket_server); manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 24)}); manager.StartUpdating(); EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32)); @@ -1416,7 +1487,8 @@ TEST_F(NetworkTest, VpnList) { #if defined(WEBRTC_POSIX) // TODO(webrtc:13114): Implement the InstallIpv4Network for windows. TEST_F(NetworkTest, VpnListOverrideAdapterType) { - BasicNetworkManager manager; + PhysicalSocketServer socket_server; + BasicNetworkManager manager(&socket_server); manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)}); manager.StartUpdating(); @@ -1434,4 +1506,19 @@ TEST_F(NetworkTest, VpnListOverrideAdapterType) { } #endif // defined(WEBRTC_POSIX) +TEST_F(NetworkTest, HardcodedVpn) { + const uint8_t cisco[] = {0x0, 0x5, 0x9A, 0x3C, 0x7A, 0x0}; + const uint8_t global[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x1}; + const uint8_t unknown[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x0}; + const uint8_t five_bytes[] = {0x2, 0x50, 0x41, 0x0, 0x0}; + EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(cisco)); + EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(global)); + + EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress( + rtc::ArrayView(cisco, 5))); + EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(five_bytes)); + EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(unknown)); + EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(nullptr)); +} + } // namespace rtc diff --git a/rtc_base/null_socket_server.cc b/rtc_base/null_socket_server.cc index ce1e963be4..4705163c4a 100644 --- a/rtc_base/null_socket_server.cc +++ b/rtc_base/null_socket_server.cc @@ -30,7 +30,7 @@ void NullSocketServer::WakeUp() { } rtc::Socket* NullSocketServer::CreateSocket(int /* family */, int /* type */) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return nullptr; } diff --git a/rtc_base/numerics/histogram_percentile_counter.cc b/rtc_base/numerics/histogram_percentile_counter.cc index 4bc8cb0dec..29d2341c85 100644 --- a/rtc_base/numerics/histogram_percentile_counter.cc +++ b/rtc_base/numerics/histogram_percentile_counter.cc @@ -72,7 +72,7 @@ absl::optional HistogramPercentileCounter::GetPercentile( elements_to_skip -= it.second; } } - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return absl::nullopt; } diff --git a/rtc_base/numerics/moving_max_counter.h b/rtc_base/numerics/moving_max_counter.h index 26dd506d63..5eb45d392b 100644 --- a/rtc_base/numerics/moving_max_counter.h +++ b/rtc_base/numerics/moving_max_counter.h @@ -19,7 +19,6 @@ #include "absl/types/optional.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" namespace rtc { @@ -34,6 +33,10 @@ 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); @@ -57,7 +60,6 @@ class MovingMaxCounter { #if RTC_DCHECK_IS_ON int64_t last_call_time_ms_ = std::numeric_limits::min(); #endif - RTC_DISALLOW_COPY_AND_ASSIGN(MovingMaxCounter); }; template diff --git a/rtc_base/numerics/moving_median_filter.h b/rtc_base/numerics/moving_median_filter.h index 157eb152c3..2a8ea7d62a 100644 --- a/rtc_base/numerics/moving_median_filter.h +++ b/rtc_base/numerics/moving_median_filter.h @@ -17,7 +17,6 @@ #include #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/numerics/percentile_filter.h" namespace webrtc { @@ -30,6 +29,9 @@ class MovingMedianFilter { // used to take median. `window_size` must be positive. explicit MovingMedianFilter(size_t window_size); + MovingMedianFilter(const MovingMedianFilter&) = delete; + MovingMedianFilter& operator=(const MovingMedianFilter&) = delete; + // Insert a new sample. void Insert(const T& value); @@ -47,8 +49,6 @@ class MovingMedianFilter { std::list samples_; size_t samples_stored_; const size_t window_size_; - - RTC_DISALLOW_COPY_AND_ASSIGN(MovingMedianFilter); }; template diff --git a/rtc_base/numerics/percentile_filter_unittest.cc b/rtc_base/numerics/percentile_filter_unittest.cc index 0de8be5609..d6baa32001 100644 --- a/rtc_base/numerics/percentile_filter_unittest.cc +++ b/rtc_base/numerics/percentile_filter_unittest.cc @@ -18,7 +18,6 @@ #include #include "absl/algorithm/container.h" -#include "rtc_base/constructor_magic.h" #include "test/gtest.h" namespace webrtc { @@ -30,11 +29,11 @@ class PercentileFilterTest : public ::testing::TestWithParam { srand(42); } + PercentileFilterTest(const PercentileFilterTest&) = delete; + PercentileFilterTest& operator=(const PercentileFilterTest&) = delete; + protected: PercentileFilter filter_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(PercentileFilterTest); }; INSTANTIATE_TEST_SUITE_P(PercentileFilterTests, diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc index d80b69d201..dc9aefaa8e 100644 --- a/rtc_base/openssl_adapter.cc +++ b/rtc_base/openssl_adapter.cc @@ -13,6 +13,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #ifdef OPENSSL_IS_BORINGSSL #include #endif @@ -250,21 +252,6 @@ void OpenSSLAdapter::SetRole(SSLRole role) { role_ = role; } -Socket* OpenSSLAdapter::Accept(SocketAddress* paddr) { - RTC_DCHECK(role_ == SSL_SERVER); - Socket* socket = SSLAdapter::Accept(paddr); - if (!socket) { - return nullptr; - } - - SSLAdapter* adapter = SSLAdapter::Create(socket); - adapter->SetIdentity(identity_->Clone()); - adapter->SetRole(rtc::SSL_SERVER); - adapter->SetIgnoreBadCert(ignore_bad_cert_); - adapter->StartSSL(""); - return adapter; -} - int OpenSSLAdapter::StartSSL(const char* hostname) { if (state_ != SSL_NONE) return -1; @@ -759,7 +746,7 @@ void OpenSSLAdapter::OnCloseEvent(Socket* socket, int err) { AsyncSocketAdapter::OnCloseEvent(socket, err); } -bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const std::string& host) { +bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, absl::string_view host) { bool is_valid_cert_name = openssl::VerifyPeerCertMatchesHost(ssl, host) && (SSL_get_verify_result(ssl) == X509_V_OK || custom_cert_verifier_status_); @@ -848,27 +835,31 @@ enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyInternal(SSL* ssl, return ssl_verify_ok; } #else // WEBRTC_USE_CRYPTO_BUFFER_CALLBACK -int OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { +int OpenSSLAdapter::SSLVerifyCallback(int status, X509_STORE_CTX* store) { // Get our stream pointer from the store SSL* ssl = reinterpret_cast( X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx())); OpenSSLAdapter* stream = reinterpret_cast(SSL_get_app_data(ssl)); - ok = stream->SSLVerifyInternal(ok, ssl, store); + // Update status with the custom verifier. + // Status is unchanged if verification fails. + status = stream->SSLVerifyInternal(status, ssl, store); // Should only be used for debugging and development. - if (!ok && stream->ignore_bad_cert_) { + if (!status && stream->ignore_bad_cert_) { RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; return 1; } - return ok; + return status; } -int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) { +int OpenSSLAdapter::SSLVerifyInternal(int previous_status, + SSL* ssl, + X509_STORE_CTX* store) { #if !defined(NDEBUG) - if (!ok) { + if (!previous_status) { char data[256]; X509* cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); @@ -883,8 +874,10 @@ int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) { << X509_verify_cert_error_string(err); } #endif - if (ssl_cert_verifier_ == nullptr) { - return ok; + // `ssl_cert_verifier_` is used to override errors; if there is no error + // there is no reason to call it. + if (previous_status || ssl_cert_verifier_ == nullptr) { + return previous_status; } RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback."; @@ -894,14 +887,14 @@ int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) { int length = i2d_X509(X509_STORE_CTX_get_current_cert(store), &data); if (length < 0) { RTC_LOG(LS_ERROR) << "Failed to encode X509."; - return ok; + return previous_status; } bssl::UniquePtr owned_data(data); bssl::UniquePtr crypto_buffer( CRYPTO_BUFFER_new(data, length, openssl::GetBufferPool())); if (!crypto_buffer) { RTC_LOG(LS_ERROR) << "Failed to allocate CRYPTO_BUFFER."; - return ok; + return previous_status; } const BoringSSLCertificate cert(std::move(crypto_buffer)); #else @@ -909,7 +902,7 @@ int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) { #endif if (!ssl_cert_verifier_->Verify(cert)) { RTC_LOG(LS_INFO) << "Failed to verify certificate using custom callback"; - return ok; + return previous_status; } custom_cert_verifier_status_ = true; @@ -1032,6 +1025,21 @@ void OpenSSLAdapterFactory::SetCertVerifier( ssl_cert_verifier_ = ssl_cert_verifier; } +void OpenSSLAdapterFactory::SetIdentity(std::unique_ptr identity) { + RTC_DCHECK(!ssl_session_cache_); + identity_ = std::move(identity); +} + +void OpenSSLAdapterFactory::SetRole(SSLRole role) { + RTC_DCHECK(!ssl_session_cache_); + ssl_role_ = role; +} + +void OpenSSLAdapterFactory::SetIgnoreBadCert(bool ignore) { + RTC_DCHECK(!ssl_session_cache_); + ignore_bad_cert_ = ignore; +} + OpenSSLAdapter* OpenSSLAdapterFactory::CreateAdapter(Socket* socket) { if (ssl_session_cache_ == nullptr) { SSL_CTX* ssl_ctx = OpenSSLAdapter::CreateContext(ssl_mode_, true); @@ -1043,8 +1051,14 @@ OpenSSLAdapter* OpenSSLAdapterFactory::CreateAdapter(Socket* socket) { std::make_unique(ssl_mode_, ssl_ctx); SSL_CTX_free(ssl_ctx); } - return new OpenSSLAdapter(socket, ssl_session_cache_.get(), - ssl_cert_verifier_); + OpenSSLAdapter* ssl_adapter = + new OpenSSLAdapter(socket, ssl_session_cache_.get(), ssl_cert_verifier_); + ssl_adapter->SetRole(ssl_role_); + ssl_adapter->SetIgnoreBadCert(ignore_bad_cert_); + if (identity_) { + ssl_adapter->SetIdentity(identity_->Clone()); + } + return ssl_adapter; } OpenSSLAdapter::EarlyExitCatcher::EarlyExitCatcher(OpenSSLAdapter& adapter_ptr) diff --git a/rtc_base/openssl_adapter.h b/rtc_base/openssl_adapter.h index 4649de0d1d..942d1fef12 100644 --- a/rtc_base/openssl_adapter.h +++ b/rtc_base/openssl_adapter.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/message_handler.h" #ifdef OPENSSL_IS_BORINGSSL @@ -60,7 +61,6 @@ class OpenSSLAdapter final : public SSLAdapter, void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) override; void SetIdentity(std::unique_ptr identity) override; void SetRole(SSLRole role) override; - Socket* Accept(SocketAddress* paddr) override; int StartSSL(const char* hostname) override; int Send(const void* pv, size_t cb) override; int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override; @@ -117,7 +117,7 @@ class OpenSSLAdapter final : public SSLAdapter, // an output parameter filled with the result of SSL_get_error. int DoSslWrite(const void* pv, size_t cb, int* error); void OnMessage(Message* msg) override; - bool SSLPostConnectionCheck(SSL* ssl, const std::string& host); + bool SSLPostConnectionCheck(SSL* ssl, absl::string_view host); #if !defined(NDEBUG) // In debug builds, logs info about the state of the SSL connection. @@ -131,7 +131,9 @@ class OpenSSLAdapter final : public SSLAdapter, enum ssl_verify_result_t SSLVerifyInternal(SSL* ssl, uint8_t* out_alert); #else static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); - int SSLVerifyInternal(int ok, SSL* ssl, 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_; @@ -189,10 +191,21 @@ class OpenSSLAdapterFactory : public SSLAdapterFactory { // 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. @@ -201,6 +214,11 @@ class OpenSSLAdapterFactory : public SSLAdapterFactory { private: // Holds the SSLMode (DTLS,TLS) that will be used to set the session cache. SSLMode ssl_mode_ = SSL_MODE_TLS; + SSLRole ssl_role_ = 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 diff --git a/rtc_base/openssl_certificate.h b/rtc_base/openssl_certificate.h index c317a72110..b2debbee89 100644 --- a/rtc_base/openssl_certificate.h +++ b/rtc_base/openssl_certificate.h @@ -18,7 +18,6 @@ #include #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -42,6 +41,9 @@ class OpenSSLCertificate final : public SSLCertificate { ~OpenSSLCertificate() override; + OpenSSLCertificate(const OpenSSLCertificate&) = delete; + OpenSSLCertificate& operator=(const OpenSSLCertificate&) = delete; + std::unique_ptr Clone() const override; X509* x509() const { return x509_; } @@ -70,7 +72,6 @@ class OpenSSLCertificate final : public SSLCertificate { private: X509* x509_; // NOT OWNED - RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLCertificate); }; } // namespace rtc diff --git a/rtc_base/openssl_digest.cc b/rtc_base/openssl_digest.cc index 1cf5bc09b4..bbf39570f6 100644 --- a/rtc_base/openssl_digest.cc +++ b/rtc_base/openssl_digest.cc @@ -10,12 +10,13 @@ #include "rtc_base/openssl_digest.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" // RTC_DCHECK, RTC_CHECK #include "rtc_base/openssl.h" namespace rtc { -OpenSSLDigest::OpenSSLDigest(const std::string& algorithm) { +OpenSSLDigest::OpenSSLDigest(absl::string_view algorithm) { ctx_ = EVP_MD_CTX_new(); RTC_CHECK(ctx_ != nullptr); EVP_MD_CTX_init(ctx_); @@ -55,7 +56,7 @@ size_t OpenSSLDigest::Finish(void* buf, size_t len) { return md_len; } -bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, +bool OpenSSLDigest::GetDigestEVP(absl::string_view algorithm, const EVP_MD** mdp) { const EVP_MD* md; if (algorithm == DIGEST_MD5) { @@ -105,8 +106,7 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, std::string* algorithm) { return true; } -bool OpenSSLDigest::GetDigestSize(const std::string& algorithm, - size_t* length) { +bool OpenSSLDigest::GetDigestSize(absl::string_view algorithm, size_t* length) { const EVP_MD* md; if (!GetDigestEVP(algorithm, &md)) return false; diff --git a/rtc_base/openssl_digest.h b/rtc_base/openssl_digest.h index 6da01a0ded..c6cc3bb86d 100644 --- a/rtc_base/openssl_digest.h +++ b/rtc_base/openssl_digest.h @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/message_digest.h" namespace rtc { @@ -24,7 +25,7 @@ namespace rtc { class OpenSSLDigest final : public MessageDigest { public: // Creates an OpenSSLDigest with `algorithm` as the hash algorithm. - explicit OpenSSLDigest(const std::string& algorithm); + explicit OpenSSLDigest(absl::string_view algorithm); ~OpenSSLDigest() override; // Returns the digest output size (e.g. 16 bytes for MD5). size_t Size() const override; @@ -34,11 +35,11 @@ class OpenSSLDigest final : public MessageDigest { size_t Finish(void* buf, size_t len) override; // Helper function to look up a digest's EVP by name. - static bool GetDigestEVP(const std::string& algorithm, const EVP_MD** md); + static bool GetDigestEVP(absl::string_view algorithm, const EVP_MD** md); // Helper function to look up a digest's name by EVP. static bool GetDigestName(const EVP_MD* md, std::string* algorithm); // Helper function to get the length of a digest. - static bool GetDigestSize(const std::string& algorithm, size_t* len); + static bool GetDigestSize(absl::string_view algorithm, size_t* len); private: EVP_MD_CTX* ctx_ = nullptr; diff --git a/rtc_base/openssl_identity.h b/rtc_base/openssl_identity.h index 00d6c74922..63f46b374d 100644 --- a/rtc_base/openssl_identity.h +++ b/rtc_base/openssl_identity.h @@ -17,7 +17,6 @@ #include #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/openssl_certificate.h" #include "rtc_base/openssl_key_pair.h" #include "rtc_base/ssl_certificate.h" @@ -43,6 +42,9 @@ class OpenSSLIdentity final : public SSLIdentity { const std::string& certificate_chain); ~OpenSSLIdentity() override; + OpenSSLIdentity(const OpenSSLIdentity&) = delete; + OpenSSLIdentity& operator=(const OpenSSLIdentity&) = delete; + const OpenSSLCertificate& certificate() const override; const SSLCertChain& cert_chain() const override; @@ -66,8 +68,6 @@ class OpenSSLIdentity final : public SSLIdentity { std::unique_ptr key_pair_; std::unique_ptr cert_chain_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLIdentity); }; } // namespace rtc diff --git a/rtc_base/openssl_key_pair.cc b/rtc_base/openssl_key_pair.cc index 911a751cbe..4c474f2d54 100644 --- a/rtc_base/openssl_key_pair.cc +++ b/rtc_base/openssl_key_pair.cc @@ -13,6 +13,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -103,7 +105,7 @@ std::unique_ptr OpenSSLKeyPair::Generate( } std::unique_ptr OpenSSLKeyPair::FromPrivateKeyPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { BIO* bio = BIO_new_mem_buf(const_cast(pem_string.data()), pem_string.size()); if (!bio) { @@ -143,14 +145,14 @@ std::string OpenSSLKeyPair::PrivateKeyToPEMString() const { BIO* temp_memory_bio = BIO_new(BIO_s_mem()); if (!temp_memory_bio) { RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr)) { RTC_LOG_F(LS_ERROR) << "Failed to write private key"; BIO_free(temp_memory_bio); - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } char* buffer; @@ -164,13 +166,13 @@ std::string OpenSSLKeyPair::PublicKeyToPEMString() const { BIO* temp_memory_bio = BIO_new(BIO_s_mem()); if (!temp_memory_bio) { RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) { RTC_LOG_F(LS_ERROR) << "Failed to write public key"; BIO_free(temp_memory_bio); - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return ""; } BIO_write(temp_memory_bio, "\0", 1); diff --git a/rtc_base/openssl_key_pair.h b/rtc_base/openssl_key_pair.h index a84c43b6bd..d09bdb0d5e 100644 --- a/rtc_base/openssl_key_pair.h +++ b/rtc_base/openssl_key_pair.h @@ -16,8 +16,8 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ssl_identity.h" namespace rtc { @@ -35,10 +35,13 @@ class OpenSSLKeyPair final { // Constructs a key pair from the private key PEM string. This must not result // in missing public key parameters. Returns null on error. static std::unique_ptr FromPrivateKeyPEMString( - const std::string& pem_string); + absl::string_view pem_string); ~OpenSSLKeyPair(); + OpenSSLKeyPair(const OpenSSLKeyPair&) = delete; + OpenSSLKeyPair& operator=(const OpenSSLKeyPair&) = delete; + std::unique_ptr Clone(); EVP_PKEY* pkey() const { return pkey_; } @@ -51,8 +54,6 @@ class OpenSSLKeyPair final { void AddReference(); EVP_PKEY* pkey_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair); }; } // namespace rtc diff --git a/rtc_base/openssl_session_cache.cc b/rtc_base/openssl_session_cache.cc index f8fcd473dc..d63724242a 100644 --- a/rtc_base/openssl_session_cache.cc +++ b/rtc_base/openssl_session_cache.cc @@ -10,6 +10,7 @@ #include "rtc_base/openssl_session_cache.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/openssl.h" @@ -30,16 +31,16 @@ OpenSSLSessionCache::~OpenSSLSessionCache() { } SSL_SESSION* OpenSSLSessionCache::LookupSession( - const std::string& hostname) const { + absl::string_view hostname) const { auto it = sessions_.find(hostname); return (it != sessions_.end()) ? it->second : nullptr; } -void OpenSSLSessionCache::AddSession(const std::string& hostname, +void OpenSSLSessionCache::AddSession(absl::string_view hostname, SSL_SESSION* new_session) { SSL_SESSION* old_session = LookupSession(hostname); SSL_SESSION_free(old_session); - sessions_[hostname] = new_session; + sessions_.insert_or_assign(std::string(hostname), new_session); } SSL_CTX* OpenSSLSessionCache::GetSSLContext() const { diff --git a/rtc_base/openssl_session_cache.h b/rtc_base/openssl_session_cache.h index b049e64dd6..75d8d9a0cf 100644 --- a/rtc_base/openssl_session_cache.h +++ b/rtc_base/openssl_session_cache.h @@ -16,8 +16,9 @@ #include #include -#include "rtc_base/constructor_magic.h" +#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; @@ -36,11 +37,15 @@ class OpenSSLSessionCache final { 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(const std::string& hostname) const; + SSL_SESSION* LookupSession(absl::string_view hostname) const; // Adds a session to the cache, and up_refs it. Any existing session with the // same hostname is replaced. - void AddSession(const std::string& hostname, SSL_SESSION* session); + void AddSession(absl::string_view hostname, SSL_SESSION* session); // Returns the true underlying SSL Context that holds these cached sessions. SSL_CTX* GetSSLContext() const; // The SSL Mode tht the OpenSSLSessionCache was constructed with. This cannot @@ -58,9 +63,8 @@ class OpenSSLSessionCache final { // Map of hostnames to SSL_SESSIONs; holds references to the SSL_SESSIONs, // which are cleaned up when the factory is destroyed. // TODO(juberti): Add LRU eviction to keep the cache from growing forever. - std::map sessions_; + std::map sessions_; // The cache should never be copied or assigned directly. - RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLSessionCache); }; } // namespace rtc diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc index 95408f5945..90870cd9cc 100644 --- a/rtc_base/openssl_stream_adapter.cc +++ b/rtc_base/openssl_stream_adapter.cc @@ -16,6 +16,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #ifndef OPENSSL_IS_BORINGSSL #include #include @@ -327,7 +329,7 @@ void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { } bool OpenSSLStreamAdapter::SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error) { @@ -353,7 +355,7 @@ bool OpenSSLStreamAdapter::SetPeerCertificateDigest( } peer_certificate_digest_value_.SetData(digest_val, digest_len); - peer_certificate_digest_algorithm_ = digest_alg; + peer_certificate_digest_algorithm_ = std::string(digest_alg); if (!peer_cert_chain_) { // Normal case, where the digest is set before we obtain the certificate @@ -445,15 +447,15 @@ bool OpenSSLStreamAdapter::GetSslVersionBytes(int* version) const { } // Key Extractor interface -bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, +bool OpenSSLStreamAdapter::ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, uint8_t* result, size_t result_len) { - if (SSL_export_keying_material(ssl_, result, result_len, label.c_str(), - label.length(), const_cast(context), - context_len, use_context) != 1) { + if (SSL_export_keying_material(ssl_, result, result_len, label.data(), + label.length(), context, context_len, + use_context) != 1) { return false; } return true; @@ -842,7 +844,7 @@ void OpenSSLStreamAdapter::SetTimeout(int delay_ms) { } ContinueSSL(); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } // This callback will never run again (stopped above). return webrtc::TimeDelta::PlusInfinity(); @@ -1263,7 +1265,7 @@ bool OpenSSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) { return false; } -bool OpenSSLStreamAdapter::IsAcceptableCipher(const std::string& cipher, +bool OpenSSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher, KeyType key_type) { if (key_type == KT_RSA) { for (const cipher_list& c : OK_RSA_ciphers) { diff --git a/rtc_base/openssl_stream_adapter.h b/rtc_base/openssl_stream_adapter.h index 236bdfdfea..de20ba93ce 100644 --- a/rtc_base/openssl_stream_adapter.h +++ b/rtc_base/openssl_stream_adapter.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "rtc_base/buffer.h" #ifdef OPENSSL_IS_BORINGSSL @@ -80,7 +81,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { // Default argument is for compatibility void SetServerRole(SSLRole role = SSL_SERVER) override; bool SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error = nullptr) override; @@ -113,7 +114,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { SSLProtocolVersion GetSslVersion() const override; bool GetSslVersionBytes(int* version) const override; // Key Extractor interface - bool ExportKeyingMaterial(const std::string& label, + bool ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -130,7 +131,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { static bool IsBoringSsl(); static bool IsAcceptableCipher(int cipher, KeyType key_type); - static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); // Use our timeutils.h source of timing in BoringSSL, allowing us to test // using a fake clock. diff --git a/rtc_base/openssl_utility.cc b/rtc_base/openssl_utility.cc index b5d649ca51..eba3788a94 100644 --- a/rtc_base/openssl_utility.cc +++ b/rtc_base/openssl_utility.cc @@ -9,6 +9,8 @@ */ #include "rtc_base/openssl_utility.h" + +#include "absl/strings/string_view.h" #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -184,7 +186,7 @@ bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, } #endif // OPENSSL_IS_BORINGSSL -bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host) { if (host.empty()) { RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate."; return false; @@ -211,8 +213,7 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { return false; } LogCertificates(ssl, x509.get()); - return X509_check_host(x509.get(), host.c_str(), host.size(), 0, nullptr) == - 1; + return X509_check_host(x509.get(), host.data(), host.size(), 0, nullptr) == 1; #else // OPENSSL_IS_BORINGSSL X509* certificate = SSL_get_peer_certificate(ssl); if (certificate == nullptr) { @@ -224,13 +225,13 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { LogCertificates(ssl, certificate); bool is_valid_cert_name = - X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1; + X509_check_host(certificate, host.data(), host.size(), 0, nullptr) == 1; X509_free(certificate); return is_valid_cert_name; #endif // !defined(OPENSSL_IS_BORINGSSL) } -void LogSSLErrors(const std::string& prefix) { +void LogSSLErrors(absl::string_view prefix) { char error_buf[200]; unsigned long err; // NOLINT diff --git a/rtc_base/openssl_utility.h b/rtc_base/openssl_utility.h index ee29ccd602..dd183c283a 100644 --- a/rtc_base/openssl_utility.h +++ b/rtc_base/openssl_utility.h @@ -15,6 +15,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // The openssl namespace holds static helper methods. All methods related // to OpenSSL that are commonly used and don't require global state should be @@ -35,11 +37,11 @@ bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, // TODO(crbug.com/webrtc/11710): When OS certificate verification is available, // skip compiling this as it adds a dependency on OpenSSL X509 objects, which we // are trying to avoid in favor of CRYPTO_BUFFERs (see crbug.com/webrtc/11410). -bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host); +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host); // Logs all the errors in the OpenSSL errror queue from the current thread. A // prefix can be provided for context. -void LogSSLErrors(const std::string& prefix); +void LogSSLErrors(absl::string_view prefix); #ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS // Attempt to add the certificates from the loader into the SSL_CTX. False is diff --git a/rtc_base/operations_chain.cc b/rtc_base/operations_chain.cc index 59d30d350e..f42482b3fe 100644 --- a/rtc_base/operations_chain.cc +++ b/rtc_base/operations_chain.cc @@ -37,10 +37,11 @@ void OperationsChain::CallbackHandle::OnOperationComplete() { // static scoped_refptr OperationsChain::Create() { - return new OperationsChain(); + // Explicit new, to access private constructor. + return rtc::scoped_refptr(new OperationsChain()); } -OperationsChain::OperationsChain() : RefCountedObject() { +OperationsChain::OperationsChain() { RTC_DCHECK_RUN_ON(&sequence_checker_); } @@ -63,8 +64,10 @@ bool OperationsChain::IsEmpty() const { } std::function OperationsChain::CreateOperationsChainCallback() { - return [handle = rtc::scoped_refptr( - new CallbackHandle(this))]() { handle->OnOperationComplete(); }; + return [handle = rtc::make_ref_counted( + rtc::scoped_refptr(this))]() { + handle->OnOperationComplete(); + }; } void OperationsChain::OnOperationComplete() { diff --git a/rtc_base/operations_chain.h b/rtc_base/operations_chain.h index 7823f6e238..0e8c0681ba 100644 --- a/rtc_base/operations_chain.h +++ b/rtc_base/operations_chain.h @@ -19,10 +19,10 @@ #include #include "absl/types/optional.h" +#include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_count.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/system/no_unique_address.h" @@ -113,11 +113,14 @@ class OperationWithFunctor final : public Operation { // 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 RefCountedObject { +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; @@ -163,11 +166,14 @@ class OperationsChain final : public RefCountedObject { // 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 RefCountedObject { + 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: @@ -175,8 +181,6 @@ class OperationsChain final : public RefCountedObject { #if RTC_DCHECK_IS_ON bool has_run_ = false; #endif // RTC_DCHECK_IS_ON - - RTC_DISALLOW_COPY_AND_ASSIGN(CallbackHandle); }; OperationsChain(); @@ -192,8 +196,6 @@ class OperationsChain final : public RefCountedObject { chained_operations_ RTC_GUARDED_BY(sequence_checker_); absl::optional> on_chain_empty_callback_ RTC_GUARDED_BY(sequence_checker_); - - RTC_DISALLOW_COPY_AND_ASSIGN(OperationsChain); }; } // namespace rtc diff --git a/rtc_base/operations_chain_unittest.cc b/rtc_base/operations_chain_unittest.cc index f99c05e0db..b598cb9353 100644 --- a/rtc_base/operations_chain_unittest.cc +++ b/rtc_base/operations_chain_unittest.cc @@ -87,17 +87,15 @@ class OperationTracker { Event* operation_complete_event, std::function callback) { Thread* current_thread = Thread::Current(); - background_thread_->PostTask( - RTC_FROM_HERE, [this, current_thread, unblock_operation_event, - operation_complete_event, callback]() { - unblock_operation_event->Wait(Event::kForever); - current_thread->PostTask( - RTC_FROM_HERE, [this, operation_complete_event, callback]() { - completed_operation_events_.push_back(operation_complete_event); - operation_complete_event->Set(); - callback(); - }); - }); + background_thread_->PostTask([this, current_thread, unblock_operation_event, + operation_complete_event, callback]() { + unblock_operation_event->Wait(Event::kForever); + current_thread->PostTask([this, operation_complete_event, callback]() { + completed_operation_events_.push_back(operation_complete_event); + operation_complete_event->Set(); + callback(); + }); + }); } std::unique_ptr background_thread_; @@ -118,19 +116,17 @@ class OperationTrackerProxy { std::unique_ptr Initialize() { std::unique_ptr event = std::make_unique(); - operations_chain_thread_->PostTask( - RTC_FROM_HERE, [this, event_ptr = event.get()]() { - operation_tracker_ = std::make_unique(); - operations_chain_ = OperationsChain::Create(); - event_ptr->Set(); - }); + operations_chain_thread_->PostTask([this, event_ptr = event.get()]() { + operation_tracker_ = std::make_unique(); + operations_chain_ = OperationsChain::Create(); + event_ptr->Set(); + }); return event; } void SetOnChainEmptyCallback(std::function on_chain_empty_callback) { Event event; operations_chain_thread_->PostTask( - RTC_FROM_HERE, [this, &event, on_chain_empty_callback = std::move(on_chain_empty_callback)]() { operations_chain_->SetOnChainEmptyCallback( @@ -143,22 +139,20 @@ class OperationTrackerProxy { bool IsEmpty() { Event event; bool is_empty = false; - operations_chain_thread_->PostTask( - RTC_FROM_HERE, [this, &event, &is_empty]() { - is_empty = operations_chain_->IsEmpty(); - event.Set(); - }); + operations_chain_thread_->PostTask([this, &event, &is_empty]() { + is_empty = operations_chain_->IsEmpty(); + event.Set(); + }); event.Wait(Event::kForever); return is_empty; } std::unique_ptr ReleaseOperationChain() { std::unique_ptr event = std::make_unique(); - operations_chain_thread_->PostTask(RTC_FROM_HERE, - [this, event_ptr = event.get()]() { - operations_chain_ = nullptr; - event_ptr->Set(); - }); + operations_chain_thread_->PostTask([this, event_ptr = event.get()]() { + operations_chain_ = nullptr; + event_ptr->Set(); + }); return event; } @@ -166,8 +160,8 @@ class OperationTrackerProxy { std::unique_ptr PostSynchronousOperation() { std::unique_ptr operation_complete_event = std::make_unique(); operations_chain_thread_->PostTask( - RTC_FROM_HERE, [this, operation_complete_event_ptr = - operation_complete_event.get()]() { + [this, + operation_complete_event_ptr = operation_complete_event.get()]() { operations_chain_->ChainOperation( operation_tracker_->BindSynchronousOperation( operation_complete_event_ptr)); @@ -181,7 +175,6 @@ class OperationTrackerProxy { Event* unblock_operation_event) { std::unique_ptr operation_complete_event = std::make_unique(); operations_chain_thread_->PostTask( - RTC_FROM_HERE, [this, unblock_operation_event, operation_complete_event_ptr = operation_complete_event.get()]() { operations_chain_->ChainOperation( @@ -219,6 +212,9 @@ class SignalOnDestruction final { } } + SignalOnDestruction(const SignalOnDestruction&) = delete; + SignalOnDestruction& operator=(const SignalOnDestruction&) = delete; + // Move operators. SignalOnDestruction(SignalOnDestruction&& other) : SignalOnDestruction(other.destructor_called_) { @@ -232,8 +228,6 @@ class SignalOnDestruction final { private: bool* destructor_called_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SignalOnDestruction); }; TEST(OperationsChainTest, SynchronousOperation) { diff --git a/rtc_base/physical_socket_server.cc b/rtc_base/physical_socket_server.cc index 7e62d6a4c8..954659319f 100644 --- a/rtc_base/physical_socket_server.cc +++ b/rtc_base/physical_socket_server.cc @@ -601,7 +601,7 @@ int PhysicalSocket::TranslateOption(Option opt, int* slevel, int* sopt) { case OPT_RTP_SENDTIME_EXTN_ID: return -1; // No logging is necessary as this not a OS socket option. default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return -1; } return 0; @@ -719,7 +719,11 @@ bool SocketDispatcher::IsDescriptorClosed() { // from readability. So test on each readable call. Is this // inefficient? Probably. char ch; - ssize_t res = ::recv(s_, &ch, 1, MSG_PEEK); + ssize_t res; + // Retry if the system call was interrupted. + do { + res = ::recv(s_, &ch, 1, MSG_PEEK); + } while (res < 0 && errno == EINTR); if (res > 0) { // Data available, so not closed. return false; @@ -730,13 +734,20 @@ bool SocketDispatcher::IsDescriptorClosed() { switch (errno) { // Returned if we've already closed s_. case EBADF: + // This is dangerous: if we keep attempting to access a FD after close, + // it could be reopened by something else making us think it's still + // open. Note that this is only a DCHECK. + RTC_DCHECK_NOTREACHED(); + return true; // Returned during ungraceful peer shutdown. case ECONNRESET: return true; + case ECONNABORTED: + return true; + case EPIPE: + return true; // The normal blocking error; don't log anything. case EWOULDBLOCK: - // Interrupted system call. - case EINTR: return false; default: // Assume that all other errors are just blocking errors, meaning the @@ -928,7 +939,7 @@ class Signaler : public Dispatcher { std::array afd = {-1, -1}; if (pipe(afd.data()) < 0) { - RTC_LOG(LERROR) << "pipe failed"; + RTC_LOG(LS_ERROR) << "pipe failed"; } return afd; }()), @@ -1172,16 +1183,29 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { return WaitSelect(cmsWait, process_io); } +// `error_event` is true if we are responding to an event where we know an +// error has occurred, which is possible with the poll/epoll implementations +// but not the select implementation. +// +// `check_error` is true if there is the possibility of an error. static void ProcessEvents(Dispatcher* dispatcher, bool readable, bool writable, + bool error_event, bool check_error) { + RTC_DCHECK(!(error_event && !check_error)); int errcode = 0; - // TODO(pthatcher): Should we set errcode if getsockopt fails? if (check_error) { socklen_t len = sizeof(errcode); - ::getsockopt(dispatcher->GetDescriptor(), SOL_SOCKET, SO_ERROR, &errcode, - &len); + int res = ::getsockopt(dispatcher->GetDescriptor(), SOL_SOCKET, SO_ERROR, + &errcode, &len); + if (res < 0) { + // If we are sure an error has occurred, or if getsockopt failed for a + // socket descriptor, make sure we set the error code to a nonzero value. + if (error_event || errno != ENOTSOCK) { + errcode = EBADF; + } + } } // Most often the socket is writable or readable or both, so make a single @@ -1194,10 +1218,10 @@ static void ProcessEvents(Dispatcher* dispatcher, // readable or really closed. // TODO(pthatcher): Only peek at TCP descriptors. if (readable) { - if (requested_events & DE_ACCEPT) { - ff |= DE_ACCEPT; - } else if (errcode || dispatcher->IsDescriptorClosed()) { + if (errcode || dispatcher->IsDescriptorClosed()) { ff |= DE_CLOSE; + } else if (requested_events & DE_ACCEPT) { + ff |= DE_ACCEPT; } else { ff |= DE_READ; } @@ -1209,14 +1233,17 @@ static void ProcessEvents(Dispatcher* dispatcher, if (requested_events & DE_CONNECT) { if (!errcode) { ff |= DE_CONNECT; - } else { - ff |= DE_CLOSE; } } else { ff |= DE_WRITE; } } + // Make sure we report any errors regardless of whether readable or writable. + if (errcode) { + ff |= DE_CLOSE; + } + // Tell the descriptor about the event. if (ff != 0) { dispatcher->OnEvent(ff, errcode); @@ -1327,7 +1354,8 @@ bool PhysicalSocketServer::WaitSelect(int cmsWait, bool process_io) { } // The error code can be signaled through reads or writes. - ProcessEvents(pdispatcher, readable, writable, readable || writable); + ProcessEvents(pdispatcher, readable, writable, /*error_event=*/false, + readable || writable); } } @@ -1359,6 +1387,11 @@ void PhysicalSocketServer::AddEpoll(Dispatcher* pdispatcher, uint64_t key) { struct epoll_event event = {0}; event.events = GetEpollEvents(pdispatcher->GetRequestedEvents()); + if (event.events == 0u) { + // Don't add at all if we don't have any requested events. Could indicate a + // closed socket. + return; + } event.data.u64 = key; int err = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event); RTC_DCHECK_EQ(err, 0); @@ -1378,13 +1411,10 @@ void PhysicalSocketServer::RemoveEpoll(Dispatcher* pdispatcher) { struct epoll_event event = {0}; int err = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &event); RTC_DCHECK(err == 0 || errno == ENOENT); - if (err == -1) { - if (errno == ENOENT) { - // Socket has already been closed. - RTC_LOG_E(LS_VERBOSE, EN, errno) << "epoll_ctl EPOLL_CTL_DEL"; - } else { - RTC_LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_DEL"; - } + // Ignore ENOENT, which could occur if this descriptor wasn't added due to + // having no requested events. + if (err == -1 && errno != ENOENT) { + RTC_LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_DEL"; } } @@ -1399,10 +1429,24 @@ void PhysicalSocketServer::UpdateEpoll(Dispatcher* pdispatcher, uint64_t key) { struct epoll_event event = {0}; event.events = GetEpollEvents(pdispatcher->GetRequestedEvents()); event.data.u64 = key; - int err = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &event); - RTC_DCHECK_EQ(err, 0); - if (err == -1) { - RTC_LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_MOD"; + // Remove if we don't have any requested events. Could indicate a closed + // socket. + if (event.events == 0u) { + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &event); + } else { + int err = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &event); + RTC_DCHECK(err == 0 || errno == ENOENT); + if (err == -1) { + // Could have been removed earlier due to no requested events. + if (errno == ENOENT) { + err = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event); + if (err == -1) { + RTC_LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_ADD"; + } + } else { + RTC_LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_MOD"; + } + } } } @@ -1449,9 +1493,9 @@ bool PhysicalSocketServer::WaitEpoll(int cmsWait) { bool readable = (event.events & (EPOLLIN | EPOLLPRI)); bool writable = (event.events & EPOLLOUT); - bool check_error = (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)); + bool error = (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)); - ProcessEvents(pdispatcher, readable, writable, check_error); + ProcessEvents(pdispatcher, readable, writable, error, error); } } @@ -1517,9 +1561,9 @@ bool PhysicalSocketServer::WaitPoll(int cmsWait, Dispatcher* dispatcher) { bool readable = (fds.revents & (POLLIN | POLLPRI)); bool writable = (fds.revents & POLLOUT); - bool check_error = (fds.revents & (POLLRDHUP | POLLERR | POLLHUP)); + bool error = (fds.revents & (POLLRDHUP | POLLERR | POLLHUP)); - ProcessEvents(dispatcher, readable, writable, check_error); + ProcessEvents(dispatcher, readable, writable, error, error); } if (cmsWait != kForever) { @@ -1605,7 +1649,7 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { // Failed? // TODO(pthatcher): need a better strategy than this! WSAGetLastError(); - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return false; } else if (dw == WSA_WAIT_TIMEOUT) { // Timeout? @@ -1642,13 +1686,13 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { { if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; } if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; } @@ -1660,13 +1704,13 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { } if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; } if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { - RTC_LOG(WARNING) + RTC_LOG(LS_WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; } diff --git a/rtc_base/proxy_server.h b/rtc_base/proxy_server.h index 6db0e12897..0b9b655a5e 100644 --- a/rtc_base/proxy_server.h +++ b/rtc_base/proxy_server.h @@ -15,7 +15,6 @@ #include #include "absl/memory/memory.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/memory/fifo_buffer.h" #include "rtc_base/server_socket_adapters.h" #include "rtc_base/socket.h" @@ -36,6 +35,10 @@ 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: @@ -59,7 +62,6 @@ class ProxyBinding : public sigslot::has_slots<> { bool connected_; FifoBuffer out_buffer_; FifoBuffer in_buffer_; - RTC_DISALLOW_COPY_AND_ASSIGN(ProxyBinding); }; class ProxyServer : public sigslot::has_slots<> { @@ -70,6 +72,9 @@ class ProxyServer : public sigslot::has_slots<> { 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(); @@ -82,7 +87,6 @@ class ProxyServer : public sigslot::has_slots<> { SocketAddress ext_ip_; std::unique_ptr server_socket_; std::vector> bindings_; - RTC_DISALLOW_COPY_AND_ASSIGN(ProxyServer); }; // SocksProxyServer is a simple extension of ProxyServer to implement SOCKS. @@ -94,9 +98,11 @@ class SocksProxyServer : public ProxyServer { const SocketAddress& ext_ip) : ProxyServer(int_factory, int_addr, ext_factory, ext_ip) {} + SocksProxyServer(const SocksProxyServer&) = delete; + SocksProxyServer& operator=(const SocksProxyServer&) = delete; + protected: AsyncProxyServerSocket* WrapSocket(Socket* socket) override; - RTC_DISALLOW_COPY_AND_ASSIGN(SocksProxyServer); }; } // namespace rtc diff --git a/rtc_base/rate_statistics.cc b/rtc_base/rate_statistics.cc index 85621fa555..5c83796471 100644 --- a/rtc_base/rate_statistics.cc +++ b/rtc_base/rate_statistics.cc @@ -58,7 +58,7 @@ void RateStatistics::Update(int64_t count, int64_t now_ms) { RTC_DCHECK_GE(count, 0); EraseOld(now_ms); - if (first_timestamp_ == -1) { + if (first_timestamp_ == -1 || num_samples_ == 0) { first_timestamp_ = now_ms; } diff --git a/rtc_base/rate_statistics_unittest.cc b/rtc_base/rate_statistics_unittest.cc index 735677082b..8f1a8384cf 100644 --- a/rtc_base/rate_statistics_unittest.cc +++ b/rtc_base/rate_statistics_unittest.cc @@ -148,14 +148,15 @@ TEST_F(RateStatisticsTest, ResetAfterSilence) { now_ms += kWindowMs + 1; EXPECT_FALSE(static_cast(stats_.Rate(now_ms))); + // Silence over window size should trigger auto reset for coming sample. stats_.Update(1000, now_ms); ++now_ms; stats_.Update(1000, now_ms); // We expect two samples of 1000 bytes, and that the bitrate is measured over - // 500 ms, i.e. 2 * 8 * 1000 / 0.500 = 32000. - EXPECT_EQ(32000u, *stats_.Rate(now_ms)); + // active window instead of full window, which is now_ms - first_timestamp + 1 + EXPECT_EQ(kExpectedBitrate, *stats_.Rate(now_ms)); - // Reset, add the same samples again. + // Manual reset, add the same samples again. stats_.Reset(); EXPECT_FALSE(static_cast(stats_.Rate(now_ms))); stats_.Update(1000, now_ms); @@ -272,9 +273,16 @@ TEST_F(RateStatisticsTest, HandlesQuietPeriods) { EXPECT_FALSE(static_cast(stats_.Rate(now_ms))); // Move window a long way out. + // This will cause an automatic reset of the window + // First data point won't give a valid result now_ms += 2 * kWindowMs; stats_.Update(0, now_ms); bitrate = stats_.Rate(now_ms); + EXPECT_FALSE(static_cast(stats_.Rate(now_ms))); + // Second data point gives valid result + ++now_ms; + stats_.Update(0, now_ms); + bitrate = stats_.Rate(now_ms); EXPECT_TRUE(static_cast(bitrate)); EXPECT_EQ(0u, *bitrate); } diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h index 2a55d863c1..54aac952ce 100644 --- a/rtc_base/ref_counted_object.h +++ b/rtc_base/ref_counted_object.h @@ -14,17 +14,36 @@ #include #include "api/scoped_refptr.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/ref_count.h" #include "rtc_base/ref_counter.h" namespace rtc { +namespace webrtc_make_ref_counted_internal { +// Determines if the given class has AddRef and Release methods. +template +class HasAddRefAndRelease { + private: + template ().AddRef())* = nullptr, + decltype(std::declval().Release())* = nullptr> + static int Test(int); + template + static char Test(...); + + public: + static constexpr bool value = std::is_same_v(0)), int>; +}; +} // namespace webrtc_make_ref_counted_internal + 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)) {} @@ -56,18 +75,15 @@ class RefCountedObject : public T { ~RefCountedObject() override {} mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; - - RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject); }; template class FinalRefCountedObject final : public T { public: using T::T; - // Until c++17 compilers are allowed not to inherit the default constructors. - // Thus the default constructors are forwarded explicitly. - FinalRefCountedObject() = default; - explicit FinalRefCountedObject(const T& other) : T(other) {} + // 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; @@ -106,8 +122,13 @@ class FinalRefCountedObject final : public T { // // auto p = scoped_refptr(new RefCountedObject("bar", 123)); // -// If the class does not inherit from RefCountInterface, the example is -// equivalent to: +// If the class does not inherit from RefCountInterface, but does have +// AddRef/Release methods (so a T* is convertible to rtc::scoped_refptr), this +// is equivalent to just +// +// auto p = scoped_refptr(new Foo("bar", 123)); +// +// Otherwise, the example is equivalent to: // // auto p = scoped_refptr>( // new FinalRefCountedObject("bar", 123)); @@ -122,24 +143,40 @@ class FinalRefCountedObject final : public T { // needed. // `make_ref_counted` for classes that are convertible to RefCountInterface. -template < - typename T, - typename... Args, - typename std::enable_if::value, - T>::type* = nullptr> +template , + T>::type* = nullptr> scoped_refptr make_ref_counted(Args&&... args) { - return new RefCountedObject(std::forward(args)...); + return scoped_refptr(new RefCountedObject(std::forward(args)...)); } // `make_ref_counted` for complete classes that are not convertible to -// RefCountInterface. +// RefCountInterface and already carry a ref count. template < typename T, typename... Args, - typename std::enable_if::value, - T>::type* = nullptr> + typename std::enable_if< + !std::is_convertible_v && + webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + T>::type* = nullptr> +scoped_refptr make_ref_counted(Args&&... args) { + return scoped_refptr(new T(std::forward(args)...)); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface and have no ref count of their own. +template < + typename T, + typename... Args, + typename std::enable_if< + !std::is_convertible_v && + !webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + + T>::type* = nullptr> scoped_refptr> make_ref_counted(Args&&... args) { - return new FinalRefCountedObject(std::forward(args)...); + return scoped_refptr>( + new FinalRefCountedObject(std::forward(args)...)); } // `Ref<>`, `Ref<>::Type` and `Ref<>::Ptr`: @@ -188,7 +225,7 @@ scoped_refptr> make_ref_counted(Args&&... args) { template struct Ref { typedef typename std::conditional< - std::is_convertible::value, + webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, T, FinalRefCountedObject>::type Type; diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc index ab7bb09191..ab051d4f8a 100644 --- a/rtc_base/ref_counted_object_unittest.cc +++ b/rtc_base/ref_counted_object_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/scoped_refptr.h" #include "rtc_base/ref_count.h" #include "test/gtest.h" @@ -27,8 +28,8 @@ class A { public: A() {} - private: - RTC_DISALLOW_COPY_AND_ASSIGN(A); + A(const A&) = delete; + A& operator=(const A&) = delete; }; class RefClass : public RefCountInterface { @@ -52,7 +53,7 @@ class RefClassWithRvalue : public RefCountInterface { class RefClassWithMixedValues : public RefCountInterface { public: - RefClassWithMixedValues(std::unique_ptr a, int b, const std::string& c) + RefClassWithMixedValues(std::unique_ptr a, int b, absl::string_view c) : a_(std::move(a)), b_(b), c_(c) {} protected: diff --git a/rtc_base/rolling_accumulator.h b/rtc_base/rolling_accumulator.h index 241bd72a11..84d791edd1 100644 --- a/rtc_base/rolling_accumulator.h +++ b/rtc_base/rolling_accumulator.h @@ -17,7 +17,6 @@ #include #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/numerics/running_statistics.h" namespace rtc { @@ -35,6 +34,9 @@ class RollingAccumulator { } ~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()); } @@ -136,8 +138,6 @@ class RollingAccumulator { mutable T min_; mutable bool min_stale_; std::vector samples_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RollingAccumulator); }; } // namespace rtc diff --git a/rtc_base/rtc_certificate.cc b/rtc_base/rtc_certificate.cc index 591cf8b30d..ef38e9a801 100644 --- a/rtc_base/rtc_certificate.cc +++ b/rtc_base/rtc_certificate.cc @@ -24,7 +24,7 @@ namespace rtc { scoped_refptr RTCCertificate::Create( std::unique_ptr identity) { // RingRTC change to make it easier to deal with RTCCertificate ref counts - return new RefCountedObject(identity.release()); + return rtc::make_ref_counted(identity.release()); } RTCCertificate::RTCCertificate(SSLIdentity* identity) : identity_(identity) { @@ -66,7 +66,7 @@ scoped_refptr RTCCertificate::FromPEM( if (!identity) return nullptr; // RingRTC change to make it easier to deal with RTCCertificate ref counts - return new RefCountedObject(identity.release()); + return rtc::make_ref_counted(identity.release()); } bool RTCCertificate::operator==(const RTCCertificate& certificate) const { diff --git a/rtc_base/rtc_certificate.h b/rtc_base/rtc_certificate.h index 9b60c59d63..88971483eb 100644 --- a/rtc_base/rtc_certificate.h +++ b/rtc_base/rtc_certificate.h @@ -17,6 +17,7 @@ #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "rtc_base/system/rtc_export.h" @@ -35,8 +36,8 @@ class SSLIdentity; // the string representations used by OpenSSL. class RTCCertificatePEM { public: - RTCCertificatePEM(const std::string& private_key, - const std::string& certificate) + RTCCertificatePEM(absl::string_view private_key, + absl::string_view certificate) : private_key_(private_key), certificate_(certificate) {} const std::string& private_key() const { return private_key_; } diff --git a/rtc_base/rtc_certificate_generator.cc b/rtc_base/rtc_certificate_generator.cc index 16ff23c740..09cd279aa4 100644 --- a/rtc_base/rtc_certificate_generator.cc +++ b/rtc_base/rtc_certificate_generator.cc @@ -79,13 +79,13 @@ void RTCCertificateGenerator::GenerateCertificateAsync( // Create a new `RTCCertificateGenerationTask` for this generation request. It // is reference counted and referenced by the message data, ensuring it lives // until the task has completed (independent of `RTCCertificateGenerator`). - worker_thread_->PostTask(RTC_FROM_HERE, [key_params, expires_ms, - signaling_thread = signaling_thread_, - cb = callback]() { + worker_thread_->PostTask([key_params, expires_ms, + signaling_thread = signaling_thread_, + cb = callback]() { scoped_refptr certificate = RTCCertificateGenerator::GenerateCertificate(key_params, expires_ms); signaling_thread->PostTask( - RTC_FROM_HERE, [cert = std::move(certificate), cb = std::move(cb)]() { + [cert = std::move(certificate), cb = std::move(cb)]() { cert ? cb->OnSuccess(cert) : cb->OnFailure(); }); }); diff --git a/rtc_base/rtc_certificate_generator_unittest.cc b/rtc_base/rtc_certificate_generator_unittest.cc index bef85f25e6..076f643091 100644 --- a/rtc_base/rtc_certificate_generator_unittest.cc +++ b/rtc_base/rtc_certificate_generator_unittest.cc @@ -70,7 +70,7 @@ class RTCCertificateGeneratorFixture : public RTCCertificateGeneratorCallback { class RTCCertificateGeneratorTest : public ::testing::Test { public: RTCCertificateGeneratorTest() - : fixture_(new RefCountedObject()) {} + : fixture_(make_ref_counted()) {} protected: static constexpr int kGenerationTimeoutMs = 10000; diff --git a/rtc_base/server_socket_adapters.h b/rtc_base/server_socket_adapters.h index 07e9636756..b18c7a6a65 100644 --- a/rtc_base/server_socket_adapters.h +++ b/rtc_base/server_socket_adapters.h @@ -31,9 +31,11 @@ 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; - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSSLServerSocket); }; // Implements a proxy server socket for the SOCKS protocol. @@ -41,6 +43,10 @@ class AsyncSocksProxyServerSocket : public AsyncProxyServerSocket { public: explicit AsyncSocksProxyServerSocket(Socket* socket); + AsyncSocksProxyServerSocket(const AsyncSocksProxyServerSocket&) = delete; + AsyncSocksProxyServerSocket& operator=(const AsyncSocksProxyServerSocket&) = + delete; + private: void ProcessInput(char* data, size_t* len) override; void DirectSend(const ByteBufferWriter& buf); @@ -64,7 +70,6 @@ class AsyncSocksProxyServerSocket : public AsyncProxyServerSocket { SS_ERROR }; State state_; - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSocksProxyServerSocket); }; } // namespace rtc diff --git a/rtc_base/sigslot_tester.h b/rtc_base/sigslot_tester.h index 58be511ef6..92483c0b8d 100644 --- a/rtc_base/sigslot_tester.h +++ b/rtc_base/sigslot_tester.h @@ -38,7 +38,6 @@ // EXPECT_EQ("hello", capture); // /* See unit-tests for more examples */ -#include "rtc_base/constructor_magic.h" #include "rtc_base/third_party/sigslot/sigslot.h" namespace rtc { @@ -50,13 +49,14 @@ class SigslotTester0 : public sigslot::has_slots<> { signal->connect(this, &SigslotTester0::OnSignalCallback); } + SigslotTester0(const SigslotTester0&) = delete; + SigslotTester0& operator=(const SigslotTester0&) = delete; + int callback_count() const { return callback_count_; } private: void OnSignalCallback() { callback_count_++; } int callback_count_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester0); }; // Versions below are for testing signals that pass arguments. For all the @@ -74,6 +74,9 @@ class SigslotTester1 : public sigslot::has_slots<> { signal->connect(this, &SigslotTester1::OnSignalCallback); } + SigslotTester1(const SigslotTester1&) = delete; + SigslotTester1& operator=(const SigslotTester1&) = delete; + int callback_count() const { return callback_count_; } private: @@ -84,8 +87,6 @@ class SigslotTester1 : public sigslot::has_slots<> { int callback_count_; C1* capture1_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester1); }; template @@ -96,6 +97,9 @@ class SigslotTester2 : public sigslot::has_slots<> { signal->connect(this, &SigslotTester2::OnSignalCallback); } + SigslotTester2(const SigslotTester2&) = delete; + SigslotTester2& operator=(const SigslotTester2&) = delete; + int callback_count() const { return callback_count_; } private: @@ -108,8 +112,6 @@ class SigslotTester2 : public sigslot::has_slots<> { int callback_count_; C1* capture1_; C2* capture2_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester2); }; template @@ -126,6 +128,9 @@ class SigslotTester3 : public sigslot::has_slots<> { signal->connect(this, &SigslotTester3::OnSignalCallback); } + SigslotTester3(const SigslotTester3&) = delete; + SigslotTester3& operator=(const SigslotTester3&) = delete; + int callback_count() const { return callback_count_; } private: @@ -140,8 +145,6 @@ class SigslotTester3 : public sigslot::has_slots<> { C1* capture1_; C2* capture2_; C3* capture3_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester3); }; template { signal->connect(this, &SigslotTester4::OnSignalCallback); } + SigslotTester4(const SigslotTester4&) = delete; + SigslotTester4& operator=(const SigslotTester4&) = delete; + int callback_count() const { return callback_count_; } private: @@ -183,8 +189,6 @@ class SigslotTester4 : public sigslot::has_slots<> { C2* capture2_; C3* capture3_; C4* capture4_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester4); }; template { signal->connect(this, &SigslotTester5::OnSignalCallback); } + SigslotTester5(const SigslotTester5&) = delete; + SigslotTester5& operator=(const SigslotTester5&) = delete; + int callback_count() const { return callback_count_; } private: @@ -232,8 +239,6 @@ class SigslotTester5 : public sigslot::has_slots<> { C3* capture3_; C4* capture4_; C5* capture5_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester5); }; } // namespace rtc diff --git a/rtc_base/sigslottester.h.pump b/rtc_base/sigslottester.h.pump index 0a1f41128d..c3d2d6e99b 100755 --- a/rtc_base/sigslottester.h.pump +++ b/rtc_base/sigslottester.h.pump @@ -35,7 +35,6 @@ // EXPECT_EQ("hello", capture); // /* See unit-tests for more examples */ -#include "rtc_base/constructor_magic.h" #include "rtc_base/third_party/sigslot/sigslot.h" namespace rtc { @@ -47,13 +46,14 @@ class SigslotTester0 : public sigslot::has_slots<> { signal->connect(this, &SigslotTester0::OnSignalCallback); } + SigslotTester0(const SigslotTester0&) = delete; + SigslotTester0& operator=(const SigslotTester0&) = delete; + int callback_count() const { return callback_count_; } private: void OnSignalCallback() { callback_count_++; } int callback_count_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester0); }; // Versions below are for testing signals that pass arguments. For all the @@ -78,6 +78,9 @@ class SigslotTester$i : public sigslot::has_slots<> { signal->connect(this, &SigslotTester$i::OnSignalCallback); } + SigslotTester$i(const SigslotTester$i&) = delete; + SigslotTester$i& operator=(const SigslotTester$i&) = delete; + int callback_count() const { return callback_count_; } private: @@ -91,9 +94,6 @@ class SigslotTester$i : public sigslot::has_slots<> { int callback_count_;$for j [[ C$j* capture$j[[]]_;]] - - - RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester$i); }; ]] diff --git a/rtc_base/socket.h b/rtc_base/socket.h index 6482117637..0ed3a7fa6a 100644 --- a/rtc_base/socket.h +++ b/rtc_base/socket.h @@ -25,7 +25,6 @@ #include "rtc_base/win32.h" #endif -#include "rtc_base/constructor_magic.h" #include "rtc_base/socket_address.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -83,6 +82,9 @@ class Socket { public: 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; @@ -138,9 +140,6 @@ class Socket { protected: Socket() {} - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(Socket); }; } // namespace rtc diff --git a/rtc_base/socket_adapters.cc b/rtc_base/socket_adapters.cc index ee8ec877cd..bc7d2d85a9 100644 --- a/rtc_base/socket_adapters.cc +++ b/rtc_base/socket_adapters.cc @@ -12,15 +12,17 @@ #pragma warning(disable : 4786) #endif +#include "rtc_base/socket_adapters.h" + #include #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/http_common.h" #include "rtc_base/logging.h" -#include "rtc_base/socket_adapters.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/zero_memory.h" @@ -97,7 +99,7 @@ void BufferedReadAdapter::OnReadEvent(Socket* socket) { if (data_len_ >= buffer_size_) { RTC_LOG(LS_ERROR) << "Input buffer overflow"; - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); data_len_ = 0; } @@ -105,7 +107,7 @@ void BufferedReadAdapter::OnReadEvent(Socket* socket) { buffer_size_ - data_len_, nullptr); if (len < 0) { // TODO: Do something better like forwarding the error to the user. - RTC_LOG_ERR(INFO) << "Recv"; + RTC_LOG_ERR(LS_INFO) << "Recv"; return; } @@ -217,9 +219,9 @@ void AsyncSSLSocket::ProcessInput(char* data, size_t* len) { /////////////////////////////////////////////////////////////////////////////// AsyncHttpsProxySocket::AsyncHttpsProxySocket(Socket* socket, - const std::string& user_agent, + absl::string_view user_agent, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password) : BufferedReadAdapter(socket, 1024), proxy_(proxy), @@ -308,12 +310,12 @@ void AsyncHttpsProxySocket::ProcessInput(char* data, size_t* len) { if (data[pos++] != '\n') continue; - size_t len = pos - start - 1; - if ((len > 0) && (data[start + len - 1] == '\r')) - --len; + size_t length = pos - start - 1; + if ((length > 0) && (data[start + length - 1] == '\r')) + --length; - data[start + len] = 0; - ProcessLine(data + start, len); + data[start + length] = 0; + ProcessLine(data + start, length); start = pos; } @@ -470,7 +472,7 @@ void AsyncHttpsProxySocket::Error(int error) { AsyncSocksProxySocket::AsyncSocksProxySocket(Socket* socket, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password) : BufferedReadAdapter(socket, 1024), state_(SS_ERROR), @@ -566,9 +568,9 @@ void AsyncSocksProxySocket::ProcessInput(char* data, size_t* len) { return; RTC_LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; } else if (atyp == 3) { - uint8_t len; + uint8_t length; std::string addr; - if (!response.ReadUInt8(&len) || !response.ReadString(&addr, len) || + if (!response.ReadUInt8(&length) || !response.ReadString(&addr, length) || !response.ReadUInt16(&port)) return; RTC_LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; diff --git a/rtc_base/socket_adapters.h b/rtc_base/socket_adapters.h index 67d3bbff7d..e78ee18a27 100644 --- a/rtc_base/socket_adapters.h +++ b/rtc_base/socket_adapters.h @@ -13,9 +13,9 @@ #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "rtc_base/async_socket.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/crypt_string.h" namespace rtc { @@ -34,6 +34,9 @@ class BufferedReadAdapter : public AsyncSocketAdapter { 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; @@ -51,7 +54,6 @@ class BufferedReadAdapter : public AsyncSocketAdapter { char* buffer_; size_t buffer_size_, data_len_; bool buffering_; - RTC_DISALLOW_COPY_AND_ASSIGN(BufferedReadAdapter); }; /////////////////////////////////////////////////////////////////////////////// @@ -65,12 +67,14 @@ class AsyncSSLSocket : public BufferedReadAdapter { 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; - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSSLSocket); }; /////////////////////////////////////////////////////////////////////////////// @@ -79,12 +83,15 @@ class AsyncSSLSocket : public BufferedReadAdapter { class AsyncHttpsProxySocket : public BufferedReadAdapter { public: AsyncHttpsProxySocket(Socket* socket, - const std::string& user_agent, + absl::string_view user_agent, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password); ~AsyncHttpsProxySocket() override; + AsyncHttpsProxySocket(const AsyncHttpsProxySocket&) = delete; + AsyncHttpsProxySocket& operator=(const AsyncHttpsProxySocket&) = delete; + // If connect is forced, the adapter will always issue an HTTP CONNECT to the // target address. Otherwise, it will connect only if the destination port // is not port 80. @@ -128,7 +135,6 @@ class AsyncHttpsProxySocket : public BufferedReadAdapter { } state_; HttpAuthContext* context_; std::string unknown_mechanisms_; - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncHttpsProxySocket); }; /////////////////////////////////////////////////////////////////////////////// @@ -138,10 +144,13 @@ class AsyncSocksProxySocket : public BufferedReadAdapter { public: AsyncSocksProxySocket(Socket* socket, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password); ~AsyncSocksProxySocket() override; + AsyncSocksProxySocket(const AsyncSocksProxySocket&) = delete; + AsyncSocksProxySocket& operator=(const AsyncSocksProxySocket&) = delete; + int Connect(const SocketAddress& addr) override; SocketAddress GetRemoteAddress() const override; int Close() override; @@ -162,7 +171,6 @@ class AsyncSocksProxySocket : public BufferedReadAdapter { SocketAddress proxy_, dest_; std::string user_; CryptString pass_; - RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSocksProxySocket); }; } // namespace rtc diff --git a/rtc_base/socket_address.cc b/rtc_base/socket_address.cc index 2996ede9d2..93d6860a70 100644 --- a/rtc_base/socket_address.cc +++ b/rtc_base/socket_address.cc @@ -10,6 +10,7 @@ #include "rtc_base/socket_address.h" +#include "absl/strings/string_view.h" #include "rtc_base/numerics/safe_conversions.h" #if defined(WEBRTC_POSIX) @@ -43,7 +44,7 @@ SocketAddress::SocketAddress() { Clear(); } -SocketAddress::SocketAddress(const std::string& hostname, int port) { +SocketAddress::SocketAddress(absl::string_view hostname, int port) { SetIP(hostname); SetPort(port); } @@ -101,8 +102,8 @@ void SocketAddress::SetIP(const IPAddress& ip) { scope_id_ = 0; } -void SocketAddress::SetIP(const std::string& hostname) { - hostname_ = hostname; +void SocketAddress::SetIP(absl::string_view hostname) { + hostname_ = std::string(hostname); literal_ = IPFromString(hostname, &ip_); if (!literal_) { ip_ = IPAddress(); @@ -188,23 +189,24 @@ std::string SocketAddress::ToResolvedSensitiveString() const { return sb.str(); } -bool SocketAddress::FromString(const std::string& str) { +bool SocketAddress::FromString(absl::string_view str) { if (str.at(0) == '[') { - std::string::size_type closebracket = str.rfind(']'); - if (closebracket != std::string::npos) { - std::string::size_type colon = str.find(':', closebracket); - if (colon != std::string::npos && colon > closebracket) { - SetPort(strtoul(str.substr(colon + 1).c_str(), nullptr, 10)); + absl::string_view::size_type closebracket = str.rfind(']'); + if (closebracket != absl::string_view::npos) { + absl::string_view::size_type colon = str.find(':', closebracket); + if (colon != absl::string_view::npos && colon > closebracket) { + SetPort( + strtoul(std::string(str.substr(colon + 1)).c_str(), nullptr, 10)); SetIP(str.substr(1, closebracket - 1)); } else { return false; } } } else { - std::string::size_type pos = str.find(':'); - if (std::string::npos == pos) + absl::string_view::size_type pos = str.find(':'); + if (absl::string_view::npos == pos) return false; - SetPort(strtoul(str.substr(pos + 1).c_str(), nullptr, 10)); + SetPort(strtoul(std::string(str.substr(pos + 1)).c_str(), nullptr, 10)); SetIP(str.substr(0, pos)); } return true; diff --git a/rtc_base/socket_address.h b/rtc_base/socket_address.h index 570a71281e..99e14d8eab 100644 --- a/rtc_base/socket_address.h +++ b/rtc_base/socket_address.h @@ -12,6 +12,8 @@ #define RTC_BASE_SOCKET_ADDRESS_H_ #include + +#include "absl/strings/string_view.h" #ifdef WEBRTC_UNIT_TEST #include // no-presubmit-check TODO(webrtc:8982) #endif // WEBRTC_UNIT_TEST @@ -34,7 +36,7 @@ class RTC_EXPORT SocketAddress { // Creates the address with the given host and port. Host may be a // literal IP string or a hostname to be resolved later. // DCHECKs that port is in valid range (0 to 2^16-1). - SocketAddress(const std::string& hostname, int port); + SocketAddress(absl::string_view hostname, int port); // Creates the address with the given IP and port. // IP is given as an integer in host byte order. V4 only, to be deprecated. @@ -69,7 +71,7 @@ class RTC_EXPORT SocketAddress { // Changes the hostname of this address to the given one. // Does not resolve the address; use Resolve to do so. - void SetIP(const std::string& hostname); + void SetIP(absl::string_view hostname); // Sets the IP address while retaining the hostname. Useful for bypassing // DNS for a pre-resolved IP. @@ -129,7 +131,7 @@ class RTC_EXPORT SocketAddress { std::string ToResolvedSensitiveString() const; // Parses hostname:port and [hostname]:port. - bool FromString(const std::string& str); + bool FromString(absl::string_view str); #ifdef WEBRTC_UNIT_TEST inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) diff --git a/rtc_base/socket_stream.h b/rtc_base/socket_stream.h index 266a6e6fe6..f678f805d7 100644 --- a/rtc_base/socket_stream.h +++ b/rtc_base/socket_stream.h @@ -13,7 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/socket.h" #include "rtc_base/stream.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -27,6 +26,9 @@ class SocketStream : public StreamInterface, public sigslot::has_slots<> { explicit SocketStream(Socket* socket); ~SocketStream() override; + SocketStream(const SocketStream&) = delete; + SocketStream& operator=(const SocketStream&) = delete; + void Attach(Socket* socket); Socket* Detach(); @@ -53,8 +55,6 @@ class SocketStream : public StreamInterface, public sigslot::has_slots<> { void OnCloseEvent(Socket* socket, int err); Socket* socket_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SocketStream); }; /////////////////////////////////////////////////////////////////////////////// diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc index 01a2bed26d..0b9c2f2c58 100644 --- a/rtc_base/socket_unittest.cc +++ b/rtc_base/socket_unittest.cc @@ -17,6 +17,7 @@ #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_udp_socket.h" @@ -287,7 +288,7 @@ void SocketTest::ConnectInternal(const IPAddress& loopback) { } void SocketTest::ConnectWithDnsLookupInternal(const IPAddress& loopback, - const std::string& host) { + absl::string_view host) { StreamSink sink; SocketAddress accept_addr; diff --git a/rtc_base/socket_unittest.h b/rtc_base/socket_unittest.h index 772df635d7..20ef003a80 100644 --- a/rtc_base/socket_unittest.h +++ b/rtc_base/socket_unittest.h @@ -11,6 +11,7 @@ #ifndef RTC_BASE_SOCKET_UNITTEST_H_ #define RTC_BASE_SOCKET_UNITTEST_H_ +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "rtc_base/thread.h" @@ -74,7 +75,7 @@ class SocketTest : public ::testing::Test { private: void ConnectInternal(const IPAddress& loopback); void ConnectWithDnsLookupInternal(const IPAddress& loopback, - const std::string& host); + absl::string_view host); void ConnectFailInternal(const IPAddress& loopback); void ConnectWithDnsLookupFailInternal(const IPAddress& loopback); diff --git a/rtc_base/ssl_adapter.cc b/rtc_base/ssl_adapter.cc index c9b54c4dc9..ff936a79fb 100644 --- a/rtc_base/ssl_adapter.cc +++ b/rtc_base/ssl_adapter.cc @@ -16,8 +16,8 @@ namespace rtc { -SSLAdapterFactory* SSLAdapterFactory::Create() { - return new OpenSSLAdapterFactory(); +std::unique_ptr SSLAdapterFactory::Create() { + return std::make_unique(); } SSLAdapter* SSLAdapter::Create(Socket* socket) { diff --git a/rtc_base/ssl_adapter.h b/rtc_base/ssl_adapter.h index 1f0616bffc..8f98141651 100644 --- a/rtc_base/ssl_adapter.h +++ b/rtc_base/ssl_adapter.h @@ -39,10 +39,21 @@ class SSLAdapterFactory { // 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 SSLAdapterFactory* Create(); + static std::unique_ptr Create(); }; // Class that abstracts a client-to-server SSL session. It can be created @@ -91,6 +102,11 @@ class SSLAdapter : public AsyncSocketAdapter { // 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); } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/rtc_base/ssl_adapter_unittest.cc b/rtc_base/ssl_adapter_unittest.cc index 2b55211086..430343ecb8 100644 --- a/rtc_base/ssl_adapter_unittest.cc +++ b/rtc_base/ssl_adapter_unittest.cc @@ -8,16 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_adapter.h" + #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/message_digest.h" #include "rtc_base/socket_stream.h" -#include "rtc_base/ssl_adapter.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" @@ -99,7 +101,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { const std::string& GetReceivedData() const { return data_; } - int Connect(const std::string& hostname, const rtc::SocketAddress& address) { + int Connect(absl::string_view hostname, const rtc::SocketAddress& address) { RTC_LOG(LS_INFO) << "Initiating connection with " << address.ToString(); int rv = ssl_adapter_->Connect(address); @@ -108,7 +110,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { RTC_LOG(LS_INFO) << "Starting " << GetSSLProtocolName(ssl_mode_) << " handshake with " << hostname; - if (ssl_adapter_->StartSSL(hostname.c_str()) != 0) { + if (ssl_adapter_->StartSSL(std::string(hostname).c_str()) != 0) { return -1; } } @@ -118,7 +120,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { int Close() { return ssl_adapter_->Close(); } - int Send(const std::string& message) { + int Send(absl::string_view message) { RTC_LOG(LS_INFO) << "Client sending '" << message << "'"; return ssl_adapter_->Send(message.data(), message.length()); @@ -189,7 +191,7 @@ class SSLAdapterTestDummyServer : public sigslot::has_slots<> { const std::string& GetReceivedData() const { return data_; } - int Send(const std::string& message) { + int Send(absl::string_view message) { if (ssl_stream_adapter_ == nullptr || ssl_stream_adapter_->GetState() != rtc::SS_OPEN) { // No connection yet. @@ -363,7 +365,7 @@ class SSLAdapterTestBase : public ::testing::Test, public sigslot::has_slots<> { } } - void TestTransfer(const std::string& message) { + void TestTransfer(absl::string_view message) { int rv; rv = client_->Send(message); diff --git a/rtc_base/ssl_certificate.cc b/rtc_base/ssl_certificate.cc index ed42998353..ddb1524f76 100644 --- a/rtc_base/ssl_certificate.cc +++ b/rtc_base/ssl_certificate.cc @@ -15,6 +15,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/openssl.h" #ifdef OPENSSL_IS_BORINGSSL @@ -121,7 +122,7 @@ std::unique_ptr SSLCertChain::GetStats() const { // static std::unique_ptr SSLCertificate::FromPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLCertificate::FromPEMString(pem_string); #else diff --git a/rtc_base/ssl_certificate.h b/rtc_base/ssl_certificate.h index 3b3f24fb91..77fbba3e9e 100644 --- a/rtc_base/ssl_certificate.h +++ b/rtc_base/ssl_certificate.h @@ -17,12 +17,13 @@ #include #include + #include #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -55,7 +56,7 @@ class RTC_EXPORT SSLCertificate { // stored in *pem_length if it is non-null, and only if // parsing was successful. static std::unique_ptr FromPEMString( - const std::string& pem_string); + absl::string_view pem_string); virtual ~SSLCertificate() = default; // Returns a new SSLCertificate object instance wrapping the same @@ -73,7 +74,7 @@ class RTC_EXPORT SSLCertificate { virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0; // Compute the digest of the certificate given algorithm - virtual bool ComputeDigest(const std::string& algorithm, + virtual bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const = 0; @@ -101,6 +102,9 @@ class RTC_EXPORT SSLCertChain final { ~SSLCertChain(); + SSLCertChain(const SSLCertChain&) = delete; + SSLCertChain& operator=(const SSLCertChain&) = delete; + // Vector access methods. size_t GetSize() const { return certs_.size(); } @@ -118,8 +122,6 @@ class RTC_EXPORT SSLCertChain final { private: std::vector> certs_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SSLCertChain); }; // SSLCertificateVerifier provides a simple interface to allow third parties to diff --git a/rtc_base/ssl_fingerprint.cc b/rtc_base/ssl_fingerprint.cc index 358402eb03..a85b7a09ce 100644 --- a/rtc_base/ssl_fingerprint.cc +++ b/rtc_base/ssl_fingerprint.cc @@ -11,11 +11,13 @@ #include "rtc_base/ssl_fingerprint.h" #include + #include #include #include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/rtc_certificate.h" @@ -25,19 +27,19 @@ namespace rtc { -SSLFingerprint* SSLFingerprint::Create(const std::string& algorithm, +SSLFingerprint* SSLFingerprint::Create(absl::string_view algorithm, const rtc::SSLIdentity* identity) { return CreateUnique(algorithm, *identity).release(); } std::unique_ptr SSLFingerprint::CreateUnique( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLIdentity& identity) { return Create(algorithm, identity.certificate()); } std::unique_ptr SSLFingerprint::Create( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLCertificate& cert) { uint8_t digest_val[64]; size_t digest_len; @@ -51,14 +53,14 @@ std::unique_ptr SSLFingerprint::Create( } SSLFingerprint* SSLFingerprint::CreateFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint) { + absl::string_view algorithm, + absl::string_view fingerprint) { return CreateUniqueFromRfc4572(algorithm, fingerprint).release(); } std::unique_ptr SSLFingerprint::CreateUniqueFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint) { + absl::string_view algorithm, + absl::string_view fingerprint) { if (algorithm.empty() || !rtc::IsFips180DigestAlgorithm(algorithm)) return nullptr; @@ -67,7 +69,7 @@ std::unique_ptr SSLFingerprint::CreateUniqueFromRfc4572( char value[rtc::MessageDigest::kMaxSize]; size_t value_len = rtc::hex_decode_with_delimiter( - value, sizeof(value), fingerprint.c_str(), fingerprint.length(), ':'); + value, sizeof(value), fingerprint.data(), fingerprint.length(), ':'); if (!value_len) return nullptr; @@ -94,11 +96,11 @@ std::unique_ptr SSLFingerprint::CreateFromCertificate( return fingerprint; } -SSLFingerprint::SSLFingerprint(const std::string& algorithm, +SSLFingerprint::SSLFingerprint(absl::string_view algorithm, ArrayView digest_view) : algorithm(algorithm), digest(digest_view.data(), digest_view.size()) {} -SSLFingerprint::SSLFingerprint(const std::string& algorithm, +SSLFingerprint::SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, size_t digest_len) : SSLFingerprint(algorithm, MakeArrayView(digest_in, digest_len)) {} diff --git a/rtc_base/ssl_fingerprint.h b/rtc_base/ssl_fingerprint.h index add3ab7911..cfa26dd433 100644 --- a/rtc_base/ssl_fingerprint.h +++ b/rtc_base/ssl_fingerprint.h @@ -13,8 +13,10 @@ #include #include + #include +#include "absl/strings/string_view.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/system/rtc_export.h" @@ -26,34 +28,34 @@ class SSLIdentity; struct RTC_EXPORT SSLFingerprint { // TODO(steveanton): Remove once downstream projects have moved off of this. - static SSLFingerprint* Create(const std::string& algorithm, + static SSLFingerprint* Create(absl::string_view algorithm, const rtc::SSLIdentity* identity); // TODO(steveanton): Rename to Create once projects have migrated. static std::unique_ptr CreateUnique( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLIdentity& identity); static std::unique_ptr Create( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLCertificate& cert); // TODO(steveanton): Remove once downstream projects have moved off of this. - static SSLFingerprint* CreateFromRfc4572(const std::string& algorithm, - const std::string& fingerprint); + static SSLFingerprint* CreateFromRfc4572(absl::string_view algorithm, + absl::string_view fingerprint); // TODO(steveanton): Rename to CreateFromRfc4572 once projects have migrated. static std::unique_ptr CreateUniqueFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint); + absl::string_view algorithm, + absl::string_view fingerprint); // Creates a fingerprint from a certificate, using the same digest algorithm // as the certificate's signature. static std::unique_ptr CreateFromCertificate( const RTCCertificate& cert); - SSLFingerprint(const std::string& algorithm, + SSLFingerprint(absl::string_view algorithm, ArrayView digest_view); // TODO(steveanton): Remove once downstream projects have moved off of this. - SSLFingerprint(const std::string& algorithm, + SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, size_t digest_len); diff --git a/rtc_base/ssl_identity.cc b/rtc_base/ssl_identity.cc index c24f46eff1..984979a8a6 100644 --- a/rtc_base/ssl_identity.cc +++ b/rtc_base/ssl_identity.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #ifdef OPENSSL_IS_BORINGSSL #include "rtc_base/boringssl_identity.h" @@ -121,7 +122,7 @@ KeyParams::KeyParams(KeyType key_type) { params_.rsa.mod_size = kRsaDefaultModSize; params_.rsa.pub_exp = kRsaDefaultExponent; } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } @@ -169,30 +170,32 @@ KeyType IntKeyTypeFamilyToKeyType(int key_type_family) { // SSLIdentity ////////////////////////////////////////////////////////////////////// -bool SSLIdentity::PemToDer(const std::string& pem_type, - const std::string& pem_string, +bool SSLIdentity::PemToDer(absl::string_view pem_type, + absl::string_view pem_string, std::string* der) { // Find the inner body. We need this to fulfill the contract of returning // pem_length. - size_t header = pem_string.find("-----BEGIN " + pem_type + "-----"); - if (header == std::string::npos) { + std::string pem_type_str = std::string(pem_type); + size_t header = pem_string.find("-----BEGIN " + pem_type_str + "-----"); + if (header == absl::string_view::npos) { return false; } size_t body = pem_string.find('\n', header); - if (body == std::string::npos) { + if (body == absl::string_view::npos) { return false; } - size_t trailer = pem_string.find("-----END " + pem_type + "-----"); - if (trailer == std::string::npos) { + size_t trailer = pem_string.find("-----END " + pem_type_str + "-----"); + if (trailer == absl::string_view::npos) { return false; } - std::string inner = pem_string.substr(body + 1, trailer - (body + 1)); + std::string inner = + std::string(pem_string.substr(body + 1, trailer - (body + 1))); *der = Base64::Decode(inner, Base64::DO_PARSE_WHITE | Base64::DO_PAD_ANY | Base64::DO_TERM_BUFFER); return true; } -std::string SSLIdentity::DerToPem(const std::string& pem_type, +std::string SSLIdentity::DerToPem(absl::string_view pem_type, const unsigned char* data, size_t length) { rtc::StringBuilder result; @@ -214,7 +217,7 @@ std::string SSLIdentity::DerToPem(const std::string& pem_type, } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, const KeyParams& key_param, time_t certificate_lifetime) { #ifdef OPENSSL_IS_BORINGSSL @@ -227,13 +230,13 @@ std::unique_ptr SSLIdentity::Create(const std::string& common_name, } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, const KeyParams& key_param) { return Create(common_name, key_param, kDefaultCertificateLifetimeInSeconds); } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, KeyType key_type) { return Create(common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds); @@ -252,8 +255,8 @@ std::unique_ptr SSLIdentity::CreateForTest( // Construct an identity from a private key and a certificate. // static std::unique_ptr SSLIdentity::CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate) { + absl::string_view private_key, + absl::string_view certificate) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLIdentity::CreateFromPEMStrings(private_key, certificate); #else @@ -264,8 +267,8 @@ std::unique_ptr SSLIdentity::CreateFromPEMStrings( // Construct an identity from a private key and a certificate chain. // static std::unique_ptr SSLIdentity::CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain) { + absl::string_view private_key, + absl::string_view certificate_chain) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLIdentity::CreateFromPEMChainStrings(private_key, certificate_chain); diff --git a/rtc_base/ssl_identity.h b/rtc_base/ssl_identity.h index 78d1ec12b7..a0119bb1c4 100644 --- a/rtc_base/ssl_identity.h +++ b/rtc_base/ssl_identity.h @@ -14,10 +14,12 @@ #define RTC_BASE_SSL_IDENTITY_H_ #include + #include #include #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -108,12 +110,12 @@ class RTC_EXPORT SSLIdentity { // should be a non-negative number. // Returns null on failure. // Caller is responsible for freeing the returned object. - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, const KeyParams& key_param, time_t certificate_lifetime); - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, const KeyParams& key_param); - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, KeyType key_type); // Allows fine-grained control over expiration time. @@ -122,13 +124,13 @@ class RTC_EXPORT SSLIdentity { // Construct an identity from a private key and a certificate. static std::unique_ptr CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate); + absl::string_view private_key, + absl::string_view certificate); // Construct an identity from a private key and a certificate chain. static std::unique_ptr CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain); + absl::string_view private_key, + absl::string_view certificate_chain); virtual ~SSLIdentity() {} @@ -144,10 +146,10 @@ class RTC_EXPORT SSLIdentity { virtual std::string PublicKeyToPEMString() const = 0; // Helpers for parsing converting between PEM and DER format. - static bool PemToDer(const std::string& pem_type, - const std::string& pem_string, + static bool PemToDer(absl::string_view pem_type, + absl::string_view pem_string, std::string* der); - static std::string DerToPem(const std::string& pem_type, + static std::string DerToPem(absl::string_view pem_type, const unsigned char* data, size_t length); diff --git a/rtc_base/ssl_identity_unittest.cc b/rtc_base/ssl_identity_unittest.cc index 53f4a2ad10..1f0278ac71 100644 --- a/rtc_base/ssl_identity_unittest.cc +++ b/rtc_base/ssl_identity_unittest.cc @@ -8,19 +8,22 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_identity.h" + #include + #include #include #include #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/ssl_fingerprint.h" -#include "rtc_base/ssl_identity.h" #include "test/gtest.h" using rtc::SSLIdentity; @@ -236,7 +239,7 @@ class SSLIdentityTest : public ::testing::Test { void TestDigestHelper(DigestType digest, const SSLIdentity* identity, - const std::string& algorithm, + absl::string_view algorithm, size_t expected_len) { DigestType digest1; size_t digest_len; @@ -258,7 +261,7 @@ class SSLIdentityTest : public ::testing::Test { EXPECT_EQ(0, memcmp(digest, digest1, expected_len)); } - void TestDigestForGeneratedCert(const std::string& algorithm, + void TestDigestForGeneratedCert(absl::string_view algorithm, size_t expected_len) { DigestType digest[4]; @@ -281,7 +284,7 @@ class SSLIdentityTest : public ::testing::Test { } } - void TestDigestForFixedCert(const std::string& algorithm, + void TestDigestForFixedCert(absl::string_view algorithm, size_t expected_len, const unsigned char* expected_digest) { bool rv; diff --git a/rtc_base/ssl_stream_adapter.cc b/rtc_base/ssl_stream_adapter.cc index b805fdc6c3..4b60d6d7b1 100644 --- a/rtc_base/ssl_stream_adapter.cc +++ b/rtc_base/ssl_stream_adapter.cc @@ -11,6 +11,7 @@ #include "rtc_base/ssl_stream_adapter.h" #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/openssl_stream_adapter.h" /////////////////////////////////////////////////////////////////////////////// @@ -39,7 +40,7 @@ std::string SrtpCryptoSuiteToName(int crypto_suite) { } } -int SrtpCryptoSuiteFromName(const std::string& crypto_suite) { +int SrtpCryptoSuiteFromName(absl::string_view crypto_suite) { if (crypto_suite == kCsAesCm128HmacSha1_32) return kSrtpAes128CmSha1_32; if (crypto_suite == kCsAesCm128HmacSha1_80) @@ -85,7 +86,7 @@ bool IsGcmCryptoSuite(int crypto_suite) { crypto_suite == kSrtpAeadAes128Gcm); } -bool IsGcmCryptoSuiteName(const std::string& crypto_suite) { +bool IsGcmCryptoSuiteName(absl::string_view crypto_suite) { return (crypto_suite == kCsAeadAes256Gcm || crypto_suite == kCsAeadAes128Gcm); } @@ -98,7 +99,7 @@ bool SSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) { return false; } -bool SSLStreamAdapter::ExportKeyingMaterial(const std::string& label, +bool SSLStreamAdapter::ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -122,7 +123,7 @@ bool SSLStreamAdapter::IsBoringSsl() { bool SSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) { return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type); } -bool SSLStreamAdapter::IsAcceptableCipher(const std::string& cipher, +bool SSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher, KeyType key_type) { return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type); } diff --git a/rtc_base/ssl_stream_adapter.h b/rtc_base/ssl_stream_adapter.h index 618ffca4d0..e68870c747 100644 --- a/rtc_base/ssl_stream_adapter.h +++ b/rtc_base/ssl_stream_adapter.h @@ -13,11 +13,13 @@ #include #include + #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/stream.h" @@ -53,7 +55,7 @@ extern const char kCsAeadAes256Gcm[]; std::string SrtpCryptoSuiteToName(int crypto_suite); // The reverse of above conversion. -int SrtpCryptoSuiteFromName(const std::string& crypto_suite); +int SrtpCryptoSuiteFromName(absl::string_view crypto_suite); // Get key length and salt length for given crypto suite. Returns true for // valid suites, otherwise false. @@ -65,7 +67,7 @@ bool GetSrtpKeyAndSaltLengths(int crypto_suite, bool IsGcmCryptoSuite(int crypto_suite); // Returns true if the given crypto suite name uses a GCM cipher. -bool IsGcmCryptoSuiteName(const std::string& crypto_suite); +bool IsGcmCryptoSuiteName(absl::string_view crypto_suite); // SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS. // After SSL has been started, the stream will only open on successful @@ -176,7 +178,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // Returns true if successful. // `error` is optional and provides more information about the failure. virtual bool SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error = nullptr) = 0; @@ -208,7 +210,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // zero-length ones). // result -- where to put the computed value // result_len -- the length of the computed value - virtual bool ExportKeyingMaterial(const std::string& label, + virtual bool ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -233,7 +235,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // Returns true iff the supplied cipher is deemed to be strong. // TODO(torbjorng): Consider removing the KeyType argument. static bool IsAcceptableCipher(int cipher, KeyType key_type); - static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); // TODO(guoweis): Move this away from a static class method. Currently this is // introduced such that any caller could depend on sslstreamadapter.h without diff --git a/rtc_base/ssl_stream_adapter_unittest.cc b/rtc_base/ssl_stream_adapter_unittest.cc index f92958dd86..262eeefb79 100644 --- a/rtc_base/ssl_stream_adapter_unittest.cc +++ b/rtc_base/ssl_stream_adapter_unittest.cc @@ -8,12 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_stream_adapter.h" + #include #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/buffer_queue.h" #include "rtc_base/checks.h" #include "rtc_base/gunit.h" @@ -24,7 +27,6 @@ #include "rtc_base/openssl_stream_adapter.h" #include "rtc_base/ssl_adapter.h" #include "rtc_base/ssl_identity.h" -#include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" @@ -147,7 +149,7 @@ class SSLDummyStreamBase : public rtc::StreamInterface, public sigslot::has_slots<> { public: SSLDummyStreamBase(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, rtc::StreamInterface* in, rtc::StreamInterface* out) : test_base_(test), side_(side), in_(in), out_(out), first_packet_(true) { @@ -235,7 +237,7 @@ class SSLDummyStreamBase : public rtc::StreamInterface, class SSLDummyStreamTLS : public SSLDummyStreamBase { public: SSLDummyStreamTLS(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, rtc::FifoBuffer* in, rtc::FifoBuffer* out) : SSLDummyStreamBase(test, side, in, out) {} @@ -303,7 +305,7 @@ class BufferQueueStream : public rtc::StreamInterface { class SSLDummyStreamDTLS : public SSLDummyStreamBase { public: SSLDummyStreamDTLS(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, BufferQueueStream* in, BufferQueueStream* out) : SSLDummyStreamBase(test, side, in, out) {} @@ -317,8 +319,8 @@ class SSLStreamAdapterTestBase : public ::testing::Test, public sigslot::has_slots<> { public: SSLStreamAdapterTestBase( - const std::string& client_cert_pem, - const std::string& client_private_key_pem, + absl::string_view client_cert_pem, + absl::string_view client_private_key_pem, bool dtls, rtc::KeyParams client_key_type = rtc::KeyParams(rtc::KT_DEFAULT), rtc::KeyParams server_key_type = rtc::KeyParams(rtc::KT_DEFAULT)) @@ -864,8 +866,8 @@ class SSLStreamAdapterTestDTLSBase : public SSLStreamAdapterTestBase { count_(0), sent_(0) {} - SSLStreamAdapterTestDTLSBase(const std::string& cert_pem, - const std::string& private_key_pem) + SSLStreamAdapterTestDTLSBase(absl::string_view cert_pem, + absl::string_view private_key_pem) : SSLStreamAdapterTestBase(cert_pem, private_key_pem, true), client_buffer_(kBufferCapacity, kDefaultBufferSize), server_buffer_(kBufferCapacity, kDefaultBufferSize), @@ -983,8 +985,8 @@ class SSLStreamAdapterTestDTLS : SSLStreamAdapterTestDTLSBase(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam())) {} - SSLStreamAdapterTestDTLS(const std::string& cert_pem, - const std::string& private_key_pem) + SSLStreamAdapterTestDTLS(absl::string_view cert_pem, + absl::string_view private_key_pem) : SSLStreamAdapterTestDTLSBase(cert_pem, private_key_pem) {} }; @@ -1551,8 +1553,8 @@ class SSLStreamAdapterTestDTLSLegacyProtocols // initialized, so we set the experiment while creationg client_ssl_ // and server_ssl_. - void ConfigureClient(std::string experiment) { - webrtc::test::ScopedFieldTrials trial(experiment); + void ConfigureClient(absl::string_view experiment) { + webrtc::test::ScopedFieldTrials trial{std::string(experiment)}; client_stream_ = new SSLDummyStreamDTLS(this, "c2s", &client_buffer_, &server_buffer_); client_ssl_ = @@ -1564,8 +1566,8 @@ class SSLStreamAdapterTestDTLSLegacyProtocols client_ssl_->SetIdentity(std::move(client_identity)); } - void ConfigureServer(std::string experiment) { - webrtc::test::ScopedFieldTrials trial(experiment); + void ConfigureServer(absl::string_view experiment) { + webrtc::test::ScopedFieldTrials trial{std::string(experiment)}; server_stream_ = new SSLDummyStreamDTLS(this, "s2c", &server_buffer_, &client_buffer_); server_ssl_ = diff --git a/rtc_base/stream.h b/rtc_base/stream.h index 70de65a75d..c0ceb4e36d 100644 --- a/rtc_base/stream.h +++ b/rtc_base/stream.h @@ -14,7 +14,6 @@ #include #include "rtc_base/buffer.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/message_handler.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -52,6 +51,9 @@ 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 @@ -110,9 +112,6 @@ class RTC_EXPORT StreamInterface { protected: StreamInterface(); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(StreamInterface); }; } // namespace rtc diff --git a/rtc_base/string_encode.cc b/rtc_base/string_encode.cc index 3b41076fd8..fa99c7a439 100644 --- a/rtc_base/string_encode.cc +++ b/rtc_base/string_encode.cc @@ -12,6 +12,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" @@ -77,8 +78,8 @@ void hex_encode_with_delimiter(char* buffer, } // namespace -std::string hex_encode(const std::string& str) { - return hex_encode(str.c_str(), str.size()); +std::string hex_encode(absl::string_view str) { + return hex_encode(str.data(), str.size()); } std::string hex_encode(const char* source, size_t srclen) { @@ -141,18 +142,18 @@ size_t hex_decode_with_delimiter(char* cbuffer, return bufpos; } -size_t hex_decode(char* buffer, size_t buflen, const std::string& source) { +size_t hex_decode(char* buffer, size_t buflen, absl::string_view source) { return hex_decode_with_delimiter(buffer, buflen, source, 0); } size_t hex_decode_with_delimiter(char* buffer, size_t buflen, - const std::string& source, + absl::string_view source, char delimiter) { - return hex_decode_with_delimiter(buffer, buflen, source.c_str(), + return hex_decode_with_delimiter(buffer, buflen, source.data(), source.length(), delimiter); } -size_t tokenize(const std::string& source, +size_t tokenize(absl::string_view source, char delimiter, std::vector* fields) { fields->clear(); @@ -160,35 +161,35 @@ size_t tokenize(const std::string& source, for (size_t i = 0; i < source.length(); ++i) { if (source[i] == delimiter) { if (i != last) { - fields->push_back(source.substr(last, i - last)); + fields->emplace_back(source.substr(last, i - last)); } last = i + 1; } } if (last != source.length()) { - fields->push_back(source.substr(last, source.length() - last)); + fields->emplace_back(source.substr(last, source.length() - last)); } return fields->size(); } -bool tokenize_first(const std::string& source, +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 == std::string::npos) { + if (left_pos == absl::string_view::npos) { return false; } // Look for additional occurrances of delimiter. size_t right_pos = left_pos + 1; - while (source[right_pos] == delimiter) { + while (right_pos < source.size() && source[right_pos] == delimiter) { right_pos++; } - *token = source.substr(0, left_pos); - *rest = source.substr(right_pos); + *token = std::string(source.substr(0, left_pos)); + *rest = std::string(source.substr(right_pos)); return true; } @@ -214,19 +215,27 @@ std::string join(const std::vector& source, char delimiter) { return joined_string; } -size_t split(const std::string& source, +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; +} + +size_t split(absl::string_view source, char delimiter, std::vector* fields) { RTC_DCHECK(fields); fields->clear(); - 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; - } + for (const absl::string_view field_view : split(source, delimiter)) { + fields->emplace_back(field_view); } - fields->push_back(source.substr(last, source.length() - last)); return fields->size(); } @@ -237,8 +246,9 @@ std::string ToString(const bool b) { std::string ToString(const char* const s) { return std::string(s); } -std::string ToString(const std::string s) { - return s; + +std::string ToString(absl::string_view s) { + return std::string(s); } std::string ToString(const short s) { diff --git a/rtc_base/string_encode.h b/rtc_base/string_encode.h index 577c45f9f0..87c5bc8d44 100644 --- a/rtc_base/string_encode.h +++ b/rtc_base/string_encode.h @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "rtc_base/checks.h" #include "rtc_base/string_to_number.h" @@ -27,7 +28,7 @@ namespace rtc { // String Encoding Utilities ////////////////////////////////////////////////////////////////////// -std::string hex_encode(const std::string& str); +std::string hex_encode(absl::string_view str); std::string hex_encode(const char* source, size_t srclen); std::string hex_encode_with_delimiter(const char* source, size_t srclen, @@ -50,10 +51,10 @@ size_t hex_decode_with_delimiter(char* buffer, char delimiter); // Helper functions for hex_decode. -size_t hex_decode(char* buffer, size_t buflen, const std::string& source); +size_t hex_decode(char* buffer, size_t buflen, absl::string_view source); size_t hex_decode_with_delimiter(char* buffer, size_t buflen, - const std::string& source, + absl::string_view source, char delimiter); // Joins the source vector of strings into a single string, with each @@ -61,22 +62,25 @@ size_t hex_decode_with_delimiter(char* buffer, std::string join(const std::vector& source, char delimiter); // Splits the source string into multiple fields separated by delimiter, -// with duplicates of delimiter creating empty fields. -size_t split(const std::string& source, +// with duplicates of delimiter creating empty fields. Empty input produces a +// single, empty, field. +std::vector split(absl::string_view source, char delimiter); + +size_t split(absl::string_view source, char delimiter, std::vector* fields); // Splits the source string into multiple fields separated by delimiter, // with duplicates of delimiter ignored. Trailing delimiter ignored. -size_t tokenize(const std::string& source, +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(const std::string& source, - const char delimiter, +bool tokenize_first(absl::string_view source, + char delimiter, std::string* token, std::string* rest); @@ -85,7 +89,7 @@ bool tokenize_first(const std::string& source, std::string ToString(bool b); std::string ToString(const char* s); -std::string ToString(std::string t); +std::string ToString(absl::string_view s); std::string ToString(short s); std::string ToString(unsigned short s); diff --git a/rtc_base/string_utils.cc b/rtc_base/string_utils.cc index 1720c62d5e..e8c13464bd 100644 --- a/rtc_base/string_utils.cc +++ b/rtc_base/string_utils.cc @@ -10,6 +10,8 @@ #include "rtc_base/string_utils.h" +#include "absl/strings/string_view.h" + namespace rtc { size_t strcpyn(char* buffer, @@ -30,19 +32,6 @@ size_t strcpyn(char* buffer, return srclen; } -static const char kWhitespace[] = " \n\r\t"; - -std::string string_trim(const std::string& s) { - std::string::size_type first = s.find_first_not_of(kWhitespace); - std::string::size_type last = s.find_last_not_of(kWhitespace); - - if (first == std::string::npos || last == std::string::npos) { - return std::string(""); - } - - return s.substr(first, last - first + 1); -} - std::string ToHex(const int i) { char buffer[50]; snprintf(buffer, sizeof(buffer), "%x", i); diff --git a/rtc_base/string_utils.h b/rtc_base/string_utils.h index d844e5e125..a9cdd61a7b 100644 --- a/rtc_base/string_utils.h +++ b/rtc_base/string_utils.h @@ -16,6 +16,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #include @@ -30,10 +32,24 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { const size_t SIZE_UNKNOWN = static_cast(-1); +// An absl::string_view comparator functor for use with container types such as +// std::map that support heterogenous lookup. +// +// Example usage: +// std::map my_map; +struct AbslStringViewCmp { + using is_transparent = void; + bool operator()(absl::string_view a, absl::string_view b) const { + return a < b; + } +}; + // Safe version of strncpy that always nul-terminate. size_t strcpyn(char* buffer, size_t buflen, @@ -57,7 +73,7 @@ inline std::wstring ToUtf16(const char* utf8, size_t len) { return ws; } -inline std::wstring ToUtf16(const std::string& str) { +inline std::wstring ToUtf16(absl::string_view str) { return ToUtf16(str.data(), str.length()); } @@ -82,11 +98,8 @@ inline std::string ToUtf8(const std::wstring& wstr) { #endif // WEBRTC_WIN -// Remove leading and trailing whitespaces. -std::string string_trim(const std::string& s); - // TODO(jonasolsson): replace with absl::Hex when that becomes available. -std::string ToHex(const int i); +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 diff --git a/rtc_base/string_utils_unittest.cc b/rtc_base/string_utils_unittest.cc index 120f7e60f5..4e4bebdda7 100644 --- a/rtc_base/string_utils_unittest.cc +++ b/rtc_base/string_utils_unittest.cc @@ -14,14 +14,6 @@ namespace rtc { -TEST(string_trim_Test, Trimming) { - EXPECT_EQ("temp", string_trim("\n\r\t temp \n\r\t")); - EXPECT_EQ("temp\n\r\t temp", string_trim(" temp\n\r\t temp ")); - EXPECT_EQ("temp temp", string_trim("temp temp")); - EXPECT_EQ("", string_trim(" \r\n\t")); - EXPECT_EQ("", string_trim("")); -} - TEST(string_toHexTest, ToHex) { EXPECT_EQ(ToHex(0), "0"); EXPECT_EQ(ToHex(0X1243E), "1243e"); diff --git a/rtc_base/strings/json.cc b/rtc_base/strings/json.cc index 99664404cf..af8ba184e7 100644 --- a/rtc_base/strings/json.cc +++ b/rtc_base/strings/json.cc @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/string_encode.h" namespace rtc { @@ -240,46 +241,47 @@ bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, double* out) { } bool GetValueFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, Json::Value* out) { - if (!in.isObject() || !in.isMember(k)) { + std::string k_str = std::string(k); + if (!in.isObject() || !in.isMember(k_str)) { return false; } - *out = in[k]; + *out = in[k_str]; return true; } bool GetIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, int* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out); } bool GetUIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, unsigned int* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out); } bool GetStringFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, std::string* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out); } bool GetBoolFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, bool* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out); } bool GetDoubleFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, double* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out); diff --git a/rtc_base/strings/json.h b/rtc_base/strings/json.h index 0cb9542c7f..618cb71b04 100644 --- a/rtc_base/strings/json.h +++ b/rtc_base/strings/json.h @@ -14,6 +14,8 @@ #include #include +#include "absl/strings/string_view.h" + #if !defined(WEBRTC_EXTERNAL_JSON) #include "json/json.h" #else @@ -62,22 +64,20 @@ Json::Value DoubleVectorToJsonArray(const std::vector& in); // Pull values out of a JSON object. bool GetValueFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, Json::Value* out); -bool GetIntFromJsonObject(const Json::Value& in, - const std::string& k, - int* out); +bool GetIntFromJsonObject(const Json::Value& in, absl::string_view k, int* out); bool GetUIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, unsigned int* out); bool GetStringFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, std::string* out); bool GetBoolFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, bool* out); bool GetDoubleFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, double* out); // Writes out a Json value as a string. diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc index caa931b594..e3e25e631b 100644 --- a/rtc_base/strings/string_builder.cc +++ b/rtc_base/strings/string_builder.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_minmax.h" @@ -26,16 +27,12 @@ SimpleStringBuilder::SimpleStringBuilder(rtc::ArrayView buffer) RTC_DCHECK(IsConsistent()); } -SimpleStringBuilder& SimpleStringBuilder::operator<<(const char* str) { - return Append(str, strlen(str)); -} - SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { return Append(&ch, 1); } -SimpleStringBuilder& SimpleStringBuilder::operator<<(const std::string& str) { - return Append(str.c_str(), str.length()); +SimpleStringBuilder& SimpleStringBuilder::operator<<(absl::string_view str) { + return Append(str.data(), str.length()); } // Numeric conversion routines. @@ -98,7 +95,7 @@ SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { } else { // This should never happen, but we're paranoid, so re-write the // terminator in case vsnprintf() overwrote it. - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); buffer_[size_] = '\0'; } va_end(args); diff --git a/rtc_base/strings/string_builder.h b/rtc_base/strings/string_builder.h index 6fe478ce4c..b35b7f1867 100644 --- a/rtc_base/strings/string_builder.h +++ b/rtc_base/strings/string_builder.h @@ -32,9 +32,8 @@ class SimpleStringBuilder { SimpleStringBuilder(const SimpleStringBuilder&) = delete; SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; - SimpleStringBuilder& operator<<(const char* str); SimpleStringBuilder& operator<<(char ch); - SimpleStringBuilder& operator<<(const std::string& str); + SimpleStringBuilder& operator<<(absl::string_view str); SimpleStringBuilder& operator<<(int i); SimpleStringBuilder& operator<<(unsigned i); SimpleStringBuilder& operator<<(long i); // NOLINT diff --git a/net/dcsctp/public/strong_alias.h b/rtc_base/strong_alias.h similarity index 93% rename from net/dcsctp/public/strong_alias.h rename to rtc_base/strong_alias.h index fd6302d2ed..3f45113f63 100644 --- a/net/dcsctp/public/strong_alias.h +++ b/rtc_base/strong_alias.h @@ -8,13 +8,13 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#ifndef NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ -#define NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ +#ifndef RTC_BASE_STRONG_ALIAS_H_ +#define RTC_BASE_STRONG_ALIAS_H_ #include #include -namespace dcsctp { +namespace webrtc { // This is a copy of // https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h @@ -71,6 +71,6 @@ class StrongAlias { UnderlyingType value_; }; -} // namespace dcsctp +} // namespace webrtc -#endif // NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ +#endif // RTC_BASE_STRONG_ALIAS_H_ diff --git a/net/dcsctp/public/strong_alias_test.cc b/rtc_base/strong_alias_unittest.cc similarity index 99% rename from net/dcsctp/public/strong_alias_test.cc rename to rtc_base/strong_alias_unittest.cc index 63aa60995c..a87bc4de37 100644 --- a/net/dcsctp/public/strong_alias_test.cc +++ b/rtc_base/strong_alias_unittest.cc @@ -8,7 +8,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include "net/dcsctp/public/strong_alias.h" +#include "rtc_base/strong_alias.h" #include #include @@ -26,7 +26,7 @@ // but adapted to use WebRTC's includes, remove unit tests that test the ostream // operator (it's removed in this port) and other adaptations to pass lint. -namespace dcsctp { +namespace webrtc { namespace { // For test correctnenss, it's important that these getters return lexically @@ -359,4 +359,4 @@ TEST(StrongAliasTest, BooleansAreEvaluatedAsBooleans) { EXPECT_TRUE(*happy); EXPECT_FALSE(*sad); } -} // namespace dcsctp +} // namespace webrtc diff --git a/rtc_base/swap_queue_unittest.cc b/rtc_base/swap_queue_unittest.cc index 3862d850fa..764a25edec 100644 --- a/rtc_base/swap_queue_unittest.cc +++ b/rtc_base/swap_queue_unittest.cc @@ -144,8 +144,7 @@ TEST(SwapQueueDeathTest, UnsuccessfulItemVerifyFunctor) { int invalid_value = -4; EXPECT_TRUE(queue.Insert(&valid_value)); EXPECT_TRUE(queue.Remove(&valid_value)); - bool result; - EXPECT_DEATH(result = queue.Insert(&invalid_value), ""); + EXPECT_DEATH((void)queue.Insert(&invalid_value), ""); } TEST(SwapQueueDeathTest, UnSuccessfulItemVerifyInsert) { @@ -154,8 +153,7 @@ TEST(SwapQueueDeathTest, UnSuccessfulItemVerifyInsert) { SwapQueueItemVerifier, &LengthVerifierFunction>> queue(2, template_element); std::vector invalid_chunk(kChunkSize - 1, 0); - bool result; - EXPECT_DEATH(result = queue.Insert(&invalid_chunk), ""); + EXPECT_DEATH((void)queue.Insert(&invalid_chunk), ""); } TEST(SwapQueueDeathTest, UnSuccessfulItemVerifyRemove) { @@ -167,8 +165,7 @@ TEST(SwapQueueDeathTest, UnSuccessfulItemVerifyRemove) { std::vector valid_chunk(kChunkSize, 0); EXPECT_TRUE(queue.Insert(&valid_chunk)); EXPECT_EQ(valid_chunk.size(), kChunkSize); - bool result; - EXPECT_DEATH(result = queue.Remove(&invalid_chunk), ""); + EXPECT_DEATH((void)queue.Remove(&invalid_chunk), ""); } #endif diff --git a/rtc_base/synchronization/mutex_unittest.cc b/rtc_base/synchronization/mutex_unittest.cc index 3021c5932f..a07e8fa429 100644 --- a/rtc_base/synchronization/mutex_unittest.cc +++ b/rtc_base/synchronization/mutex_unittest.cc @@ -103,7 +103,7 @@ class LockRunner : public rtc::MessageHandlerAutoCleanup { locker_.Lock(); shared_value = shared_value_; locker_.Unlock(); - return shared_value_; + return shared_value; } void OnMessage(Message* msg) override { diff --git a/rtc_base/synchronization/sequence_checker_internal.cc b/rtc_base/synchronization/sequence_checker_internal.cc index 63badd9538..c03ee94d1b 100644 --- a/rtc_base/synchronization/sequence_checker_internal.cc +++ b/rtc_base/synchronization/sequence_checker_internal.cc @@ -51,7 +51,7 @@ bool SequenceCheckerImpl::IsCurrent() const { valid_system_queue_ = current_system_queue; return true; } - if (valid_queue_ || current_queue) { + if (valid_queue_) { return valid_queue_ == current_queue; } if (valid_system_queue_ && valid_system_queue_ == current_system_queue) { diff --git a/rtc_base/system/unused.h b/rtc_base/system/unused.h index a5732a7e84..03d0c2f0a7 100644 --- a/rtc_base/system/unused.h +++ b/rtc_base/system/unused.h @@ -18,7 +18,11 @@ // 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/rtc_base/system_time.cc b/rtc_base/system_time.cc index 9efe76e3a6..d53d923148 100644 --- a/rtc_base/system_time.cc +++ b/rtc_base/system_time.cc @@ -48,7 +48,7 @@ int64_t SystemTimeNanos() { // Get the timebase if this is the first time we run. // Recommended by Apple's QA1398. if (mach_timebase_info(&timebase) != KERN_SUCCESS) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } // Use timebase to convert absolute time tick units into nanoseconds. diff --git a/rtc_base/task_queue.cc b/rtc_base/task_queue.cc index 965a4d8c69..0b54178405 100644 --- a/rtc_base/task_queue.cc +++ b/rtc_base/task_queue.cc @@ -30,12 +30,25 @@ bool TaskQueue::IsCurrent() const { } void TaskQueue::PostTask(std::unique_ptr task) { - return impl_->PostTask(std::move(task)); + impl_->PostTask(std::move(task)); } void TaskQueue::PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) { - return impl_->PostDelayedTask(std::move(task), milliseconds); + impl_->PostDelayedTask(std::move(task), milliseconds); +} + +void TaskQueue::PostDelayedHighPrecisionTask( + std::unique_ptr task, + uint32_t milliseconds) { + impl_->PostDelayedHighPrecisionTask(std::move(task), milliseconds); +} + +void TaskQueue::PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds) { + impl_->PostDelayedTaskWithPrecision(precision, std::move(task), milliseconds); } } // namespace rtc diff --git a/rtc_base/task_queue.h b/rtc_base/task_queue.h index 86d35976cb..ef97646988 100644 --- a/rtc_base/task_queue.h +++ b/rtc_base/task_queue.h @@ -20,7 +20,6 @@ #include "api/task_queue/queued_task.h" #include "api/task_queue/task_queue_base.h" #include "api/task_queue/task_queue_factory.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread_annotations.h" @@ -83,6 +82,9 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueue { webrtc::TaskQueueDeleter> task_queue); ~TaskQueue(); + TaskQueue(const TaskQueue&) = delete; + TaskQueue& operator=(const TaskQueue&) = delete; + // Used for DCHECKing the current queue. bool IsCurrent() const; @@ -93,14 +95,15 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueue { // Ownership of the task is passed to PostTask. void PostTask(std::unique_ptr task); - - // Schedules a task to execute a specified number of milliseconds from when - // the call is made. The precision should be considered as "best effort" - // and in some cases, such as on Windows when all high precision timers have - // been used up, can be off by as much as 15 millseconds (although 8 would be - // more likely). This can be mitigated by limiting the use of delayed tasks. + // See webrtc::TaskQueueBase for precision expectations. void PostDelayedTask(std::unique_ptr task, uint32_t milliseconds); + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds); + void PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds); // std::enable_if is used here to make sure that calls to PostTask() with // std::unique_ptr would not end up being @@ -112,8 +115,6 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueue { void PostTask(Closure&& closure) { PostTask(webrtc::ToQueuedTask(std::forward(closure))); } - - // See documentation above for performance expectations. template (closure)), milliseconds); } + template >::value>::type* = nullptr> + void PostDelayedHighPrecisionTask(Closure&& closure, uint32_t milliseconds) { + PostDelayedHighPrecisionTask( + webrtc::ToQueuedTask(std::forward(closure)), milliseconds); + } + template >::value>::type* = nullptr> + void PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + Closure&& closure, + uint32_t milliseconds) { + PostDelayedTaskWithPrecision( + precision, webrtc::ToQueuedTask(std::forward(closure)), + milliseconds); + } private: webrtc::TaskQueueBase* const impl_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TaskQueue); }; } // namespace rtc diff --git a/rtc_base/task_queue_libevent.cc b/rtc_base/task_queue_libevent.cc index a8b0dbffef..4f56400741 100644 --- a/rtc_base/task_queue_libevent.cc +++ b/rtc_base/task_queue_libevent.cc @@ -288,7 +288,7 @@ void TaskQueueLibevent::OnWakeup(int socket, break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } diff --git a/rtc_base/task_queue_win.cc b/rtc_base/task_queue_win.cc index da0535d5c5..dd14a7d8e0 100644 --- a/rtc_base/task_queue_win.cc +++ b/rtc_base/task_queue_win.cc @@ -34,7 +34,6 @@ #include "api/task_queue/task_queue_base.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/event.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -122,6 +121,9 @@ class MultimediaTimer { ::CloseHandle(event_); } + MultimediaTimer(const MultimediaTimer&) = delete; + MultimediaTimer& operator=(const MultimediaTimer&) = delete; + bool StartOneShotTimer(UINT delay_ms) { RTC_DCHECK_EQ(0, timer_id_); RTC_DCHECK(event_ != nullptr); @@ -148,8 +150,6 @@ class MultimediaTimer { private: HANDLE event_ = nullptr; MMRESULT timer_id_ = 0; - - RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); }; class TaskQueueWin : public TaskQueueBase { @@ -335,7 +335,7 @@ bool TaskQueueWin::ProcessQueuedMessages() { break; } default: - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); break; } } else { diff --git a/rtc_base/task_utils/BUILD.gn b/rtc_base/task_utils/BUILD.gn index 64a041908e..8767e9ecdc 100644 --- a/rtc_base/task_utils/BUILD.gn +++ b/rtc_base/task_utils/BUILD.gn @@ -68,9 +68,13 @@ if (rtc_include_tests) { sources = [ "repeating_task_unittest.cc" ] deps = [ ":repeating_task", + ":to_queued_task", "..:rtc_base_approved", "..:rtc_task_queue", "..:task_queue_for_test", + "../../api/task_queue", + "../../api/units:timestamp", + "../../system_wrappers:system_wrappers", "../../test:test_support", ] } diff --git a/rtc_base/task_utils/pending_task_safety_flag.cc b/rtc_base/task_utils/pending_task_safety_flag.cc index 57b3f6ce88..8bff213a03 100644 --- a/rtc_base/task_utils/pending_task_safety_flag.cc +++ b/rtc_base/task_utils/pending_task_safety_flag.cc @@ -12,23 +12,29 @@ namespace webrtc { +// static +rtc::scoped_refptr PendingTaskSafetyFlag::CreateInternal( + bool alive) { + // Explicit new, to access private constructor. + return rtc::scoped_refptr( + new PendingTaskSafetyFlag(alive)); +} + // static rtc::scoped_refptr PendingTaskSafetyFlag::Create() { - return new PendingTaskSafetyFlag(true); + return CreateInternal(true); } rtc::scoped_refptr PendingTaskSafetyFlag::CreateDetached() { - rtc::scoped_refptr safety_flag( - new PendingTaskSafetyFlag(true)); + rtc::scoped_refptr safety_flag = CreateInternal(true); safety_flag->main_sequence_.Detach(); return safety_flag; } rtc::scoped_refptr PendingTaskSafetyFlag::CreateDetachedInactive() { - rtc::scoped_refptr safety_flag( - new PendingTaskSafetyFlag(false)); + rtc::scoped_refptr safety_flag = CreateInternal(false); safety_flag->main_sequence_.Detach(); return safety_flag; } diff --git a/rtc_base/task_utils/pending_task_safety_flag.h b/rtc_base/task_utils/pending_task_safety_flag.h index 6446bfe55e..58772bcbb1 100644 --- a/rtc_base/task_utils/pending_task_safety_flag.h +++ b/rtc_base/task_utils/pending_task_safety_flag.h @@ -93,6 +93,8 @@ class PendingTaskSafetyFlag final explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {} private: + static rtc::scoped_refptr CreateInternal(bool alive); + bool alive_ = true; RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_; }; diff --git a/rtc_base/task_utils/repeating_task.cc b/rtc_base/task_utils/repeating_task.cc index 1f3eb1d064..c07df4567c 100644 --- a/rtc_base/task_utils/repeating_task.cc +++ b/rtc_base/task_utils/repeating_task.cc @@ -21,10 +21,12 @@ namespace webrtc_repeating_task_impl { RepeatingTaskBase::RepeatingTaskBase( TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Clock* clock, rtc::scoped_refptr alive_flag) : task_queue_(task_queue), + precision_(precision), clock_(clock), next_run_time_(clock_->CurrentTime() + first_delay), alive_flag_(std::move(alive_flag)) {} @@ -38,19 +40,21 @@ bool RepeatingTaskBase::Run() { return true; TimeDelta delay = RunClosure(); + RTC_DCHECK_GE(delay, TimeDelta::Zero()); - // The closure might have stopped this task, in which case we return true to - // destruct this object. - if (!alive_flag_->alive()) + // A delay of +infinity means that the task should not be run again. + // Alternatively, the closure might have stopped this task. In either which + // case we return true to destruct this object. + if (delay.IsPlusInfinity() || !alive_flag_->alive()) return true; - RTC_DCHECK(delay.IsFinite()); TimeDelta lost_time = clock_->CurrentTime() - next_run_time_; next_run_time_ += delay; delay -= lost_time; delay = std::max(delay, TimeDelta::Zero()); - task_queue_->PostDelayedTask(absl::WrapUnique(this), delay.ms()); + task_queue_->PostDelayedTaskWithPrecision(precision_, absl::WrapUnique(this), + delay.ms()); // Return false to tell the TaskQueue to not destruct this object since we // have taken ownership with absl::WrapUnique. diff --git a/rtc_base/task_utils/repeating_task.h b/rtc_base/task_utils/repeating_task.h index 91a40e0714..20f28d54f4 100644 --- a/rtc_base/task_utils/repeating_task.h +++ b/rtc_base/task_utils/repeating_task.h @@ -34,6 +34,7 @@ void RepeatingTaskImplDTraceProbeRun(); class RepeatingTaskBase : public QueuedTask { public: RepeatingTaskBase(TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Clock* clock, rtc::scoped_refptr alive_flag); @@ -45,6 +46,7 @@ class RepeatingTaskBase : public QueuedTask { bool Run() final; TaskQueueBase* const task_queue_; + const TaskQueueBase::DelayPrecision precision_; Clock* const clock_; // This is always finite. Timestamp next_run_time_ RTC_GUARDED_BY(task_queue_); @@ -52,16 +54,21 @@ class RepeatingTaskBase : public QueuedTask { RTC_GUARDED_BY(task_queue_); }; -// The template closure pattern is based on rtc::ClosureTask. +// The template closure pattern is based on rtc::ClosureTask. The provided +// closure should have a TimeDelta return value, specifing the desired +// non-negative interval to next repetition, or TimeDelta::PlusInfinity to +// indicate that the task should be deleted and not called again. template class RepeatingTaskImpl final : public RepeatingTaskBase { public: RepeatingTaskImpl(TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Closure&& closure, Clock* clock, rtc::scoped_refptr alive_flag) : RepeatingTaskBase(task_queue, + precision, first_delay, clock, std::move(alive_flag)), @@ -103,17 +110,20 @@ class RepeatingTaskHandle { // 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. template static RepeatingTaskHandle Start(TaskQueueBase* task_queue, Closure&& closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, Clock* clock = Clock::GetRealTimeClock()) { auto alive_flag = PendingTaskSafetyFlag::CreateDetached(); webrtc_repeating_task_impl::RepeatingTaskHandleDTraceProbeStart(); task_queue->PostTask( std::make_unique< webrtc_repeating_task_impl::RepeatingTaskImpl>( - task_queue, TimeDelta::Zero(), std::forward(closure), - clock, alive_flag)); + task_queue, precision, TimeDelta::Zero(), + std::forward(closure), clock, alive_flag)); return RepeatingTaskHandle(std::move(alive_flag)); } @@ -124,14 +134,17 @@ class RepeatingTaskHandle { TaskQueueBase* task_queue, TimeDelta first_delay, Closure&& closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, Clock* clock = Clock::GetRealTimeClock()) { auto alive_flag = PendingTaskSafetyFlag::CreateDetached(); webrtc_repeating_task_impl::RepeatingTaskHandleDTraceProbeDelayedStart(); - task_queue->PostDelayedTask( + task_queue->PostDelayedTaskWithPrecision( + precision, std::make_unique< webrtc_repeating_task_impl::RepeatingTaskImpl>( - task_queue, first_delay, std::forward(closure), clock, - alive_flag), + task_queue, precision, first_delay, std::forward(closure), + clock, alive_flag), first_delay.ms()); return RepeatingTaskHandle(std::move(alive_flag)); } diff --git a/rtc_base/task_utils/repeating_task_unittest.cc b/rtc_base/task_utils/repeating_task_unittest.cc index b23284f988..d6ab920e50 100644 --- a/rtc_base/task_utils/repeating_task_unittest.cc +++ b/rtc_base/task_utils/repeating_task_unittest.cc @@ -11,12 +11,15 @@ #include "rtc_base/task_utils/repeating_task.h" #include -#include // Not allowed in production per Chromium style guide. #include -#include // Not allowed in production per Chromium style guide. +#include "api/task_queue/queued_task.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/timestamp.h" #include "rtc_base/event.h" #include "rtc_base/task_queue_for_test.h" +#include "rtc_base/task_utils/to_queued_task.h" +#include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" @@ -32,12 +35,6 @@ using ::testing::Return; constexpr TimeDelta kTimeout = TimeDelta::Millis(1000); -void Sleep(TimeDelta time_delta) { - // Note that Chromium style guide prohibits use of and in - // production code, used here since webrtc::SleepMs may return early. - std::this_thread::sleep_for(std::chrono::microseconds(time_delta.us())); -} - class MockClosure { public: MOCK_METHOD(TimeDelta, Call, ()); @@ -59,6 +56,72 @@ class MockTaskQueue : public TaskQueueBase { CurrentTaskQueueSetter task_queue_setter_; }; +class FakeTaskQueue : public TaskQueueBase { + public: + explicit FakeTaskQueue(SimulatedClock* clock) + : task_queue_setter_(this), clock_(clock) {} + + void Delete() override {} + + void PostTask(std::unique_ptr task) override { + last_task_ = std::move(task); + last_precision_ = absl::nullopt; + last_delay_ = 0; + } + + void PostDelayedTask(std::unique_ptr task, + uint32_t milliseconds) override { + last_task_ = std::move(task); + last_precision_ = TaskQueueBase::DelayPrecision::kLow; + last_delay_ = milliseconds; + } + + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) override { + last_task_ = std::move(task); + last_precision_ = TaskQueueBase::DelayPrecision::kHigh; + last_delay_ = milliseconds; + } + + bool AdvanceTimeAndRunLastTask() { + EXPECT_TRUE(last_task_); + EXPECT_TRUE(last_delay_); + clock_->AdvanceTimeMilliseconds(last_delay_.value_or(0)); + last_delay_.reset(); + auto task = std::move(last_task_); + bool delete_task = task->Run(); + if (!delete_task) { + // If the task should not be deleted then just release it. + task.release(); + } + return delete_task; + } + + bool IsTaskQueued() { return !!last_task_; } + + uint32_t last_delay() const { + EXPECT_TRUE(last_delay_.has_value()); + return last_delay_.value_or(-1); + } + + absl::optional last_precision() const { + return last_precision_; + } + + private: + CurrentTaskQueueSetter task_queue_setter_; + SimulatedClock* clock_; + std::unique_ptr last_task_; + absl::optional last_delay_; + absl::optional last_precision_; +}; + +// NOTE: Since this utility class holds a raw pointer to a variable that likely +// lives on the stack, it's important that any repeating tasks that use this +// class be explicitly stopped when the test criteria have been met. If the +// task is not stopped, an instance of this class can be deleted when the +// pointed-to MockClosure has been deleted and we end up trying to call a +// virtual method on a deleted object in the dtor. class MoveOnlyClosure { public: explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {} @@ -79,65 +142,75 @@ class MoveOnlyClosure { TEST(RepeatingTaskTest, TaskIsStoppedOnStop) { const TimeDelta kShortInterval = TimeDelta::Millis(50); - const TimeDelta kLongInterval = TimeDelta::Millis(200); - const int kShortIntervalCount = 4; - const int kMargin = 1; - TaskQueueForTest task_queue("TestQueue"); + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); std::atomic_int counter(0); - auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] { - if (++counter >= kShortIntervalCount) - return kLongInterval; + auto handle = RepeatingTaskHandle::Start(&task_queue, [&] { + counter++; return kShortInterval; }); - // Sleep long enough to go through the initial phase. - Sleep(kShortInterval * (kShortIntervalCount + kMargin)); - EXPECT_EQ(counter.load(), kShortIntervalCount); + EXPECT_EQ(task_queue.last_delay(), 0u); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + EXPECT_EQ(counter.load(), 1); - task_queue.PostTask( - [handle = std::move(handle)]() mutable { handle.Stop(); }); - // Sleep long enough that the task would run at least once more if not - // stopped. - Sleep(kLongInterval * 2); - EXPECT_EQ(counter.load(), kShortIntervalCount); + // The handle reposted at the short interval. + EXPECT_EQ(task_queue.last_delay(), kShortInterval.ms()); + + // Stop the handle. This prevernts the counter from incrementing. + handle.Stop(); + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); + EXPECT_EQ(counter.load(), 1); } TEST(RepeatingTaskTest, CompensatesForLongRunTime) { - const int kTargetCount = 20; - const int kTargetCountMargin = 2; const TimeDelta kRepeatInterval = TimeDelta::Millis(2); // Sleeping inside the task for longer than the repeat interval once, should // be compensated for by repeating the task faster to catch up. const TimeDelta kSleepDuration = TimeDelta::Millis(20); - const int kSleepAtCount = 3; std::atomic_int counter(0); - TaskQueueForTest task_queue("TestQueue"); - RepeatingTaskHandle::Start(task_queue.Get(), [&] { - if (++counter == kSleepAtCount) - Sleep(kSleepDuration); - return kRepeatInterval; - }); - Sleep(kRepeatInterval * kTargetCount); - // Execution time should not have affected the run count, - // but we allow some margin to reduce flakiness. - EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin); + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + RepeatingTaskHandle::Start( + &task_queue, + [&] { + ++counter; + // Task takes longer than the repeat duration. + clock.AdvanceTime(kSleepDuration); + return kRepeatInterval; + }, + TaskQueueBase::DelayPrecision::kLow, &clock); + + EXPECT_EQ(task_queue.last_delay(), 0u); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + + // Task is posted right away since it took longer to run then the repeat + // interval. + EXPECT_EQ(task_queue.last_delay(), 0u); + EXPECT_EQ(counter.load(), 1); } TEST(RepeatingTaskTest, CompensatesForShortRunTime) { + SimulatedClock clock(Timestamp::Millis(0)); + FakeTaskQueue task_queue(&clock); std::atomic_int counter(0); - TaskQueueForTest task_queue("TestQueue"); - RepeatingTaskHandle::Start(task_queue.Get(), [&] { - ++counter; - // Sleeping for the 100 ms should be compensated. - Sleep(TimeDelta::Millis(100)); - return TimeDelta::Millis(300); - }); - Sleep(TimeDelta::Millis(400)); + RepeatingTaskHandle::Start( + &task_queue, + [&] { + // Simulate the task taking 100ms, which should be compensated for. + counter++; + clock.AdvanceTime(TimeDelta::Millis(100)); + return TimeDelta::Millis(300); + }, + TaskQueueBase::DelayPrecision::kLow, &clock); - // We expect that the task have been called twice, once directly at Start and - // once after 300 ms has passed. - EXPECT_EQ(counter.load(), 2); + // Expect instant post task. + EXPECT_EQ(task_queue.last_delay(), 0u); + // Task should be retained by the handler since it is not cancelled. + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // New delay should be 200ms since repeat delay was 300ms but task took 100ms. + EXPECT_EQ(task_queue.last_delay(), 200u); } TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) { @@ -168,16 +241,30 @@ TEST(RepeatingTaskTest, CancelTaskAfterItRuns) { TEST(RepeatingTaskTest, TaskCanStopItself) { std::atomic_int counter(0); - TaskQueueForTest task_queue("TestQueue"); - RepeatingTaskHandle handle; - task_queue.PostTask([&] { - handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] { - ++counter; - handle.Stop(); - return TimeDelta::Millis(2); - }); + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] { + ++counter; + handle.Stop(); + return TimeDelta::Millis(2); }); - Sleep(TimeDelta::Millis(10)); + EXPECT_EQ(task_queue.last_delay(), 0u); + // Task cancelled itself so wants to be released. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); + EXPECT_EQ(counter.load(), 1); +} + +TEST(RepeatingTaskTest, TaskCanStopItselfByReturningInfinity) { + std::atomic_int counter(0); + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] { + ++counter; + return TimeDelta::PlusInfinity(); + }); + EXPECT_EQ(task_queue.last_delay(), 0u); + // Task cancelled itself so wants to be released. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); EXPECT_EQ(counter.load(), 1); } @@ -186,9 +273,9 @@ TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) { rtc::Event done; EXPECT_CALL(closure, Call()) .WillOnce(Return(TimeDelta::Zero())) - .WillOnce(Invoke([&done] { + .WillOnce(Invoke([&] { done.Set(); - return kTimeout; + return TimeDelta::PlusInfinity(); })); TaskQueueForTest task_queue("queue"); RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure)); @@ -201,9 +288,9 @@ TEST(RepeatingTaskTest, StartPeriodicTask) { EXPECT_CALL(closure, Call()) .WillOnce(Return(TimeDelta::Millis(20))) .WillOnce(Return(TimeDelta::Millis(20))) - .WillOnce(Invoke([&done] { + .WillOnce(Invoke([&] { done.Set(); - return kTimeout; + return TimeDelta::PlusInfinity(); })); TaskQueueForTest task_queue("queue"); RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction()); @@ -266,7 +353,7 @@ TEST(RepeatingTaskTest, ClockIntegration) { clock.AdvanceTimeMilliseconds(10); return TimeDelta::Millis(100); }, - &clock); + TaskQueueBase::DelayPrecision::kLow, &clock); clock.AdvanceTimeMilliseconds(100); QueuedTask* task_to_run = delayed_task.release(); @@ -294,4 +381,66 @@ TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) { handle.Stop(); } +TEST(RepeatingTaskTest, DefaultPrecisionIsLow) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction()); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the default precision: low. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kLow); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + +TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithLowPrecision) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(), + TaskQueueBase::DelayPrecision::kLow); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the specified precision. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kLow); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + +TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithHighPrecision) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(), + TaskQueueBase::DelayPrecision::kHigh); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the specified precision. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kHigh); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + } // namespace webrtc diff --git a/rtc_base/test_client.h b/rtc_base/test_client.h index 6989fe1d57..dd91d37ab9 100644 --- a/rtc_base/test_client.h +++ b/rtc_base/test_client.h @@ -15,7 +15,6 @@ #include #include "rtc_base/async_udp_socket.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/fake_clock.h" #include "rtc_base/synchronization/mutex.h" @@ -53,6 +52,9 @@ class TestClient : public sigslot::has_slots<> { ThreadProcessingFakeClock* fake_clock); ~TestClient() override; + TestClient(const TestClient&) = delete; + TestClient& operator=(const TestClient&) = delete; + SocketAddress address() const { return socket_->GetLocalAddress(); } SocketAddress remote_address() const { return socket_->GetRemoteAddress(); } @@ -110,7 +112,6 @@ class TestClient : public sigslot::has_slots<> { std::vector> packets_; int ready_to_send_count_ = 0; int64_t prev_packet_timestamp_; - RTC_DISALLOW_COPY_AND_ASSIGN(TestClient); }; } // namespace rtc diff --git a/rtc_base/test_echo_server.h b/rtc_base/test_echo_server.h index 6fdfc249e4..ba5f997287 100644 --- a/rtc_base/test_echo_server.h +++ b/rtc_base/test_echo_server.h @@ -20,7 +20,6 @@ #include "absl/algorithm/container.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_tcp_socket.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -35,13 +34,16 @@ class TestEchoServer : public sigslot::has_slots<> { TestEchoServer(Thread* thread, const SocketAddress& addr); ~TestEchoServer() override; + TestEchoServer(const TestEchoServer&) = delete; + TestEchoServer& operator=(const TestEchoServer&) = delete; + SocketAddress address() const { return server_socket_->GetLocalAddress(); } private: void OnAccept(Socket* socket) { Socket* raw_socket = socket->Accept(nullptr); if (raw_socket) { - AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket, false); + AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket); packet_socket->SignalReadPacket.connect(this, &TestEchoServer::OnPacket); packet_socket->SignalClose.connect(this, &TestEchoServer::OnClose); client_sockets_.push_back(packet_socket); @@ -64,7 +66,6 @@ class TestEchoServer : public sigslot::has_slots<> { typedef std::list ClientList; std::unique_ptr server_socket_; ClientList client_sockets_; - RTC_DISALLOW_COPY_AND_ASSIGN(TestEchoServer); }; } // namespace rtc diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc index 692d80276d..f9680650a9 100644 --- a/rtc_base/thread.cc +++ b/rtc_base/thread.cc @@ -10,6 +10,8 @@ #include "rtc_base/thread.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #elif defined(WEBRTC_POSIX) @@ -75,6 +77,9 @@ class MessageHandlerWithTask final : public MessageHandler { public: MessageHandlerWithTask() {} + MessageHandlerWithTask(const MessageHandlerWithTask&) = delete; + MessageHandlerWithTask& operator=(const MessageHandlerWithTask&) = delete; + void OnMessage(Message* msg) override { static_cast(msg->pdata)->Run(); delete msg->pdata; @@ -82,8 +87,6 @@ class MessageHandlerWithTask final : public MessageHandler { private: ~MessageHandlerWithTask() override {} - - RTC_DISALLOW_COPY_AND_ASSIGN(MessageHandlerWithTask); }; class RTC_SCOPED_LOCKABLE MarkProcessingCritScope { @@ -100,11 +103,12 @@ class RTC_SCOPED_LOCKABLE MarkProcessingCritScope { cs_->Leave(); } + MarkProcessingCritScope(const MarkProcessingCritScope&) = delete; + MarkProcessingCritScope& operator=(const MarkProcessingCritScope&) = delete; + private: const RecursiveCriticalSection* const cs_; size_t* processing_; - - RTC_DISALLOW_COPY_AND_ASSIGN(MarkProcessingCritScope); }; } // namespace @@ -116,7 +120,7 @@ ThreadManager* ThreadManager::Instance() { ThreadManager::~ThreadManager() { // By above RTC_DEFINE_STATIC_LOCAL. - RTC_NOTREACHED() << "ThreadManager should never be destructed."; + RTC_DCHECK_NOTREACHED() << "ThreadManager should never be destructed."; } // static @@ -253,19 +257,11 @@ Thread* Thread::Current() { ThreadManager* manager = ThreadManager::Instance(); Thread* thread = manager->CurrentThread(); -#ifndef NO_MAIN_THREAD_WRAPPING - // Only autowrap the thread which instantiated the ThreadManager. - if (!thread && manager->IsMainThread()) { - thread = new Thread(CreateDefaultSocketServer()); - thread->WrapCurrentWithThreadManager(manager, true); - } -#endif - return thread; } #if defined(WEBRTC_POSIX) -ThreadManager::ThreadManager() : main_thread_ref_(CurrentThreadRef()) { +ThreadManager::ThreadManager() { #if defined(WEBRTC_MAC) InitCocoaMultiThreading(); #endif @@ -282,8 +278,7 @@ void ThreadManager::SetCurrentThreadInternal(Thread* thread) { #endif #if defined(WEBRTC_WIN) -ThreadManager::ThreadManager() - : key_(TlsAlloc()), main_thread_ref_(CurrentThreadRef()) {} +ThreadManager::ThreadManager() : key_(TlsAlloc()) {} Thread* ThreadManager::CurrentThread() { return static_cast(TlsGetValue(key_)); @@ -339,10 +334,6 @@ void ThreadManager::UnwrapCurrentThread() { } } -bool ThreadManager::IsMainThread() { - return IsThreadRefEqual(CurrentThreadRef(), main_thread_ref_); -} - Thread::ScopedDisallowBlockingCalls::ScopedDisallowBlockingCalls() : thread_(Thread::Current()), previous_state_(thread_->SetAllowBlockingCalls(false)) {} @@ -755,10 +746,10 @@ bool Thread::SleepMs(int milliseconds) { #endif } -bool Thread::SetName(const std::string& name, const void* obj) { +bool Thread::SetName(absl::string_view name, const void* obj) { RTC_DCHECK(!IsRunning()); - name_ = name; + name_ = std::string(name); if (obj) { // The %p specifier typically produce at most 16 hex digits, possibly with a // 0x prefix. But format is implementation defined, so add some margin. @@ -1107,10 +1098,16 @@ void Thread::PostTask(std::unique_ptr task) { void Thread::PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) { + // This implementation does not support low precision yet. + PostDelayedHighPrecisionTask(std::move(task), milliseconds); +} + +void Thread::PostDelayedHighPrecisionTask( + std::unique_ptr task, + uint32_t milliseconds) { // Though PostDelayed takes MessageData by raw pointer (last parameter), // it still takes it with ownership. - PostDelayed(RTC_FROM_HERE, milliseconds, &queued_task_handler_, - /*id=*/0, + PostDelayed(RTC_FROM_HERE, milliseconds, &queued_task_handler_, /*id=*/0, new ScopedMessageData(std::move(task))); } @@ -1228,11 +1225,6 @@ AutoSocketServerThread::AutoSocketServerThread(SocketServer* ss) AutoSocketServerThread::~AutoSocketServerThread() { RTC_DCHECK(ThreadManager::Instance()->CurrentThread() == this); - // Some tests post destroy messages to this thread. To avoid memory - // leaks, we have to process those messages. In particular - // P2PTransportChannelPingTest, relying on the message posted in - // cricket::Connection::Destroy. - ProcessMessages(0); // Stop and destroy the thread before clearing it as the current thread. // Sometimes there are messages left in the Thread that will be // destroyed by DoDestroy, and sometimes the destructors of the message and/or diff --git a/rtc_base/thread.h b/rtc_base/thread.h index 38e9732fbb..052a71b22c 100644 --- a/rtc_base/thread.h +++ b/rtc_base/thread.h @@ -22,20 +22,23 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_POSIX) #include #endif +#include "absl/base/attributes.h" #include "api/function_view.h" #include "api/task_queue/queued_task.h" #include "api/task_queue/task_queue_base.h" #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/deprecated/recursive_critical_section.h" #include "rtc_base/location.h" #include "rtc_base/message_handler.h" #include "rtc_base/platform_thread_types.h" #include "rtc_base/socket_server.h" #include "rtc_base/system/rtc_export.h" +#include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread_annotations.h" #include "rtc_base/thread_message.h" @@ -89,14 +92,15 @@ class MessageWithFunctor final : public MessageLikeTask { explicit MessageWithFunctor(FunctorT&& functor) : functor_(std::forward(functor)) {} + MessageWithFunctor(const MessageWithFunctor&) = delete; + MessageWithFunctor& operator=(const MessageWithFunctor&) = delete; + void Run() override { functor_(); } private: ~MessageWithFunctor() override {} typename std::remove_reference::type functor_; - - RTC_DISALLOW_COPY_AND_ASSIGN(MessageWithFunctor); }; } // namespace rtc_thread_internal @@ -139,8 +143,6 @@ class RTC_EXPORT ThreadManager { Thread* WrapCurrentThread(); void UnwrapCurrentThread(); - bool IsMainThread(); - #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 @@ -152,6 +154,9 @@ class RTC_EXPORT ThreadManager { 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); @@ -183,11 +188,6 @@ class RTC_EXPORT ThreadManager { #if defined(WEBRTC_WIN) const DWORD key_; #endif - - // The thread to potentially autowrap. - const PlatformThreadRef main_thread_ref_; - - RTC_DISALLOW_COPY_AND_ASSIGN(ThreadManager); }; // WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread(). @@ -221,6 +221,9 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public webrtc::TaskQueueBase { // 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(); @@ -347,7 +350,7 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public webrtc::TaskQueueBase { // Sets the thread's name, for debugging. Must be called before Start(). // If `obj` is non-null, its value is appended to `name`. const std::string& name() const { return name_; } - bool SetName(const std::string& name, const void* obj); + bool SetName(absl::string_view name, const void* obj); // Sets the expected processing time in ms. The thread will write // log messages when Invoke() takes more time than this. @@ -414,62 +417,39 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public webrtc::TaskQueueBase { // true. bool IsInvokeToThreadAllowed(rtc::Thread* target); - // Posts a task to invoke the functor on `this` thread asynchronously, i.e. - // without blocking the thread that invoked PostTask(). Ownership of `functor` - // is passed and (usually, see below) destroyed on `this` thread after it is - // invoked. - // Requirements of FunctorT: - // - FunctorT is movable. - // - FunctorT implements "T operator()()" or "T operator()() const" for some T - // (if T is not void, the return value is discarded on `this` thread). - // - FunctorT has a public destructor that can be invoked from `this` thread - // after operation() has been invoked. - // - The functor must not cause the thread to quit before PostTask() is done. - // - // Destruction of the functor/task mimics what TaskQueue::PostTask does: If - // the task is run, it will be destroyed on `this` thread. However, if there - // are pending tasks by the time the Thread is destroyed, or a task is posted - // to a thread that is quitting, the task is destroyed immediately, on the - // calling thread. Destroying the Thread only blocks for any currently running - // task to complete. Note that TQ abstraction is even vaguer on how - // destruction happens in these cases, allowing destruction to happen - // asynchronously at a later time and on some arbitrary thread. So to ease - // migration, don't depend on Thread::PostTask destroying un-run tasks - // immediately. - // - // Example - Calling a class method: - // class Foo { - // public: - // void DoTheThing(); - // }; - // Foo foo; - // thread->PostTask(RTC_FROM_HERE, Bind(&Foo::DoTheThing, &foo)); - // - // Example - Calling a lambda function: - // thread->PostTask(RTC_FROM_HERE, - // [&x, &y] { x.TrackComputations(y.Compute()); }); - template - void PostTask(const Location& posted_from, FunctorT&& functor) { - Post(posted_from, GetPostTaskMessageHandler(), /*id=*/0, - new rtc_thread_internal::MessageWithFunctor( - std::forward(functor))); - } - template - void PostDelayedTask(const Location& posted_from, - FunctorT&& functor, - uint32_t milliseconds) { - PostDelayed(posted_from, milliseconds, GetPostTaskMessageHandler(), - /*id=*/0, - new rtc_thread_internal::MessageWithFunctor( - std::forward(functor))); - } - // From TaskQueueBase void PostTask(std::unique_ptr task) override; void PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) override; + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) override; void Delete() override; + // Helper methods to avoid having to do ToQueuedTask() at the calling places. + template >::value>::type* = nullptr> + void PostTask(Closure&& closure) { + PostTask(webrtc::ToQueuedTask(std::forward(closure))); + } + template >::value>::type* = nullptr> + void PostDelayedTask(Closure&& closure, uint32_t milliseconds) { + PostDelayedTask(webrtc::ToQueuedTask(std::forward(closure)), + milliseconds); + } + template >::value>::type* = nullptr> + void PostDelayedHighPrecisionTask(Closure&& closure, uint32_t milliseconds) { + PostDelayedHighPrecisionTask( + webrtc::ToQueuedTask(std::forward(closure)), milliseconds); + } + // ProcessMessages will process I/O and dispatch messages until: // 1) cms milliseconds have elapsed (returns true) // 2) Stop() is called (returns false) @@ -685,8 +665,6 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public webrtc::TaskQueueBase { friend class ThreadManager; int dispatch_warning_ms_ RTC_GUARDED_BY(this) = kSlowDispatchLoggingThreshold; - - RTC_DISALLOW_COPY_AND_ASSIGN(Thread); }; // AutoThread automatically installs itself at construction @@ -700,8 +678,8 @@ class AutoThread : public Thread { AutoThread(); ~AutoThread() override; - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AutoThread); + AutoThread(const AutoThread&) = delete; + AutoThread& operator=(const AutoThread&) = delete; }; // AutoSocketServerThread automatically installs itself at @@ -714,10 +692,11 @@ class AutoSocketServerThread : public Thread { explicit AutoSocketServerThread(SocketServer* ss); ~AutoSocketServerThread() override; + AutoSocketServerThread(const AutoSocketServerThread&) = delete; + AutoSocketServerThread& operator=(const AutoSocketServerThread&) = delete; + private: rtc::Thread* old_thread_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AutoSocketServerThread); }; } // namespace rtc diff --git a/rtc_base/thread_unittest.cc b/rtc_base/thread_unittest.cc index 430db3d606..53b23ad904 100644 --- a/rtc_base/thread_unittest.cc +++ b/rtc_base/thread_unittest.cc @@ -204,6 +204,10 @@ struct FunctorD { public: explicit FunctorD(AtomicBool* flag) : flag_(flag) {} FunctorD(FunctorD&&) = default; + + FunctorD(const FunctorD&) = delete; + FunctorD& operator=(const FunctorD&) = delete; + FunctorD& operator=(FunctorD&&) = default; void operator()() { if (flag_) @@ -212,7 +216,6 @@ struct FunctorD { private: AtomicBool* flag_; - RTC_DISALLOW_COPY_AND_ASSIGN(FunctorD); }; // See: https://code.google.com/p/webrtc/issues/detail?id=2409 @@ -709,8 +712,8 @@ TEST(ThreadManager, ProcessAllMessageQueuesWithClearedQueue) { }; // Post messages (both delayed and non delayed) to both threads. - t->PostTask(RTC_FROM_HERE, clearer); - rtc::Thread::Current()->PostTask(RTC_FROM_HERE, event_signaler); + t->PostTask(clearer); + rtc::Thread::Current()->PostTask(event_signaler); ThreadManager::ProcessAllMessageQueuesForTesting(); } @@ -742,7 +745,7 @@ TEST(ThreadManager, ClearReentrant) { new ScopedRefMessageData(inner_handler)); } -class AsyncInvokeTest : public ::testing::Test { +class DEPRECATED_AsyncInvokeTest : public ::testing::Test { public: void IntCallback(int value) { EXPECT_EQ(expected_thread_, Thread::Current()); @@ -754,13 +757,13 @@ class AsyncInvokeTest : public ::testing::Test { protected: enum { kWaitTimeout = 1000 }; - AsyncInvokeTest() : int_value_(0), expected_thread_(nullptr) {} + DEPRECATED_AsyncInvokeTest() : int_value_(0), expected_thread_(nullptr) {} int int_value_; Thread* expected_thread_; }; -TEST_F(AsyncInvokeTest, FireAndForget) { +TEST_F(DEPRECATED_AsyncInvokeTest, FireAndForget) { DEPRECATED_AsyncInvoker invoker; // Create and start the thread. auto thread = Thread::CreateWithSocketServer(); @@ -772,7 +775,7 @@ TEST_F(AsyncInvokeTest, FireAndForget) { thread->Stop(); } -TEST_F(AsyncInvokeTest, NonCopyableFunctor) { +TEST_F(DEPRECATED_AsyncInvokeTest, NonCopyableFunctor) { DEPRECATED_AsyncInvoker invoker; // Create and start the thread. auto thread = Thread::CreateWithSocketServer(); @@ -784,7 +787,7 @@ TEST_F(AsyncInvokeTest, NonCopyableFunctor) { thread->Stop(); } -TEST_F(AsyncInvokeTest, KillInvokerDuringExecute) { +TEST_F(DEPRECATED_AsyncInvokeTest, KillInvokerDuringExecute) { // Use these events to get in a state where the functor is in the middle of // executing, and then to wait for it to finish, ensuring the "EXPECT_FALSE" // is run. @@ -821,10 +824,10 @@ TEST_F(AsyncInvokeTest, KillInvokerDuringExecute) { } // Variant of the above test where the async-invoked task calls AsyncInvoke -// *again*, for the thread on which the AsyncInvoker is currently being -// destroyed. This shouldn't deadlock or crash; this second invocation should -// just be ignored. -TEST_F(AsyncInvokeTest, KillInvokerDuringExecuteWithReentrantInvoke) { +// *again*, for the thread on which the invoker is currently being destroyed. +// This shouldn't deadlock or crash. The second invocation should be ignored. +TEST_F(DEPRECATED_AsyncInvokeTest, + KillInvokerDuringExecuteWithReentrantInvoke) { Event functor_started; // Flag used to verify that the recursively invoked task never actually runs. bool reentrant_functor_run = false; @@ -851,44 +854,6 @@ TEST_F(AsyncInvokeTest, KillInvokerDuringExecuteWithReentrantInvoke) { EXPECT_FALSE(reentrant_functor_run); } -TEST_F(AsyncInvokeTest, Flush) { - DEPRECATED_AsyncInvoker invoker; - AtomicBool flag1; - AtomicBool flag2; - // Queue two async calls to the current thread. - invoker.AsyncInvoke(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag1)); - invoker.AsyncInvoke(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag2)); - // Because we haven't pumped messages, these should not have run yet. - EXPECT_FALSE(flag1.get()); - EXPECT_FALSE(flag2.get()); - // Force them to run now. - invoker.Flush(Thread::Current()); - EXPECT_TRUE(flag1.get()); - EXPECT_TRUE(flag2.get()); -} - -TEST_F(AsyncInvokeTest, FlushWithIds) { - DEPRECATED_AsyncInvoker invoker; - AtomicBool flag1; - AtomicBool flag2; - // Queue two async calls to the current thread, one with a message id. - invoker.AsyncInvoke(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag1), - 5); - invoker.AsyncInvoke(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag2)); - // Because we haven't pumped messages, these should not have run yet. - EXPECT_FALSE(flag1.get()); - EXPECT_FALSE(flag2.get()); - // Execute pending calls with id == 5. - invoker.Flush(Thread::Current(), 5); - EXPECT_TRUE(flag1.get()); - EXPECT_FALSE(flag2.get()); - flag1 = false; - // Execute all pending calls. The id == 5 call should not execute again. - invoker.Flush(Thread::Current()); - EXPECT_FALSE(flag1.get()); - EXPECT_TRUE(flag2.get()); -} - void WaitAndSetEvent(Event* wait_event, Event* set_event) { wait_event->Wait(Event::kForever); set_event->Set(); @@ -958,7 +923,7 @@ TEST(ThreadPostTaskTest, InvokesWithLambda) { background_thread->Start(); Event event; - background_thread->PostTask(RTC_FROM_HERE, [&event] { event.Set(); }); + background_thread->PostTask([&event] { event.Set(); }); event.Wait(Event::kForever); } @@ -969,7 +934,7 @@ TEST(ThreadPostTaskTest, InvokesWithCopiedFunctor) { LifeCycleFunctor::Stats stats; Event event; LifeCycleFunctor functor(&stats, &event); - background_thread->PostTask(RTC_FROM_HERE, functor); + background_thread->PostTask(functor); event.Wait(Event::kForever); EXPECT_EQ(1u, stats.copy_count); @@ -983,7 +948,7 @@ TEST(ThreadPostTaskTest, InvokesWithMovedFunctor) { LifeCycleFunctor::Stats stats; Event event; LifeCycleFunctor functor(&stats, &event); - background_thread->PostTask(RTC_FROM_HERE, std::move(functor)); + background_thread->PostTask(std::move(functor)); event.Wait(Event::kForever); EXPECT_EQ(0u, stats.copy_count); @@ -998,7 +963,7 @@ TEST(ThreadPostTaskTest, InvokesWithReferencedFunctorShouldCopy) { Event event; LifeCycleFunctor functor(&stats, &event); LifeCycleFunctor& functor_ref = functor; - background_thread->PostTask(RTC_FROM_HERE, functor_ref); + background_thread->PostTask(functor_ref); event.Wait(Event::kForever); EXPECT_EQ(1u, stats.copy_count); @@ -1013,7 +978,7 @@ TEST(ThreadPostTaskTest, InvokesWithCopiedFunctorDestroyedOnTargetThread) { bool was_invoked_on_background_thread = false; DestructionFunctor functor(background_thread.get(), &was_invoked_on_background_thread, &event); - background_thread->PostTask(RTC_FROM_HERE, functor); + background_thread->PostTask(functor); event.Wait(Event::kForever); EXPECT_TRUE(was_invoked_on_background_thread); @@ -1027,7 +992,7 @@ TEST(ThreadPostTaskTest, InvokesWithMovedFunctorDestroyedOnTargetThread) { bool was_invoked_on_background_thread = false; DestructionFunctor functor(background_thread.get(), &was_invoked_on_background_thread, &event); - background_thread->PostTask(RTC_FROM_HERE, std::move(functor)); + background_thread->PostTask(std::move(functor)); event.Wait(Event::kForever); EXPECT_TRUE(was_invoked_on_background_thread); @@ -1043,7 +1008,7 @@ TEST(ThreadPostTaskTest, DestructionFunctor functor(background_thread.get(), &was_invoked_on_background_thread, &event); DestructionFunctor& functor_ref = functor; - background_thread->PostTask(RTC_FROM_HERE, functor_ref); + background_thread->PostTask(functor_ref); event.Wait(Event::kForever); EXPECT_TRUE(was_invoked_on_background_thread); @@ -1057,7 +1022,6 @@ TEST(ThreadPostTaskTest, InvokesOnBackgroundThread) { bool was_invoked_on_background_thread = false; Thread* background_thread_ptr = background_thread.get(); background_thread->PostTask( - RTC_FROM_HERE, [background_thread_ptr, &was_invoked_on_background_thread, &event] { was_invoked_on_background_thread = background_thread_ptr->IsCurrent(); event.Set(); @@ -1075,10 +1039,11 @@ TEST(ThreadPostTaskTest, InvokesAsynchronously) { // thread. The second event ensures that the message is processed. Event event_set_by_test_thread; Event event_set_by_background_thread; - background_thread->PostTask(RTC_FROM_HERE, [&event_set_by_test_thread, - &event_set_by_background_thread] { - WaitAndSetEvent(&event_set_by_test_thread, &event_set_by_background_thread); - }); + background_thread->PostTask( + [&event_set_by_test_thread, &event_set_by_background_thread] { + WaitAndSetEvent(&event_set_by_test_thread, + &event_set_by_background_thread); + }); event_set_by_test_thread.Set(); event_set_by_background_thread.Wait(Event::kForever); } @@ -1093,11 +1058,11 @@ TEST(ThreadPostTaskTest, InvokesInPostedOrder) { Event fourth; background_thread->PostTask( - RTC_FROM_HERE, [&first, &second] { WaitAndSetEvent(&first, &second); }); + [&first, &second] { WaitAndSetEvent(&first, &second); }); background_thread->PostTask( - RTC_FROM_HERE, [&second, &third] { WaitAndSetEvent(&second, &third); }); + [&second, &third] { WaitAndSetEvent(&second, &third); }); background_thread->PostTask( - RTC_FROM_HERE, [&third, &fourth] { WaitAndSetEvent(&third, &fourth); }); + [&third, &fourth] { WaitAndSetEvent(&third, &fourth); }); // All tasks have been posted before the first one is unblocked. first.Set(); @@ -1114,7 +1079,6 @@ TEST(ThreadPostDelayedTaskTest, InvokesAsynchronously) { Event event_set_by_test_thread; Event event_set_by_background_thread; background_thread->PostDelayedTask( - RTC_FROM_HERE, [&event_set_by_test_thread, &event_set_by_background_thread] { WaitAndSetEvent(&event_set_by_test_thread, &event_set_by_background_thread); @@ -1135,13 +1099,13 @@ TEST(ThreadPostDelayedTaskTest, InvokesInDelayOrder) { Event fourth; background_thread->PostDelayedTask( - RTC_FROM_HERE, [&third, &fourth] { WaitAndSetEvent(&third, &fourth); }, + [&third, &fourth] { WaitAndSetEvent(&third, &fourth); }, /*milliseconds=*/11); background_thread->PostDelayedTask( - RTC_FROM_HERE, [&first, &second] { WaitAndSetEvent(&first, &second); }, + [&first, &second] { WaitAndSetEvent(&first, &second); }, /*milliseconds=*/9); background_thread->PostDelayedTask( - RTC_FROM_HERE, [&second, &third] { WaitAndSetEvent(&second, &third); }, + [&second, &third] { WaitAndSetEvent(&second, &third); }, /*milliseconds=*/10); // All tasks have been posted before the first one is unblocked. diff --git a/rtc_base/time/BUILD.gn b/rtc_base/time/BUILD.gn index 9a1d99b610..890695dfeb 100644 --- a/rtc_base/time/BUILD.gn +++ b/rtc_base/time/BUILD.gn @@ -17,4 +17,6 @@ rtc_library("timestamp_extrapolator") { "timestamp_extrapolator.cc", "timestamp_extrapolator.h", ] + deps = [ "../../api/units:timestamp" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } diff --git a/rtc_base/time/timestamp_extrapolator.cc b/rtc_base/time/timestamp_extrapolator.cc index 99445284dc..521b5ba4c4 100644 --- a/rtc_base/time/timestamp_extrapolator.cc +++ b/rtc_base/time/timestamp_extrapolator.cc @@ -12,10 +12,13 @@ #include +#include "absl/types/optional.h" + namespace webrtc { -TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms) - : _startMs(0), +TimestampExtrapolator::TimestampExtrapolator(Timestamp start) + : _start(Timestamp::Zero()), + _prev(Timestamp::Zero()), _firstTimestamp(0), _wrapArounds(0), _prevUnwrappedTimestamp(-1), @@ -30,12 +33,12 @@ TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms) _accDrift(6600), // in timestamp ticks, i.e. 15 ms _accMaxError(7000), _pP11(1e10) { - Reset(start_ms); + Reset(start); } -void TimestampExtrapolator::Reset(int64_t start_ms) { - _startMs = start_ms; - _prevMs = _startMs; +void TimestampExtrapolator::Reset(Timestamp start) { + _start = start; + _prev = _start; _firstTimestamp = 0; _w[0] = 90.0; _w[1] = 0; @@ -51,17 +54,18 @@ void TimestampExtrapolator::Reset(int64_t start_ms) { _detectorAccumulatorNeg = 0; } -void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { - if (tMs - _prevMs > 10e3) { +void TimestampExtrapolator::Update(Timestamp now, uint32_t ts90khz) { + if (now - _prev > TimeDelta::Seconds(10)) { // Ten seconds without a complete frame. // Reset the extrapolator - Reset(tMs); + Reset(now); } else { - _prevMs = tMs; + _prev = now; } // Remove offset to prevent badly scaled matrices - tMs -= _startMs; + const TimeDelta offset = now - _start; + double tMs = offset.ms(); CheckForWrapArounds(ts90khz); @@ -79,7 +83,7 @@ void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { } double residual = (static_cast(unwrapped_ts90khz) - _firstTimestamp) - - static_cast(tMs) * _w[0] - _w[1]; + tMs * _w[0] - _w[1]; if (DelayChangeDetection(residual) && _packetCount >= _startUpFilterDelayInPackets) { // A sudden change of average network delay has been detected. @@ -123,32 +127,28 @@ void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { } } -int64_t TimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) { - int64_t localTimeMs = 0; +absl::optional TimestampExtrapolator::ExtrapolateLocalTime( + uint32_t timestamp90khz) { CheckForWrapArounds(timestamp90khz); double unwrapped_ts90khz = static_cast(timestamp90khz) + _wrapArounds * ((static_cast(1) << 32) - 1); if (_packetCount == 0) { - localTimeMs = -1; + return absl::nullopt; } else if (_packetCount < _startUpFilterDelayInPackets) { - localTimeMs = - _prevMs + - static_cast( - static_cast(unwrapped_ts90khz - _prevUnwrappedTimestamp) / - 90.0 + - 0.5); + auto diffMs = static_cast( + static_cast(unwrapped_ts90khz - _prevUnwrappedTimestamp) / + 90.0 + + 0.5); + return _prev + TimeDelta::Millis(diffMs); + } else if (_w[0] < 1e-3) { + return _start; } else { - if (_w[0] < 1e-3) { - localTimeMs = _startMs; - } else { - double timestampDiff = - unwrapped_ts90khz - static_cast(_firstTimestamp); - localTimeMs = static_cast(static_cast(_startMs) + - (timestampDiff - _w[1]) / _w[0] + 0.5); - } + double timestampDiff = + unwrapped_ts90khz - static_cast(_firstTimestamp); + auto diffMs = static_cast((timestampDiff - _w[1]) / _w[0] + 0.5); + return _start + TimeDelta::Millis(diffMs); } - return localTimeMs; } // Investigates if the timestamp clock has overflowed since the last timestamp diff --git a/rtc_base/time/timestamp_extrapolator.h b/rtc_base/time/timestamp_extrapolator.h index b325d2cbaa..df56eea986 100644 --- a/rtc_base/time/timestamp_extrapolator.h +++ b/rtc_base/time/timestamp_extrapolator.h @@ -13,23 +13,26 @@ #include +#include "absl/types/optional.h" +#include "api/units/timestamp.h" + namespace webrtc { // Not thread safe. class TimestampExtrapolator { public: - explicit TimestampExtrapolator(int64_t start_ms); - void Update(int64_t tMs, uint32_t ts90khz); - int64_t ExtrapolateLocalTime(uint32_t timestamp90khz); - void Reset(int64_t start_ms); + explicit TimestampExtrapolator(Timestamp start); + void Update(Timestamp now, uint32_t ts90khz); + absl::optional ExtrapolateLocalTime(uint32_t timestamp90khz); + void Reset(Timestamp start); private: void CheckForWrapArounds(uint32_t ts90khz); bool DelayChangeDetection(double error); double _w[2]; double _pP[2][2]; - int64_t _startMs; - int64_t _prevMs; + Timestamp _start; + Timestamp _prev; uint32_t _firstTimestamp; int32_t _wrapArounds; int64_t _prevUnwrappedTimestamp; diff --git a/rtc_base/time_utils.h b/rtc_base/time_utils.h index de3c58c815..6a3cfda3d1 100644 --- a/rtc_base/time_utils.h +++ b/rtc_base/time_utils.h @@ -31,6 +31,12 @@ static const int64_t kNumNanosecsPerMillisec = 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 { diff --git a/rtc_base/timestamp_aligner.h b/rtc_base/timestamp_aligner.h index 73af9debf9..138e936af2 100644 --- a/rtc_base/timestamp_aligner.h +++ b/rtc_base/timestamp_aligner.h @@ -13,7 +13,6 @@ #include -#include "rtc_base/constructor_magic.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -35,6 +34,9 @@ class RTC_EXPORT TimestampAligner { TimestampAligner(); ~TimestampAligner(); + TimestampAligner(const TimestampAligner&) = delete; + TimestampAligner& operator=(const TimestampAligner&) = delete; + public: // Translates timestamps of a capture system to the same timescale as is used // by rtc::TimeMicros(). `capturer_time_us` is assumed to be accurate, but @@ -77,7 +79,6 @@ class RTC_EXPORT TimestampAligner { // Offset between `prev_translated_time_us_` and the corresponding capturer // time. int64_t prev_time_offset_us_; - RTC_DISALLOW_COPY_AND_ASSIGN(TimestampAligner); }; } // namespace rtc diff --git a/rtc_base/unique_id_generator.cc b/rtc_base/unique_id_generator.cc index 9fa3021c6f..e68c643dbe 100644 --- a/rtc_base/unique_id_generator.cc +++ b/rtc_base/unique_id_generator.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/helpers.h" #include "rtc_base/string_encode.h" #include "rtc_base/string_to_number.h" @@ -55,8 +56,11 @@ std::string UniqueStringGenerator::GenerateString() { return ToString(unique_number_generator_.GenerateNumber()); } -bool UniqueStringGenerator::AddKnownId(const std::string& value) { - absl::optional int_value = StringToNumber(value); +bool UniqueStringGenerator::AddKnownId(absl::string_view value) { + // TODO(webrtc:13579): remove string copy here once absl::string_view version + // of StringToNumber is available. + absl::optional int_value = + StringToNumber(std::string(value)); // The underlying generator works for uint32_t values, so if the provided // value is not a uint32_t it will never be generated anyway. if (int_value.has_value()) { diff --git a/rtc_base/unique_id_generator.h b/rtc_base/unique_id_generator.h index 3e2f9d7072..342dad7766 100644 --- a/rtc_base/unique_id_generator.h +++ b/rtc_base/unique_id_generator.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/sequence_checker.h" #include "rtc_base/synchronization/mutex.h" @@ -103,7 +104,7 @@ class UniqueStringGenerator { // Adds an id that this generator should no longer generate. // Return value indicates whether the ID was hitherto unknown. - bool AddKnownId(const std::string& value); + bool AddKnownId(absl::string_view value); private: // This implementation will be simple and will generate "0", "1", ... diff --git a/rtc_base/unique_id_generator_unittest.cc b/rtc_base/unique_id_generator_unittest.cc index 835a57e162..dc5e9c245f 100644 --- a/rtc_base/unique_id_generator_unittest.cc +++ b/rtc_base/unique_id_generator_unittest.cc @@ -187,6 +187,9 @@ TEST(UniqueNumberGeneratorDeathTest, FailsWhenUsedInWrongContext) { // thread/sequence checkers will pick up a different thread environment than // `fake_task_queue` will represent. UniqueNumberGenerator generator; + + // Instantiate a fake task queue that will register itself as the current tq. + FakeTaskQueue initial_fake_task_queue; // Generate an ID on the current thread. This causes the generator to attach // to the current thread context. generator.GenerateNumber(); diff --git a/rtc_base/units/unit_base.h b/rtc_base/units/unit_base.h index 7196bae346..4ccd9b750a 100644 --- a/rtc_base/units/unit_base.h +++ b/rtc_base/units/unit_base.h @@ -298,6 +298,14 @@ template inline constexpr Unit_T operator*(int32_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 diff --git a/rtc_base/units/unit_base_unittest.cc b/rtc_base/units/unit_base_unittest.cc index bbdbd8cd10..9eb0c33ae5 100644 --- a/rtc_base/units/unit_base_unittest.cc +++ b/rtc_base/units/unit_base_unittest.cc @@ -231,5 +231,16 @@ TEST(UnitBaseTest, InfinityOperations) { EXPECT_TRUE((finite + TestUnit::MinusInfinity()).IsMinusInfinity()); EXPECT_TRUE((finite - TestUnit::PlusInfinity()).IsMinusInfinity()); } + +TEST(UnitBaseTest, UnaryMinus) { + const int64_t kValue = 1337; + const TestUnit unit = TestUnit::FromValue(kValue); + EXPECT_EQ(-unit.ToValue(), -kValue); + + // Check infinity. + EXPECT_EQ(-TestUnit::PlusInfinity(), TestUnit::MinusInfinity()); + EXPECT_EQ(-TestUnit::MinusInfinity(), TestUnit::PlusInfinity()); +} + } // namespace test } // namespace webrtc diff --git a/rtc_base/virtual_socket_server.cc b/rtc_base/virtual_socket_server.cc index d2bf56eeff..5d36e3e1de 100644 --- a/rtc_base/virtual_socket_server.cc +++ b/rtc_base/virtual_socket_server.cc @@ -372,7 +372,7 @@ void VirtualSocket::OnMessage(Message* pmsg) { } else if (pmsg->message_id == MSG_ID_SIGNALREADEVENT) { signal_read_event = !recv_buffer_.empty(); } else { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); } } // Signal events without holding `mutex_`, to avoid recursive locking, as well @@ -588,12 +588,15 @@ uint16_t VirtualSocketServer::GetNextPort() { } void VirtualSocketServer::SetSendingBlocked(bool blocked) { - if (blocked == sending_blocked_) { - // Unchanged; nothing to do. - return; + { + webrtc::MutexLock lock(&mutex_); + if (blocked == sending_blocked_) { + // Unchanged; nothing to do. + return; + } + sending_blocked_ = blocked; } - sending_blocked_ = blocked; - if (!sending_blocked_) { + if (!blocked) { // Sending was blocked, but is now unblocked. This signal gives sockets a // chance to fire SignalWriteEvent, and for TCP, send buffered data. SignalReadyToSend(); @@ -846,12 +849,12 @@ void VirtualSocketServer::CancelConnects(VirtualSocket* socket) { MessageAddress* data = static_cast(it->pdata); SocketAddress local_addr = socket->GetLocalAddress(); // Lookup remote side. - VirtualSocket* socket = LookupConnection(local_addr, data->addr); - if (socket) { + VirtualSocket* lookup_socket = LookupConnection(local_addr, data->addr); + if (lookup_socket) { // Server socket, remote side is a socket retreived by // accept. Accepted sockets are not bound so we will not // find it by looking in the bindings table. - Disconnect(socket); + Disconnect(lookup_socket); RemoveConnection(local_addr, data->addr); } else { Disconnect(data->addr); @@ -877,31 +880,27 @@ int VirtualSocketServer::SendUdp(VirtualSocket* socket, const char* data, size_t data_size, const SocketAddress& remote_addr) { - ++sent_packets_; - if (sending_blocked_) { - socket->SetToBlocked(); - return -1; - } - - if (data_size > largest_seen_udp_payload_) { - if (data_size > 1000) { - RTC_LOG(LS_VERBOSE) << "Largest UDP seen is " << data_size; + { + webrtc::MutexLock lock(&mutex_); + ++sent_packets_; + if (sending_blocked_) { + socket->SetToBlocked(); + return -1; } - largest_seen_udp_payload_ = data_size; - } - // See if we want to drop this packet. - if (data_size > max_udp_payload_) { - RTC_LOG(LS_VERBOSE) << "Dropping too large UDP payload of size " - << data_size << ", UDP payload limit is " - << max_udp_payload_; - // Return as if send was successful; packet disappears. - return data_size; - } + // See if we want to drop this packet. + if (data_size > max_udp_payload_) { + RTC_LOG(LS_VERBOSE) << "Dropping too large UDP payload of size " + << data_size << ", UDP payload limit is " + << max_udp_payload_; + // Return as if send was successful; packet disappears. + return data_size; + } - if (Random() < drop_prob_) { - RTC_LOG(LS_VERBOSE) << "Dropping packet: bad luck"; - return static_cast(data_size); + if (Random() < drop_prob_) { + RTC_LOG(LS_VERBOSE) << "Dropping packet: bad luck"; + return static_cast(data_size); + } } VirtualSocket* recipient = LookupBinding(remote_addr); @@ -938,11 +937,13 @@ int VirtualSocketServer::SendUdp(VirtualSocket* socket, // NOTE: There are better algorithms for maintaining such a queue (such as // "Derivative Random Drop"); however, this algorithm is a more accurate // simulation of what a normal network would do. - - size_t packet_size = data_size + UDP_HEADER_SIZE; - if (network_size + packet_size > network_capacity_) { - RTC_LOG(LS_VERBOSE) << "Dropping packet: network capacity exceeded"; - return static_cast(data_size); + { + webrtc::MutexLock lock(&mutex_); + size_t packet_size = data_size + UDP_HEADER_SIZE; + if (network_size + packet_size > network_capacity_) { + RTC_LOG(LS_VERBOSE) << "Dropping packet: network capacity exceeded"; + return static_cast(data_size); + } } AddPacketToNetwork(socket, recipient, cur_time, data, data_size, @@ -953,11 +954,14 @@ int VirtualSocketServer::SendUdp(VirtualSocket* socket, } void VirtualSocketServer::SendTcp(VirtualSocket* socket) { - ++sent_packets_; - if (sending_blocked_) { - // Eventually the socket's buffer will fill and VirtualSocket::SendTcp will - // set EWOULDBLOCK. - return; + { + webrtc::MutexLock lock(&mutex_); + ++sent_packets_; + if (sending_blocked_) { + // Eventually the socket's buffer will fill and VirtualSocket::SendTcp + // will set EWOULDBLOCK. + return; + } } // TCP can't send more data than will fill up the receiver's buffer. @@ -978,7 +982,7 @@ void VirtualSocketServer::SendTcp(VirtualSocket* socket) { socket->PurgeNetworkPackets(cur_time); while (true) { - size_t available = recv_buffer_capacity_ - recipient->recv_buffer_size(); + size_t available = recv_buffer_capacity() - recipient->recv_buffer_size(); size_t max_data_size = std::min(available, TCP_MSS - TCP_HEADER_SIZE); size_t data_size = std::min(socket->send_buffer_size(), max_data_size); @@ -991,7 +995,7 @@ void VirtualSocketServer::SendTcp(VirtualSocket* socket) { socket->UpdateSend(data_size); } - socket->MaybeSignalWriteEvent(send_buffer_capacity_); + socket->MaybeSignalWriteEvent(send_buffer_capacity()); } void VirtualSocketServer::SendTcp(const SocketAddress& addr) { @@ -1032,6 +1036,7 @@ void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender, } uint32_t VirtualSocketServer::SendDelay(uint32_t size) { + webrtc::MutexLock lock(&mutex_); if (bandwidth_ == 0) return 0; else @@ -1060,6 +1065,7 @@ void PrintFunction(std::vector >* f) { #endif // void VirtualSocketServer::UpdateDelayDistribution() { + webrtc::MutexLock lock(&mutex_); delay_dist_ = CreateDistribution(delay_mean_, delay_stddev_, delay_samples_); } @@ -1245,4 +1251,62 @@ void VirtualSocketServer::SetDefaultSourceAddress(const IPAddress& from_addr) { } } +void VirtualSocketServer::set_bandwidth(uint32_t bandwidth) { + webrtc::MutexLock lock(&mutex_); + bandwidth_ = bandwidth; +} +void VirtualSocketServer::set_network_capacity(uint32_t capacity) { + webrtc::MutexLock lock(&mutex_); + network_capacity_ = capacity; +} + +uint32_t VirtualSocketServer::send_buffer_capacity() const { + webrtc::MutexLock lock(&mutex_); + return send_buffer_capacity_; +} +void VirtualSocketServer::set_send_buffer_capacity(uint32_t capacity) { + webrtc::MutexLock lock(&mutex_); + send_buffer_capacity_ = capacity; +} + +uint32_t VirtualSocketServer::recv_buffer_capacity() const { + webrtc::MutexLock lock(&mutex_); + return recv_buffer_capacity_; +} +void VirtualSocketServer::set_recv_buffer_capacity(uint32_t capacity) { + webrtc::MutexLock lock(&mutex_); + recv_buffer_capacity_ = capacity; +} + +void VirtualSocketServer::set_delay_mean(uint32_t delay_mean) { + webrtc::MutexLock lock(&mutex_); + delay_mean_ = delay_mean; +} +void VirtualSocketServer::set_delay_stddev(uint32_t delay_stddev) { + webrtc::MutexLock lock(&mutex_); + delay_stddev_ = delay_stddev; +} +void VirtualSocketServer::set_delay_samples(uint32_t delay_samples) { + webrtc::MutexLock lock(&mutex_); + delay_samples_ = delay_samples; +} + +void VirtualSocketServer::set_drop_probability(double drop_prob) { + RTC_DCHECK_GE(drop_prob, 0.0); + RTC_DCHECK_LE(drop_prob, 1.0); + + webrtc::MutexLock lock(&mutex_); + drop_prob_ = drop_prob; +} + +void VirtualSocketServer::set_max_udp_payload(size_t payload_size) { + webrtc::MutexLock lock(&mutex_); + max_udp_payload_ = payload_size; +} + +uint32_t VirtualSocketServer::sent_packets() const { + webrtc::MutexLock lock(&mutex_); + return sent_packets_; +} + } // namespace rtc diff --git a/rtc_base/virtual_socket_server.h b/rtc_base/virtual_socket_server.h index 77ddb76d72..b172567937 100644 --- a/rtc_base/virtual_socket_server.h +++ b/rtc_base/virtual_socket_server.h @@ -16,7 +16,6 @@ #include #include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" #include "rtc_base/event.h" #include "rtc_base/fake_clock.h" #include "rtc_base/message_handler.h" @@ -163,6 +162,9 @@ class VirtualSocketServer : public SocketServer { 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 @@ -172,60 +174,39 @@ class VirtualSocketServer : public SocketServer { // Limits the network bandwidth (maximum bytes per second). Zero means that // all sends occur instantly. Defaults to 0. - uint32_t bandwidth() const { return bandwidth_; } - void set_bandwidth(uint32_t bandwidth) { bandwidth_ = bandwidth; } + 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. - uint32_t network_capacity() const { return network_capacity_; } - void set_network_capacity(uint32_t capacity) { network_capacity_ = capacity; } + 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 { return send_buffer_capacity_; } - void set_send_buffer_capacity(uint32_t capacity) { - send_buffer_capacity_ = capacity; - } + 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 { return recv_buffer_capacity_; } - void set_recv_buffer_capacity(uint32_t capacity) { - recv_buffer_capacity_ = capacity; - } + 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. - uint32_t delay_mean() const { return delay_mean_; } - uint32_t delay_stddev() const { return delay_stddev_; } - uint32_t delay_samples() const { return delay_samples_; } - void set_delay_mean(uint32_t delay_mean) { delay_mean_ = delay_mean; } - void set_delay_stddev(uint32_t delay_stddev) { delay_stddev_ = delay_stddev; } - void set_delay_samples(uint32_t delay_samples) { - delay_samples_ = delay_samples; - } + 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(); + 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. - double drop_probability() { return drop_prob_; } - void set_drop_probability(double drop_prob) { - RTC_DCHECK_GE(drop_prob, 0.0); - RTC_DCHECK_LE(drop_prob, 1.0); - drop_prob_ = drop_prob; - } + 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. - size_t max_udp_payload() { return max_udp_payload_; } - void set_max_udp_payload(size_t payload_size) { - max_udp_payload_ = payload_size; - } - - size_t largest_seen_udp_payload() { return largest_seen_udp_payload_; } + 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. @@ -235,7 +216,7 @@ class VirtualSocketServer : public SocketServer { // // 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); + void SetSendingBlocked(bool blocked) RTC_LOCKS_EXCLUDED(mutex_); // SocketFactory: VirtualSocket* CreateSocket(int family, int type) override; @@ -281,7 +262,7 @@ class VirtualSocketServer : public SocketServer { // 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 { return sent_packets_; } + uint32_t sent_packets() const RTC_LOCKS_EXCLUDED(mutex_); // Assign IP and Port if application's address is unspecified. Also apply // `alternative_address_mapping_`. @@ -319,13 +300,13 @@ class VirtualSocketServer : public SocketServer { const SocketAddress& remote_addr); // Moves as much data as possible from the sender's buffer to the network - void SendTcp(VirtualSocket* socket); + void SendTcp(VirtualSocket* socket) RTC_LOCKS_EXCLUDED(mutex_); // Like above, but lookup sender by address. - void SendTcp(const SocketAddress& addr); + 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); + uint32_t SendDelay(uint32_t size) RTC_LOCKS_EXCLUDED(mutex_); // Cancel attempts to connect to a socket that is being closed. void CancelConnects(VirtualSocket* socket); @@ -416,31 +397,30 @@ class VirtualSocketServer : public SocketServer { IPAddress default_source_address_v4_; IPAddress default_source_address_v6_; - uint32_t bandwidth_; - uint32_t network_capacity_; - uint32_t send_buffer_capacity_; - uint32_t recv_buffer_capacity_; - uint32_t delay_mean_; - uint32_t delay_stddev_; - uint32_t delay_samples_; + mutable webrtc::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_ = 0; + 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_; + 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_ = 65507; - // The largest UDP payload seen so far. - size_t largest_seen_udp_payload_ = 0; + size_t max_udp_payload_ RTC_GUARDED_BY(mutex_) = 65507; - bool sending_blocked_ = false; - RTC_DISALLOW_COPY_AND_ASSIGN(VirtualSocketServer); + bool sending_blocked_ RTC_GUARDED_BY(mutex_) = false; }; } // namespace rtc diff --git a/rtc_base/win/scoped_com_initializer.cc b/rtc_base/win/scoped_com_initializer.cc index 81079fb54c..4b56772ebf 100644 --- a/rtc_base/win/scoped_com_initializer.cc +++ b/rtc_base/win/scoped_com_initializer.cc @@ -16,13 +16,13 @@ namespace webrtc { ScopedCOMInitializer::ScopedCOMInitializer() { - RTC_DLOG(INFO) << "Single-Threaded Apartment (STA) COM thread"; + RTC_DLOG(LS_INFO) << "Single-Threaded Apartment (STA) COM thread"; Initialize(COINIT_APARTMENTTHREADED); } // Constructor for MTA initialization. ScopedCOMInitializer::ScopedCOMInitializer(SelectMTA mta) { - RTC_DLOG(INFO) << "Multi-Threaded Apartment (MTA) COM thread"; + RTC_DLOG(LS_INFO) << "Multi-Threaded Apartment (MTA) COM thread"; Initialize(COINIT_MULTITHREADED); } @@ -46,10 +46,10 @@ void ScopedCOMInitializer::Initialize(COINIT init) { // successful call to CoInitializeEx, including any call that returns // S_FALSE, must be balanced by a corresponding call to CoUninitialize. if (hr_ == S_OK) { - RTC_DLOG(INFO) + RTC_DLOG(LS_INFO) << "The COM library was initialized successfully on this thread"; } else if (hr_ == S_FALSE) { - RTC_DLOG(WARNING) + RTC_DLOG(LS_WARNING) << "The COM library is already initialized on this thread"; } } diff --git a/rtc_base/win/windows_version.cc b/rtc_base/win/windows_version.cc index 0ed8957bd1..80e49f2a16 100644 --- a/rtc_base/win/windows_version.cc +++ b/rtc_base/win/windows_version.cc @@ -215,7 +215,7 @@ Version MajorMinorBuildToVersion(int major, int minor, int build) { return VERSION_WIN10_20H1; } } else if (major > 6) { - RTC_NOTREACHED(); + RTC_DCHECK_NOTREACHED(); return VERSION_WIN_LAST; } @@ -275,9 +275,12 @@ OSInfo::OSInfo() // Windows 8 OS version value (6.2). Once an application is manifested for a // given operating system version, GetVersionEx() will always return the // version that the application is manifested for in future releases. - // https://docs.microsoft.com/en-us/windows/desktop/SysInfo/targeting-your-application-at-windows-8-1. - // https://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe. + // https://docs.microsoft.com/en-us/windows/desktop/SysInfo/targeting-your-application-at-windows-8-1 + // https://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe +#pragma warning(push) +#pragma warning(disable : 4996) ::GetVersionExW(reinterpret_cast(&version_info)); +#pragma warning(pop) version_number_.major = version_info.dwMajorVersion; version_number_.minor = version_info.dwMinorVersion; version_number_.build = version_info.dwBuildNumber; diff --git a/rtc_base/win/windows_version.h b/rtc_base/win/windows_version.h index dbb0d8eb58..3636eabf19 100644 --- a/rtc_base/win/windows_version.h +++ b/rtc_base/win/windows_version.h @@ -15,8 +15,6 @@ #include -#include "rtc_base/constructor_magic.h" - typedef void* HANDLE; namespace rtc { @@ -105,6 +103,9 @@ class OSInfo { WOW64_UNKNOWN, }; + OSInfo(const OSInfo&) = delete; + OSInfo& operator=(const OSInfo&) = delete; + static OSInfo* GetInstance(); Version version() const { return version_; } @@ -140,8 +141,6 @@ class OSInfo { size_t allocation_granularity_; WOW64Status wow64_status_; std::string processor_model_name_; - - RTC_DISALLOW_COPY_AND_ASSIGN(OSInfo); }; // Because this is by far the most commonly-requested value from the above diff --git a/rtc_base/win/windows_version_unittest.cc b/rtc_base/win/windows_version_unittest.cc index 9e582e549f..e1cd920157 100644 --- a/rtc_base/win/windows_version_unittest.cc +++ b/rtc_base/win/windows_version_unittest.cc @@ -18,11 +18,11 @@ namespace rtc_win { namespace { void MethodSupportedOnWin10AndLater() { - RTC_DLOG(INFO) << "MethodSupportedOnWin10AndLater"; + RTC_DLOG(LS_INFO) << "MethodSupportedOnWin10AndLater"; } void MethodNotSupportedOnWin10AndLater() { - RTC_DLOG(INFO) << "MethodNotSupportedOnWin10AndLater"; + RTC_DLOG(LS_INFO) << "MethodNotSupportedOnWin10AndLater"; } // Use global GetVersion() and use it in a way a user would typically use it @@ -39,7 +39,7 @@ TEST(WindowsVersion, GetVersionGlobalScopeAccessor) { TEST(WindowsVersion, ProcessorModelName) { std::string name = OSInfo::GetInstance()->processor_model_name(); EXPECT_FALSE(name.empty()); - RTC_DLOG(INFO) << "processor_model_name: " << name; + RTC_DLOG(LS_INFO) << "processor_model_name: " << name; } } // namespace diff --git a/rtc_base/win32.cc b/rtc_base/win32.cc index 8d8fe27369..b44e513026 100644 --- a/rtc_base/win32.cc +++ b/rtc_base/win32.cc @@ -311,28 +311,4 @@ int inet_pton_v6(const char* src, void* dst) { return 1; } -// Windows UWP applications cannot obtain versioning information from -// the sandbox with intention (as behehaviour based on OS versioning rather -// than feature discovery / compilation flags is discoraged and Windows -// 10 is living continously updated version unlike previous versions -// of Windows). -#if !defined(WINUWP) - -bool GetOsVersion(int* major, int* minor, int* build) { - OSVERSIONINFO info = {0}; - info.dwOSVersionInfoSize = sizeof(info); - if (GetVersionEx(&info)) { - if (major) - *major = info.dwMajorVersion; - if (minor) - *minor = info.dwMinorVersion; - if (build) - *build = info.dwBuildNumber; - return true; - } - return false; -} - -#endif // !defined(WINUWP) - } // namespace rtc diff --git a/rtc_base/win32.h b/rtc_base/win32.h index c90296ebbb..6e8d2873aa 100644 --- a/rtc_base/win32.h +++ b/rtc_base/win32.h @@ -20,11 +20,10 @@ #define NOMINMAX #endif -// clang-format off -// clang formating would change include order. -#include // must come first +#include + +// Must be after winsock2.h. #include -// clang-format on typedef int socklen_t; @@ -39,66 +38,11 @@ typedef struct _TOKEN_MANDATORY_LABEL { #undef SetPort -#include - namespace rtc { 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); -enum WindowsMajorVersions { - kWindows2000 = 5, - kWindowsVista = 6, - kWindows10 = 10, -}; - -#if !defined(WINUWP) -bool GetOsVersion(int* major, int* minor, int* build); - -inline bool IsWindowsVistaOrLater() { - int major; - return (GetOsVersion(&major, nullptr, nullptr) && major >= kWindowsVista); -} - -inline bool IsWindowsXpOrLater() { - int major, minor; - return (GetOsVersion(&major, &minor, nullptr) && - (major >= kWindowsVista || (major == kWindows2000 && minor >= 1))); -} - -inline bool IsWindows8OrLater() { - int major, minor; - return (GetOsVersion(&major, &minor, nullptr) && - (major > kWindowsVista || (major == kWindowsVista && minor >= 2))); -} - -inline bool IsWindows10OrLater() { - int major; - return (GetOsVersion(&major, nullptr, nullptr) && (major >= kWindows10)); -} - -#else - -// When targetting WinUWP the OS must be Windows 10 (or greater) as lesser -// Windows OS targets are not supported. -inline bool IsWindowsVistaOrLater() { - return true; -} - -inline bool IsWindowsXpOrLater() { - return true; -} - -inline bool IsWindows8OrLater() { - return true; -} - -inline bool IsWindows10OrLater() { - return true; -} - -#endif // !defined(WINUWP) - } // namespace rtc #endif // RTC_BASE_WIN32_H_ diff --git a/rtc_base/win32_socket_server.cc b/rtc_base/win32_socket_server.cc deleted file mode 100644 index 835d1a7081..0000000000 --- a/rtc_base/win32_socket_server.cc +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtc_base/win32_socket_server.h" - -#include // NOLINT - -#include - -#include "rtc_base/byte_order.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/time_utils.h" // For Time, TimeSince -#include "rtc_base/win32_window.h" - -namespace rtc { - -/////////////////////////////////////////////////////////////////////////////// -// Win32Socket -/////////////////////////////////////////////////////////////////////////////// - -// TODO: Enable for production builds also? Use FormatMessage? -#if !defined(NDEBUG) -LPCSTR WSAErrorToString(int error, LPCSTR* description_result) { - LPCSTR string = "Unspecified"; - LPCSTR description = "Unspecified description"; - switch (error) { - case ERROR_SUCCESS: - string = "SUCCESS"; - description = "Operation succeeded"; - break; - case WSAEWOULDBLOCK: - string = "WSAEWOULDBLOCK"; - description = "Using a non-blocking socket, will notify later"; - break; - case WSAEACCES: - string = "WSAEACCES"; - description = "Access denied, or sharing violation"; - break; - case WSAEADDRNOTAVAIL: - string = "WSAEADDRNOTAVAIL"; - description = "Address is not valid in this context"; - break; - case WSAENETDOWN: - string = "WSAENETDOWN"; - description = "Network is down"; - break; - case WSAENETUNREACH: - string = "WSAENETUNREACH"; - description = "Network is up, but unreachable"; - break; - case WSAENETRESET: - string = "WSANETRESET"; - description = "Connection has been reset due to keep-alive activity"; - break; - case WSAECONNABORTED: - string = "WSAECONNABORTED"; - description = "Aborted by host"; - break; - case WSAECONNRESET: - string = "WSAECONNRESET"; - description = "Connection reset by host"; - break; - case WSAETIMEDOUT: - string = "WSAETIMEDOUT"; - description = "Timed out, host failed to respond"; - break; - case WSAECONNREFUSED: - string = "WSAECONNREFUSED"; - description = "Host actively refused connection"; - break; - case WSAEHOSTDOWN: - string = "WSAEHOSTDOWN"; - description = "Host is down"; - break; - case WSAEHOSTUNREACH: - string = "WSAEHOSTUNREACH"; - description = "Host is unreachable"; - break; - case WSAHOST_NOT_FOUND: - string = "WSAHOST_NOT_FOUND"; - description = "No such host is known"; - break; - } - if (description_result) { - *description_result = description; - } - return string; -} - -void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) { - LPCSTR description_string; - LPCSTR error_string = WSAErrorToString(error, &description_string); - RTC_LOG(LS_INFO) << context << " = " << error << " (" << error_string << ":" - << description_string << ") [" << address.ToString() << "]"; -} -#else -void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {} -#endif - -///////////////////////////////////////////////////////////////////////////// -// Win32Socket::EventSink -///////////////////////////////////////////////////////////////////////////// - -#define WM_SOCKETNOTIFY (WM_USER + 50) -#define WM_DNSNOTIFY (WM_USER + 51) - -struct Win32Socket::DnsLookup { - HANDLE handle; - uint16_t port; - char buffer[MAXGETHOSTSTRUCT]; -}; - -class Win32Socket::EventSink : public Win32Window { - public: - explicit EventSink(Win32Socket* parent) : parent_(parent) {} - - void Dispose(); - - bool OnMessage(UINT uMsg, - WPARAM wParam, - LPARAM lParam, - LRESULT& result) override; - void OnNcDestroy() override; - - private: - bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result); - bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result); - - Win32Socket* parent_; -}; - -void Win32Socket::EventSink::Dispose() { - parent_ = nullptr; - if (::IsWindow(handle())) { - ::DestroyWindow(handle()); - } else { - delete this; - } -} - -bool Win32Socket::EventSink::OnMessage(UINT uMsg, - WPARAM wParam, - LPARAM lParam, - LRESULT& result) { - switch (uMsg) { - case WM_SOCKETNOTIFY: - case WM_TIMER: - return OnSocketNotify(uMsg, wParam, lParam, result); - case WM_DNSNOTIFY: - return OnDnsNotify(wParam, lParam, result); - } - return false; -} - -bool Win32Socket::EventSink::OnSocketNotify(UINT uMsg, - WPARAM wParam, - LPARAM lParam, - LRESULT& result) { - result = 0; - - int wsa_event = WSAGETSELECTEVENT(lParam); - int wsa_error = WSAGETSELECTERROR(lParam); - - // Treat connect timeouts as close notifications - if (uMsg == WM_TIMER) { - wsa_event = FD_CLOSE; - wsa_error = WSAETIMEDOUT; - } - - if (parent_) - parent_->OnSocketNotify(static_cast(wParam), wsa_event, wsa_error); - return true; -} - -bool Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, - LPARAM lParam, - LRESULT& result) { - result = 0; - - int error = WSAGETASYNCERROR(lParam); - if (parent_) - parent_->OnDnsNotify(reinterpret_cast(wParam), error); - return true; -} - -void Win32Socket::EventSink::OnNcDestroy() { - if (parent_) { - RTC_LOG(LS_ERROR) << "EventSink hwnd is being destroyed, but the event sink" - " hasn't yet been disposed."; - } else { - delete this; - } -} - -///////////////////////////////////////////////////////////////////////////// -// Win32Socket -///////////////////////////////////////////////////////////////////////////// - -Win32Socket::Win32Socket() - : socket_(INVALID_SOCKET), - error_(0), - state_(CS_CLOSED), - connect_time_(0), - closing_(false), - close_error_(0), - sink_(nullptr), - dns_(nullptr) {} - -Win32Socket::~Win32Socket() { - Close(); -} - -bool Win32Socket::CreateT(int family, int type) { - Close(); - int proto = (SOCK_DGRAM == type) ? IPPROTO_UDP : IPPROTO_TCP; - socket_ = ::WSASocket(family, type, proto, nullptr, 0, 0); - if (socket_ == INVALID_SOCKET) { - UpdateLastError(); - return false; - } - if ((SOCK_DGRAM == type) && !SetAsync(FD_READ | FD_WRITE)) { - return false; - } - return true; -} - -int Win32Socket::Attach(SOCKET s) { - RTC_DCHECK(socket_ == INVALID_SOCKET); - if (socket_ != INVALID_SOCKET) - return SOCKET_ERROR; - - RTC_DCHECK(s != INVALID_SOCKET); - if (s == INVALID_SOCKET) - return SOCKET_ERROR; - - socket_ = s; - state_ = CS_CONNECTED; - - if (!SetAsync(FD_READ | FD_WRITE | FD_CLOSE)) - return SOCKET_ERROR; - - return 0; -} - -void Win32Socket::SetTimeout(int ms) { - if (sink_) - ::SetTimer(sink_->handle(), 1, ms, 0); -} - -SocketAddress Win32Socket::GetLocalAddress() const { - sockaddr_storage addr = {0}; - socklen_t addrlen = sizeof(addr); - int result = - ::getsockname(socket_, reinterpret_cast(&addr), &addrlen); - SocketAddress address; - if (result >= 0) { - SocketAddressFromSockAddrStorage(addr, &address); - } else { - RTC_LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket=" - << socket_; - } - return address; -} - -SocketAddress Win32Socket::GetRemoteAddress() const { - sockaddr_storage addr = {0}; - socklen_t addrlen = sizeof(addr); - int result = - ::getpeername(socket_, reinterpret_cast(&addr), &addrlen); - SocketAddress address; - if (result >= 0) { - SocketAddressFromSockAddrStorage(addr, &address); - } else { - RTC_LOG(LS_WARNING) - << "GetRemoteAddress: unable to get remote addr, socket=" << socket_; - } - return address; -} - -int Win32Socket::Bind(const SocketAddress& addr) { - RTC_DCHECK(socket_ != INVALID_SOCKET); - if (socket_ == INVALID_SOCKET) - return SOCKET_ERROR; - - sockaddr_storage saddr; - size_t len = addr.ToSockAddrStorage(&saddr); - int err = ::bind(socket_, reinterpret_cast(&saddr), - static_cast(len)); - UpdateLastError(); - return err; -} - -int Win32Socket::Connect(const SocketAddress& addr) { - if (state_ != CS_CLOSED) { - SetError(EALREADY); - return SOCKET_ERROR; - } - - if (!addr.IsUnresolvedIP()) { - return DoConnect(addr); - } - - RTC_LOG_F(LS_INFO) << "async dns lookup (" << addr.hostname() << ")"; - DnsLookup* dns = new DnsLookup; - if (!sink_) { - // Explicitly create the sink ourselves here; we can't rely on SetAsync - // because we don't have a socket_ yet. - CreateSink(); - } - // TODO: Replace with IPv6 compatible lookup. - dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY, - addr.hostname().c_str(), dns->buffer, - sizeof(dns->buffer)); - - if (!dns->handle) { - RTC_LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError(); - delete dns; - UpdateLastError(); - Close(); - return SOCKET_ERROR; - } - - dns->port = addr.port(); - dns_ = dns; - state_ = CS_CONNECTING; - return 0; -} - -int Win32Socket::DoConnect(const SocketAddress& addr) { - if ((socket_ == INVALID_SOCKET) && !CreateT(addr.family(), SOCK_STREAM)) { - return SOCKET_ERROR; - } - if (!SetAsync(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE)) { - return SOCKET_ERROR; - } - - sockaddr_storage saddr = {0}; - size_t len = addr.ToSockAddrStorage(&saddr); - connect_time_ = Time(); - int result = connect(socket_, reinterpret_cast(&saddr), - static_cast(len)); - if (result != SOCKET_ERROR) { - state_ = CS_CONNECTED; - } else { - int code = WSAGetLastError(); - if (code == WSAEWOULDBLOCK) { - state_ = CS_CONNECTING; - } else { - ReportWSAError("WSAAsync:connect", code, addr); - error_ = code; - Close(); - return SOCKET_ERROR; - } - } - addr_ = addr; - - return 0; -} - -int Win32Socket::GetError() const { - return error_; -} - -void Win32Socket::SetError(int error) { - error_ = error; -} - -Socket::ConnState Win32Socket::GetState() const { - return state_; -} - -int Win32Socket::GetOption(Option opt, int* value) { - int slevel; - int sopt; - if (TranslateOption(opt, &slevel, &sopt) == -1) - return -1; - - char* p = reinterpret_cast(value); - int optlen = sizeof(value); - return ::getsockopt(socket_, slevel, sopt, p, &optlen); -} - -int Win32Socket::SetOption(Option opt, int value) { - int slevel; - int sopt; - if (TranslateOption(opt, &slevel, &sopt) == -1) - return -1; - - const char* p = reinterpret_cast(&value); - return ::setsockopt(socket_, slevel, sopt, p, sizeof(value)); -} - -int Win32Socket::Send(const void* buffer, size_t length) { - int sent = ::send(socket_, reinterpret_cast(buffer), - static_cast(length), 0); - UpdateLastError(); - return sent; -} - -int Win32Socket::SendTo(const void* buffer, - size_t length, - const SocketAddress& addr) { - sockaddr_storage saddr; - size_t addr_len = addr.ToSockAddrStorage(&saddr); - int sent = ::sendto( - socket_, reinterpret_cast(buffer), static_cast(length), - 0, reinterpret_cast(&saddr), static_cast(addr_len)); - UpdateLastError(); - return sent; -} - -int Win32Socket::Recv(void* buffer, size_t length, int64_t* timestamp) { - if (timestamp) { - *timestamp = -1; - } - int received = - ::recv(socket_, static_cast(buffer), static_cast(length), 0); - UpdateLastError(); - if (closing_ && received <= static_cast(length)) - PostClosed(); - return received; -} - -int Win32Socket::RecvFrom(void* buffer, - size_t length, - SocketAddress* out_addr, - int64_t* timestamp) { - if (timestamp) { - *timestamp = -1; - } - sockaddr_storage saddr; - socklen_t addr_len = sizeof(saddr); - int received = - ::recvfrom(socket_, static_cast(buffer), static_cast(length), - 0, reinterpret_cast(&saddr), &addr_len); - UpdateLastError(); - if (received != SOCKET_ERROR) - SocketAddressFromSockAddrStorage(saddr, out_addr); - if (closing_ && received <= static_cast(length)) - PostClosed(); - return received; -} - -int Win32Socket::Listen(int backlog) { - int err = ::listen(socket_, backlog); - if (!SetAsync(FD_ACCEPT)) - return SOCKET_ERROR; - - UpdateLastError(); - if (err == 0) - state_ = CS_CONNECTING; - return err; -} - -Win32Socket* Win32Socket::Accept(SocketAddress* out_addr) { - sockaddr_storage saddr; - socklen_t addr_len = sizeof(saddr); - SOCKET s = ::accept(socket_, reinterpret_cast(&saddr), &addr_len); - UpdateLastError(); - if (s == INVALID_SOCKET) - return nullptr; - if (out_addr) - SocketAddressFromSockAddrStorage(saddr, out_addr); - Win32Socket* socket = new Win32Socket; - if (0 == socket->Attach(s)) - return socket; - delete socket; - return nullptr; -} - -int Win32Socket::Close() { - int err = 0; - if (socket_ != INVALID_SOCKET) { - err = ::closesocket(socket_); - socket_ = INVALID_SOCKET; - closing_ = false; - close_error_ = 0; - UpdateLastError(); - } - if (dns_) { - WSACancelAsyncRequest(dns_->handle); - delete dns_; - dns_ = nullptr; - } - if (sink_) { - sink_->Dispose(); - sink_ = nullptr; - } - addr_.Clear(); - state_ = CS_CLOSED; - return err; -} - -void Win32Socket::CreateSink() { - RTC_DCHECK(nullptr == sink_); - - // Create window - sink_ = new EventSink(this); - sink_->Create(nullptr, L"EventSink", 0, 0, 0, 0, 10, 10); -} - -bool Win32Socket::SetAsync(int events) { - if (nullptr == sink_) { - CreateSink(); - RTC_DCHECK(nullptr != sink_); - } - - // start the async select - if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events) == - SOCKET_ERROR) { - UpdateLastError(); - Close(); - return false; - } - - return true; -} - -bool Win32Socket::HandleClosed(int close_error) { - // WM_CLOSE will be received before all data has been read, so we need to - // hold on to it until the read buffer has been drained. - char ch; - closing_ = true; - close_error_ = close_error; - return (::recv(socket_, &ch, 1, MSG_PEEK) <= 0); -} - -void Win32Socket::PostClosed() { - // If we see that the buffer is indeed drained, then send the close. - closing_ = false; - ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY, socket_, - WSAMAKESELECTREPLY(FD_CLOSE, close_error_)); -} - -void Win32Socket::UpdateLastError() { - error_ = WSAGetLastError(); -} - -int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) { - switch (opt) { - case OPT_DONTFRAGMENT: - *slevel = IPPROTO_IP; - *sopt = IP_DONTFRAGMENT; - break; - case OPT_RCVBUF: - *slevel = SOL_SOCKET; - *sopt = SO_RCVBUF; - break; - case OPT_SNDBUF: - *slevel = SOL_SOCKET; - *sopt = SO_SNDBUF; - break; - case OPT_NODELAY: - *slevel = IPPROTO_TCP; - *sopt = TCP_NODELAY; - break; - case OPT_DSCP: - RTC_LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; - return -1; - default: - RTC_NOTREACHED(); - return -1; - } - return 0; -} - -void Win32Socket::OnSocketNotify(SOCKET socket, int event, int error) { - // Ignore events if we're already closed. - if (socket != socket_) - return; - - error_ = error; - switch (event) { - case FD_CONNECT: - if (error != ERROR_SUCCESS) { - ReportWSAError("WSAAsync:connect notify", error, addr_); -#if !defined(NDEBUG) - int64_t duration = TimeSince(connect_time_); - RTC_LOG(LS_INFO) << "WSAAsync:connect error (" << duration - << " ms), faking close"; -#endif - state_ = CS_CLOSED; - // If you get an error connecting, close doesn't really do anything - // and it certainly doesn't send back any close notification, but - // we really only maintain a few states, so it is easiest to get - // back into a known state by pretending that a close happened, even - // though the connect event never did occur. - SignalCloseEvent(this, error); - } else { -#if !defined(NDEBUG) - int64_t duration = TimeSince(connect_time_); - RTC_LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)"; -#endif - state_ = CS_CONNECTED; - SignalConnectEvent(this); - } - break; - - case FD_ACCEPT: - case FD_READ: - if (error != ERROR_SUCCESS) { - ReportWSAError("WSAAsync:read notify", error, addr_); - } else { - SignalReadEvent(this); - } - break; - - case FD_WRITE: - if (error != ERROR_SUCCESS) { - ReportWSAError("WSAAsync:write notify", error, addr_); - } else { - SignalWriteEvent(this); - } - break; - - case FD_CLOSE: - if (HandleClosed(error)) { - ReportWSAError("WSAAsync:close notify", error, addr_); - state_ = CS_CLOSED; - SignalCloseEvent(this, error); - } - break; - } -} - -void Win32Socket::OnDnsNotify(HANDLE task, int error) { - if (!dns_ || dns_->handle != task) - return; - - uint32_t ip = 0; - if (error == 0) { - hostent* pHost = reinterpret_cast(dns_->buffer); - uint32_t net_ip = *reinterpret_cast(pHost->h_addr_list[0]); - ip = NetworkToHost32(net_ip); - } - - RTC_LOG_F(LS_INFO) << "(" << IPAddress(ip).ToSensitiveString() << ", " - << error << ")"; - - if (error == 0) { - SocketAddress address(ip, dns_->port); - error = DoConnect(address); - } else { - Close(); - } - - if (error) { - error_ = error; - SignalCloseEvent(this, error_); - } else { - delete dns_; - dns_ = nullptr; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Win32SocketServer -// Provides cricket base services on top of a win32 gui thread -/////////////////////////////////////////////////////////////////////////////// - -static UINT s_wm_wakeup_id = 0; -const wchar_t Win32SocketServer::kWindowName[] = L"libjingle Message Window"; - -Win32SocketServer::Win32SocketServer() - : wnd_(this), posted_(false), hdlg_(nullptr) { - if (s_wm_wakeup_id == 0) - s_wm_wakeup_id = RegisterWindowMessageW(L"WM_WAKEUP"); - if (!wnd_.Create(nullptr, kWindowName, 0, 0, 0, 0, 0, 0)) { - RTC_LOG_GLE(LS_ERROR) << "Failed to create message window."; - } -} - -Win32SocketServer::~Win32SocketServer() { - if (wnd_.handle() != nullptr) { - KillTimer(wnd_.handle(), 1); - wnd_.Destroy(); - } -} - -Socket* Win32SocketServer::CreateSocket(int family, int type) { - Win32Socket* socket = new Win32Socket; - if (socket->CreateT(family, type)) { - return socket; - } - delete socket; - return nullptr; -} - -void Win32SocketServer::SetMessageQueue(Thread* queue) { - message_queue_ = queue; -} - -bool Win32SocketServer::Wait(int cms, bool process_io) { - BOOL b; - if (process_io) { - // Spin the Win32 message pump at least once, and as long as requested. - // This is the Thread::ProcessMessages case. - uint32_t start = Time(); - do { - MSG msg; - SetTimer(wnd_.handle(), 0, cms, nullptr); - // Get the next available message. If we have a modeless dialog, give - // give the message to IsDialogMessage, which will return true if it - // was a message for the dialog that it handled internally. - // Otherwise, dispatch as usual via Translate/DispatchMessage. - b = GetMessage(&msg, nullptr, 0, 0); - if (b == -1) { - RTC_LOG_GLE(LS_ERROR) << "GetMessage failed."; - return false; - } else if (b) { - if (!hdlg_ || !IsDialogMessage(hdlg_, &msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - KillTimer(wnd_.handle(), 0); - } while (b && TimeSince(start) < cms); - } else if (cms != 0) { - // Sit and wait forever for a WakeUp. This is the Thread::Send case. - RTC_DCHECK(cms == -1); - MSG msg; - b = GetMessage(&msg, nullptr, s_wm_wakeup_id, s_wm_wakeup_id); - { - webrtc::MutexLock lock(&mutex_); - posted_ = false; - } - } else { - // No-op (cms == 0 && !process_io). This is the Pump case. - b = TRUE; - } - return (b != FALSE); -} - -void Win32SocketServer::WakeUp() { - if (wnd_.handle()) { - // Set the "message pending" flag, if not already set. - { - webrtc::MutexLock lock(&mutex_); - if (posted_) - return; - posted_ = true; - } - - PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0); - } -} - -void Win32SocketServer::Pump() { - // Clear the "message pending" flag. - { - webrtc::MutexLock lock(&mutex_); - posted_ = false; - } - - // Dispatch all the messages that are currently in our queue. If new messages - // are posted during the dispatch, they will be handled in the next Pump. - // We use max(1, ...) to make sure we try to dispatch at least once, since - // this allow us to process "sent" messages, not included in the size() count. - Message msg; - for (size_t max_messages_to_process = - std::max(1, message_queue_->size()); - max_messages_to_process > 0 && message_queue_->Get(&msg, 0, false); - --max_messages_to_process) { - message_queue_->Dispatch(&msg); - } - - // Anything remaining? - int delay = message_queue_->GetDelay(); - if (delay == -1) { - KillTimer(wnd_.handle(), 1); - } else { - SetTimer(wnd_.handle(), 1, delay, nullptr); - } -} - -bool Win32SocketServer::MessageWindow::OnMessage(UINT wm, - WPARAM wp, - LPARAM lp, - LRESULT& lr) { - bool handled = false; - if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) { - ss_->Pump(); - lr = 0; - handled = true; - } - return handled; -} - -Win32Thread::Win32Thread(SocketServer* ss) : Thread(ss), id_(0) {} - -Win32Thread::~Win32Thread() { - Stop(); -} - -void Win32Thread::Run() { - id_ = GetCurrentThreadId(); - Thread::Run(); - id_ = 0; -} - -void Win32Thread::Quit() { - PostThreadMessage(id_, WM_QUIT, 0, 0); -} - -} // namespace rtc diff --git a/rtc_base/win32_socket_server.h b/rtc_base/win32_socket_server.h deleted file mode 100644 index 3e7d7286ab..0000000000 --- a/rtc_base/win32_socket_server.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTC_BASE_WIN32_SOCKET_SERVER_H_ -#define RTC_BASE_WIN32_SOCKET_SERVER_H_ - -#if defined(WEBRTC_WIN) -#include "rtc_base/socket.h" -#include "rtc_base/socket_factory.h" -#include "rtc_base/socket_server.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/thread.h" -#include "rtc_base/win32_window.h" - -namespace rtc { - -/////////////////////////////////////////////////////////////////////////////// -// Win32Socket -/////////////////////////////////////////////////////////////////////////////// - -class Win32Socket : public Socket { - public: - Win32Socket(); - ~Win32Socket() override; - - bool CreateT(int family, int type); - - int Attach(SOCKET s); - void SetTimeout(int ms); - - // Socket Interface - SocketAddress GetLocalAddress() const override; - SocketAddress GetRemoteAddress() const override; - int Bind(const SocketAddress& addr) override; - int Connect(const SocketAddress& addr) override; - int Send(const void* buffer, size_t length) override; - int SendTo(const void* buffer, - size_t length, - const SocketAddress& addr) override; - int Recv(void* buffer, size_t length, int64_t* timestamp) override; - int RecvFrom(void* buffer, - size_t length, - SocketAddress* out_addr, - int64_t* timestamp) override; - int Listen(int backlog) override; - Win32Socket* Accept(SocketAddress* out_addr) 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; - - private: - void CreateSink(); - bool SetAsync(int events); - int DoConnect(const SocketAddress& addr); - bool HandleClosed(int close_error); - void PostClosed(); - void UpdateLastError(); - static int TranslateOption(Option opt, int* slevel, int* sopt); - - void OnSocketNotify(SOCKET socket, int event, int error); - void OnDnsNotify(HANDLE task, int error); - - SOCKET socket_; - int error_; - ConnState state_; - SocketAddress addr_; // address that we connected to (see DoConnect) - uint32_t connect_time_; - bool closing_; - int close_error_; - - class EventSink; - friend class EventSink; - EventSink* sink_; - - struct DnsLookup; - DnsLookup* dns_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Win32SocketServer -/////////////////////////////////////////////////////////////////////////////// - -class Win32SocketServer : public SocketServer { - public: - Win32SocketServer(); - ~Win32SocketServer() override; - - void set_modeless_dialog(HWND hdlg) { hdlg_ = hdlg; } - - // SocketServer Interface - Socket* CreateSocket(int family, int type) override; - - void SetMessageQueue(Thread* queue) override; - bool Wait(int cms, bool process_io) override; - void WakeUp() override; - - void Pump(); - - HWND handle() { return wnd_.handle(); } - - private: - class MessageWindow : public Win32Window { - public: - explicit MessageWindow(Win32SocketServer* ss) : ss_(ss) {} - - private: - bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result) override; - Win32SocketServer* ss_; - }; - - static const wchar_t kWindowName[]; - Thread* message_queue_; - MessageWindow wnd_; - webrtc::Mutex mutex_; - bool posted_; - HWND hdlg_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Win32Thread. Automatically pumps Windows messages. -/////////////////////////////////////////////////////////////////////////////// - -class Win32Thread : public Thread { - public: - explicit Win32Thread(SocketServer* ss); - ~Win32Thread() override; - - void Run() override; - void Quit() override; - - private: - DWORD id_; -}; - -/////////////////////////////////////////////////////////////////////////////// - -} // namespace rtc - -#endif // WEBRTC_WIN - -#endif // RTC_BASE_WIN32_SOCKET_SERVER_H_ diff --git a/rtc_base/win32_socket_server_unittest.cc b/rtc_base/win32_socket_server_unittest.cc deleted file mode 100644 index f75f51312c..0000000000 --- a/rtc_base/win32_socket_server_unittest.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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. - */ -#include "rtc_base/win32_socket_server.h" - -#include "rtc_base/gunit.h" -#include "rtc_base/socket_unittest.h" -#include "rtc_base/thread.h" - -namespace rtc { - -// Test that Win32SocketServer::Wait works as expected. -TEST(Win32SocketServerTest, TestWait) { - Win32SocketServer server; - uint32_t start = Time(); - server.Wait(1000, true); - EXPECT_GE(TimeSince(start), 1000); -} - -// Test that Win32Socket::Pump does not touch general Windows messages. -TEST(Win32SocketServerTest, TestPump) { - Win32SocketServer server; - rtc::AutoSocketServerThread thread(&server); - EXPECT_EQ(TRUE, PostMessage(nullptr, WM_USER, 999, 0)); - server.Pump(); - MSG msg; - EXPECT_EQ(TRUE, PeekMessage(&msg, nullptr, WM_USER, 0, PM_REMOVE)); - EXPECT_EQ(static_cast(WM_USER), msg.message); - EXPECT_EQ(999u, msg.wParam); -} - -// Test that Win32Socket passes all the generic Socket tests. -class Win32SocketTest : public SocketTest { - protected: - Win32SocketTest() : SocketTest(&server_), thread_(&server_) {} - Win32SocketServer server_; - rtc::AutoSocketServerThread thread_; -}; - -TEST_F(Win32SocketTest, TestConnectIPv4) { - SocketTest::TestConnectIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectIPv6) { - SocketTest::TestConnectIPv6(); -} - -TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv4) { - SocketTest::TestConnectWithDnsLookupIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv6) { - SocketTest::TestConnectWithDnsLookupIPv6(); -} - -TEST_F(Win32SocketTest, TestConnectFailIPv4) { - SocketTest::TestConnectFailIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectFailIPv6) { - SocketTest::TestConnectFailIPv6(); -} - -TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv4) { - SocketTest::TestConnectWithDnsLookupFailIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv6) { - SocketTest::TestConnectWithDnsLookupFailIPv6(); -} - -TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv4) { - SocketTest::TestConnectWithClosedSocketIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv6) { - SocketTest::TestConnectWithClosedSocketIPv6(); -} - -TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv4) { - SocketTest::TestConnectWhileNotClosedIPv4(); -} - -TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv6) { - SocketTest::TestConnectWhileNotClosedIPv6(); -} - -TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv4) { - SocketTest::TestServerCloseDuringConnectIPv4(); -} - -TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv6) { - SocketTest::TestServerCloseDuringConnectIPv6(); -} - -TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv4) { - SocketTest::TestClientCloseDuringConnectIPv4(); -} - -TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv6) { - SocketTest::TestClientCloseDuringConnectIPv6(); -} - -TEST_F(Win32SocketTest, TestServerCloseIPv4) { - SocketTest::TestServerCloseIPv4(); -} - -TEST_F(Win32SocketTest, TestServerCloseIPv6) { - SocketTest::TestServerCloseIPv6(); -} - -TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv4) { - SocketTest::TestCloseInClosedCallbackIPv4(); -} - -TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv6) { - SocketTest::TestCloseInClosedCallbackIPv6(); -} - -TEST_F(Win32SocketTest, TestSocketServerWaitIPv4) { - SocketTest::TestSocketServerWaitIPv4(); -} - -TEST_F(Win32SocketTest, TestSocketServerWaitIPv6) { - SocketTest::TestSocketServerWaitIPv6(); -} - -TEST_F(Win32SocketTest, TestTcpIPv4) { - SocketTest::TestTcpIPv4(); -} - -TEST_F(Win32SocketTest, TestTcpIPv6) { - SocketTest::TestTcpIPv6(); -} - -TEST_F(Win32SocketTest, TestUdpIPv4) { - SocketTest::TestUdpIPv4(); -} - -TEST_F(Win32SocketTest, TestUdpIPv6) { - SocketTest::TestUdpIPv6(); -} - -// Breaks win_x64_dbg bot. -// https://bugs.chromium.org/p/webrtc/issues/detail?id=6178 -TEST_F(Win32SocketTest, DISABLED_TestGetSetOptionsIPv4) { - SocketTest::TestGetSetOptionsIPv4(); -} - -// Breaks win_x64_dbg bot. -// https://bugs.chromium.org/p/webrtc/issues/detail?id=6178 -TEST_F(Win32SocketTest, DISABLED_TestGetSetOptionsIPv6) { - SocketTest::TestGetSetOptionsIPv6(); -} - -} // namespace rtc diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index b841228a8e..fa170138da 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -47,6 +47,9 @@ group("rtc_tools") { ":unpack_aecdump", ] } + if (!build_with_chromium && rtc_enable_grpc) { + deps += [ "data_channel_benchmark" ] + } } rtc_library("video_file_reader") { diff --git a/rtc_tools/converter/yuv_to_ivf_converter.cc b/rtc_tools/converter/yuv_to_ivf_converter.cc index e4a1e125f8..d00457dec7 100644 --- a/rtc_tools/converter/yuv_to_ivf_converter.cc +++ b/rtc_tools/converter/yuv_to_ivf_converter.cc @@ -81,8 +81,8 @@ class IvfFileWriterEncodedCallback : public EncodedImageCallback { received_frames_count_++; RTC_CHECK_LE(received_frames_count_, expected_frames_count_); if (received_frames_count_ % kFrameLogInterval == 0) { - RTC_LOG(INFO) << received_frames_count_ << " out of " - << expected_frames_count_ << " frames written"; + RTC_LOG(LS_INFO) << received_frames_count_ << " out of " + << expected_frames_count_ << " frames written"; } next_frame_written_.Set(); return Result(Result::Error::OK); @@ -231,11 +231,11 @@ void WriteVideoFile(std::string input_file_name, encoder.WaitNextFrameWritten(kMaxFrameEncodeWaitTimeoutMs); if ((i + 1) % kFrameLogInterval == 0) { - RTC_LOG(INFO) << i + 1 << " out of " << frames_count - << " frames are sent for encoding"; + RTC_LOG(LS_INFO) << i + 1 << " out of " << frames_count + << " frames are sent for encoding"; } } - RTC_LOG(INFO) << "All " << frames_count << " frame are sent for encoding"; + RTC_LOG(LS_INFO) << "All " << frames_count << " frame are sent for encoding"; } } // namespace diff --git a/rtc_tools/data_channel_benchmark/BUILD.gn b/rtc_tools/data_channel_benchmark/BUILD.gn new file mode 100644 index 0000000000..c7fbb85f9b --- /dev/null +++ b/rtc_tools/data_channel_benchmark/BUILD.gn @@ -0,0 +1,62 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/grpc/grpc_library.gni") +import("../../webrtc.gni") + +grpc_library("signaling_grpc_proto") { + testonly = true + sources = [ "peer_connection_signaling.proto" ] +} + +rtc_library("signaling_interface") { + sources = [ "signaling_interface.h" ] + deps = [ "../../api:libjingle_peerconnection_api" ] +} + +rtc_library("grpc_signaling") { + testonly = true + sources = [ + "grpc_signaling.cc", + "grpc_signaling.h", + ] + deps = [ + ":signaling_grpc_proto", + ":signaling_interface", + "../../api:libjingle_peerconnection_api", + "../../rtc_base:threading", + "//third_party/grpc:grpc++", + ] + + defines = [ "GPR_FORBID_UNREACHABLE_CODE=0" ] +} + +rtc_executable("data_channel_benchmark") { + testonly = true + sources = [ + "data_channel_benchmark.cc", + "peer_connection_client.cc", + "peer_connection_client.h", + ] + deps = [ + ":grpc_signaling", + ":signaling_interface", + "../../api:create_peerconnection_factory", + "../../api:libjingle_peerconnection_api", + "../../api:rtc_error", + "../../api:scoped_refptr", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/video_codecs:builtin_video_decoder_factory", + "../../api/video_codecs:builtin_video_encoder_factory", + "../../rtc_base", + "../../rtc_base:logging", + "../../rtc_base:refcount", + "../../rtc_base:threading", + "../../system_wrappers:field_trial", + "//third_party/abseil-cpp/absl/cleanup:cleanup", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] +} diff --git a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc new file mode 100644 index 0000000000..57ee8e5fc9 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * Data Channel Benchmarking tool. + * + * Create a server using: ./data_channel_benchmark --server --port 12345 + * Start the flow of data from the server to a client using: + * ./data_channel_benchmark --port 12345 --transfer_size 100 --packet_size 8196 + * The throughput is reported on the server console. + * + * The negotiation does not require a 3rd party server and is done over a gRPC + * transport. No TURN server is configured, so both peers need to be reachable + * using STUN only. + */ +#include + +#include + +#include "absl/cleanup/cleanup.h" +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "rtc_base/event.h" +#include "rtc_base/ssl_adapter.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/grpc_signaling.h" +#include "rtc_tools/data_channel_benchmark/peer_connection_client.h" +#include "system_wrappers/include/field_trial.h" + +ABSL_FLAG(int, verbose, 0, "verbosity level (0-5)"); +ABSL_FLAG(bool, server, false, "Server mode"); +ABSL_FLAG(bool, oneshot, true, "Terminate after serving a client"); +ABSL_FLAG(std::string, address, "localhost", "Connect to server address"); +ABSL_FLAG(uint16_t, port, 0, "Connect to port (0 for random)"); +ABSL_FLAG(uint64_t, transfer_size, 2, "Transfer size (MiB)"); +ABSL_FLAG(uint64_t, packet_size, 256 * 1024, "Packet size"); +ABSL_FLAG(std::string, + force_fieldtrials, + "", + "Field trials control experimental feature code which can be forced. " + "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" + " will assign the group Enable to field trial WebRTC-FooFeature."); + +struct SetupMessage { + size_t packet_size; + size_t transfer_size; + + std::string ToString() { + char buffer[64]; + rtc::SimpleStringBuilder sb(buffer); + sb << packet_size << "," << transfer_size; + + return sb.str(); + } + + static SetupMessage FromString(absl::string_view sv) { + SetupMessage result; + auto parameters = rtc::split(sv, ','); + std::from_chars(parameters[0].data(), + parameters[0].data() + parameters[0].size(), + result.packet_size, 10); + std::from_chars(parameters[1].data(), + parameters[1].data() + parameters[1].size(), + result.transfer_size, 10); + return result; + } +}; + +class DataChannelObserverImpl : public webrtc::DataChannelObserver { + public: + explicit DataChannelObserverImpl(webrtc::DataChannelInterface* dc) + : dc_(dc), bytes_received_(0) {} + void OnStateChange() override { + RTC_LOG(LS_INFO) << "State changed to " << dc_->state(); + switch (dc_->state()) { + case webrtc::DataChannelInterface::DataState::kOpen: + open_event_.Set(); + break; + case webrtc::DataChannelInterface::DataState::kClosed: + closed_event_.Set(); + break; + default: + break; + } + } + void OnMessage(const webrtc::DataBuffer& buffer) override { + bytes_received_ += buffer.data.size(); + if (bytes_received_threshold_ && + bytes_received_ >= bytes_received_threshold_) { + bytes_received_event_.Set(); + } + + if (setup_message_.empty() && !buffer.binary) { + setup_message_.assign(buffer.data.cdata(), buffer.data.size()); + setup_message_event_.Set(); + } + } + void OnBufferedAmountChange(uint64_t sent_data_size) override { + if (dc_->buffered_amount() < + webrtc::DataChannelInterface::MaxSendQueueSize() / 2) + low_buffered_threshold_event_.Set(); + else + low_buffered_threshold_event_.Reset(); + } + + bool WaitForOpenState(int duration_ms) { + return dc_->state() == webrtc::DataChannelInterface::DataState::kOpen || + open_event_.Wait(duration_ms); + } + bool WaitForClosedState(int duration_ms) { + return dc_->state() == webrtc::DataChannelInterface::DataState::kClosed || + closed_event_.Wait(duration_ms); + } + + // Set how many received bytes are required until + // WaitForBytesReceivedThreshold return true. + void SetBytesReceivedThreshold(uint64_t bytes_received_threshold) { + bytes_received_threshold_ = bytes_received_threshold; + if (bytes_received_ >= bytes_received_threshold_) + bytes_received_event_.Set(); + } + // Wait until the received byte count reaches the desired value. + bool WaitForBytesReceivedThreshold(int duration_ms) { + return (bytes_received_threshold_ && + bytes_received_ >= bytes_received_threshold_) || + bytes_received_event_.Wait(duration_ms); + } + + bool WaitForLowbufferedThreshold(int duration_ms) { + return low_buffered_threshold_event_.Wait(duration_ms); + } + std::string SetupMessage() { return setup_message_; } + bool WaitForSetupMessage(int duration_ms) { + return setup_message_event_.Wait(duration_ms); + } + + private: + webrtc::DataChannelInterface* dc_; + rtc::Event open_event_; + rtc::Event closed_event_; + rtc::Event bytes_received_event_; + absl::optional bytes_received_threshold_; + uint64_t bytes_received_; + rtc::Event low_buffered_threshold_event_; + std::string setup_message_; + rtc::Event setup_message_event_; +}; + +int RunServer() { + bool oneshot = absl::GetFlag(FLAGS_oneshot); + uint16_t port = absl::GetFlag(FLAGS_port); + + auto signaling_thread = rtc::Thread::Create(); + signaling_thread->Start(); + { + auto factory = webrtc::PeerConnectionClient::CreateDefaultFactory( + signaling_thread.get()); + + auto grpc_server = webrtc::GrpcSignalingServerInterface::Create( + [factory = rtc::scoped_refptr( + factory)](webrtc::SignalingInterface* signaling) { + webrtc::PeerConnectionClient client(factory.get(), signaling); + client.StartPeerConnection(); + auto peer_connection = client.peerConnection(); + + // Set up the data channel + auto dc_or_error = + peer_connection->CreateDataChannelOrError("benchmark", nullptr); + auto data_channel = dc_or_error.MoveValue(); + auto data_channel_observer = + std::make_unique(data_channel); + data_channel->RegisterObserver(data_channel_observer.get()); + absl::Cleanup unregister_observer( + [data_channel] { data_channel->UnregisterObserver(); }); + + // Wait for a first message from the remote peer. + // It configures how much data should be sent and how big the packets + // should be. + // First message is "packet_size,transfer_size". + data_channel_observer->WaitForSetupMessage(rtc::Event::kForever); + auto parameters = + SetupMessage::FromString(data_channel_observer->SetupMessage()); + + // Wait for the sender and receiver peers to stabilize (send all ACKs) + // This makes it easier to isolate the sending part when profiling. + absl::SleepFor(absl::Seconds(1)); + + std::string data(parameters.packet_size, '0'); + size_t remaining_data = parameters.transfer_size; + + auto begin_time = webrtc::Clock::GetRealTimeClock()->CurrentTime(); + + while (remaining_data) { + if (remaining_data < data.size()) + data.resize(remaining_data); + + rtc::CopyOnWriteBuffer buffer(data); + webrtc::DataBuffer data_buffer(buffer, true); + if (!data_channel->Send(data_buffer)) { + // If the send() call failed, the buffers are full. + // We wait until there's more room. + data_channel_observer->WaitForLowbufferedThreshold( + rtc::Event::kForever); + continue; + } + remaining_data -= buffer.size(); + fprintf(stderr, "Progress: %zu / %zu (%zu%%)\n", + (parameters.transfer_size - remaining_data), + parameters.transfer_size, + (100 - remaining_data * 100 / parameters.transfer_size)); + } + + // Receiver signals the data channel close event when it has received + // all the data it requested. + data_channel_observer->WaitForClosedState(rtc::Event::kForever); + + auto end_time = webrtc::Clock::GetRealTimeClock()->CurrentTime(); + auto duration_ms = (end_time - begin_time).ms(); + double throughput = (parameters.transfer_size / 1024. / 1024.) / + (duration_ms / 1000.); + printf("Elapsed time: %zums %gMiB/s\n", duration_ms, throughput); + }, + port, oneshot); + grpc_server->Start(); + + printf("Server listening on port %d\n", grpc_server->SelectedPort()); + grpc_server->Wait(); + } + + signaling_thread->Quit(); + return 0; +} + +int RunClient() { + uint16_t port = absl::GetFlag(FLAGS_port); + std::string server_address = absl::GetFlag(FLAGS_address); + size_t transfer_size = absl::GetFlag(FLAGS_transfer_size) * 1024 * 1024; + size_t packet_size = absl::GetFlag(FLAGS_packet_size); + + auto signaling_thread = rtc::Thread::Create(); + signaling_thread->Start(); + { + auto factory = webrtc::PeerConnectionClient::CreateDefaultFactory( + signaling_thread.get()); + auto grpc_client = webrtc::GrpcSignalingClientInterface::Create( + server_address + ":" + std::to_string(port)); + webrtc::PeerConnectionClient client(factory.get(), + grpc_client->signaling_client()); + + // Set up the callback to receive the data channel from the sender. + rtc::scoped_refptr data_channel; + rtc::Event got_data_channel; + client.SetOnDataChannel( + [&data_channel, &got_data_channel]( + rtc::scoped_refptr channel) { + data_channel = channel; + got_data_channel.Set(); + }); + + // Connect to the server. + if (!grpc_client->Start()) { + fprintf(stderr, "Failed to connect to server\n"); + return 1; + } + + // Wait for the data channel to be received + got_data_channel.Wait(rtc::Event::kForever); + + // DataChannel needs an observer to start draining the read queue + DataChannelObserverImpl observer(data_channel.get()); + observer.SetBytesReceivedThreshold(transfer_size); + data_channel->RegisterObserver(&observer); + absl::Cleanup unregister_observer( + [data_channel] { data_channel->UnregisterObserver(); }); + + // Send a configuration string to the server to tell it to send + // 'packet_size' bytes packets and send a total of 'transfer_size' MB. + observer.WaitForOpenState(rtc::Event::kForever); + SetupMessage setup_message = { + .packet_size = packet_size, + .transfer_size = transfer_size, + }; + if (!data_channel->Send(webrtc::DataBuffer(setup_message.ToString()))) { + fprintf(stderr, "Failed to send parameter string\n"); + return 1; + } + + // Wait until we have received all the data + observer.WaitForBytesReceivedThreshold(rtc::Event::kForever); + + // Close the data channel, signaling to the server we have received + // all the requested data. + data_channel->Close(); + } + + signaling_thread->Quit(); + + return 0; +} + +int main(int argc, char** argv) { + rtc::InitializeSSL(); + absl::ParseCommandLine(argc, argv); + + // Make sure that higher severity number means more logs by reversing the + // rtc::LoggingSeverity values. + auto logging_severity = + std::max(0, rtc::LS_NONE - absl::GetFlag(FLAGS_verbose)); + rtc::LogMessage::LogToDebug( + static_cast(logging_severity)); + + bool is_server = absl::GetFlag(FLAGS_server); + std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials); + + webrtc::field_trial::InitFieldTrialsFromString(field_trials.c_str()); + + return is_server ? RunServer() : RunClient(); +} diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.cc b/rtc_tools/data_channel_benchmark/grpc_signaling.cc new file mode 100644 index 0000000000..8db717fc71 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.cc @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_tools/data_channel_benchmark/grpc_signaling.h" + +#include +#include + +#include +#include + +#include "api/jsep.h" +#include "api/jsep_ice_candidate.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/peer_connection_signaling.grpc.pb.h" + +namespace webrtc { +namespace { + +using GrpcSignaling::IceCandidate; +using GrpcSignaling::PeerConnectionSignaling; +using GrpcSignaling::SessionDescription; +using GrpcSignaling::SignalingMessage; + +template +class SessionData : public webrtc::SignalingInterface { + public: + SessionData() {} + explicit SessionData(T* stream) : stream_(stream) {} + void SetStream(T* stream) { stream_ = stream; } + + void SendIceCandidate(const IceCandidateInterface* candidate) override { + RTC_LOG(LS_INFO) << "SendIceCandidate"; + std::string serialized_candidate; + if (!candidate->ToString(&serialized_candidate)) { + RTC_LOG(LS_ERROR) << "Failed to serialize ICE candidate"; + return; + } + + SignalingMessage message; + IceCandidate* proto_candidate = message.mutable_candidate(); + proto_candidate->set_description(serialized_candidate); + proto_candidate->set_mid(candidate->sdp_mid()); + proto_candidate->set_mline_index(candidate->sdp_mline_index()); + + stream_->Write(message); + } + + void SendDescription(const SessionDescriptionInterface* sdp) override { + RTC_LOG(LS_INFO) << "SendDescription"; + + std::string serialized_sdp; + sdp->ToString(&serialized_sdp); + + SignalingMessage message; + if (sdp->GetType() == SdpType::kOffer) + message.mutable_description()->set_type(SessionDescription::OFFER); + else if (sdp->GetType() == SdpType::kAnswer) + message.mutable_description()->set_type(SessionDescription::ANSWER); + message.mutable_description()->set_content(serialized_sdp); + + stream_->Write(message); + } + + void OnRemoteDescription( + std::function sdp)> + callback) override { + RTC_LOG(LS_INFO) << "OnRemoteDescription"; + remote_description_callback_ = callback; + } + + void OnIceCandidate( + std::function candidate)> + callback) override { + RTC_LOG(LS_INFO) << "OnIceCandidate"; + ice_candidate_callback_ = callback; + } + + T* stream_; + + std::function)> + ice_candidate_callback_; + std::function)> + remote_description_callback_; +}; + +using ServerSessionData = + SessionData>; +using ClientSessionData = + SessionData>; + +template +void ProcessMessages(StreamReader* stream, SessionData* session) { + MessageType message; + + while (stream->Read(&message)) { + switch (message.Content_case()) { + case SignalingMessage::ContentCase::kCandidate: { + webrtc::SdpParseError error; + auto jsep_candidate = std::make_unique( + message.candidate().mid(), message.candidate().mline_index()); + if (!jsep_candidate->Initialize(message.candidate().description(), + &error)) { + RTC_LOG(LS_ERROR) << "Failed to deserialize ICE candidate '" + << message.candidate().description() << "'"; + RTC_LOG(LS_ERROR) + << "Error at line " << error.line << ":" << error.description; + continue; + } + + session->ice_candidate_callback_(std::move(jsep_candidate)); + break; + } + case SignalingMessage::ContentCase::kDescription: { + auto& description = message.description(); + auto content = description.content(); + + auto sdp = webrtc::CreateSessionDescription( + description.type() == SessionDescription::OFFER + ? webrtc::SdpType::kOffer + : webrtc::SdpType::kAnswer, + description.content()); + session->remote_description_callback_(std::move(sdp)); + break; + } + default: + RTC_DCHECK_NOTREACHED(); + } + } +} + +class GrpcNegotiationServer : public GrpcSignalingServerInterface, + public PeerConnectionSignaling::Service { + public: + GrpcNegotiationServer( + std::function callback, + int port, + bool oneshot) + : connect_callback_(std::move(callback)), + requested_port_(port), + oneshot_(oneshot) {} + ~GrpcNegotiationServer() override { + Stop(); + if (server_stop_thread_) + server_stop_thread_->Stop(); + } + + void Start() override { + std::string server_address = "[::]"; + + grpc::ServerBuilder builder; + builder.AddListeningPort( + server_address + ":" + std::to_string(requested_port_), + grpc::InsecureServerCredentials(), &selected_port_); + builder.RegisterService(this); + server_ = builder.BuildAndStart(); + } + + void Wait() override { server_->Wait(); } + + void Stop() override { server_->Shutdown(); } + + int SelectedPort() override { return selected_port_; } + + grpc::Status Connect( + grpc::ServerContext* context, + grpc::ServerReaderWriter* stream) + override { + if (oneshot_) { + // Request the termination of the server early so we don't serve another + // client in parallel. + server_stop_thread_ = rtc::Thread::Create(); + server_stop_thread_->Start(); + server_stop_thread_->PostTask([this] { Stop(); }); + } + + ServerSessionData session(stream); + + auto reading_thread = rtc::Thread::Create(); + reading_thread->Start(); + reading_thread->PostTask([&session, &stream] { + ProcessMessages(stream, &session); + }); + + connect_callback_(&session); + + reading_thread->Stop(); + + return grpc::Status::OK; + } + + private: + std::function connect_callback_; + int requested_port_; + int selected_port_; + bool oneshot_; + + std::unique_ptr server_; + std::unique_ptr server_stop_thread_; +}; + +class GrpcNegotiationClient : public GrpcSignalingClientInterface { + public: + explicit GrpcNegotiationClient(const std::string& server) { + channel_ = grpc::CreateChannel(server, grpc::InsecureChannelCredentials()); + stub_ = PeerConnectionSignaling::NewStub(channel_); + } + + ~GrpcNegotiationClient() override { + context_.TryCancel(); + if (reading_thread_) + reading_thread_->Stop(); + } + + bool Start() override { + if (!channel_->WaitForConnected( + absl::ToChronoTime(absl::Now() + absl::Seconds(3)))) { + return false; + } + + stream_ = stub_->Connect(&context_); + session_.SetStream(stream_.get()); + + reading_thread_ = rtc::Thread::Create(); + reading_thread_->Start(); + reading_thread_->PostTask([this] { + ProcessMessages(stream_.get(), &session_); + }); + + return true; + } + + webrtc::SignalingInterface* signaling_client() override { return &session_; } + + private: + std::shared_ptr channel_; + std::unique_ptr stub_; + std::unique_ptr reading_thread_; + grpc::ClientContext context_; + std::unique_ptr< + ::grpc::ClientReaderWriter> + stream_; + ClientSessionData session_; +}; +} // namespace + +std::unique_ptr +GrpcSignalingServerInterface::Create( + std::function callback, + int port, + bool oneshot) { + return std::make_unique(std::move(callback), port, + oneshot); +} + +std::unique_ptr +GrpcSignalingClientInterface::Create(const std::string& server) { + return std::make_unique(server); +} + +} // namespace webrtc diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.h b/rtc_tools/data_channel_benchmark/grpc_signaling.h new file mode 100644 index 0000000000..15799d22b7 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ + +#include +#include + +#include "api/jsep.h" +#include "rtc_tools/data_channel_benchmark/signaling_interface.h" + +namespace webrtc { + +// This class defines a server enabling clients to perform a PeerConnection +// negotiation directly over gRPC. +// When a client connects, a callback is run to handle the request. +class GrpcSignalingServerInterface { + public: + virtual ~GrpcSignalingServerInterface() = default; + + // Start listening for connections. + virtual void Start() = 0; + + // Wait for the gRPC server to terminate. + virtual void Wait() = 0; + + // Stop the gRPC server instance. + virtual void Stop() = 0; + + // The port the server is listening on. + virtual int SelectedPort() = 0; + + // Create a gRPC server listening on |port| that will run |callback| on each + // request. If |oneshot| is true, it will terminate after serving one request. + static std::unique_ptr Create( + std::function callback, + int port, + bool oneshot); +}; + +// This class defines a client that can connect to a server and perform a +// PeerConnection negotiation directly over gRPC. +class GrpcSignalingClientInterface { + public: + virtual ~GrpcSignalingClientInterface() = default; + + // Connect the client to the gRPC server. + virtual bool Start() = 0; + virtual webrtc::SignalingInterface* signaling_client() = 0; + + // Create a client to connnect to a server at |server_address|. + static std::unique_ptr Create( + const std::string& server_address); +}; + +} // namespace webrtc +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ diff --git a/rtc_tools/data_channel_benchmark/peer_connection_client.cc b/rtc_tools/data_channel_benchmark/peer_connection_client.cc new file mode 100644 index 0000000000..6d2ee8101d --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_client.cc @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_tools/data_channel_benchmark/peer_connection_client.h" + +#include +#include +#include + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/create_peerconnection_factory.h" +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "api/set_remote_description_observer_interface.h" +#include "api/video_codecs/builtin_video_decoder_factory.h" +#include "api/video_codecs/builtin_video_encoder_factory.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace { + +constexpr char kStunServer[] = "stun:stun.l.google.com:19302"; + +class SetLocalDescriptionObserverAdapter + : public webrtc::SetLocalDescriptionObserverInterface { + public: + using Callback = std::function; + static rtc::scoped_refptr Create( + Callback callback) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(callback))); + } + + explicit SetLocalDescriptionObserverAdapter(Callback callback) + : callback_(std::move(callback)) {} + ~SetLocalDescriptionObserverAdapter() override = default; + + private: + void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { + callback_(std::move(error)); + } + + Callback callback_; +}; + +class SetRemoteDescriptionObserverAdapter + : public webrtc::SetRemoteDescriptionObserverInterface { + public: + using Callback = std::function; + static rtc::scoped_refptr Create( + Callback callback) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(callback))); + } + + explicit SetRemoteDescriptionObserverAdapter(Callback callback) + : callback_(std::move(callback)) {} + ~SetRemoteDescriptionObserverAdapter() override = default; + + private: + void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { + callback_(std::move(error)); + } + + Callback callback_; +}; + +class CreateSessionDescriptionObserverAdapter + : public webrtc::CreateSessionDescriptionObserver { + public: + using Success = std::function; + using Failure = std::function; + + static rtc::scoped_refptr Create( + Success success, + Failure failure) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(success), std::move(failure))); + } + + CreateSessionDescriptionObserverAdapter(Success success, Failure failure) + : success_(std::move(success)), failure_(std::move(failure)) {} + ~CreateSessionDescriptionObserverAdapter() override = default; + + private: + void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { + success_(desc); + } + + void OnFailure(webrtc::RTCError error) override { + failure_(std::move(error)); + } + + Success success_; + Failure failure_; +}; + +} // namespace + +namespace webrtc { + +PeerConnectionClient::PeerConnectionClient( + webrtc::PeerConnectionFactoryInterface* factory, + webrtc::SignalingInterface* signaling) + : signaling_(signaling) { + signaling_->OnIceCandidate( + [&](std::unique_ptr candidate) { + AddIceCandidate(std::move(candidate)); + }); + signaling_->OnRemoteDescription( + [&](std::unique_ptr sdp) { + SetRemoteDescription(std::move(sdp)); + }); + InitializePeerConnection(factory); +} + +PeerConnectionClient::~PeerConnectionClient() { + Disconnect(); +} + +rtc::scoped_refptr +PeerConnectionClient::CreateDefaultFactory(rtc::Thread* signaling_thread) { + auto factory = webrtc::CreatePeerConnectionFactory( + /*network_thread=*/nullptr, /*worker_thread=*/nullptr, + /*signaling_thread*/ signaling_thread, + /*default_adm=*/nullptr, webrtc::CreateBuiltinAudioEncoderFactory(), + webrtc::CreateBuiltinAudioDecoderFactory(), + webrtc::CreateBuiltinVideoEncoderFactory(), + webrtc::CreateBuiltinVideoDecoderFactory(), + /*audio_mixer=*/nullptr, /*audio_processing=*/nullptr); + + if (!factory) { + RTC_LOG(LS_ERROR) << "Failed to initialize PeerConnectionFactory"; + return nullptr; + } + + return factory; +} + +bool PeerConnectionClient::InitializePeerConnection( + webrtc::PeerConnectionFactoryInterface* factory) { + RTC_CHECK(factory) + << "Must call InitializeFactory before InitializePeerConnection"; + + webrtc::PeerConnectionInterface::RTCConfiguration config; + webrtc::PeerConnectionInterface::IceServer server; + server.urls.push_back(kStunServer); + config.servers.push_back(server); + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + config.enable_dtls_srtp = true; + + webrtc::PeerConnectionDependencies dependencies(this); + auto result = + factory->CreatePeerConnectionOrError(config, std::move(dependencies)); + + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "Failed to create PeerConnection: " + << result.error().message(); + DeletePeerConnection(); + return false; + } + peer_connection_ = result.MoveValue(); + RTC_LOG(LS_INFO) << "PeerConnection created successfully"; + return true; +} + +bool PeerConnectionClient::StartPeerConnection() { + RTC_LOG(LS_INFO) << "Creating offer"; + + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create([this]( + webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription(peer_connection_->local_description()); + })); + + return true; +} + +bool PeerConnectionClient::IsConnected() { + return peer_connection_->peer_connection_state() == + webrtc::PeerConnectionInterface::PeerConnectionState::kConnected; +} + +// Disconnect from the call. +void PeerConnectionClient::Disconnect() { + for (auto& data_channel : data_channels_) { + data_channel->Close(); + data_channel.release(); + } + data_channels_.clear(); + DeletePeerConnection(); +} + +// Delete the WebRTC PeerConnection. +void PeerConnectionClient::DeletePeerConnection() { + RTC_LOG(LS_INFO); + + if (peer_connection_) { + peer_connection_->Close(); + } + peer_connection_.release(); +} + +void PeerConnectionClient::OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) { + if (new_state == webrtc::PeerConnectionInterface::IceConnectionState:: + kIceConnectionCompleted) { + RTC_LOG(LS_INFO) << "State is updating to connected"; + } else if (new_state == webrtc::PeerConnectionInterface::IceConnectionState:: + kIceConnectionDisconnected) { + RTC_LOG(LS_INFO) << "Disconnecting from peer"; + Disconnect(); + } +} + +void PeerConnectionClient::OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) { + if (new_state == webrtc::PeerConnectionInterface::kIceGatheringComplete) { + RTC_LOG(LS_INFO) << "Client is ready to receive remote SDP"; + } +} + +void PeerConnectionClient::OnIceCandidate( + const webrtc::IceCandidateInterface* candidate) { + signaling_->SendIceCandidate(candidate); +} + +void PeerConnectionClient::OnDataChannel( + rtc::scoped_refptr channel) { + RTC_LOG(LS_INFO) << __FUNCTION__ << " remote datachannel created"; + if (on_data_channel_callback_) + on_data_channel_callback_(channel); + data_channels_.push_back(channel); +} + +void PeerConnectionClient::SetOnDataChannel( + std::function)> + callback) { + on_data_channel_callback_ = callback; +} + +void PeerConnectionClient::OnNegotiationNeededEvent(uint32_t event_id) { + RTC_LOG(LS_INFO) << "OnNegotiationNeededEvent"; + + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create([this]( + webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription(peer_connection_->local_description()); + })); +} + +bool PeerConnectionClient::SetRemoteDescription( + std::unique_ptr desc) { + RTC_LOG(LS_INFO) << "SetRemoteDescription"; + auto type = desc->GetType(); + + peer_connection_->SetRemoteDescription( + std::move(desc), + SetRemoteDescriptionObserverAdapter::Create([&](webrtc::RTCError) { + RTC_LOG(LS_INFO) << "SetRemoteDescription done"; + + if (type == webrtc::SdpType::kOffer) { + // Got an offer from the remote, need to set an answer and send it. + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create( + [this](webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription( + peer_connection_->local_description()); + })); + } + })); + + return true; +} + +void PeerConnectionClient::AddIceCandidate( + std::unique_ptr candidate) { + RTC_LOG(LS_INFO) << "AddIceCandidate"; + + peer_connection_->AddIceCandidate( + std::move(candidate), [](const webrtc::RTCError& error) { + RTC_LOG(LS_INFO) << "Failed to add candidate: " << error.message(); + }); +} + +} // namespace webrtc diff --git a/rtc_tools/data_channel_benchmark/peer_connection_client.h b/rtc_tools/data_channel_benchmark/peer_connection_client.h new file mode 100644 index 0000000000..a9787fe709 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_client.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ + +#include + +#include +#include +#include + +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/set_local_description_observer_interface.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/signaling_interface.h" + +namespace webrtc { + +// Handles all the details for creating a PeerConnection and negotiation using a +// SignalingInterface object. +class PeerConnectionClient : public webrtc::PeerConnectionObserver { + public: + explicit PeerConnectionClient(webrtc::PeerConnectionFactoryInterface* factory, + webrtc::SignalingInterface* signaling); + + ~PeerConnectionClient() override; + + PeerConnectionClient(const PeerConnectionClient&) = delete; + PeerConnectionClient& operator=(const PeerConnectionClient&) = delete; + + // Set the local description and send offer using the SignalingInterface, + // initiating the negotiation process. + bool StartPeerConnection(); + + // Whether the peer connection is connected to the remote peer. + bool IsConnected(); + + // Disconnect from the call. + void Disconnect(); + + rtc::scoped_refptr peerConnection() { + return peer_connection_; + } + + // Set a callback to run when a DataChannel is created by the remote peer. + void SetOnDataChannel( + std::function)> + callback); + + std::vector>& + dataChannels() { + return data_channels_; + } + + // Creates a default PeerConnectionFactory object. + static rtc::scoped_refptr + CreateDefaultFactory(rtc::Thread* signaling_thread); + + private: + void AddIceCandidate( + std::unique_ptr candidate); + bool SetRemoteDescription( + std::unique_ptr desc); + + // Initialize the PeerConnection with a given PeerConnectionFactory. + bool InitializePeerConnection( + webrtc::PeerConnectionFactoryInterface* factory); + void DeletePeerConnection(); + + // PeerConnectionObserver implementation. + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override { + RTC_LOG(LS_INFO) << __FUNCTION__ << " new state: " << new_state; + } + void OnDataChannel( + rtc::scoped_refptr channel) override; + void OnNegotiationNeededEvent(uint32_t event_id) override; + void OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) override; + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override; + void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; + void OnIceConnectionReceivingChange(bool receiving) override { + RTC_LOG(LS_INFO) << __FUNCTION__ << " receiving? " << receiving; + } + + rtc::scoped_refptr peer_connection_; + std::function)> + on_data_channel_callback_; + std::vector> data_channels_; + webrtc::SignalingInterface* signaling_; +}; + +} // namespace webrtc + +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ diff --git a/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto b/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto new file mode 100644 index 0000000000..9bd0aae912 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package webrtc.GrpcSignaling; + +service PeerConnectionSignaling { + rpc Connect(stream SignalingMessage) returns (stream SignalingMessage) {} +} + +message SignalingMessage { + oneof Content { + SessionDescription description = 1; + IceCandidate candidate = 2; + } +} + +message SessionDescription { + enum SessionDescriptionType { + OFFER = 0; + ANSWER = 1; + } + SessionDescriptionType type = 1; + string content = 2; +} + +message IceCandidate { + string mid = 1; + int32 mline_index = 2; + string description = 3; +} \ No newline at end of file diff --git a/rtc_tools/data_channel_benchmark/signaling_interface.h b/rtc_tools/data_channel_benchmark/signaling_interface.h new file mode 100644 index 0000000000..77c811acb3 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/signaling_interface.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ + +#include + +#include "api/jsep.h" + +namespace webrtc { +class SignalingInterface { + public: + virtual ~SignalingInterface() = default; + + // Send an ICE candidate over the transport. + virtual void SendIceCandidate( + const webrtc::IceCandidateInterface* candidate) = 0; + + // Send a local description over the transport. + virtual void SendDescription( + const webrtc::SessionDescriptionInterface* sdp) = 0; + + // Set a callback when receiving a description from the transport. + virtual void OnRemoteDescription( + std::function + sdp)> callback) = 0; + + // Set a callback when receiving an ICE candidate from the transport. + virtual void OnIceCandidate( + std::function + candidate)> callback) = 0; +}; +} // namespace webrtc + +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ diff --git a/rtc_tools/frame_analyzer/video_color_aligner.cc b/rtc_tools/frame_analyzer/video_color_aligner.cc index 05e963fd68..6e9f81da4c 100644 --- a/rtc_tools/frame_analyzer/video_color_aligner.cc +++ b/rtc_tools/frame_analyzer/video_color_aligner.cc @@ -155,7 +155,7 @@ ColorTransformationMatrix CalculateColorTransformationMatrix( rtc::scoped_refptr