summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/webrtc/call
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/libwebrtc/webrtc/call
parentInitial commit. (diff)
downloadfirefox-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')
-rw-r--r--third_party/libwebrtc/webrtc/call/BUILD.gn292
-rw-r--r--third_party/libwebrtc/webrtc/call/DEPS27
-rw-r--r--third_party/libwebrtc/webrtc/call/OWNERS8
-rw-r--r--third_party/libwebrtc/webrtc/call/audio_receive_stream.h155
-rw-r--r--third_party/libwebrtc/webrtc/call/audio_send_stream.h160
-rw-r--r--third_party/libwebrtc/webrtc/call/audio_send_stream_call.cc88
-rw-r--r--third_party/libwebrtc/webrtc/call/audio_state.h68
-rw-r--r--third_party/libwebrtc/webrtc/call/bitrate_allocator.cc568
-rw-r--r--third_party/libwebrtc/webrtc/call/bitrate_allocator.h213
-rw-r--r--third_party/libwebrtc/webrtc/call/bitrate_allocator_gn/moz.build225
-rw-r--r--third_party/libwebrtc/webrtc/call/bitrate_allocator_unittest.cc777
-rw-r--r--third_party/libwebrtc/webrtc/call/bitrate_estimator_tests.cc324
-rw-r--r--third_party/libwebrtc/webrtc/call/call.cc1475
-rw-r--r--third_party/libwebrtc/webrtc/call/call.h214
-rw-r--r--third_party/libwebrtc/webrtc/call/call_gn/moz.build228
-rw-r--r--third_party/libwebrtc/webrtc/call/call_interfaces_gn/moz.build226
-rw-r--r--third_party/libwebrtc/webrtc/call/call_perf_tests.cc960
-rw-r--r--third_party/libwebrtc/webrtc/call/call_unittest.cc715
-rw-r--r--third_party/libwebrtc/webrtc/call/callfactory.cc25
-rw-r--r--third_party/libwebrtc/webrtc/call/callfactory.h26
-rw-r--r--third_party/libwebrtc/webrtc/call/callfactoryinterface.h34
-rw-r--r--third_party/libwebrtc/webrtc/call/fake_rtp_transport_controller_send.h68
-rw-r--r--third_party/libwebrtc/webrtc/call/flexfec_receive_stream.h87
-rw-r--r--third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.cc197
-rw-r--r--third_party/libwebrtc/webrtc/call/flexfec_receive_stream_impl.h64
-rw-r--r--third_party/libwebrtc/webrtc/call/flexfec_receive_stream_unittest.cc157
-rw-r--r--third_party/libwebrtc/webrtc/call/rampup_tests.cc657
-rw-r--r--third_party/libwebrtc/webrtc/call/rampup_tests.h160
-rw-r--r--third_party/libwebrtc/webrtc/call/rtcp_demuxer.cc100
-rw-r--r--third_party/libwebrtc/webrtc/call/rtcp_demuxer.h85
-rw-r--r--third_party/libwebrtc/webrtc/call/rtcp_demuxer_unittest.cc504
-rw-r--r--third_party/libwebrtc/webrtc/call/rtcp_packet_sink_interface.h29
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_config.cc38
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_config.h48
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_demuxer.cc383
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_demuxer.h204
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_demuxer_unittest.cc1527
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_interfaces_gn/moz.build217
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_packet_sink_interface.h26
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_receiver_gn/moz.build229
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.cc55
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper.h98
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_rtcp_demuxer_helper_unittest.cc119
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_sender_gn/moz.build225
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.cc65
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller.h72
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_stream_receiver_controller_interface.h47
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.cc57
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_transport_controller_send.h59
-rw-r--r--third_party/libwebrtc/webrtc/call/rtp_transport_controller_send_interface.h73
-rw-r--r--third_party/libwebrtc/webrtc/call/rtx_receive_stream.cc77
-rw-r--r--third_party/libwebrtc/webrtc/call/rtx_receive_stream.h50
-rw-r--r--third_party/libwebrtc/webrtc/call/rtx_receive_stream_unittest.cc148
-rw-r--r--third_party/libwebrtc/webrtc/call/ssrc_binding_observer.h42
-rw-r--r--third_party/libwebrtc/webrtc/call/syncable.cc17
-rw-r--r--third_party/libwebrtc/webrtc/call/syncable.h43
-rw-r--r--third_party/libwebrtc/webrtc/call/test/mock_rtp_packet_sink_interface.h26
-rw-r--r--third_party/libwebrtc/webrtc/call/video_config.cc142
-rw-r--r--third_party/libwebrtc/webrtc/call/video_config.h171
-rw-r--r--third_party/libwebrtc/webrtc/call/video_receive_stream.cc132
-rw-r--r--third_party/libwebrtc/webrtc/call/video_receive_stream.h270
-rw-r--r--third_party/libwebrtc/webrtc/call/video_send_stream.cc162
-rw-r--r--third_party/libwebrtc/webrtc/call/video_send_stream.h297
-rw-r--r--third_party/libwebrtc/webrtc/call/video_stream_api_gn/moz.build229
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(&not_enforced_observer, 30000, 2500000, 0, false, "");
+ EXPECT_EQ(270000, allocator_->GetStartBitrate(&not_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(&not_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")