diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/libwebrtc/webrtc/call | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/webrtc/call')
64 files changed, 14264 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/call/BUILD.gn b/third_party/libwebrtc/webrtc/call/BUILD.gn new file mode 100644 index 0000000000..d6fdae506c --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/BUILD.gn @@ -0,0 +1,292 @@ +# Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../webrtc.gni") + +rtc_source_set("call_interfaces") { + sources = [ + "audio_receive_stream.h", + "audio_send_stream.h", + "audio_state.h", + "call.h", + "callfactoryinterface.h", + "flexfec_receive_stream.h", + "syncable.cc", + "syncable.h", + ] + deps = [ + ":rtp_interfaces", + ":video_stream_api", + "..:webrtc_common", + "../api:audio_mixer_api", + "../api:optional", + "../api:transport_api", + "../api/audio_codecs:audio_codecs_api", + "../modules/audio_processing:audio_processing_statistics", + "../rtc_base:rtc_base", + "../rtc_base:rtc_base_approved", + ] + + if (!build_with_mozilla) { + deps += [ "../api:libjingle_peerconnection_api" ] + sources += [ "audio_send_stream.cc" ] + } else { + sources += [ "audio_send_stream_call.cc" ] + } +} + +# TODO(nisse): These RTP targets should be moved elsewhere +# when interfaces have stabilized. See also TODO for |mock_rtp_interfaces|. +rtc_source_set("rtp_interfaces") { + sources = [ + "rtcp_packet_sink_interface.h", + "rtp_config.cc", + "rtp_config.h", + "rtp_packet_sink_interface.h", + "rtp_stream_receiver_controller_interface.h", + "rtp_transport_controller_send_interface.h", + ] + deps = [ + "../api:array_view", + "../rtc_base:rtc_base_approved", + ] +} + +rtc_source_set("rtp_receiver") { + sources = [ + "rtcp_demuxer.cc", + "rtcp_demuxer.h", + "rtp_demuxer.cc", + "rtp_demuxer.h", + "rtp_rtcp_demuxer_helper.cc", + "rtp_rtcp_demuxer_helper.h", + "rtp_stream_receiver_controller.cc", + "rtp_stream_receiver_controller.h", + "rtx_receive_stream.cc", + "rtx_receive_stream.h", + "ssrc_binding_observer.h", + ] + deps = [ + ":rtp_interfaces", + "..:webrtc_common", + "../api:array_view", + "../api:optional", + "../modules/rtp_rtcp", + "../rtc_base:rtc_base_approved", + ] +} + +rtc_source_set("rtp_sender") { + sources = [ + "rtp_transport_controller_send.cc", + "rtp_transport_controller_send.h", + ] + deps = [ + ":rtp_interfaces", + "..:webrtc_common", + "../modules/congestion_controller", + "../modules/pacing", + "../rtc_base:rtc_base_approved", + ] +} + +rtc_source_set("bitrate_allocator") { + sources = [ + "bitrate_allocator.cc", + "bitrate_allocator.h", + ] + deps = [ + "../modules/bitrate_controller", + "../rtc_base:rtc_base_approved", + "../rtc_base:sequenced_task_checker", + "../system_wrappers", + ] + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } +} + +rtc_static_library("call") { + sources = [ + "call.cc", + "callfactory.cc", + "callfactory.h", + "flexfec_receive_stream_impl.cc", + "flexfec_receive_stream_impl.h", + ] + + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + + public_deps = [ + ":call_interfaces", + "../api:call_api", + ] + + if (!build_with_mozilla) { + public_deps += [ "../api:libjingle_peerconnection_api" ] + } + + deps = [ + ":bitrate_allocator", + ":call_interfaces", + ":rtp_interfaces", + ":rtp_receiver", + ":rtp_sender", + ":video_stream_api", + "..:webrtc_common", + "../api:optional", + "../api:transport_api", + "../audio", + "../logging:rtc_event_log_api", + "../logging:rtc_event_log_impl", + "../modules/bitrate_controller", + "../modules/congestion_controller", + "../modules/pacing", + "../modules/rtp_rtcp", + "../modules/utility", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_task_queue", + "../rtc_base:sequenced_task_checker", + "../system_wrappers", + "../video", + ] +} + +rtc_source_set("video_stream_api") { + sources = [ + "video_config.cc", + "video_config.h", + "video_receive_stream.cc", + "video_receive_stream.h", + "video_send_stream.cc", + "video_send_stream.h", + ] + deps = [ + ":rtp_interfaces", + "../:webrtc_common", + "../api:optional", + "../api:transport_api", + "../common_video:common_video", + "../rtc_base:rtc_base_approved", + ] + + if (!build_with_mozilla) { + deps += [ "../api:libjingle_peerconnection_api" ] + } +} + +if (rtc_include_tests) { + rtc_source_set("call_tests") { + testonly = true + + sources = [ + "bitrate_allocator_unittest.cc", + "bitrate_estimator_tests.cc", + "call_unittest.cc", + "flexfec_receive_stream_unittest.cc", + "rtcp_demuxer_unittest.cc", + "rtp_demuxer_unittest.cc", + "rtp_rtcp_demuxer_helper_unittest.cc", + "rtx_receive_stream_unittest.cc", + ] + deps = [ + ":bitrate_allocator", + ":call", + ":mock_rtp_interfaces", + ":rtp_interfaces", + ":rtp_receiver", + ":rtp_sender", + "..:webrtc_common", + "../api:array_view", + "../api:mock_audio_mixer", + "../api/audio_codecs:builtin_audio_decoder_factory", + "../logging:rtc_event_log_api", + "../modules/audio_device:mock_audio_device", + "../modules/audio_mixer", + "../modules/bitrate_controller", + "../modules/congestion_controller", + "../modules/congestion_controller:mock_congestion_controller", + "../modules/pacing", + "../modules/pacing:mock_paced_sender", + "../modules/rtp_rtcp", + "../modules/rtp_rtcp:mock_rtp_rtcp", + "../modules/utility:mock_process_thread", + "../rtc_base:rtc_base_approved", + "../system_wrappers", + "../test:audio_codec_mocks", + "../test:direct_transport", + "../test:test_common", + "../test:test_support", + "../test:video_test_common", + "//testing/gmock", + "//testing/gtest", + ] + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + } + + rtc_source_set("call_perf_tests") { + testonly = true + + sources = [ + "call_perf_tests.cc", + "rampup_tests.cc", + "rampup_tests.h", + ] + deps = [ + ":call_interfaces", + ":video_stream_api", + "..:webrtc_common", + "../api/audio_codecs:builtin_audio_encoder_factory", + "../logging:rtc_event_log_api", + "../modules/audio_coding", + "../modules/audio_mixer:audio_mixer_impl", + "../modules/rtp_rtcp", + "../rtc_base:rtc_base_approved", + "../system_wrappers", + "../system_wrappers:metrics_default", + "../test:direct_transport", + "../test:fake_audio_device", + "../test:field_trial", + "../test:test_common", + "../test:test_support", + "../test:video_test_common", + "../video", + "../voice_engine", + "//testing/gtest", + ] + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + } + + # TODO(eladalon): This should be moved, as with the TODO for |rtp_interfaces|. + rtc_source_set("mock_rtp_interfaces") { + testonly = true + + sources = [ + "fake_rtp_transport_controller_send.h", + "test/mock_rtp_packet_sink_interface.h", + ] + deps = [ + ":rtp_interfaces", + "..:webrtc_common", + "../modules/congestion_controller:congestion_controller", + "../modules/pacing:pacing", + "../test:test_support", + "//testing/gmock", + ] + } +} diff --git a/third_party/libwebrtc/webrtc/call/DEPS b/third_party/libwebrtc/webrtc/call/DEPS new file mode 100644 index 0000000000..7622e24116 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/DEPS @@ -0,0 +1,27 @@ +include_rules = [ + "+audio", + "+logging/rtc_event_log", + "+modules/audio_coding", + "+modules/audio_device", + "+modules/audio_mixer", + "+modules/audio_processing", + "+modules/bitrate_controller", + "+modules/congestion_controller", + "+modules/pacing", + "+modules/rtp_rtcp", + "+modules/utility", + "+system_wrappers", + "+voice_engine", + "+video", +] + +specific_include_rules = { + "video_receive_stream\.h": [ + "+common_video/include", + "+media/base", + ], + "video_send_stream\.h": [ + "+common_video/include", + "+media/base", + ], +} diff --git a/third_party/libwebrtc/webrtc/call/OWNERS b/third_party/libwebrtc/webrtc/call/OWNERS new file mode 100644 index 0000000000..fc0487dc2a --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/OWNERS @@ -0,0 +1,8 @@ +mflodman@webrtc.org +solenberg@webrtc.org +stefan@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gn=* +per-file *.gni=* diff --git a/third_party/libwebrtc/webrtc/call/audio_receive_stream.h b/third_party/libwebrtc/webrtc/call/audio_receive_stream.h new file mode 100644 index 0000000000..44f093ccff --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/audio_receive_stream.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_AUDIO_RECEIVE_STREAM_H_ +#define CALL_AUDIO_RECEIVE_STREAM_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/call/transport.h" +#include "api/optional.h" +#include "api/rtpparameters.h" +#include "api/rtpreceiverinterface.h" +#include "call/rtp_config.h" +#include "common_types.h" // NOLINT(build/include) +#include "rtc_base/scoped_ref_ptr.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +class AudioSinkInterface; + +// WORK IN PROGRESS +// This class is under development and is not yet intended for for use outside +// of WebRtc/Libjingle. Please use the VoiceEngine API instead. +// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4690 + +class AudioReceiveStream { + public: + struct Stats { + uint32_t remote_ssrc = 0; + int64_t bytes_rcvd = 0; + uint32_t packets_rcvd = 0; + uint32_t packets_lost = 0; + float fraction_lost = 0.0f; + std::string codec_name; + rtc::Optional<int> codec_payload_type; + uint32_t ext_seqnum = 0; + uint32_t jitter_ms = 0; + uint32_t jitter_buffer_ms = 0; + uint32_t jitter_buffer_preferred_ms = 0; + uint32_t delay_estimate_ms = 0; + int32_t audio_level = -1; + // Stats below correspond to similarly-named fields in the WebRTC stats + // spec. https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats + double total_output_energy = 0.0; + uint64_t total_samples_received = 0; + double total_output_duration = 0.0; + uint64_t concealed_samples = 0; + uint64_t concealment_events = 0; + double jitter_buffer_delay_seconds = 0.0; + // Stats below DO NOT correspond directly to anything in the WebRTC stats + float expand_rate = 0.0f; + float speech_expand_rate = 0.0f; + float secondary_decoded_rate = 0.0f; + float secondary_discarded_rate = 0.0f; + float accelerate_rate = 0.0f; + float preemptive_expand_rate = 0.0f; + int32_t decoding_calls_to_silence_generator = 0; + int32_t decoding_calls_to_neteq = 0; + int32_t decoding_normal = 0; + int32_t decoding_plc = 0; + int32_t decoding_cng = 0; + int32_t decoding_plc_cng = 0; + int32_t decoding_muted_output = 0; + int64_t capture_start_ntp_time_ms = 0; + }; + + struct Config { + std::string ToString() const; + + // Receive-stream specific RTP settings. + struct Rtp { + std::string ToString() const; + + // Synchronization source (stream identifier) to be received. + uint32_t remote_ssrc = 0; + + // Sender SSRC used for sending RTCP (such as receiver reports). + uint32_t local_ssrc = 0; + + // Enable feedback for send side bandwidth estimation. + // See + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions + // for details. + bool transport_cc = false; + + // See NackConfig for description. + NackConfig nack; + + // RTP header extensions used for the received stream. + std::vector<RtpExtension> extensions; + } rtp; + + Transport* rtcp_send_transport = nullptr; + + // Underlying VoiceEngine handle, used to map AudioReceiveStream to lower- + // level components. + // TODO(solenberg): Remove when VoiceEngine channels are created outside + // of Call. + int voe_channel_id = -1; + + // Identifier for an A/V synchronization group. Empty string to disable. + // TODO(pbos): Synchronize streams in a sync group, not just one video + // stream to one audio stream. Tracked by issue webrtc:4762. + std::string sync_group; + + // Decoder specifications for every payload type that we can receive. + std::map<int, SdpAudioFormat> decoder_map; + + rtc::scoped_refptr<AudioDecoderFactory> decoder_factory; + }; + + // Starts stream activity. + // When a stream is active, it can receive, process and deliver packets. + virtual void Start() = 0; + // Stops stream activity. + // When a stream is stopped, it can't receive, process or deliver packets. + virtual void Stop() = 0; + + virtual Stats GetStats() const = 0; + // TODO(solenberg): Remove, once AudioMonitor is gone. + virtual int GetOutputLevel() const = 0; + + // Sets an audio sink that receives unmixed audio from the receive stream. + // Ownership of the sink is passed to the stream and can be used by the + // caller to do lifetime management (i.e. when the sink's dtor is called). + // Only one sink can be set and passing a null sink clears an existing one. + // NOTE: Audio must still somehow be pulled through AudioTransport for audio + // to stream through this sink. In practice, this happens if mixed audio + // is being pulled+rendered and/or if audio is being pulled for the purposes + // of feeding to the AEC. + virtual void SetSink(std::unique_ptr<AudioSinkInterface> sink) = 0; + + // Sets playback gain of the stream, applied when mixing, and thus after it + // is potentially forwarded to any attached AudioSinkInterface implementation. + virtual void SetGain(float gain) = 0; + + virtual std::vector<RtpSource> GetSources() const = 0; + + protected: + virtual ~AudioReceiveStream() {} +}; +} // namespace webrtc + +#endif // CALL_AUDIO_RECEIVE_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/audio_send_stream.h b/third_party/libwebrtc/webrtc/call/audio_send_stream.h new file mode 100644 index 0000000000..4912182c12 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/audio_send_stream.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_AUDIO_SEND_STREAM_H_ +#define CALL_AUDIO_SEND_STREAM_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/audio_codecs/audio_format.h" +#include "api/call/transport.h" +#include "api/optional.h" +#include "api/rtpparameters.h" +#include "call/rtp_config.h" +#include "modules/audio_processing/include/audio_processing_statistics.h" +#include "rtc_base/scoped_ref_ptr.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { + +// WORK IN PROGRESS +// This class is under development and is not yet intended for for use outside +// of WebRtc/Libjingle. Please use the VoiceEngine API instead. +// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4690 + +class AudioSendStream { + public: + struct Stats { + Stats(); + ~Stats(); + + // TODO(solenberg): Harmonize naming and defaults with receive stream stats. + uint32_t local_ssrc = 0; + int64_t bytes_sent = 0; + int32_t packets_sent = 0; + int32_t packets_lost = -1; + float fraction_lost = -1.0f; + std::string codec_name; + rtc::Optional<int> codec_payload_type; + int32_t ext_seqnum = -1; + int32_t jitter_ms = -1; + int64_t rtt_ms = -1; + int32_t audio_level = -1; + // See description of "totalAudioEnergy" in the WebRTC stats spec: + // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy + double total_input_energy = 0.0; + double total_input_duration = 0.0; + bool typing_noise_detected = false; + + ANAStats ana_statistics; + AudioProcessingStats apm_statistics; + }; + + struct Config { + Config() = delete; + explicit Config(Transport* send_transport); + ~Config(); + std::string ToString() const; + + // Send-stream specific RTP settings. + struct Rtp { + Rtp(); + ~Rtp(); + std::string ToString() const; + + // Sender SSRC. + uint32_t ssrc = 0; + + // RTP header extensions used for the sent stream. + std::vector<RtpExtension> extensions; + + // See NackConfig for description. + NackConfig nack; + + // RTCP CNAME, see RFC 3550. + std::string c_name; + } rtp; + + // Transport for outgoing packets. The transport is expected to exist for + // the entire life of the AudioSendStream and is owned by the API client. + Transport* send_transport = nullptr; + + // Underlying VoiceEngine handle, used to map AudioSendStream to lower-level + // components. + // TODO(solenberg): Remove when VoiceEngine channels are created outside + // of Call. + int voe_channel_id = -1; + + // Bitrate limits used for variable audio bitrate streams. Set both to -1 to + // disable audio bitrate adaptation. + // Note: This is still an experimental feature and not ready for real usage. + int min_bitrate_bps = -1; + int max_bitrate_bps = -1; + + // Defines whether to turn on audio network adaptor, and defines its config + // string. + rtc::Optional<std::string> audio_network_adaptor_config; + + struct SendCodecSpec { + SendCodecSpec(int payload_type, const SdpAudioFormat& format); + ~SendCodecSpec(); + std::string ToString() const; + + bool operator==(const SendCodecSpec& rhs) const; + bool operator!=(const SendCodecSpec& rhs) const { + return !(*this == rhs); + } + + int payload_type; + SdpAudioFormat format; + bool nack_enabled = false; + bool transport_cc_enabled = false; + rtc::Optional<int> cng_payload_type; + // If unset, use the encoder's default target bitrate. + rtc::Optional<int> target_bitrate_bps; + }; + + rtc::Optional<SendCodecSpec> send_codec_spec; + rtc::scoped_refptr<AudioEncoderFactory> encoder_factory; + + // Track ID as specified during track creation. + std::string track_id; + }; + + virtual ~AudioSendStream() = default; + + virtual const webrtc::AudioSendStream::Config& GetConfig() const = 0; + + // Reconfigure the stream according to the Configuration. + virtual void Reconfigure(const Config& config) = 0; + + // Starts stream activity. + // When a stream is active, it can receive, process and deliver packets. + virtual void Start() = 0; + // Stops stream activity. + // When a stream is stopped, it can't receive, process or deliver packets. + virtual void Stop() = 0; + + // TODO(solenberg): Make payload_type a config property instead. + virtual bool SendTelephoneEvent(int payload_type, int payload_frequency, + int event, int duration_ms) = 0; + + virtual void SetMuted(bool muted) = 0; + + virtual Stats GetStats() const = 0; + virtual Stats GetStats(bool has_remote_tracks) const = 0; +}; +} // namespace webrtc + +#endif // CALL_AUDIO_SEND_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/audio_send_stream_call.cc b/third_party/libwebrtc/webrtc/call/audio_send_stream_call.cc new file mode 100644 index 0000000000..d0043a2df1 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/audio_send_stream_call.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/audio_send_stream.h" + +#include <string> + +namespace webrtc { + +AudioSendStream::Stats::Stats() = default; +AudioSendStream::Stats::~Stats() = default; + +AudioSendStream::Config::Config(Transport* send_transport) + : send_transport(send_transport) {} + +AudioSendStream::Config::~Config() = default; + +std::string AudioSendStream::Config::ToString() const { + std::stringstream ss; + ss << "{rtp: " << rtp.ToString(); + ss << ", send_transport: " << (send_transport ? "(Transport)" : "null"); + ss << ", voe_channel_id: " << voe_channel_id; + ss << ", min_bitrate_bps: " << min_bitrate_bps; + ss << ", max_bitrate_bps: " << max_bitrate_bps; + ss << ", send_codec_spec: " + << (send_codec_spec ? send_codec_spec->ToString() : "<unset>"); + ss << '}'; + return ss.str(); +} + +AudioSendStream::Config::Rtp::Rtp() = default; + +AudioSendStream::Config::Rtp::~Rtp() = default; + +std::string AudioSendStream::Config::Rtp::ToString() const { + std::stringstream ss; + ss << "{ssrc: " << ssrc; + ss << ", extensions: ["; + for (size_t i = 0; i < extensions.size(); ++i) { + ss << extensions[i].ToString(); + if (i != extensions.size() - 1) { + ss << ", "; + } + } + ss << ']'; + ss << ", nack: " << nack.ToString(); + ss << ", c_name: " << c_name; + ss << '}'; + return ss.str(); +} + +AudioSendStream::Config::SendCodecSpec::SendCodecSpec( + int payload_type, + const SdpAudioFormat& format) + : payload_type(payload_type), format(format) {} +AudioSendStream::Config::SendCodecSpec::~SendCodecSpec() = default; + +std::string AudioSendStream::Config::SendCodecSpec::ToString() const { + std::stringstream ss; + ss << "{nack_enabled: " << (nack_enabled ? "true" : "false"); + ss << ", transport_cc_enabled: " << (transport_cc_enabled ? "true" : "false"); + ss << ", cng_payload_type: " + << (cng_payload_type ? std::to_string(*cng_payload_type) : "<unset>"); + ss << ", payload_type: " << payload_type; + ss << ", format: " << format; + ss << '}'; + return ss.str(); +} + +bool AudioSendStream::Config::SendCodecSpec::operator==( + const AudioSendStream::Config::SendCodecSpec& rhs) const { + if (nack_enabled == rhs.nack_enabled && + transport_cc_enabled == rhs.transport_cc_enabled && + cng_payload_type == rhs.cng_payload_type && + payload_type == rhs.payload_type && format == rhs.format && + target_bitrate_bps == rhs.target_bitrate_bps) { + return true; + } + return false; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/audio_state.h b/third_party/libwebrtc/webrtc/call/audio_state.h new file mode 100644 index 0000000000..e4a281aec2 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/audio_state.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_AUDIO_STATE_H_ +#define CALL_AUDIO_STATE_H_ + +#include "api/audio/audio_mixer.h" +#include "rtc_base/refcount.h" +#include "rtc_base/scoped_ref_ptr.h" + +namespace webrtc { + +class AudioProcessing; +class AudioTransport; +class VoiceEngine; + +// WORK IN PROGRESS +// This class is under development and is not yet intended for for use outside +// of WebRtc/Libjingle. Please use the VoiceEngine API instead. +// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4690 + +// AudioState holds the state which must be shared between multiple instances of +// webrtc::Call for audio processing purposes. +class AudioState : public rtc::RefCountInterface { + public: + struct Config { + // VoiceEngine used for audio streams and audio/video synchronization. + // AudioState will tickle the VoE refcount to keep it alive for as long as + // the AudioState itself. + VoiceEngine* voice_engine = nullptr; + + // The audio mixer connected to active receive streams. One per + // AudioState. + rtc::scoped_refptr<AudioMixer> audio_mixer; + + // The audio processing module. + rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing; + }; + + virtual AudioProcessing* audio_processing() = 0; + virtual AudioTransport* audio_transport() = 0; + + // Enable/disable playout of the audio channels. Enabled by default. + // This will stop playout of the underlying audio device but start a task + // which will poll for audio data every 10ms to ensure that audio processing + // happens and the audio stats are updated. + virtual void SetPlayout(bool enabled) = 0; + + // Enable/disable recording of the audio channels. Enabled by default. + // This will stop recording of the underlying audio device and no audio + // packets will be encoded or transmitted. + virtual void SetRecording(bool enabled) = 0; + + // TODO(solenberg): Replace scoped_refptr with shared_ptr once we can use it. + static rtc::scoped_refptr<AudioState> Create( + const AudioState::Config& config); + + virtual ~AudioState() {} +}; +} // namespace webrtc + +#endif // CALL_AUDIO_STATE_H_ diff --git a/third_party/libwebrtc/webrtc/call/bitrate_allocator.cc b/third_party/libwebrtc/webrtc/call/bitrate_allocator.cc new file mode 100644 index 0000000000..fc202ce22f --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/bitrate_allocator.cc @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "call/bitrate_allocator.h" + +#include <algorithm> +#include <cmath> +#include <memory> +#include <utility> + +#include "modules/bitrate_controller/include/bitrate_controller.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/clock.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +// Allow packets to be transmitted in up to 2 times max video bitrate if the +// bandwidth estimate allows it. +// TODO(bugs.webrtc.org/8541): May be worth to refactor to keep this logic in +// video send stream. Similar logic is implemented in +// AudioPriorityBitrateAllocationStrategy. +const int kTransmissionMaxBitrateMultiplier = 2; +const int kDefaultBitrateBps = 300000; + +// Require a bitrate increase of max(10%, 20kbps) to resume paused streams. +const double kToggleFactor = 0.1; +const uint32_t kMinToggleBitrateBps = 20000; + +const int64_t kBweLogIntervalMs = 5000; + +namespace { + +double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) { + RTC_DCHECK_GT(allocated_bitrate, 0); + if (protection_bitrate == 0) + return 1.0; + + uint32_t media_bitrate = allocated_bitrate - protection_bitrate; + return media_bitrate / static_cast<double>(allocated_bitrate); +} +} // namespace + +BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer) + : limit_observer_(limit_observer), + last_bitrate_bps_(0), + last_non_zero_bitrate_bps_(kDefaultBitrateBps), + last_fraction_loss_(0), + last_rtt_(0), + num_pause_events_(0), + clock_(Clock::GetRealTimeClock()), + last_bwe_log_time_(0), + total_requested_padding_bitrate_(0), + total_requested_min_bitrate_(0), + bitrate_allocation_strategy_(nullptr) { + sequenced_checker_.Detach(); +} + +BitrateAllocator::~BitrateAllocator() { + RTC_HISTOGRAM_COUNTS_100("WebRTC.Call.NumberOfPauseEvents", + num_pause_events_); +} + +void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps, + uint8_t fraction_loss, + int64_t rtt, + int64_t bwe_period_ms) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + last_bitrate_bps_ = target_bitrate_bps; + last_non_zero_bitrate_bps_ = + target_bitrate_bps > 0 ? target_bitrate_bps : last_non_zero_bitrate_bps_; + last_fraction_loss_ = fraction_loss; + last_rtt_ = rtt; + last_bwe_period_ms_ = bwe_period_ms; + + // Periodically log the incoming BWE. + int64_t now = clock_->TimeInMilliseconds(); + if (now > last_bwe_log_time_ + kBweLogIntervalMs) { + RTC_LOG(LS_INFO) << "Current BWE " << target_bitrate_bps; + last_bwe_log_time_ = now; + } + + ObserverAllocation allocation = AllocateBitrates(target_bitrate_bps); + + for (auto& config : bitrate_observer_configs_) { + uint32_t allocated_bitrate = allocation[config.observer]; + uint32_t protection_bitrate = config.observer->OnBitrateUpdated( + allocated_bitrate, last_fraction_loss_, last_rtt_, + last_bwe_period_ms_); + + if (allocated_bitrate == 0 && config.allocated_bitrate_bps > 0) { + if (target_bitrate_bps > 0) + ++num_pause_events_; + // The protection bitrate is an estimate based on the ratio between media + // and protection used before this observer was muted. + uint32_t predicted_protection_bps = + (1.0 - config.media_ratio) * config.min_bitrate_bps; + RTC_LOG(LS_INFO) << "Pausing observer " << config.observer + << " with configured min bitrate " + << config.min_bitrate_bps << " and current estimate of " + << target_bitrate_bps << " and protection bitrate " + << predicted_protection_bps; + } else if (allocated_bitrate > 0 && config.allocated_bitrate_bps == 0) { + if (target_bitrate_bps > 0) + ++num_pause_events_; + RTC_LOG(LS_INFO) << "Resuming observer " << config.observer + << ", configured min bitrate " << config.min_bitrate_bps + << ", current allocation " << allocated_bitrate + << " and protection bitrate " << protection_bitrate; + } + + // Only update the media ratio if the observer got an allocation. + if (allocated_bitrate > 0) + config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate); + config.allocated_bitrate_bps = allocated_bitrate; + } + UpdateAllocationLimits(); +} + +void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, + uint32_t min_bitrate_bps, + uint32_t max_bitrate_bps, + uint32_t pad_up_bitrate_bps, + bool enforce_min_bitrate, + std::string track_id, + double bitrate_priority) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + RTC_DCHECK_GT(bitrate_priority, 0); + RTC_DCHECK(std::isnormal(bitrate_priority)); + auto it = FindObserverConfig(observer); + + // Update settings if the observer already exists, create a new one otherwise. + if (it != bitrate_observer_configs_.end()) { + it->min_bitrate_bps = min_bitrate_bps; + it->max_bitrate_bps = max_bitrate_bps; + it->pad_up_bitrate_bps = pad_up_bitrate_bps; + it->enforce_min_bitrate = enforce_min_bitrate; + it->bitrate_priority = bitrate_priority; + } else { + bitrate_observer_configs_.push_back(ObserverConfig( + observer, min_bitrate_bps, max_bitrate_bps, pad_up_bitrate_bps, + enforce_min_bitrate, track_id, bitrate_priority)); + } + + ObserverAllocation allocation; + if (last_bitrate_bps_ > 0) { + // Calculate a new allocation and update all observers. + allocation = AllocateBitrates(last_bitrate_bps_); + for (auto& config : bitrate_observer_configs_) { + uint32_t allocated_bitrate = allocation[config.observer]; + uint32_t protection_bitrate = config.observer->OnBitrateUpdated( + allocated_bitrate, last_fraction_loss_, last_rtt_, + last_bwe_period_ms_); + config.allocated_bitrate_bps = allocated_bitrate; + if (allocated_bitrate > 0) + config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate); + } + } else { + // Currently, an encoder is not allowed to produce frames. + // But we still have to return the initial config bitrate + let the + // observer know that it can not produce frames. + allocation = AllocateBitrates(last_non_zero_bitrate_bps_); + observer->OnBitrateUpdated(0, last_fraction_loss_, last_rtt_, + last_bwe_period_ms_); + } + UpdateAllocationLimits(); +} + +void BitrateAllocator::UpdateAllocationLimits() { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + uint32_t total_requested_padding_bitrate = 0; + uint32_t total_requested_min_bitrate = 0; + + for (const auto& config : bitrate_observer_configs_) { + uint32_t stream_padding = config.pad_up_bitrate_bps; + if (config.enforce_min_bitrate) { + total_requested_min_bitrate += config.min_bitrate_bps; + } else if (config.allocated_bitrate_bps == 0) { + stream_padding = + std::max(MinBitrateWithHysteresis(config), stream_padding); + } + total_requested_padding_bitrate += stream_padding; + } + + if (total_requested_padding_bitrate == total_requested_padding_bitrate_ && + total_requested_min_bitrate == total_requested_min_bitrate_) { + return; + } + + total_requested_min_bitrate_ = total_requested_min_bitrate; + total_requested_padding_bitrate_ = total_requested_padding_bitrate; + + RTC_LOG(LS_INFO) << "UpdateAllocationLimits : total_requested_min_bitrate: " + << total_requested_min_bitrate + << "bps, total_requested_padding_bitrate: " + << total_requested_padding_bitrate << "bps"; + limit_observer_->OnAllocationLimitsChanged(total_requested_min_bitrate, + total_requested_padding_bitrate); +} + +void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + + auto it = FindObserverConfig(observer); + if (it != bitrate_observer_configs_.end()) { + bitrate_observer_configs_.erase(it); + } + + UpdateAllocationLimits(); +} + +int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + const auto& it = FindObserverConfig(observer); + if (it == bitrate_observer_configs_.end()) { + // This observer hasn't been added yet, just give it its fair share. + return last_non_zero_bitrate_bps_ / + static_cast<int>((bitrate_observer_configs_.size() + 1)); + } else if (it->allocated_bitrate_bps == -1) { + // This observer hasn't received an allocation yet, so do the same. + return last_non_zero_bitrate_bps_ / + static_cast<int>(bitrate_observer_configs_.size()); + } else { + // This observer already has an allocation. + return it->allocated_bitrate_bps; + } +} + +void BitrateAllocator::SetBitrateAllocationStrategy( + std::unique_ptr<rtc::BitrateAllocationStrategy> + bitrate_allocation_strategy) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + bitrate_allocation_strategy_ = std::move(bitrate_allocation_strategy); +} + +BitrateAllocator::ObserverConfigs::iterator +BitrateAllocator::FindObserverConfig(const BitrateAllocatorObserver* observer) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + for (auto it = bitrate_observer_configs_.begin(); + it != bitrate_observer_configs_.end(); ++it) { + if (it->observer == observer) + return it; + } + return bitrate_observer_configs_.end(); +} + +BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates( + uint32_t bitrate) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + if (bitrate_observer_configs_.empty()) + return ObserverAllocation(); + + if (bitrate_allocation_strategy_ != nullptr) { + std::vector<const rtc::BitrateAllocationStrategy::TrackConfig*> + track_configs(bitrate_observer_configs_.size()); + int i = 0; + for (const auto& c : bitrate_observer_configs_) { + track_configs[i++] = &c; + } + std::vector<uint32_t> track_allocations = + bitrate_allocation_strategy_->AllocateBitrates(bitrate, track_configs); + // The strategy should return allocation for all tracks. + RTC_CHECK(track_allocations.size() == bitrate_observer_configs_.size()); + ObserverAllocation allocation; + auto track_allocations_it = track_allocations.begin(); + for (const auto& observer_config : bitrate_observer_configs_) { + allocation[observer_config.observer] = *track_allocations_it++; + } + return allocation; + } + + if (bitrate == 0) + return ZeroRateAllocation(); + + uint32_t sum_min_bitrates = 0; + uint32_t sum_max_bitrates = 0; + for (const auto& observer_config : bitrate_observer_configs_) { + sum_min_bitrates += observer_config.min_bitrate_bps; + sum_max_bitrates += observer_config.max_bitrate_bps; + } + + // Not enough for all observers to get an allocation, allocate according to: + // enforced min bitrate -> allocated bitrate previous round -> restart paused + // streams. + if (!EnoughBitrateForAllObservers(bitrate, sum_min_bitrates)) + return LowRateAllocation(bitrate); + + // All observers will get their min bitrate plus a share of the rest. This + // share is allocated to each observer based on its bitrate_priority. + if (bitrate <= sum_max_bitrates) + return NormalRateAllocation(bitrate, sum_min_bitrates); + + // All observers will get up to kTransmissionMaxBitrateMultiplier x max. + return MaxRateAllocation(bitrate, sum_max_bitrates); +} + +BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation() { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + ObserverAllocation allocation; + for (const auto& observer_config : bitrate_observer_configs_) + allocation[observer_config.observer] = 0; + return allocation; +} + +BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( + uint32_t bitrate) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + ObserverAllocation allocation; + // Start by allocating bitrate to observers enforcing a min bitrate, hence + // remaining_bitrate might turn negative. + int64_t remaining_bitrate = bitrate; + for (const auto& observer_config : bitrate_observer_configs_) { + int32_t allocated_bitrate = 0; + if (observer_config.enforce_min_bitrate) + allocated_bitrate = observer_config.min_bitrate_bps; + + allocation[observer_config.observer] = allocated_bitrate; + remaining_bitrate -= allocated_bitrate; + } + + // Allocate bitrate to all previously active streams. + if (remaining_bitrate > 0) { + for (const auto& observer_config : bitrate_observer_configs_) { + if (observer_config.enforce_min_bitrate || + LastAllocatedBitrate(observer_config) == 0) + continue; + + uint32_t required_bitrate = MinBitrateWithHysteresis(observer_config); + if (remaining_bitrate >= required_bitrate) { + allocation[observer_config.observer] = required_bitrate; + remaining_bitrate -= required_bitrate; + } + } + } + + // Allocate bitrate to previously paused streams. + if (remaining_bitrate > 0) { + for (const auto& observer_config : bitrate_observer_configs_) { + if (LastAllocatedBitrate(observer_config) != 0) + continue; + + // Add a hysteresis to avoid toggling. + uint32_t required_bitrate = MinBitrateWithHysteresis(observer_config); + if (remaining_bitrate >= required_bitrate) { + allocation[observer_config.observer] = required_bitrate; + remaining_bitrate -= required_bitrate; + } + } + } + + // Split a possible remainder evenly on all streams with an allocation. + if (remaining_bitrate > 0) + DistributeBitrateEvenly(remaining_bitrate, false, 1, &allocation); + + RTC_DCHECK_EQ(allocation.size(), bitrate_observer_configs_.size()); + return allocation; +} + +// Allocates the bitrate based on the bitrate priority of each observer. This +// bitrate priority defines the priority for bitrate to be allocated to that +// observer in relation to other observers. For example with two observers, if +// observer 1 had a bitrate_priority = 1.0, and observer 2 has a +// bitrate_priority = 2.0, the expected behavior is that observer 2 will be +// allocated twice the bitrate as observer 1 above the each observer's +// min_bitrate_bps values, until one of the observers hits its max_bitrate_bps. +BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation( + uint32_t bitrate, + uint32_t sum_min_bitrates) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + ObserverAllocation allocation; + ObserverAllocation observers_capacities; + for (const auto& observer_config : bitrate_observer_configs_) { + allocation[observer_config.observer] = observer_config.min_bitrate_bps; + observers_capacities[observer_config.observer] = + observer_config.max_bitrate_bps - observer_config.min_bitrate_bps; + } + + bitrate -= sum_min_bitrates; + // From the remaining bitrate, allocate a proportional amount to each observer + // above the min bitrate already allocated. + if (bitrate > 0) + DistributeBitrateRelatively(bitrate, observers_capacities, &allocation); + + return allocation; +} + +BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation( + uint32_t bitrate, + uint32_t sum_max_bitrates) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + ObserverAllocation allocation; + + for (const auto& observer_config : bitrate_observer_configs_) { + allocation[observer_config.observer] = observer_config.max_bitrate_bps; + bitrate -= observer_config.max_bitrate_bps; + } + DistributeBitrateEvenly(bitrate, true, kTransmissionMaxBitrateMultiplier, + &allocation); + return allocation; +} + +uint32_t BitrateAllocator::LastAllocatedBitrate( + const ObserverConfig& observer_config) { + // Return the configured minimum bitrate for newly added observers, to avoid + // requiring an extra high bitrate for the observer to get an allocated + // bitrate. + return observer_config.allocated_bitrate_bps == -1 + ? observer_config.min_bitrate_bps + : observer_config.allocated_bitrate_bps; +} + +uint32_t BitrateAllocator::MinBitrateWithHysteresis( + const ObserverConfig& observer_config) { + uint32_t min_bitrate = observer_config.min_bitrate_bps; + if (LastAllocatedBitrate(observer_config) == 0) { + min_bitrate += std::max(static_cast<uint32_t>(kToggleFactor * min_bitrate), + kMinToggleBitrateBps); + } + // Account for protection bitrate used by this observer in the previous + // allocation. + // Note: the ratio will only be updated when the stream is active, meaning a + // paused stream won't get any ratio updates. This might lead to waiting a bit + // longer than necessary if the network condition improves, but this is to + // avoid too much toggling. + if (observer_config.media_ratio > 0.0 && observer_config.media_ratio < 1.0) + min_bitrate += min_bitrate * (1.0 - observer_config.media_ratio); + + return min_bitrate; +} + +void BitrateAllocator::DistributeBitrateEvenly(uint32_t bitrate, + bool include_zero_allocations, + int max_multiplier, + ObserverAllocation* allocation) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size()); + + ObserverSortingMap list_max_bitrates; + for (const auto& observer_config : bitrate_observer_configs_) { + if (include_zero_allocations || + allocation->at(observer_config.observer) != 0) { + list_max_bitrates.insert(std::pair<uint32_t, const ObserverConfig*>( + observer_config.max_bitrate_bps, &observer_config)); + } + } + auto it = list_max_bitrates.begin(); + while (it != list_max_bitrates.end()) { + RTC_DCHECK_GT(bitrate, 0); + uint32_t extra_allocation = + bitrate / static_cast<uint32_t>(list_max_bitrates.size()); + uint32_t total_allocation = + extra_allocation + allocation->at(it->second->observer); + bitrate -= extra_allocation; + if (total_allocation > max_multiplier * it->first) { + // There is more than we can fit for this observer, carry over to the + // remaining observers. + bitrate += total_allocation - max_multiplier * it->first; + total_allocation = max_multiplier * it->first; + } + // Finally, update the allocation for this observer. + allocation->at(it->second->observer) = total_allocation; + it = list_max_bitrates.erase(it); + } +} + +bool BitrateAllocator::EnoughBitrateForAllObservers(uint32_t bitrate, + uint32_t sum_min_bitrates) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + if (bitrate < sum_min_bitrates) + return false; + + uint32_t extra_bitrate_per_observer = + (bitrate - sum_min_bitrates) / + static_cast<uint32_t>(bitrate_observer_configs_.size()); + for (const auto& observer_config : bitrate_observer_configs_) { + if (observer_config.min_bitrate_bps + extra_bitrate_per_observer < + MinBitrateWithHysteresis(observer_config)) { + return false; + } + } + return true; +} + +void BitrateAllocator::DistributeBitrateRelatively( + uint32_t remaining_bitrate, + const ObserverAllocation& observers_capacities, + ObserverAllocation* allocation) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_); + RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size()); + RTC_DCHECK_EQ(observers_capacities.size(), bitrate_observer_configs_.size()); + + struct PriorityRateObserverConfig { + PriorityRateObserverConfig(BitrateAllocatorObserver* allocation_key, + uint32_t capacity_bps, + double bitrate_priority) + : allocation_key(allocation_key), + capacity_bps(capacity_bps), + bitrate_priority(bitrate_priority) {} + + BitrateAllocatorObserver* allocation_key; + // The amount of bitrate bps that can be allocated to this observer. + uint32_t capacity_bps; + double bitrate_priority; + + // We want to sort by which observers will be allocated their full capacity + // first. By dividing each observer's capacity by its bitrate priority we + // are "normalizing" the capacity of an observer by the rate it will be + // filled. This is because the amount allocated is based upon bitrate + // priority. We allocate twice as much bitrate to an observer with twice the + // bitrate priority of another. + bool operator<(const PriorityRateObserverConfig& other) const { + return capacity_bps / bitrate_priority < + other.capacity_bps / other.bitrate_priority; + } + }; + + double bitrate_priority_sum = 0; + std::vector<PriorityRateObserverConfig> priority_rate_observers; + for (const auto& observer_config : bitrate_observer_configs_) { + uint32_t capacity_bps = observers_capacities.at(observer_config.observer); + priority_rate_observers.emplace_back(observer_config.observer, capacity_bps, + observer_config.bitrate_priority); + bitrate_priority_sum += observer_config.bitrate_priority; + } + + // Iterate in the order observers can be allocated their full capacity. + std::sort(priority_rate_observers.begin(), priority_rate_observers.end()); + size_t i; + for (i = 0; i < priority_rate_observers.size(); ++i) { + const auto& priority_rate_observer = priority_rate_observers[i]; + // We allocate the full capacity to an observer only if its relative + // portion from the remaining bitrate is sufficient to allocate its full + // capacity. This means we aren't greedily allocating the full capacity, but + // that it is only done when there is also enough bitrate to allocate the + // proportional amounts to all other observers. + double observer_share = + priority_rate_observer.bitrate_priority / bitrate_priority_sum; + double allocation_bps = observer_share * remaining_bitrate; + bool enough_bitrate = allocation_bps >= priority_rate_observer.capacity_bps; + if (!enough_bitrate) + break; + allocation->at(priority_rate_observer.allocation_key) += + priority_rate_observer.capacity_bps; + remaining_bitrate -= priority_rate_observer.capacity_bps; + bitrate_priority_sum -= priority_rate_observer.bitrate_priority; + } + + // From the remaining bitrate, allocate the proportional amounts to the + // observers that aren't allocated their max capacity. + for (; i < priority_rate_observers.size(); ++i) { + const auto& priority_rate_observer = priority_rate_observers[i]; + double fraction_allocated = + priority_rate_observer.bitrate_priority / bitrate_priority_sum; + allocation->at(priority_rate_observer.allocation_key) += + fraction_allocated * remaining_bitrate; + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/bitrate_allocator.h b/third_party/libwebrtc/webrtc/call/bitrate_allocator.h new file mode 100644 index 0000000000..73258ad6d6 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/bitrate_allocator.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_BITRATE_ALLOCATOR_H_ +#define CALL_BITRATE_ALLOCATOR_H_ + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "rtc_base/bitrateallocationstrategy.h" +#include "rtc_base/sequenced_task_checker.h" + +namespace webrtc { + +class Clock; + +// Used by all send streams with adaptive bitrate, to get the currently +// allocated bitrate for the send stream. The current network properties are +// given at the same time, to let the send stream decide about possible loss +// protection. +class BitrateAllocatorObserver { + public: + // Returns the amount of protection used by the BitrateAllocatorObserver + // implementation, as bitrate in bps. + virtual uint32_t OnBitrateUpdated(uint32_t bitrate_bps, + uint8_t fraction_loss, + int64_t rtt, + int64_t bwe_period_ms) = 0; + + protected: + virtual ~BitrateAllocatorObserver() {} +}; + +// Usage: this class will register multiple RtcpBitrateObserver's one at each +// RTCP module. It will aggregate the results and run one bandwidth estimation +// and push the result to the encoders via BitrateAllocatorObserver(s). +class BitrateAllocator { + public: + // Used to get notified when send stream limits such as the minimum send + // bitrate and max padding bitrate is changed. + class LimitObserver { + public: + virtual void OnAllocationLimitsChanged( + uint32_t min_send_bitrate_bps, + uint32_t max_padding_bitrate_bps) = 0; + + protected: + virtual ~LimitObserver() {} + }; + + explicit BitrateAllocator(LimitObserver* limit_observer); + ~BitrateAllocator(); + + // Allocate target_bitrate across the registered BitrateAllocatorObservers. + void OnNetworkChanged(uint32_t target_bitrate_bps, + uint8_t fraction_loss, + int64_t rtt, + int64_t bwe_period_ms); + + // Set the start and max send bitrate used by the bandwidth management. + // + // |observer| updates bitrates if already in use. + // |min_bitrate_bps| = 0 equals no min bitrate. + // |max_bitrate_bps| = 0 equals no max bitrate. + // |enforce_min_bitrate| = 'true' will allocate at least |min_bitrate_bps| for + // this observer, even if the BWE is too low, 'false' will allocate 0 to + // the observer if BWE doesn't allow |min_bitrate_bps|. + // Note that |observer|->OnBitrateUpdated() will be called within the scope of + // this method with the current rtt, fraction_loss and available bitrate and + // that the bitrate in OnBitrateUpdated will be zero if the |observer| is + // currently not allowed to send data. + void AddObserver(BitrateAllocatorObserver* observer, + uint32_t min_bitrate_bps, + uint32_t max_bitrate_bps, + uint32_t pad_up_bitrate_bps, + bool enforce_min_bitrate, + std::string track_id, + // TODO(shampson): Take out default value and wire the + // bitrate_priority up to the AudioSendStream::Config and + // VideoSendStream::Config. + double bitrate_priority = 1.0); + + // Removes a previously added observer, but will not trigger a new bitrate + // allocation. + void RemoveObserver(BitrateAllocatorObserver* observer); + + // Returns initial bitrate allocated for |observer|. If |observer| is not in + // the list of added observers, a best guess is returned. + int GetStartBitrate(BitrateAllocatorObserver* observer); + + // Sets external allocation strategy. If strategy is not set default WebRTC + // allocation mechanism will be used. The strategy may be changed during call. + // Setting NULL value will restore default WEBRTC allocation strategy. + void SetBitrateAllocationStrategy( + std::unique_ptr<rtc::BitrateAllocationStrategy> + bitrate_allocation_strategy); + + private: + struct ObserverConfig : rtc::BitrateAllocationStrategy::TrackConfig { + ObserverConfig(BitrateAllocatorObserver* observer, + uint32_t min_bitrate_bps, + uint32_t max_bitrate_bps, + uint32_t pad_up_bitrate_bps, + bool enforce_min_bitrate, + std::string track_id, + double bitrate_priority) + : TrackConfig(min_bitrate_bps, + max_bitrate_bps, + enforce_min_bitrate, + track_id), + observer(observer), + pad_up_bitrate_bps(pad_up_bitrate_bps), + allocated_bitrate_bps(-1), + media_ratio(1.0), + bitrate_priority(bitrate_priority) {} + + BitrateAllocatorObserver* observer; + uint32_t pad_up_bitrate_bps; + int64_t allocated_bitrate_bps; + double media_ratio; // Part of the total bitrate used for media [0.0, 1.0]. + // The amount of bitrate allocated to this observer relative to all other + // observers. If an observer has twice the bitrate_priority of other + // observers, it should be allocated twice the bitrate above its min. + double bitrate_priority; + }; + + // Calculates the minimum requested send bitrate and max padding bitrate and + // calls LimitObserver::OnAllocationLimitsChanged. + void UpdateAllocationLimits(); + + typedef std::vector<ObserverConfig> ObserverConfigs; + ObserverConfigs::iterator FindObserverConfig( + const BitrateAllocatorObserver* observer); + + typedef std::multimap<uint32_t, const ObserverConfig*> ObserverSortingMap; + typedef std::map<BitrateAllocatorObserver*, int> ObserverAllocation; + + ObserverAllocation AllocateBitrates(uint32_t bitrate); + + // Allocates zero bitrate to all observers. + ObserverAllocation ZeroRateAllocation(); + // Allocates bitrate to observers when there isn't enough to allocate the + // minimum to all observers. + ObserverAllocation LowRateAllocation(uint32_t bitrate); + // Allocates bitrate to all observers when the available bandwidth is enough + // to allocate the minimum to all observers but not enough to allocate the + // max bitrate of each observer. + ObserverAllocation NormalRateAllocation(uint32_t bitrate, + uint32_t sum_min_bitrates); + // Allocates bitrate to observers when there is enough available bandwidth + // for all observers to be allocated their max bitrate. + ObserverAllocation MaxRateAllocation(uint32_t bitrate, + uint32_t sum_max_bitrates); + + uint32_t LastAllocatedBitrate(const ObserverConfig& observer_config); + // The minimum bitrate required by this observer, including enable-hysteresis + // if the observer is in a paused state. + uint32_t MinBitrateWithHysteresis(const ObserverConfig& observer_config); + // Splits |bitrate| evenly to observers already in |allocation|. + // |include_zero_allocations| decides if zero allocations should be part of + // the distribution or not. The allowed max bitrate is |max_multiplier| x + // observer max bitrate. + void DistributeBitrateEvenly(uint32_t bitrate, + bool include_zero_allocations, + int max_multiplier, + ObserverAllocation* allocation); + bool EnoughBitrateForAllObservers(uint32_t bitrate, + uint32_t sum_min_bitrates); + + // From the available |bitrate|, each observer will be allocated a + // proportional amount based upon its bitrate priority. If that amount is + // more than the observer's capacity, it will be allocated its capacity, and + // the excess bitrate is still allocated proportionally to other observers. + // Allocating the proportional amount means an observer with twice the + // bitrate_priority of another will be allocated twice the bitrate. + void DistributeBitrateRelatively( + uint32_t bitrate, + const ObserverAllocation& observers_capacities, + ObserverAllocation* allocation); + + rtc::SequencedTaskChecker sequenced_checker_; + LimitObserver* const limit_observer_ RTC_GUARDED_BY(&sequenced_checker_); + // Stored in a list to keep track of the insertion order. + ObserverConfigs bitrate_observer_configs_ RTC_GUARDED_BY(&sequenced_checker_); + uint32_t last_bitrate_bps_ RTC_GUARDED_BY(&sequenced_checker_); + uint32_t last_non_zero_bitrate_bps_ RTC_GUARDED_BY(&sequenced_checker_); + uint8_t last_fraction_loss_ RTC_GUARDED_BY(&sequenced_checker_); + int64_t last_rtt_ RTC_GUARDED_BY(&sequenced_checker_); + int64_t last_bwe_period_ms_ RTC_GUARDED_BY(&sequenced_checker_); + // Number of mute events based on too low BWE, not network up/down. + int num_pause_events_ RTC_GUARDED_BY(&sequenced_checker_); + Clock* const clock_ RTC_GUARDED_BY(&sequenced_checker_); + int64_t last_bwe_log_time_ RTC_GUARDED_BY(&sequenced_checker_); + uint32_t total_requested_padding_bitrate_ RTC_GUARDED_BY(&sequenced_checker_); + uint32_t total_requested_min_bitrate_ RTC_GUARDED_BY(&sequenced_checker_); + std::unique_ptr<rtc::BitrateAllocationStrategy> bitrate_allocation_strategy_ + RTC_GUARDED_BY(&sequenced_checker_); +}; + +} // namespace webrtc +#endif // CALL_BITRATE_ALLOCATOR_H_ diff --git a/third_party/libwebrtc/webrtc/call/bitrate_allocator_gn/moz.build b/third_party/libwebrtc/webrtc/call/bitrate_allocator_gn/moz.build new file mode 100644 index 0000000000..c11194442c --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/bitrate_allocator_gn/moz.build @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/bitrate_allocator.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("bitrate_allocator_gn") diff --git a/third_party/libwebrtc/webrtc/call/bitrate_allocator_unittest.cc b/third_party/libwebrtc/webrtc/call/bitrate_allocator_unittest.cc new file mode 100644 index 0000000000..c0786a073d --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/bitrate_allocator_unittest.cc @@ -0,0 +1,777 @@ +/* + * 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 <algorithm> +#include <memory> +#include <vector> + +#include "call/bitrate_allocator.h" +#include "modules/bitrate_controller/include/bitrate_controller.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::NiceMock; +using ::testing::_; + +namespace webrtc { + +class MockLimitObserver : public BitrateAllocator::LimitObserver { + public: + MOCK_METHOD2(OnAllocationLimitsChanged, + void(uint32_t min_send_bitrate_bps, + uint32_t max_padding_bitrate_bps)); +}; + +class TestBitrateObserver : public BitrateAllocatorObserver { + public: + TestBitrateObserver() + : last_bitrate_bps_(0), + last_fraction_loss_(0), + last_rtt_ms_(0), + last_probing_interval_ms_(0), + protection_ratio_(0.0) {} + + void SetBitrateProtectionRatio(double protection_ratio) { + protection_ratio_ = protection_ratio; + } + + uint32_t OnBitrateUpdated(uint32_t bitrate_bps, + uint8_t fraction_loss, + int64_t rtt, + int64_t probing_interval_ms) override { + last_bitrate_bps_ = bitrate_bps; + last_fraction_loss_ = fraction_loss; + last_rtt_ms_ = rtt; + last_probing_interval_ms_ = probing_interval_ms; + return bitrate_bps * protection_ratio_; + } + uint32_t last_bitrate_bps_; + uint8_t last_fraction_loss_; + int64_t last_rtt_ms_; + int last_probing_interval_ms_; + double protection_ratio_; +}; + +namespace { +constexpr int64_t kDefaultProbingIntervalMs = 3000; +} + +class BitrateAllocatorTest : public ::testing::Test { + protected: + BitrateAllocatorTest() : allocator_(new BitrateAllocator(&limit_observer_)) { + allocator_->OnNetworkChanged(300000u, 0, 0, kDefaultProbingIntervalMs); + } + ~BitrateAllocatorTest() {} + + NiceMock<MockLimitObserver> limit_observer_; + std::unique_ptr<BitrateAllocator> allocator_; +}; + +TEST_F(BitrateAllocatorTest, UpdatingBitrateObserver) { + TestBitrateObserver bitrate_observer; + const uint32_t kMinSendBitrateBps = 100000; + const uint32_t kPadUpToBitrateBps = 50000; + + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(kMinSendBitrateBps, + kPadUpToBitrateBps)); + allocator_->AddObserver(&bitrate_observer, kMinSendBitrateBps, 1500000, + kPadUpToBitrateBps, true, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer)); + allocator_->OnNetworkChanged(200000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer)); + + // TODO(pbos): Expect capping to 1.5M instead of 3M when not boosting the max + // bitrate for FEC/retransmissions (see todo in BitrateAllocator). + allocator_->OnNetworkChanged(4000000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(3000000, allocator_->GetStartBitrate(&bitrate_observer)); + + // Expect |max_padding_bitrate_bps| to change to 0 if the observer is updated. + EXPECT_CALL(limit_observer_, + OnAllocationLimitsChanged(kMinSendBitrateBps, 0)); + allocator_->AddObserver(&bitrate_observer, kMinSendBitrateBps, 4000000, 0, + true, ""); + EXPECT_EQ(4000000, allocator_->GetStartBitrate(&bitrate_observer)); + + allocator_->AddObserver(&bitrate_observer, kMinSendBitrateBps, 1500000, 0, + true, ""); + EXPECT_EQ(3000000, allocator_->GetStartBitrate(&bitrate_observer)); + EXPECT_EQ(3000000u, bitrate_observer.last_bitrate_bps_); + allocator_->OnNetworkChanged(1500000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(1500000u, bitrate_observer.last_bitrate_bps_); +} + +TEST_F(BitrateAllocatorTest, TwoBitrateObserversOneRtcpObserver) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(100000, 0)); + allocator_->AddObserver(&bitrate_observer_1, 100000, 300000, 0, true, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + EXPECT_CALL(limit_observer_, + OnAllocationLimitsChanged(100000 + 200000, 0)); + allocator_->AddObserver(&bitrate_observer_2, 200000, 300000, 0, true, ""); + EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2)); + + // Test too low start bitrate, hence lower than sum of min. Min bitrates + // will + // be allocated to all observers. + allocator_->OnNetworkChanged(200000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_); + EXPECT_EQ(50, bitrate_observer_1.last_rtt_ms_); + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); + EXPECT_EQ(0, bitrate_observer_2.last_fraction_loss_); + EXPECT_EQ(50, bitrate_observer_2.last_rtt_ms_); + + // Test a bitrate which should be distributed equally. + allocator_->OnNetworkChanged(500000, 0, 50, kDefaultProbingIntervalMs); + const uint32_t kBitrateToShare = 500000 - 200000 - 100000; + EXPECT_EQ(100000u + kBitrateToShare / 2, + bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(200000u + kBitrateToShare / 2, + bitrate_observer_2.last_bitrate_bps_); + + // Limited by 2x max bitrates since we leave room for FEC and + // retransmissions. + allocator_->OnNetworkChanged(1500000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(600000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(600000u, bitrate_observer_2.last_bitrate_bps_); + + // Verify that if the bandwidth estimate is set to zero, the allocated + // rate is + // zero. + allocator_->OnNetworkChanged(0, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); +} + +TEST_F(BitrateAllocatorTest, RemoveObserverTriggersLimitObserver) { + TestBitrateObserver bitrate_observer; + const uint32_t kMinSendBitrateBps = 100000; + const uint32_t kPadUpToBitrateBps = 50000; + + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(kMinSendBitrateBps, + kPadUpToBitrateBps)); + allocator_->AddObserver(&bitrate_observer, kMinSendBitrateBps, 1500000, + kPadUpToBitrateBps, true, ""); + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0)); + allocator_->RemoveObserver(&bitrate_observer); +} + +class BitrateAllocatorTestNoEnforceMin : public ::testing::Test { + protected: + BitrateAllocatorTestNoEnforceMin() + : allocator_(new BitrateAllocator(&limit_observer_)) { + allocator_->OnNetworkChanged(300000u, 0, 0, kDefaultProbingIntervalMs); + } + ~BitrateAllocatorTestNoEnforceMin() {} + + NiceMock<MockLimitObserver> limit_observer_; + std::unique_ptr<BitrateAllocator> allocator_; +}; + +// The following three tests verify enforcing a minimum bitrate works as +// intended. +TEST_F(BitrateAllocatorTestNoEnforceMin, OneBitrateObserver) { + TestBitrateObserver bitrate_observer_1; + // Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since + // AddObserver is called with |enforce_min_bitrate| = false. + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 120000)); + allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + + // High BWE. + allocator_->OnNetworkChanged(150000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_bps_); + + // Low BWE. + allocator_->OnNetworkChanged(10000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0)); + allocator_->RemoveObserver(&bitrate_observer_1); +} + +TEST_F(BitrateAllocatorTestNoEnforceMin, ThreeBitrateObservers) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + TestBitrateObserver bitrate_observer_3; + // Set up the observers with min bitrates at 100000, 200000, and 300000. + allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + + allocator_->AddObserver(&bitrate_observer_2, 200000, 400000, 0, false, ""); + EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2)); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + + allocator_->AddObserver(&bitrate_observer_3, 300000, 400000, 0, false, ""); + EXPECT_EQ(0, allocator_->GetStartBitrate(&bitrate_observer_3)); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); + + // High BWE. Make sure the controllers get a fair share of the surplus (i.e., + // what is left after each controller gets its min rate). + allocator_->OnNetworkChanged(690000, 0, 0, kDefaultProbingIntervalMs); + // Verify that each observer gets its min rate (sum of min rates is 600000), + // and that the remaining 90000 is divided equally among the three. + uint32_t bitrate_to_share = 690000u - 100000u - 200000u - 300000u; + EXPECT_EQ(100000u + bitrate_to_share / 3, + bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(200000u + bitrate_to_share / 3, + bitrate_observer_2.last_bitrate_bps_); + EXPECT_EQ(300000u + bitrate_to_share / 3, + bitrate_observer_3.last_bitrate_bps_); + + // BWE below the sum of observer's min bitrate. + allocator_->OnNetworkChanged(300000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); // Min bitrate. + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); // Min bitrate. + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_bps_); // Nothing. + + // Increased BWE, but still below the sum of configured min bitrates for all + // observers and too little for observer 3. 1 and 2 will share the rest. + allocator_->OnNetworkChanged(500000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(200000u, bitrate_observer_1.last_bitrate_bps_); // Min + split. + EXPECT_EQ(300000u, bitrate_observer_2.last_bitrate_bps_); // Min + split. + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_bps_); // Nothing. + + // Below min for all. + allocator_->OnNetworkChanged(10000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_bps_); + + // Verify that zero estimated bandwidth, means that that all gets zero, + // regardless of set min bitrate. + allocator_->OnNetworkChanged(0, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_bps_); + + allocator_->RemoveObserver(&bitrate_observer_1); + allocator_->RemoveObserver(&bitrate_observer_2); + allocator_->RemoveObserver(&bitrate_observer_3); +} + +TEST_F(BitrateAllocatorTestNoEnforceMin, OneBitrateObserverWithPacketLoss) { + TestBitrateObserver bitrate_observer; + // Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since + // AddObserver is called with |enforce_min_bitrate| = false. + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 168000)); + allocator_->AddObserver(&bitrate_observer, 100000, 400000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer)); + + // High BWE. + allocator_->OnNetworkChanged(150000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(150000u, bitrate_observer.last_bitrate_bps_); + + // Add loss and use a part of the bitrate for protection. + double protection_ratio = 0.4; + uint8_t fraction_loss = protection_ratio * 256; + bitrate_observer.SetBitrateProtectionRatio(protection_ratio); + allocator_->OnNetworkChanged(200000, 0, fraction_loss, + kDefaultProbingIntervalMs); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_bps_); + + // Above the min threshold, but not enough given the protection used. + allocator_->OnNetworkChanged(139000, 0, fraction_loss, + kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_); + + // Verify the hysteresis is added for the protection. + allocator_->OnNetworkChanged(150000, 0, fraction_loss, + kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_); + + // Just enough to enable video again. + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0)); + allocator_->OnNetworkChanged(168000, 0, fraction_loss, + kDefaultProbingIntervalMs); + EXPECT_EQ(168000u, bitrate_observer.last_bitrate_bps_); + + // Remove all protection and make sure video is not paused as earlier. + bitrate_observer.SetBitrateProtectionRatio(0.0); + allocator_->OnNetworkChanged(140000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(140000u, bitrate_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(139000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(139000u, bitrate_observer.last_bitrate_bps_); + + allocator_->RemoveObserver(&bitrate_observer); +} + +TEST_F(BitrateAllocatorTestNoEnforceMin, TwoBitrateObserverWithPacketLoss) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + + allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + allocator_->AddObserver(&bitrate_observer_2, 200000, 400000, 0, false, ""); + EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2)); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + + // Enough bitrate for both. + bitrate_observer_2.SetBitrateProtectionRatio(0.5); + allocator_->OnNetworkChanged(300000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); + + // Above min for observer 2, but too little given the protection used. + allocator_->OnNetworkChanged(330000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(330000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->OnNetworkChanged(100000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->OnNetworkChanged(99999, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->OnNetworkChanged(119000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->OnNetworkChanged(120000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(120000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + // Verify the protection is accounted for before resuming observer 2. + allocator_->OnNetworkChanged(429000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(400000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->OnNetworkChanged(430000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(330000u, bitrate_observer_2.last_bitrate_bps_); + + allocator_->RemoveObserver(&bitrate_observer_1); + allocator_->RemoveObserver(&bitrate_observer_2); +} + +TEST_F(BitrateAllocatorTest, ThreeBitrateObserversLowBweEnforceMin) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + TestBitrateObserver bitrate_observer_3; + + allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, true, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + + allocator_->AddObserver(&bitrate_observer_2, 200000, 400000, 0, true, ""); + EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2)); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); + + allocator_->AddObserver(&bitrate_observer_3, 300000, 400000, 0, true, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_3)); + EXPECT_EQ(100000, static_cast<int>(bitrate_observer_1.last_bitrate_bps_)); + EXPECT_EQ(200000, static_cast<int>(bitrate_observer_2.last_bitrate_bps_)); + + // Low BWE. Verify that all observers still get their respective min + // bitrate. + allocator_->OnNetworkChanged(1000, 0, 0, kDefaultProbingIntervalMs); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); // Min cap. + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); // Min cap. + EXPECT_EQ(300000u, bitrate_observer_3.last_bitrate_bps_); // Min cap. + + allocator_->RemoveObserver(&bitrate_observer_1); + allocator_->RemoveObserver(&bitrate_observer_2); + allocator_->RemoveObserver(&bitrate_observer_3); +} + +TEST_F(BitrateAllocatorTest, AddObserverWhileNetworkDown) { + TestBitrateObserver bitrate_observer_1; + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(50000, 0)); + + allocator_->AddObserver(&bitrate_observer_1, 50000, 400000, 0, true, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1)); + + // Set network down, ie, no available bitrate. + allocator_->OnNetworkChanged(0, 0, 0, kDefaultProbingIntervalMs); + + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + + TestBitrateObserver bitrate_observer_2; + // Adding an observer while the network is down should not affect the limits. + EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(50000 + 50000, 0)); + allocator_->AddObserver(&bitrate_observer_2, 50000, 400000, 0, true, ""); + + // Expect the start_bitrate to be set as if the network was still up but that + // the new observer have been notified that the network is down. + EXPECT_EQ(300000 / 2, allocator_->GetStartBitrate(&bitrate_observer_2)); + EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_); + + // Set network back up. + allocator_->OnNetworkChanged(1500000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(750000u, bitrate_observer_1.last_bitrate_bps_); + EXPECT_EQ(750000u, bitrate_observer_2.last_bitrate_bps_); +} + +TEST_F(BitrateAllocatorTest, MixedEnforecedConfigs) { + TestBitrateObserver enforced_observer; + allocator_->AddObserver(&enforced_observer, 6000, 30000, 0, true, ""); + EXPECT_EQ(60000, allocator_->GetStartBitrate(&enforced_observer)); + + TestBitrateObserver not_enforced_observer; + allocator_->AddObserver(¬_enforced_observer, 30000, 2500000, 0, false, ""); + EXPECT_EQ(270000, allocator_->GetStartBitrate(¬_enforced_observer)); + EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(36000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(30000u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(35000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(5000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(36000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(55000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(56000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(50000u, not_enforced_observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(56000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(16000u, enforced_observer.last_bitrate_bps_); + EXPECT_EQ(40000u, not_enforced_observer.last_bitrate_bps_); + + allocator_->RemoveObserver(&enforced_observer); + allocator_->RemoveObserver(¬_enforced_observer); +} + +TEST_F(BitrateAllocatorTest, AvoidToggleAbsolute) { + TestBitrateObserver observer; + allocator_->AddObserver(&observer, 30000, 300000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer)); + + allocator_->OnNetworkChanged(30000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(30000u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(20000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(30000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(49000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(50000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(50000u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(30000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(30000u, observer.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer); +} + +TEST_F(BitrateAllocatorTest, AvoidTogglePercent) { + TestBitrateObserver observer; + allocator_->AddObserver(&observer, 300000, 600000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer)); + + allocator_->OnNetworkChanged(300000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(300000u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(200000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(300000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(329000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(0u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(330000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(330000u, observer.last_bitrate_bps_); + + allocator_->OnNetworkChanged(300000, 0, 50, kDefaultProbingIntervalMs); + EXPECT_EQ(300000u, observer.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer); +} + +TEST_F(BitrateAllocatorTest, PassProbingInterval) { + TestBitrateObserver observer; + allocator_->AddObserver(&observer, 300000, 600000, 0, false, ""); + EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer)); + + allocator_->OnNetworkChanged(300000, 0, 50, 5000); + EXPECT_EQ(5000, observer.last_probing_interval_ms_); + + allocator_->RemoveObserver(&observer); +} + +TEST_F(BitrateAllocatorTest, PriorityRateOneObserverBasic) { + TestBitrateObserver observer; + const uint32_t kMinSendBitrateBps = 10; + const uint32_t kMaxSendBitrateBps = 60; + const uint32_t kNetworkBandwidthBps = 30; + + allocator_->AddObserver(&observer, kMinSendBitrateBps, kMaxSendBitrateBps, 0, + true, "", 2.0); + allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0, + kDefaultProbingIntervalMs); + + EXPECT_EQ(kNetworkBandwidthBps, observer.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer); +} + +// Tests that two observers with the same bitrate priority are allocated +// their bitrate evenly. +TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBasic) { + TestBitrateObserver observer_low_1; + TestBitrateObserver observer_low_2; + const uint32_t kMinSendBitrateBps = 10; + const uint32_t kMaxSendBitrateBps = 60; + const uint32_t kNetworkBandwidthBps = 60; + allocator_->AddObserver(&observer_low_1, kMinSendBitrateBps, + kMaxSendBitrateBps, 0, false, "low1", 2.0); + allocator_->AddObserver(&observer_low_2, kMinSendBitrateBps, + kMaxSendBitrateBps, 0, false, "low2", 2.0); + allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0, + kDefaultProbingIntervalMs); + + EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_1.last_bitrate_bps_); + EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_2.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low_1); + allocator_->RemoveObserver(&observer_low_2); +} + +// Tests that there is no difference in functionality when the min bitrate is +// enforced. +TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBasicMinEnforced) { + TestBitrateObserver observer_low_1; + TestBitrateObserver observer_low_2; + const uint32_t kMinSendBitrateBps = 0; + const uint32_t kMaxSendBitrateBps = 60; + const uint32_t kNetworkBandwidthBps = 60; + allocator_->AddObserver(&observer_low_1, kMinSendBitrateBps, + kMaxSendBitrateBps, 0, true, "low1", 2.0); + allocator_->AddObserver(&observer_low_2, kMinSendBitrateBps, + kMaxSendBitrateBps, 0, true, "low2", 2.0); + allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0, + kDefaultProbingIntervalMs); + + EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_1.last_bitrate_bps_); + EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_2.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low_1); + allocator_->RemoveObserver(&observer_low_2); +} + +// Tests that if the available bandwidth is the sum of the max bitrate +// of all observers, they will be allocated their max. +TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBothAllocatedMax) { + TestBitrateObserver observer_low; + TestBitrateObserver observer_mid; + const uint32_t kMinSendBitrateBps = 0; + const uint32_t kMaxSendBitrateBps = 60; + const uint32_t kNetworkBandwidthBps = kMaxSendBitrateBps * 2; + allocator_->AddObserver(&observer_low, kMinSendBitrateBps, kMaxSendBitrateBps, + 0, true, "low", 2.0); + allocator_->AddObserver(&observer_mid, kMinSendBitrateBps, kMaxSendBitrateBps, + 0, true, "mid", 4.0); + allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0, + kDefaultProbingIntervalMs); + + EXPECT_EQ(kMaxSendBitrateBps, observer_low.last_bitrate_bps_); + EXPECT_EQ(kMaxSendBitrateBps, observer_mid.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); +} + +// Tests that after a higher bitrate priority observer has been allocated its +// max bitrate the lower priority observer will then be allocated the remaining +// bitrate. +TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversOneAllocatedToMax) { + TestBitrateObserver observer_low; + TestBitrateObserver observer_mid; + allocator_->AddObserver(&observer_low, 10, 50, 0, false, "low", 2.0); + allocator_->AddObserver(&observer_mid, 10, 50, 0, false, "mid", 4.0); + allocator_->OnNetworkChanged(90, 0, 0, kDefaultProbingIntervalMs); + + EXPECT_EQ(40u, observer_low.last_bitrate_bps_); + EXPECT_EQ(50u, observer_mid.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); +} + +// Tests that three observers with three different bitrate priorities will all +// be allocated bitrate according to their relative bitrate priority. +TEST_F(BitrateAllocatorTest, + PriorityRateThreeObserversAllocatedRelativeAmounts) { + TestBitrateObserver observer_low; + TestBitrateObserver observer_mid; + TestBitrateObserver observer_high; + const uint32_t kMaxBitrate = 100; + // Not enough bandwidth to fill any observer's max bitrate. + const uint32_t kNetworkBandwidthBps = 70; + const double kLowBitratePriority = 2.0; + const double kMidBitratePriority = 4.0; + const double kHighBitratePriority = 8.0; + const double kTotalBitratePriority = + kLowBitratePriority + kMidBitratePriority + kHighBitratePriority; + allocator_->AddObserver(&observer_low, 0, kMaxBitrate, 0, false, "low", + kLowBitratePriority); + allocator_->AddObserver(&observer_mid, 0, kMaxBitrate, 0, false, "mid", + kMidBitratePriority); + allocator_->AddObserver(&observer_high, 0, kMaxBitrate, 0, false, "high", + kHighBitratePriority); + allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0, + kDefaultProbingIntervalMs); + + const double kLowFractionAllocated = + kLowBitratePriority / kTotalBitratePriority; + const double kMidFractionAllocated = + kMidBitratePriority / kTotalBitratePriority; + const double kHighFractionAllocated = + kHighBitratePriority / kTotalBitratePriority; + EXPECT_EQ(kLowFractionAllocated * kNetworkBandwidthBps, + observer_low.last_bitrate_bps_); + EXPECT_EQ(kMidFractionAllocated * kNetworkBandwidthBps, + observer_mid.last_bitrate_bps_); + EXPECT_EQ(kHighFractionAllocated * kNetworkBandwidthBps, + observer_high.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); + allocator_->RemoveObserver(&observer_high); +} + +// Tests that after the high priority observer has been allocated its maximum +// bitrate, the other two observers are still allocated bitrate according to +// their relative bitrate priority. +TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversHighAllocatedToMax) { + TestBitrateObserver observer_low; + const double kLowBitratePriority = 2.0; + TestBitrateObserver observer_mid; + const double kMidBitratePriority = 4.0; + TestBitrateObserver observer_high; + const double kHighBitratePriority = 8.0; + + const uint32_t kAvailableBitrate = 90; + const uint32_t kMaxBitrate = 40; + const uint32_t kMinBitrate = 10; + // Remaining bitrate after allocating to all mins and knowing that the high + // priority observer will have its max bitrate allocated. + const uint32_t kRemainingBitrate = + kAvailableBitrate - kMaxBitrate - (2 * kMinBitrate); + + allocator_->AddObserver(&observer_low, kMinBitrate, kMaxBitrate, 0, false, + "low", kLowBitratePriority); + allocator_->AddObserver(&observer_mid, kMinBitrate, kMaxBitrate, 0, false, + "mid", kMidBitratePriority); + allocator_->AddObserver(&observer_high, kMinBitrate, kMaxBitrate, 0, false, + "high", kHighBitratePriority); + allocator_->OnNetworkChanged(kAvailableBitrate, 0, 0, + kDefaultProbingIntervalMs); + + const double kLowFractionAllocated = + kLowBitratePriority / (kLowBitratePriority + kMidBitratePriority); + const double kMidFractionAllocated = + kMidBitratePriority / (kLowBitratePriority + kMidBitratePriority); + EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kLowFractionAllocated), + observer_low.last_bitrate_bps_); + EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kMidFractionAllocated), + observer_mid.last_bitrate_bps_); + EXPECT_EQ(40u, observer_high.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); + allocator_->RemoveObserver(&observer_high); +} + +// Tests that after the low priority observer has been allocated its maximum +// bitrate, the other two observers are still allocated bitrate according to +// their relative bitrate priority. +TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversLowAllocatedToMax) { + TestBitrateObserver observer_low; + const double kLowBitratePriority = 2.0; + const uint32_t kLowMaxBitrate = 10; + TestBitrateObserver observer_mid; + const double kMidBitratePriority = 4.0; + TestBitrateObserver observer_high; + const double kHighBitratePriority = 8.0; + + const uint32_t kMinBitrate = 0; + const uint32_t kMaxBitrate = 60; + const uint32_t kAvailableBitrate = 100; + // Remaining bitrate knowing that the low priority observer is allocated its + // max bitrate. We know this because it is allocated 2.0/14.0 (1/7) of the + // available bitrate, so 70 bps would be sufficient network bandwidth. + const uint32_t kRemainingBitrate = kAvailableBitrate - kLowMaxBitrate; + + allocator_->AddObserver(&observer_low, kMinBitrate, kLowMaxBitrate, 0, false, + "low", kLowBitratePriority); + allocator_->AddObserver(&observer_mid, kMinBitrate, kMaxBitrate, 0, false, + "mid", kMidBitratePriority); + allocator_->AddObserver(&observer_high, kMinBitrate, kMaxBitrate, 0, false, + "high", kHighBitratePriority); + allocator_->OnNetworkChanged(kAvailableBitrate, 0, 0, + kDefaultProbingIntervalMs); + + const double kMidFractionAllocated = + kMidBitratePriority / (kMidBitratePriority + kHighBitratePriority); + const double kHighFractionAllocated = + kHighBitratePriority / (kMidBitratePriority + kHighBitratePriority); + EXPECT_EQ(kLowMaxBitrate, observer_low.last_bitrate_bps_); + EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kMidFractionAllocated), + observer_mid.last_bitrate_bps_); + EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kHighFractionAllocated), + observer_high.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); + allocator_->RemoveObserver(&observer_high); +} + +// Tests that after two observers are allocated bitrate to their max, the +// the remaining observer is allocated what's left appropriately. This test +// handles an edge case where the medium and high observer reach their +// "relative" max allocation at the same time. The high has 40 to allocate +// above its min, and the mid has 20 to allocate above its min, which scaled +// by their bitrate priority is the same for each. +TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversTwoAllocatedToMax) { + TestBitrateObserver observer_low; + TestBitrateObserver observer_mid; + TestBitrateObserver observer_high; + allocator_->AddObserver(&observer_low, 10, 40, 0, false, "low", 2.0); + // Scaled allocation above the min allocation is the same for these two, + // meaning they will get allocated their max at the same time. + // Scaled (target allocation) = (max - min) / bitrate priority + allocator_->AddObserver(&observer_mid, 10, 30, 0, false, "mid", 4.0); + allocator_->AddObserver(&observer_high, 10, 50, 0, false, "high", 8.0); + allocator_->OnNetworkChanged(110, 0, 0, kDefaultProbingIntervalMs); + + EXPECT_EQ(30u, observer_low.last_bitrate_bps_); + EXPECT_EQ(30u, observer_mid.last_bitrate_bps_); + EXPECT_EQ(50u, observer_high.last_bitrate_bps_); + + allocator_->RemoveObserver(&observer_low); + allocator_->RemoveObserver(&observer_mid); + allocator_->RemoveObserver(&observer_high); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/bitrate_estimator_tests.cc b/third_party/libwebrtc/webrtc/call/bitrate_estimator_tests.cc new file mode 100644 index 0000000000..4c397875f9 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/bitrate_estimator_tests.cc @@ -0,0 +1,324 @@ +/* + * 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 <functional> +#include <list> +#include <memory> +#include <string> + +#include "call/call.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread_annotations.h" +#include "test/call_test.h" +#include "test/direct_transport.h" +#include "test/encoder_settings.h" +#include "test/fake_decoder.h" +#include "test/fake_encoder.h" +#include "test/frame_generator_capturer.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { +// Note: If you consider to re-use this class, think twice and instead consider +// writing tests that don't depend on the logging system. +class LogObserver { + public: + LogObserver() { rtc::LogMessage::AddLogToStream(&callback_, rtc::LS_INFO); } + + ~LogObserver() { rtc::LogMessage::RemoveLogToStream(&callback_); } + + void PushExpectedLogLine(const std::string& expected_log_line) { + callback_.PushExpectedLogLine(expected_log_line); + } + + bool Wait() { return callback_.Wait(); } + + private: + class Callback : public rtc::LogSink { + public: + Callback() : done_(false, false) {} + + void OnLogMessage(const std::string& message) override { + rtc::CritScope lock(&crit_sect_); + // 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); + } + + int num_popped = 0; + while (!received_log_lines_.empty() && !expected_log_lines_.empty()) { + std::string a = received_log_lines_.front(); + std::string b = expected_log_lines_.front(); + received_log_lines_.pop_front(); + expected_log_lines_.pop_front(); + num_popped++; + EXPECT_TRUE(a.find(b) != std::string::npos) << a << " != " << b; + } + if (expected_log_lines_.size() <= 0) { + if (num_popped > 0) { + done_.Set(); + } + return; + } + } + + bool Wait() { return done_.Wait(test::CallTest::kDefaultTimeoutMs); } + + void PushExpectedLogLine(const std::string& expected_log_line) { + rtc::CritScope lock(&crit_sect_); + expected_log_lines_.push_back(expected_log_line); + } + + private: + typedef std::list<std::string> Strings; + rtc::CriticalSection crit_sect_; + Strings received_log_lines_ RTC_GUARDED_BY(crit_sect_); + Strings expected_log_lines_ RTC_GUARDED_BY(crit_sect_); + rtc::Event done_; + }; + + Callback callback_; +}; +} // namespace + +static const int kTOFExtensionId = 4; +static const int kASTExtensionId = 5; + +class BitrateEstimatorTest : public test::CallTest { + public: + BitrateEstimatorTest() : receive_config_(nullptr) {} + + virtual ~BitrateEstimatorTest() { EXPECT_TRUE(streams_.empty()); } + + virtual void SetUp() { + task_queue_.SendTask([this]() { + Call::Config config(event_log_.get()); + receiver_call_.reset(Call::Create(config)); + sender_call_.reset(Call::Create(config)); + + send_transport_.reset(new test::DirectTransport( + &task_queue_, sender_call_.get(), payload_type_map_)); + send_transport_->SetReceiver(receiver_call_->Receiver()); + receive_transport_.reset(new test::DirectTransport( + &task_queue_, receiver_call_.get(), payload_type_map_)); + receive_transport_->SetReceiver(sender_call_->Receiver()); + + video_send_config_ = VideoSendStream::Config(send_transport_.get()); + video_send_config_.rtp.ssrcs.push_back(kVideoSendSsrcs[0]); + // Encoders will be set separately per stream. + video_send_config_.encoder_settings.encoder = nullptr; + video_send_config_.encoder_settings.payload_name = "FAKE"; + video_send_config_.encoder_settings.payload_type = + kFakeVideoSendPayloadType; + test::FillEncoderConfiguration(1, &video_encoder_config_); + + receive_config_ = VideoReceiveStream::Config(receive_transport_.get()); + // receive_config_.decoders will be set by every stream separately. + receive_config_.rtp.remote_ssrc = video_send_config_.rtp.ssrcs[0]; + receive_config_.rtp.local_ssrc = kReceiverLocalVideoSsrc; + receive_config_.rtp.remb = true; + receive_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTimestampOffsetUri, kTOFExtensionId)); + receive_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTimeUri, kASTExtensionId)); + }); + } + + virtual void TearDown() { + task_queue_.SendTask([this]() { + std::for_each(streams_.begin(), streams_.end(), + std::mem_fun(&Stream::StopSending)); + + while (!streams_.empty()) { + delete streams_.back(); + streams_.pop_back(); + } + + send_transport_.reset(); + receive_transport_.reset(); + + receiver_call_.reset(); + sender_call_.reset(); + }); + } + + protected: + friend class Stream; + + class Stream { + public: + explicit Stream(BitrateEstimatorTest* test) + : test_(test), + is_sending_receiving_(false), + send_stream_(nullptr), + frame_generator_capturer_(), + fake_encoder_(Clock::GetRealTimeClock()), + fake_decoder_() { + test_->video_send_config_.rtp.ssrcs[0]++; + test_->video_send_config_.encoder_settings.encoder = &fake_encoder_; + send_stream_ = test_->sender_call_->CreateVideoSendStream( + test_->video_send_config_.Copy(), + test_->video_encoder_config_.Copy()); + RTC_DCHECK_EQ(1, test_->video_encoder_config_.number_of_streams); + frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( + kDefaultWidth, kDefaultHeight, kDefaultFramerate, + Clock::GetRealTimeClock())); + send_stream_->SetSource( + frame_generator_capturer_.get(), + VideoSendStream::DegradationPreference::kMaintainFramerate); + send_stream_->Start(); + frame_generator_capturer_->Start(); + + VideoReceiveStream::Decoder decoder; + decoder.decoder = &fake_decoder_; + decoder.payload_type = + test_->video_send_config_.encoder_settings.payload_type; + decoder.payload_name = + test_->video_send_config_.encoder_settings.payload_name; + test_->receive_config_.decoders.clear(); + test_->receive_config_.decoders.push_back(decoder); + test_->receive_config_.rtp.remote_ssrc = + test_->video_send_config_.rtp.ssrcs[0]; + test_->receive_config_.rtp.local_ssrc++; + test_->receive_config_.renderer = &test->fake_renderer_; + video_receive_stream_ = test_->receiver_call_->CreateVideoReceiveStream( + test_->receive_config_.Copy()); + video_receive_stream_->Start(); + is_sending_receiving_ = true; + } + + ~Stream() { + EXPECT_FALSE(is_sending_receiving_); + test_->sender_call_->DestroyVideoSendStream(send_stream_); + frame_generator_capturer_.reset(nullptr); + send_stream_ = nullptr; + if (video_receive_stream_) { + test_->receiver_call_->DestroyVideoReceiveStream(video_receive_stream_); + video_receive_stream_ = nullptr; + } + } + + void StopSending() { + if (is_sending_receiving_) { + frame_generator_capturer_->Stop(); + send_stream_->Stop(); + if (video_receive_stream_) { + video_receive_stream_->Stop(); + } + is_sending_receiving_ = false; + } + } + + private: + BitrateEstimatorTest* test_; + bool is_sending_receiving_; + VideoSendStream* send_stream_; + VideoReceiveStream* video_receive_stream_; + std::unique_ptr<test::FrameGeneratorCapturer> frame_generator_capturer_; + test::FakeEncoder fake_encoder_; + test::FakeDecoder fake_decoder_; + }; + + LogObserver receiver_log_; + std::unique_ptr<test::DirectTransport> send_transport_; + std::unique_ptr<test::DirectTransport> receive_transport_; + std::unique_ptr<Call> sender_call_; + std::unique_ptr<Call> receiver_call_; + VideoReceiveStream::Config receive_config_; + std::vector<Stream*> streams_; +}; + +static const char* kAbsSendTimeLog = + "RemoteBitrateEstimatorAbsSendTime: Instantiating."; +static const char* kSingleStreamLog = + "RemoteBitrateEstimatorSingleStream: Instantiating."; + +TEST_F(BitrateEstimatorTest, InstantiatesTOFPerDefaultForVideo) { + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTimestampOffsetUri, kTOFExtensionId)); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); +} + +TEST_F(BitrateEstimatorTest, ImmediatelySwitchToASTForVideo) { + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTimeUri, kASTExtensionId)); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); +} + +TEST_F(BitrateEstimatorTest, SwitchesToASTForVideo) { + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTimestampOffsetUri, kTOFExtensionId)); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); + + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions[0] = + RtpExtension(RtpExtension::kAbsSendTimeUri, kASTExtensionId); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); +} + +// This test is flaky. See webrtc:5790. +TEST_F(BitrateEstimatorTest, DISABLED_SwitchesToASTThenBackToTOFForVideo) { + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTimestampOffsetUri, kTOFExtensionId)); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); + + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions[0] = + RtpExtension(RtpExtension::kAbsSendTimeUri, kASTExtensionId); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + streams_.push_back(new Stream(this)); + }); + EXPECT_TRUE(receiver_log_.Wait()); + + task_queue_.SendTask([this]() { + video_send_config_.rtp.extensions[0] = + RtpExtension(RtpExtension::kTimestampOffsetUri, kTOFExtensionId); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine( + "WrappingBitrateEstimator: Switching to transmission time offset RBE."); + streams_.push_back(new Stream(this)); + streams_[0]->StopSending(); + streams_[1]->StopSending(); + }); + EXPECT_TRUE(receiver_log_.Wait()); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/call.cc b/third_party/libwebrtc/webrtc/call/call.cc new file mode 100644 index 0000000000..9b38a63326 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call.cc @@ -0,0 +1,1475 @@ +/* + * 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 <string.h> +#include <algorithm> +#include <map> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include "api/optional.h" +#include "audio/audio_receive_stream.h" +#include "audio/audio_send_stream.h" +#include "audio/audio_state.h" +#include "audio/scoped_voe_interface.h" +#include "audio/time_interval.h" +#include "call/bitrate_allocator.h" +#include "call/call.h" +#include "call/flexfec_receive_stream_impl.h" +#include "call/rtp_stream_receiver_controller.h" +#include "call/rtp_transport_controller_send.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_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.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/rtc_event_log.h" +#include "logging/rtc_event_log/rtc_stream_config.h" +#include "modules/bitrate_controller/include/bitrate_controller.h" +#include "modules/congestion_controller/include/receive_side_congestion_controller.h" +#include "modules/rtp_rtcp/include/flexfec_receiver.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/include/rtp_header_parser.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/utility/include/process_thread.h" +#include "rtc_base/basictypes.h" +#include "rtc_base/checks.h" +#include "rtc_base/constructormagic.h" +#include "rtc_base/location.h" +#include "rtc_base/logging.h" +#include "rtc_base/ptr_util.h" +#include "rtc_base/sequenced_task_checker.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/trace_event.h" +#include "system_wrappers/include/clock.h" +#include "system_wrappers/include/cpu_info.h" +#include "system_wrappers/include/metrics.h" +#include "system_wrappers/include/rw_lock_wrapper.h" +#include "video/call_stats.h" +#include "video/send_delay_stats.h" +#include "video/stats_counter.h" +#include "video/video_receive_stream.h" +#include "video/video_send_stream.h" + +namespace webrtc { + +namespace { + +// TODO(nisse): This really begs for a shared context struct. +bool UseSendSideBwe(const std::vector<RtpExtension>& extensions, + bool transport_cc) { + if (!transport_cc) + return false; + for (const auto& extension : extensions) { + if (extension.uri == RtpExtension::kTransportSequenceNumberUri) + return true; + } + return false; +} + +bool UseSendSideBwe(const VideoReceiveStream::Config& config) { + return UseSendSideBwe(config.rtp.extensions, config.rtp.transport_cc); +} + +bool UseSendSideBwe(const AudioReceiveStream::Config& config) { + return UseSendSideBwe(config.rtp.extensions, config.rtp.transport_cc); +} + +bool UseSendSideBwe(const FlexfecReceiveStream::Config& config) { + return UseSendSideBwe(config.rtp_header_extensions, config.transport_cc); +} + +const int* FindKeyByValue(const std::map<int, int>& m, int v) { + for (const auto& kv : m) { + if (kv.second == v) + return &kv.first; + } + return nullptr; +} + +std::unique_ptr<rtclog::StreamConfig> CreateRtcLogStreamConfig( + const VideoReceiveStream::Config& config) { + auto rtclog_config = rtc::MakeUnique<rtclog::StreamConfig>(); + rtclog_config->remote_ssrc = config.rtp.remote_ssrc; + rtclog_config->local_ssrc = config.rtp.local_ssrc; + rtclog_config->rtx_ssrc = config.rtp.rtx_ssrc; + rtclog_config->rtcp_mode = config.rtp.rtcp_mode; + rtclog_config->remb = config.rtp.remb; + rtclog_config->rtp_extensions = config.rtp.extensions; + + for (const auto& d : config.decoders) { + const int* search = + FindKeyByValue(config.rtp.rtx_associated_payload_types, d.payload_type); + rtclog_config->codecs.emplace_back(d.payload_name, d.payload_type, + search ? *search : 0); + } + return rtclog_config; +} + +std::unique_ptr<rtclog::StreamConfig> CreateRtcLogStreamConfig( + const VideoSendStream::Config& config, + size_t ssrc_index) { + auto rtclog_config = rtc::MakeUnique<rtclog::StreamConfig>(); + rtclog_config->local_ssrc = config.rtp.ssrcs[ssrc_index]; + if (ssrc_index < config.rtp.rtx.ssrcs.size()) { + rtclog_config->rtx_ssrc = config.rtp.rtx.ssrcs[ssrc_index]; + } + rtclog_config->rtcp_mode = config.rtp.rtcp_mode; + rtclog_config->rtp_extensions = config.rtp.extensions; + + rtclog_config->codecs.emplace_back(config.encoder_settings.payload_name, + config.encoder_settings.payload_type, + config.rtp.rtx.payload_type); + return rtclog_config; +} + +std::unique_ptr<rtclog::StreamConfig> CreateRtcLogStreamConfig( + const AudioReceiveStream::Config& config) { + auto rtclog_config = rtc::MakeUnique<rtclog::StreamConfig>(); + rtclog_config->remote_ssrc = config.rtp.remote_ssrc; + rtclog_config->local_ssrc = config.rtp.local_ssrc; + rtclog_config->rtp_extensions = config.rtp.extensions; + return rtclog_config; +} + +std::unique_ptr<rtclog::StreamConfig> CreateRtcLogStreamConfig( + const AudioSendStream::Config& config) { + auto rtclog_config = rtc::MakeUnique<rtclog::StreamConfig>(); + rtclog_config->local_ssrc = config.rtp.ssrc; + rtclog_config->rtp_extensions = config.rtp.extensions; + if (config.send_codec_spec) { + rtclog_config->codecs.emplace_back(config.send_codec_spec->format.name, + config.send_codec_spec->payload_type, 0); + } + return rtclog_config; +} + +} // namespace + +namespace internal { + +class Call : public webrtc::Call, + public PacketReceiver, + public RecoveredPacketReceiver, + public SendSideCongestionController::Observer, + public BitrateAllocator::LimitObserver { + public: + Call(const Call::Config& config, + std::unique_ptr<RtpTransportControllerSendInterface> transport_send); + virtual ~Call(); + + // Implements webrtc::Call. + PacketReceiver* Receiver() override; + + webrtc::AudioSendStream* CreateAudioSendStream( + const webrtc::AudioSendStream::Config& config) override; + void DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) override; + + webrtc::AudioReceiveStream* CreateAudioReceiveStream( + const webrtc::AudioReceiveStream::Config& config) override; + void DestroyAudioReceiveStream( + webrtc::AudioReceiveStream* receive_stream) override; + + webrtc::VideoSendStream* CreateVideoSendStream( + webrtc::VideoSendStream::Config config, + VideoEncoderConfig encoder_config) override; + void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) override; + + webrtc::VideoReceiveStream* CreateVideoReceiveStream( + webrtc::VideoReceiveStream::Config configuration) override; + void DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) override; + + FlexfecReceiveStream* CreateFlexfecReceiveStream( + const FlexfecReceiveStream::Config& config) override; + void DestroyFlexfecReceiveStream( + FlexfecReceiveStream* receive_stream) override; + + Stats GetStats() const override; + + // Implements PacketReceiver. + DeliveryStatus DeliverPacket(MediaType media_type, + const uint8_t* packet, + size_t length, + const PacketTime& packet_time) override; + + // Implements RecoveredPacketReceiver. + void OnRecoveredPacket(const uint8_t* packet, size_t length) override; + + void SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) override; + + void SetBitrateConfigMask( + const webrtc::Call::Config::BitrateConfigMask& bitrate_config) override; + + void SetBitrateAllocationStrategy( + std::unique_ptr<rtc::BitrateAllocationStrategy> + bitrate_allocation_strategy) override; + + void SignalChannelNetworkState(MediaType media, NetworkState state) override; + + void OnTransportOverheadChanged(MediaType media, + int transport_overhead_per_packet) override; + + void OnNetworkRouteChanged(const std::string& transport_name, + const rtc::NetworkRoute& network_route) override; + + void OnSentPacket(const rtc::SentPacket& sent_packet) override; + + // Implements BitrateObserver. + void OnNetworkChanged(uint32_t bitrate_bps, + uint8_t fraction_loss, + int64_t rtt_ms, + int64_t probing_interval_ms) override; + + // Implements BitrateAllocator::LimitObserver. + void OnAllocationLimitsChanged(uint32_t min_send_bitrate_bps, + uint32_t max_padding_bitrate_bps) override; + + VoiceEngine* voice_engine() override { + internal::AudioState* audio_state = + static_cast<internal::AudioState*>(config_.audio_state.get()); + if (audio_state) + return audio_state->voice_engine(); + else + return nullptr; + } + + private: + DeliveryStatus DeliverRtcp(MediaType media_type, const uint8_t* packet, + size_t length); + DeliveryStatus DeliverRtp(MediaType media_type, + const uint8_t* packet, + size_t length, + const PacketTime& packet_time); + void ConfigureSync(const std::string& sync_group) + RTC_EXCLUSIVE_LOCKS_REQUIRED(receive_crit_); + + void NotifyBweOfReceivedPacket(const RtpPacketReceived& packet, + MediaType media_type) + RTC_SHARED_LOCKS_REQUIRED(receive_crit_); + + void UpdateSendHistograms(int64_t first_sent_packet_ms) + RTC_EXCLUSIVE_LOCKS_REQUIRED(&bitrate_crit_); + void UpdateReceiveHistograms(); + void UpdateHistograms(); + void UpdateAggregateNetworkState(); + + // Applies update to the BitrateConfig cached in |config_|, restarting + // bandwidth estimation from |new_start| if set. + void UpdateCurrentBitrateConfig(const rtc::Optional<int>& new_start); + + Clock* const clock_; + + const int num_cpu_cores_; + const std::unique_ptr<ProcessThread> module_process_thread_; + const std::unique_ptr<ProcessThread> pacer_thread_; + const std::unique_ptr<CallStats> call_stats_; + const std::unique_ptr<BitrateAllocator> bitrate_allocator_; + Call::Config config_; + rtc::SequencedTaskChecker configuration_sequence_checker_; + + NetworkState audio_network_state_; + NetworkState video_network_state_; + + std::unique_ptr<RWLockWrapper> receive_crit_; + // Audio, Video, and FlexFEC receive streams are owned by the client that + // creates them. + std::set<AudioReceiveStream*> audio_receive_streams_ + RTC_GUARDED_BY(receive_crit_); + std::set<VideoReceiveStream*> video_receive_streams_ + RTC_GUARDED_BY(receive_crit_); + + std::map<std::string, AudioReceiveStream*> sync_stream_mapping_ + RTC_GUARDED_BY(receive_crit_); + + // TODO(nisse): Should eventually be injected at creation, + // with a single object in the bundled case. + RtpStreamReceiverController audio_receiver_controller_; + RtpStreamReceiverController video_receiver_controller_; + + // This extra map is used for receive processing which is + // independent of media type. + + // TODO(nisse): In the RTP transport refactoring, we should have a + // single mapping from ssrc to a more abstract receive stream, with + // accessor methods for all configuration we need at this level. + struct ReceiveRtpConfig { + ReceiveRtpConfig() = default; // Needed by std::map + ReceiveRtpConfig(const std::vector<RtpExtension>& extensions, + bool use_send_side_bwe) + : extensions(extensions), use_send_side_bwe(use_send_side_bwe) {} + + // Registered RTP header extensions for each stream. Note that RTP header + // extensions are negotiated per track ("m= line") in the SDP, but we have + // no notion of tracks at the Call level. We therefore store the RTP header + // extensions per SSRC instead, which leads to some storage overhead. + RtpHeaderExtensionMap extensions; + // Set if both RTP extension the RTCP feedback message needed for + // send side BWE are negotiated. + bool use_send_side_bwe = false; + }; + std::map<uint32_t, ReceiveRtpConfig> receive_rtp_config_ + RTC_GUARDED_BY(receive_crit_); + + std::unique_ptr<RWLockWrapper> send_crit_; + // Audio and Video send streams are owned by the client that creates them. + std::map<uint32_t, AudioSendStream*> audio_send_ssrcs_ + RTC_GUARDED_BY(send_crit_); + std::map<uint32_t, VideoSendStream*> video_send_ssrcs_ + RTC_GUARDED_BY(send_crit_); + std::set<VideoSendStream*> video_send_streams_ RTC_GUARDED_BY(send_crit_); + + using RtpStateMap = std::map<uint32_t, RtpState>; + RtpStateMap suspended_audio_send_ssrcs_ + RTC_GUARDED_BY(configuration_sequence_checker_); + RtpStateMap suspended_video_send_ssrcs_ + RTC_GUARDED_BY(configuration_sequence_checker_); + + using RtpPayloadStateMap = std::map<uint32_t, RtpPayloadState>; + RtpPayloadStateMap suspended_video_payload_states_ + RTC_GUARDED_BY(configuration_sequence_checker_); + + webrtc::RtcEventLog* event_log_; + + // The following members are only accessed (exclusively) from one thread and + // from the destructor, and therefore doesn't need any explicit + // synchronization. + RateCounter received_bytes_per_second_counter_; + RateCounter received_audio_bytes_per_second_counter_; + RateCounter received_video_bytes_per_second_counter_; + RateCounter received_rtcp_bytes_per_second_counter_; + rtc::Optional<int64_t> first_received_rtp_audio_ms_; + rtc::Optional<int64_t> last_received_rtp_audio_ms_; + rtc::Optional<int64_t> first_received_rtp_video_ms_; + rtc::Optional<int64_t> last_received_rtp_video_ms_; + TimeInterval sent_rtp_audio_timer_ms_; + + // TODO(holmer): Remove this lock once BitrateController no longer calls + // OnNetworkChanged from multiple threads. + rtc::CriticalSection bitrate_crit_; + uint32_t min_allocated_send_bitrate_bps_ RTC_GUARDED_BY(&bitrate_crit_); + uint32_t configured_max_padding_bitrate_bps_ RTC_GUARDED_BY(&bitrate_crit_); + AvgCounter estimated_send_bitrate_kbps_counter_ + RTC_GUARDED_BY(&bitrate_crit_); + AvgCounter pacer_bitrate_kbps_counter_ RTC_GUARDED_BY(&bitrate_crit_); + + std::map<std::string, rtc::NetworkRoute> network_routes_; + + std::unique_ptr<RtpTransportControllerSendInterface> transport_send_; + ReceiveSideCongestionController receive_side_cc_; + const std::unique_ptr<SendDelayStats> video_send_delay_stats_; + const int64_t start_ms_; + // TODO(perkj): |worker_queue_| is supposed to replace + // |module_process_thread_|. + // |worker_queue| is defined last to ensure all pending tasks are cancelled + // and deleted before any other members. + rtc::TaskQueue worker_queue_; + + // The config mask set by SetBitrateConfigMask. + // 0 <= min <= start <= max + Config::BitrateConfigMask bitrate_config_mask_; + + // The config set by SetBitrateConfig. + // min >= 0, start != 0, max == -1 || max > 0 + Config::BitrateConfig base_bitrate_config_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Call); +}; +} // namespace internal + +std::string Call::Stats::ToString(int64_t time_ms) const { + std::stringstream ss; + ss << "Call stats: " << time_ms << ", {"; + ss << "send_bw_bps: " << send_bandwidth_bps << ", "; + ss << "recv_bw_bps: " << recv_bandwidth_bps << ", "; + ss << "max_pad_bps: " << max_padding_bitrate_bps << ", "; + ss << "pacer_delay_ms: " << pacer_delay_ms << ", "; + ss << "rtt_ms: " << rtt_ms; + ss << '}'; + return ss.str(); +} + +Call* Call::Create(const Call::Config& config) { + return new internal::Call(config, + rtc::MakeUnique<RtpTransportControllerSend>( + Clock::GetRealTimeClock(), config.event_log)); +} + +Call* Call::Create( + const Call::Config& config, + std::unique_ptr<RtpTransportControllerSendInterface> transport_send) { + return new internal::Call(config, std::move(transport_send)); +} + +namespace internal { + +Call::Call(const Call::Config& config, + std::unique_ptr<RtpTransportControllerSendInterface> transport_send) + : clock_(Clock::GetRealTimeClock()), + num_cpu_cores_(CpuInfo::DetectNumberOfCores()), + module_process_thread_(ProcessThread::Create("ModuleProcessThread")), + pacer_thread_(ProcessThread::Create("PacerThread")), + call_stats_(new CallStats(clock_)), + bitrate_allocator_(new BitrateAllocator(this)), + config_(config), + audio_network_state_(kNetworkDown), + video_network_state_(kNetworkDown), + receive_crit_(RWLockWrapper::CreateRWLock()), + send_crit_(RWLockWrapper::CreateRWLock()), + event_log_(config.event_log), + received_bytes_per_second_counter_(clock_, nullptr, true), + received_audio_bytes_per_second_counter_(clock_, nullptr, true), + received_video_bytes_per_second_counter_(clock_, nullptr, true), + received_rtcp_bytes_per_second_counter_(clock_, nullptr, true), + min_allocated_send_bitrate_bps_(0), + configured_max_padding_bitrate_bps_(0), + estimated_send_bitrate_kbps_counter_(clock_, nullptr, true), + pacer_bitrate_kbps_counter_(clock_, nullptr, true), + receive_side_cc_(clock_, transport_send->packet_router()), + video_send_delay_stats_(new SendDelayStats(clock_)), + start_ms_(clock_->TimeInMilliseconds()), + worker_queue_("call_worker_queue"), + base_bitrate_config_(config.bitrate_config) { + RTC_DCHECK(config.event_log != nullptr); + RTC_DCHECK_GE(config.bitrate_config.min_bitrate_bps, 0); + RTC_DCHECK_GE(config.bitrate_config.start_bitrate_bps, + config.bitrate_config.min_bitrate_bps); + if (config.bitrate_config.max_bitrate_bps != -1) { + RTC_DCHECK_GE(config.bitrate_config.max_bitrate_bps, + config.bitrate_config.start_bitrate_bps); + } + transport_send->send_side_cc()->RegisterNetworkObserver(this); + transport_send_ = std::move(transport_send); + transport_send_->send_side_cc()->SignalNetworkState(kNetworkDown); + transport_send_->send_side_cc()->SetBweBitrates( + config_.bitrate_config.min_bitrate_bps, + config_.bitrate_config.start_bitrate_bps, + config_.bitrate_config.max_bitrate_bps); + call_stats_->RegisterStatsObserver(&receive_side_cc_); + call_stats_->RegisterStatsObserver(transport_send_->send_side_cc()); + + // We have to attach the pacer to the pacer thread before starting the + // module process thread to avoid a race accessing the process thread + // both from the process thread and the pacer thread. + pacer_thread_->RegisterModule(transport_send_->pacer(), RTC_FROM_HERE); + pacer_thread_->RegisterModule( + receive_side_cc_.GetRemoteBitrateEstimator(true), RTC_FROM_HERE); + pacer_thread_->Start(); + + module_process_thread_->RegisterModule(call_stats_.get(), RTC_FROM_HERE); + module_process_thread_->RegisterModule(&receive_side_cc_, RTC_FROM_HERE); + module_process_thread_->RegisterModule(transport_send_->send_side_cc(), + RTC_FROM_HERE); + module_process_thread_->Start(); +} + +Call::~Call() { + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + RTC_CHECK(audio_send_ssrcs_.empty()); + RTC_CHECK(video_send_ssrcs_.empty()); + RTC_CHECK(video_send_streams_.empty()); + RTC_CHECK(audio_receive_streams_.empty()); + RTC_CHECK(video_receive_streams_.empty()); + + // The send-side congestion controller must be de-registered prior to + // the pacer thread being stopped to avoid a race when accessing the + // pacer thread object on the module process thread at the same time as + // the pacer thread is stopped. + module_process_thread_->DeRegisterModule(transport_send_->send_side_cc()); + pacer_thread_->Stop(); + pacer_thread_->DeRegisterModule(transport_send_->pacer()); + pacer_thread_->DeRegisterModule( + receive_side_cc_.GetRemoteBitrateEstimator(true)); + module_process_thread_->DeRegisterModule(&receive_side_cc_); + module_process_thread_->DeRegisterModule(call_stats_.get()); + module_process_thread_->Stop(); + call_stats_->DeregisterStatsObserver(&receive_side_cc_); + call_stats_->DeregisterStatsObserver(transport_send_->send_side_cc()); + + int64_t first_sent_packet_ms = + transport_send_->send_side_cc()->GetFirstPacketTimeMs(); + // Only update histograms after process threads have been shut down, so that + // they won't try to concurrently update stats. + { + rtc::CritScope lock(&bitrate_crit_); + UpdateSendHistograms(first_sent_packet_ms); + } + UpdateReceiveHistograms(); + UpdateHistograms(); +} + +void Call::UpdateHistograms() { + RTC_HISTOGRAM_COUNTS_100000( + "WebRTC.Call.LifetimeInSeconds", + (clock_->TimeInMilliseconds() - start_ms_) / 1000); +} + +void Call::UpdateSendHistograms(int64_t first_sent_packet_ms) { + if (first_sent_packet_ms == -1) + return; + if (!sent_rtp_audio_timer_ms_.Empty()) { + RTC_HISTOGRAM_COUNTS_100000( + "WebRTC.Call.TimeSendingAudioRtpPacketsInSeconds", + sent_rtp_audio_timer_ms_.Length() / 1000); + } + int64_t elapsed_sec = + (clock_->TimeInMilliseconds() - first_sent_packet_ms) / 1000; + if (elapsed_sec < metrics::kMinRunTimeInSeconds) + return; + const int kMinRequiredPeriodicSamples = 5; + AggregatedStats send_bitrate_stats = + estimated_send_bitrate_kbps_counter_.ProcessAndGetStats(); + if (send_bitrate_stats.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.EstimatedSendBitrateInKbps", + send_bitrate_stats.average); + RTC_LOG(LS_INFO) << "WebRTC.Call.EstimatedSendBitrateInKbps, " + << send_bitrate_stats.ToString(); + } + AggregatedStats pacer_bitrate_stats = + pacer_bitrate_kbps_counter_.ProcessAndGetStats(); + if (pacer_bitrate_stats.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.PacerBitrateInKbps", + pacer_bitrate_stats.average); + RTC_LOG(LS_INFO) << "WebRTC.Call.PacerBitrateInKbps, " + << pacer_bitrate_stats.ToString(); + } +} + +void Call::UpdateReceiveHistograms() { + if (first_received_rtp_audio_ms_) { + RTC_HISTOGRAM_COUNTS_100000( + "WebRTC.Call.TimeReceivingAudioRtpPacketsInSeconds", + (*last_received_rtp_audio_ms_ - *first_received_rtp_audio_ms_) / 1000); + } + if (first_received_rtp_video_ms_) { + RTC_HISTOGRAM_COUNTS_100000( + "WebRTC.Call.TimeReceivingVideoRtpPacketsInSeconds", + (*last_received_rtp_video_ms_ - *first_received_rtp_video_ms_) / 1000); + } + const int kMinRequiredPeriodicSamples = 5; + AggregatedStats video_bytes_per_sec = + received_video_bytes_per_second_counter_.GetStats(); + if (video_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.VideoBitrateReceivedInKbps", + video_bytes_per_sec.average * 8 / 1000); + RTC_LOG(LS_INFO) << "WebRTC.Call.VideoBitrateReceivedInBps, " + << video_bytes_per_sec.ToStringWithMultiplier(8); + } + AggregatedStats audio_bytes_per_sec = + received_audio_bytes_per_second_counter_.GetStats(); + if (audio_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.AudioBitrateReceivedInKbps", + audio_bytes_per_sec.average * 8 / 1000); + RTC_LOG(LS_INFO) << "WebRTC.Call.AudioBitrateReceivedInBps, " + << audio_bytes_per_sec.ToStringWithMultiplier(8); + } + AggregatedStats rtcp_bytes_per_sec = + received_rtcp_bytes_per_second_counter_.GetStats(); + if (rtcp_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.RtcpBitrateReceivedInBps", + rtcp_bytes_per_sec.average * 8); + RTC_LOG(LS_INFO) << "WebRTC.Call.RtcpBitrateReceivedInBps, " + << rtcp_bytes_per_sec.ToStringWithMultiplier(8); + } + AggregatedStats recv_bytes_per_sec = + received_bytes_per_second_counter_.GetStats(); + if (recv_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.BitrateReceivedInKbps", + recv_bytes_per_sec.average * 8 / 1000); + RTC_LOG(LS_INFO) << "WebRTC.Call.BitrateReceivedInBps, " + << recv_bytes_per_sec.ToStringWithMultiplier(8); + } +} + +PacketReceiver* Call::Receiver() { + //Mozilla: Called from STS thread while delivering packets + //RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + return this; +} + +webrtc::AudioSendStream* Call::CreateAudioSendStream( + const webrtc::AudioSendStream::Config& config) { + TRACE_EVENT0("webrtc", "Call::CreateAudioSendStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + event_log_->Log(rtc::MakeUnique<RtcEventAudioSendStreamConfig>( + CreateRtcLogStreamConfig(config))); + + rtc::Optional<RtpState> suspended_rtp_state; + { + const auto& iter = suspended_audio_send_ssrcs_.find(config.rtp.ssrc); + if (iter != suspended_audio_send_ssrcs_.end()) { + suspended_rtp_state.emplace(iter->second); + } + } + + AudioSendStream* send_stream = new AudioSendStream( + config, config_.audio_state, &worker_queue_, transport_send_.get(), + bitrate_allocator_.get(), event_log_, call_stats_->rtcp_rtt_stats(), + suspended_rtp_state); + { + WriteLockScoped write_lock(*send_crit_); + RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) == + audio_send_ssrcs_.end()); + audio_send_ssrcs_[config.rtp.ssrc] = send_stream; + } + { + ReadLockScoped read_lock(*receive_crit_); + for (AudioReceiveStream* stream : audio_receive_streams_) { + if (stream->config().rtp.local_ssrc == config.rtp.ssrc) { + stream->AssociateSendStream(send_stream); + } + } + } + send_stream->SignalNetworkState(audio_network_state_); + UpdateAggregateNetworkState(); + return send_stream; +} + +void Call::DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) { + TRACE_EVENT0("webrtc", "Call::DestroyAudioSendStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + RTC_DCHECK(send_stream != nullptr); + + send_stream->Stop(); + + const uint32_t ssrc = send_stream->GetConfig().rtp.ssrc; + webrtc::internal::AudioSendStream* audio_send_stream = + static_cast<webrtc::internal::AudioSendStream*>(send_stream); + suspended_audio_send_ssrcs_[ssrc] = audio_send_stream->GetRtpState(); + { + WriteLockScoped write_lock(*send_crit_); + size_t num_deleted = audio_send_ssrcs_.erase(ssrc); + RTC_DCHECK_EQ(1, num_deleted); + } + { + ReadLockScoped read_lock(*receive_crit_); + for (AudioReceiveStream* stream : audio_receive_streams_) { + if (stream->config().rtp.local_ssrc == ssrc) { + stream->AssociateSendStream(nullptr); + } + } + } + UpdateAggregateNetworkState(); + sent_rtp_audio_timer_ms_.Extend(audio_send_stream->GetActiveLifetime()); + delete send_stream; +} + +webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream( + const webrtc::AudioReceiveStream::Config& config) { + TRACE_EVENT0("webrtc", "Call::CreateAudioReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + event_log_->Log(rtc::MakeUnique<RtcEventAudioReceiveStreamConfig>( + CreateRtcLogStreamConfig(config))); + AudioReceiveStream* receive_stream = new AudioReceiveStream( + &audio_receiver_controller_, transport_send_->packet_router(), config, + config_.audio_state, event_log_); + { + WriteLockScoped write_lock(*receive_crit_); + receive_rtp_config_[config.rtp.remote_ssrc] = + ReceiveRtpConfig(config.rtp.extensions, UseSendSideBwe(config)); + audio_receive_streams_.insert(receive_stream); + + ConfigureSync(config.sync_group); + } + { + ReadLockScoped read_lock(*send_crit_); + auto it = audio_send_ssrcs_.find(config.rtp.local_ssrc); + if (it != audio_send_ssrcs_.end()) { + receive_stream->AssociateSendStream(it->second); + } + } + receive_stream->SignalNetworkState(audio_network_state_); + UpdateAggregateNetworkState(); + return receive_stream; +} + +void Call::DestroyAudioReceiveStream( + webrtc::AudioReceiveStream* receive_stream) { + TRACE_EVENT0("webrtc", "Call::DestroyAudioReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + RTC_DCHECK(receive_stream != nullptr); + webrtc::internal::AudioReceiveStream* audio_receive_stream = + static_cast<webrtc::internal::AudioReceiveStream*>(receive_stream); + { + WriteLockScoped write_lock(*receive_crit_); + const AudioReceiveStream::Config& config = audio_receive_stream->config(); + uint32_t ssrc = config.rtp.remote_ssrc; + receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(config)) + ->RemoveStream(ssrc); + audio_receive_streams_.erase(audio_receive_stream); + const std::string& sync_group = audio_receive_stream->config().sync_group; + const auto it = sync_stream_mapping_.find(sync_group); + if (it != sync_stream_mapping_.end() && + it->second == audio_receive_stream) { + sync_stream_mapping_.erase(it); + ConfigureSync(sync_group); + } + receive_rtp_config_.erase(ssrc); + } + UpdateAggregateNetworkState(); + delete audio_receive_stream; +} + +webrtc::VideoSendStream* Call::CreateVideoSendStream( + webrtc::VideoSendStream::Config config, + VideoEncoderConfig encoder_config) { + TRACE_EVENT0("webrtc", "Call::CreateVideoSendStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + video_send_delay_stats_->AddSsrcs(config); + for (size_t ssrc_index = 0; ssrc_index < config.rtp.ssrcs.size(); + ++ssrc_index) { + event_log_->Log(rtc::MakeUnique<RtcEventVideoSendStreamConfig>( + CreateRtcLogStreamConfig(config, ssrc_index))); + } + + // TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if + // the call has already started. + // Copy ssrcs from |config| since |config| is moved. + std::vector<uint32_t> ssrcs = config.rtp.ssrcs; + VideoSendStream* send_stream = new VideoSendStream( + num_cpu_cores_, module_process_thread_.get(), &worker_queue_, + call_stats_.get(), 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_); + + { + WriteLockScoped write_lock(*send_crit_); + for (uint32_t ssrc : ssrcs) { + RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); + video_send_ssrcs_[ssrc] = send_stream; + } + video_send_streams_.insert(send_stream); + } + send_stream->SignalNetworkState(video_network_state_); + UpdateAggregateNetworkState(); + + return send_stream; +} + +void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { + TRACE_EVENT0("webrtc", "Call::DestroyVideoSendStream"); + RTC_DCHECK(send_stream != nullptr); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + send_stream->Stop(); + + VideoSendStream* send_stream_impl = nullptr; + { + WriteLockScoped write_lock(*send_crit_); + auto it = video_send_ssrcs_.begin(); + while (it != video_send_ssrcs_.end()) { + if (it->second == static_cast<VideoSendStream*>(send_stream)) { + send_stream_impl = it->second; + video_send_ssrcs_.erase(it++); + } else { + ++it; + } + } + video_send_streams_.erase(send_stream_impl); + } + RTC_CHECK(send_stream_impl != nullptr); + + 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; + } + for (const auto& kv : rtp_payload_states) { + suspended_video_payload_states_[kv.first] = kv.second; + } + + UpdateAggregateNetworkState(); + delete send_stream_impl; +} + +webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( + webrtc::VideoReceiveStream::Config configuration) { + TRACE_EVENT0("webrtc", "Call::CreateVideoReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + VideoReceiveStream* receive_stream = new VideoReceiveStream( + &video_receiver_controller_, num_cpu_cores_, + transport_send_->packet_router(), std::move(configuration), + module_process_thread_.get(), call_stats_.get()); + + const webrtc::VideoReceiveStream::Config& config = receive_stream->config(); + ReceiveRtpConfig receive_config(config.rtp.extensions, + UseSendSideBwe(config)); + { + WriteLockScoped write_lock(*receive_crit_); + if (config.rtp.rtx_ssrc) { + // We record identical config for the rtx stream as for the main + // 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_[config.rtp.rtx_ssrc] = receive_config; + } + receive_rtp_config_[config.rtp.remote_ssrc] = receive_config; + video_receive_streams_.insert(receive_stream); + ConfigureSync(config.sync_group); + } + receive_stream->SignalNetworkState(video_network_state_); + UpdateAggregateNetworkState(); + event_log_->Log(rtc::MakeUnique<RtcEventVideoReceiveStreamConfig>( + CreateRtcLogStreamConfig(config))); + return receive_stream; +} + +void Call::DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) { + TRACE_EVENT0("webrtc", "Call::DestroyVideoReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + RTC_DCHECK(receive_stream != nullptr); + VideoReceiveStream* receive_stream_impl = + static_cast<VideoReceiveStream*>(receive_stream); + const VideoReceiveStream::Config& config = receive_stream_impl->config(); + { + WriteLockScoped write_lock(*receive_crit_); + // 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(config.rtp.remote_ssrc); + if (config.rtp.rtx_ssrc) { + receive_rtp_config_.erase(config.rtp.rtx_ssrc); + } + video_receive_streams_.erase(receive_stream_impl); + ConfigureSync(config.sync_group); + } + + receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(config)) + ->RemoveStream(config.rtp.remote_ssrc); + + UpdateAggregateNetworkState(); + delete receive_stream_impl; +} + +FlexfecReceiveStream* Call::CreateFlexfecReceiveStream( + const FlexfecReceiveStream::Config& config) { + TRACE_EVENT0("webrtc", "Call::CreateFlexfecReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + RecoveredPacketReceiver* recovered_packet_receiver = this; + + FlexfecReceiveStreamImpl* receive_stream; + { + WriteLockScoped write_lock(*receive_crit_); + // Unlike the video and audio receive streams, + // FlexfecReceiveStream implements RtpPacketSinkInterface itself, + // and hence its constructor passes its |this| pointer to + // video_receiver_controller_->CreateStream(). Calling the + // constructor while holding |receive_crit_| ensures that we don't + // call OnRtpPacket until the constructor is finished and the + // object is in a valid state. + // TODO(nisse): Fix constructor so that it can be moved outside of + // this locked scope. + receive_stream = new FlexfecReceiveStreamImpl( + &video_receiver_controller_, config, recovered_packet_receiver, + call_stats_->rtcp_rtt_stats(), module_process_thread_.get()); + + RTC_DCHECK(receive_rtp_config_.find(config.remote_ssrc) == + receive_rtp_config_.end()); + receive_rtp_config_[config.remote_ssrc] = + ReceiveRtpConfig(config.rtp_header_extensions, UseSendSideBwe(config)); + } + + // TODO(brandtr): Store config in RtcEventLog here. + + return receive_stream; +} + +void Call::DestroyFlexfecReceiveStream(FlexfecReceiveStream* receive_stream) { + TRACE_EVENT0("webrtc", "Call::DestroyFlexfecReceiveStream"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + RTC_DCHECK(receive_stream != nullptr); + { + WriteLockScoped write_lock(*receive_crit_); + + const FlexfecReceiveStream::Config& config = receive_stream->GetConfig(); + uint32_t ssrc = config.remote_ssrc; + receive_rtp_config_.erase(ssrc); + + // Remove all SSRCs pointing to the FlexfecReceiveStreamImpl to be + // destroyed. + receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(config)) + ->RemoveStream(ssrc); + } + + delete receive_stream; +} + +Call::Stats Call::GetStats() const { + // TODO(solenberg): Some test cases in EndToEndTest use this from a different + // thread. Re-enable once that is fixed. + // RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + Stats stats; + // Fetch available send/receive bitrates. + uint32_t send_bandwidth = 0; + transport_send_->send_side_cc()->AvailableBandwidth(&send_bandwidth); + std::vector<unsigned int> ssrcs; + uint32_t recv_bandwidth = 0; + receive_side_cc_.GetRemoteBitrateEstimator(false)->LatestEstimate( + &ssrcs, &recv_bandwidth); + stats.send_bandwidth_bps = send_bandwidth; + stats.recv_bandwidth_bps = recv_bandwidth; + stats.pacer_delay_ms = + transport_send_->send_side_cc()->GetPacerQueuingDelayMs(); + stats.rtt_ms = call_stats_->rtcp_rtt_stats()->LastProcessedRtt(); + { + rtc::CritScope cs(&bitrate_crit_); + stats.max_padding_bitrate_bps = configured_max_padding_bitrate_bps_; + } + return stats; +} + +void Call::SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) { + TRACE_EVENT0("webrtc", "Call::SetBitrateConfig"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + RTC_DCHECK_GE(bitrate_config.min_bitrate_bps, 0); + RTC_DCHECK_NE(bitrate_config.start_bitrate_bps, 0); + if (bitrate_config.max_bitrate_bps != -1) { + RTC_DCHECK_GT(bitrate_config.max_bitrate_bps, 0); + } + + rtc::Optional<int> new_start; + // Only update the "start" bitrate if it's set, and different from the old + // value. In practice, this value comes from the x-google-start-bitrate codec + // parameter in SDP, and setting the same remote description twice shouldn't + // restart bandwidth estimation. + if (bitrate_config.start_bitrate_bps != -1 && + bitrate_config.start_bitrate_bps != + base_bitrate_config_.start_bitrate_bps) { + new_start.emplace(bitrate_config.start_bitrate_bps); + } + base_bitrate_config_ = bitrate_config; + UpdateCurrentBitrateConfig(new_start); +} + +void Call::SetBitrateConfigMask( + const webrtc::Call::Config::BitrateConfigMask& mask) { + TRACE_EVENT0("webrtc", "Call::SetBitrateConfigMask"); + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + bitrate_config_mask_ = mask; + UpdateCurrentBitrateConfig(mask.start_bitrate_bps); +} + +void Call::UpdateCurrentBitrateConfig(const rtc::Optional<int>& new_start) { + Config::BitrateConfig updated; + updated.min_bitrate_bps = + std::max(bitrate_config_mask_.min_bitrate_bps.value_or(0), + base_bitrate_config_.min_bitrate_bps); + + updated.max_bitrate_bps = + MinPositive(bitrate_config_mask_.max_bitrate_bps.value_or(-1), + base_bitrate_config_.max_bitrate_bps); + + // If the combined min ends up greater than the combined max, the max takes + // priority. + if (updated.max_bitrate_bps != -1 && + updated.min_bitrate_bps > updated.max_bitrate_bps) { + updated.min_bitrate_bps = updated.max_bitrate_bps; + } + + // If there is nothing to update (min/max unchanged, no new bandwidth + // estimation start value), return early. + if (updated.min_bitrate_bps == config_.bitrate_config.min_bitrate_bps && + updated.max_bitrate_bps == config_.bitrate_config.max_bitrate_bps && + !new_start) { + RTC_LOG(LS_VERBOSE) << "WebRTC.Call.UpdateCurrentBitrateConfig: " + << "nothing to update"; + return; + } + + if (new_start) { + // Clamp start by min and max. + updated.start_bitrate_bps = MinPositive( + std::max(*new_start, updated.min_bitrate_bps), updated.max_bitrate_bps); + } else { + updated.start_bitrate_bps = -1; + } + + RTC_LOG(INFO) << "WebRTC.Call.UpdateCurrentBitrateConfig: " + << "calling SetBweBitrates with args (" + << updated.min_bitrate_bps << ", " << updated.start_bitrate_bps + << ", " << updated.max_bitrate_bps << ")"; + transport_send_->send_side_cc()->SetBweBitrates(updated.min_bitrate_bps, + updated.start_bitrate_bps, + updated.max_bitrate_bps); + if (!new_start) { + updated.start_bitrate_bps = config_.bitrate_config.start_bitrate_bps; + } + config_.bitrate_config = updated; +} + +void Call::SetBitrateAllocationStrategy( + std::unique_ptr<rtc::BitrateAllocationStrategy> + bitrate_allocation_strategy) { + if (!worker_queue_.IsCurrent()) { + rtc::BitrateAllocationStrategy* strategy_raw = + bitrate_allocation_strategy.release(); + auto functor = [this, strategy_raw]() { + SetBitrateAllocationStrategy( + rtc::WrapUnique<rtc::BitrateAllocationStrategy>(strategy_raw)); + }; + worker_queue_.PostTask([functor] { functor(); }); + return; + } + RTC_DCHECK_RUN_ON(&worker_queue_); + bitrate_allocator_->SetBitrateAllocationStrategy( + std::move(bitrate_allocation_strategy)); +} + +void Call::SignalChannelNetworkState(MediaType media, NetworkState state) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + switch (media) { + case MediaType::AUDIO: + audio_network_state_ = state; + break; + case MediaType::VIDEO: + video_network_state_ = state; + break; + case MediaType::ANY: + case MediaType::DATA: + RTC_NOTREACHED(); + break; + } + + UpdateAggregateNetworkState(); + { + ReadLockScoped read_lock(*send_crit_); + for (auto& kv : audio_send_ssrcs_) { + kv.second->SignalNetworkState(audio_network_state_); + } + for (auto& kv : video_send_ssrcs_) { + kv.second->SignalNetworkState(video_network_state_); + } + } + { + ReadLockScoped read_lock(*receive_crit_); + for (AudioReceiveStream* audio_receive_stream : audio_receive_streams_) { + audio_receive_stream->SignalNetworkState(audio_network_state_); + } + for (VideoReceiveStream* video_receive_stream : video_receive_streams_) { + video_receive_stream->SignalNetworkState(video_network_state_); + } + } +} + +void Call::OnTransportOverheadChanged(MediaType media, + int transport_overhead_per_packet) { + switch (media) { + case MediaType::AUDIO: { + ReadLockScoped read_lock(*send_crit_); + for (auto& kv : audio_send_ssrcs_) { + kv.second->SetTransportOverhead(transport_overhead_per_packet); + } + break; + } + case MediaType::VIDEO: { + ReadLockScoped read_lock(*send_crit_); + for (auto& kv : video_send_ssrcs_) { + kv.second->SetTransportOverhead(transport_overhead_per_packet); + } + break; + } + case MediaType::ANY: + case MediaType::DATA: + RTC_NOTREACHED(); + break; + } +} + +// TODO(honghaiz): Add tests for this method. +void Call::OnNetworkRouteChanged(const std::string& transport_name, + const rtc::NetworkRoute& network_route) { + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + // Check if the network route is connected. + if (!network_route.connected) { + RTC_LOG(LS_INFO) << "Transport " << transport_name << " is disconnected"; + // TODO(honghaiz): Perhaps handle this in SignalChannelNetworkState and + // consider merging these two methods. + return; + } + + // Check whether the network route has changed on each transport. + auto result = + network_routes_.insert(std::make_pair(transport_name, network_route)); + auto kv = result.first; + bool inserted = result.second; + if (inserted) { + // No need to reset BWE if this is the first time the network connects. + return; + } + if (kv->second != network_route) { + kv->second = network_route; + RTC_LOG(LS_INFO) + << "Network route changed on transport " << transport_name + << ": new local network id " << network_route.local_network_id + << " new remote network id " << network_route.remote_network_id + << " Reset bitrates to min: " << config_.bitrate_config.min_bitrate_bps + << " bps, start: " << config_.bitrate_config.start_bitrate_bps + << " bps, max: " << config_.bitrate_config.start_bitrate_bps + << " bps."; + RTC_DCHECK_GT(config_.bitrate_config.start_bitrate_bps, 0); + transport_send_->send_side_cc()->OnNetworkRouteChanged( + network_route, config_.bitrate_config.start_bitrate_bps, + config_.bitrate_config.min_bitrate_bps, + config_.bitrate_config.max_bitrate_bps); + } +} + +void Call::UpdateAggregateNetworkState() { + RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + + bool have_audio = false; + bool have_video = false; + { + ReadLockScoped read_lock(*send_crit_); + if (audio_send_ssrcs_.size() > 0) + have_audio = true; + if (video_send_ssrcs_.size() > 0) + have_video = true; + } + { + ReadLockScoped read_lock(*receive_crit_); + if (audio_receive_streams_.size() > 0) + have_audio = true; + if (video_receive_streams_.size() > 0) + have_video = true; + } + + NetworkState aggregate_state = kNetworkDown; + if ((have_video && video_network_state_ == kNetworkUp) || + (have_audio && audio_network_state_ == kNetworkUp)) { + aggregate_state = kNetworkUp; + } + + RTC_LOG(LS_INFO) << "UpdateAggregateNetworkState: aggregate_state=" + << (aggregate_state == kNetworkUp ? "up" : "down"); + + transport_send_->send_side_cc()->SignalNetworkState(aggregate_state); +} + +void Call::OnSentPacket(const rtc::SentPacket& sent_packet) { + video_send_delay_stats_->OnSentPacket(sent_packet.packet_id, + clock_->TimeInMilliseconds()); + transport_send_->send_side_cc()->OnSentPacket(sent_packet); +} + +void Call::OnNetworkChanged(uint32_t target_bitrate_bps, + uint8_t fraction_loss, + int64_t rtt_ms, + int64_t probing_interval_ms) { + // TODO(perkj): Consider making sure CongestionController operates on + // |worker_queue_|. + if (!worker_queue_.IsCurrent()) { + worker_queue_.PostTask( + [this, target_bitrate_bps, fraction_loss, rtt_ms, probing_interval_ms] { + OnNetworkChanged(target_bitrate_bps, fraction_loss, rtt_ms, + probing_interval_ms); + }); + return; + } + RTC_DCHECK_RUN_ON(&worker_queue_); + // For controlling the rate of feedback messages. + receive_side_cc_.OnBitrateChanged(target_bitrate_bps); + bitrate_allocator_->OnNetworkChanged(target_bitrate_bps, fraction_loss, + rtt_ms, probing_interval_ms); + + // Ignore updates if bitrate is zero (the aggregate network state is down). + if (target_bitrate_bps == 0) { + rtc::CritScope lock(&bitrate_crit_); + estimated_send_bitrate_kbps_counter_.ProcessAndPause(); + pacer_bitrate_kbps_counter_.ProcessAndPause(); + return; + } + + bool sending_video; + { + ReadLockScoped read_lock(*send_crit_); + sending_video = !video_send_streams_.empty(); + } + + rtc::CritScope lock(&bitrate_crit_); + if (!sending_video) { + // Do not update the stats if we are not sending video. + estimated_send_bitrate_kbps_counter_.ProcessAndPause(); + pacer_bitrate_kbps_counter_.ProcessAndPause(); + return; + } + estimated_send_bitrate_kbps_counter_.Add(target_bitrate_bps / 1000); + // Pacer bitrate may be higher than bitrate estimate if enforcing min bitrate. + uint32_t pacer_bitrate_bps = + std::max(target_bitrate_bps, min_allocated_send_bitrate_bps_); + pacer_bitrate_kbps_counter_.Add(pacer_bitrate_bps / 1000); +} + +void Call::OnAllocationLimitsChanged(uint32_t min_send_bitrate_bps, + uint32_t max_padding_bitrate_bps) { + transport_send_->SetAllocatedSendBitrateLimits(min_send_bitrate_bps, + max_padding_bitrate_bps); + rtc::CritScope lock(&bitrate_crit_); + min_allocated_send_bitrate_bps_ = min_send_bitrate_bps; + configured_max_padding_bitrate_bps_ = max_padding_bitrate_bps; +} + +void Call::ConfigureSync(const std::string& sync_group) { + // 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. + 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 (sync_audio_stream) + sync_stream_mapping_[sync_group] = sync_audio_stream; + size_t num_synced_streams = 0; + for (VideoReceiveStream* video_stream : video_receive_streams_) { + if (video_stream->config().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."; + } + // 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); + } else { + video_stream->SetSync(nullptr); + } + } +} + +PacketReceiver::DeliveryStatus Call::DeliverRtcp(MediaType media_type, + const uint8_t* packet, + size_t length) { + TRACE_EVENT0("webrtc", "Call::DeliverRtcp"); + // TODO(pbos): Make sure it's a valid packet. + // Return DELIVERY_UNKNOWN_SSRC if it can be determined that + // there's no receiver of the packet. + if (received_bytes_per_second_counter_.HasSample()) { + // First RTP packet has been received. + received_bytes_per_second_counter_.Add(static_cast<int>(length)); + received_rtcp_bytes_per_second_counter_.Add(static_cast<int>(length)); + } + bool rtcp_delivered = false; + if (media_type == MediaType::ANY || media_type == MediaType::VIDEO) { + ReadLockScoped read_lock(*receive_crit_); + for (VideoReceiveStream* stream : video_receive_streams_) { + if (stream->DeliverRtcp(packet, length)) + rtcp_delivered = true; + } + } + if (media_type == MediaType::ANY || media_type == MediaType::AUDIO) { + ReadLockScoped read_lock(*receive_crit_); + for (AudioReceiveStream* stream : audio_receive_streams_) { + if (stream->DeliverRtcp(packet, length)) + rtcp_delivered = true; + } + } + if (media_type == MediaType::ANY || media_type == MediaType::VIDEO) { + ReadLockScoped read_lock(*send_crit_); + for (VideoSendStream* stream : video_send_streams_) { + if (stream->DeliverRtcp(packet, length)) + rtcp_delivered = true; + } + } + if (media_type == MediaType::ANY || media_type == MediaType::AUDIO) { + ReadLockScoped read_lock(*send_crit_); + for (auto& kv : audio_send_ssrcs_) { + if (kv.second->DeliverRtcp(packet, length)) + rtcp_delivered = true; + } + } + + if (rtcp_delivered) { + event_log_->Log(rtc::MakeUnique<RtcEventRtcpPacketIncoming>( + rtc::MakeArrayView(packet, length))); + } + + return rtcp_delivered ? DELIVERY_OK : DELIVERY_PACKET_ERROR; +} + +PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, + const uint8_t* packet, + size_t length, + const PacketTime& packet_time) { + TRACE_EVENT0("webrtc", "Call::DeliverRtp"); + + RtpPacketReceived parsed_packet; + if (!parsed_packet.Parse(packet, length)) + return DELIVERY_PACKET_ERROR; + + if (packet_time.timestamp != -1) { + parsed_packet.set_arrival_time_ms((packet_time.timestamp + 500) / 1000); + } else { + parsed_packet.set_arrival_time_ms(clock_->TimeInMilliseconds()); + } + + // We might get RTP keep-alive packets in accordance with RFC6263 section 4.6. + // These are empty (zero length payload) RTP packets with an unsignaled + // payload type. + const bool is_keep_alive_packet = parsed_packet.payload_size() == 0; + + RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO || + is_keep_alive_packet); + + ReadLockScoped read_lock(*receive_crit_); + 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 |receive_crit_| lock. But + // deregistering in the |receive_rtp_config_| map is protected by that lock. + // 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. + return DELIVERY_UNKNOWN_SSRC; + } + parsed_packet.IdentifyExtensions(it->second.extensions); + + NotifyBweOfReceivedPacket(parsed_packet, media_type); + + if (media_type == MediaType::AUDIO) { + if (audio_receiver_controller_.OnRtpPacket(parsed_packet)) { + received_bytes_per_second_counter_.Add(static_cast<int>(length)); + received_audio_bytes_per_second_counter_.Add(static_cast<int>(length)); + event_log_->Log( + rtc::MakeUnique<RtcEventRtpPacketIncoming>(parsed_packet)); + const int64_t arrival_time_ms = parsed_packet.arrival_time_ms(); + if (!first_received_rtp_audio_ms_) { + first_received_rtp_audio_ms_.emplace(arrival_time_ms); + } + last_received_rtp_audio_ms_.emplace(arrival_time_ms); + return DELIVERY_OK; + } + } else if (media_type == MediaType::VIDEO) { + if (video_receiver_controller_.OnRtpPacket(parsed_packet)) { + received_bytes_per_second_counter_.Add(static_cast<int>(length)); + received_video_bytes_per_second_counter_.Add(static_cast<int>(length)); + event_log_->Log( + rtc::MakeUnique<RtcEventRtpPacketIncoming>(parsed_packet)); + const int64_t arrival_time_ms = parsed_packet.arrival_time_ms(); + if (!first_received_rtp_video_ms_) { + first_received_rtp_video_ms_.emplace(arrival_time_ms); + } + last_received_rtp_video_ms_.emplace(arrival_time_ms); + return DELIVERY_OK; + } + } + return DELIVERY_UNKNOWN_SSRC; +} + +PacketReceiver::DeliveryStatus Call::DeliverPacket( + MediaType media_type, + const uint8_t* packet, + size_t length, + const PacketTime& packet_time) { + //Mozilla: Called from STS thread while delivering packets + //RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); + if (RtpHeaderParser::IsRtcp(packet, length)) + return DeliverRtcp(media_type, packet, length); + + return DeliverRtp(media_type, packet, length, packet_time); +} + +void Call::OnRecoveredPacket(const uint8_t* packet, size_t length) { + RtpPacketReceived parsed_packet; + if (!parsed_packet.Parse(packet, length)) + return; + + parsed_packet.set_recovered(true); + + ReadLockScoped read_lock(*receive_crit_); + 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 |receive_crit_| lock. But + // deregistering in the |receive_rtp_config_| map is protected by that lock. + // 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. + return; + } + parsed_packet.IdentifyExtensions(it->second.extensions); + + // TODO(brandtr): Update here when we support protecting audio packets too. + video_receiver_controller_.OnRtpPacket(parsed_packet); +} + +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()) && it->second.use_send_side_bwe; + + RTPHeader header; + packet.GetHeader(&header); + + if (!use_send_side_bwe && header.extension.hasTransportSequenceNumber) { + // Inconsistent configuration of send side BWE. Do nothing. + // TODO(nisse): Without this check, we may produce RTCP feedback + // packets even when not negotiated. But it would be cleaner to + // move the check down to RTCPSender::SendFeedbackPacket, which + // would also help the PacketRouter to select an appropriate rtp + // module in the case that some, but not all, have RTCP feedback + // enabled. + return; + } + // For audio, we only support send side BWE. + if (media_type == MediaType::VIDEO || + (use_send_side_bwe && header.extension.hasTransportSequenceNumber)) { + receive_side_cc_.OnReceivedPacket( + packet.arrival_time_ms(), packet.payload_size() + packet.padding_size(), + header); + } +} + +} // namespace internal + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/call.h b/third_party/libwebrtc/webrtc/call/call.h new file mode 100644 index 0000000000..84b2f73ef0 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call.h @@ -0,0 +1,214 @@ +/* + * 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 CALL_CALL_H_ +#define CALL_CALL_H_ + +#include <algorithm> +#include <memory> +#include <string> +#include <vector> + +#include "api/rtcerror.h" +#include "call/audio_receive_stream.h" +#include "call/audio_send_stream.h" +#include "call/audio_state.h" +#include "call/flexfec_receive_stream.h" +#include "call/rtp_transport_controller_send_interface.h" +#include "call/video_receive_stream.h" +#include "call/video_send_stream.h" +#include "common_types.h" // NOLINT(build/include) +#include "rtc_base/bitrateallocationstrategy.h" +#include "rtc_base/networkroute.h" +#include "rtc_base/platform_file.h" +#include "rtc_base/socket.h" + +namespace webrtc { + +class AudioProcessing; +class RtcEventLog; + +enum class MediaType { + ANY, + AUDIO, + VIDEO, + DATA +}; + +// Like std::min, but considers non-positive values to be unset. +// TODO(zstein): Remove once all callers use rtc::Optional. +template <typename T> +static T MinPositive(T a, T b) { + if (a <= 0) { + return b; + } + if (b <= 0) { + return a; + } + return std::min(a, b); +} + +class PacketReceiver { + public: + enum DeliveryStatus { + DELIVERY_OK, + DELIVERY_UNKNOWN_SSRC, + DELIVERY_PACKET_ERROR, + }; + + virtual DeliveryStatus DeliverPacket(MediaType media_type, + const uint8_t* packet, + size_t length, + const PacketTime& packet_time) = 0; + + protected: + virtual ~PacketReceiver() {} +}; + +// 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. +class Call { + public: + struct Config { + explicit Config(RtcEventLog* event_log) : event_log(event_log) { + RTC_DCHECK(event_log); + } + + static constexpr int kDefaultStartBitrateBps = 300000; + + // Bitrate config used until valid bitrate estimates are calculated. Also + // used to cap total bitrate used. This comes from the remote connection. + struct BitrateConfig { + int min_bitrate_bps = 0; + int start_bitrate_bps = kDefaultStartBitrateBps; + int max_bitrate_bps = -1; + } bitrate_config; + + // The local client's bitrate preferences. The actual configuration used + // is a combination of this and |bitrate_config|. The combination is + // currently more complicated than a simple mask operation (see + // SetBitrateConfig and SetBitrateConfigMask). Assumes that 0 <= min <= + // start <= max holds for set parameters. + struct BitrateConfigMask { + rtc::Optional<int> min_bitrate_bps; + rtc::Optional<int> start_bitrate_bps; + rtc::Optional<int> max_bitrate_bps; + }; + + // AudioState which is possibly shared between multiple calls. + // TODO(solenberg): Change this to a shared_ptr once we can use C++11. + rtc::scoped_refptr<AudioState> audio_state; + + // Audio Processing Module to be used in this call. + // TODO(solenberg): Change this to a shared_ptr once we can use C++11. + AudioProcessing* audio_processing = nullptr; + + // RtcEventLog to use for this call. Required. + // Use webrtc::RtcEventLog::CreateNull() for a null implementation. + RtcEventLog* event_log = nullptr; + }; + + struct Stats { + std::string ToString(int64_t time_ms) const; + + int send_bandwidth_bps = 0; // Estimated available send bandwidth. + int max_padding_bitrate_bps = 0; // Cumulative configured max padding. + int recv_bandwidth_bps = 0; // Estimated available receive bandwidth. + int64_t pacer_delay_ms = 0; + int64_t rtt_ms = -1; + }; + + static Call* Create(const Call::Config& config); + + // Allows mocking |transport_send| for testing. + static Call* Create( + const Call::Config& config, + std::unique_ptr<RtpTransportControllerSendInterface> transport_send); + + virtual AudioSendStream* CreateAudioSendStream( + const AudioSendStream::Config& config) = 0; + virtual void DestroyAudioSendStream(AudioSendStream* send_stream) = 0; + + virtual AudioReceiveStream* CreateAudioReceiveStream( + const AudioReceiveStream::Config& config) = 0; + virtual void DestroyAudioReceiveStream( + AudioReceiveStream* receive_stream) = 0; + + virtual VideoSendStream* CreateVideoSendStream( + VideoSendStream::Config config, + VideoEncoderConfig encoder_config) = 0; + virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0; + + virtual VideoReceiveStream* CreateVideoReceiveStream( + VideoReceiveStream::Config configuration) = 0; + virtual void DestroyVideoReceiveStream( + VideoReceiveStream* receive_stream) = 0; + + // In order for a created VideoReceiveStream to be aware that it is + // protected by a FlexfecReceiveStream, the latter should be created before + // the former. + virtual FlexfecReceiveStream* CreateFlexfecReceiveStream( + const FlexfecReceiveStream::Config& config) = 0; + virtual void DestroyFlexfecReceiveStream( + FlexfecReceiveStream* receive_stream) = 0; + + // All received RTP and RTCP packets for the call should be inserted to this + // PacketReceiver. The PacketReceiver pointer is valid as long as the + // Call instance exists. + virtual PacketReceiver* Receiver() = 0; + + // Returns the call statistics, such as estimated send and receive bandwidth, + // pacing delay, etc. + virtual Stats GetStats() const = 0; + + // The greater min and smaller max set by this and SetBitrateConfigMask will + // be used. The latest non-negative start value from either call will be used. + // Specifying a start bitrate (>0) will reset the current bitrate estimate. + // This is due to how the 'x-google-start-bitrate' flag is currently + // implemented. Passing -1 leaves the start bitrate unchanged. Behavior is not + // guaranteed for other negative values or 0. + virtual void SetBitrateConfig( + const Config::BitrateConfig& bitrate_config) = 0; + + // The greater min and smaller max set by this and SetBitrateConfig will be + // used. The latest non-negative start value form either call will be used. + // Specifying a start bitrate will reset the current bitrate estimate. + // Assumes 0 <= min <= start <= max holds for set parameters. + virtual void SetBitrateConfigMask( + const Config::BitrateConfigMask& bitrate_mask) = 0; + + virtual void SetBitrateAllocationStrategy( + std::unique_ptr<rtc::BitrateAllocationStrategy> + bitrate_allocation_strategy) = 0; + + // TODO(skvlad): When the unbundled case with multiple streams for the same + // media type going over different networks is supported, track the state + // for each stream separately. Right now it's global per media type. + virtual void SignalChannelNetworkState(MediaType media, + NetworkState state) = 0; + + virtual void OnTransportOverheadChanged( + MediaType media, + int transport_overhead_per_packet) = 0; + + virtual void OnNetworkRouteChanged( + const std::string& transport_name, + const rtc::NetworkRoute& network_route) = 0; + + virtual void OnSentPacket(const rtc::SentPacket& sent_packet) = 0; + + virtual VoiceEngine* voice_engine() = 0; + + virtual ~Call() {} +}; + +} // namespace webrtc + +#endif // CALL_CALL_H_ diff --git a/third_party/libwebrtc/webrtc/call/call_gn/moz.build b/third_party/libwebrtc/webrtc/call/call_gn/moz.build new file mode 100644 index 0000000000..f077922d84 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call_gn/moz.build @@ -0,0 +1,228 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/call.cc", + "/third_party/libwebrtc/webrtc/call/callfactory.cc", + "/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "m", + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("call_gn") diff --git a/third_party/libwebrtc/webrtc/call/call_interfaces_gn/moz.build b/third_party/libwebrtc/webrtc/call/call_interfaces_gn/moz.build new file mode 100644 index 0000000000..707acc1ace --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call_interfaces_gn/moz.build @@ -0,0 +1,226 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/audio_send_stream_call.cc", + "/third_party/libwebrtc/webrtc/call/syncable.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("call_interfaces_gn") diff --git a/third_party/libwebrtc/webrtc/call/call_perf_tests.cc b/third_party/libwebrtc/webrtc/call/call_perf_tests.cc new file mode 100644 index 0000000000..90eaa5a4d5 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call_perf_tests.cc @@ -0,0 +1,960 @@ +/* + * 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 <algorithm> +#include <limits> +#include <memory> +#include <string> + +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "call/call.h" +#include "call/video_config.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/audio_mixer/audio_mixer_impl.h" +#include "modules/rtp_rtcp/include/rtp_header_parser.h" +#include "rtc_base/bitrateallocationstrategy.h" +#include "rtc_base/checks.h" +#include "rtc_base/ptr_util.h" +#include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/metrics_default.h" +#include "test/call_test.h" +#include "test/direct_transport.h" +#include "test/drifting_clock.h" +#include "test/encoder_settings.h" +#include "test/fake_audio_device.h" +#include "test/fake_encoder.h" +#include "test/field_trial.h" +#include "test/frame_generator.h" +#include "test/frame_generator_capturer.h" +#include "test/gtest.h" +#include "test/rtp_rtcp_observer.h" +#include "test/single_threaded_task_queue.h" +#include "test/testsupport/fileutils.h" +#include "test/testsupport/perf_test.h" +#include "video/transport_adapter.h" +#include "voice_engine/include/voe_base.h" + +using webrtc::test::DriftingClock; +using webrtc::test::FakeAudioDevice; + +namespace webrtc { + +class CallPerfTest : public test::CallTest { + protected: + enum class FecMode { + kOn, kOff + }; + enum class CreateOrder { + kAudioFirst, kVideoFirst + }; + void TestAudioVideoSync(FecMode fec, + CreateOrder create_first, + float video_ntp_speed, + float video_rtp_speed, + float audio_rtp_speed); + + void TestMinTransmitBitrate(bool pad_to_min_bitrate); + + void TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms); + void TestMinAudioVideoBitrate(bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe); +}; + +class VideoRtcpAndSyncObserver : public test::RtpRtcpObserver, + public rtc::VideoSinkInterface<VideoFrame> { + static const int kInSyncThresholdMs = 50; + static const int kStartupTimeMs = 2000; + static const int kMinRunTimeMs = 30000; + + public: + explicit VideoRtcpAndSyncObserver(Clock* clock) + : test::RtpRtcpObserver(CallPerfTest::kLongTimeoutMs), + clock_(clock), + creation_time_ms_(clock_->TimeInMilliseconds()), + first_time_in_sync_(-1), + receive_stream_(nullptr) {} + + void OnFrame(const VideoFrame& video_frame) override { + VideoReceiveStream::Stats stats; + { + rtc::CritScope lock(&crit_); + if (receive_stream_) + stats = receive_stream_->GetStats(); + } + if (stats.sync_offset_ms == std::numeric_limits<int>::max()) + return; + + int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t time_since_creation = now_ms - creation_time_ms_; + // During the first couple of seconds audio and video can falsely be + // estimated as being synchronized. We don't want to trigger on those. + if (time_since_creation < kStartupTimeMs) + return; + if (std::abs(stats.sync_offset_ms) < kInSyncThresholdMs) { + if (first_time_in_sync_ == -1) { + first_time_in_sync_ = now_ms; + webrtc::test::PrintResult("sync_convergence_time", + "", + "synchronization", + time_since_creation, + "ms", + false); + } + if (time_since_creation > kMinRunTimeMs) + observation_complete_.Set(); + } + if (first_time_in_sync_ != -1) + sync_offset_ms_list_.push_back(stats.sync_offset_ms); + } + + void set_receive_stream(VideoReceiveStream* receive_stream) { + rtc::CritScope lock(&crit_); + receive_stream_ = receive_stream; + } + + void PrintResults() { + test::PrintResultList("stream_offset", "", "synchronization", + sync_offset_ms_list_, "ms", false); + } + + private: + Clock* const clock_; + const int64_t creation_time_ms_; + int64_t first_time_in_sync_; + rtc::CriticalSection crit_; + VideoReceiveStream* receive_stream_ RTC_GUARDED_BY(crit_); + std::vector<double> sync_offset_ms_list_; +}; + +void CallPerfTest::TestAudioVideoSync(FecMode fec, + CreateOrder create_first, + float video_ntp_speed, + float video_rtp_speed, + float audio_rtp_speed) { + const char* kSyncGroup = "av_sync"; + const uint32_t kAudioSendSsrc = 1234; + const uint32_t kAudioRecvSsrc = 5678; + + int send_channel_id; + int recv_channel_id; + + FakeNetworkPipe::Config audio_net_config; + audio_net_config.queue_delay_ms = 500; + audio_net_config.loss_percent = 5; + + rtc::scoped_refptr<AudioProcessing> audio_processing; + VoiceEngine* voice_engine; + VoEBase* voe_base; + std::unique_ptr<FakeAudioDevice> fake_audio_device; + VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock()); + + std::map<uint8_t, MediaType> audio_pt_map; + std::map<uint8_t, MediaType> video_pt_map; + + std::unique_ptr<test::PacketTransport> audio_send_transport; + std::unique_ptr<test::PacketTransport> video_send_transport; + std::unique_ptr<test::PacketTransport> receive_transport; + + AudioSendStream* audio_send_stream; + AudioReceiveStream* audio_receive_stream; + std::unique_ptr<DriftingClock> drifting_clock; + + task_queue_.SendTask([&]() { + metrics::Reset(); + audio_processing = AudioProcessing::Create(); + voice_engine = VoiceEngine::Create(); + voe_base = VoEBase::GetInterface(voice_engine); + fake_audio_device = rtc::MakeUnique<FakeAudioDevice>( + FakeAudioDevice::CreatePulsedNoiseCapturer(256, 48000), + FakeAudioDevice::CreateDiscardRenderer(48000), audio_rtp_speed); + EXPECT_EQ(0, fake_audio_device->Init()); + EXPECT_EQ(0, voe_base->Init(fake_audio_device.get(), audio_processing.get(), + decoder_factory_)); + VoEBase::ChannelConfig config; + config.enable_voice_pacing = true; + send_channel_id = voe_base->CreateChannel(config); + recv_channel_id = voe_base->CreateChannel(); + + AudioState::Config send_audio_state_config; + send_audio_state_config.voice_engine = voice_engine; + send_audio_state_config.audio_mixer = AudioMixerImpl::Create(); + send_audio_state_config.audio_processing = audio_processing; + Call::Config sender_config(event_log_.get()); + + auto audio_state = AudioState::Create(send_audio_state_config); + fake_audio_device->RegisterAudioCallback(audio_state->audio_transport()); + sender_config.audio_state = audio_state; + Call::Config receiver_config(event_log_.get()); + receiver_config.audio_state = audio_state; + CreateCalls(sender_config, receiver_config); + + std::copy_if(std::begin(payload_type_map_), std::end(payload_type_map_), + std::inserter(audio_pt_map, audio_pt_map.end()), + [](const std::pair<const uint8_t, MediaType>& pair) { + return pair.second == MediaType::AUDIO; + }); + std::copy_if(std::begin(payload_type_map_), std::end(payload_type_map_), + std::inserter(video_pt_map, video_pt_map.end()), + [](const std::pair<const uint8_t, MediaType>& pair) { + return pair.second == MediaType::VIDEO; + }); + + audio_send_transport = rtc::MakeUnique<test::PacketTransport>( + &task_queue_, sender_call_.get(), &observer, + test::PacketTransport::kSender, audio_pt_map, audio_net_config); + audio_send_transport->SetReceiver(receiver_call_->Receiver()); + + video_send_transport = rtc::MakeUnique<test::PacketTransport>( + &task_queue_, sender_call_.get(), &observer, + test::PacketTransport::kSender, video_pt_map, + FakeNetworkPipe::Config()); + video_send_transport->SetReceiver(receiver_call_->Receiver()); + + receive_transport = rtc::MakeUnique<test::PacketTransport>( + &task_queue_, receiver_call_.get(), &observer, + test::PacketTransport::kReceiver, payload_type_map_, + FakeNetworkPipe::Config()); + receive_transport->SetReceiver(sender_call_->Receiver()); + + CreateSendConfig(1, 0, 0, video_send_transport.get()); + CreateMatchingReceiveConfigs(receive_transport.get()); + + AudioSendStream::Config audio_send_config(audio_send_transport.get()); + audio_send_config.voe_channel_id = send_channel_id; + audio_send_config.rtp.ssrc = kAudioSendSsrc; + audio_send_config.send_codec_spec = + rtc::Optional<AudioSendStream::Config::SendCodecSpec>( + {kAudioSendPayloadType, {"ISAC", 16000, 1}}); + audio_send_config.encoder_factory = CreateBuiltinAudioEncoderFactory(); + audio_send_stream = sender_call_->CreateAudioSendStream(audio_send_config); + + video_send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + if (fec == FecMode::kOn) { + video_send_config_.rtp.ulpfec.red_payload_type = kRedPayloadType; + video_send_config_.rtp.ulpfec.ulpfec_payload_type = kUlpfecPayloadType; + video_receive_configs_[0].rtp.red_payload_type = kRedPayloadType; + video_receive_configs_[0].rtp.ulpfec_payload_type = kUlpfecPayloadType; + } + video_receive_configs_[0].rtp.nack.rtp_history_ms = 1000; + video_receive_configs_[0].renderer = &observer; + video_receive_configs_[0].sync_group = kSyncGroup; + + AudioReceiveStream::Config audio_recv_config; + audio_recv_config.rtp.remote_ssrc = kAudioSendSsrc; + audio_recv_config.rtp.local_ssrc = kAudioRecvSsrc; + audio_recv_config.voe_channel_id = recv_channel_id; + audio_recv_config.sync_group = kSyncGroup; + audio_recv_config.decoder_factory = decoder_factory_; + audio_recv_config.decoder_map = { + {kAudioSendPayloadType, {"ISAC", 16000, 1}}}; + + if (create_first == CreateOrder::kAudioFirst) { + audio_receive_stream = + receiver_call_->CreateAudioReceiveStream(audio_recv_config); + CreateVideoStreams(); + } else { + CreateVideoStreams(); + audio_receive_stream = + receiver_call_->CreateAudioReceiveStream(audio_recv_config); + } + EXPECT_EQ(1u, video_receive_streams_.size()); + observer.set_receive_stream(video_receive_streams_[0]); + drifting_clock = rtc::MakeUnique<DriftingClock>(clock_, video_ntp_speed); + CreateFrameGeneratorCapturerWithDrift(drifting_clock.get(), video_rtp_speed, + kDefaultFramerate, kDefaultWidth, + kDefaultHeight); + + Start(); + + audio_send_stream->Start(); + audio_receive_stream->Start(); + }); + + EXPECT_TRUE(observer.Wait()) + << "Timed out while waiting for audio and video to be synchronized."; + + task_queue_.SendTask([&]() { + audio_send_stream->Stop(); + audio_receive_stream->Stop(); + + Stop(); + + DestroyStreams(); + + video_send_transport.reset(); + audio_send_transport.reset(); + receive_transport.reset(); + + sender_call_->DestroyAudioSendStream(audio_send_stream); + receiver_call_->DestroyAudioReceiveStream(audio_receive_stream); + + voe_base->DeleteChannel(send_channel_id); + voe_base->DeleteChannel(recv_channel_id); + voe_base->Release(); + + DestroyCalls(); + + VoiceEngine::Delete(voice_engine); + + fake_audio_device.reset(); + }); + + observer.PrintResults(); + + // In quick test synchronization may not be achieved in time. + if (!field_trial::IsEnabled("WebRTC-QuickPerfTest")) { + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AVSyncOffsetInMs")); + } +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoNtpDrift) { + TestAudioVideoSync(FecMode::kOff, CreateOrder::kAudioFirst, + DriftingClock::PercentsFaster(10.0f), + DriftingClock::kNoDrift, DriftingClock::kNoDrift); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithAudioFasterThanVideoDrift) { + TestAudioVideoSync(FecMode::kOff, CreateOrder::kAudioFirst, + DriftingClock::kNoDrift, + DriftingClock::PercentsSlower(30.0f), + DriftingClock::PercentsFaster(30.0f)); +} + +TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoFasterThanAudioDrift) { + TestAudioVideoSync(FecMode::kOn, CreateOrder::kVideoFirst, + DriftingClock::kNoDrift, + DriftingClock::PercentsFaster(30.0f), + DriftingClock::PercentsSlower(30.0f)); +} + +void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms) { + class CaptureNtpTimeObserver : public test::EndToEndTest, + public rtc::VideoSinkInterface<VideoFrame> { + public: + CaptureNtpTimeObserver(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms) + : EndToEndTest(kLongTimeoutMs), + net_config_(net_config), + clock_(Clock::GetRealTimeClock()), + threshold_ms_(threshold_ms), + start_time_ms_(start_time_ms), + run_time_ms_(run_time_ms), + creation_time_ms_(clock_->TimeInMilliseconds()), + capturer_(nullptr), + rtp_start_timestamp_set_(false), + rtp_start_timestamp_(0) {} + + private: + test::PacketTransport* CreateSendTransport( + test::SingleThreadedTaskQueueForTesting* task_queue, + Call* sender_call) override { + return new test::PacketTransport(task_queue, sender_call, this, + test::PacketTransport::kSender, + payload_type_map_, net_config_); + } + + test::PacketTransport* CreateReceiveTransport( + test::SingleThreadedTaskQueueForTesting* task_queue) override { + return new test::PacketTransport(task_queue, nullptr, this, + test::PacketTransport::kReceiver, + payload_type_map_, net_config_); + } + + void OnFrame(const VideoFrame& video_frame) override { + rtc::CritScope lock(&crit_); + if (video_frame.ntp_time_ms() <= 0) { + // Haven't got enough RTCP SR in order to calculate the capture ntp + // time. + return; + } + + int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t time_since_creation = now_ms - creation_time_ms_; + if (time_since_creation < start_time_ms_) { + // Wait for |start_time_ms_| before start measuring. + return; + } + + if (time_since_creation > run_time_ms_) { + observation_complete_.Set(); + } + + FrameCaptureTimeList::iterator iter = + capture_time_list_.find(video_frame.timestamp()); + EXPECT_TRUE(iter != capture_time_list_.end()); + + // The real capture time has been wrapped to uint32_t before converted + // to rtp timestamp in the sender side. So here we convert the estimated + // capture time to a uint32_t 90k timestamp also for comparing. + uint32_t estimated_capture_timestamp = + 90 * static_cast<uint32_t>(video_frame.ntp_time_ms()); + uint32_t real_capture_timestamp = iter->second; + int time_offset_ms = real_capture_timestamp - estimated_capture_timestamp; + time_offset_ms = time_offset_ms / 90; + time_offset_ms_list_.push_back(time_offset_ms); + + EXPECT_TRUE(std::abs(time_offset_ms) < threshold_ms_); + } + + Action OnSendRtp(const uint8_t* packet, size_t length) override { + rtc::CritScope lock(&crit_); + RTPHeader header; + EXPECT_TRUE(parser_->Parse(packet, length, &header)); + + if (!rtp_start_timestamp_set_) { + // Calculate the rtp timestamp offset in order to calculate the real + // capture time. + uint32_t first_capture_timestamp = + 90 * static_cast<uint32_t>(capturer_->first_frame_capture_time()); + rtp_start_timestamp_ = header.timestamp - first_capture_timestamp; + rtp_start_timestamp_set_ = true; + } + + uint32_t capture_timestamp = header.timestamp - rtp_start_timestamp_; + capture_time_list_.insert( + capture_time_list_.end(), + std::make_pair(header.timestamp, capture_timestamp)); + return SEND_PACKET; + } + + void OnFrameGeneratorCapturerCreated( + test::FrameGeneratorCapturer* frame_generator_capturer) override { + capturer_ = frame_generator_capturer; + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { + (*receive_configs)[0].renderer = this; + // Enable the receiver side rtt calculation. + (*receive_configs)[0].rtp.rtcp_xr.receiver_reference_time_report = true; + } + + void PerformTest() override { + EXPECT_TRUE(Wait()) << "Timed out while waiting for " + "estimated capture NTP time to be " + "within bounds."; + test::PrintResultList("capture_ntp_time", "", "real - estimated", + time_offset_ms_list_, "ms", true); + } + + rtc::CriticalSection crit_; + const FakeNetworkPipe::Config net_config_; + Clock* const clock_; + int threshold_ms_; + int start_time_ms_; + int run_time_ms_; + int64_t creation_time_ms_; + test::FrameGeneratorCapturer* capturer_; + bool rtp_start_timestamp_set_; + uint32_t rtp_start_timestamp_; + typedef std::map<uint32_t, uint32_t> FrameCaptureTimeList; + FrameCaptureTimeList capture_time_list_ RTC_GUARDED_BY(&crit_); + std::vector<double> time_offset_ms_list_; + } test(net_config, threshold_ms, start_time_ms, run_time_ms); + + RunBaseTest(&test); +} + +// Flaky tests, disabled on Mac due to webrtc:8291. +#if !(defined(WEBRTC_MAC)) +TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkDelay) { + FakeNetworkPipe::Config net_config; + net_config.queue_delay_ms = 100; + // TODO(wu): lower the threshold as the calculation/estimatation becomes more + // accurate. + const int kThresholdMs = 100; + const int kStartTimeMs = 10000; + const int kRunTimeMs = 20000; + TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); +} + +TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkJitter) { + FakeNetworkPipe::Config net_config; + net_config.queue_delay_ms = 100; + net_config.delay_standard_deviation_ms = 10; + // TODO(wu): lower the threshold as the calculation/estimatation becomes more + // accurate. + const int kThresholdMs = 100; + const int kStartTimeMs = 10000; + const int kRunTimeMs = 20000; + TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); +} +#endif + +TEST_F(CallPerfTest, ReceivesCpuOveruseAndUnderuse) { + // Minimal normal usage at the start, then 30s overuse to allow filter to + // settle, and then 80s underuse to allow plenty of time for rampup again. + test::ScopedFieldTrials fake_overuse_settings( + "WebRTC-ForceSimulatedOveruseIntervalMs/1-30000-80000/"); + + class LoadObserver : public test::SendTest, + public test::FrameGeneratorCapturer::SinkWantsObserver { + public: + LoadObserver() : SendTest(kLongTimeoutMs), test_phase_(TestPhase::kStart) {} + + void OnFrameGeneratorCapturerCreated( + test::FrameGeneratorCapturer* frame_generator_capturer) override { + frame_generator_capturer->SetSinkWantsObserver(this); + // Set a high initial resolution to be sure that we can scale down. + frame_generator_capturer->ChangeResolution(1920, 1080); + } + + // OnSinkWantsChanged is called when FrameGeneratorCapturer::AddOrUpdateSink + // is called. + // TODO(sprang): Add integration test for maintain-framerate mode? + void OnSinkWantsChanged(rtc::VideoSinkInterface<VideoFrame>* sink, + const rtc::VideoSinkWants& wants) override { + // First expect CPU overuse. Then expect CPU underuse when the encoder + // delay has been decreased. + switch (test_phase_) { + case TestPhase::kStart: + if (wants.max_pixel_count < std::numeric_limits<int>::max()) { + // On adapting down, VideoStreamEncoder::VideoSourceProxy will set + // only the max pixel count, leaving the target unset. + test_phase_ = TestPhase::kAdaptedDown; + } else { + ADD_FAILURE() << "Got unexpected adaptation request, max res = " + << wants.max_pixel_count << ", target res = " + << wants.target_pixel_count.value_or(-1) + << ", max fps = " << wants.max_framerate_fps; + } + break; + case TestPhase::kAdaptedDown: + // On adapting up, the adaptation counter will again be at zero, and + // so all constraints will be reset. + if (wants.max_pixel_count == std::numeric_limits<int>::max() && + !wants.target_pixel_count) { + test_phase_ = TestPhase::kAdaptedUp; + observation_complete_.Set(); + } else { + ADD_FAILURE() << "Got unexpected adaptation request, max res = " + << wants.max_pixel_count << ", target res = " + << wants.target_pixel_count.value_or(-1) + << ", max fps = " << wants.max_framerate_fps; + } + break; + case TestPhase::kAdaptedUp: + ADD_FAILURE() << "Got unexpected adaptation request, max res = " + << wants.max_pixel_count << ", target res = " + << wants.target_pixel_count.value_or(-1) + << ", max fps = " << wants.max_framerate_fps; + } + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { + } + + void PerformTest() override { + EXPECT_TRUE(Wait()) << "Timed out before receiving an overuse callback."; + } + + enum class TestPhase { kStart, kAdaptedDown, kAdaptedUp } test_phase_; + } test; + + RunBaseTest(&test); +} + +void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { + static const int kMaxEncodeBitrateKbps = 30; + static const int kMinTransmitBitrateBps = 150000; + static const int kMinAcceptableTransmitBitrate = 130; + static const int kMaxAcceptableTransmitBitrate = 170; + static const int kNumBitrateObservationsInRange = 100; + static const int kAcceptableBitrateErrorMargin = 15; // +- 7 + class BitrateObserver : public test::EndToEndTest { + public: + explicit BitrateObserver(bool using_min_transmit_bitrate) + : EndToEndTest(kLongTimeoutMs), + send_stream_(nullptr), + converged_(false), + pad_to_min_bitrate_(using_min_transmit_bitrate), + min_acceptable_bitrate_(using_min_transmit_bitrate + ? kMinAcceptableTransmitBitrate + : (kMaxEncodeBitrateKbps - + kAcceptableBitrateErrorMargin / 2)), + max_acceptable_bitrate_(using_min_transmit_bitrate + ? kMaxAcceptableTransmitBitrate + : (kMaxEncodeBitrateKbps + + kAcceptableBitrateErrorMargin / 2)), + num_bitrate_observations_in_range_(0) {} + + private: + // TODO(holmer): Run this with a timer instead of once per packet. + Action OnSendRtp(const uint8_t* packet, size_t length) override { + VideoSendStream::Stats stats = send_stream_->GetStats(); + if (stats.substreams.size() > 0) { + RTC_DCHECK_EQ(1, stats.substreams.size()); + int bitrate_kbps = + stats.substreams.begin()->second.total_bitrate_bps / 1000; + if (bitrate_kbps > min_acceptable_bitrate_ && + bitrate_kbps < max_acceptable_bitrate_) { + converged_ = true; + ++num_bitrate_observations_in_range_; + if (num_bitrate_observations_in_range_ == + kNumBitrateObservationsInRange) + observation_complete_.Set(); + } + if (converged_) + bitrate_kbps_list_.push_back(bitrate_kbps); + } + return SEND_PACKET; + } + + void OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) override { + send_stream_ = send_stream; + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { + if (pad_to_min_bitrate_) { + encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps; + } else { + RTC_DCHECK_EQ(0, encoder_config->min_transmit_bitrate_bps); + } + } + + void PerformTest() override { + EXPECT_TRUE(Wait()) << "Timeout while waiting for send-bitrate stats."; + test::PrintResultList( + "bitrate_stats_", + (pad_to_min_bitrate_ ? "min_transmit_bitrate" + : "without_min_transmit_bitrate"), + "bitrate_kbps", bitrate_kbps_list_, "kbps", false); + } + + VideoSendStream* send_stream_; + bool converged_; + const bool pad_to_min_bitrate_; + const int min_acceptable_bitrate_; + const int max_acceptable_bitrate_; + int num_bitrate_observations_in_range_; + std::vector<double> bitrate_kbps_list_; + } test(pad_to_min_bitrate); + + fake_encoder_.SetMaxBitrate(kMaxEncodeBitrateKbps); + RunBaseTest(&test); +} + +TEST_F(CallPerfTest, PadsToMinTransmitBitrate) { TestMinTransmitBitrate(true); } + +TEST_F(CallPerfTest, NoPadWithoutMinTransmitBitrate) { + TestMinTransmitBitrate(false); +} + +TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { + static const uint32_t kInitialBitrateKbps = 400; + static const uint32_t kReconfigureThresholdKbps = 600; + static const uint32_t kPermittedReconfiguredBitrateDiffKbps = 100; + + class VideoStreamFactory + : public VideoEncoderConfig::VideoStreamFactoryInterface { + public: + VideoStreamFactory() {} + + private: + std::vector<VideoStream> CreateEncoderStreams( + int width, + int height, + const VideoEncoderConfig& encoder_config) override { + std::vector<VideoStream> streams = + test::CreateVideoStreams(width, height, encoder_config); + streams[0].min_bitrate_bps = 50000; + streams[0].target_bitrate_bps = streams[0].max_bitrate_bps = 2000000; + return streams; + } + }; + + class BitrateObserver : public test::EndToEndTest, public test::FakeEncoder { + public: + BitrateObserver() + : EndToEndTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + time_to_reconfigure_(false, false), + encoder_inits_(0), + last_set_bitrate_kbps_(0), + send_stream_(nullptr), + frame_generator_(nullptr) {} + + int32_t InitEncode(const VideoCodec* config, + int32_t number_of_cores, + size_t max_payload_size) override { + ++encoder_inits_; + if (encoder_inits_ == 1) { + // First time initialization. Frame size is known. + // |expected_bitrate| is affected by bandwidth estimation before the + // first frame arrives to the encoder. + uint32_t expected_bitrate = last_set_bitrate_kbps_ > 0 + ? last_set_bitrate_kbps_ + : kInitialBitrateKbps; + EXPECT_EQ(expected_bitrate, config->startBitrate) + << "Encoder not initialized at expected bitrate."; + EXPECT_EQ(kDefaultWidth, config->width); + EXPECT_EQ(kDefaultHeight, config->height); + } else if (encoder_inits_ == 2) { + EXPECT_EQ(2 * kDefaultWidth, config->width); + EXPECT_EQ(2 * kDefaultHeight, config->height); + EXPECT_GE(last_set_bitrate_kbps_, kReconfigureThresholdKbps); + EXPECT_GT( + config->startBitrate, + last_set_bitrate_kbps_ - kPermittedReconfiguredBitrateDiffKbps) + << "Encoder reconfigured with bitrate too far away from last set."; + observation_complete_.Set(); + } + return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); + } + + int32_t SetRateAllocation(const BitrateAllocation& rate_allocation, + uint32_t framerate) override { + last_set_bitrate_kbps_ = rate_allocation.get_sum_kbps(); + if (encoder_inits_ == 1 && + rate_allocation.get_sum_kbps() > kReconfigureThresholdKbps) { + time_to_reconfigure_.Set(); + } + return FakeEncoder::SetRateAllocation(rate_allocation, framerate); + } + + Call::Config GetSenderCallConfig() override { + Call::Config config = EndToEndTest::GetSenderCallConfig(); + config.event_log = event_log_.get(); + config.bitrate_config.start_bitrate_bps = kInitialBitrateKbps * 1000; + return config; + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { + send_config->encoder_settings.encoder = this; + encoder_config->max_bitrate_bps = 2 * kReconfigureThresholdKbps * 1000; + encoder_config->video_stream_factory = + new rtc::RefCountedObject<VideoStreamFactory>(); + + encoder_config_ = encoder_config->Copy(); + } + + void OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) override { + send_stream_ = send_stream; + } + + void OnFrameGeneratorCapturerCreated( + test::FrameGeneratorCapturer* frame_generator_capturer) override { + frame_generator_ = frame_generator_capturer; + } + + void PerformTest() override { + ASSERT_TRUE(time_to_reconfigure_.Wait(kDefaultTimeoutMs)) + << "Timed out before receiving an initial high bitrate."; + frame_generator_->ChangeResolution(kDefaultWidth * 2, kDefaultHeight * 2); + send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy()); + EXPECT_TRUE(Wait()) + << "Timed out while waiting for a couple of high bitrate estimates " + "after reconfiguring the send stream."; + } + + private: + rtc::Event time_to_reconfigure_; + int encoder_inits_; + uint32_t last_set_bitrate_kbps_; + VideoSendStream* send_stream_; + test::FrameGeneratorCapturer* frame_generator_; + VideoEncoderConfig encoder_config_; + } test; + + RunBaseTest(&test); +} + +// Discovers the minimal supported audio+video bitrate. The test bitrate is +// considered supported if Rtt does not go above 400ms with the network +// contrained to the test bitrate. +// +// |use_bitrate_allocation_strategy| use AudioPriorityBitrateAllocationStrategy +// |test_bitrate_from test_bitrate_to| bitrate constraint range +// |test_bitrate_step| bitrate constraint update step during the test +// |min_bwe max_bwe| BWE range +// |start_bwe| initial BWE +void CallPerfTest::TestMinAudioVideoBitrate( + bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe) { + static const std::string kAudioTrackId = "audio_track_0"; + static constexpr uint32_t kSufficientAudioBitrateBps = 16000; + static constexpr int kOpusMinBitrateBps = 6000; + static constexpr int kOpusBitrateFbBps = 32000; + static constexpr int kBitrateStabilizationMs = 10000; + static constexpr int kBitrateMeasurements = 10; + static constexpr int kBitrateMeasurementMs = 1000; + static constexpr int kMinGoodRttMs = 400; + + class MinVideoAndAudioBitrateTester : public test::EndToEndTest { + public: + MinVideoAndAudioBitrateTester(bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe) + : EndToEndTest(), + allocation_strategy_(new rtc::AudioPriorityBitrateAllocationStrategy( + kAudioTrackId, + kSufficientAudioBitrateBps)), + use_bitrate_allocation_strategy_(use_bitrate_allocation_strategy), + test_bitrate_from_(test_bitrate_from), + test_bitrate_to_(test_bitrate_to), + test_bitrate_step_(test_bitrate_step), + min_bwe_(min_bwe), + start_bwe_(start_bwe), + max_bwe_(max_bwe) {} + + protected: + FakeNetworkPipe::Config GetFakeNetworkPipeConfig() { + FakeNetworkPipe::Config pipe_config; + pipe_config.link_capacity_kbps = test_bitrate_from_; + return pipe_config; + } + + test::PacketTransport* CreateSendTransport( + test::SingleThreadedTaskQueueForTesting* task_queue, + Call* sender_call) override { + return send_transport_ = new test::PacketTransport( + task_queue, sender_call, this, test::PacketTransport::kSender, + test::CallTest::payload_type_map_, GetFakeNetworkPipeConfig()); + } + + test::PacketTransport* CreateReceiveTransport( + test::SingleThreadedTaskQueueForTesting* task_queue) override { + return receive_transport_ = new test::PacketTransport( + task_queue, nullptr, this, test::PacketTransport::kReceiver, + test::CallTest::payload_type_map_, GetFakeNetworkPipeConfig()); + } + + void PerformTest() override { + int last_passed_test_bitrate = -1; + for (int test_bitrate = test_bitrate_from_; + test_bitrate_from_ < test_bitrate_to_ + ? test_bitrate <= test_bitrate_to_ + : test_bitrate >= test_bitrate_to_; + test_bitrate += test_bitrate_step_) { + FakeNetworkPipe::Config pipe_config; + pipe_config.link_capacity_kbps = test_bitrate; + send_transport_->SetConfig(pipe_config); + receive_transport_->SetConfig(pipe_config); + + rtc::ThreadManager::Instance()->CurrentThread()->SleepMs( + kBitrateStabilizationMs); + + int64_t avg_rtt = 0; + for (int i = 0; i < kBitrateMeasurements; i++) { + Call::Stats call_stats = sender_call_->GetStats(); + avg_rtt += call_stats.rtt_ms; + rtc::ThreadManager::Instance()->CurrentThread()->SleepMs( + kBitrateMeasurementMs); + } + avg_rtt = avg_rtt / kBitrateMeasurements; + if (avg_rtt > kMinGoodRttMs) { + break; + } else { + last_passed_test_bitrate = test_bitrate; + } + } + EXPECT_GT(last_passed_test_bitrate, -1) + << "Minimum supported bitrate out of the test scope"; + webrtc::test::PrintResult("min_test_bitrate_", + use_bitrate_allocation_strategy_ + ? "with_allocation_strategy" + : "no_allocation_strategy", + "", last_passed_test_bitrate, "kbps", false); + } + + void OnCallsCreated(Call* sender_call, Call* receiver_call) override { + sender_call_ = sender_call; + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = min_bwe_; + bitrate_config.start_bitrate_bps = start_bwe_; + bitrate_config.max_bitrate_bps = max_bwe_; + sender_call->SetBitrateConfig(bitrate_config); + if (use_bitrate_allocation_strategy_) { + sender_call->SetBitrateAllocationStrategy( + std::move(allocation_strategy_)); + } + } + + size_t GetNumVideoStreams() const override { return 1; } + + size_t GetNumAudioStreams() const override { return 1; } + + void ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector<AudioReceiveStream::Config>* receive_configs) override { + if (use_bitrate_allocation_strategy_) { + send_config->track_id = kAudioTrackId; + send_config->min_bitrate_bps = kOpusMinBitrateBps; + send_config->max_bitrate_bps = kOpusBitrateFbBps; + } else { + send_config->send_codec_spec->target_bitrate_bps = + rtc::Optional<int>(kOpusBitrateFbBps); + } + } + + private: + std::unique_ptr<rtc::BitrateAllocationStrategy> allocation_strategy_; + const bool use_bitrate_allocation_strategy_; + const int test_bitrate_from_; + const int test_bitrate_to_; + const int test_bitrate_step_; + const int min_bwe_; + const int start_bwe_; + const int max_bwe_; + test::PacketTransport* send_transport_; + test::PacketTransport* receive_transport_; + Call* sender_call_; + } test(use_bitrate_allocation_strategy, test_bitrate_from, test_bitrate_to, + test_bitrate_step, min_bwe, start_bwe, max_bwe); + + RunBaseTest(&test); +} + +TEST_F(CallPerfTest, MinVideoAndAudioBitrate) { + TestMinAudioVideoBitrate(false, 110, 40, -10, 10000, 70000, 200000); +} +TEST_F(CallPerfTest, MinVideoAndAudioBitrateWStrategy) { + TestMinAudioVideoBitrate(true, 110, 40, -10, 10000, 70000, 200000); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/call_unittest.cc b/third_party/libwebrtc/webrtc/call/call_unittest.cc new file mode 100644 index 0000000000..7b16271584 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call_unittest.cc @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <list> +#include <map> +#include <memory> +#include <utility> + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/test/mock_audio_mixer.h" +#include "call/audio_state.h" +#include "call/call.h" +#include "call/fake_rtp_transport_controller_send.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "modules/audio_device/include/mock_audio_device.h" +#include "modules/audio_mixer/audio_mixer_impl.h" +#include "modules/congestion_controller/include/mock/mock_send_side_congestion_controller.h" +#include "modules/pacing/mock/mock_paced_sender.h" +#include "modules/rtp_rtcp/include/rtp_rtcp.h" +#include "rtc_base/ptr_util.h" +#include "test/fake_encoder.h" +#include "test/gtest.h" +#include "test/mock_audio_decoder_factory.h" +#include "test/mock_transport.h" +#include "test/mock_voice_engine.h" + +namespace { + +struct CallHelper { + explicit CallHelper( + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory = nullptr) + : voice_engine_(decoder_factory) { + webrtc::AudioState::Config audio_state_config; + audio_state_config.voice_engine = &voice_engine_; + audio_state_config.audio_mixer = webrtc::AudioMixerImpl::Create(); + audio_state_config.audio_processing = webrtc::AudioProcessing::Create(); + EXPECT_CALL(voice_engine_, audio_transport()); + webrtc::Call::Config config(&event_log_); + config.audio_state = webrtc::AudioState::Create(audio_state_config); + call_.reset(webrtc::Call::Create(config)); + } + + webrtc::Call* operator->() { return call_.get(); } + webrtc::test::MockVoiceEngine* voice_engine() { return &voice_engine_; } + + private: + testing::NiceMock<webrtc::test::MockVoiceEngine> voice_engine_; + webrtc::RtcEventLogNullImpl event_log_; + std::unique_ptr<webrtc::Call> call_; +}; +} // namespace + +namespace webrtc { + +TEST(CallTest, ConstructDestruct) { + CallHelper call; +} + +TEST(CallTest, CreateDestroy_AudioSendStream) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = 42; + config.voe_channel_id = 123; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioSendStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioReceiveStream) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + AudioReceiveStream::Config config; + config.rtp.remote_ssrc = 42; + config.voe_channel_id = 123; + config.decoder_factory = decoder_factory; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioReceiveStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioSendStreams) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.voe_channel_id = 123; + std::list<AudioSendStream*> streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.ssrc = ssrc; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioSendStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, CreateDestroy_AudioReceiveStreams) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + AudioReceiveStream::Config config; + config.voe_channel_id = 123; + config.decoder_factory = decoder_factory; + std::list<AudioReceiveStream*> streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.remote_ssrc = ssrc; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioReceiveStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_RecvFirst) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + ::testing::NiceMock<MockRtpRtcp> mock_rtp_rtcp; + + constexpr int kRecvChannelId = 101; + + // Set up the mock to create a channel proxy which we know of, so that we can + // add our expectations to it. + test::MockVoEChannelProxy* recv_channel_proxy = nullptr; + EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_)) + .WillRepeatedly(testing::Invoke([&](int channel_id) { + test::MockVoEChannelProxy* channel_proxy = + new testing::NiceMock<test::MockVoEChannelProxy>(); + EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory()) + .WillRepeatedly(testing::ReturnRef(decoder_factory)); + EXPECT_CALL(*channel_proxy, SetReceiveCodecs(testing::_)) + .WillRepeatedly(testing::Invoke( + [](const std::map<int, SdpAudioFormat>& codecs) { + EXPECT_THAT(codecs, testing::IsEmpty()); + })); + EXPECT_CALL(*channel_proxy, GetRtpRtcp(testing::_, testing::_)) + .WillRepeatedly(testing::SetArgPointee<0>(&mock_rtp_rtcp)); + // If being called for the send channel, save a pointer to the channel + // proxy for later. + if (channel_id == kRecvChannelId) { + EXPECT_FALSE(recv_channel_proxy); + recv_channel_proxy = channel_proxy; + } + return channel_proxy; + })); + + AudioReceiveStream::Config recv_config; + recv_config.rtp.remote_ssrc = 42; + recv_config.rtp.local_ssrc = 777; + recv_config.voe_channel_id = kRecvChannelId; + recv_config.decoder_factory = decoder_factory; + AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config); + EXPECT_NE(recv_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, AssociateSendChannel(testing::_)).Times(1); + AudioSendStream::Config send_config(nullptr); + send_config.rtp.ssrc = 777; + send_config.voe_channel_id = 123; + AudioSendStream* send_stream = call->CreateAudioSendStream(send_config); + EXPECT_NE(send_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioSendStream(send_stream); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioReceiveStream(recv_stream); +} + +TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_SendFirst) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + ::testing::NiceMock<MockRtpRtcp> mock_rtp_rtcp; + + constexpr int kRecvChannelId = 101; + + // Set up the mock to create a channel proxy which we know of, so that we can + // add our expectations to it. + test::MockVoEChannelProxy* recv_channel_proxy = nullptr; + EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_)) + .WillRepeatedly(testing::Invoke([&](int channel_id) { + test::MockVoEChannelProxy* channel_proxy = + new testing::NiceMock<test::MockVoEChannelProxy>(); + EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory()) + .WillRepeatedly(testing::ReturnRef(decoder_factory)); + EXPECT_CALL(*channel_proxy, SetReceiveCodecs(testing::_)) + .WillRepeatedly(testing::Invoke( + [](const std::map<int, SdpAudioFormat>& codecs) { + EXPECT_THAT(codecs, testing::IsEmpty()); + })); + EXPECT_CALL(*channel_proxy, GetRtpRtcp(testing::_, testing::_)) + .WillRepeatedly(testing::SetArgPointee<0>(&mock_rtp_rtcp)); + // If being called for the send channel, save a pointer to the channel + // proxy for later. + if (channel_id == kRecvChannelId) { + EXPECT_FALSE(recv_channel_proxy); + recv_channel_proxy = channel_proxy; + // We need to set this expectation here since the channel proxy is + // created as a side effect of CreateAudioReceiveStream(). + EXPECT_CALL(*recv_channel_proxy, + AssociateSendChannel(testing::_)).Times(1); + } + return channel_proxy; + })); + + AudioSendStream::Config send_config(nullptr); + send_config.rtp.ssrc = 777; + send_config.voe_channel_id = 123; + AudioSendStream* send_stream = call->CreateAudioSendStream(send_config); + EXPECT_NE(send_stream, nullptr); + + AudioReceiveStream::Config recv_config; + recv_config.rtp.remote_ssrc = 42; + recv_config.rtp.local_ssrc = 777; + recv_config.voe_channel_id = kRecvChannelId; + recv_config.decoder_factory = decoder_factory; + AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config); + EXPECT_NE(recv_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioReceiveStream(recv_stream); + + call->DestroyAudioSendStream(send_stream); +} + +TEST(CallTest, CreateDestroy_FlexfecReceiveStream) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + config.remote_ssrc = 38837212; + config.protected_media_ssrcs = {27273}; + + FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyFlexfecReceiveStream(stream); +} + +TEST(CallTest, CreateDestroy_FlexfecReceiveStreams) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + std::list<FlexfecReceiveStream*> streams; + + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.remote_ssrc = ssrc; + config.protected_media_ssrcs = {ssrc + 1}; + FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyFlexfecReceiveStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, MultipleFlexfecReceiveStreamsProtectingSingleVideoStream) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + config.protected_media_ssrcs = {1324234}; + FlexfecReceiveStream* stream; + std::list<FlexfecReceiveStream*> streams; + + config.remote_ssrc = 838383; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 424993; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 99383; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 5548; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + for (auto s : streams) { + call->DestroyFlexfecReceiveStream(s); + } +} + +namespace { +struct CallBitrateHelper { + CallBitrateHelper() : CallBitrateHelper(Call::Config::BitrateConfig()) {} + + explicit CallBitrateHelper(const Call::Config::BitrateConfig& bitrate_config) + : mock_cc_(Clock::GetRealTimeClock(), &event_log_, &pacer_) { + Call::Config config(&event_log_); + config.bitrate_config = bitrate_config; + call_.reset( + Call::Create(config, rtc::MakeUnique<FakeRtpTransportControllerSend>( + &packet_router_, &pacer_, &mock_cc_))); + } + + webrtc::Call* operator->() { return call_.get(); } + testing::NiceMock<test::MockSendSideCongestionController>& mock_cc() { + return mock_cc_; + } + + private: + webrtc::RtcEventLogNullImpl event_log_; + PacketRouter packet_router_; + testing::NiceMock<MockPacedSender> pacer_; + testing::NiceMock<test::MockSendSideCongestionController> mock_cc_; + std::unique_ptr<Call> call_; +}; +} // namespace + +TEST(CallBitrateTest, SetBitrateConfigWithValidConfigCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentMinCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.min_bitrate_bps = 11; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(11, -1, 30)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentStartCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.start_bitrate_bps = 21; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(10, 21, 30)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentMaxCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.max_bitrate_bps = 31; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(10, -1, 31)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithSameConfigElidesSecondCall) { + CallBitrateHelper call; + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)).Times(1); + call->SetBitrateConfig(bitrate_config); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, + SetBitrateConfigWithSameMinMaxAndNegativeStartElidesSecondCall) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)).Times(1); + call->SetBitrateConfig(bitrate_config); + + bitrate_config.start_bitrate_bps = -1; + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallTest, RecreatingAudioStreamWithSameSsrcReusesRtpState) { + constexpr uint32_t kSSRC = 12345; + testing::NiceMock<test::MockAudioDeviceModule> mock_adm; + rtc::scoped_refptr<test::MockAudioMixer> mock_mixer( + new rtc::RefCountedObject<test::MockAudioMixer>); + + // There's similar functionality in cricket::VoEWrapper but it's not reachable + // from here. Since we're working on removing VoE interfaces, I doubt it's + // worth making VoEWrapper more easily available. + struct ScopedVoiceEngine { + ScopedVoiceEngine() + : voe(VoiceEngine::Create()), + base(VoEBase::GetInterface(voe)) {} + ~ScopedVoiceEngine() { + base->Release(); + EXPECT_TRUE(VoiceEngine::Delete(voe)); + } + + VoiceEngine* voe; + VoEBase* base; + }; + ScopedVoiceEngine voice_engine; + + AudioState::Config audio_state_config; + audio_state_config.voice_engine = voice_engine.voe; + audio_state_config.audio_mixer = mock_mixer; + audio_state_config.audio_processing = AudioProcessing::Create(); + voice_engine.base->Init(&mock_adm, audio_state_config.audio_processing.get(), + CreateBuiltinAudioDecoderFactory()); + auto audio_state = AudioState::Create(audio_state_config); + + RtcEventLogNullImpl event_log; + Call::Config call_config(&event_log); + call_config.audio_state = audio_state; + std::unique_ptr<Call> call(Call::Create(call_config)); + + auto create_stream_and_get_rtp_state = [&](uint32_t ssrc) { + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = ssrc; + config.voe_channel_id = voice_engine.base->CreateChannel(); + AudioSendStream* stream = call->CreateAudioSendStream(config); + VoiceEngineImpl* voe_impl = static_cast<VoiceEngineImpl*>(voice_engine.voe); + auto channel_proxy = voe_impl->GetChannelProxy(config.voe_channel_id); + RtpRtcp* rtp_rtcp = nullptr; + RtpReceiver* rtp_receiver = nullptr; // Unused but required for call. + channel_proxy->GetRtpRtcp(&rtp_rtcp, &rtp_receiver); + const RtpState rtp_state = rtp_rtcp->GetRtpState(); + call->DestroyAudioSendStream(stream); + voice_engine.base->DeleteChannel(config.voe_channel_id); + return rtp_state; + }; + + const RtpState rtp_state1 = create_stream_and_get_rtp_state(kSSRC); + const RtpState rtp_state2 = create_stream_and_get_rtp_state(kSSRC); + + EXPECT_EQ(rtp_state1.sequence_number, rtp_state2.sequence_number); + EXPECT_EQ(rtp_state1.start_timestamp, rtp_state2.start_timestamp); + EXPECT_EQ(rtp_state1.timestamp, rtp_state2.timestamp); + EXPECT_EQ(rtp_state1.capture_time_ms, rtp_state2.capture_time_ms); + EXPECT_EQ(rtp_state1.last_timestamp_time_ms, + rtp_state2.last_timestamp_time_ms); + EXPECT_EQ(rtp_state1.media_has_been_sent, rtp_state2.media_has_been_sent); +} +TEST(CallBitrateTest, BiggerMaskMinUsed) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(1234); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(*mask.min_bitrate_bps, testing::_, testing::_)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, BiggerConfigMinUsed) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, testing::_, testing::_)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 1234; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1234, testing::_, testing::_)); + call->SetBitrateConfig(config); +} + +// The last call to set start should be used. +TEST(CallBitrateTest, LatestStartMaskPreferred) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1300); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, *mask.start_bitrate_bps, testing::_)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 1200; + + EXPECT_CALL( + call.mock_cc(), + SetBweBitrates(testing::_, bitrate_config.start_bitrate_bps, testing::_)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SmallerMaskMaxUsed) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = + rtc::Optional<int>(bitrate_config.start_bitrate_bps + 1000); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, testing::_, *mask.max_bitrate_bps)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SmallerConfigMaxUsed) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = + rtc::Optional<int>(bitrate_config.start_bitrate_bps + 2000); + + // Expect no calls because nothing changes + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, testing::_, testing::_)) + .Times(0); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskStartLessThanConfigMinClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(2000, 2000, testing::_)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskStartGreaterThanConfigMaxClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, -1, 1000)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskMinGreaterThanConfigMaxClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, testing::_, 1000)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SettingMaskStartForcesUpdate) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + + // SetBweBitrates should be called twice with the same params since + // start_bitrate_bps is set. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, 1000, testing::_)) + .Times(2); + call->SetBitrateConfigMask(mask); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SetBitrateConfigWithNoChangesDoesNotCallSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig config1; + config1.min_bitrate_bps = 0; + config1.start_bitrate_bps = 1000; + config1.max_bitrate_bps = -1; + + Call::Config::BitrateConfig config2; + config2.min_bitrate_bps = 0; + config2.start_bitrate_bps = -1; + config2.max_bitrate_bps = -1; + + // The second call should not call SetBweBitrates because it doesn't + // change any values. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfig(config1); + call->SetBitrateConfig(config2); +} + +// If SetBitrateConfig changes the max, but not the effective max, +// SetBweBitrates shouldn't be called, to avoid unnecessary encoder +// reconfigurations. +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenEffectiveMaxUnchanged) { + CallBitrateHelper call; + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 0; + config.start_bitrate_bps = -1; + config.max_bitrate_bps = 2000; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, testing::_, 2000)); + call->SetBitrateConfig(config); + + // Reduce effective max to 1000 with the mask. + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, testing::_, 1000)); + call->SetBitrateConfigMask(mask); + + // This leaves the effective max unchanged, so SetBweBitrates shouldn't be + // called again. + config.max_bitrate_bps = 1000; + call->SetBitrateConfig(config); +} + +// When the "start bitrate" mask is removed, SetBweBitrates shouldn't be called +// again, since nothing's changing. +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenStartMaskRemoved) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfigMask(mask); + + mask.start_bitrate_bps.reset(); + call->SetBitrateConfigMask(mask); +} + +// Test that if SetBitrateConfig is called after SetBitrateConfigMask applies a +// "start" value, the SetBitrateConfig call won't apply that start value a +// second time. +TEST(CallBitrateTest, SetBitrateConfigAfterSetBitrateConfigMaskWithStart) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 0; + config.start_bitrate_bps = -1; + config.max_bitrate_bps = 5000; + // The start value isn't changing, so SetBweBitrates should be called with + // -1. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, -1, 5000)); + call->SetBitrateConfig(config); +} + +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenClampedMinUnchanged) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 500; + bitrate_config.max_bitrate_bps = 1000; + CallBitrateHelper call(bitrate_config); + + // Set min to 2000; it is clamped to the max (1000). + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(2000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, -1, 1000)); + call->SetBitrateConfigMask(mask); + + // Set min to 3000; the clamped value stays the same so nothing happens. + mask.min_bitrate_bps = rtc::Optional<int>(3000); + call->SetBitrateConfigMask(mask); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/callfactory.cc b/third_party/libwebrtc/webrtc/call/callfactory.cc new file mode 100644 index 0000000000..82acb6533c --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/callfactory.cc @@ -0,0 +1,25 @@ +/* + * 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 "call/callfactory.h" + +#include <memory> + +namespace webrtc { + +Call* CallFactory::CreateCall(const Call::Config& config) { + return Call::Create(config); +} + +std::unique_ptr<CallFactoryInterface> CreateCallFactory() { + return std::unique_ptr<CallFactoryInterface>(new CallFactory()); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/callfactory.h b/third_party/libwebrtc/webrtc/call/callfactory.h new file mode 100644 index 0000000000..167b82ad54 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/callfactory.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_CALLFACTORY_H_ +#define CALL_CALLFACTORY_H_ + +#include "call/callfactoryinterface.h" + +namespace webrtc { + +class CallFactory : public CallFactoryInterface { + ~CallFactory() override {} + + Call* CreateCall(const Call::Config& config) override; +}; + +} // namespace webrtc + +#endif // CALL_CALLFACTORY_H_ diff --git a/third_party/libwebrtc/webrtc/call/callfactoryinterface.h b/third_party/libwebrtc/webrtc/call/callfactoryinterface.h new file mode 100644 index 0000000000..a3cf6ebc46 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/callfactoryinterface.h @@ -0,0 +1,34 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_CALLFACTORYINTERFACE_H_ +#define CALL_CALLFACTORYINTERFACE_H_ + +#include <memory> + +#include "call/call.h" + +namespace webrtc { + +// This interface exists to allow webrtc to be optionally built without media +// support (i.e., if only being used for data channels). PeerConnectionFactory +// is constructed with a CallFactoryInterface, which may or may not be null. +class CallFactoryInterface { + public: + virtual ~CallFactoryInterface() {} + + virtual Call* CreateCall(const Call::Config& config) = 0; +}; + +std::unique_ptr<CallFactoryInterface> CreateCallFactory(); + +} // namespace webrtc + +#endif // CALL_CALLFACTORYINTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/fake_rtp_transport_controller_send.h b/third_party/libwebrtc/webrtc/call/fake_rtp_transport_controller_send.h new file mode 100644 index 0000000000..dda2e5f645 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/fake_rtp_transport_controller_send.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_FAKE_RTP_TRANSPORT_CONTROLLER_SEND_H_ +#define CALL_FAKE_RTP_TRANSPORT_CONTROLLER_SEND_H_ + +#include "call/rtp_transport_controller_send_interface.h" +#include "common_types.h" // NOLINT(build/include) +#include "modules/congestion_controller/include/send_side_congestion_controller.h" +#include "modules/pacing/packet_router.h" + +namespace webrtc { + +class FakeRtpTransportControllerSend + : public RtpTransportControllerSendInterface { + public: + explicit FakeRtpTransportControllerSend( + PacketRouter* packet_router, + PacedSender* paced_sender, + SendSideCongestionController* send_side_cc) + : packet_router_(packet_router), + paced_sender_(paced_sender), + send_side_cc_(send_side_cc) { + RTC_DCHECK(send_side_cc); + } + + PacketRouter* packet_router() override { return packet_router_; } + + SendSideCongestionController* send_side_cc() override { + return send_side_cc_; + } + + TransportFeedbackObserver* transport_feedback_observer() override { + return send_side_cc_; + } + + PacedSender* pacer() override { return paced_sender_; } + + RtpPacketSender* packet_sender() override { return paced_sender_; } + + const RtpKeepAliveConfig& keepalive_config() const override { + return keepalive_; + } + + void SetAllocatedSendBitrateLimits(int min_send_bitrate_bps, + int max_padding_bitrate_bps) override {} + + void set_keepalive_config(const RtpKeepAliveConfig& keepalive_config) { + keepalive_ = keepalive_config; + } + + private: + PacketRouter* packet_router_; + PacedSender* paced_sender_; + SendSideCongestionController* send_side_cc_; + RtpKeepAliveConfig keepalive_; +}; + +} // namespace webrtc + +#endif // CALL_FAKE_RTP_TRANSPORT_CONTROLLER_SEND_H_ diff --git a/third_party/libwebrtc/webrtc/call/flexfec_receive_stream.h b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream.h new file mode 100644 index 0000000000..a0b7a11d3a --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_FLEXFEC_RECEIVE_STREAM_H_ +#define CALL_FLEXFEC_RECEIVE_STREAM_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "api/call/transport.h" +#include "api/rtpparameters.h" +#include "call/rtp_packet_sink_interface.h" +#include "common_types.h" // NOLINT(build/include) + +namespace webrtc { + +class FlexfecReceiveStream : public RtpPacketSinkInterface { + public: + ~FlexfecReceiveStream() override = default; + + struct Stats { + std::string ToString(int64_t time_ms) const; + + // TODO(brandtr): Add appropriate stats here. + int flexfec_bitrate_bps; + }; + + struct Config { + explicit Config(Transport* rtcp_send_transport) + : rtcp_send_transport(rtcp_send_transport) { + RTC_DCHECK(rtcp_send_transport); + } + + std::string ToString() const; + + // Returns true if all RTP information is available in order to + // enable receiving FlexFEC. + bool IsCompleteAndEnabled() const; + + // Payload type for FlexFEC. + int payload_type = -1; + + // SSRC for FlexFEC stream to be received. + uint32_t remote_ssrc = 0; + + // Vector containing a single element, corresponding to the SSRC of the + // media stream being protected by this FlexFEC stream. The vector MUST have + // size 1. + // + // TODO(brandtr): Update comment above when we support multistream + // protection. + std::vector<uint32_t> protected_media_ssrcs; + + // SSRC for RTCP reports to be sent. + uint32_t local_ssrc = 0; + + // What RTCP mode to use in the reports. + RtcpMode rtcp_mode = RtcpMode::kCompound; + + // Transport for outgoing RTCP packets. + Transport* rtcp_send_transport = nullptr; + + // |transport_cc| is true whenever the send-side BWE RTCP feedback message + // has been negotiated. This is a prerequisite for enabling send-side BWE. + bool transport_cc = false; + + // RTP header extensions that have been negotiated for this track. + std::vector<RtpExtension> rtp_header_extensions; + }; + + virtual Stats GetStats() const = 0; + + virtual const Config& GetConfig() const = 0; +}; + +} // namespace webrtc + +#endif // CALL_FLEXFEC_RECEIVE_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.cc b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.cc new file mode 100644 index 0000000000..038c1f6a1b --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.cc @@ -0,0 +1,197 @@ +/* + * 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 "call/flexfec_receive_stream_impl.h" + +#include <string> + +#include "call/rtp_stream_receiver_controller_interface.h" +#include "modules/rtp_rtcp/include/flexfec_receiver.h" +#include "modules/rtp_rtcp/include/receive_statistics.h" +#include "modules/rtp_rtcp/include/rtp_rtcp.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" +#include "system_wrappers/include/clock.h" + +namespace webrtc { + +std::string FlexfecReceiveStream::Stats::ToString(int64_t time_ms) const { + std::stringstream ss; + ss << "FlexfecReceiveStream stats: " << time_ms + << ", {flexfec_bitrate_bps: " << flexfec_bitrate_bps << "}"; + return ss.str(); +} + +std::string FlexfecReceiveStream::Config::ToString() const { + std::stringstream ss; + ss << "{payload_type: " << payload_type; + ss << ", remote_ssrc: " << remote_ssrc; + ss << ", local_ssrc: " << local_ssrc; + ss << ", protected_media_ssrcs: ["; + size_t i = 0; + for (; i + 1 < protected_media_ssrcs.size(); ++i) + ss << protected_media_ssrcs[i] << ", "; + if (!protected_media_ssrcs.empty()) + ss << protected_media_ssrcs[i]; + ss << "], transport_cc: " << (transport_cc ? "on" : "off"); + ss << ", rtp_header_extensions: ["; + i = 0; + for (; i + 1 < rtp_header_extensions.size(); ++i) + ss << rtp_header_extensions[i].ToString() << ", "; + if (!rtp_header_extensions.empty()) + ss << rtp_header_extensions[i].ToString(); + ss << "]}"; + return ss.str(); +} + +bool FlexfecReceiveStream::Config::IsCompleteAndEnabled() const { + // Check if FlexFEC is enabled. + if (payload_type < 0) + return false; + // Do we have the necessary SSRC information? + if (remote_ssrc == 0) + return false; + // TODO(brandtr): Update this check when we support multistream protection. + if (protected_media_ssrcs.size() != 1u) + return false; + return true; +} + +namespace { + +// TODO(brandtr): Update this function when we support multistream protection. +std::unique_ptr<FlexfecReceiver> MaybeCreateFlexfecReceiver( + const FlexfecReceiveStream::Config& config, + RecoveredPacketReceiver* recovered_packet_receiver) { + if (config.payload_type < 0) { + RTC_LOG(LS_WARNING) + << "Invalid FlexFEC payload type given. " + << "This FlexfecReceiveStream will therefore be useless."; + return nullptr; + } + RTC_DCHECK_GE(config.payload_type, 0); + RTC_DCHECK_LE(config.payload_type, 127); + if (config.remote_ssrc == 0) { + RTC_LOG(LS_WARNING) + << "Invalid FlexFEC SSRC given. " + << "This FlexfecReceiveStream will therefore be useless."; + return nullptr; + } + if (config.protected_media_ssrcs.empty()) { + RTC_LOG(LS_WARNING) + << "No protected media SSRC supplied. " + << "This FlexfecReceiveStream will therefore be useless."; + return nullptr; + } + + if (config.protected_media_ssrcs.size() > 1) { + RTC_LOG(LS_WARNING) + << "The supplied FlexfecConfig contained multiple protected " + "media streams, but our implementation currently only " + "supports protecting a single media stream. " + "To avoid confusion, disabling FlexFEC completely."; + return nullptr; + } + RTC_DCHECK_EQ(1U, config.protected_media_ssrcs.size()); + return std::unique_ptr<FlexfecReceiver>( + new FlexfecReceiver(config.remote_ssrc, config.protected_media_ssrcs[0], + recovered_packet_receiver)); +} + +std::unique_ptr<RtpRtcp> CreateRtpRtcpModule( + ReceiveStatistics* receive_statistics, + Transport* rtcp_send_transport, + RtcpRttStats* rtt_stats) { + RtpRtcp::Configuration configuration; + configuration.audio = false; + configuration.receiver_only = true; + configuration.clock = Clock::GetRealTimeClock(); + configuration.receive_statistics = receive_statistics; + configuration.outgoing_transport = rtcp_send_transport; + configuration.rtt_stats = rtt_stats; + std::unique_ptr<RtpRtcp> rtp_rtcp(RtpRtcp::CreateRtpRtcp(configuration)); + return rtp_rtcp; +} + +} // namespace + +FlexfecReceiveStreamImpl::FlexfecReceiveStreamImpl( + RtpStreamReceiverControllerInterface* receiver_controller, + const Config& config, + RecoveredPacketReceiver* recovered_packet_receiver, + RtcpRttStats* rtt_stats, + ProcessThread* process_thread) + : config_(config), + receiver_(MaybeCreateFlexfecReceiver(config_, recovered_packet_receiver)), + rtp_receive_statistics_( + ReceiveStatistics::Create(Clock::GetRealTimeClock())), + rtp_rtcp_(CreateRtpRtcpModule(rtp_receive_statistics_.get(), + config_.rtcp_send_transport, + rtt_stats)), + process_thread_(process_thread) { + RTC_LOG(LS_INFO) << "FlexfecReceiveStreamImpl: " << config_.ToString(); + + // RTCP reporting. + rtp_rtcp_->SetRTCPStatus(config_.rtcp_mode); + rtp_rtcp_->SetSSRC(config_.local_ssrc); + process_thread_->RegisterModule(rtp_rtcp_.get(), RTC_FROM_HERE); + + // Register with transport. + // TODO(nisse): OnRtpPacket in this class delegates all real work to + // |receiver_|. So maybe we don't need to implement RtpPacketSinkInterface + // here at all, we'd then delete the OnRtpPacket method and instead register + // |receiver_| as the RtpPacketSinkInterface for this stream. + // TODO(nisse): Passing |this| from the constructor to the RtpDemuxer, before + // the object is fully initialized, is risky. But it works in this case + // because locking in our caller, Call::CreateFlexfecReceiveStream, ensures + // that the demuxer doesn't call OnRtpPacket before this object is fully + // constructed. Registering |receiver_| instead of |this| would solve this + // problem too. + rtp_stream_receiver_ = + receiver_controller->CreateReceiver(config_.remote_ssrc, this); +} + +FlexfecReceiveStreamImpl::~FlexfecReceiveStreamImpl() { + RTC_LOG(LS_INFO) << "~FlexfecReceiveStreamImpl: " << config_.ToString(); + process_thread_->DeRegisterModule(rtp_rtcp_.get()); +} + +void FlexfecReceiveStreamImpl::OnRtpPacket(const RtpPacketReceived& packet) { + if (!receiver_) + return; + + receiver_->OnRtpPacket(packet); + + // Do not report media packets in the RTCP RRs generated by |rtp_rtcp_|. + if (packet.Ssrc() == config_.remote_ssrc) { + RTPHeader header; + packet.GetHeader(&header); + // FlexFEC packets are never retransmitted. + const bool kNotRetransmitted = false; + rtp_receive_statistics_->IncomingPacket(header, packet.size(), + kNotRetransmitted); + } +} + +// TODO(brandtr): Implement this member function when we have designed the +// stats for FlexFEC. +FlexfecReceiveStreamImpl::Stats FlexfecReceiveStreamImpl::GetStats() const { + return FlexfecReceiveStream::Stats(); +} + +const FlexfecReceiveStream::Config& FlexfecReceiveStreamImpl::GetConfig() + const { + return config_; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.h b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.h new file mode 100644 index 0000000000..6bcbc7c9ca --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_FLEXFEC_RECEIVE_STREAM_IMPL_H_ +#define CALL_FLEXFEC_RECEIVE_STREAM_IMPL_H_ + +#include <memory> + +#include "call/flexfec_receive_stream.h" +#include "call/rtp_packet_sink_interface.h" + +namespace webrtc { + +class FlexfecReceiver; +class ProcessThread; +class ReceiveStatistics; +class RecoveredPacketReceiver; +class RtcpRttStats; +class RtpPacketReceived; +class RtpRtcp; +class RtpStreamReceiverControllerInterface; +class RtpStreamReceiverInterface; + +class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { + public: + FlexfecReceiveStreamImpl( + RtpStreamReceiverControllerInterface* receiver_controller, + const Config& config, + RecoveredPacketReceiver* recovered_packet_receiver, + RtcpRttStats* rtt_stats, + ProcessThread* process_thread); + ~FlexfecReceiveStreamImpl() override; + + // RtpPacketSinkInterface. + void OnRtpPacket(const RtpPacketReceived& packet) override; + + Stats GetStats() const override; + const Config& GetConfig() const override; + + private: + // Config. + const Config config_; + + // Erasure code interfacing. + const std::unique_ptr<FlexfecReceiver> receiver_; + + // RTCP reporting. + const std::unique_ptr<ReceiveStatistics> rtp_receive_statistics_; + const std::unique_ptr<RtpRtcp> rtp_rtcp_; + ProcessThread* process_thread_; + + std::unique_ptr<RtpStreamReceiverInterface> rtp_stream_receiver_; +}; + +} // namespace webrtc + +#endif // CALL_FLEXFEC_RECEIVE_STREAM_IMPL_H_ diff --git a/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_unittest.cc b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_unittest.cc new file mode 100644 index 0000000000..21dbeb7609 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/flexfec_receive_stream_unittest.cc @@ -0,0 +1,157 @@ +/* + * 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 "call/flexfec_receive_stream_impl.h" + +#include <stdint.h> +#include <memory> + +#include "api/array_view.h" +#include "call/rtp_stream_receiver_controller.h" +#include "modules/pacing/packet_router.h" +#include "modules/rtp_rtcp/include/flexfec_receiver.h" +#include "modules/rtp_rtcp/mocks/mock_recovered_packet_receiver.h" +#include "modules/rtp_rtcp/mocks/mock_rtcp_rtt_stats.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/utility/include/mock/mock_process_thread.h" +#include "rtc_base/ptr_util.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/mock_transport.h" + +namespace webrtc { + +namespace { + +using ::testing::_; + +constexpr uint8_t kFlexfecPlType = 118; +constexpr uint8_t kFlexfecSsrc[] = {0x00, 0x00, 0x00, 0x01}; +constexpr uint8_t kMediaSsrc[] = {0x00, 0x00, 0x00, 0x02}; + +FlexfecReceiveStream::Config CreateDefaultConfig( + Transport* rtcp_send_transport) { + FlexfecReceiveStream::Config config(rtcp_send_transport); + config.payload_type = kFlexfecPlType; + config.remote_ssrc = ByteReader<uint32_t>::ReadBigEndian(kFlexfecSsrc); + config.protected_media_ssrcs = { + ByteReader<uint32_t>::ReadBigEndian(kMediaSsrc)}; + EXPECT_TRUE(config.IsCompleteAndEnabled()); + return config; +} + +RtpPacketReceived ParsePacket(rtc::ArrayView<const uint8_t> packet) { + RtpPacketReceived parsed_packet(nullptr); + EXPECT_TRUE(parsed_packet.Parse(packet)); + return parsed_packet; +} + +} // namespace + +TEST(FlexfecReceiveStreamConfigTest, IsCompleteAndEnabled) { + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + + config.local_ssrc = 18374743; + config.rtcp_mode = RtcpMode::kCompound; + config.transport_cc = true; + config.rtp_header_extensions.emplace_back(TransportSequenceNumber::kUri, 7); + EXPECT_FALSE(config.IsCompleteAndEnabled()); + + config.payload_type = 123; + EXPECT_FALSE(config.IsCompleteAndEnabled()); + + config.remote_ssrc = 238423838; + EXPECT_FALSE(config.IsCompleteAndEnabled()); + + config.protected_media_ssrcs.push_back(138989393); + EXPECT_TRUE(config.IsCompleteAndEnabled()); + + config.protected_media_ssrcs.push_back(33423423); + EXPECT_FALSE(config.IsCompleteAndEnabled()); +} + +class FlexfecReceiveStreamTest : public ::testing::Test { + protected: + FlexfecReceiveStreamTest() + : config_(CreateDefaultConfig(&rtcp_send_transport_)) { + EXPECT_CALL(process_thread_, RegisterModule(_, _)).Times(1); + receive_stream_ = rtc::MakeUnique<FlexfecReceiveStreamImpl>( + &rtp_stream_receiver_controller_, config_, &recovered_packet_receiver_, + &rtt_stats_, &process_thread_); + } + + ~FlexfecReceiveStreamTest() { + EXPECT_CALL(process_thread_, DeRegisterModule(_)).Times(1); + } + + MockTransport rtcp_send_transport_; + FlexfecReceiveStream::Config config_; + MockRecoveredPacketReceiver recovered_packet_receiver_; + MockRtcpRttStats rtt_stats_; + MockProcessThread process_thread_; + RtpStreamReceiverController rtp_stream_receiver_controller_; + std::unique_ptr<FlexfecReceiveStreamImpl> receive_stream_; +}; + +TEST_F(FlexfecReceiveStreamTest, ConstructDestruct) {} + +// Create a FlexFEC packet that protects a single media packet and ensure +// that the callback is called. Correctness of recovery is checked in the +// FlexfecReceiver unit tests. +TEST_F(FlexfecReceiveStreamTest, RecoversPacket) { + constexpr uint8_t kFlexfecSeqNum[] = {0x00, 0x01}; + constexpr uint8_t kFlexfecTs[] = {0x00, 0x11, 0x22, 0x33}; + constexpr uint8_t kMediaPlType = 107; + constexpr uint8_t kMediaSeqNum[] = {0x00, 0x02}; + constexpr uint8_t kMediaTs[] = {0xaa, 0xbb, 0xcc, 0xdd}; + + // This packet mask protects a single media packet, i.e., the FlexFEC payload + // is a copy of that media packet. When inserted in the FlexFEC pipeline, + // it will thus trivially recover the lost media packet. + constexpr uint8_t kKBit0 = 1 << 7; + constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x00, 0x01}; + constexpr uint8_t kPayloadLength[] = {0x00, 0x04}; + constexpr uint8_t kSsrcCount = 1; + constexpr uint8_t kReservedBits = 0x00; + constexpr uint8_t kPayloadBits = 0x00; + // clang-format off + constexpr uint8_t kFlexfecPacket[] = { + // RTP header. + 0x80, kFlexfecPlType, kFlexfecSeqNum[0], kFlexfecSeqNum[1], + kFlexfecTs[0], kFlexfecTs[1], kFlexfecTs[2], kFlexfecTs[3], + kFlexfecSsrc[0], kFlexfecSsrc[1], kFlexfecSsrc[2], kFlexfecSsrc[3], + // FlexFEC header. + 0x00, kMediaPlType, kPayloadLength[0], kPayloadLength[1], + kMediaTs[0], kMediaTs[1], kMediaTs[2], kMediaTs[3], + kSsrcCount, kReservedBits, kReservedBits, kReservedBits, + kMediaSsrc[0], kMediaSsrc[1], kMediaSsrc[2], kMediaSsrc[3], + kMediaSeqNum[0], kMediaSeqNum[1], kFlexfecPktMask[0], kFlexfecPktMask[1], + // FEC payload. + kPayloadBits, kPayloadBits, kPayloadBits, kPayloadBits}; + // clang-format on + + testing::StrictMock<MockRecoveredPacketReceiver> recovered_packet_receiver; + EXPECT_CALL(process_thread_, RegisterModule(_, _)).Times(1); + FlexfecReceiveStreamImpl receive_stream(&rtp_stream_receiver_controller_, + config_, &recovered_packet_receiver, + &rtt_stats_, &process_thread_); + + EXPECT_CALL(recovered_packet_receiver, + OnRecoveredPacket(_, kRtpHeaderSize + kPayloadLength[1])); + + receive_stream.OnRtpPacket(ParsePacket(kFlexfecPacket)); + + // Tear-down + EXPECT_CALL(process_thread_, DeRegisterModule(_)).Times(1); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rampup_tests.cc b/third_party/libwebrtc/webrtc/call/rampup_tests.cc new file mode 100644 index 0000000000..1d584195d2 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rampup_tests.cc @@ -0,0 +1,657 @@ +/* + * 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 "call/rampup_tests.h" + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/platform_thread.h" +#include "test/encoder_settings.h" +#include "test/gtest.h" +#include "test/testsupport/perf_test.h" + +namespace webrtc { +namespace { + +static const int64_t kPollIntervalMs = 20; +static const int kExpectedHighVideoBitrateBps = 80000; +static const int kExpectedHighAudioBitrateBps = 30000; +static const int kLowBandwidthLimitBps = 20000; +static const int kExpectedLowBitrateBps = 20000; + +std::vector<uint32_t> GenerateSsrcs(size_t num_streams, uint32_t ssrc_offset) { + std::vector<uint32_t> ssrcs; + for (size_t i = 0; i != num_streams; ++i) + ssrcs.push_back(static_cast<uint32_t>(ssrc_offset + i)); + return ssrcs; +} +} // namespace + +RampUpTester::RampUpTester(size_t num_video_streams, + size_t num_audio_streams, + size_t num_flexfec_streams, + unsigned int start_bitrate_bps, + int64_t min_run_time_ms, + const std::string& extension_type, + bool rtx, + bool red, + bool report_perf_stats) + : EndToEndTest(test::CallTest::kLongTimeoutMs), + stop_event_(false, false), + clock_(Clock::GetRealTimeClock()), + num_video_streams_(num_video_streams), + num_audio_streams_(num_audio_streams), + num_flexfec_streams_(num_flexfec_streams), + rtx_(rtx), + red_(red), + report_perf_stats_(report_perf_stats), + sender_call_(nullptr), + send_stream_(nullptr), + send_transport_(nullptr), + start_bitrate_bps_(start_bitrate_bps), + min_run_time_ms_(min_run_time_ms), + expected_bitrate_bps_(0), + test_start_ms_(-1), + ramp_up_finished_ms_(-1), + extension_type_(extension_type), + video_ssrcs_(GenerateSsrcs(num_video_streams_, 100)), + video_rtx_ssrcs_(GenerateSsrcs(num_video_streams_, 200)), + audio_ssrcs_(GenerateSsrcs(num_audio_streams_, 300)), + poller_thread_(&BitrateStatsPollingThread, + this, + "BitrateStatsPollingThread") { + if (red_) + EXPECT_EQ(0u, num_flexfec_streams_); + EXPECT_LE(num_audio_streams_, 1u); +} + +RampUpTester::~RampUpTester() { +} + +Call::Config RampUpTester::GetSenderCallConfig() { + Call::Config call_config(&event_log_); + if (start_bitrate_bps_ != 0) { + call_config.bitrate_config.start_bitrate_bps = start_bitrate_bps_; + } + call_config.bitrate_config.min_bitrate_bps = 10000; + return call_config; +} + +void RampUpTester::OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) { + send_stream_ = send_stream; +} + +test::PacketTransport* RampUpTester::CreateSendTransport( + test::SingleThreadedTaskQueueForTesting* task_queue, + Call* sender_call) { + send_transport_ = new test::PacketTransport( + task_queue, sender_call, this, test::PacketTransport::kSender, + test::CallTest::payload_type_map_, forward_transport_config_); + return send_transport_; +} + +size_t RampUpTester::GetNumVideoStreams() const { + return num_video_streams_; +} + +size_t RampUpTester::GetNumAudioStreams() const { + return num_audio_streams_; +} + +size_t RampUpTester::GetNumFlexfecStreams() const { + return num_flexfec_streams_; +} + +class RampUpTester::VideoStreamFactory + : public VideoEncoderConfig::VideoStreamFactoryInterface { + public: + VideoStreamFactory() {} + + private: + std::vector<VideoStream> CreateEncoderStreams( + int width, + int height, + const VideoEncoderConfig& encoder_config) override { + std::vector<VideoStream> streams = + test::CreateVideoStreams(width, height, encoder_config); + if (encoder_config.number_of_streams == 1) { + streams[0].target_bitrate_bps = streams[0].max_bitrate_bps = 2000000; + } + return streams; + } +}; + +void RampUpTester::ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) { + send_config->suspend_below_min_bitrate = true; + encoder_config->number_of_streams = num_video_streams_; + encoder_config->max_bitrate_bps = 2000000; + encoder_config->video_stream_factory = + new rtc::RefCountedObject<RampUpTester::VideoStreamFactory>(); + if (num_video_streams_ == 1) { + // For single stream rampup until 1mbps + expected_bitrate_bps_ = kSingleStreamTargetBps; + } else { + // For multi stream rampup until all streams are being sent. That means + // enough bitrate to send all the target streams plus the min bitrate of + // the last one. + std::vector<VideoStream> streams = test::CreateVideoStreams( + test::CallTest::kDefaultWidth, test::CallTest::kDefaultHeight, + *encoder_config); + expected_bitrate_bps_ = streams.back().min_bitrate_bps; + for (size_t i = 0; i < streams.size() - 1; ++i) { + expected_bitrate_bps_ += streams[i].target_bitrate_bps; + } + } + + 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)); + } + + send_config->rtp.nack.rtp_history_ms = test::CallTest::kNackRtpHistoryMs; + send_config->rtp.ssrcs = video_ssrcs_; + if (rtx_) { + send_config->rtp.rtx.payload_type = test::CallTest::kSendRtxPayloadType; + send_config->rtp.rtx.ssrcs = video_rtx_ssrcs_; + } + if (red_) { + send_config->rtp.ulpfec.ulpfec_payload_type = + test::CallTest::kUlpfecPayloadType; + send_config->rtp.ulpfec.red_payload_type = test::CallTest::kRedPayloadType; + if (rtx_) { + send_config->rtp.ulpfec.red_rtx_payload_type = + test::CallTest::kRtxRedPayloadType; + } + } + + size_t i = 0; + for (VideoReceiveStream::Config& recv_config : *receive_configs) { + recv_config.rtp.remb = remb; + recv_config.rtp.transport_cc = transport_cc; + recv_config.rtp.extensions = send_config->rtp.extensions; + + recv_config.rtp.remote_ssrc = video_ssrcs_[i]; + recv_config.rtp.nack.rtp_history_ms = send_config->rtp.nack.rtp_history_ms; + + if (red_) { + recv_config.rtp.red_payload_type = + send_config->rtp.ulpfec.red_payload_type; + recv_config.rtp.ulpfec_payload_type = + send_config->rtp.ulpfec.ulpfec_payload_type; + if (rtx_) { + recv_config.rtp.rtx_associated_payload_types + [send_config->rtp.ulpfec.red_rtx_payload_type] = + send_config->rtp.ulpfec.red_payload_type; + } + } + + if (rtx_) { + recv_config.rtp.rtx_ssrc = video_rtx_ssrcs_[i]; + recv_config.rtp + .rtx_associated_payload_types[send_config->rtp.rtx.payload_type] = + send_config->encoder_settings.payload_type; + } + ++i; + } + + RTC_DCHECK_LE(num_flexfec_streams_, 1); + if (num_flexfec_streams_ == 1) { + send_config->rtp.flexfec.payload_type = test::CallTest::kFlexfecPayloadType; + send_config->rtp.flexfec.ssrc = test::CallTest::kFlexfecSendSsrc; + send_config->rtp.flexfec.protected_media_ssrcs = {video_ssrcs_[0]}; + } +} + +void RampUpTester::ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector<AudioReceiveStream::Config>* receive_configs) { + if (num_audio_streams_ == 0) + return; + + EXPECT_NE(RtpExtension::kTimestampOffsetUri, extension_type_) + << "Audio BWE not supported with toffset."; + EXPECT_NE(RtpExtension::kAbsSendTimeUri, extension_type_) + << "Audio BWE not supported with abs-send-time."; + + send_config->rtp.ssrc = audio_ssrcs_[0]; + send_config->rtp.extensions.clear(); + + send_config->min_bitrate_bps = 6000; + send_config->max_bitrate_bps = 60000; + + bool transport_cc = false; + if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) { + transport_cc = true; + send_config->rtp.extensions.push_back(RtpExtension( + extension_type_.c_str(), kTransportSequenceNumberExtensionId)); + } + + for (AudioReceiveStream::Config& recv_config : *receive_configs) { + recv_config.rtp.transport_cc = transport_cc; + recv_config.rtp.extensions = send_config->rtp.extensions; + recv_config.rtp.remote_ssrc = send_config->rtp.ssrc; + } +} + +void RampUpTester::ModifyFlexfecConfigs( + std::vector<FlexfecReceiveStream::Config>* receive_configs) { + if (num_flexfec_streams_ == 0) + return; + RTC_DCHECK_EQ(1, num_flexfec_streams_); + (*receive_configs)[0].payload_type = test::CallTest::kFlexfecPayloadType; + (*receive_configs)[0].remote_ssrc = test::CallTest::kFlexfecSendSsrc; + (*receive_configs)[0].protected_media_ssrcs = {video_ssrcs_[0]}; + (*receive_configs)[0].local_ssrc = video_ssrcs_[0]; + if (extension_type_ == RtpExtension::kAbsSendTimeUri) { + (*receive_configs)[0].transport_cc = false; + (*receive_configs)[0].rtp_header_extensions.push_back( + RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId)); + } else if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) { + (*receive_configs)[0].transport_cc = true; + (*receive_configs)[0].rtp_header_extensions.push_back(RtpExtension( + extension_type_.c_str(), kTransportSequenceNumberExtensionId)); + } +} + +void RampUpTester::OnCallsCreated(Call* sender_call, Call* receiver_call) { + sender_call_ = sender_call; +} + +void RampUpTester::BitrateStatsPollingThread(void* obj) { + static_cast<RampUpTester*>(obj)->PollStats(); +} + +void RampUpTester::PollStats() { + do { + if (sender_call_) { + Call::Stats stats = sender_call_->GetStats(); + + EXPECT_GE(stats.send_bandwidth_bps, start_bitrate_bps_); + EXPECT_GE(expected_bitrate_bps_, 0); + if (stats.send_bandwidth_bps >= expected_bitrate_bps_ && + (min_run_time_ms_ == -1 || + clock_->TimeInMilliseconds() - test_start_ms_ >= min_run_time_ms_)) { + ramp_up_finished_ms_ = clock_->TimeInMilliseconds(); + observation_complete_.Set(); + } + } + } while (!stop_event_.Wait(kPollIntervalMs)); +} + +void RampUpTester::ReportResult(const std::string& measurement, + size_t value, + const std::string& units) const { + webrtc::test::PrintResult( + measurement, "", + ::testing::UnitTest::GetInstance()->current_test_info()->name(), value, + units, false); +} + +void RampUpTester::AccumulateStats(const VideoSendStream::StreamStats& stream, + size_t* total_packets_sent, + size_t* total_sent, + size_t* padding_sent, + size_t* media_sent) const { + *total_packets_sent += stream.rtp_stats.transmitted.packets + + stream.rtp_stats.retransmitted.packets + + stream.rtp_stats.fec.packets; + *total_sent += stream.rtp_stats.transmitted.TotalBytes() + + stream.rtp_stats.retransmitted.TotalBytes() + + stream.rtp_stats.fec.TotalBytes(); + *padding_sent += stream.rtp_stats.transmitted.padding_bytes + + stream.rtp_stats.retransmitted.padding_bytes + + stream.rtp_stats.fec.padding_bytes; + *media_sent += stream.rtp_stats.MediaPayloadBytes(); +} + +void RampUpTester::TriggerTestDone() { + RTC_DCHECK_GE(test_start_ms_, 0); + + // TODO(holmer): Add audio send stats here too when those APIs are available. + if (!send_stream_) + return; + + VideoSendStream::Stats send_stats = send_stream_->GetStats(); + + size_t total_packets_sent = 0; + size_t total_sent = 0; + size_t padding_sent = 0; + size_t media_sent = 0; + for (uint32_t ssrc : video_ssrcs_) { + AccumulateStats(send_stats.substreams[ssrc], &total_packets_sent, + &total_sent, &padding_sent, &media_sent); + } + + size_t rtx_total_packets_sent = 0; + size_t rtx_total_sent = 0; + size_t rtx_padding_sent = 0; + size_t rtx_media_sent = 0; + for (uint32_t rtx_ssrc : video_rtx_ssrcs_) { + AccumulateStats(send_stats.substreams[rtx_ssrc], &rtx_total_packets_sent, + &rtx_total_sent, &rtx_padding_sent, &rtx_media_sent); + } + + 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"); + if (ramp_up_finished_ms_ >= 0) { + ReportResult("ramp-up-time", ramp_up_finished_ms_ - test_start_ms_, + "milliseconds"); + } + ReportResult("ramp-up-average-network-latency", + send_transport_->GetAverageDelayMs(), "milliseconds"); + } +} + +void RampUpTester::PerformTest() { + test_start_ms_ = clock_->TimeInMilliseconds(); + poller_thread_.Start(); + EXPECT_TRUE(Wait()) << "Timed out while waiting for ramp-up to complete."; + TriggerTestDone(); + stop_event_.Set(); + poller_thread_.Stop(); +} + +RampUpDownUpTester::RampUpDownUpTester(size_t num_video_streams, + size_t num_audio_streams, + size_t num_flexfec_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red, + const std::vector<int>& loss_rates, + bool report_perf_stats) + : RampUpTester(num_video_streams, + num_audio_streams, + num_flexfec_streams, + start_bitrate_bps, + 0, + extension_type, + rtx, + red, + report_perf_stats), + link_rates_({4 * GetExpectedHighBitrate() / (3 * 1000), + kLowBandwidthLimitBps / 1000, + 4 * GetExpectedHighBitrate() / (3 * 1000), 0}), + test_state_(kFirstRampup), + next_state_(kTransitionToNextState), + state_start_ms_(clock_->TimeInMilliseconds()), + interval_start_ms_(clock_->TimeInMilliseconds()), + sent_bytes_(0), + loss_rates_(loss_rates) { + forward_transport_config_.link_capacity_kbps = link_rates_[test_state_]; + forward_transport_config_.queue_delay_ms = 100; + forward_transport_config_.loss_percent = loss_rates_[test_state_]; +} + +RampUpDownUpTester::~RampUpDownUpTester() {} + +void RampUpDownUpTester::PollStats() { + do { + int transmit_bitrate_bps = 0; + bool suspended = false; + if (num_video_streams_ > 0) { + webrtc::VideoSendStream::Stats stats = send_stream_->GetStats(); + for (auto it : stats.substreams) { + transmit_bitrate_bps += it.second.total_bitrate_bps; + } + suspended = stats.suspended; + } + if (num_audio_streams_ > 0 && sender_call_ != nullptr) { + // An audio send stream doesn't have bitrate stats, so the call send BW is + // currently used instead. + transmit_bitrate_bps = sender_call_->GetStats().send_bandwidth_bps; + } + EvolveTestState(transmit_bitrate_bps, suspended); + } while (!stop_event_.Wait(kPollIntervalMs)); +} + +Call::Config RampUpDownUpTester::GetReceiverCallConfig() { + Call::Config config(&event_log_); + config.bitrate_config.min_bitrate_bps = 10000; + return config; +} + +std::string RampUpDownUpTester::GetModifierString() const { + std::string str("_"); + if (num_video_streams_ > 0) { + std::ostringstream s; + s << num_video_streams_; + str += s.str(); + str += "stream"; + str += (num_video_streams_ > 1 ? "s" : ""); + str += "_"; + } + if (num_audio_streams_ > 0) { + std::ostringstream s; + s << num_audio_streams_; + str += s.str(); + str += "stream"; + str += (num_audio_streams_ > 1 ? "s" : ""); + str += "_"; + } + str += (rtx_ ? "" : "no"); + str += "rtx"; + return str; +} + +int RampUpDownUpTester::GetExpectedHighBitrate() const { + int expected_bitrate_bps = 0; + if (num_audio_streams_ > 0) + expected_bitrate_bps += kExpectedHighAudioBitrateBps; + if (num_video_streams_ > 0) + expected_bitrate_bps += kExpectedHighVideoBitrateBps; + return expected_bitrate_bps; +} + +size_t RampUpDownUpTester::GetFecBytes() const { + size_t flex_fec_bytes = 0; + if (num_flexfec_streams_ > 0) { + webrtc::VideoSendStream::Stats stats = send_stream_->GetStats(); + for (const auto& kv : stats.substreams) + flex_fec_bytes += kv.second.rtp_stats.fec.TotalBytes(); + } + return flex_fec_bytes; +} + +bool RampUpDownUpTester::ExpectingFec() const { + return num_flexfec_streams_ > 0 && forward_transport_config_.loss_percent > 0; +} + +void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) { + int64_t now = clock_->TimeInMilliseconds(); + switch (test_state_) { + case kFirstRampup: + EXPECT_FALSE(suspended); + if (bitrate_bps >= GetExpectedHighBitrate()) { + if (report_perf_stats_) { + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "first_rampup", now - state_start_ms_, "ms", + false); + } + // Apply loss during the transition between states if FEC is enabled. + forward_transport_config_.loss_percent = loss_rates_[test_state_]; + test_state_ = kTransitionToNextState; + next_state_ = kLowRate; + } + break; + case kLowRate: { + // Audio streams are never suspended. + bool check_suspend_state = num_video_streams_ > 0; + if (bitrate_bps < kExpectedLowBitrateBps && + suspended == check_suspend_state) { + if (report_perf_stats_) { + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "rampdown", now - state_start_ms_, "ms", + false); + } + // Apply loss during the transition between states if FEC is enabled. + forward_transport_config_.loss_percent = loss_rates_[test_state_]; + test_state_ = kTransitionToNextState; + next_state_ = kSecondRampup; + } + break; + } + case kSecondRampup: + if (bitrate_bps >= GetExpectedHighBitrate() && !suspended) { + if (report_perf_stats_) { + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "second_rampup", now - state_start_ms_, + "ms", false); + ReportResult("ramp-up-down-up-average-network-latency", + send_transport_->GetAverageDelayMs(), "milliseconds"); + } + // Apply loss during the transition between states if FEC is enabled. + forward_transport_config_.loss_percent = loss_rates_[test_state_]; + test_state_ = kTransitionToNextState; + next_state_ = kTestEnd; + } + break; + case kTestEnd: + observation_complete_.Set(); + break; + case kTransitionToNextState: + if (!ExpectingFec() || GetFecBytes() > 0) { + test_state_ = next_state_; + forward_transport_config_.link_capacity_kbps = link_rates_[test_state_]; + // No loss while ramping up and down as it may affect the BWE + // negatively, making the test flaky. + forward_transport_config_.loss_percent = 0; + state_start_ms_ = now; + interval_start_ms_ = now; + sent_bytes_ = 0; + send_transport_->SetConfig(forward_transport_config_); + } + break; + } +} + +class RampUpTest : public test::CallTest { + public: + RampUpTest() {} + + virtual ~RampUpTest() { + EXPECT_EQ(nullptr, video_send_stream_); + EXPECT_TRUE(video_receive_streams_.empty()); + } +}; + +static const uint32_t kStartBitrateBps = 60000; + +TEST_F(RampUpTest, UpDownUpAbsSendTimeSimulcastRedRtx) { + std::vector<int> loss_rates = {0, 0, 0, 0}; + RampUpDownUpTester test(3, 0, 0, kStartBitrateBps, + RtpExtension::kAbsSendTimeUri, true, true, loss_rates, + true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpTransportSequenceNumberRtx) { + std::vector<int> loss_rates = {0, 0, 0, 0}; + RampUpDownUpTester test(3, 0, 0, kStartBitrateBps, + RtpExtension::kTransportSequenceNumberUri, true, + false, loss_rates, true); + RunBaseTest(&test); +} + +// TODO(holmer): Tests which don't report perf stats should be moved to a +// different executable since they per definition are not perf tests. +// This test is disabled because it crashes on Linux, and is flaky on other +// platforms. See: crbug.com/webrtc/7919 +TEST_F(RampUpTest, DISABLED_UpDownUpTransportSequenceNumberPacketLoss) { + std::vector<int> loss_rates = {20, 0, 0, 0}; + RampUpDownUpTester test(1, 0, 1, kStartBitrateBps, + RtpExtension::kTransportSequenceNumberUri, true, + false, loss_rates, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpAudioVideoTransportSequenceNumberRtx) { + std::vector<int> loss_rates = {0, 0, 0, 0}; + RampUpDownUpTester test(3, 1, 0, kStartBitrateBps, + RtpExtension::kTransportSequenceNumberUri, true, + false, loss_rates, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpAudioTransportSequenceNumberRtx) { + std::vector<int> loss_rates = {0, 0, 0, 0}; + RampUpDownUpTester test(0, 1, 0, kStartBitrateBps, + RtpExtension::kTransportSequenceNumberUri, true, + false, loss_rates, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TOffsetSimulcastRedRtx) { + RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTimestampOffsetUri, true, + true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTime) { + RampUpTester test(1, 0, 0, 0, 0, RtpExtension::kAbsSendTimeUri, false, false, + false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTimeSimulcastRedRtx) { + RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kAbsSendTimeUri, true, true, + true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumber) { + RampUpTester test(1, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri, + false, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSimulcast) { + RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri, + false, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSimulcastRedRtx) { + RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri, + true, true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AudioTransportSequenceNumber) { + RampUpTester test(0, 1, 0, 300000, 10000, + RtpExtension::kTransportSequenceNumberUri, false, false, + false); + RunBaseTest(&test); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rampup_tests.h b/third_party/libwebrtc/webrtc/call/rampup_tests.h new file mode 100644 index 0000000000..1339f1f2dc --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rampup_tests.h @@ -0,0 +1,160 @@ +/* + * 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 CALL_RAMPUP_TESTS_H_ +#define CALL_RAMPUP_TESTS_H_ + +#include <map> +#include <string> +#include <vector> + +#include "call/call.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "rtc_base/event.h" +#include "test/call_test.h" + +namespace webrtc { + +static const int kTransmissionTimeOffsetExtensionId = 6; +static const int kAbsSendTimeExtensionId = 7; +static const int kTransportSequenceNumberExtensionId = 8; +static const unsigned int kSingleStreamTargetBps = 1000000; + +class Clock; + +class RampUpTester : public test::EndToEndTest { + public: + RampUpTester(size_t num_video_streams, + size_t num_audio_streams, + size_t num_flexfec_streams, + unsigned int start_bitrate_bps, + int64_t min_run_time_ms, + const std::string& extension_type, + bool rtx, + bool red, + bool report_perf_stats); + ~RampUpTester() override; + + size_t GetNumVideoStreams() const override; + size_t GetNumAudioStreams() const override; + size_t GetNumFlexfecStreams() const override; + + void PerformTest() override; + + protected: + virtual void PollStats(); + + void AccumulateStats(const VideoSendStream::StreamStats& stream, + size_t* total_packets_sent, + size_t* total_sent, + size_t* padding_sent, + size_t* media_sent) const; + + void ReportResult(const std::string& measurement, + size_t value, + const std::string& units) const; + void TriggerTestDone(); + + webrtc::RtcEventLogNullImpl event_log_; + rtc::Event stop_event_; + Clock* const clock_; + FakeNetworkPipe::Config forward_transport_config_; + const size_t num_video_streams_; + const size_t num_audio_streams_; + const size_t num_flexfec_streams_; + const bool rtx_; + const bool red_; + const bool report_perf_stats_; + Call* sender_call_; + VideoSendStream* send_stream_; + test::PacketTransport* send_transport_; + + private: + typedef std::map<uint32_t, uint32_t> SsrcMap; + class VideoStreamFactory; + + Call::Config GetSenderCallConfig() override; + void OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) override; + test::PacketTransport* CreateSendTransport( + test::SingleThreadedTaskQueueForTesting* task_queue, + Call* sender_call) override; + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override; + void ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector<AudioReceiveStream::Config>* receive_configs) override; + void ModifyFlexfecConfigs( + std::vector<FlexfecReceiveStream::Config>* receive_configs) override; + void OnCallsCreated(Call* sender_call, Call* receiver_call) override; + + static void BitrateStatsPollingThread(void* obj); + + const int start_bitrate_bps_; + const int64_t min_run_time_ms_; + int expected_bitrate_bps_; + int64_t test_start_ms_; + int64_t ramp_up_finished_ms_; + + const std::string extension_type_; + std::vector<uint32_t> video_ssrcs_; + std::vector<uint32_t> video_rtx_ssrcs_; + std::vector<uint32_t> audio_ssrcs_; + + rtc::PlatformThread poller_thread_; +}; + +class RampUpDownUpTester : public RampUpTester { + public: + RampUpDownUpTester(size_t num_video_streams, + size_t num_audio_streams, + size_t num_flexfec_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red, + const std::vector<int>& loss_rates, + bool report_perf_stats); + ~RampUpDownUpTester() override; + + protected: + void PollStats() override; + + private: + enum TestStates { + kFirstRampup = 0, + kLowRate, + kSecondRampup, + kTestEnd, + kTransitionToNextState, + }; + + Call::Config GetReceiverCallConfig() override; + + std::string GetModifierString() const; + int GetExpectedHighBitrate() const; + int GetHighLinkCapacity() const; + size_t GetFecBytes() const; + bool ExpectingFec() const; + void EvolveTestState(int bitrate_bps, bool suspended); + + const std::vector<int> link_rates_; + TestStates test_state_; + TestStates next_state_; + int64_t state_start_ms_; + int64_t interval_start_ms_; + int sent_bytes_; + std::vector<int> loss_rates_; +}; +} // namespace webrtc +#endif // CALL_RAMPUP_TESTS_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtcp_demuxer.cc b/third_party/libwebrtc/webrtc/call/rtcp_demuxer.cc new file mode 100644 index 0000000000..0adbdf3133 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtcp_demuxer.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtcp_demuxer.h" + +#include "call/rtcp_packet_sink_interface.h" +#include "call/rtp_rtcp_demuxer_helper.h" +#include "common_types.h" // NOLINT(build/include) +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcpDemuxer::RtcpDemuxer() = default; + +RtcpDemuxer::~RtcpDemuxer() { + RTC_DCHECK(ssrc_sinks_.empty()); + RTC_DCHECK(rsid_sinks_.empty()); + RTC_DCHECK(broadcast_sinks_.empty()); +} + +void RtcpDemuxer::AddSink(uint32_t sender_ssrc, RtcpPacketSinkInterface* sink) { + RTC_DCHECK(sink); + RTC_DCHECK(!ContainerHasKey(broadcast_sinks_, sink)); + RTC_DCHECK(!MultimapAssociationExists(ssrc_sinks_, sender_ssrc, sink)); + ssrc_sinks_.emplace(sender_ssrc, sink); +} + +void RtcpDemuxer::AddSink(const std::string& rsid, + RtcpPacketSinkInterface* sink) { + RTC_DCHECK(StreamId::IsLegalName(rsid)); + RTC_DCHECK(sink); + RTC_DCHECK(!ContainerHasKey(broadcast_sinks_, sink)); + RTC_DCHECK(!MultimapAssociationExists(rsid_sinks_, rsid, sink)); + rsid_sinks_.emplace(rsid, sink); +} + +void RtcpDemuxer::AddBroadcastSink(RtcpPacketSinkInterface* sink) { + RTC_DCHECK(sink); + RTC_DCHECK(!MultimapHasValue(ssrc_sinks_, sink)); + RTC_DCHECK(!MultimapHasValue(rsid_sinks_, sink)); + RTC_DCHECK(!ContainerHasKey(broadcast_sinks_, sink)); + broadcast_sinks_.push_back(sink); +} + +void RtcpDemuxer::RemoveSink(const RtcpPacketSinkInterface* sink) { + RTC_DCHECK(sink); + size_t removal_count = RemoveFromMultimapByValue(&ssrc_sinks_, sink) + + RemoveFromMultimapByValue(&rsid_sinks_, sink); + RTC_DCHECK_GT(removal_count, 0); +} + +void RtcpDemuxer::RemoveBroadcastSink(const RtcpPacketSinkInterface* sink) { + RTC_DCHECK(sink); + auto it = std::find(broadcast_sinks_.begin(), broadcast_sinks_.end(), sink); + RTC_DCHECK(it != broadcast_sinks_.end()); + broadcast_sinks_.erase(it); +} + +void RtcpDemuxer::OnRtcpPacket(rtc::ArrayView<const uint8_t> packet) { + // Perform sender-SSRC-based demuxing for packets with a sender-SSRC. + rtc::Optional<uint32_t> sender_ssrc = ParseRtcpPacketSenderSsrc(packet); + if (sender_ssrc) { + auto it_range = ssrc_sinks_.equal_range(*sender_ssrc); + for (auto it = it_range.first; it != it_range.second; ++it) { + it->second->OnRtcpPacket(packet); + } + } + + // All packets, even those without a sender-SSRC, are broadcast to sinks + // which listen to broadcasts. + for (RtcpPacketSinkInterface* sink : broadcast_sinks_) { + sink->OnRtcpPacket(packet); + } +} + +void RtcpDemuxer::OnSsrcBoundToRsid(const std::string& rsid, uint32_t ssrc) { + // Record the new SSRC association for all of the sinks that were associated + // with the RSID. + auto it_range = rsid_sinks_.equal_range(rsid); + for (auto it = it_range.first; it != it_range.second; ++it) { + RtcpPacketSinkInterface* sink = it->second; + // Watch out for pre-existing SSRC-based associations. + if (!MultimapAssociationExists(ssrc_sinks_, ssrc, sink)) { + AddSink(ssrc, sink); + } + } + + // RSIDs are uniquely associated with SSRCs; no need to keep in memory + // the RSID-to-sink association of resolved RSIDs. + rsid_sinks_.erase(it_range.first, it_range.second); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtcp_demuxer.h b/third_party/libwebrtc/webrtc/call/rtcp_demuxer.h new file mode 100644 index 0000000000..87b5816282 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtcp_demuxer.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTCP_DEMUXER_H_ +#define CALL_RTCP_DEMUXER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "api/array_view.h" +#include "call/ssrc_binding_observer.h" +#include "rtc_base/basictypes.h" + +namespace webrtc { + +class RtcpPacketSinkInterface; + +// This class represents the RTCP demuxing, for a single RTP session (i.e., one +// SSRC space, see RFC 7656). It isn't thread aware, leaving responsibility of +// multithreading issues to the user of this class. +class RtcpDemuxer : public SsrcBindingObserver { + public: + RtcpDemuxer(); + ~RtcpDemuxer() override; + + // Registers a sink. The sink will be notified of incoming RTCP packets with + // that sender-SSRC. The same sink can be registered for multiple SSRCs, and + // the same SSRC can have multiple sinks. Null pointer is not allowed. + // Sinks may be associated with both an SSRC and an RSID. + // Sinks may be registered as SSRC/RSID-specific or broadcast, but not both. + void AddSink(uint32_t sender_ssrc, RtcpPacketSinkInterface* sink); + + // Registers a sink. Once the RSID is resolved to an SSRC, the sink will be + // notified of all RTCP packets with that sender-SSRC. + // The same sink can be registered for multiple RSIDs, and + // the same RSID can have multiple sinks. Null pointer is not allowed. + // Sinks may be associated with both an SSRC and an RSID. + // Sinks may be registered as SSRC/RSID-specific or broadcast, but not both. + void AddSink(const std::string& rsid, RtcpPacketSinkInterface* sink); + + // Registers a sink. The sink will be notified of any incoming RTCP packet. + // Null pointer is not allowed. + // Sinks may be registered as SSRC/RSID-specific or broadcast, but not both. + void AddBroadcastSink(RtcpPacketSinkInterface* sink); + + // Undo previous AddSink() calls with the given sink. + void RemoveSink(const RtcpPacketSinkInterface* sink); + + // Undo AddBroadcastSink(). + void RemoveBroadcastSink(const RtcpPacketSinkInterface* sink); + + // Process a new RTCP packet and forward it to the appropriate sinks. + void OnRtcpPacket(rtc::ArrayView<const uint8_t> packet); + + // Implement SsrcBindingObserver - become notified whenever RSIDs resolve to + // an SSRC. + void OnSsrcBoundToRsid(const std::string& rsid, uint32_t ssrc) override; + + // TODO(eladalon): Add the ability to resolve RSIDs and inform observers, + // like in the RtpDemuxer case, once the relevant standard is finalized. + + private: + // Records the association SSRCs to sinks. + std::multimap<uint32_t, RtcpPacketSinkInterface*> ssrc_sinks_; + + // Records the association RSIDs to sinks. + std::multimap<std::string, RtcpPacketSinkInterface*> rsid_sinks_; + + // Sinks which will receive notifications of all incoming RTCP packets. + // Additional/removal of sinks is expected to be significantly less frequent + // than RTCP message reception; container chosen for iteration performance. + std::vector<RtcpPacketSinkInterface*> broadcast_sinks_; +}; + +} // namespace webrtc + +#endif // CALL_RTCP_DEMUXER_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtcp_demuxer_unittest.cc b/third_party/libwebrtc/webrtc/call/rtcp_demuxer_unittest.cc new file mode 100644 index 0000000000..dd5aa55147 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtcp_demuxer_unittest.cc @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtcp_demuxer.h" + +#include <memory> +#include <set> + +#include "call/rtcp_packet_sink_interface.h" +#include "common_types.h" // NOLINT(build/include) +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/basictypes.h" +#include "rtc_base/checks.h" +#include "rtc_base/ptr_util.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::ElementsAreArray; +using ::testing::InSequence; +using ::testing::Matcher; +using ::testing::NiceMock; + +class MockRtcpPacketSink : public RtcpPacketSinkInterface { + public: + MOCK_METHOD1(OnRtcpPacket, void(rtc::ArrayView<const uint8_t>)); +}; + +class RtcpDemuxerTest : public testing::Test { + protected: + ~RtcpDemuxerTest() { + for (auto* sink : sinks_to_tear_down_) { + demuxer_.RemoveSink(sink); + } + for (auto* sink : broadcast_sinks_to_tear_down_) { + demuxer_.RemoveBroadcastSink(sink); + } + } + + void AddSsrcSink(uint32_t ssrc, RtcpPacketSinkInterface* sink) { + demuxer_.AddSink(ssrc, sink); + sinks_to_tear_down_.insert(sink); + } + + void AddRsidSink(const std::string& rsid, RtcpPacketSinkInterface* sink) { + demuxer_.AddSink(rsid, sink); + sinks_to_tear_down_.insert(sink); + } + + void RemoveSink(RtcpPacketSinkInterface* sink) { + sinks_to_tear_down_.erase(sink); + demuxer_.RemoveSink(sink); + } + + void AddBroadcastSink(RtcpPacketSinkInterface* sink) { + demuxer_.AddBroadcastSink(sink); + broadcast_sinks_to_tear_down_.insert(sink); + } + + void RemoveBroadcastSink(RtcpPacketSinkInterface* sink) { + broadcast_sinks_to_tear_down_.erase(sink); + demuxer_.RemoveBroadcastSink(sink); + } + + RtcpDemuxer demuxer_; + std::set<RtcpPacketSinkInterface*> sinks_to_tear_down_; + std::set<RtcpPacketSinkInterface*> broadcast_sinks_to_tear_down_; +}; + +// Produces a packet buffer representing an RTCP packet with a given SSRC, +// as it would look when sent over the wire. +// |distinguishing_string| allows different RTCP packets with the same SSRC +// to be distinguished. How this is set into the actual packet is +// unimportant, and depends on which RTCP message we choose to use. +rtc::Buffer CreateRtcpPacket(uint32_t ssrc, + const std::string& distinguishing_string = "") { + rtcp::Bye packet; + packet.SetSenderSsrc(ssrc); + if (distinguishing_string != "") { + // Actual way we use |distinguishing_string| is unimportant, so long + // as it ends up in the packet. + packet.SetReason(distinguishing_string); + } + return packet.Build(); +} + +static Matcher<rtc::ArrayView<const uint8_t>> SamePacketAs( + const rtc::Buffer& other) { + return ElementsAreArray(other.cbegin(), other.cend()); +} + +} // namespace + +TEST_F(RtcpDemuxerTest, OnRtcpPacketCalledOnCorrectSinkBySsrc) { + constexpr uint32_t ssrcs[] = {101, 202, 303}; + MockRtcpPacketSink sinks[arraysize(ssrcs)]; + for (size_t i = 0; i < arraysize(ssrcs); i++) { + AddSsrcSink(ssrcs[i], &sinks[i]); + } + + for (size_t i = 0; i < arraysize(ssrcs); i++) { + auto packet = CreateRtcpPacket(ssrcs[i]); + EXPECT_CALL(sinks[i], OnRtcpPacket(SamePacketAs(packet))).Times(1); + demuxer_.OnRtcpPacket(packet); + } +} + +TEST_F(RtcpDemuxerTest, OnRtcpPacketCalledOnResolvedRsidSink) { + // Set up some RSID sinks. + const std::string rsids[] = {"a", "b", "c"}; + MockRtcpPacketSink sinks[arraysize(rsids)]; + for (size_t i = 0; i < arraysize(rsids); i++) { + AddRsidSink(rsids[i], &sinks[i]); + } + + // Only resolve one of the sinks. + constexpr size_t resolved_sink_index = 0; + constexpr uint32_t ssrc = 345; + demuxer_.OnSsrcBoundToRsid(rsids[resolved_sink_index], ssrc); + + // The resolved sink gets notifications of RTCP messages with its SSRC. + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sinks[resolved_sink_index], OnRtcpPacket(SamePacketAs(packet))) + .Times(1); + + // RTCP received; expected calls triggered. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, + SingleCallbackAfterResolutionOfAnRsidToAlreadyRegisteredSsrc) { + // Associate a sink with an SSRC. + MockRtcpPacketSink sink; + constexpr uint32_t ssrc = 999; + AddSsrcSink(ssrc, &sink); + + // Associate the same sink with an RSID. + const std::string rsid = "r"; + AddRsidSink(rsid, &sink); + + // Resolve the RSID to the aforementioned SSRC. + demuxer_.OnSsrcBoundToRsid(rsid, ssrc); + + // OnRtcpPacket still called only a single time for messages with this SSRC. + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, + OnRtcpPacketCalledOnAllBroadcastSinksForAllRtcpPackets) { + MockRtcpPacketSink sinks[3]; + for (MockRtcpPacketSink& sink : sinks) { + AddBroadcastSink(&sink); + } + + constexpr uint32_t ssrc = 747; + auto packet = CreateRtcpPacket(ssrc); + + for (MockRtcpPacketSink& sink : sinks) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + } + + // RTCP received; expected calls triggered. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, PacketsDeliveredInRightOrderToNonBroadcastSink) { + constexpr uint32_t ssrc = 101; + MockRtcpPacketSink sink; + AddSsrcSink(ssrc, &sink); + + std::vector<rtc::Buffer> packets; + for (size_t i = 0; i < 5; i++) { + packets.push_back(CreateRtcpPacket(ssrc, std::to_string(i))); + } + + InSequence sequence; + for (const auto& packet : packets) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + } + + for (const auto& packet : packets) { + demuxer_.OnRtcpPacket(packet); + } +} + +TEST_F(RtcpDemuxerTest, PacketsDeliveredInRightOrderToBroadcastSink) { + MockRtcpPacketSink sink; + AddBroadcastSink(&sink); + + std::vector<rtc::Buffer> packets; + for (size_t i = 0; i < 5; i++) { + constexpr uint32_t ssrc = 101; + packets.push_back(CreateRtcpPacket(ssrc, std::to_string(i))); + } + + InSequence sequence; + for (const auto& packet : packets) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + } + + for (const auto& packet : packets) { + demuxer_.OnRtcpPacket(packet); + } +} + +TEST_F(RtcpDemuxerTest, MultipleSinksMappedToSameSsrc) { + MockRtcpPacketSink sinks[3]; + constexpr uint32_t ssrc = 404; + for (auto& sink : sinks) { + AddSsrcSink(ssrc, &sink); + } + + // Reception of an RTCP packet associated with the shared SSRC triggers the + // callback on all of the sinks associated with it. + auto packet = CreateRtcpPacket(ssrc); + for (auto& sink : sinks) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))); + } + + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, SinkMappedToMultipleSsrcs) { + constexpr uint32_t ssrcs[] = {404, 505, 606}; + MockRtcpPacketSink sink; + for (uint32_t ssrc : ssrcs) { + AddSsrcSink(ssrc, &sink); + } + + // The sink which is associated with multiple SSRCs gets the callback + // triggered for each of those SSRCs. + for (uint32_t ssrc : ssrcs) { + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))); + demuxer_.OnRtcpPacket(packet); + } +} + +TEST_F(RtcpDemuxerTest, MultipleRsidsOnSameSink) { + // Sink associated with multiple sinks. + MockRtcpPacketSink sink; + const std::string rsids[] = {"a", "b", "c"}; + for (const auto& rsid : rsids) { + AddRsidSink(rsid, &sink); + } + + // RSIDs resolved to SSRCs. + uint32_t ssrcs[arraysize(rsids)]; + for (size_t i = 0; i < arraysize(rsids); i++) { + ssrcs[i] = 1000 + static_cast<uint32_t>(i); + demuxer_.OnSsrcBoundToRsid(rsids[i], ssrcs[i]); + } + + // Set up packets to match those RSIDs/SSRCs. + std::vector<rtc::Buffer> packets; + for (size_t i = 0; i < arraysize(rsids); i++) { + packets.push_back(CreateRtcpPacket(ssrcs[i])); + } + + // The sink expects to receive all of the packets. + for (const auto& packet : packets) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + } + + // Packet demuxed correctly; OnRtcpPacket() triggered on sink. + for (const auto& packet : packets) { + demuxer_.OnRtcpPacket(packet); + } +} + +TEST_F(RtcpDemuxerTest, RsidUsedByMultipleSinks) { + MockRtcpPacketSink sinks[3]; + const std::string shared_rsid = "a"; + + for (MockRtcpPacketSink& sink : sinks) { + AddRsidSink(shared_rsid, &sink); + } + + constexpr uint32_t shared_ssrc = 888; + demuxer_.OnSsrcBoundToRsid(shared_rsid, shared_ssrc); + + auto packet = CreateRtcpPacket(shared_ssrc); + + for (MockRtcpPacketSink& sink : sinks) { + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet))).Times(1); + } + + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnSsrcSinkRemovedBeforeFirstPacket) { + constexpr uint32_t ssrc = 404; + MockRtcpPacketSink sink; + AddSsrcSink(ssrc, &sink); + + RemoveSink(&sink); + + // The removed sink does not get callbacks. + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnSsrcSinkRemovedAfterFirstPacket) { + constexpr uint32_t ssrc = 404; + NiceMock<MockRtcpPacketSink> sink; + AddSsrcSink(ssrc, &sink); + + auto before_packet = CreateRtcpPacket(ssrc); + demuxer_.OnRtcpPacket(before_packet); + + RemoveSink(&sink); + + // The removed sink does not get callbacks. + auto after_packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(after_packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnRsidSinkRemovedBeforeRsidResolution) { + const std::string rsid = "a"; + constexpr uint32_t ssrc = 404; + MockRtcpPacketSink sink; + AddRsidSink(rsid, &sink); + + // Removal before resolution. + RemoveSink(&sink); + demuxer_.OnSsrcBoundToRsid(rsid, ssrc); + + // The removed sink does not get callbacks. + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnRsidSinkRemovedAfterRsidResolution) { + const std::string rsid = "a"; + constexpr uint32_t ssrc = 404; + MockRtcpPacketSink sink; + AddRsidSink(rsid, &sink); + + // Removal after resolution. + demuxer_.OnSsrcBoundToRsid(rsid, ssrc); + RemoveSink(&sink); + + // The removed sink does not get callbacks. + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnBroadcastSinkRemovedBeforeFirstPacket) { + MockRtcpPacketSink sink; + AddBroadcastSink(&sink); + + RemoveBroadcastSink(&sink); + + // The removed sink does not get callbacks. + constexpr uint32_t ssrc = 404; + auto packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(packet); +} + +TEST_F(RtcpDemuxerTest, NoCallbackOnBroadcastSinkRemovedAfterFirstPacket) { + NiceMock<MockRtcpPacketSink> sink; + AddBroadcastSink(&sink); + + constexpr uint32_t ssrc = 404; + auto before_packet = CreateRtcpPacket(ssrc); + demuxer_.OnRtcpPacket(before_packet); + + RemoveBroadcastSink(&sink); + + // The removed sink does not get callbacks. + auto after_packet = CreateRtcpPacket(ssrc); + EXPECT_CALL(sink, OnRtcpPacket(_)).Times(0); // Not called. + demuxer_.OnRtcpPacket(after_packet); +} + +// The RSID to SSRC mapping should be one-to-one. If we end up receiving +// two (or more) packets with the same SSRC, but different RSIDs, we guarantee +// remembering the first one; no guarantees are made about further associations. +TEST_F(RtcpDemuxerTest, FirstResolutionOfRsidNotForgotten) { + MockRtcpPacketSink sink; + const std::string rsid = "a"; + AddRsidSink(rsid, &sink); + + constexpr uint32_t ssrc_a = 111; // First resolution - guaranteed effective. + demuxer_.OnSsrcBoundToRsid(rsid, ssrc_a); + + constexpr uint32_t ssrc_b = 222; // Second resolution - no guarantees. + demuxer_.OnSsrcBoundToRsid(rsid, ssrc_b); + + auto packet_a = CreateRtcpPacket(ssrc_a); + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet_a))).Times(1); + demuxer_.OnRtcpPacket(packet_a); + + auto packet_b = CreateRtcpPacket(ssrc_b); + EXPECT_CALL(sink, OnRtcpPacket(SamePacketAs(packet_b))).Times(AtLeast(0)); + demuxer_.OnRtcpPacket(packet_b); +} + +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +TEST_F(RtcpDemuxerTest, RepeatedSsrcToSinkAssociationsDisallowed) { + MockRtcpPacketSink sink; + + constexpr uint32_t ssrc = 101; + AddSsrcSink(ssrc, &sink); + EXPECT_DEATH(AddSsrcSink(ssrc, &sink), ""); +} + +TEST_F(RtcpDemuxerTest, RepeatedRsidToSinkAssociationsDisallowed) { + MockRtcpPacketSink sink; + + const std::string rsid = "z"; + AddRsidSink(rsid, &sink); + EXPECT_DEATH(AddRsidSink(rsid, &sink), ""); +} + +TEST_F(RtcpDemuxerTest, RepeatedBroadcastSinkRegistrationDisallowed) { + MockRtcpPacketSink sink; + + AddBroadcastSink(&sink); + EXPECT_DEATH(AddBroadcastSink(&sink), ""); +} + +TEST_F(RtcpDemuxerTest, SsrcSinkCannotAlsoBeRegisteredAsBroadcast) { + MockRtcpPacketSink sink; + + constexpr uint32_t ssrc = 101; + AddSsrcSink(ssrc, &sink); + EXPECT_DEATH(AddBroadcastSink(&sink), ""); +} + +TEST_F(RtcpDemuxerTest, RsidSinkCannotAlsoBeRegisteredAsBroadcast) { + MockRtcpPacketSink sink; + + const std::string rsid = "z"; + AddRsidSink(rsid, &sink); + EXPECT_DEATH(AddBroadcastSink(&sink), ""); +} + +TEST_F(RtcpDemuxerTest, BroadcastSinkCannotAlsoBeRegisteredAsSsrcSink) { + MockRtcpPacketSink sink; + + AddBroadcastSink(&sink); + constexpr uint32_t ssrc = 101; + EXPECT_DEATH(AddSsrcSink(ssrc, &sink), ""); +} + +TEST_F(RtcpDemuxerTest, BroadcastSinkCannotAlsoBeRegisteredAsRsidSink) { + MockRtcpPacketSink sink; + + AddBroadcastSink(&sink); + const std::string rsid = "j"; + EXPECT_DEATH(AddRsidSink(rsid, &sink), ""); +} + +TEST_F(RtcpDemuxerTest, MayNotCallRemoveSinkOnNeverAddedSink) { + MockRtcpPacketSink sink; + EXPECT_DEATH(RemoveSink(&sink), ""); +} + +TEST_F(RtcpDemuxerTest, MayNotCallRemoveBroadcastSinkOnNeverAddedSink) { + MockRtcpPacketSink sink; + EXPECT_DEATH(RemoveBroadcastSink(&sink), ""); +} + +TEST_F(RtcpDemuxerTest, RsidMustBeNonEmpty) { + MockRtcpPacketSink sink; + EXPECT_DEATH(AddRsidSink("", &sink), ""); +} + +TEST_F(RtcpDemuxerTest, RsidMustBeAlphaNumeric) { + MockRtcpPacketSink sink; + EXPECT_DEATH(AddRsidSink("a_3", &sink), ""); +} + +TEST_F(RtcpDemuxerTest, RsidMustNotExceedMaximumLength) { + MockRtcpPacketSink sink; + std::string rsid(StreamId::kMaxSize + 1, 'a'); + EXPECT_DEATH(AddRsidSink(rsid, &sink), ""); +} + +#endif + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtcp_packet_sink_interface.h b/third_party/libwebrtc/webrtc/call/rtcp_packet_sink_interface.h new file mode 100644 index 0000000000..8ea3f7d21c --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtcp_packet_sink_interface.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_RTCP_PACKET_SINK_INTERFACE_H_ +#define CALL_RTCP_PACKET_SINK_INTERFACE_H_ + +#include "api/array_view.h" + +namespace webrtc { + +// This class represents a receiver of unparsed RTCP packets. +// TODO(eladalon): Replace this by demuxing over parsed rather than raw data. +// Whether this should be over an entire RTCP packet, or over RTCP blocks, +// is still under discussion. +class RtcpPacketSinkInterface { + public: + virtual ~RtcpPacketSinkInterface() = default; + virtual void OnRtcpPacket(rtc::ArrayView<const uint8_t> packet) = 0; +}; + +} // namespace webrtc + +#endif // CALL_RTCP_PACKET_SINK_INTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_config.cc b/third_party/libwebrtc/webrtc/call/rtp_config.cc new file mode 100644 index 0000000000..3621f72890 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_config.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_config.h" + +#include <sstream> + +namespace webrtc { + +std::string NackConfig::ToString() const { + std::stringstream ss; + ss << "{rtp_history_ms: " << rtp_history_ms; + ss << '}'; + return ss.str(); +} + +std::string UlpfecConfig::ToString() const { + std::stringstream ss; + ss << "{ulpfec_payload_type: " << ulpfec_payload_type; + ss << ", red_payload_type: " << red_payload_type; + ss << ", red_rtx_payload_type: " << red_rtx_payload_type; + ss << '}'; + return ss.str(); +} + +bool UlpfecConfig::operator==(const UlpfecConfig& other) const { + return ulpfec_payload_type == other.ulpfec_payload_type && + red_payload_type == other.red_payload_type && + red_rtx_payload_type == other.red_rtx_payload_type; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_config.h b/third_party/libwebrtc/webrtc/call/rtp_config.h new file mode 100644 index 0000000000..86d32ac104 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_config.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTP_CONFIG_H_ +#define CALL_RTP_CONFIG_H_ + +#include <string> + +namespace webrtc { +// Settings for NACK, see RFC 4585 for details. +struct NackConfig { + NackConfig() : rtp_history_ms(0) {} + std::string ToString() const; + // Send side: the time RTP packets are stored for retransmissions. + // Receive side: the time the receiver is prepared to wait for + // retransmissions. + // Set to '0' to disable. + int rtp_history_ms; +}; + +// Settings for ULPFEC forward error correction. +// Set the payload types to '-1' to disable. +struct UlpfecConfig { + UlpfecConfig() + : ulpfec_payload_type(-1), + red_payload_type(-1), + red_rtx_payload_type(-1) {} + std::string ToString() const; + bool operator==(const UlpfecConfig& other) const; + + // Payload type used for ULPFEC packets. + int ulpfec_payload_type; + + // Payload type used for RED packets. + int red_payload_type; + + // RTX payload type for RED payload. + int red_rtx_payload_type; +}; +} // namespace webrtc +#endif // CALL_RTP_CONFIG_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_demuxer.cc b/third_party/libwebrtc/webrtc/call/rtp_demuxer.cc new file mode 100644 index 0000000000..835b09ff1f --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_demuxer.cc @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_demuxer.h" + +#include "call/rtp_packet_sink_interface.h" +#include "call/rtp_rtcp_demuxer_helper.h" +#include "call/ssrc_binding_observer.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +RtpDemuxerCriteria::RtpDemuxerCriteria() = default; +RtpDemuxerCriteria::~RtpDemuxerCriteria() = default; + +RtpDemuxer::RtpDemuxer() = default; + +RtpDemuxer::~RtpDemuxer() { + RTC_DCHECK(sink_by_mid_.empty()); + RTC_DCHECK(sink_by_ssrc_.empty()); + RTC_DCHECK(sinks_by_pt_.empty()); + RTC_DCHECK(sink_by_mid_and_rsid_.empty()); + RTC_DCHECK(sink_by_rsid_.empty()); + RTC_DCHECK(ssrc_binding_observers_.empty()); +} + +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() || Mid::IsLegalName(criteria.mid)); + RTC_DCHECK(criteria.rsid.empty() || StreamId::IsLegalName(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)) { + return false; + } + + 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); + } + } else { + if (!criteria.rsid.empty()) { + sink_by_rsid_.emplace(criteria.rsid, sink); + } + } + + for (uint32_t ssrc : criteria.ssrcs) { + sink_by_ssrc_.emplace(ssrc, sink); + } + + for (uint8_t payload_type : criteria.payload_types) { + sinks_by_pt_.emplace(payload_type, sink); + } + + RefreshKnownMids(); + + return true; +} + +bool RtpDemuxer::CriteriaWouldConflict( + const RtpDemuxerCriteria& criteria) const { + 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()) { + return true; + } + } else { + // If the exact rule already exists, then reject this duplicate. + if (sink_by_mid_and_rsid_.find(std::make_pair( + criteria.mid, criteria.rsid)) != sink_by_mid_and_rsid_.end()) { + return true; + } + // 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. + if (sink_by_mid_.find(criteria.mid) != sink_by_mid_.end()) { + return true; + } + } + } + + for (uint32_t ssrc : criteria.ssrcs) { + if (sink_by_ssrc_.find(ssrc) != sink_by_ssrc_.end()) { + return true; + } + } + + // TODO(steveanton): May also sanity check payload types. + + return false; +} + +void RtpDemuxer::RefreshKnownMids() { + known_mids_.clear(); + + for (auto const& item : sink_by_mid_) { + const std::string& mid = item.first; + known_mids_.insert(mid); + } + + for (auto const& item : sink_by_mid_and_rsid_) { + const std::string& mid = item.first.first; + known_mids_.insert(mid); + } +} + +bool RtpDemuxer::AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink) { + RtpDemuxerCriteria criteria; + criteria.ssrcs.insert(ssrc); + return AddSink(criteria, sink); +} + +void RtpDemuxer::AddSink(const std::string& rsid, + RtpPacketSinkInterface* sink) { + RtpDemuxerCriteria criteria; + criteria.rsid = rsid; + AddSink(criteria, sink); +} + +bool RtpDemuxer::RemoveSink(const RtpPacketSinkInterface* sink) { + RTC_DCHECK(sink); + size_t num_removed = RemoveFromMapByValue(&sink_by_mid_, sink) + + RemoveFromMapByValue(&sink_by_ssrc_, sink) + + RemoveFromMultimapByValue(&sinks_by_pt_, sink) + + RemoveFromMapByValue(&sink_by_mid_and_rsid_, sink) + + RemoveFromMapByValue(&sink_by_rsid_, sink); + RefreshKnownMids(); + return num_removed > 0; +} + +bool RtpDemuxer::OnRtpPacket(const RtpPacketReceived& packet) { + RtpPacketSinkInterface* sink = ResolveSink(packet); + if (sink != nullptr) { + sink->OnRtpPacket(packet); + return true; + } + return false; +} + +RtpPacketSinkInterface* RtpDemuxer::ResolveSink( + const RtpPacketReceived& packet) { + // See the BUNDLE spec for high level reference to this algorithm: + // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38#section-10.2 + + // RSID and RRID are routed to the same sinks. If an RSID is specified on a + // repair packet, it should be ignored and the RRID should be used. + std::string packet_mid, packet_rsid; + bool has_mid = packet.GetExtension<RtpMid>(&packet_mid); + bool has_rsid = packet.GetExtension<RepairedRtpStreamId>(&packet_rsid); + if (!has_rsid) { + has_rsid = packet.GetExtension<RtpStreamId>(&packet_rsid); + } + uint32_t ssrc = packet.Ssrc(); + + // Mid support is half-baked in branch 64. RtpStreamReceiverController only + // supports adding sinks by ssrc, so our mids will never show up in + // known_mids_, causing us to drop packets here. +#if 0 + // The BUNDLE spec says to drop any packets with unknown MIDs, even if the + // SSRC is known/latched. + if (has_mid && known_mids_.find(packet_mid) == known_mids_.end()) { + return nullptr; + } + + // Cache information we learn about SSRCs and IDs. We need to do this even if + // there isn't a rule/sink yet because we might add an MID/RSID rule after + // learning an MID/RSID<->SSRC association. + + std::string* mid = nullptr; + if (has_mid) { + mid_by_ssrc_[ssrc] = packet_mid; + mid = &packet_mid; + } else { + // If the packet does not include a MID header extension, check if there is + // a latched MID for the SSRC. + const auto it = mid_by_ssrc_.find(ssrc); + if (it != mid_by_ssrc_.end()) { + mid = &it->second; + } + } + + std::string* rsid = nullptr; + if (has_rsid) { + rsid_by_ssrc_[ssrc] = packet_rsid; + rsid = &packet_rsid; + } else { + // If the packet does not include an RRID/RSID header extension, check if + // there is a latched RSID for the SSRC. + const auto it = rsid_by_ssrc_.find(ssrc); + if (it != rsid_by_ssrc_.end()) { + rsid = &it->second; + } + } + + // If MID and/or RSID is specified, prioritize that for demuxing the packet. + // The motivation behind the BUNDLE algorithm is that we trust these are used + // deliberately by senders and are more likely to be correct than SSRC/payload + // type which are included with every packet. + // TODO(steveanton): According to the BUNDLE spec, new SSRC mappings are only + // accepted if the packet's extended sequence number is + // greater than that of the last SSRC mapping update. + // https://tools.ietf.org/html/rfc7941#section-4.2.6 + if (mid != nullptr) { + RtpPacketSinkInterface* sink_by_mid = ResolveSinkByMid(*mid, ssrc); + if (sink_by_mid != nullptr) { + return sink_by_mid; + } + + // RSID is scoped to a given MID if both are included. + if (rsid != nullptr) { + RtpPacketSinkInterface* sink_by_mid_rsid = + ResolveSinkByMidRsid(*mid, *rsid, ssrc); + if (sink_by_mid_rsid != nullptr) { + return sink_by_mid_rsid; + } + } + + // At this point, there is at least one sink added for this MID and an RSID + // but either the packet does not have an RSID or it is for a different + // RSID. This falls outside the BUNDLE spec so drop the packet. + return nullptr; + } + + // RSID can be used without MID as long as they are unique. + if (rsid != nullptr) { + RtpPacketSinkInterface* sink_by_rsid = ResolveSinkByRsid(*rsid, ssrc); + if (sink_by_rsid != nullptr) { + return sink_by_rsid; + } + } + +#endif + // We trust signaled SSRC more than payload type which is likely to conflict + // between streams. + const auto ssrc_sink_it = sink_by_ssrc_.find(ssrc); + if (ssrc_sink_it != sink_by_ssrc_.end()) { + return ssrc_sink_it->second; + } + + // Legacy senders will only signal payload type, support that as last resort. + return ResolveSinkByPayloadType(packet.PayloadType(), ssrc); +} + +RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByMid(const std::string& mid, + uint32_t ssrc) { + const auto it = sink_by_mid_.find(mid); + if (it != sink_by_mid_.end()) { + RtpPacketSinkInterface* sink = it->second; + bool notify = AddSsrcSinkBinding(ssrc, sink); + if (notify) { + for (auto* observer : ssrc_binding_observers_) { + observer->OnSsrcBoundToMid(mid, ssrc); + } + } + return sink; + } + return nullptr; +} + +RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByMidRsid( + const std::string& mid, + const std::string& rsid, + uint32_t ssrc) { + const auto it = sink_by_mid_and_rsid_.find(std::make_pair(mid, rsid)); + if (it != sink_by_mid_and_rsid_.end()) { + RtpPacketSinkInterface* sink = it->second; + bool notify = AddSsrcSinkBinding(ssrc, sink); + if (notify) { + for (auto* observer : ssrc_binding_observers_) { + observer->OnSsrcBoundToMidRsid(mid, rsid, ssrc); + } + } + return sink; + } + return nullptr; +} +void RtpDemuxer::RegisterRsidResolutionObserver(SsrcBindingObserver* observer) { + RegisterSsrcBindingObserver(observer); +} + +RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByRsid(const std::string& rsid, + uint32_t ssrc) { + const auto it = sink_by_rsid_.find(rsid); + if (it != sink_by_rsid_.end()) { + RtpPacketSinkInterface* sink = it->second; + bool notify = AddSsrcSinkBinding(ssrc, sink); + if (notify) { + for (auto* observer : ssrc_binding_observers_) { + observer->OnSsrcBoundToRsid(rsid, ssrc); + } + } + return sink; + } + return nullptr; +} +void RtpDemuxer::DeregisterRsidResolutionObserver( + const SsrcBindingObserver* observer) { + DeregisterSsrcBindingObserver(observer); +} + +RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByPayloadType( + uint8_t payload_type, + uint32_t ssrc) { + const auto range = sinks_by_pt_.equal_range(payload_type); + if (range.first != range.second) { + auto it = range.first; + const auto end = range.second; + if (std::next(it) == end) { + RtpPacketSinkInterface* sink = it->second; + bool notify = AddSsrcSinkBinding(ssrc, sink); + if (notify) { + for (auto* observer : ssrc_binding_observers_) { + observer->OnSsrcBoundToPayloadType(payload_type, ssrc); + } + } + return sink; + } + } + return nullptr; +} + +bool RtpDemuxer::AddSsrcSinkBinding(uint32_t ssrc, + RtpPacketSinkInterface* sink) { + if (sink_by_ssrc_.size() >= kMaxSsrcBindings) { + RTC_LOG(LS_WARNING) << "New SSRC=" << ssrc + << " sink binding ignored; limit of" << kMaxSsrcBindings + << " bindings has been reached."; + return false; + } + + auto result = sink_by_ssrc_.emplace(ssrc, sink); + auto it = result.first; + bool inserted = result.second; + if (inserted) { + return true; + } + if (it->second != sink) { + it->second = sink; + return true; + } + return false; +} + +void RtpDemuxer::RegisterSsrcBindingObserver(SsrcBindingObserver* observer) { + RTC_DCHECK(observer); + RTC_DCHECK(!ContainerHasKey(ssrc_binding_observers_, observer)); + + ssrc_binding_observers_.push_back(observer); +} + +void RtpDemuxer::DeregisterSsrcBindingObserver( + const SsrcBindingObserver* observer) { + RTC_DCHECK(observer); + auto it = std::find(ssrc_binding_observers_.begin(), + ssrc_binding_observers_.end(), observer); + RTC_DCHECK(it != ssrc_binding_observers_.end()); + ssrc_binding_observers_.erase(it); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_demuxer.h b/third_party/libwebrtc/webrtc/call/rtp_demuxer.h new file mode 100644 index 0000000000..971c151648 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_demuxer.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTP_DEMUXER_H_ +#define CALL_RTP_DEMUXER_H_ + +#include <map> +#include <set> +#include <string> +#include <utility> +#include <vector> + +namespace webrtc { + +class RtpPacketReceived; +class RtpPacketSinkInterface; +class SsrcBindingObserver; + +// This struct describes the criteria that will be used to match packets to a +// specific sink. +struct RtpDemuxerCriteria { + RtpDemuxerCriteria(); + ~RtpDemuxerCriteria(); + + // If not the empty string, will match packets with this MID. + std::string mid; + + // 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; + + // Will match packets with any of these SSRCs. + std::set<uint32_t> ssrcs; + + // Will match packets with any of these payload types. + std::set<uint8_t> payload_types; +}; + +// This class represents the RTP demuxing, for a single RTP session (i.e., one +// SSRC space, see RFC 7656). It isn't thread aware, leaving responsibility of +// multithreading issues to the user of this class. +// The demuxing algorithm follows the sketch given in the BUNDLE draft: +// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38#section-10.2 +// with modifications to support RTP stream IDs also. +// +// When a packet is received, the RtpDemuxer will route according to the +// following rules: +// 1. If the packet contains the MID header extension, and no sink has been +// added with that MID as a criteria, the packet is not routed. +// 2. If the packet has the MID header extension, but no RSID or RRID extension, +// and the MID is bound to a sink, then bind its SSRC to the same sink and +// forward the packet to that sink. Note that rebinding to the same sink is +// not an error. (Later packets with that SSRC would therefore be forwarded +// to the same sink, whether they have the MID header extension or not.) +// 3. If the packet has the MID header extension and either the RSID or RRID +// extension, and the MID, RSID (or RRID) pair is bound to a sink, then bind +// its SSRC to the same sink and forward the packet to that sink. Later +// packets with that SSRC will be forwarded to the same sink. +// 4. If the packet has the RSID or RRID header extension, but no MID extension, +// and the RSID or RRID is bound to an RSID sink, then bind its SSRC to the +// same sink and forward the packet to that sink. Later packets with that +// SSRC will be forwarded to the same sink. +// 5. If the packet's SSRC is bound to an SSRC through a previous call to +// AddSink, then forward the packet to that sink. Note that the RtpDemuxer +// will not verify the payload type even if included in the sink's criteria. +// The sink is expected to do the check in its handler. +// 6. If the packet's payload type is bound to exactly one payload type sink +// through an earlier call to AddSink, then forward the packet to that sink. +// 7. Otherwise, the packet is not routed. +// +// In summary, the routing algorithm will always try to first match MID and RSID +// (including through SSRC binding), match SSRC directly as needed, and use +// payload types only if all else fails. +class RtpDemuxer { + public: + // Maximum number of unique SSRC bindings allowed. This limit is to prevent + // memory overuse attacks due to a malicious peer sending many packets with + // different SSRCs. + static constexpr int kMaxSsrcBindings = 1000; + + RtpDemuxer(); + ~RtpDemuxer(); + + RtpDemuxer(const RtpDemuxer&) = delete; + void operator=(const RtpDemuxer&) = delete; + + // Registers a sink that will be notified when RTP packets match its given + // criteria according to the algorithm described in the class description. + // Returns true if the sink was successfully added. + // Returns false in the following situations: + // - Only MID is specified and the MID is already registered. + // - Only RSID is specified and the RSID is already registered. + // - Both MID and RSID is specified and the (MID, RSID) pair is already + // registered. + // - Any of the criteria SSRCs are already registered. + // If false is returned, no changes are made to the demuxer state. + bool AddSink(const RtpDemuxerCriteria& criteria, + RtpPacketSinkInterface* sink); + + // Registers a sink. Multiple SSRCs may be mapped to the same sink, but + // each SSRC may only be mapped to one sink. The return value reports + // whether the association has been recorded or rejected. Rejection may occur + // if the SSRC has already been associated with a sink. The previously added + // sink is *not* forgotten. + bool AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink); + + // Registers a sink's association to an RSID. Only one sink may be associated + // with a given RSID. Null pointer is not allowed. + void AddSink(const std::string& rsid, RtpPacketSinkInterface* sink); + + // Removes a sink. Return value reports if anything was actually removed. + // Null pointer is not allowed. + bool RemoveSink(const RtpPacketSinkInterface* sink); + + // Demuxes the given packet and forwards it to the chosen sink. Returns true + // if the packet was forwarded and false if the packet was dropped. + bool OnRtpPacket(const RtpPacketReceived& packet); + + // The Observer will be notified when an attribute (e.g., RSID, MID, etc.) is + // bound to an SSRC. + void RegisterSsrcBindingObserver(SsrcBindingObserver* observer); + // Deprecated: Use the above method. + void RegisterRsidResolutionObserver(SsrcBindingObserver* observer); + + // Undo a previous RegisterSsrcBindingObserver(). + void DeregisterSsrcBindingObserver(const SsrcBindingObserver* observer); + // Deprecated: Use the above method. + void DeregisterRsidResolutionObserver(const SsrcBindingObserver* observer); + + private: + // Returns true if adding a sink with the given criteria would cause conflicts + // with the existing criteria and should be rejected. + bool CriteriaWouldConflict(const RtpDemuxerCriteria& criteria) const; + + // Runs the demux algorithm on the given packet and returns the sink that + // should receive the packet. + // Will record any SSRC<->ID associations along the way. + // If the packet should be dropped, this method returns null. + RtpPacketSinkInterface* ResolveSink(const RtpPacketReceived& packet); + + // Used by the ResolveSink algorithm. + RtpPacketSinkInterface* ResolveSinkByMid(const std::string& mid, + uint32_t ssrc); + RtpPacketSinkInterface* ResolveSinkByMidRsid(const std::string& mid, + const std::string& rsid, + uint32_t ssrc); + RtpPacketSinkInterface* ResolveSinkByRsid(const std::string& rsid, + uint32_t ssrc); + RtpPacketSinkInterface* ResolveSinkByPayloadType(uint8_t payload_type, + uint32_t ssrc); + + // Regenerate the known_mids_ set from information in the sink_by_mid_ and + // sink_by_mid_and_rsid_ maps. + void RefreshKnownMids(); + + // Map each sink by its component attributes to facilitate quick lookups. + // Payload Type mapping is a multimap because if two sinks register for the + // same payload type, both AddSinks succeed but we must know not to demux on + // that attribute since it is ambiguous. + // Note: Mappings are only modified by AddSink/RemoveSink (except for + // SSRC mapping which receives all MID, payload type, or RSID to SSRC bindings + // discovered when demuxing packets). + std::map<std::string, RtpPacketSinkInterface*> sink_by_mid_; + std::map<uint32_t, RtpPacketSinkInterface*> sink_by_ssrc_; + std::multimap<uint8_t, RtpPacketSinkInterface*> sinks_by_pt_; + std::map<std::pair<std::string, std::string>, RtpPacketSinkInterface*> + sink_by_mid_and_rsid_; + std::map<std::string, RtpPacketSinkInterface*> sink_by_rsid_; + + // Tracks all the MIDs that have been identified in added criteria. Used to + // determine if a packet should be dropped right away because the MID is + // unknown. + std::set<std::string> known_mids_; + + // Records learned mappings of MID --> SSRC and RSID --> SSRC as packets are + // received. + // This is stored separately from the sink mappings because if a sink is + // removed we want to still remember these associations. + std::map<uint32_t, std::string> mid_by_ssrc_; + std::map<uint32_t, std::string> rsid_by_ssrc_; + + // Adds a binding from the SSRC to the given sink. Returns true if there was + // not already a sink bound to the SSRC or if the sink replaced a different + // sink. Returns false if the binding was unchanged. + bool AddSsrcSinkBinding(uint32_t ssrc, RtpPacketSinkInterface* sink); + + // Observers which will be notified when an RSID association to an SSRC is + // resolved by this object. + std::vector<SsrcBindingObserver*> ssrc_binding_observers_; +}; + +} // namespace webrtc + +#endif // CALL_RTP_DEMUXER_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_demuxer_unittest.cc b/third_party/libwebrtc/webrtc/call/rtp_demuxer_unittest.cc new file mode 100644 index 0000000000..ef092ee98f --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_demuxer_unittest.cc @@ -0,0 +1,1527 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_demuxer.h" + +#include <memory> +#include <set> +#include <string> + +#include "call/ssrc_binding_observer.h" +#include "call/test/mock_rtp_packet_sink_interface.h" +#include "common_types.h" // NOLINT(build/include) +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/basictypes.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/ptr_util.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::AtMost; +using ::testing::InSequence; +using ::testing::NiceMock; + +class MockSsrcBindingObserver : public SsrcBindingObserver { + public: + MOCK_METHOD2(OnSsrcBoundToRsid, void(const std::string& rsid, uint32_t ssrc)); + MOCK_METHOD2(OnSsrcBoundToMid, void(const std::string& mid, uint32_t ssrc)); + MOCK_METHOD3(OnSsrcBoundToMidRsid, + void(const std::string& mid, + const std::string& rsid, + uint32_t ssrc)); + MOCK_METHOD2(OnSsrcBoundToPayloadType, + void(uint8_t payload_type, uint32_t ssrc)); +}; + +class RtpDemuxerTest : public testing::Test { + protected: + ~RtpDemuxerTest() { + for (auto* sink : sinks_to_tear_down_) { + demuxer_.RemoveSink(sink); + } + for (auto* observer : observers_to_tear_down_) { + demuxer_.DeregisterSsrcBindingObserver(observer); + } + } + + // These are convenience methods for calling demuxer.AddSink with different + // parameters and will ensure that the sink is automatically removed when the + // test case finishes. + + bool AddSink(const RtpDemuxerCriteria& criteria, + RtpPacketSinkInterface* sink) { + bool added = demuxer_.AddSink(criteria, sink); + if (added) { + sinks_to_tear_down_.insert(sink); + } + return added; + } + + bool AddSinkOnlySsrc(uint32_t ssrc, RtpPacketSinkInterface* sink) { + RtpDemuxerCriteria criteria; + criteria.ssrcs = {ssrc}; + return AddSink(criteria, sink); + } + + bool AddSinkOnlyRsid(const std::string& rsid, RtpPacketSinkInterface* sink) { + RtpDemuxerCriteria criteria; + criteria.rsid = rsid; + return AddSink(criteria, sink); + } + + bool AddSinkOnlyMid(const std::string& mid, RtpPacketSinkInterface* sink) { + RtpDemuxerCriteria criteria; + criteria.mid = 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; + return AddSink(criteria, sink); + } + + bool RemoveSink(RtpPacketSinkInterface* sink) { + sinks_to_tear_down_.erase(sink); + return demuxer_.RemoveSink(sink); + } + + // These are convenience methods for calling + // demuxer.{Register|Unregister}SsrcBindingObserver such that observers are + // automatically removed when the test finishes. + + void RegisterSsrcBindingObserver(SsrcBindingObserver* observer) { + demuxer_.RegisterSsrcBindingObserver(observer); + observers_to_tear_down_.insert(observer); + } + + void DeregisterSsrcBindingObserver(SsrcBindingObserver* observer) { + demuxer_.DeregisterSsrcBindingObserver(observer); + observers_to_tear_down_.erase(observer); + } + + // The CreatePacket* methods are helpers for creating new RTP packets with + // various attributes set. Tests should use the helper that provides the + // minimum information needed to exercise the behavior under test. Tests also + // should not rely on any behavior which is not clearly described in the + // helper name/arguments. Any additional settings that are not covered by the + // helper should be set manually on the packet once it has been returned. + // For example, most tests in this file do not care about the RTP sequence + // number, but to ensure that the returned packets are valid the helpers will + // auto-increment the sequence number starting with 1. Tests that rely on + // specific sequence number behavior should call SetSequenceNumber manually on + // the returned packet. + + // Intended for use only by other CreatePacket* helpers. + std::unique_ptr<RtpPacketReceived> CreatePacket( + uint32_t ssrc, + RtpPacketReceived::ExtensionManager* extension_manager) { + auto packet = rtc::MakeUnique<RtpPacketReceived>(extension_manager); + packet->SetSsrc(ssrc); + packet->SetSequenceNumber(next_sequence_number_++); + return packet; + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrc(uint32_t ssrc) { + return CreatePacket(ssrc, nullptr); + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcMid( + uint32_t ssrc, + const std::string& mid) { + RtpPacketReceived::ExtensionManager extension_manager; + extension_manager.Register<RtpMid>(11); + + auto packet = CreatePacket(ssrc, &extension_manager); + packet->SetExtension<RtpMid>(mid); + return packet; + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRsid( + uint32_t ssrc, + const std::string& rsid) { + RtpPacketReceived::ExtensionManager extension_manager; + extension_manager.Register<RtpStreamId>(6); + + auto packet = CreatePacket(ssrc, &extension_manager); + packet->SetExtension<RtpStreamId>(rsid); + return packet; + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRrid( + uint32_t ssrc, + const std::string& rrid) { + RtpPacketReceived::ExtensionManager extension_manager; + extension_manager.Register<RepairedRtpStreamId>(7); + + auto packet = CreatePacket(ssrc, &extension_manager); + packet->SetExtension<RepairedRtpStreamId>(rrid); + return packet; + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcMidRsid( + uint32_t ssrc, + const std::string& mid, + const std::string& rsid) { + RtpPacketReceived::ExtensionManager extension_manager; + extension_manager.Register<RtpMid>(11); + extension_manager.Register<RtpStreamId>(6); + + auto packet = CreatePacket(ssrc, &extension_manager); + packet->SetExtension<RtpMid>(mid); + packet->SetExtension<RtpStreamId>(rsid); + return packet; + } + + std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRsidRrid( + uint32_t ssrc, + const std::string& rsid, + const std::string& rrid) { + RtpPacketReceived::ExtensionManager extension_manager; + extension_manager.Register<RtpStreamId>(6); + extension_manager.Register<RepairedRtpStreamId>(7); + + auto packet = CreatePacket(ssrc, &extension_manager); + packet->SetExtension<RtpStreamId>(rsid); + packet->SetExtension<RepairedRtpStreamId>(rrid); + return packet; + } + + RtpDemuxer demuxer_; + std::set<RtpPacketSinkInterface*> sinks_to_tear_down_; + std::set<SsrcBindingObserver*> observers_to_tear_down_; + uint16_t next_sequence_number_ = 1; +}; + +MATCHER_P(SamePacketAs, other, "") { + return arg.Ssrc() == other.Ssrc() && + arg.SequenceNumber() == other.SequenceNumber(); +} + +TEST_F(RtpDemuxerTest, CanAddSinkBySsrc) { + MockRtpPacketSink sink; + constexpr uint32_t ssrc = 1; + + EXPECT_TRUE(AddSinkOnlySsrc(ssrc, &sink)); +} + +TEST_F(RtpDemuxerTest, AllowAddSinkWithOverlappingPayloadTypesIfDifferentMid) { + const std::string mid1 = "v"; + const std::string mid2 = "a"; + constexpr uint8_t pt1 = 30; + constexpr uint8_t pt2 = 31; + constexpr uint8_t pt3 = 32; + + RtpDemuxerCriteria pt1_pt2; + pt1_pt2.mid = 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}; + MockRtpPacketSink sink2; + EXPECT_TRUE(AddSink(pt1_pt3, &sink2)); +} + +TEST_F(RtpDemuxerTest, RejectAddSinkForSameMidOnly) { + const std::string mid = "mid"; + + MockRtpPacketSink sink; + AddSinkOnlyMid(mid, &sink); + EXPECT_FALSE(AddSinkOnlyMid(mid, &sink)); +} + +TEST_F(RtpDemuxerTest, RejectAddSinkForSameMidRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + + MockRtpPacketSink sink1; + AddSinkBothMidRsid(mid, rsid, &sink1); + + MockRtpPacketSink sink2; + EXPECT_FALSE(AddSinkBothMidRsid(mid, rsid, &sink2)); +} + +TEST_F(RtpDemuxerTest, RejectAddSinkForConflictingMidAndMidRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + + MockRtpPacketSink mid_sink; + AddSinkOnlyMid(mid, &mid_sink); + + // This sink would never get any packets routed to it because the above sink + // would receive them all. + MockRtpPacketSink mid_rsid_sink; + EXPECT_FALSE(AddSinkBothMidRsid(mid, rsid, &mid_rsid_sink)); +} + +TEST_F(RtpDemuxerTest, RejectAddSinkForConflictingMidRsidAndMid) { + const std::string mid = "v"; + const std::string rsid = ""; + + MockRtpPacketSink mid_rsid_sink; + AddSinkBothMidRsid(mid, rsid, &mid_rsid_sink); + + // This sink would shadow the above sink. + MockRtpPacketSink mid_sink; + EXPECT_FALSE(AddSinkOnlyMid(mid, &mid_sink)); +} + +TEST_F(RtpDemuxerTest, AddSinkFailsIfCalledForTwoSinksWithSameSsrc) { + MockRtpPacketSink sink_a; + MockRtpPacketSink sink_b; + constexpr uint32_t ssrc = 1; + ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink_a)); + + EXPECT_FALSE(AddSinkOnlySsrc(ssrc, &sink_b)); +} + +TEST_F(RtpDemuxerTest, AddSinkFailsIfCalledTwiceEvenIfSameSinkWithSameSsrc) { + MockRtpPacketSink sink; + constexpr uint32_t ssrc = 1; + ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink)); + + EXPECT_FALSE(AddSinkOnlySsrc(ssrc, &sink)); +} + +// TODO(steveanton): Currently fails because payload type validation is not +// complete in AddSink (see note in rtp_demuxer.cc). +TEST_F(RtpDemuxerTest, DISABLED_RejectAddSinkForSamePayloadTypes) { + constexpr uint8_t pt1 = 30; + constexpr uint8_t pt2 = 31; + + RtpDemuxerCriteria pt1_pt2; + pt1_pt2.payload_types = {pt1, pt2}; + MockRtpPacketSink sink1; + AddSink(pt1_pt2, &sink1); + + RtpDemuxerCriteria pt2_pt1; + pt2_pt1.payload_types = {pt2, pt1}; + MockRtpPacketSink sink2; + EXPECT_FALSE(AddSink(pt2_pt1, &sink2)); +} + +// Routing Tests + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkBySsrc) { + constexpr uint32_t ssrcs[] = {101, 202, 303}; + MockRtpPacketSink sinks[arraysize(ssrcs)]; + for (size_t i = 0; i < arraysize(ssrcs); i++) { + AddSinkOnlySsrc(ssrcs[i], &sinks[i]); + } + + for (size_t i = 0; i < arraysize(ssrcs); i++) { + auto packet = CreatePacketWithSsrc(ssrcs[i]); + EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByRsid) { + const std::string rsids[] = {"a", "b", "c"}; + MockRtpPacketSink sinks[arraysize(rsids)]; + for (size_t i = 0; i < arraysize(rsids); i++) { + AddSinkOnlyRsid(rsids[i], &sinks[i]); + } + + for (size_t i = 0; i < arraysize(rsids); i++) { + auto packet = CreatePacketWithSsrcRsid(rtc::checked_cast<uint32_t>(i), + rsids[i]); + EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByMid) { + const std::string mids[] = {"a", "v", "s"}; + MockRtpPacketSink sinks[arraysize(mids)]; + for (size_t i = 0; i < arraysize(mids); i++) { + AddSinkOnlyMid(mids[i], &sinks[i]); + } + + for (size_t i = 0; i < arraysize(mids); i++) { + auto packet = CreatePacketWithSsrcMid(rtc::checked_cast<uint32_t>(i), + mids[i]); + EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByMidAndRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByRepairedRsid) { + const std::string rrid = "1"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink; + AddSinkOnlyRsid(rrid, &sink); + + auto packet_with_rrid = CreatePacketWithSsrcRrid(ssrc, rrid); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_rrid))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_rrid)); +} + +TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByPayloadType) { + constexpr uint32_t ssrc = 10; + constexpr uint8_t payload_type = 30; + + MockRtpPacketSink sink; + RtpDemuxerCriteria criteria; + criteria.payload_types = {payload_type}; + AddSink(criteria, &sink); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, PacketsDeliveredInRightOrder) { + constexpr uint32_t ssrc = 101; + MockRtpPacketSink sink; + AddSinkOnlySsrc(ssrc, &sink); + + std::unique_ptr<RtpPacketReceived> packets[5]; + for (size_t i = 0; i < arraysize(packets); i++) { + packets[i] = CreatePacketWithSsrc(ssrc); + packets[i]->SetSequenceNumber(rtc::checked_cast<uint16_t>(i)); + } + + InSequence sequence; + for (const auto& packet : packets) { + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + } + + for (const auto& packet : packets) { + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, SinkMappedToMultipleSsrcs) { + constexpr uint32_t ssrcs[] = {404, 505, 606}; + MockRtpPacketSink sink; + for (uint32_t ssrc : ssrcs) { + AddSinkOnlySsrc(ssrc, &sink); + } + + // The sink which is associated with multiple SSRCs gets the callback + // triggered for each of those SSRCs. + for (uint32_t ssrc : ssrcs) { + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, NoCallbackOnSsrcSinkRemovedBeforeFirstPacket) { + constexpr uint32_t ssrc = 404; + MockRtpPacketSink sink; + AddSinkOnlySsrc(ssrc, &sink); + + ASSERT_TRUE(RemoveSink(&sink)); + + // The removed sink does not get callbacks. + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called. + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, NoCallbackOnSsrcSinkRemovedAfterFirstPacket) { + constexpr uint32_t ssrc = 404; + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlySsrc(ssrc, &sink); + + InSequence sequence; + for (size_t i = 0; i < 10; i++) { + ASSERT_TRUE(demuxer_.OnRtpPacket(*CreatePacketWithSsrc(ssrc))); + } + + ASSERT_TRUE(RemoveSink(&sink)); + + // The removed sink does not get callbacks. + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called. + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +// An SSRC may only be mapped to a single sink. However, since configuration +// of this associations might come from the network, we need to fail gracefully. +TEST_F(RtpDemuxerTest, OnlyOneSinkPerSsrcGetsOnRtpPacketTriggered) { + MockRtpPacketSink sinks[3]; + constexpr uint32_t ssrc = 404; + ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sinks[0])); + ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sinks[1])); + ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sinks[2])); + + // The first sink associated with the SSRC remains active; other sinks + // were not really added, and so do not get OnRtpPacket() called. + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sinks[0], OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_CALL(sinks[1], OnRtpPacket(_)).Times(0); + EXPECT_CALL(sinks[2], OnRtpPacket(_)).Times(0); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, NoRepeatedCallbackOnRepeatedAddSinkForSameSink) { + constexpr uint32_t ssrc = 111; + MockRtpPacketSink sink; + + ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink)); + ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sink)); + + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, RemoveSinkReturnsFalseForNeverAddedSink) { + MockRtpPacketSink sink; + EXPECT_FALSE(RemoveSink(&sink)); +} + +TEST_F(RtpDemuxerTest, RemoveSinkReturnsTrueForPreviouslyAddedSsrcSink) { + constexpr uint32_t ssrc = 101; + MockRtpPacketSink sink; + AddSinkOnlySsrc(ssrc, &sink); + + EXPECT_TRUE(RemoveSink(&sink)); +} + +TEST_F(RtpDemuxerTest, + RemoveSinkReturnsTrueForUnresolvedPreviouslyAddedRsidSink) { + const std::string rsid = "a"; + MockRtpPacketSink sink; + AddSinkOnlyRsid(rsid, &sink); + + EXPECT_TRUE(RemoveSink(&sink)); +} + +TEST_F(RtpDemuxerTest, + RemoveSinkReturnsTrueForResolvedPreviouslyAddedRsidSink) { + const std::string rsid = "a"; + constexpr uint32_t ssrc = 101; + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyRsid(rsid, &sink); + ASSERT_TRUE(demuxer_.OnRtpPacket(*CreatePacketWithSsrcRsid(ssrc, rsid))); + + EXPECT_TRUE(RemoveSink(&sink)); +} + +TEST_F(RtpDemuxerTest, RsidLearnedAndLaterPacketsDeliveredWithOnlySsrc) { + MockRtpPacketSink sink; + const std::string rsid = "a"; + AddSinkOnlyRsid(rsid, &sink); + + // Create a sequence of RTP packets, where only the first one actually + // mentions the RSID. + std::unique_ptr<RtpPacketReceived> packets[5]; + constexpr uint32_t rsid_ssrc = 111; + packets[0] = CreatePacketWithSsrcRsid(rsid_ssrc, rsid); + for (size_t i = 1; i < arraysize(packets); i++) { + packets[i] = CreatePacketWithSsrc(rsid_ssrc); + } + + // The first packet associates the RSID with the SSRC, thereby allowing the + // demuxer to correctly demux all of the packets. + InSequence sequence; + for (const auto& packet : packets) { + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + } + for (const auto& packet : packets) { + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +TEST_F(RtpDemuxerTest, NoCallbackOnRsidSinkRemovedBeforeFirstPacket) { + MockRtpPacketSink sink; + const std::string rsid = "a"; + AddSinkOnlyRsid(rsid, &sink); + + // Sink removed - it won't get triggers even if packets with its RSID arrive. + ASSERT_TRUE(RemoveSink(&sink)); + + constexpr uint32_t ssrc = 111; + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called. + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, NoCallbackOnRsidSinkRemovedAfterFirstPacket) { + NiceMock<MockRtpPacketSink> sink; + const std::string rsid = "a"; + AddSinkOnlyRsid(rsid, &sink); + + InSequence sequence; + constexpr uint32_t ssrc = 111; + for (size_t i = 0; i < 10; i++) { + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet)); + } + + // Sink removed - it won't get triggers even if packets with its RSID arrive. + ASSERT_TRUE(RemoveSink(&sink)); + + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called. + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, NoCallbackOnMidSinkRemovedBeforeFirstPacket) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink; + AddSinkOnlyMid(mid, &sink); + RemoveSink(&sink); + + auto packet = CreatePacketWithSsrcMid(ssrc, mid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, NoCallbackOnMidSinkRemovedAfterFirstPacket) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + auto p1 = CreatePacketWithSsrcMid(ssrc, mid); + demuxer_.OnRtpPacket(*p1); + + RemoveSink(&sink); + + auto p2 = CreatePacketWithSsrcMid(ssrc, mid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*p2)); +} + +TEST_F(RtpDemuxerTest, NoCallbackOnMidRsidSinkRemovedAfterFirstPacket) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto p1 = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + demuxer_.OnRtpPacket(*p1); + + RemoveSink(&sink); + + auto p2 = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*p2)); +} + +// The RSID to SSRC mapping should be one-to-one. If we end up receiving +// two (or more) packets with the same SSRC, but different RSIDs, we guarantee +// delivery to one of them but not both. +TEST_F(RtpDemuxerTest, FirstSsrcAssociatedWithAnRsidIsNotForgotten) { + // Each sink has a distinct RSID. + MockRtpPacketSink sink_a; + const std::string rsid_a = "a"; + AddSinkOnlyRsid(rsid_a, &sink_a); + + MockRtpPacketSink sink_b; + const std::string rsid_b = "b"; + AddSinkOnlyRsid(rsid_b, &sink_b); + + InSequence sequence; // Verify that the order of delivery is unchanged. + + constexpr uint32_t shared_ssrc = 100; + + // First a packet with |rsid_a| is received, and |sink_a| is associated with + // its SSRC. + auto packet_a = CreatePacketWithSsrcRsid(shared_ssrc, rsid_a); + EXPECT_CALL(sink_a, OnRtpPacket(SamePacketAs(*packet_a))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_a)); + + // Second, a packet with |rsid_b| is received. We guarantee that |sink_b| + // receives it. + auto packet_b = CreatePacketWithSsrcRsid(shared_ssrc, rsid_b); + EXPECT_CALL(sink_a, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink_b, OnRtpPacket(SamePacketAs(*packet_b))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_b)); + + // Known edge-case; adding a new RSID association makes us re-examine all + // SSRCs. |sink_b| may or may not be associated with the SSRC now; we make + // no promises on that. However, since the RSID is specified and it cannot be + // found the packet should be dropped. + MockRtpPacketSink sink_c; + const std::string rsid_c = "c"; + constexpr uint32_t some_other_ssrc = shared_ssrc + 1; + AddSinkOnlySsrc(some_other_ssrc, &sink_c); + + auto packet_c = CreatePacketWithSsrcMid(shared_ssrc, rsid_c); + EXPECT_CALL(sink_a, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink_b, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink_c, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_c)); +} + +TEST_F(RtpDemuxerTest, MultipleRsidsOnSameSink) { + MockRtpPacketSink sink; + const std::string rsids[] = {"a", "b", "c"}; + + for (const std::string& rsid : rsids) { + AddSinkOnlyRsid(rsid, &sink); + } + + InSequence sequence; + for (size_t i = 0; i < arraysize(rsids); i++) { + // Assign different SSRCs and sequence numbers to all packets. + const uint32_t ssrc = 1000 + static_cast<uint32_t>(i); + const uint16_t sequence_number = 50 + static_cast<uint16_t>(i); + auto packet = CreatePacketWithSsrcRsid(ssrc, rsids[i]); + packet->SetSequenceNumber(sequence_number); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +// RSIDs are given higher priority than SSRC because we believe senders are less +// likely to mislabel packets with RSID than mislabel them with SSRCs. +TEST_F(RtpDemuxerTest, SinkWithBothRsidAndSsrcAssociations) { + MockRtpPacketSink sink; + constexpr uint32_t standalone_ssrc = 10101; + constexpr uint32_t rsid_ssrc = 20202; + const std::string rsid = "1"; + + AddSinkOnlySsrc(standalone_ssrc, &sink); + AddSinkOnlyRsid(rsid, &sink); + + InSequence sequence; + + auto ssrc_packet = CreatePacketWithSsrc(standalone_ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*ssrc_packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*ssrc_packet)); + + auto rsid_packet = CreatePacketWithSsrcRsid(rsid_ssrc, rsid); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*rsid_packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*rsid_packet)); +} + +// Packets are always guaranteed to be routed to only one sink. +TEST_F(RtpDemuxerTest, AssociatingByRsidAndBySsrcCannotTriggerDoubleCall) { + constexpr uint32_t ssrc = 10101; + const std::string rsid = "a"; + + MockRtpPacketSink sink; + AddSinkOnlySsrc(ssrc, &sink); + AddSinkOnlyRsid(rsid, &sink); + + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundToMid) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + auto packet = CreatePacketWithSsrcMid(ssrc, mid); + EXPECT_CALL(observer, OnSsrcBoundToMid(mid, ssrc)); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundToRsid) { + const std::string rsid = "1"; + constexpr uint32_t ssrc = 111; + + // Only RSIDs which the demuxer knows may be resolved. + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyRsid(rsid, &sink); + + NiceMock<MockSsrcBindingObserver> rsid_resolution_observers[3]; + for (auto& observer : rsid_resolution_observers) { + RegisterSsrcBindingObserver(&observer); + EXPECT_CALL(observer, OnSsrcBoundToRsid(rsid, ssrc)).Times(1); + } + + // The expected calls to OnSsrcBoundToRsid() will be triggered by this. + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundToMidRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + EXPECT_CALL(observer, OnSsrcBoundToMidRsid(mid, rsid, ssrc)); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundToPayloadType) { + constexpr uint8_t payload_type = 3; + constexpr uint32_t ssrc = 10; + + RtpDemuxerCriteria criteria; + criteria.payload_types = {payload_type}; + NiceMock<MockRtpPacketSink> sink; + AddSink(criteria, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + EXPECT_CALL(observer, OnSsrcBoundToPayloadType(payload_type, ssrc)); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// If one sink is associated with SSRC x, and another sink with RSID y, then if +// we receive a packet with both SSRC x and RSID y, route that to only the sink +// for RSID y since we believe RSID tags to be more trustworthy than signaled +// SSRCs. +TEST_F(RtpDemuxerTest, + PacketFittingBothRsidSinkAndSsrcSinkGivenOnlyToRsidSink) { + constexpr uint32_t ssrc = 111; + MockRtpPacketSink ssrc_sink; + AddSinkOnlySsrc(ssrc, &ssrc_sink); + + const std::string rsid = "a"; + MockRtpPacketSink rsid_sink; + AddSinkOnlyRsid(rsid, &rsid_sink); + + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + + EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0); + EXPECT_CALL(rsid_sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// We're not expecting RSIDs to be resolved to SSRCs which were previously +// mapped to sinks, and make no guarantees except for graceful handling. +TEST_F(RtpDemuxerTest, + GracefullyHandleRsidBeingMappedToPrevouslyAssociatedSsrc) { + constexpr uint32_t ssrc = 111; + NiceMock<MockRtpPacketSink> ssrc_sink; + AddSinkOnlySsrc(ssrc, &ssrc_sink); + + const std::string rsid = "a"; + NiceMock<MockRtpPacketSink> rsid_sink; + AddSinkOnlyRsid(rsid, &rsid_sink); + + NiceMock<MockSsrcBindingObserver> observer; + RegisterSsrcBindingObserver(&observer); + + // The SSRC was mapped to an SSRC sink, but was even active (packets flowed + // over it). + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + demuxer_.OnRtpPacket(*packet); + + // If the SSRC sink is ever removed, the RSID sink *might* receive indications + // of packets, and observers *might* be informed. Only graceful handling + // is guaranteed. + RemoveSink(&ssrc_sink); + EXPECT_CALL(rsid_sink, OnRtpPacket(SamePacketAs(*packet))).Times(AtLeast(0)); + EXPECT_CALL(observer, OnSsrcBoundToRsid(rsid, ssrc)).Times(AtLeast(0)); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// Tests that when one MID sink is configured, packets that include the MID +// extension will get routed to that sink and any packets that use the same +// SSRC as one of those packets later will also get routed to the sink, even +// if a new SSRC is introduced for the same MID. +TEST_F(RtpDemuxerTest, RoutedByMidWhenSsrcAdded) { + const std::string mid = "v"; + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + constexpr uint32_t ssrc1 = 10; + constexpr uint32_t ssrc2 = 11; + + auto packet_ssrc1_mid = CreatePacketWithSsrcMid(ssrc1, mid); + demuxer_.OnRtpPacket(*packet_ssrc1_mid); + auto packet_ssrc2_mid = CreatePacketWithSsrcMid(ssrc2, mid); + demuxer_.OnRtpPacket(*packet_ssrc2_mid); + + auto packet_ssrc1_only = CreatePacketWithSsrc(ssrc1); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc1_only))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc1_only)); + + auto packet_ssrc2_only = CreatePacketWithSsrc(ssrc2); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc2_only))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc2_only)); +} + +TEST_F(RtpDemuxerTest, DontLearnMidSsrcBindingBeforeSinkAdded) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + auto packet_ssrc_mid = CreatePacketWithSsrcMid(ssrc, mid); + ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_ssrc_mid)); + + MockRtpPacketSink sink; + AddSinkOnlyMid(mid, &sink); + + auto packet_ssrc_only = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_ssrc_only)); +} + +TEST_F(RtpDemuxerTest, DontForgetMidSsrcBindingWhenSinkRemoved) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink1; + AddSinkOnlyMid(mid, &sink1); + + auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid); + demuxer_.OnRtpPacket(*packet_with_mid); + + RemoveSink(&sink1); + + MockRtpPacketSink sink2; + AddSinkOnlyMid(mid, &sink2); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet_with_ssrc))); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +// If a sink is added with only a MID, then any packet with that MID no matter +// the RSID should be routed to that sink. +TEST_F(RtpDemuxerTest, RoutedByMidWithAnyRsid) { + const std::string mid = "v"; + const std::string rsid1 = "1"; + const std::string rsid2 = "2"; + constexpr uint32_t ssrc1 = 10; + constexpr uint32_t ssrc2 = 11; + + MockRtpPacketSink sink; + AddSinkOnlyMid(mid, &sink); + + InSequence sequence; + + auto packet_ssrc1_rsid1 = CreatePacketWithSsrcMidRsid(ssrc1, mid, rsid1); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc1_rsid1))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc1_rsid1)); + + auto packet_ssrc2_rsid2 = CreatePacketWithSsrcMidRsid(ssrc2, mid, rsid2); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc2_rsid2))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc2_rsid2)); +} + +// These two tests verify that for a sink added with a MID, RSID pair, if the +// MID and RSID are learned in separate packets (e.g., because the header +// extensions are sent separately), then a later packet with just SSRC will get +// routed to that sink. +// The first test checks that the functionality works when MID is learned first. +// The second test checks that the functionality works when RSID is learned +// first. +TEST_F(RtpDemuxerTest, LearnMidThenRsidSeparatelyAndRouteBySsrc) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid); + ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_mid)); + + auto packet_with_rsid = CreatePacketWithSsrcRsid(ssrc, rsid); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_rsid)); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +TEST_F(RtpDemuxerTest, LearnRsidThenMidSeparatelyAndRouteBySsrc) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto packet_with_rsid = CreatePacketWithSsrcRsid(ssrc, rsid); + ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_rsid)); + + auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_mid)); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +TEST_F(RtpDemuxerTest, DontLearnMidRsidBindingBeforeSinkAdded) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_both)); + + MockRtpPacketSink sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +TEST_F(RtpDemuxerTest, DontForgetMidRsidBindingWhenSinkRemoved) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink1; + AddSinkBothMidRsid(mid, rsid, &sink1); + + auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + demuxer_.OnRtpPacket(*packet_with_both); + + RemoveSink(&sink1); + + MockRtpPacketSink sink2; + AddSinkBothMidRsid(mid, rsid, &sink2); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet_with_ssrc))); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +TEST_F(RtpDemuxerTest, LearnMidRsidBindingAfterSinkAdded) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid); + demuxer_.OnRtpPacket(*packet_with_both); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +TEST_F(RtpDemuxerTest, DropByPayloadTypeIfNoSink) { + constexpr uint8_t payload_type = 30; + constexpr uint32_t ssrc = 10; + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +// For legacy applications, it's possible for us to demux if the payload type is +// unique. But if multiple sinks are registered with different MIDs and the same +// payload types, then we cannot route a packet with just payload type because +// it is ambiguous which sink it should be sent to. +TEST_F(RtpDemuxerTest, DropByPayloadTypeIfAddedInMultipleSinks) { + const std::string mid1 = "v"; + const std::string mid2 = "a"; + constexpr uint8_t payload_type = 30; + constexpr uint32_t ssrc = 10; + + RtpDemuxerCriteria mid1_pt; + mid1_pt.mid = 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}; + MockRtpPacketSink sink2; + AddSink(mid2_pt, &sink2); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + + EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink2, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +// If two sinks are added with different MIDs but the same payload types, then +// we cannot demux on the payload type only unless one of the sinks is removed. +TEST_F(RtpDemuxerTest, RoutedByPayloadTypeIfAmbiguousSinkRemoved) { + const std::string mid1 = "v"; + const std::string mid2 = "a"; + constexpr uint8_t payload_type = 30; + constexpr uint32_t ssrc = 10; + + RtpDemuxerCriteria mid1_pt; + mid1_pt.mid = 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}; + MockRtpPacketSink sink2; + AddSink(mid2_pt, &sink2); + + RemoveSink(&sink1); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + + EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet))).Times(1); + + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, RoutedByPayloadTypeLatchesSsrc) { + constexpr uint8_t payload_type = 30; + constexpr uint32_t ssrc = 10; + + RtpDemuxerCriteria pt; + pt.payload_types = {payload_type}; + NiceMock<MockRtpPacketSink> sink; + AddSink(pt, &sink); + + auto packet_with_pt = CreatePacketWithSsrc(ssrc); + packet_with_pt->SetPayloadType(payload_type); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt)); + + auto packet_with_ssrc = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc)); +} + +// RSIDs are scoped within MID, so if two sinks are registered with the same +// RSIDs but different MIDs, then packets containing both extensions should be +// routed to the correct one. +TEST_F(RtpDemuxerTest, PacketWithSameRsidDifferentMidRoutedToProperSink) { + const std::string mid1 = "mid1"; + const std::string mid2 = "mid2"; + const std::string rsid = "rsid"; + constexpr uint32_t ssrc1 = 10; + constexpr uint32_t ssrc2 = 11; + + NiceMock<MockRtpPacketSink> mid1_sink; + AddSinkBothMidRsid(mid1, rsid, &mid1_sink); + + MockRtpPacketSink mid2_sink; + AddSinkBothMidRsid(mid2, rsid, &mid2_sink); + + auto packet_mid1 = CreatePacketWithSsrcMidRsid(ssrc1, mid1, rsid); + ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_mid1)); + + auto packet_mid2 = CreatePacketWithSsrcMidRsid(ssrc2, mid2, rsid); + EXPECT_CALL(mid2_sink, OnRtpPacket(SamePacketAs(*packet_mid2))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_mid2)); +} + +// If a sink is first bound to a given SSRC by signaling but later a new sink is +// bound to a given MID by a later signaling, then when a packet arrives with +// both the SSRC and MID, then the signaled MID sink should take precedence. +TEST_F(RtpDemuxerTest, SignaledMidShouldOverwriteSignaledSsrc) { + constexpr uint32_t ssrc = 11; + const std::string mid = "mid"; + + MockRtpPacketSink ssrc_sink; + AddSinkOnlySsrc(ssrc, &ssrc_sink); + + MockRtpPacketSink mid_sink; + AddSinkOnlyMid(mid, &mid_sink); + + auto p = CreatePacketWithSsrcMid(ssrc, mid); + EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0); + EXPECT_CALL(mid_sink, OnRtpPacket(SamePacketAs(*p))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*p)); +} + +// Extends the previous test to also ensure that later packets that do not +// specify MID are still routed to the MID sink rather than the overwritten SSRC +// sink. +TEST_F(RtpDemuxerTest, SignaledMidShouldOverwriteSignalledSsrcPersistent) { + constexpr uint32_t ssrc = 11; + const std::string mid = "mid"; + + MockRtpPacketSink ssrc_sink; + AddSinkOnlySsrc(ssrc, &ssrc_sink); + + NiceMock<MockRtpPacketSink> mid_sink; + AddSinkOnlyMid(mid, &mid_sink); + + EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0); + + auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid); + demuxer_.OnRtpPacket(*packet_with_mid); + + auto packet_without_mid = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(mid_sink, OnRtpPacket(SamePacketAs(*packet_without_mid))) + .Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_without_mid)); +} + +TEST_F(RtpDemuxerTest, RouteByPayloadTypeMultipleMatch) { + constexpr uint32_t ssrc = 10; + constexpr uint8_t pt1 = 30; + constexpr uint8_t pt2 = 31; + + MockRtpPacketSink sink; + RtpDemuxerCriteria criteria; + criteria.payload_types = {pt1, pt2}; + AddSink(criteria, &sink); + + auto packet_with_pt1 = CreatePacketWithSsrc(ssrc); + packet_with_pt1->SetPayloadType(pt1); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_pt1))); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt1)); + + auto packet_with_pt2 = CreatePacketWithSsrc(ssrc); + packet_with_pt2->SetPayloadType(pt2); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_pt2))); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt2)); +} + +TEST_F(RtpDemuxerTest, DontDemuxOnMidAloneIfAddedWithRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink; + AddSinkBothMidRsid(mid, rsid, &sink); + + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + + auto packet = CreatePacketWithSsrcMid(ssrc, mid); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, DemuxBySsrcEvenWithMidAndRsid) { + const std::string mid = "v"; + const std::string rsid = "1"; + constexpr uint32_t ssrc = 10; + + RtpDemuxerCriteria criteria; + criteria.rsid = rsid; + criteria.mid = mid; + criteria.ssrcs = {ssrc}; + MockRtpPacketSink sink; + AddSink(criteria, &sink); + + auto packet = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// In slight deviation from the BUNDLE spec, if we match a sink according to +// SSRC, then we do not verify payload type against the criteria and defer to +// the sink to check that it is correct. +TEST_F(RtpDemuxerTest, DoNotCheckPayloadTypeIfMatchedByOtherCriteria) { + constexpr uint32_t ssrc = 10; + constexpr uint8_t payload_type = 30; + constexpr uint8_t different_payload_type = payload_type + 1; + + RtpDemuxerCriteria criteria; + criteria.ssrcs = {ssrc}; + criteria.payload_types = {payload_type}; + MockRtpPacketSink sink; + AddSink(criteria, &sink); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(different_payload_type); + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// If a repair packet includes an RSID it should be ignored and the packet +// should be routed by its RRID. +TEST_F(RtpDemuxerTest, PacketWithRsidAndRridRoutedByRrid) { + const std::string rsid = "1"; + const std::string rrid = "1r"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink_rsid; + AddSinkOnlyRsid(rsid, &sink_rsid); + + MockRtpPacketSink sink_rrid; + AddSinkOnlyRsid(rrid, &sink_rrid); + + auto packet = CreatePacketWithSsrcRsidRrid(ssrc, rsid, rrid); + EXPECT_CALL(sink_rsid, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink_rrid, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +// Same test as above but checks that the latched SSRC routes to the RRID sink. +TEST_F(RtpDemuxerTest, PacketWithRsidAndRridLatchesSsrcToRrid) { + const std::string rsid = "1"; + const std::string rrid = "1r"; + constexpr uint32_t ssrc = 10; + + MockRtpPacketSink sink_rsid; + AddSinkOnlyRsid(rsid, &sink_rsid); + + NiceMock<MockRtpPacketSink> sink_rrid; + AddSinkOnlyRsid(rrid, &sink_rrid); + + auto packet_rsid_rrid = CreatePacketWithSsrcRsidRrid(ssrc, rsid, rrid); + demuxer_.OnRtpPacket(*packet_rsid_rrid); + + auto packet_ssrc_only = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink_rsid, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink_rrid, OnRtpPacket(SamePacketAs(*packet_ssrc_only))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc_only)); +} + +// Tests that a packet which includes MID and RSID is dropped and not routed by +// SSRC if the MID and RSID do not match an added sink. +TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedBySsrc) { + constexpr uint32_t ssrc = 10; + const std::string mid = "v"; + const std::string rsid = "1"; + const std::string wrong_rsid = "2"; + + RtpDemuxerCriteria criteria; + criteria.mid = mid; + criteria.rsid = rsid; + criteria.ssrcs = {ssrc}; + MockRtpPacketSink sink; + AddSink(criteria, &sink); + + auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, wrong_rsid); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +// Tests that a packet which includes MID and RSID is dropped and not routed by +// payload type if the MID and RSID do not match an added sink. +TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedByPayloadType) { + constexpr uint32_t ssrc = 10; + const std::string mid = "v"; + const std::string rsid = "1"; + 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}; + MockRtpPacketSink sink; + AddSink(criteria, &sink); + + auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, wrong_rsid); + packet->SetPayloadType(payload_type); + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); +} + +// Observers are only notified of an SSRC binding to an RSID if we care about +// the RSID (i.e., have a sink added for that RSID). +TEST_F(RtpDemuxerTest, ObserversNotNotifiedOfUntrackedRsids) { + const std::string rsid = "1"; + constexpr uint32_t ssrc = 111; + + MockSsrcBindingObserver rsid_resolution_observers[3]; + for (auto& observer : rsid_resolution_observers) { + RegisterSsrcBindingObserver(&observer); + EXPECT_CALL(observer, OnSsrcBoundToRsid(_, _)).Times(0); + } + + // Since no sink is registered for this SSRC/RSID, expect the packet to not be + // routed and no observers notified of the SSRC -> RSID binding. + EXPECT_FALSE(demuxer_.OnRtpPacket(*CreatePacketWithSsrcRsid(ssrc, rsid))); +} + +// Ensure that observers are notified of SSRC bindings only once per unique +// binding source (e.g., SSRC -> MID, SSRC -> RSID, etc.) +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundtoMidOnlyOnce) { + const std::string mid = "v"; + constexpr uint32_t ssrc = 10; + + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + EXPECT_CALL(observer, OnSsrcBoundToMid(mid, ssrc)).Times(1); + + demuxer_.OnRtpPacket(*CreatePacketWithSsrcMid(ssrc, mid)); + demuxer_.OnRtpPacket(*CreatePacketWithSsrcMid(ssrc, mid)); +} + +// Ensure that when a new SSRC -> MID binding is discovered observers are also +// notified of that, even if there has already been an SSRC bound to the MID. +TEST_F(RtpDemuxerTest, ObserversNotifiedOfSsrcBoundtoMidWhenSsrcChanges) { + const std::string mid = "v"; + constexpr uint32_t ssrc1 = 10; + constexpr uint32_t ssrc2 = 11; + + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + InSequence seq; + EXPECT_CALL(observer, OnSsrcBoundToMid(mid, ssrc1)).Times(1); + EXPECT_CALL(observer, OnSsrcBoundToMid(mid, ssrc2)).Times(1); + + auto p1 = CreatePacketWithSsrcMid(ssrc1, mid); + demuxer_.OnRtpPacket(*p1); + + auto p2 = CreatePacketWithSsrcMid(ssrc2, mid); + demuxer_.OnRtpPacket(*p2); +} + +TEST_F(RtpDemuxerTest, DeregisteredRsidObserversNotInformedOfResolutions) { + constexpr uint32_t ssrc = 111; + const std::string rsid = "a"; + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyRsid(rsid, &sink); + + // Register several, then deregister only one, to show that not all of the + // observers had been forgotten when one was removed. + MockSsrcBindingObserver observer_1; + MockSsrcBindingObserver observer_2_removed; + MockSsrcBindingObserver observer_3; + + RegisterSsrcBindingObserver(&observer_1); + RegisterSsrcBindingObserver(&observer_2_removed); + RegisterSsrcBindingObserver(&observer_3); + + DeregisterSsrcBindingObserver(&observer_2_removed); + + EXPECT_CALL(observer_1, OnSsrcBoundToRsid(rsid, ssrc)).Times(1); + EXPECT_CALL(observer_2_removed, OnSsrcBoundToRsid(_, _)).Times(0); + EXPECT_CALL(observer_3, OnSsrcBoundToRsid(rsid, ssrc)).Times(1); + + // The expected calls to OnSsrcBoundToRsid() will be triggered by this. + demuxer_.OnRtpPacket(*CreatePacketWithSsrcRsid(ssrc, rsid)); +} + +TEST_F(RtpDemuxerTest, + PacketFittingBothRsidSinkAndSsrcSinkTriggersResolutionCallbacks) { + constexpr uint32_t ssrc = 111; + NiceMock<MockRtpPacketSink> ssrc_sink; + AddSinkOnlySsrc(ssrc, &ssrc_sink); + + const std::string rsid = "a"; + NiceMock<MockRtpPacketSink> rsid_sink; + AddSinkOnlyRsid(rsid, &rsid_sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + auto packet = CreatePacketWithSsrcRsid(ssrc, rsid); + EXPECT_CALL(observer, OnSsrcBoundToRsid(rsid, ssrc)).Times(1); + demuxer_.OnRtpPacket(*packet); +} + +TEST_F(RtpDemuxerTest, MaliciousPeerCannotCauseMemoryOveruse) { + const std::string mid = "v"; + + NiceMock<MockRtpPacketSink> sink; + AddSinkOnlyMid(mid, &sink); + + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + + EXPECT_CALL(observer, OnSsrcBoundToMid(_, _)) + .Times(AtMost(RtpDemuxer::kMaxSsrcBindings)); + + for (int i = 0; i < RtpDemuxer::kMaxSsrcBindings + 1; i++) { + auto packet = CreatePacketWithSsrcMid(i, mid); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); + } +} + +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +TEST_F(RtpDemuxerTest, CriteriaMustBeNonEmpty) { + MockRtpPacketSink sink; + RtpDemuxerCriteria criteria; + EXPECT_DEATH(AddSink(criteria, &sink), ""); +} + +TEST_F(RtpDemuxerTest, RsidMustBeAlphaNumeric) { + MockRtpPacketSink sink; + EXPECT_DEATH(AddSinkOnlyRsid("a_3", &sink), ""); +} + +TEST_F(RtpDemuxerTest, MidMustBeAlphaNumeric) { + MockRtpPacketSink sink; + EXPECT_DEATH(AddSinkOnlyMid("a_3", &sink), ""); +} + +TEST_F(RtpDemuxerTest, RsidMustNotExceedMaximumLength) { + MockRtpPacketSink sink; + std::string rsid(StreamId::kMaxSize + 1, 'a'); + EXPECT_DEATH(AddSinkOnlyRsid(rsid, &sink), ""); +} + +TEST_F(RtpDemuxerTest, MidMustNotExceedMaximumLength) { + MockRtpPacketSink sink; + std::string mid(Mid::kMaxSize + 1, 'a'); + EXPECT_DEATH(AddSinkOnlyMid(mid, &sink), ""); +} + +TEST_F(RtpDemuxerTest, DoubleRegisterationOfSsrcBindingObserverDisallowed) { + MockSsrcBindingObserver observer; + RegisterSsrcBindingObserver(&observer); + EXPECT_DEATH(RegisterSsrcBindingObserver(&observer), ""); +} + +TEST_F(RtpDemuxerTest, + DregisterationOfNeverRegisteredSsrcBindingObserverDisallowed) { + MockSsrcBindingObserver observer; + EXPECT_DEATH(DeregisterSsrcBindingObserver(&observer), ""); +} + +#endif + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_interfaces_gn/moz.build b/third_party/libwebrtc/webrtc/call/rtp_interfaces_gn/moz.build new file mode 100644 index 0000000000..b93140bbf1 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_interfaces_gn/moz.build @@ -0,0 +1,217 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/rtp_config.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("rtp_interfaces_gn") diff --git a/third_party/libwebrtc/webrtc/call/rtp_packet_sink_interface.h b/third_party/libwebrtc/webrtc/call/rtp_packet_sink_interface.h new file mode 100644 index 0000000000..ffbd58c398 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_packet_sink_interface.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_RTP_PACKET_SINK_INTERFACE_H_ +#define CALL_RTP_PACKET_SINK_INTERFACE_H_ + +namespace webrtc { + +class RtpPacketReceived; + +// This class represents a receiver of already parsed RTP packets. +class RtpPacketSinkInterface { + public: + virtual ~RtpPacketSinkInterface() = default; + virtual void OnRtpPacket(const RtpPacketReceived& packet) = 0; +}; + +} // namespace webrtc + +#endif // CALL_RTP_PACKET_SINK_INTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_receiver_gn/moz.build b/third_party/libwebrtc/webrtc/call/rtp_receiver_gn/moz.build new file mode 100644 index 0000000000..8b0688339a --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_receiver_gn/moz.build @@ -0,0 +1,229 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/rtcp_demuxer.cc", + "/third_party/libwebrtc/webrtc/call/rtp_demuxer.cc", + "/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.cc", + "/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.cc", + "/third_party/libwebrtc/webrtc/call/rtx_receive_stream.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("rtp_receiver_gn") diff --git a/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.cc b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.cc new file mode 100644 index 0000000000..d36242b11b --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_rtcp_demuxer_helper.h" + +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" + +namespace webrtc { + +rtc::Optional<uint32_t> ParseRtcpPacketSenderSsrc( + rtc::ArrayView<const uint8_t> packet) { + rtcp::CommonHeader header; + for (const uint8_t* next_packet = packet.begin(); next_packet < packet.end(); + next_packet = header.NextPacket()) { + if (!header.Parse(next_packet, packet.end() - next_packet)) { + return rtc::Optional<uint32_t>(); + } + + switch (header.type()) { + case rtcp::Bye::kPacketType: + case rtcp::ExtendedReports::kPacketType: + case rtcp::Psfb::kPacketType: + case rtcp::ReceiverReport::kPacketType: + case rtcp::Rtpfb::kPacketType: + case rtcp::SenderReport::kPacketType: { + // Sender SSRC at the beginning of the RTCP payload. + if (header.payload_size_bytes() >= sizeof(uint32_t)) { + const uint32_t ssrc_sender = + ByteReader<uint32_t>::ReadBigEndian(header.payload()); + return rtc::Optional<uint32_t>(ssrc_sender); + } else { + return rtc::Optional<uint32_t>(); + } + } + } + } + + return rtc::Optional<uint32_t>(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.h b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.h new file mode 100644 index 0000000000..32408e8a7a --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTP_RTCP_DEMUXER_HELPER_H_ +#define CALL_RTP_RTCP_DEMUXER_HELPER_H_ + +#include <algorithm> +#include <map> +#include <utility> + +#include "api/array_view.h" +#include "api/optional.h" +#include "rtc_base/basictypes.h" + +namespace webrtc { + +// TODO(eladalon): Remove this in the next CL. +template <typename Container> +bool MultimapAssociationExists(const Container& multimap, + const typename Container::key_type& key, + const typename Container::mapped_type& val) { + auto it_range = multimap.equal_range(key); + using Reference = typename Container::const_reference; + return std::any_of(it_range.first, it_range.second, + [val](Reference elem) { return elem.second == val; }); +} + +template <typename Container, typename Value> +size_t RemoveFromMultimapByValue(Container* multimap, const Value& value) { + size_t count = 0; + for (auto it = multimap->begin(); it != multimap->end();) { + if (it->second == value) { + it = multimap->erase(it); + ++count; + } else { + ++it; + } + } + return count; +} + +template <typename Map, typename Value> +size_t RemoveFromMapByValue(Map* map, const Value& value) { + size_t count = 0; + for (auto it = map->begin(); it != map->end();) { + if (it->second == value) { + it = map->erase(it); + ++count; + } else { + ++it; + } + } + return count; +} + +template <typename Container, typename Key> +bool ContainerHasKey(const Container& c, const Key& k) { + return std::find(c.cbegin(), c.cend(), k) != c.cend(); +} + +// TODO(eladalon): Remove this in the next CL. +template <typename Container> +bool MultimapHasValue(const Container& c, + const typename Container::mapped_type& v) { + auto predicate = [v](const typename Container::value_type& it) { + return it.second == v; + }; + return std::any_of(c.cbegin(), c.cend(), predicate); +} + +template <typename Map> +bool MapHasValue(const Map& map, const typename Map::mapped_type& value) { + auto predicate = [value](const typename Map::value_type& it) { + return it.second == value; + }; + return std::any_of(map.cbegin(), map.cend(), predicate); +} + +template <typename Container> +bool MultimapHasKey(const Container& c, + const typename Container::key_type& key) { + auto it_range = c.equal_range(key); + return it_range.first != it_range.second; +} + +rtc::Optional<uint32_t> ParseRtcpPacketSenderSsrc( + rtc::ArrayView<const uint8_t> packet); + +} // namespace webrtc + +#endif // CALL_RTP_RTCP_DEMUXER_HELPER_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper_unittest.cc b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper_unittest.cc new file mode 100644 index 0000000000..cb8a092616 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper_unittest.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <cstdio> + +#include "call/rtp_rtcp_demuxer_helper.h" + +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/basictypes.h" +#include "rtc_base/buffer.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +constexpr uint32_t kSsrc = 8374; +} // namespace + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_ByePacket) { + webrtc::rtcp::Bye rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, + ParseRtcpPacketSenderSsrc_ExtendedReportsPacket) { + webrtc::rtcp::ExtendedReports rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_PsfbPacket) { + webrtc::rtcp::Pli rtcp_packet; // Psfb is abstract; use a subclass. + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_ReceiverReportPacket) { + webrtc::rtcp::ReceiverReport rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_RtpfbPacket) { + // Rtpfb is abstract; use a subclass. + webrtc::rtcp::RapidResyncRequest rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_SenderReportPacket) { + webrtc::rtcp::SenderReport rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_EQ(ssrc, kSsrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_MalformedRtcpPacket) { + uint8_t garbage[100]; + memset(&garbage[0], 0, arraysize(garbage)); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(garbage); + EXPECT_FALSE(ssrc); +} + +TEST(RtpRtcpDemuxerHelperTest, + ParseRtcpPacketSenderSsrc_RtcpMessageWithoutSenderSsrc) { + webrtc::rtcp::ExtendedJitterReport rtcp_packet; // Has no sender SSRC. + rtc::Buffer raw_packet = rtcp_packet.Build(); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc(raw_packet); + EXPECT_FALSE(ssrc); +} + +TEST(RtpRtcpDemuxerHelperTest, ParseRtcpPacketSenderSsrc_TruncatedRtcpMessage) { + webrtc::rtcp::Bye rtcp_packet; + rtcp_packet.SetSenderSsrc(kSsrc); + rtc::Buffer raw_packet = rtcp_packet.Build(); + + constexpr size_t rtcp_length_bytes = 8; + ASSERT_EQ(rtcp_length_bytes, raw_packet.size()); + + rtc::Optional<uint32_t> ssrc = ParseRtcpPacketSenderSsrc( + rtc::ArrayView<const uint8_t>(raw_packet.data(), rtcp_length_bytes - 1)); + EXPECT_FALSE(ssrc); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_sender_gn/moz.build b/third_party/libwebrtc/webrtc/call/rtp_sender_gn/moz.build new file mode 100644 index 0000000000..f5abe18b6c --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_sender_gn/moz.build @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("rtp_sender_gn") diff --git a/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.cc b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.cc new file mode 100644 index 0000000000..a5d73f5784 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.cc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_stream_receiver_controller.h" + +#include "rtc_base/logging.h" +#include "rtc_base/ptr_util.h" + +namespace webrtc { + +RtpStreamReceiverController::Receiver::Receiver( + RtpStreamReceiverController* controller, + uint32_t ssrc, + RtpPacketSinkInterface* sink) + : controller_(controller), sink_(sink) { + const bool sink_added = controller_->AddSink(ssrc, sink_); + if (!sink_added) { + RTC_LOG(LS_ERROR) + << "RtpStreamReceiverController::Receiver::Receiver: Sink " + << "could not be added for SSRC=" << ssrc << "."; + } +} + +RtpStreamReceiverController::Receiver::~Receiver() { + // Don't require return value > 0, since for RTX we currently may + // have multiple Receiver objects with the same sink. + // TODO(nisse): Consider adding a DCHECK when RtxReceiveStream is wired up. + controller_->RemoveSink(sink_); +} + +RtpStreamReceiverController::RtpStreamReceiverController() = default; +RtpStreamReceiverController::~RtpStreamReceiverController() = default; + +std::unique_ptr<RtpStreamReceiverInterface> +RtpStreamReceiverController::CreateReceiver( + uint32_t ssrc, + RtpPacketSinkInterface* sink) { + return rtc::MakeUnique<Receiver>(this, ssrc, sink); +} + +bool RtpStreamReceiverController::OnRtpPacket(const RtpPacketReceived& packet) { + rtc::CritScope cs(&lock_); + return demuxer_.OnRtpPacket(packet); +} + +bool RtpStreamReceiverController::AddSink(uint32_t ssrc, + RtpPacketSinkInterface* sink) { + rtc::CritScope cs(&lock_); + return demuxer_.AddSink(ssrc, sink); +} + +size_t RtpStreamReceiverController::RemoveSink( + const RtpPacketSinkInterface* sink) { + rtc::CritScope cs(&lock_); + return demuxer_.RemoveSink(sink); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.h b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.h new file mode 100644 index 0000000000..c523e3f5b3 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_RTP_STREAM_RECEIVER_CONTROLLER_H_ +#define CALL_RTP_STREAM_RECEIVER_CONTROLLER_H_ + +#include <memory> + +#include "call/rtp_demuxer.h" +#include "call/rtp_stream_receiver_controller_interface.h" +#include "rtc_base/criticalsection.h" + +namespace webrtc { + +class RtpPacketReceived; + +// This class represents the RTP receive parsing and demuxing, for a +// single RTP session. +// TODO(nisse): Add RTCP processing, we should aim to terminate RTCP +// and not leave any RTCP processing to individual receive streams. +// TODO(nisse): Extract per-packet processing, including parsing and +// demuxing, into a separate class. +class RtpStreamReceiverController + : public RtpStreamReceiverControllerInterface { + public: + RtpStreamReceiverController(); + ~RtpStreamReceiverController() override; + + // Implements RtpStreamReceiverControllerInterface. + std::unique_ptr<RtpStreamReceiverInterface> CreateReceiver( + uint32_t ssrc, + RtpPacketSinkInterface* sink) override; + + // Thread-safe wrappers for the corresponding RtpDemuxer methods. + bool AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink) override; + size_t RemoveSink(const RtpPacketSinkInterface* sink) override; + + // TODO(nisse): Not yet responsible for parsing. + bool OnRtpPacket(const RtpPacketReceived& packet); + + private: + class Receiver : public RtpStreamReceiverInterface { + public: + Receiver(RtpStreamReceiverController* controller, + uint32_t ssrc, + RtpPacketSinkInterface* sink); + + ~Receiver() override; + + private: + RtpStreamReceiverController* const controller_; + RtpPacketSinkInterface* const sink_; + }; + + // TODO(nisse): Move to a TaskQueue for synchronization. When used + // by Call, we expect construction and all methods but OnRtpPacket + // to be called on the same thread, and OnRtpPacket to be called + // by a single, but possibly distinct, thread. But applications not + // using Call may have use threads differently. + rtc::CriticalSection lock_; + RtpDemuxer demuxer_ RTC_GUARDED_BY(&lock_); +}; + +} // namespace webrtc + +#endif // CALL_RTP_STREAM_RECEIVER_CONTROLLER_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller_interface.h b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller_interface.h new file mode 100644 index 0000000000..a5e5295c31 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller_interface.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_RTP_STREAM_RECEIVER_CONTROLLER_INTERFACE_H_ +#define CALL_RTP_STREAM_RECEIVER_CONTROLLER_INTERFACE_H_ + +#include <memory> + +#include "call/rtp_packet_sink_interface.h" + +namespace webrtc { + +// An RtpStreamReceiver is responsible for the rtp-specific but +// media-independent state needed for receiving an RTP stream. +// TODO(nisse): Currently, only owns the association between ssrc and +// the stream's RtpPacketSinkInterface. Ownership of corresponding +// objects from modules/rtp_rtcp/ should move to this class (or +// rather, the corresponding implementation class). We should add +// methods for getting rtp receive stats, and for sending RTCP +// messages related to the receive stream. +class RtpStreamReceiverInterface { + public: + virtual ~RtpStreamReceiverInterface() {} +}; + +// This class acts as a factory for RtpStreamReceiver objects. +class RtpStreamReceiverControllerInterface { + public: + virtual ~RtpStreamReceiverControllerInterface() {} + + virtual std::unique_ptr<RtpStreamReceiverInterface> CreateReceiver( + uint32_t ssrc, + RtpPacketSinkInterface* sink) = 0; + // For registering additional sinks, needed for FlexFEC. + virtual bool AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink) = 0; + virtual size_t RemoveSink(const RtpPacketSinkInterface* sink) = 0; +}; + +} // namespace webrtc + +#endif // CALL_RTP_STREAM_RECEIVER_CONTROLLER_INTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.cc b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.cc new file mode 100644 index 0000000000..090c261e4e --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtp_transport_controller_send.h" + +namespace webrtc { + +RtpTransportControllerSend::RtpTransportControllerSend( + Clock* clock, + webrtc::RtcEventLog* event_log) + : pacer_(clock, &packet_router_, event_log), + send_side_cc_(clock, nullptr /* observer */, event_log, &pacer_) {} + +PacketRouter* RtpTransportControllerSend::packet_router() { + return &packet_router_; +} + +PacedSender* RtpTransportControllerSend::pacer() { + return &pacer_; +} + +SendSideCongestionController* RtpTransportControllerSend::send_side_cc() { + return &send_side_cc_; +} + +TransportFeedbackObserver* +RtpTransportControllerSend::transport_feedback_observer() { + return &send_side_cc_; +} + +RtpPacketSender* RtpTransportControllerSend::packet_sender() { + return &pacer_; +} + +const RtpKeepAliveConfig& RtpTransportControllerSend::keepalive_config() const { + return keepalive_; +} + +void RtpTransportControllerSend::SetAllocatedSendBitrateLimits( + int min_send_bitrate_bps, + int max_padding_bitrate_bps) { + pacer_.SetSendBitrateLimits(min_send_bitrate_bps, max_padding_bitrate_bps); +} + +void RtpTransportControllerSend::SetKeepAliveConfig( + const RtpKeepAliveConfig& config) { + keepalive_ = config; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.h b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.h new file mode 100644 index 0000000000..45d6db0fee --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTP_TRANSPORT_CONTROLLER_SEND_H_ +#define CALL_RTP_TRANSPORT_CONTROLLER_SEND_H_ + +#include "call/rtp_transport_controller_send_interface.h" +#include "common_types.h" // NOLINT(build/include) +#include "modules/congestion_controller/include/send_side_congestion_controller.h" +#include "modules/pacing/packet_router.h" +#include "rtc_base/constructormagic.h" + +namespace webrtc { +class Clock; +class RtcEventLog; + +// TODO(nisse): When we get the underlying transports here, we should +// have one object implementing RtpTransportControllerSendInterface +// per transport, sharing the same congestion controller. +class RtpTransportControllerSend : public RtpTransportControllerSendInterface { + public: + RtpTransportControllerSend(Clock* clock, webrtc::RtcEventLog* event_log); + + // Implements RtpTransportControllerSendInterface + PacketRouter* packet_router() override; + // TODO(holmer): Temporarily exposed, should be removed and the + // appropriate methods should be added to this class instead. + // In addition the PacedSender should be driven by this class, either + // by owning the process thread, or later by using a task queue. + PacedSender* pacer() override; + SendSideCongestionController* send_side_cc() override; + TransportFeedbackObserver* transport_feedback_observer() override; + RtpPacketSender* packet_sender() override; + const RtpKeepAliveConfig& keepalive_config() const override; + + void SetAllocatedSendBitrateLimits(int min_send_bitrate_bps, + int max_padding_bitrate_bps) override; + + void SetKeepAliveConfig(const RtpKeepAliveConfig& config); + + private: + PacketRouter packet_router_; + PacedSender pacer_; + SendSideCongestionController send_side_cc_; + RtpKeepAliveConfig keepalive_; + + RTC_DISALLOW_COPY_AND_ASSIGN(RtpTransportControllerSend); +}; + +} // namespace webrtc + +#endif // CALL_RTP_TRANSPORT_CONTROLLER_SEND_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send_interface.h b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send_interface.h new file mode 100644 index 0000000000..7b0dbefd14 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtp_transport_controller_send_interface.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTP_TRANSPORT_CONTROLLER_SEND_INTERFACE_H_ +#define CALL_RTP_TRANSPORT_CONTROLLER_SEND_INTERFACE_H_ + +namespace webrtc { + +class PacedSender; +class PacketRouter; +class RtpPacketSender; +struct RtpKeepAliveConfig; +class SendSideCongestionController; +class TransportFeedbackObserver; + +// An RtpTransportController should own everything related to the RTP +// transport to/from a remote endpoint. We should have separate +// interfaces for send and receive side, even if they are implemented +// by the same class. This is an ongoing refactoring project. At some +// point, this class should be promoted to a public api under +// webrtc/api/rtp/. +// +// For a start, this object is just a collection of the objects needed +// by the VideoSendStream constructor. The plan is to move ownership +// of all RTP-related objects here, and add methods to create per-ssrc +// objects which would then be passed to VideoSendStream. Eventually, +// direct accessors like packet_router() should be removed. +// +// This should also have a reference to the underlying +// webrtc::Transport(s). Currently, webrtc::Transport is implemented by +// WebRtcVideoChannel and WebRtcVoiceMediaChannel, and owned by +// WebrtcSession. Video and audio always uses different transport +// objects, even in the common case where they are bundled over the +// same underlying transport. +// +// Extracting the logic of the webrtc::Transport from BaseChannel and +// subclasses into a separate class seems to be a prerequesite for +// moving the transport here. +class RtpTransportControllerSendInterface { + public: + virtual ~RtpTransportControllerSendInterface() {} + virtual PacketRouter* packet_router() = 0; + virtual PacedSender* pacer() = 0; + // Currently returning the same pointer, but with different types. + virtual SendSideCongestionController* send_side_cc() = 0; + virtual TransportFeedbackObserver* transport_feedback_observer() = 0; + + virtual RtpPacketSender* packet_sender() = 0; + virtual const RtpKeepAliveConfig& keepalive_config() const = 0; + + // SetAllocatedSendBitrateLimits sets bitrates limits imposed by send codec + // settings. + // |min_send_bitrate_bps| is the total minimum send bitrate required by all + // sending streams. This is the minimum bitrate the PacedSender will use. + // Note that SendSideCongestionController::OnNetworkChanged can still be + // called with a lower bitrate estimate. |max_padding_bitrate_bps| is the max + // bitrate the send streams request for padding. This can be higher than the + // current network estimate and tells the PacedSender how much it should max + // pad unless there is real packets to send. + virtual void SetAllocatedSendBitrateLimits(int min_send_bitrate_bps, + int max_padding_bitrate_bps) = 0; +}; + +} // namespace webrtc + +#endif // CALL_RTP_TRANSPORT_CONTROLLER_SEND_INTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtx_receive_stream.cc b/third_party/libwebrtc/webrtc/call/rtx_receive_stream.cc new file mode 100644 index 0000000000..09200791f8 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtx_receive_stream.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <utility> + +#include "call/rtx_receive_stream.h" +#include "modules/rtp_rtcp/include/receive_statistics.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +RtxReceiveStream::RtxReceiveStream( + RtpPacketSinkInterface* media_sink, + std::map<int, int> associated_payload_types, + uint32_t media_ssrc, + ReceiveStatistics* rtp_receive_statistics /* = nullptr */) + : media_sink_(media_sink), + associated_payload_types_(std::move(associated_payload_types)), + media_ssrc_(media_ssrc), + rtp_receive_statistics_(rtp_receive_statistics) { + if (associated_payload_types_.empty()) { + RTC_LOG(LS_WARNING) + << "RtxReceiveStream created with empty payload type mapping."; + } +} + +RtxReceiveStream::~RtxReceiveStream() = default; + +void RtxReceiveStream::OnRtpPacket(const RtpPacketReceived& rtx_packet) { + if (rtp_receive_statistics_) { + RTPHeader header; + rtx_packet.GetHeader(&header); + rtp_receive_statistics_->IncomingPacket(header, rtx_packet.size(), + false /* retransmitted */); + } + rtc::ArrayView<const uint8_t> payload = rtx_packet.payload(); + + if (payload.size() < kRtxHeaderSize) { + return; + } + + auto it = associated_payload_types_.find(rtx_packet.PayloadType()); + if (it == associated_payload_types_.end()) { + RTC_LOG(LS_VERBOSE) << "Unknown payload type " + << static_cast<int>(rtx_packet.PayloadType()) + << " on rtx ssrc " << rtx_packet.Ssrc(); + return; + } + RtpPacketReceived media_packet; + media_packet.CopyHeaderFrom(rtx_packet); + + media_packet.SetSsrc(media_ssrc_); + media_packet.SetSequenceNumber((payload[0] << 8) + payload[1]); + media_packet.SetPayloadType(it->second); + media_packet.set_recovered(true); + + // Skip the RTX header. + rtc::ArrayView<const uint8_t> rtx_payload = + payload.subview(kRtxHeaderSize); + + uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size()); + RTC_DCHECK(media_payload != nullptr); + + memcpy(media_payload, rtx_payload.data(), rtx_payload.size()); + + media_sink_->OnRtpPacket(media_packet); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/rtx_receive_stream.h b/third_party/libwebrtc/webrtc/call/rtx_receive_stream.h new file mode 100644 index 0000000000..8ffa4400a9 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtx_receive_stream.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CALL_RTX_RECEIVE_STREAM_H_ +#define CALL_RTX_RECEIVE_STREAM_H_ + +#include <map> + +#include "call/rtp_packet_sink_interface.h" + +namespace webrtc { + +class ReceiveStatistics; + +// This class is responsible for RTX decapsulation. The resulting media packets +// are passed on to a sink representing the associated media stream. +class RtxReceiveStream : public RtpPacketSinkInterface { + public: + RtxReceiveStream(RtpPacketSinkInterface* media_sink, + std::map<int, int> associated_payload_types, + uint32_t media_ssrc, + // TODO(nisse): Delete this argument, and + // corresponding member variable, by moving the + // responsibility for rtcp feedback to + // RtpStreamReceiverController. + ReceiveStatistics* rtp_receive_statistics = nullptr); + ~RtxReceiveStream() override; + // RtpPacketSinkInterface. + void OnRtpPacket(const RtpPacketReceived& packet) override; + + private: + RtpPacketSinkInterface* const media_sink_; + // Map from rtx payload type -> media payload type. + const std::map<int, int> associated_payload_types_; + // TODO(nisse): Ultimately, the media receive stream shouldn't care about the + // ssrc, and we should delete this. + const uint32_t media_ssrc_; + ReceiveStatistics* const rtp_receive_statistics_; +}; + +} // namespace webrtc + +#endif // CALL_RTX_RECEIVE_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/rtx_receive_stream_unittest.cc b/third_party/libwebrtc/webrtc/call/rtx_receive_stream_unittest.cc new file mode 100644 index 0000000000..65ab82b0dd --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/rtx_receive_stream_unittest.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/rtx_receive_stream.h" +#include "call/test/mock_rtp_packet_sink_interface.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::_; +using ::testing::StrictMock; + +constexpr int kMediaPayloadType = 100; +constexpr int kRtxPayloadType = 98; +constexpr int kUnknownPayloadType = 90; +constexpr uint32_t kMediaSSRC = 0x3333333; +constexpr uint16_t kMediaSeqno = 0x5657; + +constexpr uint8_t kRtxPacket[] = { + 0x80, // Version 2. + 98, // Payload type. + 0x12, 0x34, // Seqno. + 0x11, 0x11, 0x11, 0x11, // Timestamp. + 0x22, 0x22, 0x22, 0x22, // SSRC. + // RTX header. + 0x56, 0x57, // Orig seqno. + // Payload. + 0xee, +}; + +constexpr uint8_t kRtxPacketWithCVO[] = { + 0x90, // Version 2, X set. + 98, // Payload type. + 0x12, 0x34, // Seqno. + 0x11, 0x11, 0x11, 0x11, // Timestamp. + 0x22, 0x22, 0x22, 0x22, // SSRC. + 0xbe, 0xde, 0x00, 0x01, // Extension header. + 0x30, 0x01, 0x00, 0x00, // 90 degree rotation. + // RTX header. + 0x56, 0x57, // Orig seqno. + // Payload. + 0xee, +}; + +std::map<int, int> PayloadTypeMapping() { + const std::map<int, int> m = {{kRtxPayloadType, kMediaPayloadType}}; + return m; +} + +template <typename T> +rtc::ArrayView<T> Truncate(rtc::ArrayView<T> a, size_t drop) { + return a.subview(0, a.size() - drop); +} + +} // namespace + +TEST(RtxReceiveStreamTest, RestoresPacketPayload) { + StrictMock<MockRtpPacketSink> media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(rtc::ArrayView<const uint8_t>(kRtxPacket))); + + EXPECT_CALL(media_sink, OnRtpPacket(_)).WillOnce(testing::Invoke( + [](const RtpPacketReceived& packet) { + EXPECT_EQ(packet.SequenceNumber(), kMediaSeqno); + EXPECT_EQ(packet.Ssrc(), kMediaSSRC); + EXPECT_EQ(packet.PayloadType(), kMediaPayloadType); + EXPECT_THAT(packet.payload(), testing::ElementsAre(0xee)); + })); + + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, SetsRecoveredFlag) { + StrictMock<MockRtpPacketSink> media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(rtc::ArrayView<const uint8_t>(kRtxPacket))); + EXPECT_FALSE(rtx_packet.recovered()); + EXPECT_CALL(media_sink, OnRtpPacket(_)) + .WillOnce(testing::Invoke([](const RtpPacketReceived& packet) { + EXPECT_TRUE(packet.recovered()); + })); + + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, IgnoresUnknownPayloadType) { + StrictMock<MockRtpPacketSink> media_sink; + const std::map<int, int> payload_type_mapping = { + {kUnknownPayloadType, kMediaPayloadType}}; + + RtxReceiveStream rtx_sink(&media_sink, payload_type_mapping, kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(rtc::ArrayView<const uint8_t>(kRtxPacket))); + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, IgnoresTruncatedPacket) { + StrictMock<MockRtpPacketSink> media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE( + rtx_packet.Parse(Truncate(rtc::ArrayView<const uint8_t>(kRtxPacket), 2))); + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, CopiesRtpHeaderExtensions) { + StrictMock<MockRtpPacketSink> media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpHeaderExtensionMap extension_map; + extension_map.RegisterByType(3, kRtpExtensionVideoRotation); + RtpPacketReceived rtx_packet(&extension_map); + EXPECT_TRUE(rtx_packet.Parse( + rtc::ArrayView<const uint8_t>(kRtxPacketWithCVO))); + + VideoRotation rotation = kVideoRotation_0; + EXPECT_TRUE(rtx_packet.GetExtension<VideoOrientation>(&rotation)); + EXPECT_EQ(kVideoRotation_90, rotation); + + EXPECT_CALL(media_sink, OnRtpPacket(_)).WillOnce(testing::Invoke( + [](const RtpPacketReceived& packet) { + EXPECT_EQ(packet.SequenceNumber(), kMediaSeqno); + EXPECT_EQ(packet.Ssrc(), kMediaSSRC); + EXPECT_EQ(packet.PayloadType(), kMediaPayloadType); + EXPECT_THAT(packet.payload(), testing::ElementsAre(0xee)); + VideoRotation rotation = kVideoRotation_0; + EXPECT_TRUE(packet.GetExtension<VideoOrientation>(&rotation)); + EXPECT_EQ(rotation, kVideoRotation_90); + })); + + rtx_sink.OnRtpPacket(rtx_packet); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/ssrc_binding_observer.h b/third_party/libwebrtc/webrtc/call/ssrc_binding_observer.h new file mode 100644 index 0000000000..73a8e8e586 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/ssrc_binding_observer.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_SSRC_BINDING_OBSERVER_H_ +#define CALL_SSRC_BINDING_OBSERVER_H_ + +#include <string> + +#include "rtc_base/basictypes.h" + +namespace webrtc { + +// With newer versions of SDP, SSRC is often not explicitly signaled and must +// be learned on the fly. This happens by correlating packet SSRCs with included +// RTP extension headers like MID and RSID, or by receiving information from +// RTCP messages. +// SsrcBindingObservers will be notified when a new binding is learned, which +// can happen during call setup and/or during the call. +class SsrcBindingObserver { + public: + virtual ~SsrcBindingObserver() = default; + + virtual void OnSsrcBoundToRsid(const std::string& rsid, uint32_t ssrc) {} + + virtual void OnSsrcBoundToMid(const std::string& mid, uint32_t ssrc) {} + + virtual void OnSsrcBoundToMidRsid(const std::string& mid, + const std::string& rsid, + uint32_t ssrc) {} + + virtual void OnSsrcBoundToPayloadType(uint8_t payload_type, uint32_t ssrc) {} +}; + +} // namespace webrtc + +#endif // CALL_SSRC_BINDING_OBSERVER_H_ diff --git a/third_party/libwebrtc/webrtc/call/syncable.cc b/third_party/libwebrtc/webrtc/call/syncable.cc new file mode 100644 index 0000000000..a821881884 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/syncable.cc @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/syncable.h" + +namespace webrtc { + +Syncable::~Syncable() = default; + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/syncable.h b/third_party/libwebrtc/webrtc/call/syncable.h new file mode 100644 index 0000000000..a97990b2be --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/syncable.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Syncable is used by RtpStreamsSynchronizer in VideoReceiveStream, and +// implemented by AudioReceiveStream. + +#ifndef CALL_SYNCABLE_H_ +#define CALL_SYNCABLE_H_ + +#include <stdint.h> + +#include "api/optional.h" + +namespace webrtc { + +class Syncable { + public: + struct Info { + int64_t latest_receive_time_ms = 0; + uint32_t latest_received_capture_timestamp = 0; + uint32_t capture_time_ntp_secs = 0; + uint32_t capture_time_ntp_frac = 0; + uint32_t capture_time_source_clock = 0; + int current_delay_ms = 0; + }; + + virtual ~Syncable(); + + virtual int id() const = 0; + virtual rtc::Optional<Info> GetInfo() const = 0; + virtual uint32_t GetPlayoutTimestamp() const = 0; + virtual void SetMinimumPlayoutDelay(int delay_ms) = 0; +}; +} // namespace webrtc + +#endif // CALL_SYNCABLE_H_ diff --git a/third_party/libwebrtc/webrtc/call/test/mock_rtp_packet_sink_interface.h b/third_party/libwebrtc/webrtc/call/test/mock_rtp_packet_sink_interface.h new file mode 100644 index 0000000000..add399b690 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/test/mock_rtp_packet_sink_interface.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CALL_TEST_MOCK_RTP_PACKET_SINK_INTERFACE_H_ +#define CALL_TEST_MOCK_RTP_PACKET_SINK_INTERFACE_H_ + +#include "call/rtp_packet_sink_interface.h" + +#include "test/gmock.h" + +namespace webrtc { + +class MockRtpPacketSink : public RtpPacketSinkInterface { + public: + MOCK_METHOD1(OnRtpPacket, void(const RtpPacketReceived&)); +}; + +} // namespace webrtc + +#endif // CALL_TEST_MOCK_RTP_PACKET_SINK_INTERFACE_H_ diff --git a/third_party/libwebrtc/webrtc/call/video_config.cc b/third_party/libwebrtc/webrtc/call/video_config.cc new file mode 100644 index 0000000000..3a845d5130 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_config.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "call/video_config.h" + +#include <algorithm> +#include <sstream> +#include <string> + +#include "rtc_base/checks.h" + +namespace webrtc { +VideoStream::VideoStream() + : width(0), + height(0), + max_framerate(-1), + min_bitrate_bps(-1), + target_bitrate_bps(-1), + max_bitrate_bps(-1), + max_qp(-1) { + rid[0] = '\0'; +} + +VideoStream::~VideoStream() = default; + +std::string VideoStream::ToString() const { + std::stringstream ss; + ss << "{width: " << width; + ss << ", height: " << height; + ss << ", max_framerate: " << max_framerate; + ss << ", min_bitrate_bps:" << min_bitrate_bps; + ss << ", target_bitrate_bps:" << target_bitrate_bps; + ss << ", max_bitrate_bps:" << max_bitrate_bps; + ss << ", max_qp: " << max_qp; + ss << ", rid: " << rid; + + ss << ", temporal_layer_thresholds_bps: ["; + for (size_t i = 0; i < temporal_layer_thresholds_bps.size(); ++i) { + ss << temporal_layer_thresholds_bps[i]; + if (i != temporal_layer_thresholds_bps.size() - 1) + ss << ", "; + } + ss << ']'; + + ss << '}'; + return ss.str(); +} + +VideoEncoderConfig::VideoEncoderConfig() + : content_type(ContentType::kRealtimeVideo), + encoder_specific_settings(nullptr), + min_transmit_bitrate_bps(0), + max_bitrate_bps(0), + number_of_streams(0) {} + +VideoEncoderConfig::VideoEncoderConfig(VideoEncoderConfig&&) = default; + +VideoEncoderConfig::~VideoEncoderConfig() = default; + +std::string VideoEncoderConfig::ToString() const { + std::stringstream ss; + ss << "{content_type: "; + switch (content_type) { + case ContentType::kRealtimeVideo: + ss << "kRealtimeVideo"; + break; + case ContentType::kScreen: + ss << "kScreenshare"; + break; + } + ss << ", encoder_specific_settings: "; + ss << (encoder_specific_settings != NULL ? "(ptr)" : "NULL"); + + ss << ", min_transmit_bitrate_bps: " << min_transmit_bitrate_bps; + ss << '}'; + return ss.str(); +} + +VideoEncoderConfig::VideoEncoderConfig(const VideoEncoderConfig&) = default; + +void VideoEncoderConfig::EncoderSpecificSettings::FillEncoderSpecificSettings( + VideoCodec* codec) const { + if (codec->codecType == kVideoCodecH264) { + FillVideoCodecH264(codec->H264()); + } else if (codec->codecType == kVideoCodecVP8) { + FillVideoCodecVp8(codec->VP8()); + } else if (codec->codecType == kVideoCodecVP9) { + FillVideoCodecVp9(codec->VP9()); + } else { + RTC_NOTREACHED() << "Encoder specifics set/used for unknown codec type."; + } +} + +void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecH264( + VideoCodecH264* h264_settings) const { + RTC_NOTREACHED(); +} + +void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecVp8( + VideoCodecVP8* vp8_settings) const { + RTC_NOTREACHED(); +} + +void VideoEncoderConfig::EncoderSpecificSettings::FillVideoCodecVp9( + VideoCodecVP9* vp9_settings) const { + RTC_NOTREACHED(); +} + +VideoEncoderConfig::H264EncoderSpecificSettings::H264EncoderSpecificSettings( + const VideoCodecH264& specifics) + : specifics_(specifics) {} + +void VideoEncoderConfig::H264EncoderSpecificSettings::FillVideoCodecH264( + VideoCodecH264* h264_settings) const { + *h264_settings = specifics_; +} + +VideoEncoderConfig::Vp8EncoderSpecificSettings::Vp8EncoderSpecificSettings( + const VideoCodecVP8& specifics) + : specifics_(specifics) {} + +void VideoEncoderConfig::Vp8EncoderSpecificSettings::FillVideoCodecVp8( + VideoCodecVP8* vp8_settings) const { + *vp8_settings = specifics_; +} + +VideoEncoderConfig::Vp9EncoderSpecificSettings::Vp9EncoderSpecificSettings( + const VideoCodecVP9& specifics) + : specifics_(specifics) {} + +void VideoEncoderConfig::Vp9EncoderSpecificSettings::FillVideoCodecVp9( + VideoCodecVP9* vp9_settings) const { + *vp9_settings = specifics_; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/video_config.h b/third_party/libwebrtc/webrtc/call/video_config.h new file mode 100644 index 0000000000..f9f4490922 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_config.h @@ -0,0 +1,171 @@ +/* + * 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 CALL_VIDEO_CONFIG_H_ +#define CALL_VIDEO_CONFIG_H_ + +#include <string> +#include <vector> + +#include "api/optional.h" +#include "common_types.h" // NOLINT(build/include) +#include "rtc_base/basictypes.h" +#include "rtc_base/refcount.h" +#include "rtc_base/scoped_ref_ptr.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { + +struct VideoStream { + VideoStream(); + ~VideoStream(); + std::string ToString() const; + + size_t width; + size_t height; + int max_framerate; + + int min_bitrate_bps; + int target_bitrate_bps; + int max_bitrate_bps; + + int max_qp; + + char rid[kRIDSize+1]; + + const std::string Rid() const { + return std::string(rid); + } + + void SetRid(const std::string & aRid) { + static_assert(sizeof(rid) > kRIDSize, + "mRid must be large enought to hold a RID + null termination"); + auto len = std::min((size_t)kRIDSize-1, aRid.length()); + strncpy(&rid[0], aRid.c_str(), len); + rid[len] = 0; + } + + // Bitrate thresholds for enabling additional temporal layers. Since these are + // thresholds in between layers, we have one additional layer. One threshold + // gives two temporal layers, one below the threshold and one above, two give + // three, and so on. + // The VideoEncoder may redistribute bitrates over the temporal layers so a + // bitrate threshold of 100k and an estimate of 105k does not imply that we + // get 100k in one temporal layer and 5k in the other, just that the bitrate + // in the first temporal layer should not exceed 100k. + // TODO(kthelgason): Apart from a special case for two-layer screencast these + // thresholds are not propagated to the VideoEncoder. To be implemented. + std::vector<int> temporal_layer_thresholds_bps; +}; + +class VideoEncoderConfig { + public: + // These are reference counted to permit copying VideoEncoderConfig and be + // kept alive until all encoder_specific_settings go out of scope. + // TODO(kthelgason): Consider removing the need for copying VideoEncoderConfig + // and use rtc::Optional for encoder_specific_settings instead. + class EncoderSpecificSettings : public rtc::RefCountInterface { + public: + // TODO(pbos): Remove FillEncoderSpecificSettings as soon as VideoCodec is + // not in use and encoder implementations ask for codec-specific structs + // directly. + void FillEncoderSpecificSettings(VideoCodec* codec_struct) const; + + virtual void FillVideoCodecVp8(VideoCodecVP8* vp8_settings) const; + virtual void FillVideoCodecVp9(VideoCodecVP9* vp9_settings) const; + virtual void FillVideoCodecH264(VideoCodecH264* h264_settings) const; + + private: + ~EncoderSpecificSettings() override {} + friend class VideoEncoderConfig; + }; + + class H264EncoderSpecificSettings : public EncoderSpecificSettings { + public: + explicit H264EncoderSpecificSettings(const VideoCodecH264& specifics); + void FillVideoCodecH264(VideoCodecH264* h264_settings) const override; + + private: + VideoCodecH264 specifics_; + }; + + class Vp8EncoderSpecificSettings : public EncoderSpecificSettings { + public: + explicit Vp8EncoderSpecificSettings(const VideoCodecVP8& specifics); + void FillVideoCodecVp8(VideoCodecVP8* vp8_settings) const override; + + private: + VideoCodecVP8 specifics_; + }; + + class Vp9EncoderSpecificSettings : public EncoderSpecificSettings { + public: + explicit Vp9EncoderSpecificSettings(const VideoCodecVP9& specifics); + void FillVideoCodecVp9(VideoCodecVP9* vp9_settings) const override; + + private: + VideoCodecVP9 specifics_; + }; + + enum class ContentType { + kRealtimeVideo, + kScreen, + }; + + class VideoStreamFactoryInterface : public rtc::RefCountInterface { + public: + // An implementation should return a std::vector<VideoStream> with the + // wanted VideoStream settings for the given video resolution. + // The size of the vector may not be larger than + // |encoder_config.number_of_streams|. + virtual std::vector<VideoStream> CreateEncoderStreams( + int width, + int height, + const VideoEncoderConfig& encoder_config) = 0; + + protected: + ~VideoStreamFactoryInterface() override {} + }; + + VideoEncoderConfig& operator=(VideoEncoderConfig&&) = default; + VideoEncoderConfig& operator=(const VideoEncoderConfig&) = delete; + + // Mostly used by tests. Avoid creating copies if you can. + VideoEncoderConfig Copy() const { return VideoEncoderConfig(*this); } + + VideoEncoderConfig(); + VideoEncoderConfig(VideoEncoderConfig&&); + ~VideoEncoderConfig(); + std::string ToString() const; + + rtc::scoped_refptr<VideoStreamFactoryInterface> video_stream_factory; + std::vector<SpatialLayer> spatial_layers; + ContentType content_type; + rtc::scoped_refptr<const EncoderSpecificSettings> encoder_specific_settings; + + // Padding will be used up to this bitrate regardless of the bitrate produced + // by the encoder. Padding above what's actually produced by the encoder helps + // maintaining a higher bitrate estimate. Padding will however not be sent + // unless the estimated bandwidth indicates that the link can handle it. + int min_transmit_bitrate_bps; + int max_bitrate_bps; + + // Max number of encoded VideoStreams to produce. + size_t number_of_streams; + + private: + // Access to the copy constructor is private to force use of the Copy() + // method for those exceptional cases where we do use it. + VideoEncoderConfig(const VideoEncoderConfig&); +}; + +} // namespace webrtc + +#endif // CALL_VIDEO_CONFIG_H_ diff --git a/third_party/libwebrtc/webrtc/call/video_receive_stream.cc b/third_party/libwebrtc/webrtc/call/video_receive_stream.cc new file mode 100644 index 0000000000..f338805746 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_receive_stream.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/video_receive_stream.h" + +namespace webrtc { + +VideoReceiveStream::Decoder::Decoder() = default; +VideoReceiveStream::Decoder::Decoder(const Decoder&) = default; +VideoReceiveStream::Decoder::~Decoder() = default; + +std::string VideoReceiveStream::Decoder::ToString() const { + std::stringstream ss; + ss << "{decoder: " << (decoder ? "(VideoDecoder)" : "nullptr"); + ss << ", payload_type: " << payload_type; + ss << ", payload_name: " << payload_name; + ss << ", codec_params: {"; + for (const auto& it : codec_params) + ss << it.first << ": " << it.second; + ss << '}'; + ss << '}'; + + return ss.str(); +} + +VideoReceiveStream::Stats::Stats() = default; +VideoReceiveStream::Stats::~Stats() = default; + +std::string VideoReceiveStream::Stats::ToString(int64_t time_ms) const { + std::stringstream ss; + ss << "VideoReceiveStream stats: " << time_ms << ", {ssrc: " << ssrc << ", "; + ss << "total_bps: " << total_bitrate_bps << ", "; + ss << "width: " << width << ", "; + ss << "height: " << height << ", "; + ss << "key: " << frame_counts.key_frames << ", "; + ss << "delta: " << frame_counts.delta_frames << ", "; + ss << "network_fps: " << network_frame_rate << ", "; + ss << "decode_fps: " << decode_frame_rate << ", "; + ss << "render_fps: " << render_frame_rate << ", "; + ss << "decode_ms: " << decode_ms << ", "; + ss << "max_decode_ms: " << max_decode_ms << ", "; + ss << "cur_delay_ms: " << current_delay_ms << ", "; + ss << "targ_delay_ms: " << target_delay_ms << ", "; + ss << "jb_delay_ms: " << jitter_buffer_ms << ", "; + ss << "min_playout_delay_ms: " << min_playout_delay_ms << ", "; + ss << "discarded: " << discarded_packets << ", "; + ss << "sync_offset_ms: " << sync_offset_ms << ", "; + ss << "cum_loss: " << rtcp_stats.packets_lost << ", "; + ss << "max_ext_seq: " << rtcp_stats.extended_highest_sequence_number << ", "; + ss << "nack: " << rtcp_packet_type_counts.nack_packets << ", "; + ss << "fir: " << rtcp_packet_type_counts.fir_packets << ", "; + ss << "pli: " << rtcp_packet_type_counts.pli_packets; + ss << '}'; + return ss.str(); +} + +VideoReceiveStream::Config::Config(const Config&) = default; +VideoReceiveStream::Config::Config(Config&&) = default; +VideoReceiveStream::Config::Config(Transport* rtcp_send_transport) + : rtcp_send_transport(rtcp_send_transport) {} + +VideoReceiveStream::Config& VideoReceiveStream::Config::operator=(Config&&) = + default; +VideoReceiveStream::Config::Config::~Config() = default; + +std::string VideoReceiveStream::Config::ToString() const { + std::stringstream ss; + ss << "{decoders: ["; + for (size_t i = 0; i < decoders.size(); ++i) { + ss << decoders[i].ToString(); + if (i != decoders.size() - 1) + ss << ", "; + } + ss << ']'; + ss << ", rtp: " << rtp.ToString(); + ss << ", renderer: " << (renderer ? "(renderer)" : "nullptr"); + ss << ", render_delay_ms: " << render_delay_ms; + if (!sync_group.empty()) + ss << ", sync_group: " << sync_group; + ss << ", pre_decode_callback: " + << (pre_decode_callback ? "(EncodedFrameObserver)" : "nullptr"); + ss << ", target_delay_ms: " << target_delay_ms; + ss << '}'; + + return ss.str(); +} + +VideoReceiveStream::Config::Rtp::Rtp() = default; +VideoReceiveStream::Config::Rtp::Rtp(const Rtp&) = default; +VideoReceiveStream::Config::Rtp::~Rtp() = default; + +std::string VideoReceiveStream::Config::Rtp::ToString() const { + std::stringstream ss; + ss << "{remote_ssrc: " << remote_ssrc; + ss << ", local_ssrc: " << local_ssrc; + ss << ", rtcp_mode: " + << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound" + : "RtcpMode::kReducedSize"); + ss << ", rtcp_xr: "; + ss << "{receiver_reference_time_report: " + << (rtcp_xr.receiver_reference_time_report ? "on" : "off"); + ss << '}'; + ss << ", remb: " << (remb ? "on" : "off"); + ss << ", transport_cc: " << (transport_cc ? "on" : "off"); + ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}'; + ss << ", ulpfec_payload_type: " << ulpfec_payload_type; + ss << ", red_type: " << red_payload_type; + ss << ", rtx_ssrc: " << rtx_ssrc; + ss << ", rtx_payload_types: {"; + for (auto& kv : rtx_associated_payload_types) { + ss << kv.first << " (pt) -> " << kv.second << " (apt), "; + } + ss << '}'; + ss << ", extensions: ["; + for (size_t i = 0; i < extensions.size(); ++i) { + ss << extensions[i].ToString(); + if (i != extensions.size() - 1) + ss << ", "; + } + ss << ']'; + ss << '}'; + return ss.str(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/video_receive_stream.h b/third_party/libwebrtc/webrtc/call/video_receive_stream.h new file mode 100644 index 0000000000..c315ecfcce --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_receive_stream.h @@ -0,0 +1,270 @@ +/* + * 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 CALL_VIDEO_RECEIVE_STREAM_H_ +#define CALL_VIDEO_RECEIVE_STREAM_H_ + +#include <limits> +#include <map> +#include <string> +#include <vector> + +#include "api/call/transport.h" +#include "api/rtpparameters.h" +#include "call/rtp_config.h" +#include "common_types.h" // NOLINT(build/include) +#include "common_video/include/frame_callback.h" +#include "media/base/videosinkinterface.h" +#include "rtc_base/platform_file.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" + +namespace webrtc { + +class RtpPacketSinkInterface; +class VideoDecoder; + +class VideoReceiveStream { + public: + // TODO(mflodman) Move all these settings to VideoDecoder and move the + // declaration to common_types.h. + struct Decoder { + Decoder(); + Decoder(const Decoder&); + ~Decoder(); + std::string ToString() const; + + // The actual decoder instance. + VideoDecoder* decoder = nullptr; + + // Received RTP packets with this payload type will be sent to this decoder + // instance. + int payload_type = 0; + + // Name of the decoded payload (such as VP8). Maps back to the depacketizer + // used to unpack incoming packets. + std::string payload_name; + + // This map contains the codec specific parameters from SDP, i.e. the "fmtp" + // parameters. It is the same as cricket::CodecParameterMap used in + // cricket::VideoCodec. + std::map<std::string, std::string> codec_params; + }; + + struct Stats { + Stats(); + ~Stats(); + std::string ToString(int64_t time_ms) const; + + int network_frame_rate = 0; + int decode_frame_rate = 0; + int render_frame_rate = 0; + uint32_t frames_rendered = 0; + + // Decoder stats. + std::string decoder_implementation_name = "unknown"; + FrameCounts frame_counts; + int decode_ms = 0; + int max_decode_ms = 0; + int current_delay_ms = 0; + int target_delay_ms = 0; + int jitter_buffer_ms = 0; + int min_playout_delay_ms = 0; + int render_delay_ms = 10; + int64_t interframe_delay_max_ms = -1; + uint32_t frames_decoded = 0; + rtc::Optional<uint64_t> qp_sum; + + int current_payload_type = -1; + + int total_bitrate_bps = 0; + int discarded_packets = 0; + + int width = 0; + int height = 0; + + VideoContentType content_type = VideoContentType::UNSPECIFIED; + + int sync_offset_ms = std::numeric_limits<int>::max(); + + uint32_t ssrc = 0; + std::string c_name; + StreamDataCounters rtp_stats; + RtcpPacketTypeCounter rtcp_packet_type_counts; + RtcpStatistics rtcp_stats; + + uint32_t rtcp_sender_packets_sent; + uint32_t rtcp_sender_octets_sent; + NtpTime rtcp_sender_ntp_timestamp; + + // Timing frame info: all important timestamps for a full lifetime of a + // single 'timing frame'. + rtc::Optional<webrtc::TimingFrameInfo> timing_frame_info; + }; + + struct Config { + private: + // Access to the copy constructor is private to force use of the Copy() + // method for those exceptional cases where we do use it. + Config(const Config&); + + public: + Config() = delete; + Config(Config&&); + explicit Config(Transport* rtcp_send_transport); + Config& operator=(Config&&); + Config& operator=(const Config&) = delete; + ~Config(); + + // Mostly used by tests. Avoid creating copies if you can. + Config Copy() const { return Config(*this); } + + std::string ToString() const; + + // Decoders for every payload that we can receive. + std::vector<Decoder> decoders; + + // Receive-stream specific RTP settings. + struct Rtp { + Rtp(); + Rtp(const Rtp&); + ~Rtp(); + std::string ToString() const; + + // Synchronization source (stream identifier) to be received. + uint32_t remote_ssrc = 0; + + // Sender SSRC used for sending RTCP (such as receiver reports). + uint32_t local_ssrc = 0; + + // See RtcpMode for description. + RtcpMode rtcp_mode = RtcpMode::kCompound; + + // Extended RTCP settings. + struct RtcpXr { + // True if RTCP Receiver Reference Time Report Block extension + // (RFC 3611) should be enabled. + bool receiver_reference_time_report = false; + } rtcp_xr; + + // TODO(nisse): This remb setting is currently set but never + // applied. REMB logic is now the responsibility of + // PacketRouter, and it will generate REMB feedback if + // OnReceiveBitrateChanged is used, which depends on how the + // estimators belonging to the ReceiveSideCongestionController + // are configured. Decide if this setting should be deleted, and + // if it needs to be replaced by a setting in PacketRouter to + // disable REMB feedback. + + // See draft-alvestrand-rmcat-remb for information. + bool remb = false; + + bool tmmbr = false; + + // See draft-holmer-rmcat-transport-wide-cc-extensions for details. + bool transport_cc = false; + + // TODO(jesup) - there should be a kKeyFrameReqNone + KeyFrameRequestMethod keyframe_method = kKeyFrameReqPliRtcp; + + // See NackConfig for description. + NackConfig nack; + + // Payload types for ULPFEC and RED, respectively. + int ulpfec_payload_type = -1; + int red_payload_type = -1; + + // SSRC for retransmissions. + uint32_t rtx_ssrc = 0; + + // Set if the stream is protected using FlexFEC. + bool protected_by_flexfec = false; + + // Map from rtx payload type -> media payload type. + // For RTX to be enabled, both an SSRC and this mapping are needed. + std::map<int, int> rtx_associated_payload_types; + // TODO(nisse): This is a temporary accessor function to enable + // reversing and renaming of the rtx_payload_types mapping. + void AddRtxBinding(int rtx_payload_type, int media_payload_type) { + rtx_associated_payload_types[rtx_payload_type] = media_payload_type; + } + + // RTP header extensions used for the received stream. + std::vector<RtpExtension> extensions; + } rtp; + + // Transport for outgoing packets (RTCP). + Transport* rtcp_send_transport = nullptr; + + // Must not be 'nullptr' when the stream is started. + rtc::VideoSinkInterface<VideoFrame>* renderer = nullptr; + + // Expected delay needed by the renderer, i.e. the frame will be delivered + // this many milliseconds, if possible, earlier than the ideal render time. + // Only valid if 'renderer' is set. + int render_delay_ms = 10; + + // If set, pass frames on to the renderer as soon as they are + // available. + bool disable_prerenderer_smoothing = false; + + // Identifier for an A/V synchronization group. Empty string to disable. + // TODO(pbos): Synchronize streams in a sync group, not just video streams + // to one of the audio streams. + std::string sync_group; + + // Called for each incoming video frame, i.e. in encoded state. E.g. used + // when + // saving the stream to a file. 'nullptr' disables the callback. + EncodedFrameObserver* pre_decode_callback = nullptr; + + // Target delay in milliseconds. A positive value indicates this stream is + // used for streaming instead of a real-time call. + int target_delay_ms = 0; + + // Called when a RTCP bye or timeout occurs. 'nullptr' disables the + // callback. + RtcpEventObserver* rtcp_event_observer = nullptr; + }; + + // Starts stream activity. + // When a stream is active, it can receive, process and deliver packets. + virtual void Start() = 0; + // Stops stream activity. + // When a stream is stopped, it can't receive, process or deliver packets. + virtual void Stop() = 0; + + // TODO(pbos): Add info on currently-received codec to Stats. + virtual Stats GetStats() const = 0; + + // Takes ownership of the file, is responsible for closing it later. + // Calling this method will close and finalize any current log. + // Giving rtc::kInvalidPlatformFileValue disables logging. + // If a frame to be written would make the log too large the write fails and + // the log is closed and finalized. A |byte_limit| of 0 means no limit. + virtual void EnableEncodedFrameRecording(rtc::PlatformFile file, + size_t byte_limit) = 0; + inline void DisableEncodedFrameRecording() { + EnableEncodedFrameRecording(rtc::kInvalidPlatformFileValue, 0); + } + + // 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; + + protected: + virtual ~VideoReceiveStream() {} +}; + +} // namespace webrtc + +#endif // CALL_VIDEO_RECEIVE_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/video_send_stream.cc b/third_party/libwebrtc/webrtc/call/video_send_stream.cc new file mode 100644 index 0000000000..4f8c059ee4 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_send_stream.cc @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "call/video_send_stream.h" + +namespace webrtc { + +VideoSendStream::StreamStats::StreamStats() = default; +VideoSendStream::StreamStats::~StreamStats() = default; + +std::string VideoSendStream::StreamStats::ToString() const { + std::stringstream ss; + ss << "width: " << width << ", "; + ss << "height: " << height << ", "; + ss << "key: " << frame_counts.key_frames << ", "; + ss << "delta: " << frame_counts.delta_frames << ", "; + ss << "total_bps: " << total_bitrate_bps << ", "; + ss << "retransmit_bps: " << retransmit_bitrate_bps << ", "; + ss << "avg_delay_ms: " << avg_delay_ms << ", "; + ss << "max_delay_ms: " << max_delay_ms << ", "; + ss << "cum_loss: " << rtcp_stats.packets_lost << ", "; + ss << "max_ext_seq: " << rtcp_stats.extended_highest_sequence_number << ", "; + ss << "nack: " << rtcp_packet_type_counts.nack_packets << ", "; + ss << "fir: " << rtcp_packet_type_counts.fir_packets << ", "; + ss << "pli: " << rtcp_packet_type_counts.pli_packets; + return ss.str(); +} + +VideoSendStream::Stats::Stats() = default; +VideoSendStream::Stats::~Stats() = default; + +std::string VideoSendStream::Stats::ToString(int64_t time_ms) const { + std::stringstream ss; + ss << "VideoSendStream stats: " << time_ms << ", {"; + ss << "input_fps: " << input_frame_rate << ", "; + ss << "encode_fps: " << encode_frame_rate << ", "; + ss << "encode_ms: " << avg_encode_time_ms << ", "; + ss << "encode_usage_perc: " << encode_usage_percent << ", "; + ss << "target_bps: " << target_media_bitrate_bps << ", "; + ss << "media_bps: " << media_bitrate_bps << ", "; + ss << "preferred_media_bitrate_bps: " << preferred_media_bitrate_bps << ", "; + ss << "suspended: " << (suspended ? "true" : "false") << ", "; + ss << "bw_adapted: " << (bw_limited_resolution ? "true" : "false"); + ss << '}'; + for (const auto& substream : substreams) { + if (!substream.second.is_rtx && !substream.second.is_flexfec) { + ss << " {ssrc: " << substream.first << ", "; + ss << substream.second.ToString(); + ss << '}'; + } + } + return ss.str(); +} + +VideoSendStream::Config::Config(const Config&) = default; +VideoSendStream::Config::Config(Config&&) = default; +VideoSendStream::Config::Config(Transport* send_transport) + : send_transport(send_transport) {} + +VideoSendStream::Config& VideoSendStream::Config::operator=(Config&&) = default; +VideoSendStream::Config::Config::~Config() = default; + +std::string VideoSendStream::Config::ToString() const { + std::stringstream ss; + ss << "{encoder_settings: " << encoder_settings.ToString(); + ss << ", rtp: " << rtp.ToString(); + ss << ", pre_encode_callback: " + << (pre_encode_callback ? "(VideoSinkInterface)" : "nullptr"); + ss << ", post_encode_callback: " + << (post_encode_callback ? "(EncodedFrameObserver)" : "nullptr"); + ss << ", render_delay_ms: " << render_delay_ms; + ss << ", target_delay_ms: " << target_delay_ms; + ss << ", suspend_below_min_bitrate: " + << (suspend_below_min_bitrate ? "on" : "off"); + ss << '}'; + return ss.str(); +} + +std::string VideoSendStream::Config::EncoderSettings::ToString() const { + std::stringstream ss; + ss << "{payload_name: " << payload_name; + ss << ", payload_type: " << payload_type; + ss << ", encoder: " << (encoder ? "(VideoEncoder)" : "nullptr"); + ss << '}'; + return ss.str(); +} + +VideoSendStream::Config::Rtp::Rtp() = default; +VideoSendStream::Config::Rtp::Rtp(const Rtp&) = default; +VideoSendStream::Config::Rtp::~Rtp() = default; + +VideoSendStream::Config::Rtp::Flexfec::Flexfec() = default; +VideoSendStream::Config::Rtp::Flexfec::Flexfec(const Flexfec&) = default; +VideoSendStream::Config::Rtp::Flexfec::~Flexfec() = default; + +std::string VideoSendStream::Config::Rtp::ToString() const { + std::stringstream ss; + ss << "{ssrcs: ["; + for (size_t i = 0; i < ssrcs.size(); ++i) { + ss << ssrcs[i]; + if (i != ssrcs.size() - 1) + ss << ", "; + } + ss << ']'; + ss << ", rtcp_mode: " + << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound" + : "RtcpMode::kReducedSize"); + ss << ", max_packet_size: " << max_packet_size; + ss << ", extensions: ["; + for (size_t i = 0; i < extensions.size(); ++i) { + ss << extensions[i].ToString(); + if (i != extensions.size() - 1) + ss << ", "; + } + ss << ']'; + + ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}'; + ss << ", ulpfec: " << ulpfec.ToString(); + + ss << ", flexfec: {payload_type: " << flexfec.payload_type; + ss << ", ssrc: " << flexfec.ssrc; + ss << ", protected_media_ssrcs: ["; + for (size_t i = 0; i < flexfec.protected_media_ssrcs.size(); ++i) { + ss << flexfec.protected_media_ssrcs[i]; + if (i != flexfec.protected_media_ssrcs.size() - 1) + ss << ", "; + } + ss << "]}"; + + ss << ", rtx: " << rtx.ToString(); + ss << ", c_name: " << c_name; + ss << '}'; + return ss.str(); +} + +VideoSendStream::Config::Rtp::Rtx::Rtx() = default; +VideoSendStream::Config::Rtp::Rtx::Rtx(const Rtx&) = default; +VideoSendStream::Config::Rtp::Rtx::~Rtx() = default; + +std::string VideoSendStream::Config::Rtp::Rtx::ToString() const { + std::stringstream ss; + ss << "{ssrcs: ["; + for (size_t i = 0; i < ssrcs.size(); ++i) { + ss << ssrcs[i]; + if (i != ssrcs.size() - 1) + ss << ", "; + } + ss << ']'; + + ss << ", payload_type: " << payload_type; + ss << '}'; + return ss.str(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/call/video_send_stream.h b/third_party/libwebrtc/webrtc/call/video_send_stream.h new file mode 100644 index 0000000000..6ceecda75b --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_send_stream.h @@ -0,0 +1,297 @@ +/* + * 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 CALL_VIDEO_SEND_STREAM_H_ +#define CALL_VIDEO_SEND_STREAM_H_ + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "api/call/transport.h" +#include "api/rtpparameters.h" +#include "call/rtp_config.h" +#include "call/video_config.h" +#include "common_types.h" // NOLINT(build/include) +#include "common_video/include/frame_callback.h" +#include "media/base/videosinkinterface.h" +#include "media/base/videosourceinterface.h" +#include "rtc_base/platform_file.h" + +namespace webrtc { + +class VideoEncoder; + +class VideoSendStream { + public: + struct StreamStats { + StreamStats(); + ~StreamStats(); + + std::string ToString() const; + + FrameCounts frame_counts; + bool is_rtx = false; + bool is_flexfec = false; + int width = 0; + int height = 0; + // TODO(holmer): Move bitrate_bps out to the webrtc::Call layer. + int total_bitrate_bps = 0; + int retransmit_bitrate_bps = 0; + int avg_delay_ms = 0; + int max_delay_ms = 0; + StreamDataCounters rtp_stats; + RtcpPacketTypeCounter rtcp_packet_type_counts; + RtcpStatistics rtcp_stats; + }; + + struct Stats { + Stats(); + ~Stats(); + std::string ToString(int64_t time_ms) const; + std::string encoder_implementation_name = "unknown"; + int input_frame_rate = 0; + int encode_frame_rate = 0; + int avg_encode_time_ms = 0; + int encode_usage_percent = 0; + uint32_t frames_encoded = 0; + uint32_t frames_dropped_by_capturer = 0; + uint32_t frames_dropped_by_encoder_queue = 0; + uint32_t frames_dropped_by_rate_limiter = 0; + uint32_t frames_dropped_by_encoder = 0; + rtc::Optional<uint64_t> qp_sum; + // Bitrate the encoder is currently configured to use due to bandwidth + // limitations. + int target_media_bitrate_bps = 0; + // Bitrate the encoder is actually producing. + int media_bitrate_bps = 0; + // Media bitrate this VideoSendStream is configured to prefer if there are + // no bandwidth limitations. + int preferred_media_bitrate_bps = 0; + bool suspended = false; + bool bw_limited_resolution = false; + bool cpu_limited_resolution = false; + bool bw_limited_framerate = false; + bool cpu_limited_framerate = false; + // Total number of times resolution as been requested to be changed due to + // CPU/quality adaptation. + int number_of_cpu_adapt_changes = 0; + int number_of_quality_adapt_changes = 0; + bool has_entered_low_resolution = false; + std::map<uint32_t, StreamStats> substreams; + webrtc::VideoContentType content_type = + webrtc::VideoContentType::UNSPECIFIED; + }; + + struct Config { + public: + Config() = delete; + Config(Config&&); + explicit Config(Transport* send_transport); + + Config& operator=(Config&&); + Config& operator=(const Config&) = delete; + + ~Config(); + + // Mostly used by tests. Avoid creating copies if you can. + Config Copy() const { return Config(*this); } + + std::string ToString() const; + + struct EncoderSettings { + EncoderSettings() = default; + EncoderSettings(std::string payload_name, + int payload_type, + VideoEncoder* encoder) + : payload_name(std::move(payload_name)), + payload_type(payload_type), + encoder(encoder) {} + std::string ToString() const; + + std::string payload_name; + int payload_type = -1; + + // TODO(sophiechang): Delete this field when no one is using internal + // sources anymore. + bool internal_source = false; + + // Allow 100% encoder utilization. Used for HW encoders where CPU isn't + // expected to be the limiting factor, but a chip could be running at + // 30fps (for example) exactly. + bool full_overuse_time = false; + + // Uninitialized VideoEncoder instance to be used for encoding. Will be + // initialized from inside the VideoSendStream. + VideoEncoder* encoder = nullptr; + } encoder_settings; + + static const size_t kDefaultMaxPacketSize = 1500 - 40; // TCP over IPv4. + struct Rtp { + Rtp(); + Rtp(const Rtp&); + ~Rtp(); + std::string ToString() const; + + std::vector<uint32_t> ssrcs; + + // See RtcpMode for description. + RtcpMode rtcp_mode = RtcpMode::kCompound; + + // Max RTP packet size delivered to send transport from VideoEngine. + size_t max_packet_size = kDefaultMaxPacketSize; + + // RTP header extensions to use for this send stream. + std::vector<RtpExtension> extensions; + + // See NackConfig for description. + NackConfig nack; + + // See UlpfecConfig for description. + UlpfecConfig ulpfec; + + struct Flexfec { + Flexfec(); + Flexfec(const Flexfec&); + ~Flexfec(); + // Payload type of FlexFEC. Set to -1 to disable sending FlexFEC. + int payload_type = -1; + + // SSRC of FlexFEC stream. + uint32_t ssrc = 0; + + // Vector containing a single element, corresponding to the SSRC of the + // media stream being protected by this FlexFEC stream. + // The vector MUST have size 1. + // + // TODO(brandtr): Update comment above when we support + // multistream protection. + std::vector<uint32_t> protected_media_ssrcs; + } flexfec; + + // Settings for RTP retransmission payload format, see RFC 4588 for + // details. + struct Rtx { + Rtx(); + Rtx(const Rtx&); + ~Rtx(); + std::string ToString() const; + // SSRCs to use for the RTX streams. + std::vector<uint32_t> ssrcs; + + // Payload type to use for the RTX stream. + int payload_type = -1; + } rtx; + + // RTCP CNAME, see RFC 3550. + std::string c_name; + + std::vector<std::string> rids; + std::string mid; + } rtp; + + // Transport for outgoing packets. + Transport* send_transport = nullptr; + + // Called for each I420 frame before encoding the frame. Can be used for + // effects, snapshots etc. 'nullptr' disables the callback. + rtc::VideoSinkInterface<VideoFrame>* pre_encode_callback = nullptr; + + // Called for each encoded frame, e.g. used for file storage. 'nullptr' + // disables the callback. Also measures timing and passes the time + // spent on encoding. This timing will not fire if encoding takes longer + // than the measuring window, since the sample data will have been dropped. + EncodedFrameObserver* post_encode_callback = nullptr; + + // Expected delay needed by the renderer, i.e. the frame will be delivered + // this many milliseconds, if possible, earlier than expected render time. + // Only valid if |local_renderer| is set. + int render_delay_ms = 0; + + // Target delay in milliseconds. A positive value indicates this stream is + // used for streaming instead of a real-time call. + int target_delay_ms = 0; + + // True if the stream should be suspended when the available bitrate fall + // below the minimum configured bitrate. If this variable is false, the + // stream may send at a rate higher than the estimated available bitrate. + bool suspend_below_min_bitrate = false; + + // Enables periodic bandwidth probing in application-limited region. + bool periodic_alr_bandwidth_probing = false; + + // Track ID as specified during track creation. + std::string track_id; + + private: + // Access to the copy constructor is private to force use of the Copy() + // method for those exceptional cases where we do use it. + Config(const Config&); + }; + + // Starts stream activity. + // When a stream is active, it can receive, process and deliver packets. + virtual void Start() = 0; + // Stops stream activity. + // When a stream is stopped, it can't receive, process or deliver packets. + virtual void Stop() = 0; + + // Based on the spec in + // https://w3c.github.io/webrtc-pc/#idl-def-rtcdegradationpreference. + // These options are enforced on a best-effort basis. For instance, all of + // these options may suffer some frame drops in order to avoid queuing. + // TODO(sprang): Look into possibility of more strictly enforcing the + // maintain-framerate option. + enum class DegradationPreference { + // Don't take any actions based on over-utilization signals. + kDegradationDisabled, + // On over-use, request lower frame rate, possibly causing frame drops. + kMaintainResolution, + // On over-use, request lower resolution, possibly causing down-scaling. + kMaintainFramerate, + // Try to strike a "pleasing" balance between frame rate or resolution. + kBalanced, + }; + + virtual void SetSource( + rtc::VideoSourceInterface<webrtc::VideoFrame>* source, + const DegradationPreference& degradation_preference) = 0; + + // Set which streams to send. Must have at least as many SSRCs as configured + // in the config. Encoder settings are passed on to the encoder instance along + // with the VideoStream settings. + virtual void ReconfigureVideoEncoder(VideoEncoderConfig config) = 0; + + virtual Stats GetStats() = 0; + + // Takes ownership of each file, is responsible for closing them later. + // Calling this method will close and finalize any current logs. + // Some codecs produce multiple streams (VP8 only at present), each of these + // streams will log to a separate file. kMaxSimulcastStreams in common_types.h + // gives the max number of such streams. If there is no file for a stream, or + // the file is rtc::kInvalidPlatformFileValue, frames from that stream will + // not be logged. + // If a frame to be written would make the log too large the write fails and + // the log is closed and finalized. A |byte_limit| of 0 means no limit. + virtual void EnableEncodedFrameRecording( + const std::vector<rtc::PlatformFile>& files, + size_t byte_limit) = 0; + inline void DisableEncodedFrameRecording() { + EnableEncodedFrameRecording(std::vector<rtc::PlatformFile>(), 0); + } + + protected: + virtual ~VideoSendStream() {} +}; + +} // namespace webrtc + +#endif // CALL_VIDEO_SEND_STREAM_H_ diff --git a/third_party/libwebrtc/webrtc/call/video_stream_api_gn/moz.build b/third_party/libwebrtc/webrtc/call/video_stream_api_gn/moz.build new file mode 100644 index 0000000000..fdbe448c7b --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/video_stream_api_gn/moz.build @@ -0,0 +1,229 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/media/libyuv/libyuv/include/", + "/third_party/libwebrtc/webrtc/", + "/third_party/libwebrtc/webrtc/common_video/include/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/call/video_config.cc", + "/third_party/libwebrtc/webrtc/call/video_receive_stream.cc", + "/third_party/libwebrtc/webrtc/call/video_send_stream.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("video_stream_api_gn") |