summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/media/base
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/media/base
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/media/base')
-rw-r--r--third_party/libwebrtc/media/base/adapted_video_track_source.cc126
-rw-r--r--third_party/libwebrtc/media/base/adapted_video_track_source.h104
-rw-r--r--third_party/libwebrtc/media/base/audio_source.h58
-rw-r--r--third_party/libwebrtc/media/base/codec.cc471
-rw-r--r--third_party/libwebrtc/media/base/codec.h240
-rw-r--r--third_party/libwebrtc/media/base/codec_unittest.cc545
-rw-r--r--third_party/libwebrtc/media/base/delayable.h38
-rw-r--r--third_party/libwebrtc/media/base/fake_frame_source.cc88
-rw-r--r--third_party/libwebrtc/media/base/fake_frame_source.h50
-rw-r--r--third_party/libwebrtc/media/base/fake_media_engine.cc607
-rw-r--r--third_party/libwebrtc/media/base/fake_media_engine.h621
-rw-r--r--third_party/libwebrtc/media/base/fake_network_interface.h232
-rw-r--r--third_party/libwebrtc/media/base/fake_rtp.cc71
-rw-r--r--third_party/libwebrtc/media/base/fake_rtp.h301
-rw-r--r--third_party/libwebrtc/media/base/fake_video_renderer.cc87
-rw-r--r--third_party/libwebrtc/media/base/fake_video_renderer.h73
-rw-r--r--third_party/libwebrtc/media/base/media_channel.h992
-rw-r--r--third_party/libwebrtc/media/base/media_channel_impl.cc295
-rw-r--r--third_party/libwebrtc/media/base/media_channel_impl.h683
-rw-r--r--third_party/libwebrtc/media/base/media_config.h93
-rw-r--r--third_party/libwebrtc/media/base/media_constants.cc128
-rw-r--r--third_party/libwebrtc/media/base/media_constants.h149
-rw-r--r--third_party/libwebrtc/media/base/media_engine.cc245
-rw-r--r--third_party/libwebrtc/media/base/media_engine.h213
-rw-r--r--third_party/libwebrtc/media/base/media_engine_unittest.cc58
-rw-r--r--third_party/libwebrtc/media/base/rid_description.cc28
-rw-r--r--third_party/libwebrtc/media/base/rid_description.h93
-rw-r--r--third_party/libwebrtc/media/base/rtp_utils.cc401
-rw-r--r--third_party/libwebrtc/media/base/rtp_utils.h80
-rw-r--r--third_party/libwebrtc/media/base/rtp_utils_unittest.cc303
-rw-r--r--third_party/libwebrtc/media/base/sdp_video_format_utils.cc121
-rw-r--r--third_party/libwebrtc/media/base/sdp_video_format_utils.h52
-rw-r--r--third_party/libwebrtc/media/base/sdp_video_format_utils_unittest.cc115
-rw-r--r--third_party/libwebrtc/media/base/stream_params.cc233
-rw-r--r--third_party/libwebrtc/media/base/stream_params.h314
-rw-r--r--third_party/libwebrtc/media/base/stream_params_unittest.cc301
-rw-r--r--third_party/libwebrtc/media/base/test_utils.cc61
-rw-r--r--third_party/libwebrtc/media/base/test_utils.h70
-rw-r--r--third_party/libwebrtc/media/base/turn_utils.cc126
-rw-r--r--third_party/libwebrtc/media/base/turn_utils.h30
-rw-r--r--third_party/libwebrtc/media/base/turn_utils_unittest.cc127
-rw-r--r--third_party/libwebrtc/media/base/video_adapter.cc468
-rw-r--r--third_party/libwebrtc/media/base/video_adapter.h172
-rw-r--r--third_party/libwebrtc/media/base/video_adapter_unittest.cc1326
-rw-r--r--third_party/libwebrtc/media/base/video_broadcaster.cc214
-rw-r--r--third_party/libwebrtc/media/base/video_broadcaster.h82
-rw-r--r--third_party/libwebrtc/media/base/video_broadcaster_unittest.cc436
-rw-r--r--third_party/libwebrtc/media/base/video_common.cc97
-rw-r--r--third_party/libwebrtc/media/base/video_common.h224
-rw-r--r--third_party/libwebrtc/media/base/video_common_unittest.cc108
-rw-r--r--third_party/libwebrtc/media/base/video_source_base.cc104
-rw-r--r--third_party/libwebrtc/media/base/video_source_base.h83
52 files changed, 12337 insertions, 0 deletions
diff --git a/third_party/libwebrtc/media/base/adapted_video_track_source.cc b/third_party/libwebrtc/media/base/adapted_video_track_source.cc
new file mode 100644
index 0000000000..816ada5f16
--- /dev/null
+++ b/third_party/libwebrtc/media/base/adapted_video_track_source.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/adapted_video_track_source.h"
+
+#include "api/scoped_refptr.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_frame_buffer.h"
+#include "api/video/video_rotation.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/time_utils.h"
+
+namespace rtc {
+
+AdaptedVideoTrackSource::AdaptedVideoTrackSource() = default;
+
+AdaptedVideoTrackSource::AdaptedVideoTrackSource(int required_alignment)
+ : video_adapter_(required_alignment) {}
+
+AdaptedVideoTrackSource::~AdaptedVideoTrackSource() = default;
+
+bool AdaptedVideoTrackSource::GetStats(Stats* stats) {
+ webrtc::MutexLock lock(&stats_mutex_);
+
+ if (!stats_) {
+ return false;
+ }
+
+ *stats = *stats_;
+ return true;
+}
+
+void AdaptedVideoTrackSource::OnFrame(const webrtc::VideoFrame& frame) {
+ rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer(
+ frame.video_frame_buffer());
+ /* Note that this is a "best effort" approach to
+ wants.rotation_applied; apply_rotation_ can change from false to
+ true between the check of apply_rotation() and the call to
+ broadcaster_.OnFrame(), in which case we generate a frame with
+ pending rotation despite some sink with wants.rotation_applied ==
+ true was just added. The VideoBroadcaster enforces
+ synchronization for us in this case, by not passing the frame on
+ to sinks which don't want it. */
+ if (apply_rotation() && frame.rotation() != webrtc::kVideoRotation_0 &&
+ buffer->type() == webrtc::VideoFrameBuffer::Type::kI420) {
+ /* Apply pending rotation. */
+ webrtc::VideoFrame rotated_frame(frame);
+ rotated_frame.set_video_frame_buffer(
+ webrtc::I420Buffer::Rotate(*buffer->GetI420(), frame.rotation()));
+ rotated_frame.set_rotation(webrtc::kVideoRotation_0);
+ broadcaster_.OnFrame(rotated_frame);
+ } else {
+ broadcaster_.OnFrame(frame);
+ }
+}
+
+void AdaptedVideoTrackSource::OnFrameDropped() {
+ broadcaster_.OnDiscardedFrame();
+}
+
+void AdaptedVideoTrackSource::AddOrUpdateSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const rtc::VideoSinkWants& wants) {
+ broadcaster_.AddOrUpdateSink(sink, wants);
+ OnSinkWantsChanged(broadcaster_.wants());
+}
+
+void AdaptedVideoTrackSource::RemoveSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ broadcaster_.RemoveSink(sink);
+ OnSinkWantsChanged(broadcaster_.wants());
+}
+
+bool AdaptedVideoTrackSource::apply_rotation() {
+ return broadcaster_.wants().rotation_applied;
+}
+
+void AdaptedVideoTrackSource::OnSinkWantsChanged(
+ const rtc::VideoSinkWants& wants) {
+ video_adapter_.OnSinkWants(wants);
+}
+
+bool AdaptedVideoTrackSource::AdaptFrame(int width,
+ int height,
+ int64_t time_us,
+ int* out_width,
+ int* out_height,
+ int* crop_width,
+ int* crop_height,
+ int* crop_x,
+ int* crop_y) {
+ {
+ webrtc::MutexLock lock(&stats_mutex_);
+ stats_ = Stats{width, height};
+ }
+
+ if (!broadcaster_.frame_wanted()) {
+ return false;
+ }
+
+ if (!video_adapter_.AdaptFrameResolution(
+ width, height, time_us * rtc::kNumNanosecsPerMicrosec, crop_width,
+ crop_height, out_width, out_height)) {
+ broadcaster_.OnDiscardedFrame();
+ // VideoAdapter dropped the frame.
+ return false;
+ }
+
+ *crop_x = (width - *crop_width) / 2;
+ *crop_y = (height - *crop_height) / 2;
+ return true;
+}
+
+void AdaptedVideoTrackSource::ProcessConstraints(
+ const webrtc::VideoTrackSourceConstraints& constraints) {
+ broadcaster_.ProcessConstraints(constraints);
+}
+
+} // namespace rtc
diff --git a/third_party/libwebrtc/media/base/adapted_video_track_source.h b/third_party/libwebrtc/media/base/adapted_video_track_source.h
new file mode 100644
index 0000000000..1c3e0b68d3
--- /dev/null
+++ b/third_party/libwebrtc/media/base/adapted_video_track_source.h
@@ -0,0 +1,104 @@
+/*
+ * 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 MEDIA_BASE_ADAPTED_VIDEO_TRACK_SOURCE_H_
+#define MEDIA_BASE_ADAPTED_VIDEO_TRACK_SOURCE_H_
+
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+#include "api/media_stream_interface.h"
+#include "api/notifier.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_sink_interface.h"
+#include "api/video/video_source_interface.h"
+#include "media/base/video_adapter.h"
+#include "media/base/video_broadcaster.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace rtc {
+
+// Base class for sources which needs video adaptation, e.g., video
+// capture sources. Sinks must be added and removed on one and only
+// one thread, while AdaptFrame and OnFrame may be called on any
+// thread.
+class RTC_EXPORT AdaptedVideoTrackSource
+ : public webrtc::Notifier<webrtc::VideoTrackSourceInterface> {
+ public:
+ AdaptedVideoTrackSource();
+ ~AdaptedVideoTrackSource() override;
+
+ protected:
+ // Allows derived classes to initialize `video_adapter_` with a custom
+ // alignment.
+ explicit AdaptedVideoTrackSource(int required_alignment);
+ // Checks the apply_rotation() flag. If the frame needs rotation, and it is a
+ // plain memory frame, it is rotated. Subclasses producing native frames must
+ // handle apply_rotation() themselves.
+ void OnFrame(const webrtc::VideoFrame& frame);
+ // Indication from source that a frame was dropped.
+ void OnFrameDropped();
+
+ // Reports the appropriate frame size after adaptation. Returns true
+ // if a frame is wanted. Returns false if there are no interested
+ // sinks, or if the VideoAdapter decides to drop the frame.
+ bool AdaptFrame(int width,
+ int height,
+ int64_t time_us,
+ int* out_width,
+ int* out_height,
+ int* crop_width,
+ int* crop_height,
+ int* crop_x,
+ int* crop_y);
+
+ // Returns the current value of the apply_rotation flag, derived
+ // from the VideoSinkWants of registered sinks. The value is derived
+ // from sinks' wants, in AddOrUpdateSink and RemoveSink. Beware that
+ // when using this method from a different thread, the value may
+ // become stale before it is used.
+ bool apply_rotation();
+
+ cricket::VideoAdapter* video_adapter() { return &video_adapter_; }
+
+ private:
+ // Implements rtc::VideoSourceInterface.
+ void AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const rtc::VideoSinkWants& wants) override;
+ void RemoveSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+
+ // Part of VideoTrackSourceInterface.
+ bool GetStats(Stats* stats) override;
+
+ void OnSinkWantsChanged(const rtc::VideoSinkWants& wants);
+
+ // Encoded sinks not implemented for AdaptedVideoTrackSource.
+ bool SupportsEncodedOutput() const override { return false; }
+ void GenerateKeyFrame() override {}
+ void AddEncodedSink(
+ rtc::VideoSinkInterface<webrtc::RecordableEncodedFrame>* sink) override {}
+ void RemoveEncodedSink(
+ rtc::VideoSinkInterface<webrtc::RecordableEncodedFrame>* sink) override {}
+ void ProcessConstraints(
+ const webrtc::VideoTrackSourceConstraints& constraints) override;
+
+ cricket::VideoAdapter video_adapter_;
+
+ webrtc::Mutex stats_mutex_;
+ absl::optional<Stats> stats_ RTC_GUARDED_BY(stats_mutex_);
+
+ VideoBroadcaster broadcaster_;
+};
+
+} // namespace rtc
+
+#endif // MEDIA_BASE_ADAPTED_VIDEO_TRACK_SOURCE_H_
diff --git a/third_party/libwebrtc/media/base/audio_source.h b/third_party/libwebrtc/media/base/audio_source.h
new file mode 100644
index 0000000000..51fe0e13e1
--- /dev/null
+++ b/third_party/libwebrtc/media/base/audio_source.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_AUDIO_SOURCE_H_
+#define MEDIA_BASE_AUDIO_SOURCE_H_
+
+#include <cstddef>
+
+#include "absl/types/optional.h"
+
+namespace cricket {
+
+// Abstract interface for providing the audio data.
+// TODO(deadbeef): Rename this to AudioSourceInterface, and rename
+// webrtc::AudioSourceInterface to AudioTrackSourceInterface.
+class AudioSource {
+ public:
+ class Sink {
+ public:
+ // Callback to receive data from the AudioSource.
+ virtual void OnData(
+ const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ size_t number_of_channels,
+ size_t number_of_frames,
+ absl::optional<int64_t> absolute_capture_timestamp_ms) = 0;
+
+ // Called when the AudioSource is going away.
+ virtual void OnClose() = 0;
+
+ // Returns the number of channels encoded by the sink. This can be less than
+ // the number_of_channels if down-mixing occur. A value of -1 means an
+ // unknown number.
+ virtual int NumPreferredChannels() const = 0;
+
+ protected:
+ virtual ~Sink() {}
+ };
+
+ // Sets a sink to the AudioSource. There can be only one sink connected
+ // to the source at a time.
+ virtual void SetSink(Sink* sink) = 0;
+
+ protected:
+ virtual ~AudioSource() {}
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_AUDIO_SOURCE_H_
diff --git a/third_party/libwebrtc/media/base/codec.cc b/third_party/libwebrtc/media/base/codec.cc
new file mode 100644
index 0000000000..e43d61cc1b
--- /dev/null
+++ b/third_party/libwebrtc/media/base/codec.cc
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/codec.h"
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/match.h"
+#include "api/video_codecs/av1_profile.h"
+#include "api/video_codecs/h264_profile_level_id.h"
+#include "api/video_codecs/vp9_profile.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace cricket {
+namespace {
+
+std::string GetH264PacketizationModeOrDefault(const CodecParameterMap& params) {
+ auto it = params.find(kH264FmtpPacketizationMode);
+ if (it != params.end()) {
+ return it->second;
+ }
+ // If packetization-mode is not present, default to "0".
+ // https://tools.ietf.org/html/rfc6184#section-6.2
+ return "0";
+}
+
+bool IsSameH264PacketizationMode(const CodecParameterMap& left,
+ const CodecParameterMap& right) {
+ return GetH264PacketizationModeOrDefault(left) ==
+ GetH264PacketizationModeOrDefault(right);
+}
+
+// Some (video) codecs are actually families of codecs and rely on parameters
+// to distinguish different incompatible family members.
+bool IsSameCodecSpecific(const std::string& name1,
+ const CodecParameterMap& params1,
+ const std::string& name2,
+ const CodecParameterMap& params2) {
+ // The names might not necessarily match, so check both.
+ auto either_name_matches = [&](const std::string name) {
+ return absl::EqualsIgnoreCase(name, name1) ||
+ absl::EqualsIgnoreCase(name, name2);
+ };
+ if (either_name_matches(kH264CodecName))
+ return webrtc::H264IsSameProfile(params1, params2) &&
+ IsSameH264PacketizationMode(params1, params2);
+ if (either_name_matches(kVp9CodecName))
+ return webrtc::VP9IsSameProfile(params1, params2);
+ if (either_name_matches(kAv1CodecName))
+ return webrtc::AV1IsSameProfile(params1, params2);
+ return true;
+}
+
+} // namespace
+
+FeedbackParams::FeedbackParams() = default;
+FeedbackParams::~FeedbackParams() = default;
+
+bool FeedbackParam::operator==(const FeedbackParam& other) const {
+ return absl::EqualsIgnoreCase(other.id(), id()) &&
+ absl::EqualsIgnoreCase(other.param(), param());
+}
+
+bool FeedbackParams::operator==(const FeedbackParams& other) const {
+ return params_ == other.params_;
+}
+
+bool FeedbackParams::Has(const FeedbackParam& param) const {
+ return absl::c_linear_search(params_, param);
+}
+
+void FeedbackParams::Add(const FeedbackParam& param) {
+ if (param.id().empty()) {
+ return;
+ }
+ if (Has(param)) {
+ // Param already in `this`.
+ return;
+ }
+ params_.push_back(param);
+ RTC_CHECK(!HasDuplicateEntries());
+}
+
+void FeedbackParams::Intersect(const FeedbackParams& from) {
+ std::vector<FeedbackParam>::iterator iter_to = params_.begin();
+ while (iter_to != params_.end()) {
+ if (!from.Has(*iter_to)) {
+ iter_to = params_.erase(iter_to);
+ } else {
+ ++iter_to;
+ }
+ }
+}
+
+bool FeedbackParams::HasDuplicateEntries() const {
+ for (std::vector<FeedbackParam>::const_iterator iter = params_.begin();
+ iter != params_.end(); ++iter) {
+ for (std::vector<FeedbackParam>::const_iterator found = iter + 1;
+ found != params_.end(); ++found) {
+ if (*found == *iter) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+Codec::Codec(int id, const std::string& name, int clockrate)
+ : id(id), name(name), clockrate(clockrate) {}
+
+Codec::Codec() : id(0), clockrate(0) {}
+
+Codec::Codec(const Codec& c) = default;
+Codec::Codec(Codec&& c) = default;
+Codec::~Codec() = default;
+Codec& Codec::operator=(const Codec& c) = default;
+Codec& Codec::operator=(Codec&& c) = default;
+
+bool Codec::operator==(const Codec& c) const {
+ return this->id == c.id && // id is reserved in objective-c
+ name == c.name && clockrate == c.clockrate && params == c.params &&
+ feedback_params == c.feedback_params;
+}
+
+bool Codec::Matches(const Codec& codec,
+ const webrtc::FieldTrialsView* field_trials) const {
+ // Match the codec id/name based on the typical static/dynamic name rules.
+ // Matching is case-insensitive.
+
+ // Legacy behaviour with killswitch.
+ if (field_trials &&
+ field_trials->IsDisabled("WebRTC-PayloadTypes-Lower-Dynamic-Range")) {
+ const int kMaxStaticPayloadId = 95;
+ return (id <= kMaxStaticPayloadId || codec.id <= kMaxStaticPayloadId)
+ ? (id == codec.id)
+ : (absl::EqualsIgnoreCase(name, codec.name));
+ }
+ // We support the ranges [96, 127] and more recently [35, 65].
+ // https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-1
+ // Within those ranges we match by codec name, outside by codec id.
+ const int kLowerDynamicRangeMin = 35;
+ const int kLowerDynamicRangeMax = 65;
+ const int kUpperDynamicRangeMin = 96;
+ const int kUpperDynamicRangeMax = 127;
+ const bool is_id_in_dynamic_range =
+ (id >= kLowerDynamicRangeMin && id <= kLowerDynamicRangeMax) ||
+ (id >= kUpperDynamicRangeMin && id <= kUpperDynamicRangeMax);
+ const bool is_codec_id_in_dynamic_range =
+ (codec.id >= kLowerDynamicRangeMin &&
+ codec.id <= kLowerDynamicRangeMax) ||
+ (codec.id >= kUpperDynamicRangeMin && codec.id <= kUpperDynamicRangeMax);
+ return is_id_in_dynamic_range && is_codec_id_in_dynamic_range
+ ? (absl::EqualsIgnoreCase(name, codec.name))
+ : (id == codec.id);
+}
+
+bool Codec::MatchesCapability(
+ const webrtc::RtpCodecCapability& codec_capability) const {
+ webrtc::RtpCodecParameters codec_parameters = ToCodecParameters();
+
+ return codec_parameters.name == codec_capability.name &&
+ codec_parameters.kind == codec_capability.kind &&
+ (codec_parameters.name == cricket::kRtxCodecName ||
+ (codec_parameters.num_channels == codec_capability.num_channels &&
+ codec_parameters.clock_rate == codec_capability.clock_rate &&
+ codec_parameters.parameters == codec_capability.parameters));
+}
+
+bool Codec::GetParam(const std::string& name, std::string* out) const {
+ CodecParameterMap::const_iterator iter = params.find(name);
+ if (iter == params.end())
+ return false;
+ *out = iter->second;
+ return true;
+}
+
+bool Codec::GetParam(const std::string& name, int* out) const {
+ CodecParameterMap::const_iterator iter = params.find(name);
+ if (iter == params.end())
+ return false;
+ return rtc::FromString(iter->second, out);
+}
+
+void Codec::SetParam(const std::string& name, const std::string& value) {
+ params[name] = value;
+}
+
+void Codec::SetParam(const std::string& name, int value) {
+ params[name] = rtc::ToString(value);
+}
+
+bool Codec::RemoveParam(const std::string& name) {
+ return params.erase(name) == 1;
+}
+
+void Codec::AddFeedbackParam(const FeedbackParam& param) {
+ feedback_params.Add(param);
+}
+
+bool Codec::HasFeedbackParam(const FeedbackParam& param) const {
+ return feedback_params.Has(param);
+}
+
+void Codec::IntersectFeedbackParams(const Codec& other) {
+ feedback_params.Intersect(other.feedback_params);
+}
+
+webrtc::RtpCodecParameters Codec::ToCodecParameters() const {
+ webrtc::RtpCodecParameters codec_params;
+ codec_params.payload_type = id;
+ codec_params.name = name;
+ codec_params.clock_rate = clockrate;
+ codec_params.parameters.insert(params.begin(), params.end());
+ return codec_params;
+}
+
+AudioCodec::AudioCodec(int id,
+ const std::string& name,
+ int clockrate,
+ int bitrate,
+ size_t channels)
+ : Codec(id, name, clockrate), bitrate(bitrate), channels(channels) {}
+
+AudioCodec::AudioCodec() : Codec(), bitrate(0), channels(0) {}
+
+AudioCodec::AudioCodec(const AudioCodec& c) = default;
+AudioCodec::AudioCodec(AudioCodec&& c) = default;
+AudioCodec& AudioCodec::operator=(const AudioCodec& c) = default;
+AudioCodec& AudioCodec::operator=(AudioCodec&& c) = default;
+
+bool AudioCodec::operator==(const AudioCodec& c) const {
+ return bitrate == c.bitrate && channels == c.channels && Codec::operator==(c);
+}
+
+bool AudioCodec::Matches(const AudioCodec& codec,
+ const webrtc::FieldTrialsView* field_trials) const {
+ // If a nonzero clockrate is specified, it must match the actual clockrate.
+ // If a nonzero bitrate is specified, it must match the actual bitrate,
+ // unless the codec is VBR (0), where we just force the supplied value.
+ // The number of channels must match exactly, with the exception
+ // that channels=0 is treated synonymously as channels=1, per RFC
+ // 4566 section 6: " [The channels] parameter is OPTIONAL and may be
+ // omitted if the number of channels is one."
+ // Preference is ignored.
+ // TODO(juberti): Treat a zero clockrate as 8000Hz, the RTP default clockrate.
+ return Codec::Matches(codec, field_trials) &&
+ ((codec.clockrate == 0 /*&& clockrate == 8000*/) ||
+ clockrate == codec.clockrate) &&
+ (codec.bitrate == 0 || bitrate <= 0 || bitrate == codec.bitrate) &&
+ ((codec.channels < 2 && channels < 2) || channels == codec.channels);
+}
+
+std::string AudioCodec::ToString() const {
+ char buf[256];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << "AudioCodec[" << id << ":" << name << ":" << clockrate << ":" << bitrate
+ << ":" << channels << "]";
+ return sb.str();
+}
+
+webrtc::RtpCodecParameters AudioCodec::ToCodecParameters() const {
+ webrtc::RtpCodecParameters codec_params = Codec::ToCodecParameters();
+ codec_params.num_channels = static_cast<int>(channels);
+ codec_params.kind = MEDIA_TYPE_AUDIO;
+ return codec_params;
+}
+
+std::string VideoCodec::ToString() const {
+ char buf[256];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << "VideoCodec[" << id << ":" << name;
+ if (packetization.has_value()) {
+ sb << ":" << *packetization;
+ }
+ sb << "]";
+ return sb.str();
+}
+
+webrtc::RtpCodecParameters VideoCodec::ToCodecParameters() const {
+ webrtc::RtpCodecParameters codec_params = Codec::ToCodecParameters();
+ codec_params.kind = MEDIA_TYPE_VIDEO;
+ return codec_params;
+}
+
+VideoCodec::VideoCodec(int id, const std::string& name)
+ : Codec(id, name, kVideoCodecClockrate) {
+ SetDefaultParameters();
+}
+
+VideoCodec::VideoCodec(const std::string& name) : VideoCodec(0 /* id */, name) {
+ SetDefaultParameters();
+}
+
+VideoCodec::VideoCodec() : Codec() {
+ clockrate = kVideoCodecClockrate;
+}
+
+VideoCodec::VideoCodec(const webrtc::SdpVideoFormat& c)
+ : Codec(0 /* id */, c.name, kVideoCodecClockrate) {
+ params = c.parameters;
+ scalability_modes = c.scalability_modes;
+}
+
+VideoCodec::VideoCodec(const VideoCodec& c) = default;
+VideoCodec::VideoCodec(VideoCodec&& c) = default;
+VideoCodec& VideoCodec::operator=(const VideoCodec& c) = default;
+VideoCodec& VideoCodec::operator=(VideoCodec&& c) = default;
+
+void VideoCodec::SetDefaultParameters() {
+ if (absl::EqualsIgnoreCase(kH264CodecName, name)) {
+ // This default is set for all H.264 codecs created because
+ // that was the default before packetization mode support was added.
+ // TODO(hta): Move this to the places that create VideoCodecs from
+ // SDP or from knowledge of implementation capabilities.
+ SetParam(kH264FmtpPacketizationMode, "1");
+ }
+}
+
+bool VideoCodec::operator==(const VideoCodec& c) const {
+ return Codec::operator==(c) && packetization == c.packetization;
+}
+
+bool VideoCodec::Matches(const VideoCodec& other,
+ const webrtc::FieldTrialsView* field_trials) const {
+ return Codec::Matches(other, field_trials) &&
+ IsSameCodecSpecific(name, params, other.name, other.params);
+}
+
+absl::optional<std::string> VideoCodec::IntersectPacketization(
+ const VideoCodec& local_codec,
+ const VideoCodec& remote_codec) {
+ if (local_codec.packetization == remote_codec.packetization) {
+ return local_codec.packetization;
+ }
+ return absl::nullopt;
+}
+
+VideoCodec VideoCodec::CreateRtxCodec(int rtx_payload_type,
+ int associated_payload_type) {
+ VideoCodec rtx_codec(rtx_payload_type, kRtxCodecName);
+ rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type);
+ return rtx_codec;
+}
+
+VideoCodec::CodecType VideoCodec::GetCodecType() const {
+ if (absl::EqualsIgnoreCase(name, kRedCodecName)) {
+ return CODEC_RED;
+ }
+ if (absl::EqualsIgnoreCase(name, kUlpfecCodecName)) {
+ return CODEC_ULPFEC;
+ }
+ if (absl::EqualsIgnoreCase(name, kFlexfecCodecName)) {
+ return CODEC_FLEXFEC;
+ }
+ if (absl::EqualsIgnoreCase(name, kRtxCodecName)) {
+ return CODEC_RTX;
+ }
+
+ return CODEC_VIDEO;
+}
+
+bool VideoCodec::ValidateCodecFormat() const {
+ if (id < 0 || id > 127) {
+ RTC_LOG(LS_ERROR) << "Codec with invalid payload type: " << ToString();
+ return false;
+ }
+ if (GetCodecType() != CODEC_VIDEO) {
+ return true;
+ }
+
+ // Video validation from here on.
+ int min_bitrate = -1;
+ int max_bitrate = -1;
+ if (GetParam(kCodecParamMinBitrate, &min_bitrate) &&
+ GetParam(kCodecParamMaxBitrate, &max_bitrate)) {
+ if (max_bitrate < min_bitrate) {
+ RTC_LOG(LS_ERROR) << "Codec with max < min bitrate: " << ToString();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool HasLntf(const Codec& codec) {
+ return codec.HasFeedbackParam(
+ FeedbackParam(kRtcpFbParamLntf, kParamValueEmpty));
+}
+
+bool HasNack(const Codec& codec) {
+ return codec.HasFeedbackParam(
+ FeedbackParam(kRtcpFbParamNack, kParamValueEmpty));
+}
+
+bool HasRemb(const Codec& codec) {
+ return codec.HasFeedbackParam(
+ FeedbackParam(kRtcpFbParamRemb, kParamValueEmpty));
+}
+
+bool HasRrtr(const Codec& codec) {
+ return codec.HasFeedbackParam(
+ FeedbackParam(kRtcpFbParamRrtr, kParamValueEmpty));
+}
+
+bool HasTransportCc(const Codec& codec) {
+ return codec.HasFeedbackParam(
+ FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty));
+}
+
+const VideoCodec* FindMatchingCodec(
+ const std::vector<VideoCodec>& supported_codecs,
+ const VideoCodec& codec) {
+ webrtc::SdpVideoFormat sdp_video_format{codec.name, codec.params};
+ for (const VideoCodec& supported_codec : supported_codecs) {
+ if (sdp_video_format.IsSameCodec(
+ {supported_codec.name, supported_codec.params})) {
+ return &supported_codec;
+ }
+ }
+ return nullptr;
+}
+
+// If a decoder supports any H264 profile, it is implicitly assumed to also
+// support constrained base line even though it's not explicitly listed.
+void AddH264ConstrainedBaselineProfileToSupportedFormats(
+ std::vector<webrtc::SdpVideoFormat>* supported_formats) {
+ std::vector<webrtc::SdpVideoFormat> cbr_supported_formats;
+
+ // For any H264 supported profile, add the corresponding constrained baseline
+ // profile.
+ for (auto it = supported_formats->cbegin(); it != supported_formats->cend();
+ ++it) {
+ if (it->name == cricket::kH264CodecName) {
+ const absl::optional<webrtc::H264ProfileLevelId> profile_level_id =
+ webrtc::ParseSdpForH264ProfileLevelId(it->parameters);
+ if (profile_level_id &&
+ profile_level_id->profile !=
+ webrtc::H264Profile::kProfileConstrainedBaseline) {
+ webrtc::SdpVideoFormat cbp_format = *it;
+ webrtc::H264ProfileLevelId cbp_profile = *profile_level_id;
+ cbp_profile.profile = webrtc::H264Profile::kProfileConstrainedBaseline;
+ cbp_format.parameters[cricket::kH264FmtpProfileLevelId] =
+ *webrtc::H264ProfileLevelIdToString(cbp_profile);
+ cbr_supported_formats.push_back(cbp_format);
+ }
+ }
+ }
+
+ size_t original_size = supported_formats->size();
+ // ...if it's not already in the list.
+ std::copy_if(cbr_supported_formats.begin(), cbr_supported_formats.end(),
+ std::back_inserter(*supported_formats),
+ [supported_formats](const webrtc::SdpVideoFormat& format) {
+ return !format.IsCodecInList(*supported_formats);
+ });
+
+ if (supported_formats->size() > original_size) {
+ RTC_LOG(LS_WARNING) << "Explicitly added H264 constrained baseline to list "
+ "of supported formats.";
+ }
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/codec.h b/third_party/libwebrtc/media/base/codec.h
new file mode 100644
index 0000000000..f0ed25123c
--- /dev/null
+++ b/third_party/libwebrtc/media/base/codec.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_CODEC_H_
+#define MEDIA_BASE_CODEC_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/rtp_parameters.h"
+#include "api/video_codecs/sdp_video_format.h"
+#include "media/base/media_constants.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace cricket {
+
+typedef std::map<std::string, std::string> CodecParameterMap;
+
+class FeedbackParam {
+ public:
+ FeedbackParam() = default;
+ FeedbackParam(absl::string_view id, const std::string& param)
+ : id_(id), param_(param) {}
+ explicit FeedbackParam(absl::string_view id)
+ : id_(id), param_(kParamValueEmpty) {}
+
+ bool operator==(const FeedbackParam& other) const;
+
+ const std::string& id() const { return id_; }
+ const std::string& param() const { return param_; }
+
+ private:
+ std::string id_; // e.g. "nack", "ccm"
+ std::string param_; // e.g. "", "rpsi", "fir"
+};
+
+class FeedbackParams {
+ public:
+ FeedbackParams();
+ ~FeedbackParams();
+ bool operator==(const FeedbackParams& other) const;
+
+ bool Has(const FeedbackParam& param) const;
+ void Add(const FeedbackParam& param);
+
+ void Intersect(const FeedbackParams& from);
+
+ const std::vector<FeedbackParam>& params() const { return params_; }
+
+ private:
+ bool HasDuplicateEntries() const;
+
+ std::vector<FeedbackParam> params_;
+};
+
+struct RTC_EXPORT Codec {
+ int id;
+ std::string name;
+ int clockrate;
+ // Non key-value parameters such as the telephone-event "0‐15" are
+ // represented using an empty string as key, i.e. {"": "0-15"}.
+ CodecParameterMap params;
+ FeedbackParams feedback_params;
+
+ virtual ~Codec();
+
+ // Indicates if this codec is compatible with the specified codec.
+ bool Matches(const Codec& codec,
+ const webrtc::FieldTrialsView* field_trials = nullptr) const;
+ bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const;
+
+ // Find the parameter for `name` and write the value to `out`.
+ bool GetParam(const std::string& name, std::string* out) const;
+ bool GetParam(const std::string& name, int* out) const;
+
+ void SetParam(const std::string& name, const std::string& value);
+ void SetParam(const std::string& name, int value);
+
+ // It is safe to input a non-existent parameter.
+ // Returns true if the parameter existed, false if it did not exist.
+ bool RemoveParam(const std::string& name);
+
+ bool HasFeedbackParam(const FeedbackParam& param) const;
+ void AddFeedbackParam(const FeedbackParam& param);
+
+ // Filter `this` feedbacks params such that only those shared by both `this`
+ // and `other` are kept.
+ void IntersectFeedbackParams(const Codec& other);
+
+ virtual webrtc::RtpCodecParameters ToCodecParameters() const;
+
+ Codec& operator=(const Codec& c);
+ Codec& operator=(Codec&& c);
+
+ bool operator==(const Codec& c) const;
+
+ bool operator!=(const Codec& c) const { return !(*this == c); }
+
+ protected:
+ // A Codec can't be created without a subclass.
+ // Creates a codec with the given parameters.
+ Codec(int id, const std::string& name, int clockrate);
+ // Creates an empty codec.
+ Codec();
+ Codec(const Codec& c);
+ Codec(Codec&& c);
+};
+
+struct AudioCodec : public Codec {
+ int bitrate;
+ size_t channels;
+
+ // Creates a codec with the given parameters.
+ AudioCodec(int id,
+ const std::string& name,
+ int clockrate,
+ int bitrate,
+ size_t channels);
+ // Creates an empty codec.
+ AudioCodec();
+ AudioCodec(const AudioCodec& c);
+ AudioCodec(AudioCodec&& c);
+ ~AudioCodec() override = default;
+
+ // Indicates if this codec is compatible with the specified codec.
+ bool Matches(const AudioCodec& codec,
+ const webrtc::FieldTrialsView* field_trials = nullptr) const;
+
+ std::string ToString() const;
+
+ webrtc::RtpCodecParameters ToCodecParameters() const override;
+
+ AudioCodec& operator=(const AudioCodec& c);
+ AudioCodec& operator=(AudioCodec&& c);
+
+ bool operator==(const AudioCodec& c) const;
+
+ bool operator!=(const AudioCodec& c) const { return !(*this == c); }
+};
+
+struct RTC_EXPORT VideoCodec : public Codec {
+ absl::optional<std::string> packetization;
+ absl::InlinedVector<webrtc::ScalabilityMode, webrtc::kScalabilityModeCount>
+ scalability_modes;
+
+ // Creates a codec with the given parameters.
+ VideoCodec(int id, const std::string& name);
+ // Creates a codec with the given name and empty id.
+ explicit VideoCodec(const std::string& name);
+ // Creates an empty codec.
+ VideoCodec();
+ VideoCodec(const VideoCodec& c);
+ explicit VideoCodec(const webrtc::SdpVideoFormat& c);
+ VideoCodec(VideoCodec&& c);
+ ~VideoCodec() override = default;
+
+ // Indicates if this video codec is the same as the other video codec, e.g. if
+ // they are both VP8 or VP9, or if they are both H264 with the same H264
+ // profile. H264 levels however are not compared.
+ bool Matches(const VideoCodec& codec,
+ const webrtc::FieldTrialsView* field_trials = nullptr) const;
+
+ std::string ToString() const;
+
+ webrtc::RtpCodecParameters ToCodecParameters() const override;
+
+ VideoCodec& operator=(const VideoCodec& c);
+ VideoCodec& operator=(VideoCodec&& c);
+
+ bool operator==(const VideoCodec& c) const;
+
+ bool operator!=(const VideoCodec& c) const { return !(*this == c); }
+
+ // Return packetization which both `local_codec` and `remote_codec` support.
+ static absl::optional<std::string> IntersectPacketization(
+ const VideoCodec& local_codec,
+ const VideoCodec& remote_codec);
+
+ static VideoCodec CreateRtxCodec(int rtx_payload_type,
+ int associated_payload_type);
+
+ enum CodecType {
+ CODEC_VIDEO,
+ CODEC_RED,
+ CODEC_ULPFEC,
+ CODEC_FLEXFEC,
+ CODEC_RTX,
+ };
+
+ CodecType GetCodecType() const;
+ // Validates a VideoCodec's payload type, dimensions and bitrates etc. If they
+ // don't make sense (such as max < min bitrate), and error is logged and
+ // ValidateCodecFormat returns false.
+ bool ValidateCodecFormat() const;
+
+ private:
+ void SetDefaultParameters();
+};
+
+// Get the codec setting associated with `payload_type`. If there
+// is no codec associated with that payload type it returns nullptr.
+template <class Codec>
+const Codec* FindCodecById(const std::vector<Codec>& codecs, int payload_type) {
+ for (const auto& codec : codecs) {
+ if (codec.id == payload_type)
+ return &codec;
+ }
+ return nullptr;
+}
+
+bool HasLntf(const Codec& codec);
+bool HasNack(const Codec& codec);
+bool HasRemb(const Codec& codec);
+bool HasRrtr(const Codec& codec);
+bool HasTransportCc(const Codec& codec);
+// Returns the first codec in `supported_codecs` that matches `codec`, or
+// nullptr if no codec matches.
+const VideoCodec* FindMatchingCodec(
+ const std::vector<VideoCodec>& supported_codecs,
+ const VideoCodec& codec);
+
+RTC_EXPORT void AddH264ConstrainedBaselineProfileToSupportedFormats(
+ std::vector<webrtc::SdpVideoFormat>* supported_formats);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_CODEC_H_
diff --git a/third_party/libwebrtc/media/base/codec_unittest.cc b/third_party/libwebrtc/media/base/codec_unittest.cc
new file mode 100644
index 0000000000..2851c60e08
--- /dev/null
+++ b/third_party/libwebrtc/media/base/codec_unittest.cc
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2009 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/codec.h"
+
+#include <tuple>
+
+#include "api/video_codecs/av1_profile.h"
+#include "api/video_codecs/h264_profile_level_id.h"
+#include "api/video_codecs/vp9_profile.h"
+#include "modules/video_coding/codecs/h264/include/h264.h"
+#include "rtc_base/gunit.h"
+
+using cricket::AudioCodec;
+using cricket::Codec;
+using cricket::FeedbackParam;
+using cricket::kCodecParamAssociatedPayloadType;
+using cricket::kCodecParamMaxBitrate;
+using cricket::kCodecParamMinBitrate;
+using cricket::VideoCodec;
+
+class TestCodec : public Codec {
+ public:
+ TestCodec(int id, const std::string& name, int clockrate)
+ : Codec(id, name, clockrate) {}
+ TestCodec() : Codec() {}
+ TestCodec(const TestCodec& c) = default;
+ TestCodec& operator=(const TestCodec& c) = default;
+};
+
+TEST(CodecTest, TestCodecOperators) {
+ TestCodec c0(96, "D", 1000);
+ c0.SetParam("a", 1);
+
+ TestCodec c1 = c0;
+ EXPECT_TRUE(c1 == c0);
+
+ int param_value0;
+ int param_value1;
+ EXPECT_TRUE(c0.GetParam("a", &param_value0));
+ EXPECT_TRUE(c1.GetParam("a", &param_value1));
+ EXPECT_EQ(param_value0, param_value1);
+
+ c1.id = 86;
+ EXPECT_TRUE(c0 != c1);
+
+ c1 = c0;
+ c1.name = "x";
+ EXPECT_TRUE(c0 != c1);
+
+ c1 = c0;
+ c1.clockrate = 2000;
+ EXPECT_TRUE(c0 != c1);
+
+ c1 = c0;
+ c1.SetParam("a", 2);
+ EXPECT_TRUE(c0 != c1);
+
+ TestCodec c5;
+ TestCodec c6(0, "", 0);
+ EXPECT_TRUE(c5 == c6);
+}
+
+TEST(CodecTest, TestAudioCodecOperators) {
+ AudioCodec c0(96, "A", 44100, 20000, 2);
+ AudioCodec c1(95, "A", 44100, 20000, 2);
+ AudioCodec c2(96, "x", 44100, 20000, 2);
+ AudioCodec c3(96, "A", 48000, 20000, 2);
+ AudioCodec c4(96, "A", 44100, 10000, 2);
+ AudioCodec c5(96, "A", 44100, 20000, 1);
+ EXPECT_NE(c0, c1);
+ EXPECT_NE(c0, c2);
+ EXPECT_NE(c0, c3);
+ EXPECT_NE(c0, c4);
+ EXPECT_NE(c0, c5);
+
+ AudioCodec c7;
+ AudioCodec c8(0, "", 0, 0, 0);
+ AudioCodec c9 = c0;
+ EXPECT_EQ(c8, c7);
+ EXPECT_NE(c9, c7);
+ EXPECT_EQ(c9, c0);
+
+ AudioCodec c10(c0);
+ AudioCodec c11(c0);
+ AudioCodec c12(c0);
+ AudioCodec c13(c0);
+ c10.params["x"] = "abc";
+ c11.params["x"] = "def";
+ c12.params["y"] = "abc";
+ c13.params["x"] = "abc";
+ EXPECT_NE(c10, c0);
+ EXPECT_NE(c11, c0);
+ EXPECT_NE(c11, c10);
+ EXPECT_NE(c12, c0);
+ EXPECT_NE(c12, c10);
+ EXPECT_NE(c12, c11);
+ EXPECT_EQ(c13, c10);
+}
+
+TEST(CodecTest, TestAudioCodecMatches) {
+ // Test a codec with a static payload type.
+ AudioCodec c0(34, "A", 44100, 20000, 1);
+ EXPECT_TRUE(c0.Matches(AudioCodec(34, "", 44100, 20000, 1)));
+ EXPECT_TRUE(c0.Matches(AudioCodec(34, "", 44100, 20000, 0)));
+ EXPECT_TRUE(c0.Matches(AudioCodec(34, "", 44100, 0, 0)));
+ EXPECT_TRUE(c0.Matches(AudioCodec(34, "", 0, 0, 0)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(96, "A", 44100, 20000, 1)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(96, "", 44100, 20000, 1)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(95, "", 55100, 20000, 1)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(95, "", 44100, 30000, 1)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(95, "", 44100, 20000, 2)));
+ EXPECT_FALSE(c0.Matches(AudioCodec(95, "", 55100, 30000, 2)));
+
+ // Test a codec with a dynamic payload type.
+ AudioCodec c1(96, "A", 44100, 20000, 1);
+ EXPECT_TRUE(c1.Matches(AudioCodec(96, "A", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(97, "A", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(96, "a", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(97, "a", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(35, "a", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(42, "a", 0, 0, 0)));
+ EXPECT_TRUE(c1.Matches(AudioCodec(65, "a", 0, 0, 0)));
+ EXPECT_FALSE(c1.Matches(AudioCodec(95, "A", 0, 0, 0)));
+ EXPECT_FALSE(c1.Matches(AudioCodec(34, "A", 0, 0, 0)));
+ EXPECT_FALSE(c1.Matches(AudioCodec(96, "", 44100, 20000, 2)));
+ EXPECT_FALSE(c1.Matches(AudioCodec(96, "A", 55100, 30000, 1)));
+
+ // Test a codec with a dynamic payload type, and auto bitrate.
+ AudioCodec c2(97, "A", 16000, 0, 1);
+ // Use default bitrate.
+ EXPECT_TRUE(c2.Matches(AudioCodec(97, "A", 16000, 0, 1)));
+ EXPECT_TRUE(c2.Matches(AudioCodec(97, "A", 16000, 0, 0)));
+ // Use explicit bitrate.
+ EXPECT_TRUE(c2.Matches(AudioCodec(97, "A", 16000, 32000, 1)));
+ // Backward compatibility with clients that might send "-1" (for default).
+ EXPECT_TRUE(c2.Matches(AudioCodec(97, "A", 16000, -1, 1)));
+
+ // Stereo doesn't match channels = 0.
+ AudioCodec c3(96, "A", 44100, 20000, 2);
+ EXPECT_TRUE(c3.Matches(AudioCodec(96, "A", 44100, 20000, 2)));
+ EXPECT_FALSE(c3.Matches(AudioCodec(96, "A", 44100, 20000, 1)));
+ EXPECT_FALSE(c3.Matches(AudioCodec(96, "A", 44100, 20000, 0)));
+}
+
+TEST(CodecTest, TestVideoCodecOperators) {
+ VideoCodec c0(96, "V");
+ VideoCodec c1(95, "V");
+ VideoCodec c2(96, "x");
+
+ EXPECT_TRUE(c0 != c1);
+ EXPECT_TRUE(c0 != c2);
+
+ VideoCodec c7;
+ VideoCodec c8(0, "");
+ VideoCodec c9 = c0;
+ EXPECT_TRUE(c8 == c7);
+ EXPECT_TRUE(c9 != c7);
+ EXPECT_TRUE(c9 == c0);
+
+ VideoCodec c10(c0);
+ VideoCodec c11(c0);
+ VideoCodec c12(c0);
+ VideoCodec c13(c0);
+ c10.params["x"] = "abc";
+ c11.params["x"] = "def";
+ c12.params["y"] = "abc";
+ c13.params["x"] = "abc";
+ EXPECT_TRUE(c10 != c0);
+ EXPECT_TRUE(c11 != c0);
+ EXPECT_TRUE(c11 != c10);
+ EXPECT_TRUE(c12 != c0);
+ EXPECT_TRUE(c12 != c10);
+ EXPECT_TRUE(c12 != c11);
+ EXPECT_TRUE(c13 == c10);
+}
+
+TEST(CodecTest, TestVideoCodecIntersectPacketization) {
+ VideoCodec c1;
+ c1.packetization = "raw";
+ VideoCodec c2;
+ c2.packetization = "raw";
+ VideoCodec c3;
+
+ EXPECT_EQ(VideoCodec::IntersectPacketization(c1, c2), "raw");
+ EXPECT_EQ(VideoCodec::IntersectPacketization(c1, c3), absl::nullopt);
+}
+
+TEST(CodecTest, TestVideoCodecEqualsWithDifferentPacketization) {
+ VideoCodec c0(100, cricket::kVp8CodecName);
+ VideoCodec c1(100, cricket::kVp8CodecName);
+ VideoCodec c2(100, cricket::kVp8CodecName);
+ c2.packetization = "raw";
+
+ EXPECT_EQ(c0, c1);
+ EXPECT_NE(c0, c2);
+ EXPECT_NE(c2, c0);
+ EXPECT_EQ(c2, c2);
+}
+
+TEST(CodecTest, TestVideoCodecMatches) {
+ // Test a codec with a static payload type.
+ VideoCodec c0(34, "V");
+ EXPECT_TRUE(c0.Matches(VideoCodec(34, "")));
+ EXPECT_FALSE(c0.Matches(VideoCodec(96, "")));
+ EXPECT_FALSE(c0.Matches(VideoCodec(96, "V")));
+
+ // Test a codec with a dynamic payload type.
+ VideoCodec c1(96, "V");
+ EXPECT_TRUE(c1.Matches(VideoCodec(96, "V")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(97, "V")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(96, "v")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(97, "v")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(35, "v")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(42, "v")));
+ EXPECT_TRUE(c1.Matches(VideoCodec(65, "v")));
+ EXPECT_FALSE(c1.Matches(VideoCodec(96, "")));
+ EXPECT_FALSE(c1.Matches(VideoCodec(95, "V")));
+ EXPECT_FALSE(c1.Matches(VideoCodec(34, "V")));
+}
+
+TEST(CodecTest, TestVideoCodecMatchesWithDifferentPacketization) {
+ VideoCodec c0(100, cricket::kVp8CodecName);
+ VideoCodec c1(101, cricket::kVp8CodecName);
+ c1.packetization = "raw";
+
+ EXPECT_TRUE(c0.Matches(c1));
+ EXPECT_TRUE(c1.Matches(c0));
+}
+
+// AV1 codecs compare profile information.
+TEST(CodecTest, TestAV1CodecMatches) {
+ const char kProfile0[] = "0";
+ const char kProfile1[] = "1";
+ const char kProfile2[] = "2";
+
+ VideoCodec c_no_profile(95, cricket::kAv1CodecName);
+ VideoCodec c_profile0(95, cricket::kAv1CodecName);
+ c_profile0.params[webrtc::kAV1FmtpProfile] = kProfile0;
+ VideoCodec c_profile1(95, cricket::kAv1CodecName);
+ c_profile1.params[webrtc::kAV1FmtpProfile] = kProfile1;
+ VideoCodec c_profile2(95, cricket::kAv1CodecName);
+ c_profile2.params[webrtc::kAV1FmtpProfile] = kProfile2;
+
+ // An AV1 entry with no profile specified should be treated as profile-0.
+ EXPECT_TRUE(c_profile0.Matches(c_no_profile));
+
+ {
+ // Two AV1 entries without a profile specified are treated as duplicates.
+ VideoCodec c_no_profile_eq(95, cricket::kAv1CodecName);
+ EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq));
+ }
+
+ {
+ // Two AV1 entries with profile 0 specified are treated as duplicates.
+ VideoCodec c_profile0_eq(95, cricket::kAv1CodecName);
+ c_profile0_eq.params[webrtc::kAV1FmtpProfile] = kProfile0;
+ EXPECT_TRUE(c_profile0.Matches(c_profile0_eq));
+ }
+
+ {
+ // Two AV1 entries with profile 1 specified are treated as duplicates.
+ VideoCodec c_profile1_eq(95, cricket::kAv1CodecName);
+ c_profile1_eq.params[webrtc::kAV1FmtpProfile] = kProfile1;
+ EXPECT_TRUE(c_profile1.Matches(c_profile1_eq));
+ }
+
+ // AV1 entries with different profiles (0 and 1) are seen as distinct.
+ EXPECT_FALSE(c_profile0.Matches(c_profile1));
+ EXPECT_FALSE(c_no_profile.Matches(c_profile1));
+
+ // AV1 entries with different profiles (0 and 2) are seen as distinct.
+ EXPECT_FALSE(c_profile0.Matches(c_profile2));
+ EXPECT_FALSE(c_no_profile.Matches(c_profile2));
+}
+
+// VP9 codecs compare profile information.
+TEST(CodecTest, TestVP9CodecMatches) {
+ const char kProfile0[] = "0";
+ const char kProfile2[] = "2";
+
+ VideoCodec c_no_profile(95, cricket::kVp9CodecName);
+ VideoCodec c_profile0(95, cricket::kVp9CodecName);
+ c_profile0.params[webrtc::kVP9FmtpProfileId] = kProfile0;
+
+ EXPECT_TRUE(c_profile0.Matches(c_no_profile));
+
+ {
+ VideoCodec c_profile0_eq(95, cricket::kVp9CodecName);
+ c_profile0_eq.params[webrtc::kVP9FmtpProfileId] = kProfile0;
+ EXPECT_TRUE(c_profile0.Matches(c_profile0_eq));
+ }
+
+ {
+ VideoCodec c_profile2(95, cricket::kVp9CodecName);
+ c_profile2.params[webrtc::kVP9FmtpProfileId] = kProfile2;
+ EXPECT_FALSE(c_profile0.Matches(c_profile2));
+ EXPECT_FALSE(c_no_profile.Matches(c_profile2));
+ }
+
+ {
+ VideoCodec c_no_profile_eq(95, cricket::kVp9CodecName);
+ EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq));
+ }
+}
+
+// Matching H264 codecs also need to have matching profile-level-id and
+// packetization-mode.
+TEST(CodecTest, TestH264CodecMatches) {
+ const char kProfileLevelId1[] = "42e01f";
+ const char kProfileLevelId2[] = "42a01e";
+
+ VideoCodec pli_1_pm_0(95, "H264");
+ pli_1_pm_0.params[cricket::kH264FmtpProfileLevelId] = kProfileLevelId1;
+ pli_1_pm_0.params[cricket::kH264FmtpPacketizationMode] = "0";
+
+ {
+ VideoCodec pli_1_pm_blank(95, "H264");
+ pli_1_pm_blank.params[cricket::kH264FmtpProfileLevelId] = kProfileLevelId1;
+ pli_1_pm_blank.params.erase(
+ pli_1_pm_blank.params.find(cricket::kH264FmtpPacketizationMode));
+
+ // Matches since if packetization-mode is not specified it defaults to "0".
+ EXPECT_TRUE(pli_1_pm_0.Matches(pli_1_pm_blank));
+ }
+
+ {
+ VideoCodec pli_1_pm_1(95, "H264");
+ pli_1_pm_1.params[cricket::kH264FmtpProfileLevelId] = kProfileLevelId1;
+ pli_1_pm_1.params[cricket::kH264FmtpPacketizationMode] = "1";
+
+ // Does not match since packetization-mode is different.
+ EXPECT_FALSE(pli_1_pm_0.Matches(pli_1_pm_1));
+ }
+
+ {
+ VideoCodec pli_2_pm_0(95, "H264");
+ pli_2_pm_0.params[cricket::kH264FmtpProfileLevelId] = kProfileLevelId2;
+ pli_2_pm_0.params[cricket::kH264FmtpPacketizationMode] = "0";
+
+ // Does not match since profile-level-id is different.
+ EXPECT_FALSE(pli_1_pm_0.Matches(pli_2_pm_0));
+ }
+}
+
+TEST(CodecTest, TestSetParamGetParamAndRemoveParam) {
+ AudioCodec codec;
+ codec.SetParam("a", "1");
+ codec.SetParam("b", "x");
+
+ int int_value = 0;
+ EXPECT_TRUE(codec.GetParam("a", &int_value));
+ EXPECT_EQ(1, int_value);
+ EXPECT_FALSE(codec.GetParam("b", &int_value));
+ EXPECT_FALSE(codec.GetParam("c", &int_value));
+
+ std::string str_value;
+ EXPECT_TRUE(codec.GetParam("a", &str_value));
+ EXPECT_EQ("1", str_value);
+ EXPECT_TRUE(codec.GetParam("b", &str_value));
+ EXPECT_EQ("x", str_value);
+ EXPECT_FALSE(codec.GetParam("c", &str_value));
+ EXPECT_TRUE(codec.RemoveParam("a"));
+ EXPECT_FALSE(codec.RemoveParam("c"));
+}
+
+TEST(CodecTest, TestIntersectFeedbackParams) {
+ const FeedbackParam a1("a", "1");
+ const FeedbackParam b2("b", "2");
+ const FeedbackParam b3("b", "3");
+ const FeedbackParam c3("c", "3");
+ TestCodec c1;
+ c1.AddFeedbackParam(a1); // Only match with c2.
+ c1.AddFeedbackParam(b2); // Same param different values.
+ c1.AddFeedbackParam(c3); // Not in c2.
+ TestCodec c2;
+ c2.AddFeedbackParam(a1);
+ c2.AddFeedbackParam(b3);
+
+ c1.IntersectFeedbackParams(c2);
+ EXPECT_TRUE(c1.HasFeedbackParam(a1));
+ EXPECT_FALSE(c1.HasFeedbackParam(b2));
+ EXPECT_FALSE(c1.HasFeedbackParam(c3));
+}
+
+TEST(CodecTest, TestGetCodecType) {
+ // Codec type comparison should be case insenstive on names.
+ const VideoCodec codec(96, "V");
+ const VideoCodec rtx_codec(96, "rTx");
+ const VideoCodec ulpfec_codec(96, "ulpFeC");
+ const VideoCodec flexfec_codec(96, "FlExFeC-03");
+ const VideoCodec red_codec(96, "ReD");
+ EXPECT_EQ(VideoCodec::CODEC_VIDEO, codec.GetCodecType());
+ EXPECT_EQ(VideoCodec::CODEC_RTX, rtx_codec.GetCodecType());
+ EXPECT_EQ(VideoCodec::CODEC_ULPFEC, ulpfec_codec.GetCodecType());
+ EXPECT_EQ(VideoCodec::CODEC_FLEXFEC, flexfec_codec.GetCodecType());
+ EXPECT_EQ(VideoCodec::CODEC_RED, red_codec.GetCodecType());
+}
+
+TEST(CodecTest, TestCreateRtxCodec) {
+ VideoCodec rtx_codec = VideoCodec::CreateRtxCodec(96, 120);
+ EXPECT_EQ(96, rtx_codec.id);
+ EXPECT_EQ(VideoCodec::CODEC_RTX, rtx_codec.GetCodecType());
+ int associated_payload_type;
+ ASSERT_TRUE(rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
+ &associated_payload_type));
+ EXPECT_EQ(120, associated_payload_type);
+}
+
+TEST(CodecTest, TestValidateCodecFormat) {
+ const VideoCodec codec(96, "V");
+ ASSERT_TRUE(codec.ValidateCodecFormat());
+
+ // Accept 0-127 as payload types.
+ VideoCodec low_payload_type = codec;
+ low_payload_type.id = 0;
+ VideoCodec high_payload_type = codec;
+ high_payload_type.id = 127;
+ ASSERT_TRUE(low_payload_type.ValidateCodecFormat());
+ EXPECT_TRUE(high_payload_type.ValidateCodecFormat());
+
+ // Reject negative payloads.
+ VideoCodec negative_payload_type = codec;
+ negative_payload_type.id = -1;
+ EXPECT_FALSE(negative_payload_type.ValidateCodecFormat());
+
+ // Reject too-high payloads.
+ VideoCodec too_high_payload_type = codec;
+ too_high_payload_type.id = 128;
+ EXPECT_FALSE(too_high_payload_type.ValidateCodecFormat());
+
+ // Reject codecs with min bitrate > max bitrate.
+ VideoCodec incorrect_bitrates = codec;
+ incorrect_bitrates.params[kCodecParamMinBitrate] = "100";
+ incorrect_bitrates.params[kCodecParamMaxBitrate] = "80";
+ EXPECT_FALSE(incorrect_bitrates.ValidateCodecFormat());
+
+ // Accept min bitrate == max bitrate.
+ VideoCodec equal_bitrates = codec;
+ equal_bitrates.params[kCodecParamMinBitrate] = "100";
+ equal_bitrates.params[kCodecParamMaxBitrate] = "100";
+ EXPECT_TRUE(equal_bitrates.ValidateCodecFormat());
+
+ // Accept min bitrate < max bitrate.
+ VideoCodec different_bitrates = codec;
+ different_bitrates.params[kCodecParamMinBitrate] = "99";
+ different_bitrates.params[kCodecParamMaxBitrate] = "100";
+ EXPECT_TRUE(different_bitrates.ValidateCodecFormat());
+}
+
+TEST(CodecTest, TestToCodecParameters) {
+ VideoCodec v(96, "V");
+ v.SetParam("p1", "v1");
+ webrtc::RtpCodecParameters codec_params_1 = v.ToCodecParameters();
+ EXPECT_EQ(96, codec_params_1.payload_type);
+ EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, codec_params_1.kind);
+ EXPECT_EQ("V", codec_params_1.name);
+ EXPECT_EQ(cricket::kVideoCodecClockrate, codec_params_1.clock_rate);
+ EXPECT_EQ(absl::nullopt, codec_params_1.num_channels);
+ ASSERT_EQ(1u, codec_params_1.parameters.size());
+ EXPECT_EQ("p1", codec_params_1.parameters.begin()->first);
+ EXPECT_EQ("v1", codec_params_1.parameters.begin()->second);
+
+ AudioCodec a(97, "A", 44100, 20000, 2);
+ a.SetParam("p1", "a1");
+ webrtc::RtpCodecParameters codec_params_2 = a.ToCodecParameters();
+ EXPECT_EQ(97, codec_params_2.payload_type);
+ EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, codec_params_2.kind);
+ EXPECT_EQ("A", codec_params_2.name);
+ EXPECT_EQ(44100, codec_params_2.clock_rate);
+ EXPECT_EQ(2, codec_params_2.num_channels);
+ ASSERT_EQ(1u, codec_params_2.parameters.size());
+ EXPECT_EQ("p1", codec_params_2.parameters.begin()->first);
+ EXPECT_EQ("a1", codec_params_2.parameters.begin()->second);
+}
+
+TEST(CodecTest, H264CostrainedBaselineIsAddedIfH264IsSupported) {
+ const std::vector<webrtc::SdpVideoFormat> kExplicitlySupportedFormats = {
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileBaseline,
+ webrtc::H264Level::kLevel3_1, "1"),
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileBaseline,
+ webrtc::H264Level::kLevel3_1, "0")};
+
+ std::vector<webrtc::SdpVideoFormat> supported_formats =
+ kExplicitlySupportedFormats;
+ cricket::AddH264ConstrainedBaselineProfileToSupportedFormats(
+ &supported_formats);
+
+ const webrtc::SdpVideoFormat kH264ConstrainedBasedlinePacketization1 =
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline,
+ webrtc::H264Level::kLevel3_1, "1");
+ const webrtc::SdpVideoFormat kH264ConstrainedBasedlinePacketization0 =
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline,
+ webrtc::H264Level::kLevel3_1, "0");
+
+ EXPECT_EQ(supported_formats[0], kExplicitlySupportedFormats[0]);
+ EXPECT_EQ(supported_formats[1], kExplicitlySupportedFormats[1]);
+ EXPECT_EQ(supported_formats[2], kH264ConstrainedBasedlinePacketization1);
+ EXPECT_EQ(supported_formats[3], kH264ConstrainedBasedlinePacketization0);
+}
+
+TEST(CodecTest, H264CostrainedBaselineIsNotAddedIfH264IsUnsupported) {
+ const std::vector<webrtc::SdpVideoFormat> kExplicitlySupportedFormats = {
+ {cricket::kVp9CodecName,
+ {{webrtc::kVP9FmtpProfileId,
+ VP9ProfileToString(webrtc::VP9Profile::kProfile0)}}}};
+
+ std::vector<webrtc::SdpVideoFormat> supported_formats =
+ kExplicitlySupportedFormats;
+ cricket::AddH264ConstrainedBaselineProfileToSupportedFormats(
+ &supported_formats);
+
+ EXPECT_EQ(supported_formats[0], kExplicitlySupportedFormats[0]);
+ EXPECT_EQ(supported_formats.size(), kExplicitlySupportedFormats.size());
+}
+
+TEST(CodecTest, H264CostrainedBaselineNotAddedIfAlreadySpecified) {
+ const std::vector<webrtc::SdpVideoFormat> kExplicitlySupportedFormats = {
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileBaseline,
+ webrtc::H264Level::kLevel3_1, "1"),
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileBaseline,
+ webrtc::H264Level::kLevel3_1, "0"),
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline,
+ webrtc::H264Level::kLevel3_1, "1"),
+ webrtc::CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline,
+ webrtc::H264Level::kLevel3_1, "0")};
+
+ std::vector<webrtc::SdpVideoFormat> supported_formats =
+ kExplicitlySupportedFormats;
+ cricket::AddH264ConstrainedBaselineProfileToSupportedFormats(
+ &supported_formats);
+
+ EXPECT_EQ(supported_formats[0], kExplicitlySupportedFormats[0]);
+ EXPECT_EQ(supported_formats[1], kExplicitlySupportedFormats[1]);
+ EXPECT_EQ(supported_formats[2], kExplicitlySupportedFormats[2]);
+ EXPECT_EQ(supported_formats[3], kExplicitlySupportedFormats[3]);
+ EXPECT_EQ(supported_formats.size(), kExplicitlySupportedFormats.size());
+}
diff --git a/third_party/libwebrtc/media/base/delayable.h b/third_party/libwebrtc/media/base/delayable.h
new file mode 100644
index 0000000000..f0344c5dec
--- /dev/null
+++ b/third_party/libwebrtc/media/base/delayable.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_DELAYABLE_H_
+#define MEDIA_BASE_DELAYABLE_H_
+
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+
+namespace cricket {
+
+// Delayable is used by user code through ApplyConstraints algorithm. Its
+// methods must take precendence over similar functional in `syncable.h`.
+class Delayable {
+ public:
+ virtual ~Delayable() {}
+ // Set base minimum delay of the receive stream with specified ssrc.
+ // Base minimum delay sets lower bound on minimum delay value which
+ // determines minimum delay until audio playout.
+ // Returns false if there is no stream with given ssrc.
+ virtual bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) = 0;
+
+ // Returns current value of base minimum delay in milliseconds.
+ virtual absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const = 0;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_DELAYABLE_H_
diff --git a/third_party/libwebrtc/media/base/fake_frame_source.cc b/third_party/libwebrtc/media/base/fake_frame_source.cc
new file mode 100644
index 0000000000..61bc5857d9
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_frame_source.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/fake_frame_source.h"
+
+#include "api/scoped_refptr.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_frame_buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/time_utils.h"
+
+namespace cricket {
+
+FakeFrameSource::FakeFrameSource(int width,
+ int height,
+ int interval_us,
+ int64_t timestamp_offset_us)
+ : width_(width),
+ height_(height),
+ interval_us_(interval_us),
+ next_timestamp_us_(timestamp_offset_us) {
+ RTC_CHECK_GT(width_, 0);
+ RTC_CHECK_GT(height_, 0);
+ RTC_CHECK_GT(interval_us_, 0);
+ RTC_CHECK_GE(next_timestamp_us_, 0);
+}
+
+FakeFrameSource::FakeFrameSource(int width, int height, int interval_us)
+ : FakeFrameSource(width, height, interval_us, rtc::TimeMicros()) {}
+
+webrtc::VideoRotation FakeFrameSource::GetRotation() const {
+ return rotation_;
+}
+
+void FakeFrameSource::SetRotation(webrtc::VideoRotation rotation) {
+ rotation_ = rotation;
+}
+
+webrtc::VideoFrame FakeFrameSource::GetFrameRotationApplied() {
+ switch (rotation_) {
+ case webrtc::kVideoRotation_0:
+ case webrtc::kVideoRotation_180:
+ return GetFrame(width_, height_, webrtc::kVideoRotation_0, interval_us_);
+ case webrtc::kVideoRotation_90:
+ case webrtc::kVideoRotation_270:
+ return GetFrame(height_, width_, webrtc::kVideoRotation_0, interval_us_);
+ }
+ RTC_DCHECK_NOTREACHED() << "Invalid rotation value: "
+ << static_cast<int>(rotation_);
+ // Without this return, the Windows Visual Studio compiler complains
+ // "not all control paths return a value".
+ return GetFrame();
+}
+
+webrtc::VideoFrame FakeFrameSource::GetFrame() {
+ return GetFrame(width_, height_, rotation_, interval_us_);
+}
+
+webrtc::VideoFrame FakeFrameSource::GetFrame(int width,
+ int height,
+ webrtc::VideoRotation rotation,
+ int interval_us) {
+ RTC_CHECK_GT(width, 0);
+ RTC_CHECK_GT(height, 0);
+ RTC_CHECK_GT(interval_us, 0);
+
+ rtc::scoped_refptr<webrtc::I420Buffer> buffer(
+ webrtc::I420Buffer::Create(width, height));
+
+ buffer->InitializeData();
+ webrtc::VideoFrame frame = webrtc::VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_rotation(rotation)
+ .set_timestamp_us(next_timestamp_us_)
+ .build();
+
+ next_timestamp_us_ += interval_us;
+ return frame;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/fake_frame_source.h b/third_party/libwebrtc/media/base/fake_frame_source.h
new file mode 100644
index 0000000000..4c56204e69
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_frame_source.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_FAKE_FRAME_SOURCE_H_
+#define MEDIA_BASE_FAKE_FRAME_SOURCE_H_
+
+#include "api/video/video_frame.h"
+#include "rtc_base/time_utils.h"
+
+namespace cricket {
+
+class FakeFrameSource {
+ public:
+ FakeFrameSource(int width,
+ int height,
+ int interval_us,
+ int64_t timestamp_offset_us);
+ FakeFrameSource(int width, int height, int interval_us);
+
+ webrtc::VideoRotation GetRotation() const;
+ void SetRotation(webrtc::VideoRotation rotation);
+
+ webrtc::VideoFrame GetFrame();
+ webrtc::VideoFrame GetFrameRotationApplied();
+
+ // Override configuration.
+ webrtc::VideoFrame GetFrame(int width,
+ int height,
+ webrtc::VideoRotation rotation,
+ int interval_us);
+
+ private:
+ const int width_;
+ const int height_;
+ const int interval_us_;
+
+ webrtc::VideoRotation rotation_ = webrtc::kVideoRotation_0;
+ int64_t next_timestamp_us_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_FAKE_FRAME_SOURCE_H_
diff --git a/third_party/libwebrtc/media/base/fake_media_engine.cc b/third_party/libwebrtc/media/base/fake_media_engine.cc
new file mode 100644
index 0000000000..d4d24cda85
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_media_engine.cc
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/fake_media_engine.h"
+
+#include <memory>
+#include <utility>
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/match.h"
+#include "absl/types/optional.h"
+#include "rtc_base/checks.h"
+
+namespace cricket {
+using webrtc::TaskQueueBase;
+
+FakeVoiceMediaChannel::DtmfInfo::DtmfInfo(uint32_t ssrc,
+ int event_code,
+ int duration)
+ : ssrc(ssrc), event_code(event_code), duration(duration) {}
+
+FakeVoiceMediaChannel::VoiceChannelAudioSink::VoiceChannelAudioSink(
+ AudioSource* source)
+ : source_(source) {
+ source_->SetSink(this);
+}
+FakeVoiceMediaChannel::VoiceChannelAudioSink::~VoiceChannelAudioSink() {
+ if (source_) {
+ source_->SetSink(nullptr);
+ }
+}
+void FakeVoiceMediaChannel::VoiceChannelAudioSink::OnData(
+ const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ size_t number_of_channels,
+ size_t number_of_frames,
+ absl::optional<int64_t> absolute_capture_timestamp_ms) {}
+void FakeVoiceMediaChannel::VoiceChannelAudioSink::OnClose() {
+ source_ = nullptr;
+}
+AudioSource* FakeVoiceMediaChannel::VoiceChannelAudioSink::source() const {
+ return source_;
+}
+
+FakeVoiceMediaChannel::FakeVoiceMediaChannel(FakeVoiceEngine* engine,
+ const AudioOptions& options,
+ TaskQueueBase* network_thread)
+ : RtpHelper<VoiceMediaChannel>(network_thread),
+ engine_(engine),
+ max_bps_(-1) {
+ output_scalings_[0] = 1.0; // For default channel.
+ SetOptions(options);
+}
+FakeVoiceMediaChannel::~FakeVoiceMediaChannel() {
+ if (engine_) {
+ engine_->UnregisterChannel(this);
+ }
+}
+const std::vector<AudioCodec>& FakeVoiceMediaChannel::recv_codecs() const {
+ return recv_codecs_;
+}
+const std::vector<AudioCodec>& FakeVoiceMediaChannel::send_codecs() const {
+ return send_codecs_;
+}
+const std::vector<AudioCodec>& FakeVoiceMediaChannel::codecs() const {
+ return send_codecs();
+}
+const std::vector<FakeVoiceMediaChannel::DtmfInfo>&
+FakeVoiceMediaChannel::dtmf_info_queue() const {
+ return dtmf_info_queue_;
+}
+const AudioOptions& FakeVoiceMediaChannel::options() const {
+ return options_;
+}
+int FakeVoiceMediaChannel::max_bps() const {
+ return max_bps_;
+}
+bool FakeVoiceMediaChannel::SetSendParameters(
+ const AudioSendParameters& params) {
+ set_send_rtcp_parameters(params.rtcp);
+ return (SetSendCodecs(params.codecs) &&
+ SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
+ SetSendRtpHeaderExtensions(params.extensions) &&
+ SetMaxSendBandwidth(params.max_bandwidth_bps) &&
+ SetOptions(params.options));
+}
+bool FakeVoiceMediaChannel::SetRecvParameters(
+ const AudioRecvParameters& params) {
+ set_recv_rtcp_parameters(params.rtcp);
+ return (SetRecvCodecs(params.codecs) &&
+ SetRecvRtpHeaderExtensions(params.extensions));
+}
+void FakeVoiceMediaChannel::SetPlayout(bool playout) {
+ set_playout(playout);
+}
+void FakeVoiceMediaChannel::SetSend(bool send) {
+ set_sending(send);
+}
+bool FakeVoiceMediaChannel::SetAudioSend(uint32_t ssrc,
+ bool enable,
+ const AudioOptions* options,
+ AudioSource* source) {
+ if (!SetLocalSource(ssrc, source)) {
+ return false;
+ }
+ if (!RtpHelper<VoiceMediaChannel>::MuteStream(ssrc, !enable)) {
+ return false;
+ }
+ if (enable && options) {
+ return SetOptions(*options);
+ }
+ return true;
+}
+bool FakeVoiceMediaChannel::HasSource(uint32_t ssrc) const {
+ return local_sinks_.find(ssrc) != local_sinks_.end();
+}
+bool FakeVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
+ if (!RtpHelper<VoiceMediaChannel>::AddRecvStream(sp))
+ return false;
+ output_scalings_[sp.first_ssrc()] = 1.0;
+ output_delays_[sp.first_ssrc()] = 0;
+ return true;
+}
+bool FakeVoiceMediaChannel::RemoveRecvStream(uint32_t ssrc) {
+ if (!RtpHelper<VoiceMediaChannel>::RemoveRecvStream(ssrc))
+ return false;
+ output_scalings_.erase(ssrc);
+ output_delays_.erase(ssrc);
+ return true;
+}
+bool FakeVoiceMediaChannel::CanInsertDtmf() {
+ for (std::vector<AudioCodec>::const_iterator it = send_codecs_.begin();
+ it != send_codecs_.end(); ++it) {
+ // Find the DTMF telephone event "codec".
+ if (absl::EqualsIgnoreCase(it->name, "telephone-event")) {
+ return true;
+ }
+ }
+ return false;
+}
+bool FakeVoiceMediaChannel::InsertDtmf(uint32_t ssrc,
+ int event_code,
+ int duration) {
+ dtmf_info_queue_.push_back(DtmfInfo(ssrc, event_code, duration));
+ return true;
+}
+bool FakeVoiceMediaChannel::SetOutputVolume(uint32_t ssrc, double volume) {
+ if (output_scalings_.find(ssrc) != output_scalings_.end()) {
+ output_scalings_[ssrc] = volume;
+ return true;
+ }
+ return false;
+}
+bool FakeVoiceMediaChannel::SetDefaultOutputVolume(double volume) {
+ for (auto& entry : output_scalings_) {
+ entry.second = volume;
+ }
+ return true;
+}
+bool FakeVoiceMediaChannel::GetOutputVolume(uint32_t ssrc, double* volume) {
+ if (output_scalings_.find(ssrc) == output_scalings_.end())
+ return false;
+ *volume = output_scalings_[ssrc];
+ return true;
+}
+bool FakeVoiceMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
+ int delay_ms) {
+ if (output_delays_.find(ssrc) == output_delays_.end()) {
+ return false;
+ } else {
+ output_delays_[ssrc] = delay_ms;
+ return true;
+ }
+}
+absl::optional<int> FakeVoiceMediaChannel::GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const {
+ const auto it = output_delays_.find(ssrc);
+ if (it != output_delays_.end()) {
+ return it->second;
+ }
+ return absl::nullopt;
+}
+bool FakeVoiceMediaChannel::GetSendStats(VoiceMediaSendInfo* info) {
+ return false;
+}
+bool FakeVoiceMediaChannel::GetReceiveStats(VoiceMediaReceiveInfo* info,
+ bool get_and_clear_legacy_stats) {
+ return false;
+}
+void FakeVoiceMediaChannel::SetRawAudioSink(
+ uint32_t ssrc,
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) {
+ sink_ = std::move(sink);
+}
+void FakeVoiceMediaChannel::SetDefaultRawAudioSink(
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) {
+ sink_ = std::move(sink);
+}
+std::vector<webrtc::RtpSource> FakeVoiceMediaChannel::GetSources(
+ uint32_t ssrc) const {
+ return std::vector<webrtc::RtpSource>();
+}
+bool FakeVoiceMediaChannel::SetRecvCodecs(
+ const std::vector<AudioCodec>& codecs) {
+ if (fail_set_recv_codecs()) {
+ // Fake the failure in SetRecvCodecs.
+ return false;
+ }
+ recv_codecs_ = codecs;
+ return true;
+}
+bool FakeVoiceMediaChannel::SetSendCodecs(
+ const std::vector<AudioCodec>& codecs) {
+ if (fail_set_send_codecs()) {
+ // Fake the failure in SetSendCodecs.
+ return false;
+ }
+ send_codecs_ = codecs;
+ return true;
+}
+bool FakeVoiceMediaChannel::SetMaxSendBandwidth(int bps) {
+ max_bps_ = bps;
+ return true;
+}
+bool FakeVoiceMediaChannel::SetOptions(const AudioOptions& options) {
+ // Does a "merge" of current options and set options.
+ options_.SetAll(options);
+ return true;
+}
+bool FakeVoiceMediaChannel::SetLocalSource(uint32_t ssrc, AudioSource* source) {
+ auto it = local_sinks_.find(ssrc);
+ if (source) {
+ if (it != local_sinks_.end()) {
+ RTC_CHECK(it->second->source() == source);
+ } else {
+ local_sinks_.insert(std::make_pair(
+ ssrc, std::make_unique<VoiceChannelAudioSink>(source)));
+ }
+ } else {
+ if (it != local_sinks_.end()) {
+ local_sinks_.erase(it);
+ }
+ }
+ return true;
+}
+
+bool CompareDtmfInfo(const FakeVoiceMediaChannel::DtmfInfo& info,
+ uint32_t ssrc,
+ int event_code,
+ int duration) {
+ return (info.duration == duration && info.event_code == event_code &&
+ info.ssrc == ssrc);
+}
+
+FakeVideoMediaChannel::FakeVideoMediaChannel(FakeVideoEngine* engine,
+ const VideoOptions& options,
+ TaskQueueBase* network_thread)
+ : RtpHelper<VideoMediaChannel>(network_thread),
+ engine_(engine),
+ max_bps_(-1) {
+ SetOptions(options);
+}
+FakeVideoMediaChannel::~FakeVideoMediaChannel() {
+ if (engine_) {
+ engine_->UnregisterChannel(this);
+ }
+}
+const std::vector<VideoCodec>& FakeVideoMediaChannel::recv_codecs() const {
+ return recv_codecs_;
+}
+const std::vector<VideoCodec>& FakeVideoMediaChannel::send_codecs() const {
+ return send_codecs_;
+}
+const std::vector<VideoCodec>& FakeVideoMediaChannel::codecs() const {
+ return send_codecs();
+}
+bool FakeVideoMediaChannel::rendering() const {
+ return playout();
+}
+const VideoOptions& FakeVideoMediaChannel::options() const {
+ return options_;
+}
+const std::map<uint32_t, rtc::VideoSinkInterface<webrtc::VideoFrame>*>&
+FakeVideoMediaChannel::sinks() const {
+ return sinks_;
+}
+int FakeVideoMediaChannel::max_bps() const {
+ return max_bps_;
+}
+bool FakeVideoMediaChannel::SetSendParameters(
+ const VideoSendParameters& params) {
+ set_send_rtcp_parameters(params.rtcp);
+ return (SetSendCodecs(params.codecs) &&
+ SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
+ SetSendRtpHeaderExtensions(params.extensions) &&
+ SetMaxSendBandwidth(params.max_bandwidth_bps));
+}
+bool FakeVideoMediaChannel::SetRecvParameters(
+ const VideoRecvParameters& params) {
+ set_recv_rtcp_parameters(params.rtcp);
+ return (SetRecvCodecs(params.codecs) &&
+ SetRecvRtpHeaderExtensions(params.extensions));
+}
+bool FakeVideoMediaChannel::AddSendStream(const StreamParams& sp) {
+ return RtpHelper<VideoMediaChannel>::AddSendStream(sp);
+}
+bool FakeVideoMediaChannel::RemoveSendStream(uint32_t ssrc) {
+ return RtpHelper<VideoMediaChannel>::RemoveSendStream(ssrc);
+}
+bool FakeVideoMediaChannel::GetSendCodec(VideoCodec* send_codec) {
+ if (send_codecs_.empty()) {
+ return false;
+ }
+ *send_codec = send_codecs_[0];
+ return true;
+}
+bool FakeVideoMediaChannel::SetSink(
+ uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ auto it = sinks_.find(ssrc);
+ if (it == sinks_.end()) {
+ return false;
+ }
+ it->second = sink;
+ return true;
+}
+void FakeVideoMediaChannel::SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {}
+bool FakeVideoMediaChannel::HasSink(uint32_t ssrc) const {
+ return sinks_.find(ssrc) != sinks_.end() && sinks_.at(ssrc) != nullptr;
+}
+bool FakeVideoMediaChannel::SetSend(bool send) {
+ return set_sending(send);
+}
+bool FakeVideoMediaChannel::SetVideoSend(
+ uint32_t ssrc,
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
+ if (options) {
+ if (!SetOptions(*options)) {
+ return false;
+ }
+ }
+ sources_[ssrc] = source;
+ return true;
+}
+bool FakeVideoMediaChannel::HasSource(uint32_t ssrc) const {
+ return sources_.find(ssrc) != sources_.end() && sources_.at(ssrc) != nullptr;
+}
+bool FakeVideoMediaChannel::AddRecvStream(const StreamParams& sp) {
+ if (!RtpHelper<VideoMediaChannel>::AddRecvStream(sp))
+ return false;
+ sinks_[sp.first_ssrc()] = NULL;
+ output_delays_[sp.first_ssrc()] = 0;
+ return true;
+}
+bool FakeVideoMediaChannel::RemoveRecvStream(uint32_t ssrc) {
+ if (!RtpHelper<VideoMediaChannel>::RemoveRecvStream(ssrc))
+ return false;
+ sinks_.erase(ssrc);
+ output_delays_.erase(ssrc);
+ return true;
+}
+void FakeVideoMediaChannel::FillBitrateInfo(BandwidthEstimationInfo* bwe_info) {
+}
+bool FakeVideoMediaChannel::GetSendStats(VideoMediaSendInfo* info) {
+ return false;
+}
+bool FakeVideoMediaChannel::GetReceiveStats(VideoMediaReceiveInfo* info) {
+ return false;
+}
+std::vector<webrtc::RtpSource> FakeVideoMediaChannel::GetSources(
+ uint32_t ssrc) const {
+ return {};
+}
+bool FakeVideoMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
+ int delay_ms) {
+ if (output_delays_.find(ssrc) == output_delays_.end()) {
+ return false;
+ } else {
+ output_delays_[ssrc] = delay_ms;
+ return true;
+ }
+}
+absl::optional<int> FakeVideoMediaChannel::GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const {
+ const auto it = output_delays_.find(ssrc);
+ if (it != output_delays_.end()) {
+ return it->second;
+ }
+ return absl::nullopt;
+}
+bool FakeVideoMediaChannel::SetRecvCodecs(
+ const std::vector<VideoCodec>& codecs) {
+ if (fail_set_recv_codecs()) {
+ // Fake the failure in SetRecvCodecs.
+ return false;
+ }
+ recv_codecs_ = codecs;
+ return true;
+}
+bool FakeVideoMediaChannel::SetSendCodecs(
+ const std::vector<VideoCodec>& codecs) {
+ if (fail_set_send_codecs()) {
+ // Fake the failure in SetSendCodecs.
+ return false;
+ }
+ send_codecs_ = codecs;
+
+ return true;
+}
+bool FakeVideoMediaChannel::SetOptions(const VideoOptions& options) {
+ options_ = options;
+ return true;
+}
+
+bool FakeVideoMediaChannel::SetMaxSendBandwidth(int bps) {
+ max_bps_ = bps;
+ return true;
+}
+
+void FakeVideoMediaChannel::SetRecordableEncodedFrameCallback(
+ uint32_t ssrc,
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback) {}
+
+void FakeVideoMediaChannel::ClearRecordableEncodedFrameCallback(uint32_t ssrc) {
+}
+
+void FakeVideoMediaChannel::RequestRecvKeyFrame(uint32_t ssrc) {}
+void FakeVideoMediaChannel::GenerateSendKeyFrame(
+ uint32_t ssrc,
+ const std::vector<std::string>& rids) {}
+
+FakeVoiceEngine::FakeVoiceEngine() : fail_create_channel_(false) {
+ // Add a fake audio codec. Note that the name must not be "" as there are
+ // sanity checks against that.
+ SetCodecs({AudioCodec(101, "fake_audio_codec", 0, 0, 1)});
+}
+void FakeVoiceEngine::Init() {}
+rtc::scoped_refptr<webrtc::AudioState> FakeVoiceEngine::GetAudioState() const {
+ return rtc::scoped_refptr<webrtc::AudioState>();
+}
+VoiceMediaChannel* FakeVoiceEngine::CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const AudioOptions& options,
+ const webrtc::CryptoOptions& crypto_options) {
+ if (fail_create_channel_) {
+ return nullptr;
+ }
+
+ FakeVoiceMediaChannel* ch =
+ new FakeVoiceMediaChannel(this, options, call->network_thread());
+ channels_.push_back(ch);
+ return ch;
+}
+FakeVoiceMediaChannel* FakeVoiceEngine::GetChannel(size_t index) {
+ return (channels_.size() > index) ? channels_[index] : NULL;
+}
+void FakeVoiceEngine::UnregisterChannel(VoiceMediaChannel* channel) {
+ channels_.erase(absl::c_find(channels_, channel));
+}
+const std::vector<AudioCodec>& FakeVoiceEngine::send_codecs() const {
+ return send_codecs_;
+}
+const std::vector<AudioCodec>& FakeVoiceEngine::recv_codecs() const {
+ return recv_codecs_;
+}
+void FakeVoiceEngine::SetCodecs(const std::vector<AudioCodec>& codecs) {
+ send_codecs_ = codecs;
+ recv_codecs_ = codecs;
+}
+void FakeVoiceEngine::SetRecvCodecs(const std::vector<AudioCodec>& codecs) {
+ recv_codecs_ = codecs;
+}
+void FakeVoiceEngine::SetSendCodecs(const std::vector<AudioCodec>& codecs) {
+ send_codecs_ = codecs;
+}
+int FakeVoiceEngine::GetInputLevel() {
+ return 0;
+}
+bool FakeVoiceEngine::StartAecDump(webrtc::FileWrapper file,
+ int64_t max_size_bytes) {
+ return false;
+}
+absl::optional<webrtc::AudioDeviceModule::Stats>
+FakeVoiceEngine::GetAudioDeviceStats() {
+ return absl::nullopt;
+}
+void FakeVoiceEngine::StopAecDump() {}
+
+std::vector<webrtc::RtpHeaderExtensionCapability>
+FakeVoiceEngine::GetRtpHeaderExtensions() const {
+ return header_extensions_;
+}
+
+void FakeVoiceEngine::SetRtpHeaderExtensions(
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
+ header_extensions_ = std::move(header_extensions);
+}
+
+FakeVideoEngine::FakeVideoEngine()
+ : capture_(false), fail_create_channel_(false) {
+ // Add a fake video codec. Note that the name must not be "" as there are
+ // sanity checks against that.
+ send_codecs_.push_back(VideoCodec(0, "fake_video_codec"));
+ recv_codecs_.push_back(VideoCodec(0, "fake_video_codec"));
+}
+bool FakeVideoEngine::SetOptions(const VideoOptions& options) {
+ options_ = options;
+ return true;
+}
+VideoMediaChannel* FakeVideoEngine::CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) {
+ if (fail_create_channel_) {
+ return nullptr;
+ }
+
+ FakeVideoMediaChannel* ch =
+ new FakeVideoMediaChannel(this, options, call->network_thread());
+ channels_.emplace_back(ch);
+ return ch;
+}
+FakeVideoMediaChannel* FakeVideoEngine::GetChannel(size_t index) {
+ return (channels_.size() > index) ? channels_[index] : nullptr;
+}
+void FakeVideoEngine::UnregisterChannel(VideoMediaChannel* channel) {
+ auto it = absl::c_find(channels_, channel);
+ RTC_DCHECK(it != channels_.end());
+ channels_.erase(it);
+}
+std::vector<VideoCodec> FakeVideoEngine::send_codecs(bool use_rtx) const {
+ return send_codecs_;
+}
+
+std::vector<VideoCodec> FakeVideoEngine::recv_codecs(bool use_rtx) const {
+ return recv_codecs_;
+}
+
+void FakeVideoEngine::SetSendCodecs(const std::vector<VideoCodec>& codecs) {
+ send_codecs_ = codecs;
+}
+
+void FakeVideoEngine::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
+ recv_codecs_ = codecs;
+}
+
+bool FakeVideoEngine::SetCapture(bool capture) {
+ capture_ = capture;
+ return true;
+}
+std::vector<webrtc::RtpHeaderExtensionCapability>
+FakeVideoEngine::GetRtpHeaderExtensions() const {
+ return header_extensions_;
+}
+void FakeVideoEngine::SetRtpHeaderExtensions(
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
+ header_extensions_ = std::move(header_extensions);
+}
+
+FakeMediaEngine::FakeMediaEngine()
+ : CompositeMediaEngine(std::make_unique<FakeVoiceEngine>(),
+ std::make_unique<FakeVideoEngine>()),
+ voice_(static_cast<FakeVoiceEngine*>(&voice())),
+ video_(static_cast<FakeVideoEngine*>(&video())) {}
+FakeMediaEngine::~FakeMediaEngine() {}
+void FakeMediaEngine::SetAudioCodecs(const std::vector<AudioCodec>& codecs) {
+ voice_->SetCodecs(codecs);
+}
+void FakeMediaEngine::SetAudioRecvCodecs(
+ const std::vector<AudioCodec>& codecs) {
+ voice_->SetRecvCodecs(codecs);
+}
+void FakeMediaEngine::SetAudioSendCodecs(
+ const std::vector<AudioCodec>& codecs) {
+ voice_->SetSendCodecs(codecs);
+}
+void FakeMediaEngine::SetVideoCodecs(const std::vector<VideoCodec>& codecs) {
+ video_->SetSendCodecs(codecs);
+ video_->SetRecvCodecs(codecs);
+}
+
+FakeVoiceMediaChannel* FakeMediaEngine::GetVoiceChannel(size_t index) {
+ return voice_->GetChannel(index);
+}
+FakeVideoMediaChannel* FakeMediaEngine::GetVideoChannel(size_t index) {
+ return video_->GetChannel(index);
+}
+
+void FakeMediaEngine::set_fail_create_channel(bool fail) {
+ voice_->fail_create_channel_ = fail;
+ video_->fail_create_channel_ = fail;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/fake_media_engine.h b/third_party/libwebrtc/media/base/fake_media_engine.h
new file mode 100644
index 0000000000..082c7f37f9
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_media_engine.h
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_FAKE_MEDIA_ENGINE_H_
+#define MEDIA_BASE_FAKE_MEDIA_ENGINE_H_
+
+#include <atomic>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "api/call/audio_sink.h"
+#include "media/base/audio_source.h"
+#include "media/base/media_engine.h"
+#include "media/base/rtp_utils.h"
+#include "media/base/stream_params.h"
+#include "media/engine/webrtc_video_engine.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/network_route.h"
+#include "rtc_base/thread.h"
+
+using webrtc::RtpExtension;
+
+namespace cricket {
+
+class FakeMediaEngine;
+class FakeVideoEngine;
+class FakeVoiceEngine;
+
+// A common helper class that handles sending and receiving RTP/RTCP packets.
+template <class Base>
+class RtpHelper : public Base {
+ public:
+ explicit RtpHelper(webrtc::TaskQueueBase* network_thread)
+ : Base(network_thread),
+ sending_(false),
+ playout_(false),
+ fail_set_send_codecs_(false),
+ fail_set_recv_codecs_(false),
+ send_ssrc_(0),
+ ready_to_send_(false),
+ transport_overhead_per_packet_(0),
+ num_network_route_changes_(0) {}
+ virtual ~RtpHelper() = default;
+ const std::vector<RtpExtension>& recv_extensions() {
+ return recv_extensions_;
+ }
+ const std::vector<RtpExtension>& send_extensions() {
+ return send_extensions_;
+ }
+ bool sending() const { return sending_; }
+ bool playout() const { return playout_; }
+ const std::list<std::string>& rtp_packets() const { return rtp_packets_; }
+ const std::list<std::string>& rtcp_packets() const { return rtcp_packets_; }
+
+ bool SendRtp(const void* data,
+ size_t len,
+ const rtc::PacketOptions& options) {
+ if (!sending_) {
+ return false;
+ }
+ rtc::CopyOnWriteBuffer packet(reinterpret_cast<const uint8_t*>(data), len,
+ kMaxRtpPacketLen);
+ return Base::SendPacket(&packet, options);
+ }
+ bool SendRtcp(const void* data, size_t len) {
+ rtc::CopyOnWriteBuffer packet(reinterpret_cast<const uint8_t*>(data), len,
+ kMaxRtpPacketLen);
+ return Base::SendRtcp(&packet, rtc::PacketOptions());
+ }
+
+ bool CheckRtp(const void* data, size_t len) {
+ bool success = !rtp_packets_.empty();
+ if (success) {
+ std::string packet = rtp_packets_.front();
+ rtp_packets_.pop_front();
+ success = (packet == std::string(static_cast<const char*>(data), len));
+ }
+ return success;
+ }
+ bool CheckRtcp(const void* data, size_t len) {
+ bool success = !rtcp_packets_.empty();
+ if (success) {
+ std::string packet = rtcp_packets_.front();
+ rtcp_packets_.pop_front();
+ success = (packet == std::string(static_cast<const char*>(data), len));
+ }
+ return success;
+ }
+ bool CheckNoRtp() { return rtp_packets_.empty(); }
+ bool CheckNoRtcp() { return rtcp_packets_.empty(); }
+ void set_fail_set_send_codecs(bool fail) { fail_set_send_codecs_ = fail; }
+ void set_fail_set_recv_codecs(bool fail) { fail_set_recv_codecs_ = fail; }
+ virtual bool AddSendStream(const StreamParams& sp) {
+ if (absl::c_linear_search(send_streams_, sp)) {
+ return false;
+ }
+ send_streams_.push_back(sp);
+ rtp_send_parameters_[sp.first_ssrc()] =
+ CreateRtpParametersWithEncodings(sp);
+ return true;
+ }
+ virtual bool RemoveSendStream(uint32_t ssrc) {
+ auto parameters_iterator = rtp_send_parameters_.find(ssrc);
+ if (parameters_iterator != rtp_send_parameters_.end()) {
+ rtp_send_parameters_.erase(parameters_iterator);
+ }
+ return RemoveStreamBySsrc(&send_streams_, ssrc);
+ }
+ virtual void ResetUnsignaledRecvStream() {}
+ virtual absl::optional<uint32_t> GetUnsignaledSsrc() const {
+ return absl::nullopt;
+ }
+ virtual void OnDemuxerCriteriaUpdatePending() {}
+ virtual void OnDemuxerCriteriaUpdateComplete() {}
+
+ virtual bool AddRecvStream(const StreamParams& sp) {
+ if (absl::c_linear_search(receive_streams_, sp)) {
+ return false;
+ }
+ receive_streams_.push_back(sp);
+ rtp_receive_parameters_[sp.first_ssrc()] =
+ CreateRtpParametersWithEncodings(sp);
+ return true;
+ }
+ virtual bool RemoveRecvStream(uint32_t ssrc) {
+ auto parameters_iterator = rtp_receive_parameters_.find(ssrc);
+ if (parameters_iterator != rtp_receive_parameters_.end()) {
+ rtp_receive_parameters_.erase(parameters_iterator);
+ }
+ return RemoveStreamBySsrc(&receive_streams_, ssrc);
+ }
+
+ virtual webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const {
+ auto parameters_iterator = rtp_send_parameters_.find(ssrc);
+ if (parameters_iterator != rtp_send_parameters_.end()) {
+ return parameters_iterator->second;
+ }
+ return webrtc::RtpParameters();
+ }
+ virtual webrtc::RTCError SetRtpSendParameters(
+ uint32_t ssrc,
+ const webrtc::RtpParameters& parameters,
+ webrtc::SetParametersCallback callback) {
+ auto parameters_iterator = rtp_send_parameters_.find(ssrc);
+ if (parameters_iterator != rtp_send_parameters_.end()) {
+ auto result = CheckRtpParametersInvalidModificationAndValues(
+ parameters_iterator->second, parameters);
+ if (!result.ok()) {
+ return webrtc::InvokeSetParametersCallback(callback, result);
+ }
+
+ parameters_iterator->second = parameters;
+
+ return webrtc::InvokeSetParametersCallback(callback,
+ webrtc::RTCError::OK());
+ }
+ // Replicate the behavior of the real media channel: return false
+ // when setting parameters for unknown SSRCs.
+ return InvokeSetParametersCallback(
+ callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
+ }
+
+ virtual webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const {
+ auto parameters_iterator = rtp_receive_parameters_.find(ssrc);
+ if (parameters_iterator != rtp_receive_parameters_.end()) {
+ return parameters_iterator->second;
+ }
+ return webrtc::RtpParameters();
+ }
+ virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const {
+ return webrtc::RtpParameters();
+ }
+
+ bool IsStreamMuted(uint32_t ssrc) const {
+ bool ret = muted_streams_.find(ssrc) != muted_streams_.end();
+ // If |ssrc = 0| check if the first send stream is muted.
+ if (!ret && ssrc == 0 && !send_streams_.empty()) {
+ return muted_streams_.find(send_streams_[0].first_ssrc()) !=
+ muted_streams_.end();
+ }
+ return ret;
+ }
+ const std::vector<StreamParams>& send_streams() const {
+ return send_streams_;
+ }
+ const std::vector<StreamParams>& recv_streams() const {
+ return receive_streams_;
+ }
+ bool HasRecvStream(uint32_t ssrc) const {
+ return GetStreamBySsrc(receive_streams_, ssrc) != nullptr;
+ }
+ bool HasSendStream(uint32_t ssrc) const {
+ return GetStreamBySsrc(send_streams_, ssrc) != nullptr;
+ }
+ // TODO(perkj): This is to support legacy unit test that only check one
+ // sending stream.
+ uint32_t send_ssrc() const {
+ if (send_streams_.empty())
+ return 0;
+ return send_streams_[0].first_ssrc();
+ }
+
+ // TODO(perkj): This is to support legacy unit test that only check one
+ // sending stream.
+ const std::string rtcp_cname() {
+ if (send_streams_.empty())
+ return "";
+ return send_streams_[0].cname;
+ }
+ const RtcpParameters& send_rtcp_parameters() { return send_rtcp_parameters_; }
+ const RtcpParameters& recv_rtcp_parameters() { return recv_rtcp_parameters_; }
+
+ bool ready_to_send() const { return ready_to_send_; }
+
+ int transport_overhead_per_packet() const {
+ return transport_overhead_per_packet_;
+ }
+
+ rtc::NetworkRoute last_network_route() const { return last_network_route_; }
+ int num_network_route_changes() const { return num_network_route_changes_; }
+ void set_num_network_route_changes(int changes) {
+ num_network_route_changes_ = changes;
+ }
+
+ void OnRtcpPacketReceived(rtc::CopyOnWriteBuffer* packet,
+ int64_t packet_time_us) {
+ rtcp_packets_.push_back(std::string(packet->cdata<char>(), packet->size()));
+ }
+
+ // Stuff that deals with encryptors, transformers and the like
+ void SetFrameEncryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+ frame_encryptor) override {}
+ void SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {}
+
+ void SetFrameDecryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+ frame_decryptor) override {}
+
+ void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {}
+
+ protected:
+ bool MuteStream(uint32_t ssrc, bool mute) {
+ if (!HasSendStream(ssrc) && ssrc != 0) {
+ return false;
+ }
+ if (mute) {
+ muted_streams_.insert(ssrc);
+ } else {
+ muted_streams_.erase(ssrc);
+ }
+ return true;
+ }
+ bool set_sending(bool send) {
+ sending_ = send;
+ return true;
+ }
+ void set_playout(bool playout) { playout_ = playout; }
+ bool SetRecvRtpHeaderExtensions(const std::vector<RtpExtension>& extensions) {
+ recv_extensions_ = extensions;
+ return true;
+ }
+ bool SetSendExtmapAllowMixed(bool extmap_allow_mixed) {
+ if (Base::ExtmapAllowMixed() != extmap_allow_mixed) {
+ Base::SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ return true;
+ }
+ bool SetSendRtpHeaderExtensions(const std::vector<RtpExtension>& extensions) {
+ send_extensions_ = extensions;
+ return true;
+ }
+ void set_send_rtcp_parameters(const RtcpParameters& params) {
+ send_rtcp_parameters_ = params;
+ }
+ void set_recv_rtcp_parameters(const RtcpParameters& params) {
+ recv_rtcp_parameters_ = params;
+ }
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override {
+ rtp_packets_.push_back(
+ std::string(packet.Buffer().cdata<char>(), packet.size()));
+ }
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override {}
+ void OnReadyToSend(bool ready) override { ready_to_send_ = ready; }
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override {
+ last_network_route_ = network_route;
+ ++num_network_route_changes_;
+ transport_overhead_per_packet_ = network_route.packet_overhead;
+ }
+ bool fail_set_send_codecs() const { return fail_set_send_codecs_; }
+ bool fail_set_recv_codecs() const { return fail_set_recv_codecs_; }
+
+ private:
+ // TODO(bugs.webrtc.org/12783): This flag is used from more than one thread.
+ // As a workaround for tsan, it's currently std::atomic but that might not
+ // be the appropriate fix.
+ std::atomic<bool> sending_;
+ bool playout_;
+ std::vector<RtpExtension> recv_extensions_;
+ std::vector<RtpExtension> send_extensions_;
+ std::list<std::string> rtp_packets_;
+ std::list<std::string> rtcp_packets_;
+ std::vector<StreamParams> send_streams_;
+ std::vector<StreamParams> receive_streams_;
+ RtcpParameters send_rtcp_parameters_;
+ RtcpParameters recv_rtcp_parameters_;
+ std::set<uint32_t> muted_streams_;
+ std::map<uint32_t, webrtc::RtpParameters> rtp_send_parameters_;
+ std::map<uint32_t, webrtc::RtpParameters> rtp_receive_parameters_;
+ bool fail_set_send_codecs_;
+ bool fail_set_recv_codecs_;
+ uint32_t send_ssrc_;
+ std::string rtcp_cname_;
+ bool ready_to_send_;
+ int transport_overhead_per_packet_;
+ rtc::NetworkRoute last_network_route_;
+ int num_network_route_changes_;
+};
+
+class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
+ public:
+ struct DtmfInfo {
+ DtmfInfo(uint32_t ssrc, int event_code, int duration);
+ uint32_t ssrc;
+ int event_code;
+ int duration;
+ };
+ FakeVoiceMediaChannel(FakeVoiceEngine* engine,
+ const AudioOptions& options,
+ webrtc::TaskQueueBase* network_thread);
+ ~FakeVoiceMediaChannel();
+ const std::vector<AudioCodec>& recv_codecs() const;
+ const std::vector<AudioCodec>& send_codecs() const;
+ const std::vector<AudioCodec>& codecs() const;
+ const std::vector<DtmfInfo>& dtmf_info_queue() const;
+ const AudioOptions& options() const;
+ int max_bps() const;
+ bool SetSendParameters(const AudioSendParameters& params) override;
+
+ bool SetRecvParameters(const AudioRecvParameters& params) override;
+
+ void SetPlayout(bool playout) override;
+ void SetSend(bool send) override;
+ bool SetAudioSend(uint32_t ssrc,
+ bool enable,
+ const AudioOptions* options,
+ AudioSource* source) override;
+
+ bool HasSource(uint32_t ssrc) const;
+
+ bool AddRecvStream(const StreamParams& sp) override;
+ bool RemoveRecvStream(uint32_t ssrc) override;
+
+ bool CanInsertDtmf() override;
+ bool InsertDtmf(uint32_t ssrc, int event_code, int duration) override;
+
+ bool SetOutputVolume(uint32_t ssrc, double volume) override;
+ bool SetDefaultOutputVolume(double volume) override;
+
+ bool GetOutputVolume(uint32_t ssrc, double* volume);
+
+ bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
+ absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const override;
+
+ bool GetSendStats(VoiceMediaSendInfo* info) override;
+ bool GetReceiveStats(VoiceMediaReceiveInfo* info,
+ bool get_and_clear_legacy_stats) override;
+
+ void SetRawAudioSink(
+ uint32_t ssrc,
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
+ void SetDefaultRawAudioSink(
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
+
+ std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
+
+ private:
+ class VoiceChannelAudioSink : public AudioSource::Sink {
+ public:
+ explicit VoiceChannelAudioSink(AudioSource* source);
+ ~VoiceChannelAudioSink() override;
+ void OnData(const void* audio_data,
+ int bits_per_sample,
+ int sample_rate,
+ size_t number_of_channels,
+ size_t number_of_frames,
+ absl::optional<int64_t> absolute_capture_timestamp_ms) override;
+ void OnClose() override;
+ int NumPreferredChannels() const override { return -1; }
+ AudioSource* source() const;
+
+ private:
+ AudioSource* source_;
+ };
+
+ bool SetRecvCodecs(const std::vector<AudioCodec>& codecs);
+ bool SetSendCodecs(const std::vector<AudioCodec>& codecs);
+ bool SetMaxSendBandwidth(int bps);
+ bool SetOptions(const AudioOptions& options);
+ bool SetLocalSource(uint32_t ssrc, AudioSource* source);
+
+ FakeVoiceEngine* engine_;
+ std::vector<AudioCodec> recv_codecs_;
+ std::vector<AudioCodec> send_codecs_;
+ std::map<uint32_t, double> output_scalings_;
+ std::map<uint32_t, int> output_delays_;
+ std::vector<DtmfInfo> dtmf_info_queue_;
+ AudioOptions options_;
+ std::map<uint32_t, std::unique_ptr<VoiceChannelAudioSink>> local_sinks_;
+ std::unique_ptr<webrtc::AudioSinkInterface> sink_;
+ int max_bps_;
+};
+
+// A helper function to compare the FakeVoiceMediaChannel::DtmfInfo.
+bool CompareDtmfInfo(const FakeVoiceMediaChannel::DtmfInfo& info,
+ uint32_t ssrc,
+ int event_code,
+ int duration);
+
+class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> {
+ public:
+ FakeVideoMediaChannel(FakeVideoEngine* engine,
+ const VideoOptions& options,
+ webrtc::TaskQueueBase* network_thread);
+
+ ~FakeVideoMediaChannel();
+
+ const std::vector<VideoCodec>& recv_codecs() const;
+ const std::vector<VideoCodec>& send_codecs() const;
+ const std::vector<VideoCodec>& codecs() const;
+ bool rendering() const;
+ const VideoOptions& options() const;
+ const std::map<uint32_t, rtc::VideoSinkInterface<webrtc::VideoFrame>*>&
+ sinks() const;
+ int max_bps() const;
+ bool SetSendParameters(const VideoSendParameters& params) override;
+ bool SetRecvParameters(const VideoRecvParameters& params) override;
+ bool AddSendStream(const StreamParams& sp) override;
+ bool RemoveSendStream(uint32_t ssrc) override;
+
+ bool GetSendCodec(VideoCodec* send_codec) override;
+ bool SetSink(uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+ void SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+ bool HasSink(uint32_t ssrc) const;
+
+ bool SetSend(bool send) override;
+ bool SetVideoSend(
+ uint32_t ssrc,
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) override;
+
+ bool HasSource(uint32_t ssrc) const;
+ bool AddRecvStream(const StreamParams& sp) override;
+ bool RemoveRecvStream(uint32_t ssrc) override;
+
+ void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override;
+ bool GetSendStats(VideoMediaSendInfo* info) override;
+ bool GetReceiveStats(VideoMediaReceiveInfo* info) override;
+
+ std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
+
+ bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
+ absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const override;
+
+ void SetRecordableEncodedFrameCallback(
+ uint32_t ssrc,
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback)
+ override;
+ void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override;
+ void RequestRecvKeyFrame(uint32_t ssrc) override;
+ void GenerateSendKeyFrame(uint32_t ssrc,
+ const std::vector<std::string>& rids) override;
+
+ private:
+ bool SetRecvCodecs(const std::vector<VideoCodec>& codecs);
+ bool SetSendCodecs(const std::vector<VideoCodec>& codecs);
+ bool SetOptions(const VideoOptions& options);
+ bool SetMaxSendBandwidth(int bps);
+
+ FakeVideoEngine* engine_;
+ std::vector<VideoCodec> recv_codecs_;
+ std::vector<VideoCodec> send_codecs_;
+ std::map<uint32_t, rtc::VideoSinkInterface<webrtc::VideoFrame>*> sinks_;
+ std::map<uint32_t, rtc::VideoSourceInterface<webrtc::VideoFrame>*> sources_;
+ std::map<uint32_t, int> output_delays_;
+ VideoOptions options_;
+ int max_bps_;
+};
+
+class FakeVoiceEngine : public VoiceEngineInterface {
+ public:
+ FakeVoiceEngine();
+ void Init() override;
+ rtc::scoped_refptr<webrtc::AudioState> GetAudioState() const override;
+
+ VoiceMediaChannel* CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const AudioOptions& options,
+ const webrtc::CryptoOptions& crypto_options) override;
+ FakeVoiceMediaChannel* GetChannel(size_t index);
+ void UnregisterChannel(VoiceMediaChannel* channel);
+
+ // TODO(ossu): For proper testing, These should either individually settable
+ // or the voice engine should reference mockable factories.
+ const std::vector<AudioCodec>& send_codecs() const override;
+ const std::vector<AudioCodec>& recv_codecs() const override;
+ void SetCodecs(const std::vector<AudioCodec>& codecs);
+ void SetRecvCodecs(const std::vector<AudioCodec>& codecs);
+ void SetSendCodecs(const std::vector<AudioCodec>& codecs);
+ int GetInputLevel();
+ bool StartAecDump(webrtc::FileWrapper file, int64_t max_size_bytes) override;
+ void StopAecDump() override;
+ absl::optional<webrtc::AudioDeviceModule::Stats> GetAudioDeviceStats()
+ override;
+ std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+ const override;
+ void SetRtpHeaderExtensions(
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions);
+
+ private:
+ std::vector<FakeVoiceMediaChannel*> channels_;
+ std::vector<AudioCodec> recv_codecs_;
+ std::vector<AudioCodec> send_codecs_;
+ bool fail_create_channel_;
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions_;
+
+ friend class FakeMediaEngine;
+};
+
+class FakeVideoEngine : public VideoEngineInterface {
+ public:
+ FakeVideoEngine();
+ bool SetOptions(const VideoOptions& options);
+ VideoMediaChannel* CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory)
+ override;
+ FakeVideoMediaChannel* GetChannel(size_t index);
+ void UnregisterChannel(VideoMediaChannel* channel);
+ std::vector<VideoCodec> send_codecs() const override {
+ return send_codecs(true);
+ }
+ std::vector<VideoCodec> recv_codecs() const override {
+ return recv_codecs(true);
+ }
+ std::vector<VideoCodec> send_codecs(bool include_rtx) const override;
+ std::vector<VideoCodec> recv_codecs(bool include_rtx) const override;
+ void SetSendCodecs(const std::vector<VideoCodec>& codecs);
+ void SetRecvCodecs(const std::vector<VideoCodec>& codecs);
+ bool SetCapture(bool capture);
+ std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+ const override;
+ void SetRtpHeaderExtensions(
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions);
+
+ private:
+ std::vector<FakeVideoMediaChannel*> channels_;
+ std::vector<VideoCodec> send_codecs_;
+ std::vector<VideoCodec> recv_codecs_;
+ bool capture_;
+ VideoOptions options_;
+ bool fail_create_channel_;
+ std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions_;
+
+ friend class FakeMediaEngine;
+};
+
+class FakeMediaEngine : public CompositeMediaEngine {
+ public:
+ FakeMediaEngine();
+
+ ~FakeMediaEngine() override;
+
+ void SetAudioCodecs(const std::vector<AudioCodec>& codecs);
+ void SetAudioRecvCodecs(const std::vector<AudioCodec>& codecs);
+ void SetAudioSendCodecs(const std::vector<AudioCodec>& codecs);
+ void SetVideoCodecs(const std::vector<VideoCodec>& codecs);
+
+ FakeVoiceMediaChannel* GetVoiceChannel(size_t index);
+ FakeVideoMediaChannel* GetVideoChannel(size_t index);
+
+ void set_fail_create_channel(bool fail);
+
+ private:
+ FakeVoiceEngine* const voice_;
+ FakeVideoEngine* const video_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_FAKE_MEDIA_ENGINE_H_
diff --git a/third_party/libwebrtc/media/base/fake_network_interface.h b/third_party/libwebrtc/media/base/fake_network_interface.h
new file mode 100644
index 0000000000..993c6e1aff
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_network_interface.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_FAKE_NETWORK_INTERFACE_H_
+#define MEDIA_BASE_FAKE_NETWORK_INTERFACE_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "api/task_queue/task_queue_base.h"
+#include "media/base/media_channel.h"
+#include "media/base/rtp_utils.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "modules/rtp_rtcp/source/rtp_util.h"
+#include "rtc_base/byte_order.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/dscp.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
+
+namespace cricket {
+
+// Fake NetworkInterface that sends/receives RTP/RTCP packets.
+class FakeNetworkInterface : public MediaChannelNetworkInterface {
+ public:
+ FakeNetworkInterface()
+ : thread_(rtc::Thread::Current()),
+ dest_(NULL),
+ conf_(false),
+ sendbuf_size_(-1),
+ recvbuf_size_(-1),
+ dscp_(rtc::DSCP_NO_CHANGE) {}
+
+ void SetDestination(MediaChannel* dest) { dest_ = dest; }
+
+ // Conference mode is a mode where instead of simply forwarding the packets,
+ // the transport will send multiple copies of the packet with the specified
+ // SSRCs. This allows us to simulate receiving media from multiple sources.
+ void SetConferenceMode(bool conf, const std::vector<uint32_t>& ssrcs)
+ RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ conf_ = conf;
+ conf_sent_ssrcs_ = ssrcs;
+ }
+
+ int NumRtpBytes() RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ int bytes = 0;
+ for (size_t i = 0; i < rtp_packets_.size(); ++i) {
+ bytes += static_cast<int>(rtp_packets_[i].size());
+ }
+ return bytes;
+ }
+
+ int NumRtpBytes(uint32_t ssrc) RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ int bytes = 0;
+ GetNumRtpBytesAndPackets(ssrc, &bytes, NULL);
+ return bytes;
+ }
+
+ int NumRtpPackets() RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ return static_cast<int>(rtp_packets_.size());
+ }
+
+ int NumRtpPackets(uint32_t ssrc) RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ int packets = 0;
+ GetNumRtpBytesAndPackets(ssrc, NULL, &packets);
+ return packets;
+ }
+
+ int NumSentSsrcs() RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ return static_cast<int>(sent_ssrcs_.size());
+ }
+
+ rtc::CopyOnWriteBuffer GetRtpPacket(int index) RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ if (index >= static_cast<int>(rtp_packets_.size())) {
+ return {};
+ }
+ return rtp_packets_[index];
+ }
+
+ int NumRtcpPackets() RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ return static_cast<int>(rtcp_packets_.size());
+ }
+
+ // Note: callers are responsible for deleting the returned buffer.
+ const rtc::CopyOnWriteBuffer* GetRtcpPacket(int index)
+ RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ if (index >= static_cast<int>(rtcp_packets_.size())) {
+ return NULL;
+ }
+ return new rtc::CopyOnWriteBuffer(rtcp_packets_[index]);
+ }
+
+ int sendbuf_size() const { return sendbuf_size_; }
+ int recvbuf_size() const { return recvbuf_size_; }
+ rtc::DiffServCodePoint dscp() const { return dscp_; }
+ rtc::PacketOptions options() const { return options_; }
+
+ protected:
+ virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options)
+ RTC_LOCKS_EXCLUDED(mutex_) {
+ if (!webrtc::IsRtpPacket(*packet)) {
+ return false;
+ }
+
+ webrtc::MutexLock lock(&mutex_);
+ sent_ssrcs_[webrtc::ParseRtpSsrc(*packet)]++;
+ options_ = options;
+
+ rtp_packets_.push_back(*packet);
+ if (conf_) {
+ for (size_t i = 0; i < conf_sent_ssrcs_.size(); ++i) {
+ SetRtpSsrc(conf_sent_ssrcs_[i], *packet);
+ PostPacket(*packet);
+ }
+ } else {
+ PostPacket(*packet);
+ }
+ return true;
+ }
+
+ virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options)
+ RTC_LOCKS_EXCLUDED(mutex_) {
+ webrtc::MutexLock lock(&mutex_);
+ rtcp_packets_.push_back(*packet);
+ options_ = options;
+ if (!conf_) {
+ // don't worry about RTCP in conf mode for now
+ RTC_LOG(LS_VERBOSE) << "Dropping RTCP packet, they are not handled by "
+ "MediaChannel anymore.";
+ }
+ return true;
+ }
+
+ virtual int SetOption(SocketType type, rtc::Socket::Option opt, int option) {
+ if (opt == rtc::Socket::OPT_SNDBUF) {
+ sendbuf_size_ = option;
+ } else if (opt == rtc::Socket::OPT_RCVBUF) {
+ recvbuf_size_ = option;
+ } else if (opt == rtc::Socket::OPT_DSCP) {
+ dscp_ = static_cast<rtc::DiffServCodePoint>(option);
+ }
+ return 0;
+ }
+
+ void PostPacket(rtc::CopyOnWriteBuffer packet) {
+ thread_->PostTask(
+ SafeTask(safety_.flag(), [this, packet = std::move(packet)]() mutable {
+ if (dest_) {
+ webrtc::RtpPacketReceived parsed_packet;
+ if (parsed_packet.Parse(packet)) {
+ parsed_packet.set_arrival_time(
+ webrtc::Timestamp::Micros(rtc::TimeMicros()));
+ dest_->OnPacketReceived(std::move(parsed_packet));
+ } else {
+ RTC_DCHECK_NOTREACHED();
+ }
+ }
+ }));
+ }
+
+ private:
+ void SetRtpSsrc(uint32_t ssrc, rtc::CopyOnWriteBuffer& buffer) {
+ RTC_CHECK_GE(buffer.size(), 12);
+ rtc::SetBE32(buffer.MutableData() + 8, ssrc);
+ }
+
+ void GetNumRtpBytesAndPackets(uint32_t ssrc, int* bytes, int* packets) {
+ if (bytes) {
+ *bytes = 0;
+ }
+ if (packets) {
+ *packets = 0;
+ }
+ for (size_t i = 0; i < rtp_packets_.size(); ++i) {
+ if (ssrc == webrtc::ParseRtpSsrc(rtp_packets_[i])) {
+ if (bytes) {
+ *bytes += static_cast<int>(rtp_packets_[i].size());
+ }
+ if (packets) {
+ ++(*packets);
+ }
+ }
+ }
+ }
+
+ webrtc::TaskQueueBase* thread_;
+ MediaChannel* dest_;
+ bool conf_;
+ // The ssrcs used in sending out packets in conference mode.
+ std::vector<uint32_t> conf_sent_ssrcs_;
+ // Map to track counts of packets that have been sent per ssrc.
+ // This includes packets that are dropped.
+ std::map<uint32_t, uint32_t> sent_ssrcs_;
+ // Map to track packet-number that needs to be dropped per ssrc.
+ std::map<uint32_t, std::set<uint32_t> > drop_map_;
+ webrtc::Mutex mutex_;
+ std::vector<rtc::CopyOnWriteBuffer> rtp_packets_;
+ std::vector<rtc::CopyOnWriteBuffer> rtcp_packets_;
+ int sendbuf_size_;
+ int recvbuf_size_;
+ rtc::DiffServCodePoint dscp_;
+ // Options of the most recently sent packet.
+ rtc::PacketOptions options_;
+ webrtc::ScopedTaskSafety safety_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_FAKE_NETWORK_INTERFACE_H_
diff --git a/third_party/libwebrtc/media/base/fake_rtp.cc b/third_party/libwebrtc/media/base/fake_rtp.cc
new file mode 100644
index 0000000000..21322419e1
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_rtp.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "media/base/fake_rtp.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "absl/algorithm/container.h"
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+
+void CompareHeaderExtensions(const char* packet1,
+ size_t packet1_size,
+ const char* packet2,
+ size_t packet2_size,
+ const std::vector<int>& encrypted_headers,
+ bool expect_equal) {
+ // Sanity check: packets must be large enough to contain the RTP header and
+ // extensions header.
+ RTC_CHECK_GE(packet1_size, 12 + 4);
+ RTC_CHECK_GE(packet2_size, 12 + 4);
+ // RTP extension headers are the same.
+ EXPECT_EQ(0, memcmp(packet1 + 12, packet2 + 12, 4));
+ // Check for one-byte header extensions.
+ EXPECT_EQ('\xBE', packet1[12]);
+ EXPECT_EQ('\xDE', packet1[13]);
+ // Determine position and size of extension headers.
+ size_t extension_words = packet1[14] << 8 | packet1[15];
+ const char* extension_data1 = packet1 + 12 + 4;
+ const char* extension_end1 = extension_data1 + extension_words * 4;
+ const char* extension_data2 = packet2 + 12 + 4;
+ // Sanity check: packets must be large enough to contain the RTP header
+ // extensions.
+ RTC_CHECK_GE(packet1_size, 12 + 4 + extension_words * 4);
+ RTC_CHECK_GE(packet2_size, 12 + 4 + extension_words * 4);
+ while (extension_data1 < extension_end1) {
+ uint8_t id = (*extension_data1 & 0xf0) >> 4;
+ uint8_t len = (*extension_data1 & 0x0f) + 1;
+ extension_data1++;
+ extension_data2++;
+ EXPECT_LE(extension_data1, extension_end1);
+ if (id == 15) {
+ // Finished parsing.
+ break;
+ }
+
+ // The header extension doesn't get encrypted if the id is not in the
+ // list of header extensions to encrypt.
+ if (expect_equal || !absl::c_linear_search(encrypted_headers, id)) {
+ EXPECT_EQ(0, memcmp(extension_data1, extension_data2, len));
+ } else {
+ EXPECT_NE(0, memcmp(extension_data1, extension_data2, len));
+ }
+
+ extension_data1 += len;
+ extension_data2 += len;
+ // Skip padding.
+ while (extension_data1 < extension_end1 && *extension_data1 == 0) {
+ extension_data1++;
+ extension_data2++;
+ }
+ }
+}
diff --git a/third_party/libwebrtc/media/base/fake_rtp.h b/third_party/libwebrtc/media/base/fake_rtp.h
new file mode 100644
index 0000000000..8a176038cb
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_rtp.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Fake RTP and RTCP packets to use in unit tests.
+
+#ifndef MEDIA_BASE_FAKE_RTP_H_
+#define MEDIA_BASE_FAKE_RTP_H_
+
+#include <cstddef> // size_t
+#include <vector>
+
+// A typical PCMU RTP packet.
+// PT=0, SN=1, TS=0, SSRC=1
+// all data FF
+static const unsigned char kPcmuFrame[] = {
+ 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static const int kHeaderExtensionIDs[] = {1, 4};
+
+// A typical PCMU RTP packet with header extensions.
+// PT=0, SN=1, TS=0, SSRC=1
+// all data FF
+static const unsigned char kPcmuFrameWithExtensions[] = {
+ 0x90,
+ 0x00,
+ 0x00,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ // RFC 5285, section 4.2. One-Byte Header.
+ 0xBE,
+ 0xDE,
+ // Header extension length 6 * 32 bits.
+ 0x00,
+ 0x06,
+ // 8 bytes header id 1.
+ 0x17,
+ 0x41,
+ 0x42,
+ 0x73,
+ 0xA4,
+ 0x75,
+ 0x26,
+ 0x27,
+ 0x48,
+ // 3 bytes header id 2.
+ 0x22,
+ 0x00,
+ 0x00,
+ 0xC8,
+ // 1 byte header id 3.
+ 0x30,
+ 0x8E,
+ // 7 bytes header id 4.
+ 0x46,
+ 0x55,
+ 0x99,
+ 0x63,
+ 0x86,
+ 0xB3,
+ 0x95,
+ 0xFB,
+ // 1 byte header padding.
+ 0x00,
+ // Payload data.
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+};
+
+// A typical Receiver Report RTCP packet.
+// PT=RR, LN=1, SSRC=1
+// send SSRC=2, all other fields 0
+static const unsigned char kRtcpReport[] = {
+ 0x80, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// PT = 97, TS = 0, Seq = 1, SSRC = 2
+// H264 - NRI = 1, Type = 1, bit stream = FF
+
+static const unsigned char kH264Packet[] = {
+ 0x80, 0x61, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x21, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+// PT= 101, SN=2, TS=3, SSRC = 4
+static const unsigned char kDataPacket[] = {
+ 0x80, 0x65, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+};
+
+// This expects both packets to be based on kPcmuFrameWithExtensions.
+// Header extensions with an id in "encrypted_headers" are expected to be
+// different in the packets unless "expect_equal" is set to "true".
+void CompareHeaderExtensions(const char* packet1,
+ size_t packet1_size,
+ const char* packet2,
+ size_t packet2_size,
+ const std::vector<int>& encrypted_headers,
+ bool expect_equal);
+
+#endif // MEDIA_BASE_FAKE_RTP_H_
diff --git a/third_party/libwebrtc/media/base/fake_video_renderer.cc b/third_party/libwebrtc/media/base/fake_video_renderer.cc
new file mode 100644
index 0000000000..b235738d24
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_video_renderer.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/fake_video_renderer.h"
+
+namespace cricket {
+namespace {
+bool CheckFrameColorYuv(const webrtc::VideoFrame& frame) {
+ // TODO(zhurunz) Check with VP8 team to see if we can remove this
+ // tolerance on Y values. Some unit tests produce Y values close
+ // to 16 rather than close to zero, for supposedly black frames.
+ // Largest value observed is 34, e.g., running
+ // PeerConnectionIntegrationTest.SendAndReceive16To9AspectRatio.
+ static constexpr uint8_t y_min = 0;
+ static constexpr uint8_t y_max = 48;
+ static constexpr uint8_t u_min = 128;
+ static constexpr uint8_t u_max = 128;
+ static constexpr uint8_t v_min = 128;
+ static constexpr uint8_t v_max = 128;
+
+ if (!frame.video_frame_buffer()) {
+ return false;
+ }
+ rtc::scoped_refptr<const webrtc::I420BufferInterface> i420_buffer =
+ frame.video_frame_buffer()->ToI420();
+ // Y
+ int y_width = frame.width();
+ int y_height = frame.height();
+ const uint8_t* y_plane = i420_buffer->DataY();
+ const uint8_t* y_pos = y_plane;
+ int32_t y_pitch = i420_buffer->StrideY();
+ for (int i = 0; i < y_height; ++i) {
+ for (int j = 0; j < y_width; ++j) {
+ uint8_t y_value = *(y_pos + j);
+ if (y_value < y_min || y_value > y_max) {
+ return false;
+ }
+ }
+ y_pos += y_pitch;
+ }
+ // U and V
+ int chroma_width = i420_buffer->ChromaWidth();
+ int chroma_height = i420_buffer->ChromaHeight();
+ const uint8_t* u_plane = i420_buffer->DataU();
+ const uint8_t* v_plane = i420_buffer->DataV();
+ const uint8_t* u_pos = u_plane;
+ const uint8_t* v_pos = v_plane;
+ int32_t u_pitch = i420_buffer->StrideU();
+ int32_t v_pitch = i420_buffer->StrideV();
+ for (int i = 0; i < chroma_height; ++i) {
+ for (int j = 0; j < chroma_width; ++j) {
+ uint8_t u_value = *(u_pos + j);
+ if (u_value < u_min || u_value > u_max) {
+ return false;
+ }
+ uint8_t v_value = *(v_pos + j);
+ if (v_value < v_min || v_value > v_max) {
+ return false;
+ }
+ }
+ u_pos += u_pitch;
+ v_pos += v_pitch;
+ }
+ return true;
+}
+} // namespace
+
+FakeVideoRenderer::FakeVideoRenderer() = default;
+
+void FakeVideoRenderer::OnFrame(const webrtc::VideoFrame& frame) {
+ webrtc::MutexLock lock(&mutex_);
+ black_frame_ = CheckFrameColorYuv(frame);
+ ++num_rendered_frames_;
+ width_ = frame.width();
+ height_ = frame.height();
+ rotation_ = frame.rotation();
+ timestamp_us_ = frame.timestamp_us();
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/fake_video_renderer.h b/third_party/libwebrtc/media/base/fake_video_renderer.h
new file mode 100644
index 0000000000..33d99a2668
--- /dev/null
+++ b/third_party/libwebrtc/media/base/fake_video_renderer.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_FAKE_VIDEO_RENDERER_H_
+#define MEDIA_BASE_FAKE_VIDEO_RENDERER_H_
+
+#include <stdint.h>
+
+#include "api/scoped_refptr.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_frame_buffer.h"
+#include "api/video/video_rotation.h"
+#include "api/video/video_sink_interface.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace cricket {
+
+// Faked video renderer that has a callback for actions on rendering.
+class FakeVideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
+ public:
+ FakeVideoRenderer();
+
+ void OnFrame(const webrtc::VideoFrame& frame) override;
+
+ int width() const {
+ webrtc::MutexLock lock(&mutex_);
+ return width_;
+ }
+ int height() const {
+ webrtc::MutexLock lock(&mutex_);
+ return height_;
+ }
+
+ webrtc::VideoRotation rotation() const {
+ webrtc::MutexLock lock(&mutex_);
+ return rotation_;
+ }
+
+ int64_t timestamp_us() const {
+ webrtc::MutexLock lock(&mutex_);
+ return timestamp_us_;
+ }
+
+ int num_rendered_frames() const {
+ webrtc::MutexLock lock(&mutex_);
+ return num_rendered_frames_;
+ }
+
+ bool black_frame() const {
+ webrtc::MutexLock lock(&mutex_);
+ return black_frame_;
+ }
+
+ private:
+ int width_ = 0;
+ int height_ = 0;
+ webrtc::VideoRotation rotation_ = webrtc::kVideoRotation_0;
+ int64_t timestamp_us_ = 0;
+ int num_rendered_frames_ = 0;
+ bool black_frame_ = false;
+ mutable webrtc::Mutex mutex_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_FAKE_VIDEO_RENDERER_H_
diff --git a/third_party/libwebrtc/media/base/media_channel.h b/third_party/libwebrtc/media/base/media_channel.h
new file mode 100644
index 0000000000..138d28ae4c
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_channel.h
@@ -0,0 +1,992 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_CHANNEL_H_
+#define MEDIA_BASE_MEDIA_CHANNEL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/audio_codecs/audio_encoder.h"
+#include "api/audio_options.h"
+#include "api/call/audio_sink.h"
+#include "api/crypto/frame_decryptor_interface.h"
+#include "api/crypto/frame_encryptor_interface.h"
+#include "api/frame_transformer_interface.h"
+#include "api/media_stream_interface.h"
+#include "api/rtc_error.h"
+#include "api/rtp_parameters.h"
+#include "api/rtp_sender_interface.h"
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "api/transport/data_channel_transport_interface.h"
+#include "api/transport/rtp/rtp_source.h"
+#include "api/units/time_delta.h"
+#include "api/video/video_content_type.h"
+#include "api/video/video_sink_interface.h"
+#include "api/video/video_source_interface.h"
+#include "api/video/video_timing.h"
+#include "api/video_codecs/scalability_mode.h"
+#include "api/video_codecs/video_encoder_factory.h"
+#include "call/video_receive_stream.h"
+#include "common_video/include/quality_limitation_reason.h"
+#include "media/base/codec.h"
+#include "media/base/delayable.h"
+#include "media/base/media_constants.h"
+#include "media/base/stream_params.h"
+#include "modules/audio_processing/include/audio_processing_statistics.h"
+#include "modules/rtp_rtcp/include/report_block_data.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/dscp.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/network_route.h"
+#include "rtc_base/socket.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/strings/string_builder.h"
+#include "video/config/video_encoder_config.h"
+
+namespace rtc {
+class Timing;
+}
+
+namespace webrtc {
+class VideoFrame;
+} // namespace webrtc
+
+namespace cricket {
+
+class AudioSource;
+class VideoCapturer;
+struct RtpHeader;
+struct VideoFormat;
+class VideoMediaSendChannelInterface;
+class VideoMediaReceiveChannelInterface;
+class VoiceMediaSendChannelInterface;
+class VoiceMediaReceiveChannelInterface;
+
+const int kScreencastDefaultFps = 5;
+
+template <class T>
+static std::string ToStringIfSet(const char* key,
+ const absl::optional<T>& val) {
+ std::string str;
+ if (val) {
+ str = key;
+ str += ": ";
+ str += val ? rtc::ToString(*val) : "";
+ str += ", ";
+ }
+ return str;
+}
+
+template <class T>
+static std::string VectorToString(const std::vector<T>& vals) {
+ rtc::StringBuilder ost; // no-presubmit-check TODO(webrtc:8982)
+ ost << "[";
+ for (size_t i = 0; i < vals.size(); ++i) {
+ if (i > 0) {
+ ost << ", ";
+ }
+ ost << vals[i].ToString();
+ }
+ ost << "]";
+ return ost.Release();
+}
+
+// Options that can be applied to a VideoMediaChannel or a VideoMediaEngine.
+// Used to be flags, but that makes it hard to selectively apply options.
+// We are moving all of the setting of options to structs like this,
+// but some things currently still use flags.
+struct VideoOptions {
+ VideoOptions();
+ ~VideoOptions();
+
+ void SetAll(const VideoOptions& change) {
+ SetFrom(&video_noise_reduction, change.video_noise_reduction);
+ SetFrom(&screencast_min_bitrate_kbps, change.screencast_min_bitrate_kbps);
+ SetFrom(&is_screencast, change.is_screencast);
+ }
+
+ bool operator==(const VideoOptions& o) const {
+ return video_noise_reduction == o.video_noise_reduction &&
+ screencast_min_bitrate_kbps == o.screencast_min_bitrate_kbps &&
+ is_screencast == o.is_screencast;
+ }
+ bool operator!=(const VideoOptions& o) const { return !(*this == o); }
+
+ std::string ToString() const {
+ rtc::StringBuilder ost;
+ ost << "VideoOptions {";
+ ost << ToStringIfSet("noise reduction", video_noise_reduction);
+ ost << ToStringIfSet("screencast min bitrate kbps",
+ screencast_min_bitrate_kbps);
+ ost << ToStringIfSet("is_screencast ", is_screencast);
+ ost << "}";
+ return ost.Release();
+ }
+
+ // Enable denoising? This flag comes from the getUserMedia
+ // constraint 'googNoiseReduction', and WebRtcVideoEngine passes it
+ // on to the codec options. Disabled by default.
+ absl::optional<bool> video_noise_reduction;
+ // Force screencast to use a minimum bitrate. This flag comes from
+ // the PeerConnection constraint 'googScreencastMinBitrate'. It is
+ // copied to the encoder config by WebRtcVideoChannel.
+ // TODO(https://crbug.com/1315155): Remove the ability to set it in Chromium
+ // and delete this flag (it should default to 100 kbps).
+ absl::optional<int> screencast_min_bitrate_kbps;
+ // Set by screencast sources. Implies selection of encoding settings
+ // suitable for screencast. Most likely not the right way to do
+ // things, e.g., screencast of a text document and screencast of a
+ // youtube video have different needs.
+ absl::optional<bool> is_screencast;
+ webrtc::VideoTrackInterface::ContentHint content_hint;
+
+ private:
+ template <typename T>
+ static void SetFrom(absl::optional<T>* s, const absl::optional<T>& o) {
+ if (o) {
+ *s = o;
+ }
+ }
+};
+
+class MediaChannelNetworkInterface {
+ public:
+ enum SocketType { ST_RTP, ST_RTCP };
+ virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options) = 0;
+ virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options) = 0;
+ virtual int SetOption(SocketType type,
+ rtc::Socket::Option opt,
+ int option) = 0;
+ virtual ~MediaChannelNetworkInterface() {}
+};
+
+// Functions shared across all MediaChannel interfaces.
+// Because there are implementation types that implement multiple
+// interfaces, this is not a base class (no diamond inheritance).
+template <class T>
+class MediaBaseChannelInterface {
+ public:
+ virtual ~MediaBaseChannelInterface() = default;
+ virtual cricket::MediaType media_type() const = 0;
+
+ // Networking functions. We assume that both the send channel and the
+ // receive channel send RTP packets (RTCP packets in the case of a receive
+ // channel).
+
+ // Called on the network when an RTP packet is received.
+ virtual void OnPacketReceived(const webrtc::RtpPacketReceived& packet) = 0;
+ // Called on the network thread after a transport has finished sending a
+ // packet.
+ virtual void OnPacketSent(const rtc::SentPacket& sent_packet) = 0;
+ // Called when the socket's ability to send has changed.
+ virtual void OnReadyToSend(bool ready) = 0;
+ // Called when the network route used for sending packets changed.
+ virtual void OnNetworkRouteChanged(
+ absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) = 0;
+
+ // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+ // Set to true if it's allowed to mix one- and two-byte RTP header extensions
+ // in the same stream. The setter and getter must only be called from
+ // worker_thread.
+ virtual void SetExtmapAllowMixed(bool extmap_allow_mixed) = 0;
+ virtual bool ExtmapAllowMixed() const = 0;
+};
+
+class MediaSendChannelInterface
+ : public MediaBaseChannelInterface<MediaSendChannelInterface> {
+ public:
+ virtual ~MediaSendChannelInterface() = default;
+
+ virtual VideoMediaSendChannelInterface* AsVideoSendChannel() = 0;
+
+ virtual VoiceMediaSendChannelInterface* AsVoiceSendChannel() = 0;
+
+ // Creates a new outgoing media stream with SSRCs and CNAME as described
+ // by sp.
+ virtual bool AddSendStream(const StreamParams& sp) = 0;
+ // Removes an outgoing media stream.
+ // SSRC must be the first SSRC of the media stream if the stream uses
+ // multiple SSRCs. In the case of an ssrc of 0, the possibly cached
+ // StreamParams is removed.
+ virtual bool RemoveSendStream(uint32_t ssrc) = 0;
+ // Set the frame encryptor to use on all outgoing frames. This is optional.
+ // This pointers lifetime is managed by the set of RtpSender it is attached
+ // to.
+ virtual void SetFrameEncryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) = 0;
+
+ virtual webrtc::RTCError SetRtpSendParameters(
+ uint32_t ssrc,
+ const webrtc::RtpParameters& parameters,
+ webrtc::SetParametersCallback callback = nullptr) = 0;
+
+ virtual void SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+ frame_transformer) = 0;
+
+ // note: The encoder_selector object must remain valid for the lifetime of the
+ // MediaChannel, unless replaced.
+ virtual void SetEncoderSelector(
+ uint32_t ssrc,
+ webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+ }
+ virtual webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const = 0;
+};
+
+class MediaReceiveChannelInterface
+ : public MediaBaseChannelInterface<MediaReceiveChannelInterface>,
+ public Delayable {
+ public:
+ virtual ~MediaReceiveChannelInterface() = default;
+
+ virtual VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ virtual VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ // Creates a new incoming media stream with SSRCs, CNAME as described
+ // by sp. In the case of a sp without SSRCs, the unsignaled sp is cached
+ // to be used later for unsignaled streams received.
+ virtual bool AddRecvStream(const StreamParams& sp) = 0;
+ // Removes an incoming media stream.
+ // ssrc must be the first SSRC of the media stream if the stream uses
+ // multiple SSRCs.
+ virtual bool RemoveRecvStream(uint32_t ssrc) = 0;
+ // Resets any cached StreamParams for an unsignaled RecvStream, and removes
+ // any existing unsignaled streams.
+ virtual void ResetUnsignaledRecvStream() = 0;
+ // Gets the current unsignaled receive stream's SSRC, if there is one.
+ virtual absl::optional<uint32_t> GetUnsignaledSsrc() const = 0;
+ // This is currently a workaround because of the demuxer state being managed
+ // across two separate threads. Once the state is consistently managed on
+ // the same thread (network), this workaround can be removed.
+ // These two notifications inform the media channel when the transport's
+ // demuxer criteria is being updated.
+ // * OnDemuxerCriteriaUpdatePending() happens on the same thread that the
+ // channel's streams are added and removed (worker thread).
+ // * OnDemuxerCriteriaUpdateComplete() happens on the same thread.
+ // Because the demuxer is updated asynchronously, there is a window of time
+ // where packets are arriving to the channel for streams that have already
+ // been removed on the worker thread. It is important NOT to treat these as
+ // new unsignalled ssrcs.
+ virtual void OnDemuxerCriteriaUpdatePending() = 0;
+ virtual void OnDemuxerCriteriaUpdateComplete() = 0;
+ // Set the frame decryptor to use on all incoming frames. This is optional.
+ // This pointers lifetimes is managed by the set of RtpReceivers it is
+ // attached to.
+ virtual void SetFrameDecryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) = 0;
+
+ virtual void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+ frame_transformer) = 0;
+};
+
+// The stats information is structured as follows:
+// Media are represented by either MediaSenderInfo or MediaReceiverInfo.
+// Media contains a vector of SSRC infos that are exclusively used by this
+// media. (SSRCs shared between media streams can't be represented.)
+
+// Information about an SSRC.
+// This data may be locally recorded, or received in an RTCP SR or RR.
+struct SsrcSenderInfo {
+ uint32_t ssrc = 0;
+ double timestamp = 0.0; // NTP timestamp, represented as seconds since epoch.
+};
+
+struct SsrcReceiverInfo {
+ uint32_t ssrc = 0;
+ double timestamp = 0.0;
+};
+
+struct MediaSenderInfo {
+ MediaSenderInfo();
+ ~MediaSenderInfo();
+ void add_ssrc(const SsrcSenderInfo& stat) { local_stats.push_back(stat); }
+ // Temporary utility function for call sites that only provide SSRC.
+ // As more info is added into SsrcSenderInfo, this function should go away.
+ void add_ssrc(uint32_t ssrc) {
+ SsrcSenderInfo stat;
+ stat.ssrc = ssrc;
+ add_ssrc(stat);
+ }
+ // Utility accessor for clients that are only interested in ssrc numbers.
+ std::vector<uint32_t> ssrcs() const {
+ std::vector<uint32_t> retval;
+ for (std::vector<SsrcSenderInfo>::const_iterator it = local_stats.begin();
+ it != local_stats.end(); ++it) {
+ retval.push_back(it->ssrc);
+ }
+ return retval;
+ }
+ // Returns true if the media has been connected.
+ bool connected() const { return local_stats.size() > 0; }
+ // Utility accessor for clients that make the assumption only one ssrc
+ // exists per media.
+ // This will eventually go away.
+ // Call sites that compare this to zero should use connected() instead.
+ // https://bugs.webrtc.org/8694
+ uint32_t ssrc() const {
+ if (connected()) {
+ return local_stats[0].ssrc;
+ } else {
+ return 0;
+ }
+ }
+ // https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats-bytessent
+ int64_t payload_bytes_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-headerbytessent
+ int64_t header_and_padding_bytes_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedbytessent
+ uint64_t retransmitted_bytes_sent = 0;
+ int packets_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedpacketssent
+ uint64_t retransmitted_packets_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-nackcount
+ uint32_t nacks_rcvd = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-targetbitrate
+ absl::optional<double> target_bitrate;
+ int packets_lost = 0;
+ float fraction_lost = 0.0f;
+ int64_t rtt_ms = 0;
+ std::string codec_name;
+ absl::optional<int> codec_payload_type;
+ std::vector<SsrcSenderInfo> local_stats;
+ std::vector<SsrcReceiverInfo> remote_stats;
+ // A snapshot of the most recent Report Block with additional data of interest
+ // to statistics. Used to implement RTCRemoteInboundRtpStreamStats. Within
+ // this list, the ReportBlockData::RTCPReportBlock::source_ssrc(), which is
+ // the SSRC of the corresponding outbound RTP stream, is unique.
+ std::vector<webrtc::ReportBlockData> report_block_datas;
+ absl::optional<bool> active;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
+ webrtc::TimeDelta total_packet_send_delay = webrtc::TimeDelta::Zero();
+};
+
+struct MediaReceiverInfo {
+ MediaReceiverInfo();
+ ~MediaReceiverInfo();
+
+ void add_ssrc(const SsrcReceiverInfo& stat) { local_stats.push_back(stat); }
+ // Temporary utility function for call sites that only provide SSRC.
+ // As more info is added into SsrcSenderInfo, this function should go away.
+ void add_ssrc(uint32_t ssrc) {
+ SsrcReceiverInfo stat;
+ stat.ssrc = ssrc;
+ add_ssrc(stat);
+ }
+ std::vector<uint32_t> ssrcs() const {
+ std::vector<uint32_t> retval;
+ for (std::vector<SsrcReceiverInfo>::const_iterator it = local_stats.begin();
+ it != local_stats.end(); ++it) {
+ retval.push_back(it->ssrc);
+ }
+ return retval;
+ }
+ // Returns true if the media has been connected.
+ bool connected() const { return local_stats.size() > 0; }
+ // Utility accessor for clients that make the assumption only one ssrc
+ // exists per media.
+ // This will eventually go away.
+ // Call sites that compare this to zero should use connected();
+ // https://bugs.webrtc.org/8694
+ uint32_t ssrc() const {
+ if (connected()) {
+ return local_stats[0].ssrc;
+ } else {
+ return 0;
+ }
+ }
+
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-bytesreceived
+ int64_t payload_bytes_rcvd = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-headerbytesreceived
+ int64_t header_and_padding_bytes_rcvd = 0;
+ int packets_rcvd = 0;
+ int packets_lost = 0;
+ absl::optional<uint32_t> nacks_sent;
+ // Jitter (network-related) latency (cumulative).
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferdelay
+ double jitter_buffer_delay_seconds = 0.0;
+ // Target delay for the jitter buffer (cumulative).
+ // TODO(crbug.com/webrtc/14244): This metric is only implemented for
+ // audio, it should be implemented for video as well.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbuffertargetdelay
+ absl::optional<double> jitter_buffer_target_delay_seconds;
+ // Minimum obtainable delay for the jitter buffer (cumulative).
+ // TODO(crbug.com/webrtc/14244): This metric is only implemented for
+ // audio, it should be implemented for video as well.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferminimumdelay
+ absl::optional<double> jitter_buffer_minimum_delay_seconds;
+ // Number of observations for cumulative jitter latency.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferemittedcount
+ uint64_t jitter_buffer_emitted_count = 0;
+ // The timestamp at which the last packet was received, i.e. the time of the
+ // local clock when it was received - not the RTP timestamp of that packet.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
+ absl::optional<int64_t> last_packet_received_timestamp_ms;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-estimatedplayouttimestamp
+ absl::optional<int64_t> estimated_playout_ntp_timestamp_ms;
+ std::string codec_name;
+ absl::optional<int> codec_payload_type;
+ std::vector<SsrcReceiverInfo> local_stats;
+ std::vector<SsrcSenderInfo> remote_stats;
+};
+
+struct VoiceSenderInfo : public MediaSenderInfo {
+ VoiceSenderInfo();
+ ~VoiceSenderInfo();
+ int jitter_ms = 0;
+ // Current audio level, expressed linearly [0,32767].
+ int audio_level = 0;
+ // 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;
+ webrtc::ANAStats ana_statistics;
+ webrtc::AudioProcessingStats apm_statistics;
+};
+
+struct VoiceReceiverInfo : public MediaReceiverInfo {
+ VoiceReceiverInfo();
+ ~VoiceReceiverInfo();
+ int jitter_ms = 0;
+ int jitter_buffer_ms = 0;
+ int jitter_buffer_preferred_ms = 0;
+ int delay_estimate_ms = 0;
+ int audio_level = 0;
+ // Stats below correspond to similarly-named fields in the WebRTC stats spec.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats
+ 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 silent_concealed_samples = 0;
+ uint64_t concealment_events = 0;
+ uint64_t inserted_samples_for_deceleration = 0;
+ uint64_t removed_samples_for_acceleration = 0;
+ uint64_t fec_packets_received = 0;
+ uint64_t fec_packets_discarded = 0;
+ // Stats below correspond to similarly-named fields in the WebRTC stats spec.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats
+ uint64_t packets_discarded = 0;
+ // Stats below DO NOT correspond directly to anything in the WebRTC stats
+ // fraction of synthesized audio inserted through expansion.
+ float expand_rate = 0.0f;
+ // fraction of synthesized speech inserted through expansion.
+ float speech_expand_rate = 0.0f;
+ // fraction of data out of secondary decoding, including FEC and RED.
+ float secondary_decoded_rate = 0.0f;
+ // Fraction of secondary data, including FEC and RED, that is discarded.
+ // Discarding of secondary data can be caused by the reception of the primary
+ // data, obsoleting the secondary data. It can also be caused by early
+ // or late arrival of secondary data. This metric is the percentage of
+ // discarded secondary data since last query of receiver info.
+ float secondary_discarded_rate = 0.0f;
+ // Fraction of data removed through time compression.
+ float accelerate_rate = 0.0f;
+ // Fraction of data inserted through time stretching.
+ float preemptive_expand_rate = 0.0f;
+ int decoding_calls_to_silence_generator = 0;
+ int decoding_calls_to_neteq = 0;
+ int decoding_normal = 0;
+ // TODO(alexnarest): Consider decoding_neteq_plc for consistency
+ int decoding_plc = 0;
+ int decoding_codec_plc = 0;
+ int decoding_cng = 0;
+ int decoding_plc_cng = 0;
+ int decoding_muted_output = 0;
+ // Estimated capture start time in NTP time in ms.
+ int64_t capture_start_ntp_time_ms = -1;
+ // Count of the number of buffer flushes.
+ uint64_t jitter_buffer_flushes = 0;
+ // Number of samples expanded due to delayed packets.
+ uint64_t delayed_packet_outage_samples = 0;
+ // Arrival delay of received audio packets.
+ double relative_packet_arrival_delay_seconds = 0.0;
+ // Count and total duration of audio interruptions (loss-concealement periods
+ // longer than 150 ms).
+ int32_t interruption_count = 0;
+ int32_t total_interruption_duration_ms = 0;
+ // Remote outbound stats derived by the received RTCP sender reports.
+ // https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*
+ absl::optional<int64_t> last_sender_report_timestamp_ms;
+ absl::optional<int64_t> last_sender_report_remote_timestamp_ms;
+ uint32_t sender_reports_packets_sent = 0;
+ uint64_t sender_reports_bytes_sent = 0;
+ uint64_t sender_reports_reports_count = 0;
+ absl::optional<webrtc::TimeDelta> round_trip_time;
+ webrtc::TimeDelta total_round_trip_time = webrtc::TimeDelta::Zero();
+ int round_trip_time_measurements = 0;
+};
+
+struct VideoSenderInfo : public MediaSenderInfo {
+ VideoSenderInfo();
+ ~VideoSenderInfo();
+ std::vector<SsrcGroup> ssrc_groups;
+ std::string encoder_implementation_name;
+ int firs_rcvd = 0;
+ int plis_rcvd = 0;
+ int send_frame_width = 0;
+ int send_frame_height = 0;
+ int frames = 0;
+ double framerate_input = 0;
+ int framerate_sent = 0;
+ int aggregated_framerate_sent = 0;
+ int nominal_bitrate = 0;
+ int adapt_reason = 0;
+ int adapt_changes = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationreason
+ webrtc::QualityLimitationReason quality_limitation_reason =
+ webrtc::QualityLimitationReason::kNone;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations
+ std::map<webrtc::QualityLimitationReason, int64_t>
+ quality_limitation_durations_ms;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges
+ uint32_t quality_limitation_resolution_changes = 0;
+ int avg_encode_ms = 0;
+ int encode_usage_percent = 0;
+ uint32_t frames_encoded = 0;
+ uint32_t key_frames_encoded = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodetime
+ uint64_t total_encode_time_ms = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodedbytestarget
+ uint64_t total_encoded_bytes_target = 0;
+ bool has_entered_low_resolution = false;
+ absl::optional<uint64_t> qp_sum;
+ webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED;
+ uint32_t frames_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
+ uint32_t huge_frames_sent = 0;
+ uint32_t aggregated_huge_frames_sent = 0;
+ absl::optional<std::string> rid;
+ absl::optional<bool> power_efficient_encoder;
+ absl::optional<webrtc::ScalabilityMode> scalability_mode;
+};
+
+struct VideoReceiverInfo : public MediaReceiverInfo {
+ VideoReceiverInfo();
+ ~VideoReceiverInfo();
+ std::vector<SsrcGroup> ssrc_groups;
+ std::string decoder_implementation_name;
+ absl::optional<bool> power_efficient_decoder;
+ int packets_concealed = 0;
+ int firs_sent = 0;
+ int plis_sent = 0;
+ int frame_width = 0;
+ int frame_height = 0;
+ int framerate_rcvd = 0;
+ int framerate_decoded = 0;
+ int framerate_output = 0;
+ // Framerate as sent to the renderer.
+ int framerate_render_input = 0;
+ // Framerate that the renderer reports.
+ int framerate_render_output = 0;
+ uint32_t frames_received = 0;
+ uint32_t frames_dropped = 0;
+ uint32_t frames_decoded = 0;
+ uint32_t key_frames_decoded = 0;
+ uint32_t frames_rendered = 0;
+ absl::optional<uint64_t> qp_sum;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime
+ webrtc::TimeDelta total_decode_time = webrtc::TimeDelta::Zero();
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay
+ webrtc::TimeDelta total_processing_delay = webrtc::TimeDelta::Zero();
+ webrtc::TimeDelta total_assembly_time = webrtc::TimeDelta::Zero();
+ uint32_t frames_assembled_from_multiple_packets = 0;
+ double total_inter_frame_delay = 0;
+ double total_squared_inter_frame_delay = 0;
+ int64_t interframe_delay_max_ms = -1;
+ uint32_t freeze_count = 0;
+ uint32_t pause_count = 0;
+ uint32_t total_freezes_duration_ms = 0;
+ uint32_t total_pauses_duration_ms = 0;
+ uint32_t jitter_ms = 0;
+
+ webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED;
+
+ // All stats below are gathered per-VideoReceiver, but some will be correlated
+ // across MediaStreamTracks. NOTE(hta): when sinking stats into per-SSRC
+ // structures, reflect this in the new layout.
+
+ // Current frame decode latency.
+ int decode_ms = 0;
+ // Maximum observed frame decode latency.
+ int max_decode_ms = 0;
+ // Jitter (network-related) latency.
+ int jitter_buffer_ms = 0;
+ // Requested minimum playout latency.
+ int min_playout_delay_ms = 0;
+ // Requested latency to account for rendering delay.
+ int render_delay_ms = 0;
+ // Target overall delay: network+decode+render, accounting for
+ // min_playout_delay_ms.
+ int target_delay_ms = 0;
+ // Current overall delay, possibly ramping towards target_delay_ms.
+ int current_delay_ms = 0;
+
+ // Estimated capture start time in NTP time in ms.
+ int64_t capture_start_ntp_time_ms = -1;
+
+ // First frame received to first frame decoded latency.
+ int64_t first_frame_received_to_decoded_ms = -1;
+
+ // Timing frame info: all important timestamps for a full lifetime of a
+ // single 'timing frame'.
+ absl::optional<webrtc::TimingFrameInfo> timing_frame_info;
+};
+
+struct BandwidthEstimationInfo {
+ int available_send_bandwidth = 0;
+ int available_recv_bandwidth = 0;
+ int target_enc_bitrate = 0;
+ int actual_enc_bitrate = 0;
+ int retransmit_bitrate = 0;
+ int transmit_bitrate = 0;
+ int64_t bucket_delay = 0;
+};
+
+// Maps from payload type to `RtpCodecParameters`.
+typedef std::map<int, webrtc::RtpCodecParameters> RtpCodecParametersMap;
+
+// Stats returned from VoiceMediaSendChannel.GetStats()
+struct VoiceMediaSendInfo {
+ VoiceMediaSendInfo();
+ ~VoiceMediaSendInfo();
+ void Clear() {
+ senders.clear();
+ send_codecs.clear();
+ }
+ std::vector<VoiceSenderInfo> senders;
+ RtpCodecParametersMap send_codecs;
+};
+
+// Stats returned from VoiceMediaReceiveChannel.GetStats()
+struct VoiceMediaReceiveInfo {
+ VoiceMediaReceiveInfo();
+ ~VoiceMediaReceiveInfo();
+ void Clear() {
+ receivers.clear();
+ receive_codecs.clear();
+ }
+ std::vector<VoiceReceiverInfo> receivers;
+ RtpCodecParametersMap receive_codecs;
+ int32_t device_underrun_count = 0;
+};
+
+// Combined VoiceMediaSendInfo and VoiceMediaReceiveInfo
+// Returned from Transceiver.getStats()
+struct VoiceMediaInfo {
+ VoiceMediaInfo();
+ VoiceMediaInfo(VoiceMediaSendInfo&& send, VoiceMediaReceiveInfo&& receive)
+ : senders(std::move(send.senders)),
+ receivers(std::move(receive.receivers)),
+ send_codecs(std::move(send.send_codecs)),
+ receive_codecs(std::move(receive.receive_codecs)),
+ device_underrun_count(receive.device_underrun_count) {}
+ ~VoiceMediaInfo();
+ void Clear() {
+ senders.clear();
+ receivers.clear();
+ send_codecs.clear();
+ receive_codecs.clear();
+ }
+ std::vector<VoiceSenderInfo> senders;
+ std::vector<VoiceReceiverInfo> receivers;
+ RtpCodecParametersMap send_codecs;
+ RtpCodecParametersMap receive_codecs;
+ int32_t device_underrun_count = 0;
+};
+
+// Stats for a VideoMediaSendChannel
+struct VideoMediaSendInfo {
+ VideoMediaSendInfo();
+ ~VideoMediaSendInfo();
+ void Clear() {
+ senders.clear();
+ aggregated_senders.clear();
+ send_codecs.clear();
+ }
+ // Each sender info represents one "outbound-rtp" stream.In non - simulcast,
+ // this means one info per RtpSender but if simulcast is used this means
+ // one info per simulcast layer.
+ std::vector<VideoSenderInfo> senders;
+ // Used for legacy getStats() API's "ssrc" stats and modern getStats() API's
+ // "track" stats. If simulcast is used, instead of having one sender info per
+ // simulcast layer, the metrics of all layers of an RtpSender are aggregated
+ // into a single sender info per RtpSender.
+ std::vector<VideoSenderInfo> aggregated_senders;
+ RtpCodecParametersMap send_codecs;
+};
+
+// Stats for a VideoMediaReceiveChannel
+struct VideoMediaReceiveInfo {
+ VideoMediaReceiveInfo();
+ ~VideoMediaReceiveInfo();
+ void Clear() {
+ receivers.clear();
+ receive_codecs.clear();
+ }
+ std::vector<VideoReceiverInfo> receivers;
+ RtpCodecParametersMap receive_codecs;
+};
+
+// Combined VideoMediaSenderInfo and VideoMediaReceiverInfo.
+// Returned from channel.GetStats()
+struct VideoMediaInfo {
+ VideoMediaInfo();
+ VideoMediaInfo(VideoMediaSendInfo&& send, VideoMediaReceiveInfo&& receive)
+ : senders(std::move(send.senders)),
+ aggregated_senders(std::move(send.aggregated_senders)),
+ receivers(std::move(receive.receivers)),
+ send_codecs(std::move(send.send_codecs)),
+ receive_codecs(std::move(receive.receive_codecs)) {}
+ ~VideoMediaInfo();
+ void Clear() {
+ senders.clear();
+ aggregated_senders.clear();
+ receivers.clear();
+ send_codecs.clear();
+ receive_codecs.clear();
+ }
+ // Each sender info represents one "outbound-rtp" stream.In non - simulcast,
+ // this means one info per RtpSender but if simulcast is used this means
+ // one info per simulcast layer.
+ std::vector<VideoSenderInfo> senders;
+ // Used for legacy getStats() API's "ssrc" stats and modern getStats() API's
+ // "track" stats. If simulcast is used, instead of having one sender info per
+ // simulcast layer, the metrics of all layers of an RtpSender are aggregated
+ // into a single sender info per RtpSender.
+ std::vector<VideoSenderInfo> aggregated_senders;
+ std::vector<VideoReceiverInfo> receivers;
+ RtpCodecParametersMap send_codecs;
+ RtpCodecParametersMap receive_codecs;
+};
+
+struct RtcpParameters {
+ bool reduced_size = false;
+ bool remote_estimate = false;
+};
+
+template <class Codec>
+struct RtpParameters {
+ virtual ~RtpParameters() = default;
+
+ std::vector<Codec> codecs;
+ std::vector<webrtc::RtpExtension> extensions;
+ // For a send stream this is true if we've neogtiated a send direction,
+ // for a receive stream this is true if we've negotiated a receive direction.
+ bool is_stream_active = true;
+
+ // TODO(pthatcher): Add streams.
+ RtcpParameters rtcp;
+
+ std::string ToString() const {
+ rtc::StringBuilder ost;
+ ost << "{";
+ const char* separator = "";
+ for (const auto& entry : ToStringMap()) {
+ ost << separator << entry.first << ": " << entry.second;
+ separator = ", ";
+ }
+ ost << "}";
+ return ost.Release();
+ }
+
+ protected:
+ virtual std::map<std::string, std::string> ToStringMap() const {
+ return {{"codecs", VectorToString(codecs)},
+ {"extensions", VectorToString(extensions)}};
+ }
+};
+
+// TODO(deadbeef): Rename to RtpSenderParameters, since they're intended to
+// encapsulate all the parameters needed for an RtpSender.
+template <class Codec>
+struct RtpSendParameters : RtpParameters<Codec> {
+ int max_bandwidth_bps = -1;
+ // This is the value to be sent in the MID RTP header extension (if the header
+ // extension in included in the list of extensions).
+ std::string mid;
+ bool extmap_allow_mixed = false;
+
+ protected:
+ std::map<std::string, std::string> ToStringMap() const override {
+ auto params = RtpParameters<Codec>::ToStringMap();
+ params["max_bandwidth_bps"] = rtc::ToString(max_bandwidth_bps);
+ params["mid"] = (mid.empty() ? "<not set>" : mid);
+ params["extmap-allow-mixed"] = extmap_allow_mixed ? "true" : "false";
+ return params;
+ }
+};
+
+struct AudioSendParameters : RtpSendParameters<AudioCodec> {
+ AudioSendParameters();
+ ~AudioSendParameters() override;
+ AudioOptions options;
+
+ protected:
+ std::map<std::string, std::string> ToStringMap() const override;
+};
+
+struct AudioRecvParameters : RtpParameters<AudioCodec> {};
+
+class VoiceMediaSendChannelInterface : public MediaSendChannelInterface {
+ public:
+ virtual bool SetSendParameters(const AudioSendParameters& params) = 0;
+ // Starts or stops sending (and potentially capture) of local audio.
+ virtual void SetSend(bool send) = 0;
+ // Configure stream for sending.
+ virtual bool SetAudioSend(uint32_t ssrc,
+ bool enable,
+ const AudioOptions* options,
+ AudioSource* source) = 0;
+ // Returns if the telephone-event has been negotiated.
+ virtual bool CanInsertDtmf() = 0;
+ // Send a DTMF `event`. The DTMF out-of-band signal will be used.
+ // The `ssrc` should be either 0 or a valid send stream ssrc.
+ // The valid value for the `event` are 0 to 15 which corresponding to
+ // DTMF event 0-9, *, #, A-D.
+ virtual bool InsertDtmf(uint32_t ssrc, int event, int duration) = 0;
+ virtual bool GetStats(VoiceMediaSendInfo* stats) = 0;
+};
+
+class VoiceMediaReceiveChannelInterface : public MediaReceiveChannelInterface {
+ public:
+ virtual bool SetRecvParameters(const AudioRecvParameters& params) = 0;
+ // Get the receive parameters for the incoming stream identified by `ssrc`.
+ virtual webrtc::RtpParameters GetRtpReceiveParameters(
+ uint32_t ssrc) const = 0;
+ virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
+ // Retrieve the receive parameters for the default receive
+ // stream, which is used when SSRCs are not signaled.
+ virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
+ // Starts or stops playout of received audio.
+ virtual void SetPlayout(bool playout) = 0;
+ // Set speaker output volume of the specified ssrc.
+ virtual bool SetOutputVolume(uint32_t ssrc, double volume) = 0;
+ // Set speaker output volume for future unsignaled streams.
+ virtual bool SetDefaultOutputVolume(double volume) = 0;
+ virtual void SetRawAudioSink(
+ uint32_t ssrc,
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) = 0;
+ virtual void SetDefaultRawAudioSink(
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) = 0;
+ virtual bool GetStats(VoiceMediaReceiveInfo* stats, bool reset_legacy) = 0;
+};
+
+// TODO(deadbeef): Rename to VideoSenderParameters, since they're intended to
+// encapsulate all the parameters needed for a video RtpSender.
+struct VideoSendParameters : RtpSendParameters<VideoCodec> {
+ VideoSendParameters();
+ ~VideoSendParameters() override;
+ // Use conference mode? This flag comes from the remote
+ // description's SDP line 'a=x-google-flag:conference', copied over
+ // by VideoChannel::SetRemoteContent_w, and ultimately used by
+ // conference mode screencast logic in
+ // WebRtcVideoChannel::WebRtcVideoSendStream::CreateVideoEncoderConfig.
+ // The special screencast behaviour is disabled by default.
+ bool conference_mode = false;
+
+ protected:
+ std::map<std::string, std::string> ToStringMap() const override;
+};
+
+// TODO(deadbeef): Rename to VideoReceiverParameters, since they're intended to
+// encapsulate all the parameters needed for a video RtpReceiver.
+struct VideoRecvParameters : RtpParameters<VideoCodec> {};
+
+class VideoMediaSendChannelInterface : public MediaSendChannelInterface {
+ public:
+ virtual bool SetSendParameters(const VideoSendParameters& params) = 0;
+ // Gets the currently set codecs/payload types to be used for outgoing media.
+ virtual bool GetSendCodec(VideoCodec* send_codec) = 0;
+ // Starts or stops transmission (and potentially capture) of local video.
+ virtual bool SetSend(bool send) = 0;
+ // Configure stream for sending and register a source.
+ // The `ssrc` must correspond to a registered send stream.
+ virtual bool SetVideoSend(
+ uint32_t ssrc,
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) = 0;
+ // Cause generation of a keyframe for `ssrc` on a sending channel.
+ virtual void GenerateSendKeyFrame(uint32_t ssrc,
+ const std::vector<std::string>& rids) = 0;
+ // Enable network condition based codec switching.
+ virtual void SetVideoCodecSwitchingEnabled(bool enabled) = 0;
+ virtual bool GetStats(VideoMediaSendInfo* stats) = 0;
+ virtual void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) = 0;
+};
+
+class VideoMediaReceiveChannelInterface : public MediaReceiveChannelInterface {
+ public:
+ virtual bool SetRecvParameters(const VideoRecvParameters& params) = 0;
+ // Get the receive parameters for the incoming stream identified by `ssrc`.
+ virtual webrtc::RtpParameters GetRtpReceiveParameters(
+ uint32_t ssrc) const = 0;
+ // Retrieve the receive parameters for the default receive
+ // stream, which is used when SSRCs are not signaled.
+ virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
+ // Sets the sink object to be used for the specified stream.
+ virtual bool SetSink(uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) = 0;
+ // The sink is used for the 'default' stream.
+ virtual void SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) = 0;
+ // Request generation of a keyframe for `ssrc` on a receiving channel via
+ // RTCP feedback.
+ virtual void RequestRecvKeyFrame(uint32_t ssrc) = 0;
+
+ virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
+ // Set recordable encoded frame callback for `ssrc`
+ virtual void SetRecordableEncodedFrameCallback(
+ uint32_t ssrc,
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback) = 0;
+ // Clear recordable encoded frame callback for `ssrc`
+ virtual void ClearRecordableEncodedFrameCallback(uint32_t ssrc) = 0;
+ virtual bool GetStats(VideoMediaReceiveInfo* stats) = 0;
+};
+
+// Info about data received in DataMediaChannel. For use in
+// DataMediaChannel::SignalDataReceived and in all of the signals that
+// signal fires, on up the chain.
+struct ReceiveDataParams {
+ // The in-packet stream indentifier.
+ // SCTP data channels use SIDs.
+ int sid = 0;
+ // The type of message (binary, text, or control).
+ webrtc::DataMessageType type = webrtc::DataMessageType::kText;
+ // A per-stream value incremented per packet in the stream.
+ int seq_num = 0;
+};
+
+enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK };
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_MEDIA_CHANNEL_H_
diff --git a/third_party/libwebrtc/media/base/media_channel_impl.cc b/third_party/libwebrtc/media/base/media_channel_impl.cc
new file mode 100644
index 0000000000..0e72f47d6d
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_channel_impl.cc
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/media_channel_impl.h"
+
+#include <map>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/functional/any_invocable.h"
+#include "api/audio_options.h"
+#include "api/media_stream_interface.h"
+#include "api/rtc_error.h"
+#include "api/rtp_sender_interface.h"
+#include "api/units/time_delta.h"
+#include "api/video/video_timing.h"
+#include "api/video_codecs/scalability_mode.h"
+#include "common_video/include/quality_limitation_reason.h"
+#include "media/base/codec.h"
+#include "media/base/media_channel.h"
+#include "media/base/rtp_utils.h"
+#include "media/base/stream_params.h"
+#include "modules/rtp_rtcp/include/report_block_data.h"
+#include "rtc_base/checks.h"
+
+namespace cricket {
+using webrtc::FrameDecryptorInterface;
+using webrtc::FrameEncryptorInterface;
+using webrtc::FrameTransformerInterface;
+using webrtc::PendingTaskSafetyFlag;
+using webrtc::SafeTask;
+using webrtc::TaskQueueBase;
+using webrtc::VideoTrackInterface;
+
+VideoOptions::VideoOptions()
+ : content_hint(VideoTrackInterface::ContentHint::kNone) {}
+VideoOptions::~VideoOptions() = default;
+
+MediaChannel::MediaChannel(TaskQueueBase* network_thread, bool enable_dscp)
+ : enable_dscp_(enable_dscp),
+ network_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()),
+ network_thread_(network_thread) {}
+
+MediaChannel::~MediaChannel() {
+ RTC_DCHECK(!network_interface_);
+}
+
+void MediaChannel::SetInterface(MediaChannelNetworkInterface* iface) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ iface ? network_safety_->SetAlive() : network_safety_->SetNotAlive();
+ network_interface_ = iface;
+ UpdateDscp();
+}
+
+int MediaChannel::GetRtpSendTimeExtnId() const {
+ return -1;
+}
+
+void MediaChannel::SetFrameEncryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
+ // Placeholder should be pure virtual once internal supports it.
+}
+
+void MediaChannel::SetFrameDecryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
+ // Placeholder should be pure virtual once internal supports it.
+}
+
+bool MediaChannel::SendPacket(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options) {
+ return DoSendPacket(packet, false, options);
+}
+
+bool MediaChannel::SendRtcp(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options) {
+ return DoSendPacket(packet, true, options);
+}
+
+int MediaChannel::SetOption(MediaChannelNetworkInterface::SocketType type,
+ rtc::Socket::Option opt,
+ int option) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ return SetOptionLocked(type, opt, option);
+}
+
+// Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+// Set to true if it's allowed to mix one- and two-byte RTP header extensions
+// in the same stream. The setter and getter must only be called from
+// worker_thread.
+void MediaChannel::SetExtmapAllowMixed(bool extmap_allow_mixed) {
+ extmap_allow_mixed_ = extmap_allow_mixed;
+}
+
+bool MediaChannel::ExtmapAllowMixed() const {
+ return extmap_allow_mixed_;
+}
+
+bool MediaChannel::HasNetworkInterface() const {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ return network_interface_ != nullptr;
+}
+
+void MediaChannel::SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {}
+
+void MediaChannel::SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {}
+
+int MediaChannel::SetOptionLocked(MediaChannelNetworkInterface::SocketType type,
+ rtc::Socket::Option opt,
+ int option) {
+ if (!network_interface_)
+ return -1;
+ return network_interface_->SetOption(type, opt, option);
+}
+
+bool MediaChannel::DscpEnabled() const {
+ return enable_dscp_;
+}
+
+// This is the DSCP value used for both RTP and RTCP channels if DSCP is
+// enabled. It can be changed at any time via `SetPreferredDscp`.
+rtc::DiffServCodePoint MediaChannel::PreferredDscp() const {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ return preferred_dscp_;
+}
+
+void MediaChannel::SetPreferredDscp(rtc::DiffServCodePoint new_dscp) {
+ if (!network_thread_->IsCurrent()) {
+ // This is currently the common path as the derived channel classes
+ // get called on the worker thread. There are still some tests though
+ // that call directly on the network thread.
+ network_thread_->PostTask(SafeTask(
+ network_safety_, [this, new_dscp]() { SetPreferredDscp(new_dscp); }));
+ return;
+ }
+
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (new_dscp == preferred_dscp_)
+ return;
+
+ preferred_dscp_ = new_dscp;
+ UpdateDscp();
+}
+
+rtc::scoped_refptr<PendingTaskSafetyFlag> MediaChannel::network_safety() {
+ return network_safety_;
+}
+
+void MediaChannel::UpdateDscp() {
+ rtc::DiffServCodePoint value =
+ enable_dscp_ ? preferred_dscp_ : rtc::DSCP_DEFAULT;
+ int ret = SetOptionLocked(MediaChannelNetworkInterface::ST_RTP,
+ rtc::Socket::OPT_DSCP, value);
+ if (ret == 0)
+ SetOptionLocked(MediaChannelNetworkInterface::ST_RTCP,
+ rtc::Socket::OPT_DSCP, value);
+}
+
+bool MediaChannel::DoSendPacket(rtc::CopyOnWriteBuffer* packet,
+ bool rtcp,
+ const rtc::PacketOptions& options) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!network_interface_)
+ return false;
+
+ return (!rtcp) ? network_interface_->SendPacket(packet, options)
+ : network_interface_->SendRtcp(packet, options);
+}
+
+void MediaChannel::SendRtp(const uint8_t* data,
+ size_t len,
+ const webrtc::PacketOptions& options) {
+ auto send =
+ [this, packet_id = options.packet_id,
+ included_in_feedback = options.included_in_feedback,
+ included_in_allocation = options.included_in_allocation,
+ packet = rtc::CopyOnWriteBuffer(data, len, kMaxRtpPacketLen)]() mutable {
+ rtc::PacketOptions rtc_options;
+ rtc_options.packet_id = packet_id;
+ if (DscpEnabled()) {
+ rtc_options.dscp = PreferredDscp();
+ }
+ rtc_options.info_signaled_after_sent.included_in_feedback =
+ included_in_feedback;
+ rtc_options.info_signaled_after_sent.included_in_allocation =
+ included_in_allocation;
+ SendPacket(&packet, rtc_options);
+ };
+
+ // TODO(bugs.webrtc.org/11993): ModuleRtpRtcpImpl2 and related classes (e.g.
+ // RTCPSender) aren't aware of the network thread and may trigger calls to
+ // this function from different threads. Update those classes to keep
+ // network traffic on the network thread.
+ if (network_thread_->IsCurrent()) {
+ send();
+ } else {
+ network_thread_->PostTask(SafeTask(network_safety_, std::move(send)));
+ }
+}
+
+void MediaChannel::SendRtcp(const uint8_t* data, size_t len) {
+ auto send = [this, packet = rtc::CopyOnWriteBuffer(
+ data, len, kMaxRtpPacketLen)]() mutable {
+ rtc::PacketOptions rtc_options;
+ if (DscpEnabled()) {
+ rtc_options.dscp = PreferredDscp();
+ }
+ SendRtcp(&packet, rtc_options);
+ };
+
+ if (network_thread_->IsCurrent()) {
+ send();
+ } else {
+ network_thread_->PostTask(SafeTask(network_safety_, std::move(send)));
+ }
+}
+
+MediaSenderInfo::MediaSenderInfo() = default;
+MediaSenderInfo::~MediaSenderInfo() = default;
+
+MediaReceiverInfo::MediaReceiverInfo() = default;
+MediaReceiverInfo::~MediaReceiverInfo() = default;
+
+VoiceSenderInfo::VoiceSenderInfo() = default;
+VoiceSenderInfo::~VoiceSenderInfo() = default;
+
+VoiceReceiverInfo::VoiceReceiverInfo() = default;
+VoiceReceiverInfo::~VoiceReceiverInfo() = default;
+
+VideoSenderInfo::VideoSenderInfo() = default;
+VideoSenderInfo::~VideoSenderInfo() = default;
+
+VideoReceiverInfo::VideoReceiverInfo() = default;
+VideoReceiverInfo::~VideoReceiverInfo() = default;
+
+VoiceMediaInfo::VoiceMediaInfo() = default;
+VoiceMediaInfo::~VoiceMediaInfo() = default;
+
+VideoMediaInfo::VideoMediaInfo() = default;
+VideoMediaInfo::~VideoMediaInfo() = default;
+
+VideoMediaSendInfo::VideoMediaSendInfo() = default;
+VideoMediaSendInfo::~VideoMediaSendInfo() = default;
+
+VoiceMediaSendInfo::VoiceMediaSendInfo() = default;
+VoiceMediaSendInfo::~VoiceMediaSendInfo() = default;
+
+VideoMediaReceiveInfo::VideoMediaReceiveInfo() = default;
+VideoMediaReceiveInfo::~VideoMediaReceiveInfo() = default;
+
+VoiceMediaReceiveInfo::VoiceMediaReceiveInfo() = default;
+VoiceMediaReceiveInfo::~VoiceMediaReceiveInfo() = default;
+
+AudioSendParameters::AudioSendParameters() = default;
+AudioSendParameters::~AudioSendParameters() = default;
+
+std::map<std::string, std::string> AudioSendParameters::ToStringMap() const {
+ auto params = RtpSendParameters<AudioCodec>::ToStringMap();
+ params["options"] = options.ToString();
+ return params;
+}
+
+cricket::MediaType VoiceMediaChannel::media_type() const {
+ return cricket::MediaType::MEDIA_TYPE_AUDIO;
+}
+
+VideoSendParameters::VideoSendParameters() = default;
+VideoSendParameters::~VideoSendParameters() = default;
+
+std::map<std::string, std::string> VideoSendParameters::ToStringMap() const {
+ auto params = RtpSendParameters<VideoCodec>::ToStringMap();
+ params["conference_mode"] = (conference_mode ? "yes" : "no");
+ return params;
+}
+
+cricket::MediaType VideoMediaChannel::media_type() const {
+ return cricket::MediaType::MEDIA_TYPE_VIDEO;
+}
+
+void VideoMediaChannel::SetVideoCodecSwitchingEnabled(bool enabled) {}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/media_channel_impl.h b/third_party/libwebrtc/media/base/media_channel_impl.h
new file mode 100644
index 0000000000..8142dd45b6
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_channel_impl.h
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
+#define MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "api/audio_options.h"
+#include "api/call/audio_sink.h"
+#include "api/call/transport.h"
+#include "api/crypto/frame_decryptor_interface.h"
+#include "api/crypto/frame_encryptor_interface.h"
+#include "api/frame_transformer_interface.h"
+#include "api/media_types.h"
+#include "api/rtc_error.h"
+#include "api/rtp_parameters.h"
+#include "api/rtp_sender_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "api/task_queue/task_queue_base.h"
+#include "api/transport/rtp/rtp_source.h"
+#include "api/video/recordable_encoded_frame.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_sink_interface.h"
+#include "api/video/video_source_interface.h"
+#include "api/video_codecs/video_encoder_factory.h"
+#include "media/base/codec.h"
+#include "media/base/media_channel.h"
+#include "media/base/stream_params.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/dscp.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/network/sent_packet.h"
+#include "rtc_base/network_route.h"
+#include "rtc_base/socket.h"
+#include "rtc_base/thread_annotations.h"
+// This file contains the base classes for classes that implement
+// the MediaChannel interfaces.
+// These implementation classes used to be the exposed interface names,
+// but this is in the process of being changed.
+// TODO(bugs.webrtc.org/13931): Consider removing these classes.
+
+// The target
+
+namespace cricket {
+
+class VoiceMediaChannel;
+class VideoMediaChannel;
+
+class MediaChannel : public MediaSendChannelInterface,
+ public MediaReceiveChannelInterface {
+ public:
+ explicit MediaChannel(webrtc::TaskQueueBase* network_thread,
+ bool enable_dscp = false);
+ virtual ~MediaChannel();
+
+ // Downcasting to the subclasses.
+ virtual VideoMediaChannel* AsVideoChannel() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ virtual VoiceMediaChannel* AsVoiceChannel() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ // Must declare the methods inherited from the base interface template,
+ // even when abstract, to tell the compiler that all instances of the name
+ // referred to by subclasses of this share the same implementation.
+ cricket::MediaType media_type() const override = 0;
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override = 0;
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override = 0;
+ void OnReadyToSend(bool ready) override = 0;
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override =
+ 0;
+
+ // Sets the abstract interface class for sending RTP/RTCP data.
+ virtual void SetInterface(MediaChannelNetworkInterface* iface);
+ // Returns the absolute sendtime extension id value from media channel.
+ virtual int GetRtpSendTimeExtnId() const;
+ // Base method to send packet using MediaChannelNetworkInterface.
+ bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options);
+
+ bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options);
+
+ int SetOption(MediaChannelNetworkInterface::SocketType type,
+ rtc::Socket::Option opt,
+ int option);
+
+ // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+ // Set to true if it's allowed to mix one- and two-byte RTP header extensions
+ // in the same stream. The setter and getter must only be called from
+ // worker_thread.
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override;
+ bool ExtmapAllowMixed() const override;
+
+ // Returns `true` if a non-null MediaChannelNetworkInterface pointer is held.
+ // Must be called on the network thread.
+ bool HasNetworkInterface() const;
+
+ void SetFrameEncryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+ frame_encryptor) override;
+ void SetFrameDecryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+ frame_decryptor) override;
+
+ void SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override;
+ void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override;
+
+ protected:
+ int SetOptionLocked(MediaChannelNetworkInterface::SocketType type,
+ rtc::Socket::Option opt,
+ int option) RTC_RUN_ON(network_thread_);
+
+ bool DscpEnabled() const;
+
+ // This is the DSCP value used for both RTP and RTCP channels if DSCP is
+ // enabled. It can be changed at any time via `SetPreferredDscp`.
+ rtc::DiffServCodePoint PreferredDscp() const;
+ void SetPreferredDscp(rtc::DiffServCodePoint new_dscp);
+
+ rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety();
+
+ // Utility implementation for derived classes (video/voice) that applies
+ // the packet options and passes the data onwards to `SendPacket`.
+ void SendRtp(const uint8_t* data,
+ size_t len,
+ const webrtc::PacketOptions& options);
+
+ void SendRtcp(const uint8_t* data, size_t len);
+
+ private:
+ // Apply the preferred DSCP setting to the underlying network interface RTP
+ // and RTCP channels. If DSCP is disabled, then apply the default DSCP value.
+ void UpdateDscp() RTC_RUN_ON(network_thread_);
+
+ bool DoSendPacket(rtc::CopyOnWriteBuffer* packet,
+ bool rtcp,
+ const rtc::PacketOptions& options);
+
+ const bool enable_dscp_;
+ const rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety_
+ RTC_PT_GUARDED_BY(network_thread_);
+ webrtc::TaskQueueBase* const network_thread_;
+ MediaChannelNetworkInterface* network_interface_
+ RTC_GUARDED_BY(network_thread_) = nullptr;
+ rtc::DiffServCodePoint preferred_dscp_ RTC_GUARDED_BY(network_thread_) =
+ rtc::DSCP_DEFAULT;
+ bool extmap_allow_mixed_ = false;
+};
+
+// Base class for implementation classes
+
+class VideoMediaChannel : public MediaChannel,
+ public VideoMediaSendChannelInterface,
+ public VideoMediaReceiveChannelInterface {
+ public:
+ explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread,
+ bool enable_dscp = false)
+ : MediaChannel(network_thread, enable_dscp) {}
+ ~VideoMediaChannel() override {}
+
+ // Downcasting to the implemented interfaces.
+ VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; }
+ VoiceMediaSendChannelInterface* AsVoiceSendChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override {
+ return this;
+ }
+ cricket::MediaType media_type() const override;
+
+ // Downcasting to the subclasses.
+ VideoMediaChannel* AsVideoChannel() override { return this; }
+
+ void SetExtmapAllowMixed(bool mixed) override {
+ MediaChannel::SetExtmapAllowMixed(mixed);
+ }
+ bool ExtmapAllowMixed() const override {
+ return MediaChannel::ExtmapAllowMixed();
+ }
+ // This fills the "bitrate parts" (rtx, video bitrate) of the
+ // BandwidthEstimationInfo, since that part that isn't possible to get
+ // through webrtc::Call::GetStats, as they are statistics of the send
+ // streams.
+ // TODO(holmer): We should change this so that either BWE graphs doesn't
+ // need access to bitrates of the streams, or change the (RTC)StatsCollector
+ // so that it's getting the send stream stats separately by calling
+ // GetStats(), and merges with BandwidthEstimationInfo by itself.
+ void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override = 0;
+ // Gets quality stats for the channel.
+ virtual bool GetSendStats(VideoMediaSendInfo* info) = 0;
+ virtual bool GetReceiveStats(VideoMediaReceiveInfo* info) = 0;
+ // Enable network condition based codec switching.
+ void SetVideoCodecSwitchingEnabled(bool enabled) override;
+
+ private:
+ // Functions not implemented on this interface
+ bool GetStats(VideoMediaSendInfo* info) override {
+ RTC_CHECK_NOTREACHED();
+ return false;
+ }
+ bool GetStats(VideoMediaReceiveInfo* info) override {
+ RTC_CHECK_NOTREACHED();
+ return false;
+ }
+};
+
+// Base class for implementation classes
+class VoiceMediaChannel : public MediaChannel,
+ public VoiceMediaSendChannelInterface,
+ public VoiceMediaReceiveChannelInterface {
+ public:
+ MediaType media_type() const override;
+ VoiceMediaChannel(webrtc::TaskQueueBase* network_thread,
+ bool enable_dscp = false)
+ : MediaChannel(network_thread, enable_dscp) {}
+ ~VoiceMediaChannel() override {}
+
+ // Downcasting to the implemented interfaces.
+ VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; }
+
+ VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() override {
+ return this;
+ }
+
+ VoiceMediaChannel* AsVoiceChannel() override { return this; }
+
+ VideoMediaSendChannelInterface* AsVideoSendChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ void SetExtmapAllowMixed(bool mixed) override {
+ MediaChannel::SetExtmapAllowMixed(mixed);
+ }
+ bool ExtmapAllowMixed() const override {
+ return MediaChannel::ExtmapAllowMixed();
+ }
+
+ // Gets quality stats for the channel.
+ virtual bool GetSendStats(VoiceMediaSendInfo* info) = 0;
+ virtual bool GetReceiveStats(VoiceMediaReceiveInfo* info,
+ bool get_and_clear_legacy_stats) = 0;
+
+ private:
+ // Functions not implemented on this interface
+ bool GetStats(VoiceMediaSendInfo* info) override {
+ RTC_CHECK_NOTREACHED();
+ return false;
+ }
+ bool GetStats(VoiceMediaReceiveInfo* info,
+ bool get_and_clear_legacy_stats) override {
+ RTC_CHECK_NOTREACHED();
+ return false;
+ }
+};
+
+// The externally exposed objects that support the Send and Receive interfaces.
+// These dispatch their functions to the underlying MediaChannel objects.
+
+class VoiceMediaSendChannel : public VoiceMediaSendChannelInterface {
+ public:
+ explicit VoiceMediaSendChannel(VoiceMediaChannel* impl) : impl_(impl) {}
+ virtual ~VoiceMediaSendChannel() {}
+ VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; }
+ VideoMediaSendChannelInterface* AsVideoSendChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ // Implementation of MediaBaseChannelInterface
+ cricket::MediaType media_type() const override { return MEDIA_TYPE_AUDIO; }
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override {
+ impl()->OnPacketReceived(packet);
+ }
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override {
+ impl()->OnPacketSent(sent_packet);
+ }
+ void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); }
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override {
+ impl()->OnNetworkRouteChanged(transport_name, network_route);
+ }
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+ impl()->SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); }
+ // Implementation of MediaSendChannelInterface
+ bool AddSendStream(const StreamParams& sp) override {
+ return impl()->AddSendStream(sp);
+ }
+ bool RemoveSendStream(uint32_t ssrc) override {
+ return impl()->RemoveSendStream(ssrc);
+ }
+ void SetFrameEncryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+ frame_encryptor) override {
+ impl()->SetFrameEncryptor(ssrc, frame_encryptor);
+ }
+ webrtc::RTCError SetRtpSendParameters(
+ uint32_t ssrc,
+ const webrtc::RtpParameters& parameters,
+ webrtc::SetParametersCallback callback = nullptr) override {
+ return impl()->SetRtpSendParameters(ssrc, parameters, std::move(callback));
+ }
+
+ void SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {
+ return impl()->SetEncoderToPacketizerFrameTransformer(ssrc,
+ frame_transformer);
+ }
+ void SetEncoderSelector(uint32_t ssrc,
+ webrtc::VideoEncoderFactory::EncoderSelectorInterface*
+ encoder_selector) override {
+ impl()->SetEncoderSelector(ssrc, encoder_selector);
+ }
+ webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override {
+ return impl()->GetRtpSendParameters(ssrc);
+ }
+ // Implementation of VoiceMediaSendChannel
+ bool SetSendParameters(const AudioSendParameters& params) override {
+ return impl()->SetSendParameters(params);
+ }
+ void SetSend(bool send) override { return impl()->SetSend(send); }
+ bool SetAudioSend(uint32_t ssrc,
+ bool enable,
+ const AudioOptions* options,
+ AudioSource* source) override {
+ return impl()->SetAudioSend(ssrc, enable, options, source);
+ }
+ bool CanInsertDtmf() override { return impl()->CanInsertDtmf(); }
+ bool InsertDtmf(uint32_t ssrc, int event, int duration) override {
+ return impl()->InsertDtmf(ssrc, event, duration);
+ }
+ bool GetStats(VoiceMediaSendInfo* info) override {
+ return impl_->GetSendStats(info);
+ }
+
+ private:
+ VoiceMediaSendChannelInterface* impl() { return impl_; }
+ const VoiceMediaSendChannelInterface* impl() const { return impl_; }
+ VoiceMediaChannel* impl_;
+};
+
+class VoiceMediaReceiveChannel : public VoiceMediaReceiveChannelInterface {
+ public:
+ explicit VoiceMediaReceiveChannel(VoiceMediaChannel* impl) : impl_(impl) {}
+ virtual ~VoiceMediaReceiveChannel() {}
+ // Implementation of MediaBaseChannelInterface
+ cricket::MediaType media_type() const override { return MEDIA_TYPE_AUDIO; }
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override {
+ impl()->OnPacketReceived(packet);
+ }
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override {
+ impl()->OnPacketSent(sent_packet);
+ }
+ void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); }
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override {
+ impl()->OnNetworkRouteChanged(transport_name, network_route);
+ }
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+ impl()->SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); }
+ // Implementation of Delayable
+ bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override {
+ return impl()->SetBaseMinimumPlayoutDelayMs(ssrc, delay_ms);
+ }
+ absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const override {
+ return impl()->GetBaseMinimumPlayoutDelayMs(ssrc);
+ }
+ // Implementation of MediaReceiveChannelInterface
+ bool AddRecvStream(const StreamParams& sp) override {
+ return impl()->AddRecvStream(sp);
+ }
+ bool RemoveRecvStream(uint32_t ssrc) override {
+ return impl()->RemoveRecvStream(ssrc);
+ }
+ void ResetUnsignaledRecvStream() override {
+ return impl()->ResetUnsignaledRecvStream();
+ }
+ absl::optional<uint32_t> GetUnsignaledSsrc() const override {
+ return impl()->GetUnsignaledSsrc();
+ }
+ void OnDemuxerCriteriaUpdatePending() override {
+ impl()->OnDemuxerCriteriaUpdatePending();
+ }
+ void OnDemuxerCriteriaUpdateComplete() override {
+ impl()->OnDemuxerCriteriaUpdateComplete();
+ }
+ void SetFrameDecryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+ frame_decryptor) override {
+ impl()->SetFrameDecryptor(ssrc, frame_decryptor);
+ }
+ void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {
+ impl()->SetDepacketizerToDecoderFrameTransformer(ssrc, frame_transformer);
+ }
+ // Implementation of VoiceMediaReceiveChannelInterface
+ bool SetRecvParameters(const AudioRecvParameters& params) override {
+ return impl()->SetRecvParameters(params);
+ }
+ webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override {
+ return impl()->GetRtpReceiveParameters(ssrc);
+ }
+ std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override {
+ return impl()->GetSources(ssrc);
+ }
+ webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override {
+ return impl()->GetDefaultRtpReceiveParameters();
+ }
+ void SetPlayout(bool playout) override { return impl()->SetPlayout(playout); }
+ bool SetOutputVolume(uint32_t ssrc, double volume) override {
+ return impl()->SetOutputVolume(ssrc, volume);
+ }
+ bool SetDefaultOutputVolume(double volume) override {
+ return impl()->SetDefaultOutputVolume(volume);
+ }
+ void SetRawAudioSink(
+ uint32_t ssrc,
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) override {
+ return impl()->SetRawAudioSink(ssrc, std::move(sink));
+ }
+ void SetDefaultRawAudioSink(
+ std::unique_ptr<webrtc::AudioSinkInterface> sink) override {
+ return impl()->SetDefaultRawAudioSink(std::move(sink));
+ }
+ bool GetStats(VoiceMediaReceiveInfo* info, bool reset_legacy) override {
+ return impl_->GetReceiveStats(info, reset_legacy);
+ }
+
+ private:
+ VoiceMediaReceiveChannelInterface* impl() { return impl_; }
+ const VoiceMediaReceiveChannelInterface* impl() const { return impl_; }
+ VoiceMediaChannel* impl_;
+};
+
+class VideoMediaSendChannel : public VideoMediaSendChannelInterface {
+ public:
+ explicit VideoMediaSendChannel(VideoMediaChannel* impl) : impl_(impl) {}
+
+ VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; }
+ VoiceMediaSendChannelInterface* AsVoiceSendChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+
+ // Implementation of MediaBaseChannelInterface
+ cricket::MediaType media_type() const override { return MEDIA_TYPE_VIDEO; }
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override {
+ impl()->OnPacketReceived(packet);
+ }
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override {
+ impl()->OnPacketSent(sent_packet);
+ }
+ void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); }
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override {
+ impl()->OnNetworkRouteChanged(transport_name, network_route);
+ }
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+ impl()->SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); }
+ // Implementation of MediaSendChannelInterface
+ bool AddSendStream(const StreamParams& sp) override {
+ return impl()->AddSendStream(sp);
+ }
+ bool RemoveSendStream(uint32_t ssrc) override {
+ return impl()->RemoveSendStream(ssrc);
+ }
+ void SetFrameEncryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+ frame_encryptor) override {
+ impl()->SetFrameEncryptor(ssrc, frame_encryptor);
+ }
+ webrtc::RTCError SetRtpSendParameters(
+ uint32_t ssrc,
+ const webrtc::RtpParameters& parameters,
+ webrtc::SetParametersCallback callback = nullptr) override {
+ return impl()->SetRtpSendParameters(ssrc, parameters, std::move(callback));
+ }
+
+ void SetEncoderToPacketizerFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {
+ return impl()->SetEncoderToPacketizerFrameTransformer(ssrc,
+ frame_transformer);
+ }
+ void SetEncoderSelector(uint32_t ssrc,
+ webrtc::VideoEncoderFactory::EncoderSelectorInterface*
+ encoder_selector) override {
+ impl()->SetEncoderSelector(ssrc, encoder_selector);
+ }
+ webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override {
+ return impl()->GetRtpSendParameters(ssrc);
+ }
+ // Implementation of VideoMediaSendChannelInterface
+ bool SetSendParameters(const VideoSendParameters& params) override {
+ return impl()->SetSendParameters(params);
+ }
+ bool GetSendCodec(VideoCodec* send_codec) override {
+ return impl()->GetSendCodec(send_codec);
+ }
+ bool SetSend(bool send) override { return impl()->SetSend(send); }
+ bool SetVideoSend(
+ uint32_t ssrc,
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) override {
+ return impl()->SetVideoSend(ssrc, options, source);
+ }
+ void GenerateSendKeyFrame(uint32_t ssrc,
+ const std::vector<std::string>& rids) override {
+ return impl()->GenerateSendKeyFrame(ssrc, rids);
+ }
+ void SetVideoCodecSwitchingEnabled(bool enabled) override {
+ return impl()->SetVideoCodecSwitchingEnabled(enabled);
+ }
+ bool GetStats(VideoMediaSendInfo* info) override {
+ return impl_->GetSendStats(info);
+ }
+ void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override {
+ return impl_->FillBitrateInfo(bwe_info);
+ }
+
+ private:
+ VideoMediaSendChannelInterface* impl() { return impl_; }
+ const VideoMediaSendChannelInterface* impl() const { return impl_; }
+ VideoMediaChannel* const impl_;
+};
+
+class VideoMediaReceiveChannel : public VideoMediaReceiveChannelInterface {
+ public:
+ explicit VideoMediaReceiveChannel(VideoMediaChannel* impl) : impl_(impl) {}
+ // Implementation of MediaBaseChannelInterface
+ cricket::MediaType media_type() const override { return MEDIA_TYPE_VIDEO; }
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override {
+ impl()->OnPacketReceived(packet);
+ }
+ void OnPacketSent(const rtc::SentPacket& sent_packet) override {
+ impl()->OnPacketSent(sent_packet);
+ }
+ void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); }
+ void OnNetworkRouteChanged(absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) override {
+ impl()->OnNetworkRouteChanged(transport_name, network_route);
+ }
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+ impl()->SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); }
+ // Implementation of Delayable
+ bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override {
+ return impl()->SetBaseMinimumPlayoutDelayMs(ssrc, delay_ms);
+ }
+ absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const override {
+ return impl()->GetBaseMinimumPlayoutDelayMs(ssrc);
+ }
+ // Implementation of MediaReceiveChannelInterface
+ VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override {
+ return this;
+ }
+ bool AddRecvStream(const StreamParams& sp) override {
+ return impl()->AddRecvStream(sp);
+ }
+ bool RemoveRecvStream(uint32_t ssrc) override {
+ return impl()->RemoveRecvStream(ssrc);
+ }
+ void ResetUnsignaledRecvStream() override {
+ return impl()->ResetUnsignaledRecvStream();
+ }
+ absl::optional<uint32_t> GetUnsignaledSsrc() const override {
+ return impl()->GetUnsignaledSsrc();
+ }
+ void OnDemuxerCriteriaUpdatePending() override {
+ impl()->OnDemuxerCriteriaUpdatePending();
+ }
+ void OnDemuxerCriteriaUpdateComplete() override {
+ impl()->OnDemuxerCriteriaUpdateComplete();
+ }
+ void SetFrameDecryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+ frame_decryptor) override {
+ impl()->SetFrameDecryptor(ssrc, frame_decryptor);
+ }
+ void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override {
+ impl()->SetDepacketizerToDecoderFrameTransformer(ssrc, frame_transformer);
+ }
+ // Implementation on videoMediaReceiveChannelInterface
+ bool SetRecvParameters(const VideoRecvParameters& params) override {
+ return impl()->SetRecvParameters(params);
+ }
+ webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override {
+ return impl()->GetRtpReceiveParameters(ssrc);
+ }
+ webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override {
+ return impl()->GetDefaultRtpReceiveParameters();
+ }
+ bool SetSink(uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override {
+ return impl()->SetSink(ssrc, sink);
+ }
+ void SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override {
+ return impl()->SetDefaultSink(sink);
+ }
+ void RequestRecvKeyFrame(uint32_t ssrc) override {
+ return impl()->RequestRecvKeyFrame(ssrc);
+ }
+ std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override {
+ return impl()->GetSources(ssrc);
+ }
+ // Set recordable encoded frame callback for `ssrc`
+ void SetRecordableEncodedFrameCallback(
+ uint32_t ssrc,
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback)
+ override {
+ return impl()->SetRecordableEncodedFrameCallback(ssrc, std::move(callback));
+ }
+ // Clear recordable encoded frame callback for `ssrc`
+ void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override {
+ impl()->ClearRecordableEncodedFrameCallback(ssrc);
+ }
+ bool GetStats(VideoMediaReceiveInfo* info) override {
+ return impl_->GetReceiveStats(info);
+ }
+
+ private:
+ VideoMediaReceiveChannelInterface* impl() { return impl_; }
+ const VideoMediaReceiveChannelInterface* impl() const { return impl_; }
+ VideoMediaChannel* const impl_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
diff --git a/third_party/libwebrtc/media/base/media_config.h b/third_party/libwebrtc/media/base/media_config.h
new file mode 100644
index 0000000000..b383c9aa3d
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_config.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_CONFIG_H_
+#define MEDIA_BASE_MEDIA_CONFIG_H_
+
+namespace cricket {
+
+// Construction-time settings, passed on when creating
+// MediaChannels.
+struct MediaConfig {
+ // Set DSCP value on packets. This flag comes from the
+ // PeerConnection constraint 'googDscp'.
+ // TODO(https://crbug.com/1315574): Remove the ability to set it in Chromium
+ // and delete this flag.
+ bool enable_dscp = true;
+
+ // Video-specific config.
+ struct Video {
+ // Enable WebRTC CPU Overuse Detection. This flag comes from the
+ // PeerConnection constraint 'googCpuOveruseDetection'.
+ // TODO(https://crbug.com/1315569): Remove the ability to set it in Chromium
+ // and delete this flag.
+ bool enable_cpu_adaptation = true;
+
+ // Enable WebRTC suspension of video. No video frames will be sent
+ // when the bitrate is below the configured minimum bitrate. This
+ // flag comes from the PeerConnection constraint
+ // 'googSuspendBelowMinBitrate', and WebRtcVideoChannel copies it
+ // to VideoSendStream::Config::suspend_below_min_bitrate.
+ // TODO(https://crbug.com/1315564): Remove the ability to set it in Chromium
+ // and delete this flag.
+ bool suspend_below_min_bitrate = false;
+
+ // Enable buffering and playout timing smoothing of decoded frames.
+ // If set to true, then WebRTC will buffer and potentially drop decoded
+ // frames in order to keep a smooth rendering.
+ // If set to false, then WebRTC will hand over the frame from the decoder
+ // to the renderer as soon as possible, meaning that the renderer is
+ // responsible for smooth rendering.
+ // Note that even if this flag is set to false, dropping of frames can
+ // still happen pre-decode, e.g., dropping of higher temporal layers.
+ // This flag comes from the PeerConnection RtcConfiguration.
+ bool enable_prerenderer_smoothing = true;
+
+ // Enables periodic bandwidth probing in application-limited region.
+ bool periodic_alr_bandwidth_probing = false;
+
+ // Enables the new method to estimate the cpu load from encoding, used for
+ // cpu adaptation. This flag is intended to be controlled primarily by a
+ // Chrome origin-trial.
+ // TODO(bugs.webrtc.org/8504): If all goes well, the flag will be removed
+ // together with the old method of estimation.
+ bool experiment_cpu_load_estimator = false;
+
+ // Time interval between RTCP report for video
+ int rtcp_report_interval_ms = 1000;
+ } video;
+
+ // Audio-specific config.
+ struct Audio {
+ // Time interval between RTCP report for audio
+ int rtcp_report_interval_ms = 5000;
+ } audio;
+
+ bool operator==(const MediaConfig& o) const {
+ return enable_dscp == o.enable_dscp &&
+ video.enable_cpu_adaptation == o.video.enable_cpu_adaptation &&
+ video.suspend_below_min_bitrate ==
+ o.video.suspend_below_min_bitrate &&
+ video.enable_prerenderer_smoothing ==
+ o.video.enable_prerenderer_smoothing &&
+ video.periodic_alr_bandwidth_probing ==
+ o.video.periodic_alr_bandwidth_probing &&
+ video.experiment_cpu_load_estimator ==
+ o.video.experiment_cpu_load_estimator &&
+ video.rtcp_report_interval_ms == o.video.rtcp_report_interval_ms &&
+ audio.rtcp_report_interval_ms == o.audio.rtcp_report_interval_ms;
+ }
+
+ bool operator!=(const MediaConfig& o) const { return !(*this == o); }
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_MEDIA_CONFIG_H_
diff --git a/third_party/libwebrtc/media/base/media_constants.cc b/third_party/libwebrtc/media/base/media_constants.cc
new file mode 100644
index 0000000000..2f29a2036b
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_constants.cc
@@ -0,0 +1,128 @@
+/*
+ * 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 "media/base/media_constants.h"
+
+namespace cricket {
+
+const int kVideoCodecClockrate = 90000;
+
+const int kVideoMtu = 1200;
+const int kVideoRtpSendBufferSize = 65536;
+const int kVideoRtpRecvBufferSize = 262144;
+
+const float kHighSystemCpuThreshold = 0.85f;
+const float kLowSystemCpuThreshold = 0.65f;
+const float kProcessCpuThreshold = 0.10f;
+
+const char kRedCodecName[] = "red";
+const char kUlpfecCodecName[] = "ulpfec";
+const char kMultiplexCodecName[] = "multiplex";
+
+// TODO(brandtr): Change this to 'flexfec' when we are confident that the
+// header format is not changing anymore.
+const char kFlexfecCodecName[] = "flexfec-03";
+
+// draft-ietf-payload-flexible-fec-scheme-02.txt
+const char kFlexfecFmtpRepairWindow[] = "repair-window";
+
+// RFC 4588 RTP Retransmission Payload Format
+const char kRtxCodecName[] = "rtx";
+const char kCodecParamRtxTime[] = "rtx-time";
+const char kCodecParamAssociatedPayloadType[] = "apt";
+
+const char kCodecParamAssociatedCodecName[] = "acn";
+// Parameters that do not follow the key-value convention
+// are treated as having the empty string as key.
+const char kCodecParamNotInNameValueFormat[] = "";
+
+const char kOpusCodecName[] = "opus";
+const char kIsacCodecName[] = "ISAC";
+const char kL16CodecName[] = "L16";
+const char kG722CodecName[] = "G722";
+const char kIlbcCodecName[] = "ILBC";
+const char kPcmuCodecName[] = "PCMU";
+const char kPcmaCodecName[] = "PCMA";
+const char kCnCodecName[] = "CN";
+const char kDtmfCodecName[] = "telephone-event";
+
+// draft-spittka-payload-rtp-opus-03.txt
+const char kCodecParamPTime[] = "ptime";
+const char kCodecParamMaxPTime[] = "maxptime";
+const char kCodecParamMinPTime[] = "minptime";
+const char kCodecParamSPropStereo[] = "sprop-stereo";
+const char kCodecParamStereo[] = "stereo";
+const char kCodecParamUseInbandFec[] = "useinbandfec";
+const char kCodecParamUseDtx[] = "usedtx";
+const char kCodecParamMaxAverageBitrate[] = "maxaveragebitrate";
+const char kCodecParamMaxPlaybackRate[] = "maxplaybackrate";
+
+const char kParamValueTrue[] = "1";
+const char kParamValueEmpty[] = "";
+
+const int kOpusDefaultMaxPTime = 120;
+const int kOpusDefaultPTime = 20;
+const int kOpusDefaultMinPTime = 3;
+const int kOpusDefaultSPropStereo = 0;
+const int kOpusDefaultStereo = 0;
+const int kOpusDefaultUseInbandFec = 0;
+const int kOpusDefaultUseDtx = 0;
+const int kOpusDefaultMaxPlaybackRate = 48000;
+
+const int kPreferredMaxPTime = 120;
+const int kPreferredMinPTime = 10;
+const int kPreferredSPropStereo = 0;
+const int kPreferredStereo = 0;
+const int kPreferredUseInbandFec = 0;
+
+const char kPacketizationParamRaw[] = "raw";
+
+const char kRtcpFbParamLntf[] = "goog-lntf";
+const char kRtcpFbParamNack[] = "nack";
+const char kRtcpFbNackParamPli[] = "pli";
+const char kRtcpFbParamRemb[] = "goog-remb";
+const char kRtcpFbParamTransportCc[] = "transport-cc";
+
+const char kRtcpFbParamCcm[] = "ccm";
+const char kRtcpFbCcmParamFir[] = "fir";
+const char kRtcpFbParamRrtr[] = "rrtr";
+const char kCodecParamMaxBitrate[] = "x-google-max-bitrate";
+const char kCodecParamMinBitrate[] = "x-google-min-bitrate";
+const char kCodecParamStartBitrate[] = "x-google-start-bitrate";
+const char kCodecParamMaxQuantization[] = "x-google-max-quantization";
+
+const char kComfortNoiseCodecName[] = "CN";
+
+const char kVp8CodecName[] = "VP8";
+const char kVp9CodecName[] = "VP9";
+const char kAv1CodecName[] = "AV1";
+const char kH264CodecName[] = "H264";
+
+// RFC 6184 RTP Payload Format for H.264 video
+const char kH264FmtpProfileLevelId[] = "profile-level-id";
+const char kH264FmtpLevelAsymmetryAllowed[] = "level-asymmetry-allowed";
+const char kH264FmtpPacketizationMode[] = "packetization-mode";
+const char kH264FmtpSpropParameterSets[] = "sprop-parameter-sets";
+const char kH264FmtpSpsPpsIdrInKeyframe[] = "sps-pps-idr-in-keyframe";
+const char kH264ProfileLevelConstrainedBaseline[] = "42e01f";
+const char kH264ProfileLevelConstrainedHigh[] = "640c1f";
+
+const char kVP9ProfileId[] = "profile-id";
+
+const int kDefaultVideoMaxFramerate = 60;
+
+const size_t kConferenceMaxNumSpatialLayers = 3;
+const size_t kConferenceMaxNumTemporalLayers = 3;
+const size_t kConferenceDefaultNumTemporalLayers = 3;
+
+// RFC 3556 and RFC 3890
+const char kApplicationSpecificBandwidth[] = "AS";
+const char kTransportSpecificBandwidth[] = "TIAS";
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/media_constants.h b/third_party/libwebrtc/media/base/media_constants.h
new file mode 100644
index 0000000000..f843d50ce5
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_constants.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_CONSTANTS_H_
+#define MEDIA_BASE_MEDIA_CONSTANTS_H_
+
+#include <stddef.h>
+
+#include "rtc_base/system/rtc_export.h"
+
+// This file contains constants related to media.
+
+namespace cricket {
+
+extern const int kVideoCodecClockrate;
+
+extern const int kVideoMtu;
+extern const int kVideoRtpSendBufferSize;
+extern const int kVideoRtpRecvBufferSize;
+
+// Default CPU thresholds.
+extern const float kHighSystemCpuThreshold;
+extern const float kLowSystemCpuThreshold;
+extern const float kProcessCpuThreshold;
+
+extern const char kRedCodecName[];
+extern const char kUlpfecCodecName[];
+extern const char kFlexfecCodecName[];
+extern const char kMultiplexCodecName[];
+
+extern const char kFlexfecFmtpRepairWindow[];
+
+extern const char kRtxCodecName[];
+extern const char kCodecParamRtxTime[];
+extern const char kCodecParamAssociatedPayloadType[];
+
+extern const char kCodecParamAssociatedCodecName[];
+extern const char kCodecParamNotInNameValueFormat[];
+
+extern const char kOpusCodecName[];
+extern const char kIsacCodecName[];
+extern const char kL16CodecName[];
+extern const char kG722CodecName[];
+extern const char kIlbcCodecName[];
+extern const char kPcmuCodecName[];
+extern const char kPcmaCodecName[];
+extern const char kCnCodecName[];
+extern const char kDtmfCodecName[];
+
+// Attribute parameters
+extern const char kCodecParamPTime[];
+extern const char kCodecParamMaxPTime[];
+// fmtp parameters
+extern const char kCodecParamMinPTime[];
+extern const char kCodecParamSPropStereo[];
+extern const char kCodecParamStereo[];
+extern const char kCodecParamUseInbandFec[];
+extern const char kCodecParamUseDtx[];
+extern const char kCodecParamMaxAverageBitrate[];
+extern const char kCodecParamMaxPlaybackRate[];
+
+extern const char kParamValueTrue[];
+// Parameters are stored as parameter/value pairs. For parameters who do not
+// have a value, `kParamValueEmpty` should be used as value.
+extern const char kParamValueEmpty[];
+
+// opus parameters.
+// Default value for maxptime according to
+// http://tools.ietf.org/html/draft-spittka-payload-rtp-opus-03
+extern const int kOpusDefaultMaxPTime;
+extern const int kOpusDefaultPTime;
+extern const int kOpusDefaultMinPTime;
+extern const int kOpusDefaultSPropStereo;
+extern const int kOpusDefaultStereo;
+extern const int kOpusDefaultUseInbandFec;
+extern const int kOpusDefaultUseDtx;
+extern const int kOpusDefaultMaxPlaybackRate;
+
+// Prefered values in this code base. Note that they may differ from the default
+// values in http://tools.ietf.org/html/draft-spittka-payload-rtp-opus-03
+// Only frames larger or equal to 10 ms are currently supported in this code
+// base.
+extern const int kPreferredMaxPTime;
+extern const int kPreferredMinPTime;
+extern const int kPreferredSPropStereo;
+extern const int kPreferredStereo;
+extern const int kPreferredUseInbandFec;
+
+extern const char kPacketizationParamRaw[];
+
+// rtcp-fb message in its first experimental stages. Documentation pending.
+extern const char kRtcpFbParamLntf[];
+// rtcp-fb messages according to RFC 4585
+extern const char kRtcpFbParamNack[];
+extern const char kRtcpFbNackParamPli[];
+// rtcp-fb messages according to
+// http://tools.ietf.org/html/draft-alvestrand-rmcat-remb-00
+extern const char kRtcpFbParamRemb[];
+// rtcp-fb messages according to
+// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
+extern const char kRtcpFbParamTransportCc[];
+// ccm submessages according to RFC 5104
+extern const char kRtcpFbParamCcm[];
+extern const char kRtcpFbCcmParamFir[];
+// Receiver reference time report
+// https://tools.ietf.org/html/rfc3611 section 4.4
+extern const char kRtcpFbParamRrtr[];
+// Google specific parameters
+extern const char kCodecParamMaxBitrate[];
+extern const char kCodecParamMinBitrate[];
+extern const char kCodecParamStartBitrate[];
+extern const char kCodecParamMaxQuantization[];
+
+extern const char kComfortNoiseCodecName[];
+
+RTC_EXPORT extern const char kVp8CodecName[];
+RTC_EXPORT extern const char kVp9CodecName[];
+RTC_EXPORT extern const char kAv1CodecName[];
+RTC_EXPORT extern const char kH264CodecName[];
+
+// RFC 6184 RTP Payload Format for H.264 video
+RTC_EXPORT extern const char kH264FmtpProfileLevelId[];
+RTC_EXPORT extern const char kH264FmtpLevelAsymmetryAllowed[];
+RTC_EXPORT extern const char kH264FmtpPacketizationMode[];
+extern const char kH264FmtpSpropParameterSets[];
+extern const char kH264FmtpSpsPpsIdrInKeyframe[];
+extern const char kH264ProfileLevelConstrainedBaseline[];
+extern const char kH264ProfileLevelConstrainedHigh[];
+
+extern const char kVP9ProfileId[];
+
+extern const int kDefaultVideoMaxFramerate;
+
+extern const size_t kConferenceMaxNumSpatialLayers;
+extern const size_t kConferenceMaxNumTemporalLayers;
+extern const size_t kConferenceDefaultNumTemporalLayers;
+
+extern const char kApplicationSpecificBandwidth[];
+extern const char kTransportSpecificBandwidth[];
+} // namespace cricket
+
+#endif // MEDIA_BASE_MEDIA_CONSTANTS_H_
diff --git a/third_party/libwebrtc/media/base/media_engine.cc b/third_party/libwebrtc/media/base/media_engine.cc
new file mode 100644
index 0000000000..0efbd71bf7
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_engine.cc
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/media_engine.h"
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "absl/algorithm/container.h"
+#include "api/video/video_bitrate_allocation.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/string_encode.h"
+
+namespace cricket {
+
+RtpCapabilities::RtpCapabilities() = default;
+RtpCapabilities::~RtpCapabilities() = default;
+
+webrtc::RtpParameters CreateRtpParametersWithOneEncoding() {
+ webrtc::RtpParameters parameters;
+ webrtc::RtpEncodingParameters encoding;
+ parameters.encodings.push_back(encoding);
+ return parameters;
+}
+
+webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) {
+ std::vector<uint32_t> primary_ssrcs;
+ sp.GetPrimarySsrcs(&primary_ssrcs);
+ size_t encoding_count = primary_ssrcs.size();
+
+ std::vector<webrtc::RtpEncodingParameters> encodings(encoding_count);
+ for (size_t i = 0; i < encodings.size(); ++i) {
+ encodings[i].ssrc = primary_ssrcs[i];
+ }
+
+ const std::vector<RidDescription>& rids = sp.rids();
+ RTC_DCHECK(rids.size() == 0 || rids.size() == encoding_count);
+ for (size_t i = 0; i < rids.size(); ++i) {
+ encodings[i].rid = rids[i].rid;
+ }
+
+ webrtc::RtpParameters parameters;
+ parameters.encodings = encodings;
+ parameters.rtcp.cname = sp.cname;
+ return parameters;
+}
+
+std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
+ const RtpHeaderExtensionQueryInterface& query_interface) {
+ std::vector<webrtc::RtpExtension> extensions;
+ for (const auto& entry : query_interface.GetRtpHeaderExtensions()) {
+ if (entry.direction != webrtc::RtpTransceiverDirection::kStopped)
+ extensions.emplace_back(entry.uri, *entry.preferred_id);
+ }
+ return extensions;
+}
+
+webrtc::RTCError CheckScalabilityModeValues(
+ const webrtc::RtpParameters& rtp_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs) {
+ using webrtc::RTCErrorType;
+
+ if (codecs.empty()) {
+ // This is an audio sender or an extra check in the stack where the codec
+ // list is not available and we can't check the scalability_mode values.
+ return webrtc::RTCError::OK();
+ }
+
+ for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
+ if (rtp_parameters.encodings[i].scalability_mode) {
+ bool scalabilityModeFound = false;
+ for (const cricket::VideoCodec& codec : codecs) {
+ for (const auto& scalability_mode : codec.scalability_modes) {
+ if (ScalabilityModeToString(scalability_mode) ==
+ *rtp_parameters.encodings[i].scalability_mode) {
+ scalabilityModeFound = true;
+ break;
+ }
+ }
+ if (scalabilityModeFound)
+ break;
+ }
+
+ if (!scalabilityModeFound) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Attempted to set RtpParameters scalabilityMode "
+ "to an unsupported value for the current codecs.");
+ }
+ }
+ }
+
+ return webrtc::RTCError::OK();
+}
+
+webrtc::RTCError CheckRtpParametersValues(
+ const webrtc::RtpParameters& rtp_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs) {
+ using webrtc::RTCErrorType;
+
+ for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
+ if (rtp_parameters.encodings[i].bitrate_priority <= 0) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters bitrate_priority to "
+ "an invalid number. bitrate_priority must be > 0.");
+ }
+ if (rtp_parameters.encodings[i].scale_resolution_down_by &&
+ *rtp_parameters.encodings[i].scale_resolution_down_by < 1.0) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters scale_resolution_down_by to an "
+ "invalid value. scale_resolution_down_by must be >= 1.0");
+ }
+ if (rtp_parameters.encodings[i].max_framerate &&
+ *rtp_parameters.encodings[i].max_framerate < 0.0) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters max_framerate to an "
+ "invalid value. max_framerate must be >= 0.0");
+ }
+ if (rtp_parameters.encodings[i].min_bitrate_bps &&
+ rtp_parameters.encodings[i].max_bitrate_bps) {
+ if (*rtp_parameters.encodings[i].max_bitrate_bps <
+ *rtp_parameters.encodings[i].min_bitrate_bps) {
+ LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters min bitrate "
+ "larger than max bitrate.");
+ }
+ }
+ if (rtp_parameters.encodings[i].num_temporal_layers) {
+ if (*rtp_parameters.encodings[i].num_temporal_layers < 1 ||
+ *rtp_parameters.encodings[i].num_temporal_layers >
+ webrtc::kMaxTemporalStreams) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters "
+ "num_temporal_layers to an invalid number.");
+ }
+ }
+
+ if (rtp_parameters.encodings[i].requested_resolution &&
+ rtp_parameters.encodings[i].scale_resolution_down_by) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Attempted to set scale_resolution_down_by and "
+ "requested_resolution simultaniously.");
+ }
+ }
+
+ return CheckScalabilityModeValues(rtp_parameters, codecs);
+}
+
+webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
+ const webrtc::RtpParameters& old_rtp_parameters,
+ const webrtc::RtpParameters& rtp_parameters) {
+ return CheckRtpParametersInvalidModificationAndValues(old_rtp_parameters,
+ rtp_parameters, {});
+}
+
+webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
+ const webrtc::RtpParameters& old_rtp_parameters,
+ const webrtc::RtpParameters& rtp_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs) {
+ using webrtc::RTCErrorType;
+ if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ "Attempted to set RtpParameters with different encoding count");
+ }
+ if (rtp_parameters.rtcp != old_rtp_parameters.rtcp) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ "Attempted to set RtpParameters with modified RTCP parameters");
+ }
+ if (rtp_parameters.header_extensions !=
+ old_rtp_parameters.header_extensions) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ "Attempted to set RtpParameters with modified header extensions");
+ }
+ if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
+ [](const webrtc::RtpEncodingParameters& encoding1,
+ const webrtc::RtpEncodingParameters& encoding2) {
+ return encoding1.rid == encoding2.rid;
+ })) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
+ "Attempted to change RID values in the encodings.");
+ }
+ if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
+ [](const webrtc::RtpEncodingParameters& encoding1,
+ const webrtc::RtpEncodingParameters& encoding2) {
+ return encoding1.ssrc == encoding2.ssrc;
+ })) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
+ "Attempted to set RtpParameters with modified SSRC");
+ }
+
+ return CheckRtpParametersValues(rtp_parameters, codecs);
+}
+
+CompositeMediaEngine::CompositeMediaEngine(
+ std::unique_ptr<webrtc::FieldTrialsView> trials,
+ std::unique_ptr<VoiceEngineInterface> audio_engine,
+ std::unique_ptr<VideoEngineInterface> video_engine)
+ : trials_(std::move(trials)),
+ voice_engine_(std::move(audio_engine)),
+ video_engine_(std::move(video_engine)) {}
+
+CompositeMediaEngine::CompositeMediaEngine(
+ std::unique_ptr<VoiceEngineInterface> audio_engine,
+ std::unique_ptr<VideoEngineInterface> video_engine)
+ : CompositeMediaEngine(nullptr,
+ std::move(audio_engine),
+ std::move(video_engine)) {}
+
+CompositeMediaEngine::~CompositeMediaEngine() = default;
+
+bool CompositeMediaEngine::Init() {
+ voice().Init();
+ return true;
+}
+
+VoiceEngineInterface& CompositeMediaEngine::voice() {
+ return *voice_engine_.get();
+}
+
+VideoEngineInterface& CompositeMediaEngine::video() {
+ return *video_engine_.get();
+}
+
+const VoiceEngineInterface& CompositeMediaEngine::voice() const {
+ return *voice_engine_.get();
+}
+
+const VideoEngineInterface& CompositeMediaEngine::video() const {
+ return *video_engine_.get();
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/media_engine.h b/third_party/libwebrtc/media/base/media_engine.h
new file mode 100644
index 0000000000..cc7563a85e
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_engine.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_ENGINE_H_
+#define MEDIA_BASE_MEDIA_ENGINE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/audio_codecs/audio_decoder_factory.h"
+#include "api/audio_codecs/audio_encoder_factory.h"
+#include "api/crypto/crypto_options.h"
+#include "api/field_trials_view.h"
+#include "api/rtp_parameters.h"
+#include "api/video/video_bitrate_allocator_factory.h"
+#include "call/audio_state.h"
+#include "media/base/codec.h"
+#include "media/base/media_channel.h"
+#include "media/base/media_config.h"
+#include "media/base/video_common.h"
+#include "rtc_base/system/file_wrapper.h"
+
+namespace webrtc {
+class AudioDeviceModule;
+class AudioMixer;
+class AudioProcessing;
+class Call;
+} // namespace webrtc
+
+namespace cricket {
+
+class VideoMediaChannel;
+class VoiceMediaChannel;
+
+// Checks that the scalability_mode value of each encoding is supported by at
+// least one video codec of the list. If the list is empty, no check is done.
+webrtc::RTCError CheckScalabilityModeValues(
+ const webrtc::RtpParameters& new_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs);
+
+// Checks the parameters have valid and supported values, and checks parameters
+// with CheckScalabilityModeValues().
+webrtc::RTCError CheckRtpParametersValues(
+ const webrtc::RtpParameters& new_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs);
+
+// Checks that the immutable values have not changed in new_parameters and
+// checks all parameters with CheckRtpParametersValues().
+webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
+ const webrtc::RtpParameters& old_parameters,
+ const webrtc::RtpParameters& new_parameters,
+ rtc::ArrayView<cricket::VideoCodec> codecs);
+
+// Checks that the immutable values have not changed in new_parameters and
+// checks parameters (except SVC) with CheckRtpParametersValues(). It should
+// usually be paired with a call to CheckScalabilityModeValues().
+webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
+ const webrtc::RtpParameters& old_parameters,
+ const webrtc::RtpParameters& new_parameters);
+
+struct RtpCapabilities {
+ RtpCapabilities();
+ ~RtpCapabilities();
+ std::vector<webrtc::RtpExtension> header_extensions;
+};
+
+class RtpHeaderExtensionQueryInterface {
+ public:
+ virtual ~RtpHeaderExtensionQueryInterface() = default;
+
+ // Returns a vector of RtpHeaderExtensionCapability, whose direction is
+ // kStopped if the extension is stopped (not used) by default.
+ virtual std::vector<webrtc::RtpHeaderExtensionCapability>
+ GetRtpHeaderExtensions() const = 0;
+};
+
+class VoiceEngineInterface : public RtpHeaderExtensionQueryInterface {
+ public:
+ VoiceEngineInterface() = default;
+ virtual ~VoiceEngineInterface() = default;
+
+ VoiceEngineInterface(const VoiceEngineInterface&) = delete;
+ VoiceEngineInterface& operator=(const VoiceEngineInterface&) = delete;
+
+ // Initialization
+ // Starts the engine.
+ virtual void Init() = 0;
+
+ // TODO(solenberg): Remove once VoE API refactoring is done.
+ virtual rtc::scoped_refptr<webrtc::AudioState> GetAudioState() const = 0;
+
+ // MediaChannel creation
+ // Creates a voice media channel. Returns NULL on failure.
+ virtual VoiceMediaChannel* CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const AudioOptions& options,
+ const webrtc::CryptoOptions& crypto_options) = 0;
+
+ virtual const std::vector<AudioCodec>& send_codecs() const = 0;
+ virtual const std::vector<AudioCodec>& recv_codecs() const = 0;
+
+ // Starts AEC dump using existing file, a maximum file size in bytes can be
+ // specified. Logging is stopped just before the size limit is exceeded.
+ // If max_size_bytes is set to a value <= 0, no limit will be used.
+ virtual bool StartAecDump(webrtc::FileWrapper file,
+ int64_t max_size_bytes) = 0;
+
+ // Stops recording AEC dump.
+ virtual void StopAecDump() = 0;
+
+ virtual absl::optional<webrtc::AudioDeviceModule::Stats>
+ GetAudioDeviceStats() = 0;
+};
+
+class VideoEngineInterface : public RtpHeaderExtensionQueryInterface {
+ public:
+ VideoEngineInterface() = default;
+ virtual ~VideoEngineInterface() = default;
+
+ VideoEngineInterface(const VideoEngineInterface&) = delete;
+ VideoEngineInterface& operator=(const VideoEngineInterface&) = delete;
+
+ // Creates a video media channel, paired with the specified voice channel.
+ // Returns NULL on failure.
+ virtual VideoMediaChannel* CreateMediaChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoBitrateAllocatorFactory*
+ video_bitrate_allocator_factory) = 0;
+
+ // Retrieve list of supported codecs.
+ virtual std::vector<VideoCodec> send_codecs() const = 0;
+ virtual std::vector<VideoCodec> recv_codecs() const = 0;
+ // As above, but if include_rtx is false, don't include RTX codecs.
+ // TODO(bugs.webrtc.org/13931): Remove default implementation once
+ // upstream subclasses have converted.
+ virtual std::vector<VideoCodec> send_codecs(bool include_rtx) const {
+ RTC_DCHECK(include_rtx);
+ return send_codecs();
+ }
+ virtual std::vector<VideoCodec> recv_codecs(bool include_rtx) const {
+ RTC_DCHECK(include_rtx);
+ return recv_codecs();
+ }
+};
+
+// MediaEngineInterface is an abstraction of a media engine which can be
+// subclassed to support different media componentry backends.
+// It supports voice and video operations in the same class to facilitate
+// proper synchronization between both media types.
+class MediaEngineInterface {
+ public:
+ virtual ~MediaEngineInterface() {}
+
+ // Initialization. Needs to be called on the worker thread.
+ virtual bool Init() = 0;
+
+ virtual VoiceEngineInterface& voice() = 0;
+ virtual VideoEngineInterface& video() = 0;
+ virtual const VoiceEngineInterface& voice() const = 0;
+ virtual const VideoEngineInterface& video() const = 0;
+};
+
+// CompositeMediaEngine constructs a MediaEngine from separate
+// voice and video engine classes.
+// Optionally owns a FieldTrialsView trials map.
+class CompositeMediaEngine : public MediaEngineInterface {
+ public:
+ CompositeMediaEngine(std::unique_ptr<webrtc::FieldTrialsView> trials,
+ std::unique_ptr<VoiceEngineInterface> audio_engine,
+ std::unique_ptr<VideoEngineInterface> video_engine);
+ CompositeMediaEngine(std::unique_ptr<VoiceEngineInterface> audio_engine,
+ std::unique_ptr<VideoEngineInterface> video_engine);
+ ~CompositeMediaEngine() override;
+
+ // Always succeeds.
+ bool Init() override;
+
+ VoiceEngineInterface& voice() override;
+ VideoEngineInterface& video() override;
+ const VoiceEngineInterface& voice() const override;
+ const VideoEngineInterface& video() const override;
+
+ private:
+ const std::unique_ptr<webrtc::FieldTrialsView> trials_;
+ const std::unique_ptr<VoiceEngineInterface> voice_engine_;
+ const std::unique_ptr<VideoEngineInterface> video_engine_;
+};
+
+webrtc::RtpParameters CreateRtpParametersWithOneEncoding();
+webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp);
+
+// Returns a vector of RTP extensions as visible from RtpSender/Receiver
+// GetCapabilities(). The returned vector only shows what will definitely be
+// offered by default, i.e. the list of extensions returned from
+// GetRtpHeaderExtensions() that are not kStopped.
+std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
+ const RtpHeaderExtensionQueryInterface& query_interface);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_MEDIA_ENGINE_H_
diff --git a/third_party/libwebrtc/media/base/media_engine_unittest.cc b/third_party/libwebrtc/media/base/media_engine_unittest.cc
new file mode 100644
index 0000000000..83f80c4669
--- /dev/null
+++ b/third_party/libwebrtc/media/base/media_engine_unittest.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/media_engine.h"
+
+#include "test/gmock.h"
+
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::webrtc::RtpExtension;
+using ::webrtc::RtpHeaderExtensionCapability;
+using ::webrtc::RtpTransceiverDirection;
+
+namespace cricket {
+namespace {
+
+class MockRtpHeaderExtensionQueryInterface
+ : public RtpHeaderExtensionQueryInterface {
+ public:
+ MOCK_METHOD(std::vector<RtpHeaderExtensionCapability>,
+ GetRtpHeaderExtensions,
+ (),
+ (const, override));
+};
+
+} // namespace
+
+TEST(MediaEngineTest, ReturnsNotStoppedHeaderExtensions) {
+ MockRtpHeaderExtensionQueryInterface mock;
+ std::vector<RtpHeaderExtensionCapability> extensions(
+ {RtpHeaderExtensionCapability("uri1", 1,
+ RtpTransceiverDirection::kInactive),
+ RtpHeaderExtensionCapability("uri2", 2,
+ RtpTransceiverDirection::kSendRecv),
+ RtpHeaderExtensionCapability("uri3", 3,
+ RtpTransceiverDirection::kStopped),
+ RtpHeaderExtensionCapability("uri4", 4,
+ RtpTransceiverDirection::kSendOnly),
+ RtpHeaderExtensionCapability("uri5", 5,
+ RtpTransceiverDirection::kRecvOnly)});
+ EXPECT_CALL(mock, GetRtpHeaderExtensions).WillOnce(Return(extensions));
+ EXPECT_THAT(GetDefaultEnabledRtpHeaderExtensions(mock),
+ ElementsAre(Field(&RtpExtension::uri, StrEq("uri1")),
+ Field(&RtpExtension::uri, StrEq("uri2")),
+ Field(&RtpExtension::uri, StrEq("uri4")),
+ Field(&RtpExtension::uri, StrEq("uri5"))));
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/rid_description.cc b/third_party/libwebrtc/media/base/rid_description.cc
new file mode 100644
index 0000000000..b3eae272f9
--- /dev/null
+++ b/third_party/libwebrtc/media/base/rid_description.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/rid_description.h"
+
+namespace cricket {
+
+RidDescription::RidDescription() = default;
+RidDescription::RidDescription(const std::string& rid, RidDirection direction)
+ : rid{rid}, direction{direction} {}
+RidDescription::RidDescription(const RidDescription& other) = default;
+RidDescription::~RidDescription() = default;
+RidDescription& RidDescription::operator=(const RidDescription& other) =
+ default;
+bool RidDescription::operator==(const RidDescription& other) const {
+ return rid == other.rid && direction == other.direction &&
+ payload_types == other.payload_types &&
+ restrictions == other.restrictions;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/rid_description.h b/third_party/libwebrtc/media/base/rid_description.h
new file mode 100644
index 0000000000..04c0f3d4bc
--- /dev/null
+++ b/third_party/libwebrtc/media/base/rid_description.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_RID_DESCRIPTION_H_
+#define MEDIA_BASE_RID_DESCRIPTION_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+enum class RidDirection { kSend, kReceive };
+
+// Description of a Restriction Id (RID) according to:
+// https://tools.ietf.org/html/draft-ietf-mmusic-rid-15
+// A Restriction Identifier serves two purposes:
+// 1. Uniquely identifies an RTP stream inside an RTP session.
+// When combined with MIDs (https://tools.ietf.org/html/rfc5888),
+// RIDs uniquely identify an RTP stream within an RTP session.
+// The MID will identify the media section and the RID will identify
+// the stream within the section.
+// RID identifiers must be unique within the media section.
+// 2. Allows indicating further restrictions to the stream.
+// These restrictions are added according to the direction specified.
+// The direction field identifies the direction of the RTP stream packets
+// to which the restrictions apply. The direction is independent of the
+// transceiver direction and can be one of {send, recv}.
+// The following are some examples of these restrictions:
+// a. max-width, max-height, max-fps, max-br, ...
+// b. further restricting the codec set (from what m= section specified)
+//
+// Note: Indicating dependencies between streams (using depend) will not be
+// supported, since the WG is adopting a different approach to achieve this.
+// As of 2018-12-04, the new SVC (Scalable Video Coder) approach is still not
+// mature enough to be implemented as part of this work.
+// See: https://w3c.github.io/webrtc-svc/ for more details.
+struct RidDescription final {
+ RidDescription();
+ RidDescription(const std::string& rid, RidDirection direction);
+ RidDescription(const RidDescription& other);
+ ~RidDescription();
+ RidDescription& operator=(const RidDescription& other);
+
+ // This is currently required for unit tests of StreamParams which contains
+ // RidDescription objects and checks for equality using operator==.
+ bool operator==(const RidDescription& other) const;
+ bool operator!=(const RidDescription& other) const {
+ return !(*this == other);
+ }
+
+ // The RID identifier that uniquely identifies the stream within the session.
+ std::string rid;
+
+ // Specifies the direction for which the specified restrictions hold.
+ // This direction is either send or receive and is independent of the
+ // direction of the transceiver.
+ // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15#section-4 :
+ // The "direction" field identifies the direction of the RTP Stream
+ // packets to which the indicated restrictions are applied. It may be
+ // either "send" or "recv". Note that these restriction directions are
+ // expressed independently of any "inactive", "sendonly", "recvonly", or
+ // "sendrecv" attributes associated with the media section. It is, for
+ // example, valid to indicate "recv" restrictions on a "sendonly"
+ // stream; those restrictions would apply if, at a future point in time,
+ // the stream were changed to "sendrecv" or "recvonly".
+ RidDirection direction;
+
+ // The list of codec payload types for this stream.
+ // It should be a subset of the payloads supported for the media section.
+ std::vector<int> payload_types;
+
+ // Contains key-value pairs for restrictions.
+ // The keys are not validated against a known set.
+ // The meaning to infer for the values depends on each key.
+ // Examples:
+ // 1. An entry for max-width will have a value that is interpreted as an int.
+ // 2. An entry for max-bpp (bits per pixel) will have a float value.
+ // Interpretation (and validation of value) is left for the implementation.
+ // I.E. the media engines should validate values for parameters they support.
+ std::map<std::string, std::string> restrictions;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_RID_DESCRIPTION_H_
diff --git a/third_party/libwebrtc/media/base/rtp_utils.cc b/third_party/libwebrtc/media/base/rtp_utils.cc
new file mode 100644
index 0000000000..c630cbc7e4
--- /dev/null
+++ b/third_party/libwebrtc/media/base/rtp_utils.cc
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/rtp_utils.h"
+
+#include <string.h>
+
+#include <vector>
+
+// PacketTimeUpdateParams is defined in asyncpacketsocket.h.
+// TODO(sergeyu): Find more appropriate place for PacketTimeUpdateParams.
+#include "media/base/turn_utils.h"
+#include "modules/rtp_rtcp/source/rtp_util.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/byte_order.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/message_digest.h"
+
+namespace cricket {
+
+static const size_t kRtcpPayloadTypeOffset = 1;
+static const size_t kRtpExtensionHeaderLen = 4;
+static const size_t kAbsSendTimeExtensionLen = 3;
+static const size_t kOneByteExtensionHeaderLen = 1;
+static const size_t kTwoByteExtensionHeaderLen = 2;
+
+namespace {
+
+// Fake auth tag written by the sender when external authentication is enabled.
+// HMAC in packet will be compared against this value before updating packet
+// with actual HMAC value.
+static const uint8_t kFakeAuthTag[10] = {0xba, 0xdd, 0xba, 0xdd, 0xba,
+ 0xdd, 0xba, 0xdd, 0xba, 0xdd};
+
+void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data,
+ size_t length,
+ uint64_t time_us) {
+ // Absolute send time in RTP streams.
+ //
+ // The absolute send time is signaled to the receiver in-band using the
+ // general mechanism for RTP header extensions [RFC5285]. The payload
+ // of this extension (the transmitted value) is a 24-bit unsigned integer
+ // containing the sender's current time in seconds as a fixed point number
+ // with 18 bits fractional part.
+ //
+ // The form of the absolute send time extension block:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | len=2 | absolute send time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ if (length != kAbsSendTimeExtensionLen) {
+ RTC_DCHECK_NOTREACHED();
+ return;
+ }
+
+ // Convert microseconds to a 6.18 fixed point value in seconds.
+ uint32_t send_time = ((time_us << 18) / 1000000) & 0x00FFFFFF;
+ extension_data[0] = static_cast<uint8_t>(send_time >> 16);
+ extension_data[1] = static_cast<uint8_t>(send_time >> 8);
+ extension_data[2] = static_cast<uint8_t>(send_time);
+}
+
+// Assumes `length` is actual packet length + tag length. Updates HMAC at end of
+// the RTP packet.
+void UpdateRtpAuthTag(uint8_t* rtp,
+ size_t length,
+ const rtc::PacketTimeUpdateParams& packet_time_params) {
+ // If there is no key, return.
+ if (packet_time_params.srtp_auth_key.empty()) {
+ return;
+ }
+
+ size_t tag_length = packet_time_params.srtp_auth_tag_len;
+
+ // ROC (rollover counter) is at the beginning of the auth tag.
+ const size_t kRocLength = 4;
+ if (tag_length < kRocLength || tag_length > length) {
+ RTC_DCHECK_NOTREACHED();
+ return;
+ }
+
+ uint8_t* auth_tag = rtp + (length - tag_length);
+
+ // We should have a fake HMAC value @ auth_tag.
+ RTC_DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length));
+
+ // Copy ROC after end of rtp packet.
+ memcpy(auth_tag, &packet_time_params.srtp_packet_index, kRocLength);
+ // Authentication of a RTP packet will have RTP packet + ROC size.
+ size_t auth_required_length = length - tag_length + kRocLength;
+
+ uint8_t output[64];
+ size_t result =
+ rtc::ComputeHmac(rtc::DIGEST_SHA_1, &packet_time_params.srtp_auth_key[0],
+ packet_time_params.srtp_auth_key.size(), rtp,
+ auth_required_length, output, sizeof(output));
+
+ if (result < tag_length) {
+ RTC_DCHECK_NOTREACHED();
+ return;
+ }
+
+ // Copy HMAC from output to packet. This is required as auth tag length
+ // may not be equal to the actual HMAC length.
+ memcpy(auth_tag, output, tag_length);
+}
+
+bool GetUint8(const void* data, size_t offset, int* value) {
+ if (!data || !value) {
+ return false;
+ }
+ *value = *(static_cast<const uint8_t*>(data) + offset);
+ return true;
+}
+
+} // namespace
+
+bool GetRtcpType(const void* data, size_t len, int* value) {
+ if (len < kMinRtcpPacketLen) {
+ return false;
+ }
+ return GetUint8(data, kRtcpPayloadTypeOffset, value);
+}
+
+// This method returns SSRC first of RTCP packet, except if packet is SDES.
+// TODO(mallinath) - Fully implement RFC 5506. This standard doesn't restrict
+// to send non-compound packets only to feedback messages.
+bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value) {
+ // Packet should be at least of 8 bytes, to get SSRC from a RTCP packet.
+ if (!data || len < kMinRtcpPacketLen + 4 || !value)
+ return false;
+ int pl_type;
+ if (!GetRtcpType(data, len, &pl_type))
+ return false;
+ // SDES packet parsing is not supported.
+ if (pl_type == kRtcpTypeSDES)
+ return false;
+ *value = rtc::GetBE32(static_cast<const uint8_t*>(data) + 4);
+ return true;
+}
+
+bool IsValidRtpPayloadType(int payload_type) {
+ return payload_type >= 0 && payload_type <= 127;
+}
+
+bool IsValidRtpPacketSize(RtpPacketType packet_type, size_t size) {
+ RTC_DCHECK_NE(RtpPacketType::kUnknown, packet_type);
+ size_t min_packet_length = packet_type == RtpPacketType::kRtcp
+ ? kMinRtcpPacketLen
+ : kMinRtpPacketLen;
+ return size >= min_packet_length && size <= kMaxRtpPacketLen;
+}
+
+absl::string_view RtpPacketTypeToString(RtpPacketType packet_type) {
+ switch (packet_type) {
+ case RtpPacketType::kRtp:
+ return "RTP";
+ case RtpPacketType::kRtcp:
+ return "RTCP";
+ case RtpPacketType::kUnknown:
+ return "Unknown";
+ }
+ RTC_CHECK_NOTREACHED();
+}
+
+RtpPacketType InferRtpPacketType(rtc::ArrayView<const char> packet) {
+ if (webrtc::IsRtcpPacket(
+ rtc::reinterpret_array_view<const uint8_t>(packet))) {
+ return RtpPacketType::kRtcp;
+ }
+ if (webrtc::IsRtpPacket(rtc::reinterpret_array_view<const uint8_t>(packet))) {
+ return RtpPacketType::kRtp;
+ }
+ return RtpPacketType::kUnknown;
+}
+
+bool ValidateRtpHeader(const uint8_t* rtp,
+ size_t length,
+ size_t* header_length) {
+ if (header_length) {
+ *header_length = 0;
+ }
+
+ if (length < kMinRtpPacketLen) {
+ return false;
+ }
+
+ size_t cc_count = rtp[0] & 0x0F;
+ size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count;
+ if (header_length_without_extension > length) {
+ return false;
+ }
+
+ // If extension bit is not set, we are done with header processing, as input
+ // length is verified above.
+ if (!(rtp[0] & 0x10)) {
+ if (header_length)
+ *header_length = header_length_without_extension;
+
+ return true;
+ }
+
+ rtp += header_length_without_extension;
+
+ if (header_length_without_extension + kRtpExtensionHeaderLen > length) {
+ return false;
+ }
+
+ // Getting extension profile length.
+ // Length is in 32 bit words.
+ uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2);
+ size_t extension_length = extension_length_in_32bits * 4;
+
+ size_t rtp_header_length = extension_length +
+ header_length_without_extension +
+ kRtpExtensionHeaderLen;
+
+ // Verify input length against total header size.
+ if (rtp_header_length > length) {
+ return false;
+ }
+
+ if (header_length) {
+ *header_length = rtp_header_length;
+ }
+ return true;
+}
+
+// ValidateRtpHeader() must be called before this method to make sure, we have
+// a sane rtp packet.
+bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp,
+ size_t length,
+ int extension_id,
+ uint64_t time_us) {
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |V=2|P|X| CC |M| PT | sequence number |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | timestamp |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | synchronization source (SSRC) identifier |
+ // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ // | contributing source (CSRC) identifiers |
+ // | .... |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ // Return if extension bit is not set.
+ if (!(rtp[0] & 0x10)) {
+ return true;
+ }
+
+ size_t cc_count = rtp[0] & 0x0F;
+ size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count;
+
+ rtp += header_length_without_extension;
+
+ // Getting extension profile ID and length.
+ uint16_t profile_id = rtc::GetBE16(rtp);
+ // Length is in 32 bit words.
+ uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2);
+ size_t extension_length = extension_length_in_32bits * 4;
+
+ rtp += kRtpExtensionHeaderLen; // Moving past extension header.
+
+ constexpr uint16_t kOneByteExtensionProfileId = 0xBEDE;
+ constexpr uint16_t kTwoByteExtensionProfileId = 0x1000;
+
+ bool found = false;
+ if (profile_id == kOneByteExtensionProfileId ||
+ profile_id == kTwoByteExtensionProfileId) {
+ // OneByte extension header
+ // 0
+ // 0 1 2 3 4 5 6 7
+ // +-+-+-+-+-+-+-+-+
+ // | ID |length |
+ // +-+-+-+-+-+-+-+-+
+
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | 0xBE | 0xDE | length=3 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | L=0 | data | ID | L=1 | data...
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // ...data | 0 (pad) | 0 (pad) | ID | L=3 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | data |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ // TwoByte extension header
+ // 0
+ // 0 1 2 3 4 5 6 7
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | 0x10 | 0x00 | length=3 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | L=1 | data | ID |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | L=2 | data | 0 (pad) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | L=2 | data |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ size_t extension_header_length = profile_id == kOneByteExtensionProfileId
+ ? kOneByteExtensionHeaderLen
+ : kTwoByteExtensionHeaderLen;
+
+ const uint8_t* extension_start = rtp;
+ const uint8_t* extension_end = extension_start + extension_length;
+
+ // rtp + 1 since the minimum size per header extension is two bytes for both
+ // one- and two-byte header extensions.
+ while (rtp + 1 < extension_end) {
+ // See RFC8285 Section 4.2-4.3 for more information about one- and
+ // two-byte header extensions.
+ const int id =
+ profile_id == kOneByteExtensionProfileId ? (*rtp & 0xF0) >> 4 : *rtp;
+ const size_t length = profile_id == kOneByteExtensionProfileId
+ ? (*rtp & 0x0F) + 1
+ : *(rtp + 1);
+ if (rtp + extension_header_length + length > extension_end) {
+ return false;
+ }
+ if (id == extension_id) {
+ UpdateAbsSendTimeExtensionValue(rtp + extension_header_length, length,
+ time_us);
+ found = true;
+ break;
+ }
+ rtp += extension_header_length + length;
+ // Counting padding bytes.
+ while ((rtp < extension_end) && (*rtp == 0)) {
+ ++rtp;
+ }
+ }
+ }
+ return found;
+}
+
+bool ApplyPacketOptions(uint8_t* data,
+ size_t length,
+ const rtc::PacketTimeUpdateParams& packet_time_params,
+ uint64_t time_us) {
+ RTC_DCHECK(data);
+ RTC_DCHECK(length);
+
+ // if there is no valid `rtp_sendtime_extension_id` and `srtp_auth_key` in
+ // PacketOptions, nothing to be updated in this packet.
+ if (packet_time_params.rtp_sendtime_extension_id == -1 &&
+ packet_time_params.srtp_auth_key.empty()) {
+ return true;
+ }
+
+ // If there is a srtp auth key present then the packet must be an RTP packet.
+ // RTP packet may have been wrapped in a TURN Channel Data or TURN send
+ // indication.
+ size_t rtp_start_pos;
+ size_t rtp_length;
+ if (!UnwrapTurnPacket(data, length, &rtp_start_pos, &rtp_length)) {
+ RTC_DCHECK_NOTREACHED();
+ return false;
+ }
+
+ // Making sure we have a valid RTP packet at the end.
+ auto packet = rtc::MakeArrayView(data + rtp_start_pos, rtp_length);
+ if (!webrtc::IsRtpPacket(packet) ||
+ !ValidateRtpHeader(data + rtp_start_pos, rtp_length, nullptr)) {
+ RTC_DCHECK_NOTREACHED();
+ return false;
+ }
+
+ uint8_t* start = data + rtp_start_pos;
+ // If packet option has non default value (-1) for sendtime extension id,
+ // then we should parse the rtp packet to update the timestamp. Otherwise
+ // just calculate HMAC and update packet with it.
+ if (packet_time_params.rtp_sendtime_extension_id != -1) {
+ UpdateRtpAbsSendTimeExtension(start, rtp_length,
+ packet_time_params.rtp_sendtime_extension_id,
+ time_us);
+ }
+
+ UpdateRtpAuthTag(start, rtp_length, packet_time_params);
+ return true;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/rtp_utils.h b/third_party/libwebrtc/media/base/rtp_utils.h
new file mode 100644
index 0000000000..a501fd7af3
--- /dev/null
+++ b/third_party/libwebrtc/media/base/rtp_utils.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_RTP_UTILS_H_
+#define MEDIA_BASE_RTP_UTILS_H_
+
+#include "absl/strings/string_view.h"
+#include "api/array_view.h"
+#include "rtc_base/byte_order.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace rtc {
+struct PacketTimeUpdateParams;
+} // namespace rtc
+
+namespace cricket {
+
+const size_t kMinRtpPacketLen = 12;
+const size_t kMaxRtpPacketLen = 2048;
+const size_t kMinRtcpPacketLen = 4;
+
+enum RtcpTypes {
+ kRtcpTypeSR = 200, // Sender report payload type.
+ kRtcpTypeRR = 201, // Receiver report payload type.
+ kRtcpTypeSDES = 202, // SDES payload type.
+ kRtcpTypeBye = 203, // BYE payload type.
+ kRtcpTypeApp = 204, // APP payload type.
+ kRtcpTypeRTPFB = 205, // Transport layer Feedback message payload type.
+ kRtcpTypePSFB = 206, // Payload-specific Feedback message payload type.
+};
+
+enum class RtpPacketType {
+ kRtp,
+ kRtcp,
+ kUnknown,
+};
+
+bool GetRtcpType(const void* data, size_t len, int* value);
+bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value);
+
+// Checks the packet header to determine if it can be an RTP or RTCP packet.
+RtpPacketType InferRtpPacketType(rtc::ArrayView<const char> packet);
+// True if |payload type| is 0-127.
+bool IsValidRtpPayloadType(int payload_type);
+
+// True if `size` is appropriate for the indicated packet type.
+bool IsValidRtpPacketSize(RtpPacketType packet_type, size_t size);
+
+// Returns "RTCP", "RTP" or "Unknown" according to `packet_type`.
+absl::string_view RtpPacketTypeToString(RtpPacketType packet_type);
+
+// Verifies that a packet has a valid RTP header.
+bool RTC_EXPORT ValidateRtpHeader(const uint8_t* rtp,
+ size_t length,
+ size_t* header_length);
+
+// Helper method which updates the absolute send time extension if present.
+bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp,
+ size_t length,
+ int extension_id,
+ uint64_t time_us);
+
+// Applies specified `options` to the packet. It updates the absolute send time
+// extension header if it is present present then updates HMAC.
+bool RTC_EXPORT
+ApplyPacketOptions(uint8_t* data,
+ size_t length,
+ const rtc::PacketTimeUpdateParams& packet_time_params,
+ uint64_t time_us);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_RTP_UTILS_H_
diff --git a/third_party/libwebrtc/media/base/rtp_utils_unittest.cc b/third_party/libwebrtc/media/base/rtp_utils_unittest.cc
new file mode 100644
index 0000000000..a594f944c0
--- /dev/null
+++ b/third_party/libwebrtc/media/base/rtp_utils_unittest.cc
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/rtp_utils.h"
+
+#include <string.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "media/base/fake_rtp.h"
+#include "rtc_base/async_packet_socket.h"
+#include "test/gtest.h"
+
+namespace cricket {
+
+static const uint8_t kInvalidPacket[] = {0x80, 0x00};
+
+// PT = 206, FMT = 1, Sender SSRC = 0x1111, Media SSRC = 0x1111
+// No FCI information is needed for PLI.
+static const uint8_t kNonCompoundRtcpPliFeedbackPacket[] = {
+ 0x81, 0xCE, 0x00, 0x0C, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x11, 0x11};
+
+// Packet has only mandatory fixed RTCP header
+// PT = 204, SSRC = 0x1111
+static const uint8_t kNonCompoundRtcpAppPacket[] = {0x81, 0xCC, 0x00, 0x0C,
+ 0x00, 0x00, 0x11, 0x11};
+
+// PT = 202, Source count = 0
+static const uint8_t kNonCompoundRtcpSDESPacket[] = {0x80, 0xCA, 0x00, 0x00};
+
+static uint8_t kFakeTag[4] = {0xba, 0xdd, 0xba, 0xdd};
+static uint8_t kTestKey[] = "12345678901234567890";
+static uint8_t kTestAstValue[3] = {0xaa, 0xbb, 0xcc};
+
+// Valid rtp Message with 2 byte header extension.
+static uint8_t kRtpMsgWith2ByteExtnHeader[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xBB, 0xCC, 0XDD, // SSRC
+ 0x10, 0x00, 0x00, 0x01, // 2 Byte header extension
+ 0x01, 0x00, 0x00, 0x00
+ // clang-format on
+};
+
+// RTP packet with two one-byte header extensions. The last 4 bytes consist of
+// abs-send-time with extension id = 3 and length = 3.
+static uint8_t kRtpMsgWithOneByteAbsSendTimeExtension[] = {
+ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xBE, 0xDE, 0x00, 0x02, 0x22, 0x00, 0x02, 0x1c, 0x32, 0xaa, 0xbb, 0xcc,
+};
+
+// RTP packet with two two-byte header extensions. The last 5 bytes consist of
+// abs-send-time with extension id = 3 and length = 3.
+static uint8_t kRtpMsgWithTwoByteAbsSendTimeExtension[] = {
+ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x03, 0x03, 0xaa, 0xbb, 0xcc,
+};
+
+// Index of AbsSendTimeExtn data in message
+// `kRtpMsgWithOneByteAbsSendTimeExtension`.
+static const int kAstIndexInOneByteRtpMsg = 21;
+// and in message `kRtpMsgWithTwoByteAbsSendTimeExtension`.
+static const int kAstIndexInTwoByteRtpMsg = 21;
+
+static const rtc::ArrayView<const char> kPcmuFrameArrayView =
+ rtc::MakeArrayView(reinterpret_cast<const char*>(kPcmuFrame),
+ sizeof(kPcmuFrame));
+static const rtc::ArrayView<const char> kRtcpReportArrayView =
+ rtc::MakeArrayView(reinterpret_cast<const char*>(kRtcpReport),
+ sizeof(kRtcpReport));
+static const rtc::ArrayView<const char> kInvalidPacketArrayView =
+ rtc::MakeArrayView(reinterpret_cast<const char*>(kInvalidPacket),
+ sizeof(kInvalidPacket));
+
+TEST(RtpUtilsTest, GetRtcp) {
+ int pt;
+ EXPECT_TRUE(GetRtcpType(kRtcpReport, sizeof(kRtcpReport), &pt));
+ EXPECT_EQ(0xc9, pt);
+
+ EXPECT_FALSE(GetRtcpType(kInvalidPacket, sizeof(kInvalidPacket), &pt));
+
+ uint32_t ssrc;
+ EXPECT_TRUE(GetRtcpSsrc(kNonCompoundRtcpPliFeedbackPacket,
+ sizeof(kNonCompoundRtcpPliFeedbackPacket), &ssrc));
+ EXPECT_TRUE(GetRtcpSsrc(kNonCompoundRtcpAppPacket,
+ sizeof(kNonCompoundRtcpAppPacket), &ssrc));
+ EXPECT_FALSE(GetRtcpSsrc(kNonCompoundRtcpSDESPacket,
+ sizeof(kNonCompoundRtcpSDESPacket), &ssrc));
+}
+
+// Invalid RTP packets.
+TEST(RtpUtilsTest, InvalidRtpHeader) {
+ // Rtp message with invalid length.
+ const uint8_t kRtpMsgWithInvalidLength[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xBB, 0xCC, 0XDD, // SSRC
+ 0xDD, 0xCC, 0xBB, 0xAA, // Only 1 CSRC, but CC count is 4.
+ // clang-format on
+ };
+ EXPECT_FALSE(ValidateRtpHeader(kRtpMsgWithInvalidLength,
+ sizeof(kRtpMsgWithInvalidLength), nullptr));
+
+ // Rtp message with single byte header extension, invalid extension length.
+ const uint8_t kRtpMsgWithInvalidExtnLength[] = {
+ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xBE, 0xDE, 0x0A, 0x00, // Extn length - 0x0A00
+ };
+ EXPECT_FALSE(ValidateRtpHeader(kRtpMsgWithInvalidExtnLength,
+ sizeof(kRtpMsgWithInvalidExtnLength),
+ nullptr));
+}
+
+// Valid RTP packet with a 2byte header extension.
+TEST(RtpUtilsTest, Valid2ByteExtnHdrRtpMessage) {
+ EXPECT_TRUE(ValidateRtpHeader(kRtpMsgWith2ByteExtnHeader,
+ sizeof(kRtpMsgWith2ByteExtnHeader), nullptr));
+}
+
+// Valid RTP packet which has 1 byte header AbsSendTime extension in it.
+TEST(RtpUtilsTest, ValidRtpPacketWithOneByteAbsSendTimeExtension) {
+ EXPECT_TRUE(ValidateRtpHeader(kRtpMsgWithOneByteAbsSendTimeExtension,
+ sizeof(kRtpMsgWithOneByteAbsSendTimeExtension),
+ nullptr));
+}
+
+// Valid RTP packet which has 2 byte header AbsSendTime extension in it.
+TEST(RtpUtilsTest, ValidRtpPacketWithTwoByteAbsSendTimeExtension) {
+ EXPECT_TRUE(ValidateRtpHeader(kRtpMsgWithTwoByteAbsSendTimeExtension,
+ sizeof(kRtpMsgWithTwoByteAbsSendTimeExtension),
+ nullptr));
+}
+
+// Verify finding an extension ID in the TURN send indication message.
+TEST(RtpUtilsTest, UpdateAbsSendTimeExtensionInTurnSendIndication) {
+ // A valid STUN indication message with a valid RTP header in data attribute
+ // payload field and no extension bit set.
+ uint8_t message_without_extension[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x00, 0x16, 0x00, 0x18, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x13, 0x00, 0x0C, // Data attribute.
+ 0x80, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ // clang-format on
+ };
+ EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(
+ message_without_extension, sizeof(message_without_extension), 3, 0));
+
+ // A valid STUN indication message with a valid RTP header and a extension
+ // header.
+ uint8_t message[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x00, 0x16, 0x00, 0x24, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x13, 0x00, 0x18, // Data attribute.
+ 0x90, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xDE,
+ 0x00, 0x02, 0x22, 0xaa, 0xbb, 0xcc, 0x32, 0xaa, 0xbb, 0xcc,
+ // clang-format on
+ };
+ EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(message, sizeof(message), 3, 0));
+}
+
+// Test without any packet options variables set. This method should return
+// without HMAC value in the packet.
+TEST(RtpUtilsTest, ApplyPacketOptionsWithDefaultValues) {
+ rtc::PacketTimeUpdateParams packet_time_params;
+ std::vector<uint8_t> rtp_packet(
+ kRtpMsgWithOneByteAbsSendTimeExtension,
+ kRtpMsgWithOneByteAbsSendTimeExtension +
+ sizeof(kRtpMsgWithOneByteAbsSendTimeExtension));
+ rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
+ EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
+ packet_time_params, 0));
+
+ // Making sure HMAC wasn't updated..
+ EXPECT_EQ(0,
+ memcmp(&rtp_packet[sizeof(kRtpMsgWithOneByteAbsSendTimeExtension)],
+ kFakeTag, 4));
+
+ // Verify AbsouluteSendTime extension field wasn't modified.
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInOneByteRtpMsg], kTestAstValue,
+ sizeof(kTestAstValue)));
+}
+
+// Veirfy HMAC is updated when packet option parameters are set.
+TEST(RtpUtilsTest, ApplyPacketOptionsWithAuthParams) {
+ rtc::PacketTimeUpdateParams packet_time_params;
+ packet_time_params.srtp_auth_key.assign(kTestKey,
+ kTestKey + sizeof(kTestKey));
+ packet_time_params.srtp_auth_tag_len = 4;
+
+ std::vector<uint8_t> rtp_packet(
+ kRtpMsgWithOneByteAbsSendTimeExtension,
+ kRtpMsgWithOneByteAbsSendTimeExtension +
+ sizeof(kRtpMsgWithOneByteAbsSendTimeExtension));
+ rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
+ EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
+ packet_time_params, 0));
+
+ uint8_t kExpectedTag[] = {0xc1, 0x7a, 0x8c, 0xa0};
+ EXPECT_EQ(0,
+ memcmp(&rtp_packet[sizeof(kRtpMsgWithOneByteAbsSendTimeExtension)],
+ kExpectedTag, sizeof(kExpectedTag)));
+
+ // Verify AbsouluteSendTime extension field is not modified.
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInOneByteRtpMsg], kTestAstValue,
+ sizeof(kTestAstValue)));
+}
+
+// Verify finding an extension ID in a raw rtp message.
+TEST(RtpUtilsTest, UpdateOneByteAbsSendTimeExtensionInRtpPacket) {
+ std::vector<uint8_t> rtp_packet(
+ kRtpMsgWithOneByteAbsSendTimeExtension,
+ kRtpMsgWithOneByteAbsSendTimeExtension +
+ sizeof(kRtpMsgWithOneByteAbsSendTimeExtension));
+
+ EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(&rtp_packet[0], rtp_packet.size(),
+ 3, 51183266));
+
+ // Verify that the timestamp was updated.
+ const uint8_t kExpectedTimestamp[3] = {0xcc, 0xbb, 0xaa};
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInOneByteRtpMsg], kExpectedTimestamp,
+ sizeof(kExpectedTimestamp)));
+}
+
+// Verify finding an extension ID in a raw rtp message.
+TEST(RtpUtilsTest, UpdateTwoByteAbsSendTimeExtensionInRtpPacket) {
+ std::vector<uint8_t> rtp_packet(
+ kRtpMsgWithTwoByteAbsSendTimeExtension,
+ kRtpMsgWithTwoByteAbsSendTimeExtension +
+ sizeof(kRtpMsgWithTwoByteAbsSendTimeExtension));
+
+ EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(&rtp_packet[0], rtp_packet.size(),
+ 3, 51183266));
+
+ // Verify that the timestamp was updated.
+ const uint8_t kExpectedTimestamp[3] = {0xcc, 0xbb, 0xaa};
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInTwoByteRtpMsg], kExpectedTimestamp,
+ sizeof(kExpectedTimestamp)));
+}
+
+// Verify we update both AbsSendTime extension header and HMAC.
+TEST(RtpUtilsTest, ApplyPacketOptionsWithAuthParamsAndAbsSendTime) {
+ rtc::PacketTimeUpdateParams packet_time_params;
+ packet_time_params.srtp_auth_key.assign(kTestKey,
+ kTestKey + sizeof(kTestKey));
+ packet_time_params.srtp_auth_tag_len = 4;
+ packet_time_params.rtp_sendtime_extension_id = 3;
+ // 3 is also present in the test message.
+
+ std::vector<uint8_t> rtp_packet(
+ kRtpMsgWithOneByteAbsSendTimeExtension,
+ kRtpMsgWithOneByteAbsSendTimeExtension +
+ sizeof(kRtpMsgWithOneByteAbsSendTimeExtension));
+ rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
+ EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
+ packet_time_params, 51183266));
+
+ const uint8_t kExpectedTag[] = {0x81, 0xd1, 0x2c, 0x0e};
+ EXPECT_EQ(0,
+ memcmp(&rtp_packet[sizeof(kRtpMsgWithOneByteAbsSendTimeExtension)],
+ kExpectedTag, sizeof(kExpectedTag)));
+
+ // Verify that the timestamp was updated.
+ const uint8_t kExpectedTimestamp[3] = {0xcc, 0xbb, 0xaa};
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInOneByteRtpMsg], kExpectedTimestamp,
+ sizeof(kExpectedTimestamp)));
+}
+
+TEST(RtpUtilsTest, InferRtpPacketType) {
+ EXPECT_EQ(RtpPacketType::kRtp, InferRtpPacketType(kPcmuFrameArrayView));
+ EXPECT_EQ(RtpPacketType::kRtcp, InferRtpPacketType(kRtcpReportArrayView));
+ EXPECT_EQ(RtpPacketType::kUnknown,
+ InferRtpPacketType(kInvalidPacketArrayView));
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/sdp_video_format_utils.cc b/third_party/libwebrtc/media/base/sdp_video_format_utils.cc
new file mode 100644
index 0000000000..a156afdc02
--- /dev/null
+++ b/third_party/libwebrtc/media/base/sdp_video_format_utils.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/sdp_video_format_utils.h"
+
+#include <cstring>
+#include <map>
+#include <utility>
+
+#include "api/video_codecs/h264_profile_level_id.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+namespace {
+const char kProfileLevelId[] = "profile-level-id";
+const char kH264LevelAsymmetryAllowed[] = "level-asymmetry-allowed";
+// Max frame rate for VP8 and VP9 video.
+const char kVPxFmtpMaxFrameRate[] = "max-fr";
+// Max frame size for VP8 and VP9 video.
+const char kVPxFmtpMaxFrameSize[] = "max-fs";
+const int kVPxFmtpFrameSizeSubBlockPixels = 256;
+
+bool IsH264LevelAsymmetryAllowed(const SdpVideoFormat::Parameters& params) {
+ const auto it = params.find(kH264LevelAsymmetryAllowed);
+ return it != params.end() && strcmp(it->second.c_str(), "1") == 0;
+}
+
+// Compare H264 levels and handle the level 1b case.
+bool H264LevelIsLess(H264Level a, H264Level b) {
+ if (a == H264Level::kLevel1_b)
+ return b != H264Level::kLevel1 && b != H264Level::kLevel1_b;
+ if (b == H264Level::kLevel1_b)
+ return a == H264Level::kLevel1;
+ return a < b;
+}
+
+H264Level H264LevelMin(H264Level a, H264Level b) {
+ return H264LevelIsLess(a, b) ? a : b;
+}
+
+absl::optional<int> ParsePositiveNumberFromParams(
+ const SdpVideoFormat::Parameters& params,
+ const char* parameter_name) {
+ const auto max_frame_rate_it = params.find(parameter_name);
+ if (max_frame_rate_it == params.end())
+ return absl::nullopt;
+
+ const absl::optional<int> i =
+ rtc::StringToNumber<int>(max_frame_rate_it->second);
+ if (!i.has_value() || i.value() <= 0)
+ return absl::nullopt;
+ return i;
+}
+
+} // namespace
+
+// Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2.
+void H264GenerateProfileLevelIdForAnswer(
+ const SdpVideoFormat::Parameters& local_supported_params,
+ const SdpVideoFormat::Parameters& remote_offered_params,
+ SdpVideoFormat::Parameters* answer_params) {
+ // If both local and remote haven't set profile-level-id, they are both using
+ // the default profile. In this case, don't set profile-level-id in answer
+ // either.
+ if (!local_supported_params.count(kProfileLevelId) &&
+ !remote_offered_params.count(kProfileLevelId)) {
+ return;
+ }
+
+ // Parse profile-level-ids.
+ const absl::optional<H264ProfileLevelId> local_profile_level_id =
+ ParseSdpForH264ProfileLevelId(local_supported_params);
+ const absl::optional<H264ProfileLevelId> remote_profile_level_id =
+ ParseSdpForH264ProfileLevelId(remote_offered_params);
+ // The local and remote codec must have valid and equal H264 Profiles.
+ RTC_DCHECK(local_profile_level_id);
+ RTC_DCHECK(remote_profile_level_id);
+ RTC_DCHECK_EQ(local_profile_level_id->profile,
+ remote_profile_level_id->profile);
+
+ // Parse level information.
+ const bool level_asymmetry_allowed =
+ IsH264LevelAsymmetryAllowed(local_supported_params) &&
+ IsH264LevelAsymmetryAllowed(remote_offered_params);
+ const H264Level local_level = local_profile_level_id->level;
+ const H264Level remote_level = remote_profile_level_id->level;
+ const H264Level min_level = H264LevelMin(local_level, remote_level);
+
+ // Determine answer level. When level asymmetry is not allowed, level upgrade
+ // is not allowed, i.e., the level in the answer must be equal to or lower
+ // than the level in the offer.
+ const H264Level answer_level =
+ level_asymmetry_allowed ? local_level : min_level;
+
+ // Set the resulting profile-level-id in the answer parameters.
+ (*answer_params)[kProfileLevelId] = *H264ProfileLevelIdToString(
+ H264ProfileLevelId(local_profile_level_id->profile, answer_level));
+}
+
+absl::optional<int> ParseSdpForVPxMaxFrameRate(
+ const SdpVideoFormat::Parameters& params) {
+ return ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameRate);
+}
+
+absl::optional<int> ParseSdpForVPxMaxFrameSize(
+ const SdpVideoFormat::Parameters& params) {
+ const absl::optional<int> i =
+ ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameSize);
+ return i ? absl::make_optional(i.value() * kVPxFmtpFrameSizeSubBlockPixels)
+ : absl::nullopt;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/media/base/sdp_video_format_utils.h b/third_party/libwebrtc/media/base/sdp_video_format_utils.h
new file mode 100644
index 0000000000..80c1e4d501
--- /dev/null
+++ b/third_party/libwebrtc/media/base/sdp_video_format_utils.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_SDP_VIDEO_FORMAT_UTILS_H_
+#define MEDIA_BASE_SDP_VIDEO_FORMAT_UTILS_H_
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/sdp_video_format.h"
+
+namespace webrtc {
+// Generate codec parameters that will be used as answer in an SDP negotiation
+// based on local supported parameters and remote offered parameters. Both
+// `local_supported_params`, `remote_offered_params`, and `answer_params`
+// represent sendrecv media descriptions, i.e they are a mix of both encode and
+// decode capabilities. In theory, when the profile in `local_supported_params`
+// represent a strict superset of the profile in `remote_offered_params`, we
+// could limit the profile in `answer_params` to the profile in
+// `remote_offered_params`. However, to simplify the code, each supported H264
+// profile should be listed explicitly in the list of local supported codecs,
+// even if they are redundant. Then each local codec in the list should be
+// tested one at a time against the remote codec, and only when the profiles are
+// equal should this function be called. Therefore, this function does not need
+// to handle profile intersection, and the profile of `local_supported_params`
+// and `remote_offered_params` must be equal before calling this function. The
+// parameters that are used when negotiating are the level part of
+// profile-level-id and level-asymmetry-allowed.
+void H264GenerateProfileLevelIdForAnswer(
+ const SdpVideoFormat::Parameters& local_supported_params,
+ const SdpVideoFormat::Parameters& remote_offered_params,
+ SdpVideoFormat::Parameters* answer_params);
+
+// Parse max frame rate from SDP FMTP line. absl::nullopt is returned if the
+// field is missing or not a number.
+absl::optional<int> ParseSdpForVPxMaxFrameRate(
+ const SdpVideoFormat::Parameters& params);
+
+// Parse max frame size from SDP FMTP line. absl::nullopt is returned if the
+// field is missing or not a number. Please note that the value is stored in sub
+// blocks but the returned value is in total number of pixels.
+absl::optional<int> ParseSdpForVPxMaxFrameSize(
+ const SdpVideoFormat::Parameters& params);
+
+} // namespace webrtc
+
+#endif // MEDIA_BASE_SDP_VIDEO_FORMAT_UTILS_H_
diff --git a/third_party/libwebrtc/media/base/sdp_video_format_utils_unittest.cc b/third_party/libwebrtc/media/base/sdp_video_format_utils_unittest.cc
new file mode 100644
index 0000000000..d8ef9ab827
--- /dev/null
+++ b/third_party/libwebrtc/media/base/sdp_video_format_utils_unittest.cc
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/sdp_video_format_utils.h"
+
+#include <string.h>
+
+#include <map>
+#include <utility>
+
+#include "rtc_base/string_to_number.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+// Max frame rate for VP8 and VP9 video.
+const char kVPxFmtpMaxFrameRate[] = "max-fr";
+// Max frame size for VP8 and VP9 video.
+const char kVPxFmtpMaxFrameSize[] = "max-fs";
+} // namespace
+
+TEST(SdpVideoFormatUtilsTest, TestH264GenerateProfileLevelIdForAnswerEmpty) {
+ SdpVideoFormat::Parameters answer_params;
+ H264GenerateProfileLevelIdForAnswer(SdpVideoFormat::Parameters(),
+ SdpVideoFormat::Parameters(),
+ &answer_params);
+ EXPECT_TRUE(answer_params.empty());
+}
+
+TEST(SdpVideoFormatUtilsTest,
+ TestH264GenerateProfileLevelIdForAnswerLevelSymmetryCapped) {
+ SdpVideoFormat::Parameters low_level;
+ low_level["profile-level-id"] = "42e015";
+ SdpVideoFormat::Parameters high_level;
+ high_level["profile-level-id"] = "42e01f";
+
+ // Level asymmetry is not allowed; test that answer level is the lower of the
+ // local and remote levels.
+ SdpVideoFormat::Parameters answer_params;
+ H264GenerateProfileLevelIdForAnswer(low_level /* local_supported */,
+ high_level /* remote_offered */,
+ &answer_params);
+ EXPECT_EQ("42e015", answer_params["profile-level-id"]);
+
+ SdpVideoFormat::Parameters answer_params2;
+ H264GenerateProfileLevelIdForAnswer(high_level /* local_supported */,
+ low_level /* remote_offered */,
+ &answer_params2);
+ EXPECT_EQ("42e015", answer_params2["profile-level-id"]);
+}
+
+TEST(SdpVideoFormatUtilsTest,
+ TestH264GenerateProfileLevelIdForAnswerConstrainedBaselineLevelAsymmetry) {
+ SdpVideoFormat::Parameters local_params;
+ local_params["profile-level-id"] = "42e01f";
+ local_params["level-asymmetry-allowed"] = "1";
+ SdpVideoFormat::Parameters remote_params;
+ remote_params["profile-level-id"] = "42e015";
+ remote_params["level-asymmetry-allowed"] = "1";
+ SdpVideoFormat::Parameters answer_params;
+ H264GenerateProfileLevelIdForAnswer(local_params, remote_params,
+ &answer_params);
+ // When level asymmetry is allowed, we can answer a higher level than what was
+ // offered.
+ EXPECT_EQ("42e01f", answer_params["profile-level-id"]);
+}
+
+TEST(SdpVideoFormatUtilsTest, MaxFrameRateIsMissingOrInvalid) {
+ SdpVideoFormat::Parameters params;
+ absl::optional<int> empty = ParseSdpForVPxMaxFrameRate(params);
+ EXPECT_FALSE(empty);
+ params[kVPxFmtpMaxFrameRate] = "-1";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameRate(params));
+ params[kVPxFmtpMaxFrameRate] = "0";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameRate(params));
+ params[kVPxFmtpMaxFrameRate] = "abcde";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameRate(params));
+}
+
+TEST(SdpVideoFormatUtilsTest, MaxFrameRateIsSpecified) {
+ SdpVideoFormat::Parameters params;
+ params[kVPxFmtpMaxFrameRate] = "30";
+ EXPECT_EQ(ParseSdpForVPxMaxFrameRate(params), 30);
+ params[kVPxFmtpMaxFrameRate] = "60";
+ EXPECT_EQ(ParseSdpForVPxMaxFrameRate(params), 60);
+}
+
+TEST(SdpVideoFormatUtilsTest, MaxFrameSizeIsMissingOrInvalid) {
+ SdpVideoFormat::Parameters params;
+ absl::optional<int> empty = ParseSdpForVPxMaxFrameSize(params);
+ EXPECT_FALSE(empty);
+ params[kVPxFmtpMaxFrameSize] = "-1";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameSize(params));
+ params[kVPxFmtpMaxFrameSize] = "0";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameSize(params));
+ params[kVPxFmtpMaxFrameSize] = "abcde";
+ EXPECT_FALSE(ParseSdpForVPxMaxFrameSize(params));
+}
+
+TEST(SdpVideoFormatUtilsTest, MaxFrameSizeIsSpecified) {
+ SdpVideoFormat::Parameters params;
+ params[kVPxFmtpMaxFrameSize] = "8100"; // 1920 x 1080 / (16^2)
+ EXPECT_EQ(ParseSdpForVPxMaxFrameSize(params), 1920 * 1080);
+ params[kVPxFmtpMaxFrameSize] = "32400"; // 3840 x 2160 / (16^2)
+ EXPECT_EQ(ParseSdpForVPxMaxFrameSize(params), 3840 * 2160);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/media/base/stream_params.cc b/third_party/libwebrtc/media/base/stream_params.cc
new file mode 100644
index 0000000000..0fe1be6ac7
--- /dev/null
+++ b/third_party/libwebrtc/media/base/stream_params.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/stream_params.h"
+
+#include <stdint.h>
+
+#include <list>
+
+#include "absl/algorithm/container.h"
+#include "api/array_view.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace cricket {
+namespace {
+
+void AppendSsrcs(rtc::ArrayView<const uint32_t> ssrcs,
+ rtc::SimpleStringBuilder* sb) {
+ *sb << "ssrcs:[";
+ const char* delimiter = "";
+ for (uint32_t ssrc : ssrcs) {
+ *sb << delimiter << ssrc;
+ delimiter = ",";
+ }
+ *sb << "]";
+}
+
+void AppendSsrcGroups(rtc::ArrayView<const SsrcGroup> ssrc_groups,
+ rtc::SimpleStringBuilder* sb) {
+ *sb << "ssrc_groups:";
+ const char* delimiter = "";
+ for (const SsrcGroup& ssrc_group : ssrc_groups) {
+ *sb << delimiter << ssrc_group.ToString();
+ delimiter = ",";
+ }
+}
+
+void AppendStreamIds(rtc::ArrayView<const std::string> stream_ids,
+ rtc::SimpleStringBuilder* sb) {
+ *sb << "stream_ids:";
+ const char* delimiter = "";
+ for (const std::string& stream_id : stream_ids) {
+ *sb << delimiter << stream_id;
+ delimiter = ",";
+ }
+}
+
+void AppendRids(rtc::ArrayView<const RidDescription> rids,
+ rtc::SimpleStringBuilder* sb) {
+ *sb << "rids:[";
+ const char* delimiter = "";
+ for (const RidDescription& rid : rids) {
+ *sb << delimiter << rid.rid;
+ delimiter = ",";
+ }
+ *sb << "]";
+}
+
+} // namespace
+
+const char kFecSsrcGroupSemantics[] = "FEC";
+const char kFecFrSsrcGroupSemantics[] = "FEC-FR";
+const char kFidSsrcGroupSemantics[] = "FID";
+const char kSimSsrcGroupSemantics[] = "SIM";
+
+bool GetStream(const StreamParamsVec& streams,
+ const StreamSelector& selector,
+ StreamParams* stream_out) {
+ const StreamParams* found = GetStream(streams, selector);
+ if (found && stream_out)
+ *stream_out = *found;
+ return found != nullptr;
+}
+
+SsrcGroup::SsrcGroup(const std::string& usage,
+ const std::vector<uint32_t>& ssrcs)
+ : semantics(usage), ssrcs(ssrcs) {}
+SsrcGroup::SsrcGroup(const SsrcGroup&) = default;
+SsrcGroup::SsrcGroup(SsrcGroup&&) = default;
+SsrcGroup::~SsrcGroup() = default;
+
+SsrcGroup& SsrcGroup::operator=(const SsrcGroup&) = default;
+SsrcGroup& SsrcGroup::operator=(SsrcGroup&&) = default;
+
+bool SsrcGroup::has_semantics(const std::string& semantics_in) const {
+ return (semantics == semantics_in && ssrcs.size() > 0);
+}
+
+std::string SsrcGroup::ToString() const {
+ char buf[1024];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << "{";
+ sb << "semantics:" << semantics << ";";
+ AppendSsrcs(ssrcs, &sb);
+ sb << "}";
+ return sb.str();
+}
+
+StreamParams::StreamParams() = default;
+StreamParams::StreamParams(const StreamParams&) = default;
+StreamParams::StreamParams(StreamParams&&) = default;
+StreamParams::~StreamParams() = default;
+StreamParams& StreamParams::operator=(const StreamParams&) = default;
+StreamParams& StreamParams::operator=(StreamParams&&) = default;
+
+bool StreamParams::operator==(const StreamParams& other) const {
+ return (id == other.id && ssrcs == other.ssrcs &&
+ ssrc_groups == other.ssrc_groups && cname == other.cname &&
+ stream_ids_ == other.stream_ids_ &&
+ // RIDs are not required to be in the same order for equality.
+ absl::c_is_permutation(rids_, other.rids_));
+}
+
+std::string StreamParams::ToString() const {
+ char buf[2 * 1024];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << "{";
+ if (!id.empty()) {
+ sb << "id:" << id << ";";
+ }
+ AppendSsrcs(ssrcs, &sb);
+ sb << ";";
+ AppendSsrcGroups(ssrc_groups, &sb);
+ sb << ";";
+ if (!cname.empty()) {
+ sb << "cname:" << cname << ";";
+ }
+ AppendStreamIds(stream_ids_, &sb);
+ sb << ";";
+ if (!rids_.empty()) {
+ AppendRids(rids_, &sb);
+ sb << ";";
+ }
+ sb << "}";
+ return sb.str();
+}
+
+void StreamParams::GenerateSsrcs(int num_layers,
+ bool generate_fid,
+ bool generate_fec_fr,
+ rtc::UniqueRandomIdGenerator* ssrc_generator) {
+ RTC_DCHECK_GE(num_layers, 0);
+ RTC_DCHECK(ssrc_generator);
+ std::vector<uint32_t> primary_ssrcs;
+ for (int i = 0; i < num_layers; ++i) {
+ uint32_t ssrc = ssrc_generator->GenerateId();
+ primary_ssrcs.push_back(ssrc);
+ add_ssrc(ssrc);
+ }
+
+ if (num_layers > 1) {
+ SsrcGroup simulcast(kSimSsrcGroupSemantics, primary_ssrcs);
+ ssrc_groups.push_back(simulcast);
+ }
+
+ if (generate_fid) {
+ for (uint32_t ssrc : primary_ssrcs) {
+ AddFidSsrc(ssrc, ssrc_generator->GenerateId());
+ }
+ }
+
+ if (generate_fec_fr) {
+ for (uint32_t ssrc : primary_ssrcs) {
+ AddFecFrSsrc(ssrc, ssrc_generator->GenerateId());
+ }
+ }
+}
+
+void StreamParams::GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const {
+ const SsrcGroup* sim_group = get_ssrc_group(kSimSsrcGroupSemantics);
+ if (sim_group == NULL) {
+ ssrcs->push_back(first_ssrc());
+ } else {
+ ssrcs->insert(ssrcs->end(), sim_group->ssrcs.begin(),
+ sim_group->ssrcs.end());
+ }
+}
+
+void StreamParams::GetFidSsrcs(const std::vector<uint32_t>& primary_ssrcs,
+ std::vector<uint32_t>* fid_ssrcs) const {
+ for (uint32_t primary_ssrc : primary_ssrcs) {
+ uint32_t fid_ssrc;
+ if (GetFidSsrc(primary_ssrc, &fid_ssrc)) {
+ fid_ssrcs->push_back(fid_ssrc);
+ }
+ }
+}
+
+bool StreamParams::AddSecondarySsrc(const std::string& semantics,
+ uint32_t primary_ssrc,
+ uint32_t secondary_ssrc) {
+ if (!has_ssrc(primary_ssrc)) {
+ return false;
+ }
+
+ ssrcs.push_back(secondary_ssrc);
+ ssrc_groups.push_back(SsrcGroup(semantics, {primary_ssrc, secondary_ssrc}));
+ return true;
+}
+
+bool StreamParams::GetSecondarySsrc(const std::string& semantics,
+ uint32_t primary_ssrc,
+ uint32_t* secondary_ssrc) const {
+ for (const SsrcGroup& ssrc_group : ssrc_groups) {
+ if (ssrc_group.has_semantics(semantics) && ssrc_group.ssrcs.size() >= 2 &&
+ ssrc_group.ssrcs[0] == primary_ssrc) {
+ *secondary_ssrc = ssrc_group.ssrcs[1];
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<std::string> StreamParams::stream_ids() const {
+ return stream_ids_;
+}
+
+void StreamParams::set_stream_ids(const std::vector<std::string>& stream_ids) {
+ stream_ids_ = stream_ids;
+}
+
+std::string StreamParams::first_stream_id() const {
+ return stream_ids_.empty() ? "" : stream_ids_[0];
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/stream_params.h b/third_party/libwebrtc/media/base/stream_params.h
new file mode 100644
index 0000000000..c9c8a09592
--- /dev/null
+++ b/third_party/libwebrtc/media/base/stream_params.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file contains structures for describing SSRCs from a media source such
+// as a MediaStreamTrack when it is sent across an RTP session. Multiple media
+// sources may be sent across the same RTP session, each of them will be
+// described by one StreamParams object
+// SsrcGroup is used to describe the relationship between the SSRCs that
+// are used for this media source.
+// E.x: Consider a source that is sent as 3 simulcast streams
+// Let the simulcast elements have SSRC 10, 20, 30.
+// Let each simulcast element use FEC and let the protection packets have
+// SSRC 11,21,31.
+// To describe this 4 SsrcGroups are needed,
+// StreamParams would then contain ssrc = {10,11,20,21,30,31} and
+// ssrc_groups = {{SIM,{10,20,30}, {FEC,{10,11}, {FEC, {20,21}, {FEC {30,31}}}
+// Please see RFC 5576.
+// A spec-compliant way to achieve this is to use RIDs and Simulcast attribute
+// instead of the ssrc-group. In this method, the StreamParam object will
+// have multiple RidDescriptions, each corresponding to a simulcast layer
+// and the media section will have a simulcast attribute that indicates
+// that these layers are for the same source. This also removes the extra
+// lines for redundancy streams, as the same RIDs appear in the redundancy
+// packets.
+// Note: in the spec compliant simulcast scenario, some of the RIDs might be
+// alternatives for one another (such as different encodings for same data).
+// In the context of the StreamParams class, the notion of alternatives does
+// not exist and all the RIDs will describe different layers of the same source.
+// When the StreamParams class is used to configure the media engine, simulcast
+// considerations will be used to remove the alternative layers outside of this
+// class.
+// As an example, let the simulcast layers have RID 10, 20, 30.
+// StreamParams would contain rid = { 10, 20, 30 }.
+// MediaSection would contain SimulcastDescription specifying these rids.
+// a=simulcast:send 10;20;30 (or a=simulcast:send 10,20;30 or similar).
+// See https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13
+// and https://tools.ietf.org/html/draft-ietf-mmusic-rid-15.
+
+#ifndef MEDIA_BASE_STREAM_PARAMS_H_
+#define MEDIA_BASE_STREAM_PARAMS_H_
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "media/base/rid_description.h"
+#include "rtc_base/unique_id_generator.h"
+
+namespace cricket {
+
+extern const char kFecSsrcGroupSemantics[];
+extern const char kFecFrSsrcGroupSemantics[];
+extern const char kFidSsrcGroupSemantics[];
+extern const char kSimSsrcGroupSemantics[];
+
+struct SsrcGroup {
+ SsrcGroup(const std::string& usage, const std::vector<uint32_t>& ssrcs);
+ SsrcGroup(const SsrcGroup&);
+ SsrcGroup(SsrcGroup&&);
+ ~SsrcGroup();
+ SsrcGroup& operator=(const SsrcGroup&);
+ SsrcGroup& operator=(SsrcGroup&&);
+
+ bool operator==(const SsrcGroup& other) const {
+ return (semantics == other.semantics && ssrcs == other.ssrcs);
+ }
+ bool operator!=(const SsrcGroup& other) const { return !(*this == other); }
+
+ bool has_semantics(const std::string& semantics) const;
+
+ std::string ToString() const;
+
+ std::string semantics; // e.g FIX, FEC, SIM.
+ std::vector<uint32_t> ssrcs; // SSRCs of this type.
+};
+
+// StreamParams is used to represent a sender/track in a SessionDescription.
+// In Plan B, this means that multiple StreamParams can exist within one
+// MediaContentDescription, while in UnifiedPlan this means that there is one
+// StreamParams per MediaContentDescription.
+struct StreamParams {
+ StreamParams();
+ StreamParams(const StreamParams&);
+ StreamParams(StreamParams&&);
+ ~StreamParams();
+ StreamParams& operator=(const StreamParams&);
+ StreamParams& operator=(StreamParams&&);
+
+ static StreamParams CreateLegacy(uint32_t ssrc) {
+ StreamParams stream;
+ stream.ssrcs.push_back(ssrc);
+ return stream;
+ }
+
+ bool operator==(const StreamParams& other) const;
+ bool operator!=(const StreamParams& other) const { return !(*this == other); }
+
+ uint32_t first_ssrc() const {
+ if (ssrcs.empty()) {
+ return 0;
+ }
+
+ return ssrcs[0];
+ }
+ bool has_ssrcs() const { return !ssrcs.empty(); }
+ bool has_ssrc(uint32_t ssrc) const {
+ return absl::c_linear_search(ssrcs, ssrc);
+ }
+ void add_ssrc(uint32_t ssrc) { ssrcs.push_back(ssrc); }
+ bool has_ssrc_groups() const { return !ssrc_groups.empty(); }
+ bool has_ssrc_group(const std::string& semantics) const {
+ return (get_ssrc_group(semantics) != NULL);
+ }
+ const SsrcGroup* get_ssrc_group(const std::string& semantics) const {
+ for (const SsrcGroup& ssrc_group : ssrc_groups) {
+ if (ssrc_group.has_semantics(semantics)) {
+ return &ssrc_group;
+ }
+ }
+ return NULL;
+ }
+
+ // Convenience function to add an FID ssrc for a primary_ssrc
+ // that's already been added.
+ bool AddFidSsrc(uint32_t primary_ssrc, uint32_t fid_ssrc) {
+ return AddSecondarySsrc(kFidSsrcGroupSemantics, primary_ssrc, fid_ssrc);
+ }
+
+ // Convenience function to lookup the FID ssrc for a primary_ssrc.
+ // Returns false if primary_ssrc not found or FID not defined for it.
+ bool GetFidSsrc(uint32_t primary_ssrc, uint32_t* fid_ssrc) const {
+ return GetSecondarySsrc(kFidSsrcGroupSemantics, primary_ssrc, fid_ssrc);
+ }
+
+ // Convenience function to add an FEC-FR ssrc for a primary_ssrc
+ // that's already been added.
+ bool AddFecFrSsrc(uint32_t primary_ssrc, uint32_t fecfr_ssrc) {
+ return AddSecondarySsrc(kFecFrSsrcGroupSemantics, primary_ssrc, fecfr_ssrc);
+ }
+
+ // Convenience function to lookup the FEC-FR ssrc for a primary_ssrc.
+ // Returns false if primary_ssrc not found or FEC-FR not defined for it.
+ bool GetFecFrSsrc(uint32_t primary_ssrc, uint32_t* fecfr_ssrc) const {
+ return GetSecondarySsrc(kFecFrSsrcGroupSemantics, primary_ssrc, fecfr_ssrc);
+ }
+
+ // Convenience function to populate the StreamParams with the requested number
+ // of SSRCs along with accompanying FID and FEC-FR ssrcs if requested.
+ // SSRCs are generated using the given generator.
+ void GenerateSsrcs(int num_layers,
+ bool generate_fid,
+ bool generate_fec_fr,
+ rtc::UniqueRandomIdGenerator* ssrc_generator);
+
+ // Convenience to get all the SIM SSRCs if there are SIM ssrcs, or
+ // the first SSRC otherwise.
+ void GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const;
+
+ // Convenience to get all the FID SSRCs for the given primary ssrcs.
+ // If a given primary SSRC does not have a FID SSRC, the list of FID
+ // SSRCS will be smaller than the list of primary SSRCs.
+ void GetFidSsrcs(const std::vector<uint32_t>& primary_ssrcs,
+ std::vector<uint32_t>* fid_ssrcs) const;
+
+ // Stream ids serialized to SDP.
+ std::vector<std::string> stream_ids() const;
+ void set_stream_ids(const std::vector<std::string>& stream_ids);
+
+ // Returns the first stream id or "" if none exist. This method exists only
+ // as temporary backwards compatibility with the old sync_label.
+ std::string first_stream_id() const;
+
+ std::string ToString() const;
+
+ // A unique identifier of the StreamParams object. When the SDP is created,
+ // this comes from the track ID of the sender that the StreamParams object
+ // is associated with.
+ std::string id;
+ // There may be no SSRCs stored in unsignaled case when stream_ids are
+ // signaled with a=msid lines.
+ std::vector<uint32_t> ssrcs; // All SSRCs for this source
+ std::vector<SsrcGroup> ssrc_groups; // e.g. FID, FEC, SIM
+ std::string cname; // RTCP CNAME
+
+ // RID functionality according to
+ // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15
+ // Each layer can be represented by a RID identifier and can also have
+ // restrictions (such as max-width, max-height, etc.)
+ // If the track has multiple layers (ex. Simulcast), each layer will be
+ // represented by a RID.
+ bool has_rids() const { return !rids_.empty(); }
+ const std::vector<RidDescription>& rids() const { return rids_; }
+ void set_rids(const std::vector<RidDescription>& rids) { rids_ = rids; }
+
+ private:
+ bool AddSecondarySsrc(const std::string& semantics,
+ uint32_t primary_ssrc,
+ uint32_t secondary_ssrc);
+ bool GetSecondarySsrc(const std::string& semantics,
+ uint32_t primary_ssrc,
+ uint32_t* secondary_ssrc) const;
+
+ // The stream IDs of the sender that the StreamParams object is associated
+ // with. In Plan B this should always be size of 1, while in Unified Plan this
+ // could be none or multiple stream IDs.
+ std::vector<std::string> stream_ids_;
+
+ std::vector<RidDescription> rids_;
+};
+
+// A Stream can be selected by either id or ssrc.
+struct StreamSelector {
+ explicit StreamSelector(uint32_t ssrc) : ssrc(ssrc) {}
+
+ explicit StreamSelector(const std::string& streamid)
+ : ssrc(0), streamid(streamid) {}
+
+ bool Matches(const StreamParams& stream) const {
+ if (ssrc == 0) {
+ return stream.id == streamid;
+ } else {
+ return stream.has_ssrc(ssrc);
+ }
+ }
+
+ uint32_t ssrc;
+ std::string streamid;
+};
+
+typedef std::vector<StreamParams> StreamParamsVec;
+
+template <class Condition>
+const StreamParams* GetStream(const StreamParamsVec& streams,
+ Condition condition) {
+ auto found = absl::c_find_if(streams, condition);
+ return found == streams.end() ? nullptr : &(*found);
+}
+
+template <class Condition>
+StreamParams* GetStream(StreamParamsVec& streams, Condition condition) {
+ auto found = absl::c_find_if(streams, condition);
+ return found == streams.end() ? nullptr : &(*found);
+}
+
+inline bool HasStreamWithNoSsrcs(const StreamParamsVec& streams) {
+ return GetStream(streams,
+ [](const StreamParams& sp) { return !sp.has_ssrcs(); });
+}
+
+inline const StreamParams* GetStreamBySsrc(const StreamParamsVec& streams,
+ uint32_t ssrc) {
+ return GetStream(
+ streams, [&ssrc](const StreamParams& sp) { return sp.has_ssrc(ssrc); });
+}
+
+inline const StreamParams* GetStreamByIds(const StreamParamsVec& streams,
+ const std::string& id) {
+ return GetStream(streams,
+ [&id](const StreamParams& sp) { return sp.id == id; });
+}
+
+inline StreamParams* GetStreamByIds(StreamParamsVec& streams,
+ const std::string& id) {
+ return GetStream(streams,
+ [&id](const StreamParams& sp) { return sp.id == id; });
+}
+
+inline const StreamParams* GetStream(const StreamParamsVec& streams,
+ const StreamSelector& selector) {
+ return GetStream(streams, [&selector](const StreamParams& sp) {
+ return selector.Matches(sp);
+ });
+}
+
+template <class Condition>
+bool RemoveStream(StreamParamsVec* streams, Condition condition) {
+ auto iter(std::remove_if(streams->begin(), streams->end(), condition));
+ if (iter == streams->end())
+ return false;
+ streams->erase(iter, streams->end());
+ return true;
+}
+
+// Removes the stream from streams. Returns true if a stream is
+// found and removed.
+inline bool RemoveStream(StreamParamsVec* streams,
+ const StreamSelector& selector) {
+ return RemoveStream(streams, [&selector](const StreamParams& sp) {
+ return selector.Matches(sp);
+ });
+}
+inline bool RemoveStreamBySsrc(StreamParamsVec* streams, uint32_t ssrc) {
+ return RemoveStream(
+ streams, [&ssrc](const StreamParams& sp) { return sp.has_ssrc(ssrc); });
+}
+inline bool RemoveStreamByIds(StreamParamsVec* streams,
+ const std::string& id) {
+ return RemoveStream(streams,
+ [&id](const StreamParams& sp) { return sp.id == id; });
+}
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_STREAM_PARAMS_H_
diff --git a/third_party/libwebrtc/media/base/stream_params_unittest.cc b/third_party/libwebrtc/media/base/stream_params_unittest.cc
new file mode 100644
index 0000000000..7adf0f517d
--- /dev/null
+++ b/third_party/libwebrtc/media/base/stream_params_unittest.cc
@@ -0,0 +1,301 @@
+/*
+ * 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 "media/base/stream_params.h"
+
+#include <stdint.h>
+
+#include "media/base/test_utils.h"
+#include "rtc_base/arraysize.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::Each;
+using ::testing::Ne;
+
+static const uint32_t kSsrcs1[] = {1};
+static const uint32_t kSsrcs2[] = {1, 2};
+
+static cricket::StreamParams CreateStreamParamsWithSsrcGroup(
+ const std::string& semantics,
+ const uint32_t ssrcs_in[],
+ size_t len) {
+ cricket::StreamParams stream;
+ std::vector<uint32_t> ssrcs(ssrcs_in, ssrcs_in + len);
+ cricket::SsrcGroup sg(semantics, ssrcs);
+ stream.ssrcs = ssrcs;
+ stream.ssrc_groups.push_back(sg);
+ return stream;
+}
+
+TEST(SsrcGroup, EqualNotEqual) {
+ cricket::SsrcGroup ssrc_groups[] = {
+ cricket::SsrcGroup("ABC", MAKE_VECTOR(kSsrcs1)),
+ cricket::SsrcGroup("ABC", MAKE_VECTOR(kSsrcs2)),
+ cricket::SsrcGroup("Abc", MAKE_VECTOR(kSsrcs2)),
+ cricket::SsrcGroup("abc", MAKE_VECTOR(kSsrcs2)),
+ };
+
+ for (size_t i = 0; i < arraysize(ssrc_groups); ++i) {
+ for (size_t j = 0; j < arraysize(ssrc_groups); ++j) {
+ EXPECT_EQ((ssrc_groups[i] == ssrc_groups[j]), (i == j));
+ EXPECT_EQ((ssrc_groups[i] != ssrc_groups[j]), (i != j));
+ }
+ }
+}
+
+TEST(SsrcGroup, HasSemantics) {
+ cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSsrcs1));
+ EXPECT_TRUE(sg1.has_semantics("ABC"));
+
+ cricket::SsrcGroup sg2("Abc", MAKE_VECTOR(kSsrcs1));
+ EXPECT_FALSE(sg2.has_semantics("ABC"));
+
+ cricket::SsrcGroup sg3("abc", MAKE_VECTOR(kSsrcs1));
+ EXPECT_FALSE(sg3.has_semantics("ABC"));
+}
+
+TEST(SsrcGroup, ToString) {
+ cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSsrcs1));
+ EXPECT_STREQ("{semantics:ABC;ssrcs:[1]}", sg1.ToString().c_str());
+}
+
+TEST(StreamParams, CreateLegacy) {
+ const uint32_t ssrc = 7;
+ cricket::StreamParams one_sp = cricket::StreamParams::CreateLegacy(ssrc);
+ EXPECT_EQ(1U, one_sp.ssrcs.size());
+ EXPECT_EQ(ssrc, one_sp.first_ssrc());
+ EXPECT_TRUE(one_sp.has_ssrcs());
+ EXPECT_TRUE(one_sp.has_ssrc(ssrc));
+ EXPECT_FALSE(one_sp.has_ssrc(ssrc + 1));
+ EXPECT_FALSE(one_sp.has_ssrc_groups());
+ EXPECT_EQ(0U, one_sp.ssrc_groups.size());
+}
+
+TEST(StreamParams, HasSsrcGroup) {
+ cricket::StreamParams sp =
+ CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, arraysize(kSsrcs2));
+ EXPECT_EQ(2U, sp.ssrcs.size());
+ EXPECT_EQ(kSsrcs2[0], sp.first_ssrc());
+ EXPECT_TRUE(sp.has_ssrcs());
+ EXPECT_TRUE(sp.has_ssrc(kSsrcs2[0]));
+ EXPECT_TRUE(sp.has_ssrc(kSsrcs2[1]));
+ EXPECT_TRUE(sp.has_ssrc_group("XYZ"));
+ EXPECT_EQ(1U, sp.ssrc_groups.size());
+ EXPECT_EQ(2U, sp.ssrc_groups[0].ssrcs.size());
+ EXPECT_EQ(kSsrcs2[0], sp.ssrc_groups[0].ssrcs[0]);
+ EXPECT_EQ(kSsrcs2[1], sp.ssrc_groups[0].ssrcs[1]);
+}
+
+TEST(StreamParams, GetSsrcGroup) {
+ cricket::StreamParams sp =
+ CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, arraysize(kSsrcs2));
+ EXPECT_EQ(NULL, sp.get_ssrc_group("xyz"));
+ EXPECT_EQ(&sp.ssrc_groups[0], sp.get_ssrc_group("XYZ"));
+}
+
+TEST(StreamParams, HasStreamWithNoSsrcs) {
+ cricket::StreamParams sp_1 = cricket::StreamParams::CreateLegacy(kSsrcs1[0]);
+ cricket::StreamParams sp_2 = cricket::StreamParams::CreateLegacy(kSsrcs2[0]);
+ std::vector<cricket::StreamParams> streams({sp_1, sp_2});
+ EXPECT_FALSE(HasStreamWithNoSsrcs(streams));
+
+ cricket::StreamParams unsignaled_stream;
+ streams.push_back(unsignaled_stream);
+ EXPECT_TRUE(HasStreamWithNoSsrcs(streams));
+}
+
+TEST(StreamParams, EqualNotEqual) {
+ cricket::StreamParams l1 = cricket::StreamParams::CreateLegacy(1);
+ cricket::StreamParams l2 = cricket::StreamParams::CreateLegacy(2);
+ cricket::StreamParams sg1 =
+ CreateStreamParamsWithSsrcGroup("ABC", kSsrcs1, arraysize(kSsrcs1));
+ cricket::StreamParams sg2 =
+ CreateStreamParamsWithSsrcGroup("ABC", kSsrcs2, arraysize(kSsrcs2));
+ cricket::StreamParams sg3 =
+ CreateStreamParamsWithSsrcGroup("Abc", kSsrcs2, arraysize(kSsrcs2));
+ cricket::StreamParams sg4 =
+ CreateStreamParamsWithSsrcGroup("abc", kSsrcs2, arraysize(kSsrcs2));
+ cricket::StreamParams sps[] = {l1, l2, sg1, sg2, sg3, sg4};
+
+ for (size_t i = 0; i < arraysize(sps); ++i) {
+ for (size_t j = 0; j < arraysize(sps); ++j) {
+ EXPECT_EQ((sps[i] == sps[j]), (i == j));
+ EXPECT_EQ((sps[i] != sps[j]), (i != j));
+ }
+ }
+}
+
+TEST(StreamParams, FidFunctions) {
+ uint32_t fid_ssrc;
+
+ cricket::StreamParams sp = cricket::StreamParams::CreateLegacy(1);
+ EXPECT_FALSE(sp.AddFidSsrc(10, 20));
+ EXPECT_TRUE(sp.AddFidSsrc(1, 2));
+ EXPECT_TRUE(sp.GetFidSsrc(1, &fid_ssrc));
+ EXPECT_EQ(2u, fid_ssrc);
+ EXPECT_FALSE(sp.GetFidSsrc(15, &fid_ssrc));
+
+ sp.add_ssrc(20);
+ EXPECT_TRUE(sp.AddFidSsrc(20, 30));
+ EXPECT_TRUE(sp.GetFidSsrc(20, &fid_ssrc));
+ EXPECT_EQ(30u, fid_ssrc);
+
+ // Manually create SsrcGroup to test bounds-checking
+ // in GetSecondarySsrc. We construct an invalid StreamParams
+ // for this.
+ std::vector<uint32_t> fid_vector;
+ fid_vector.push_back(13);
+ cricket::SsrcGroup invalid_fid_group(cricket::kFidSsrcGroupSemantics,
+ fid_vector);
+ cricket::StreamParams sp_invalid;
+ sp_invalid.add_ssrc(13);
+ sp_invalid.ssrc_groups.push_back(invalid_fid_group);
+ EXPECT_FALSE(sp_invalid.GetFidSsrc(13, &fid_ssrc));
+}
+
+TEST(StreamParams, GetPrimaryAndFidSsrcs) {
+ cricket::StreamParams sp;
+ sp.ssrcs.push_back(1);
+ sp.ssrcs.push_back(2);
+ sp.ssrcs.push_back(3);
+
+ std::vector<uint32_t> primary_ssrcs;
+ sp.GetPrimarySsrcs(&primary_ssrcs);
+ std::vector<uint32_t> fid_ssrcs;
+ sp.GetFidSsrcs(primary_ssrcs, &fid_ssrcs);
+ ASSERT_EQ(1u, primary_ssrcs.size());
+ EXPECT_EQ(1u, primary_ssrcs[0]);
+ ASSERT_EQ(0u, fid_ssrcs.size());
+
+ sp.ssrc_groups.push_back(
+ cricket::SsrcGroup(cricket::kSimSsrcGroupSemantics, sp.ssrcs));
+ sp.AddFidSsrc(1, 10);
+ sp.AddFidSsrc(2, 20);
+
+ primary_ssrcs.clear();
+ sp.GetPrimarySsrcs(&primary_ssrcs);
+ fid_ssrcs.clear();
+ sp.GetFidSsrcs(primary_ssrcs, &fid_ssrcs);
+ ASSERT_EQ(3u, primary_ssrcs.size());
+ EXPECT_EQ(1u, primary_ssrcs[0]);
+ EXPECT_EQ(2u, primary_ssrcs[1]);
+ EXPECT_EQ(3u, primary_ssrcs[2]);
+ ASSERT_EQ(2u, fid_ssrcs.size());
+ EXPECT_EQ(10u, fid_ssrcs[0]);
+ EXPECT_EQ(20u, fid_ssrcs[1]);
+}
+
+TEST(StreamParams, FecFrFunctions) {
+ uint32_t fecfr_ssrc;
+
+ cricket::StreamParams sp = cricket::StreamParams::CreateLegacy(1);
+ EXPECT_FALSE(sp.AddFecFrSsrc(10, 20));
+ EXPECT_TRUE(sp.AddFecFrSsrc(1, 2));
+ EXPECT_TRUE(sp.GetFecFrSsrc(1, &fecfr_ssrc));
+ EXPECT_EQ(2u, fecfr_ssrc);
+ EXPECT_FALSE(sp.GetFecFrSsrc(15, &fecfr_ssrc));
+
+ sp.add_ssrc(20);
+ EXPECT_TRUE(sp.AddFecFrSsrc(20, 30));
+ EXPECT_TRUE(sp.GetFecFrSsrc(20, &fecfr_ssrc));
+ EXPECT_EQ(30u, fecfr_ssrc);
+
+ // Manually create SsrcGroup to test bounds-checking
+ // in GetSecondarySsrc. We construct an invalid StreamParams
+ // for this.
+ std::vector<uint32_t> fecfr_vector;
+ fecfr_vector.push_back(13);
+ cricket::SsrcGroup invalid_fecfr_group(cricket::kFecFrSsrcGroupSemantics,
+ fecfr_vector);
+ cricket::StreamParams sp_invalid;
+ sp_invalid.add_ssrc(13);
+ sp_invalid.ssrc_groups.push_back(invalid_fecfr_group);
+ EXPECT_FALSE(sp_invalid.GetFecFrSsrc(13, &fecfr_ssrc));
+}
+
+TEST(StreamParams, ToString) {
+ cricket::StreamParams sp =
+ CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, arraysize(kSsrcs2));
+ sp.set_stream_ids({"stream_id"});
+ EXPECT_STREQ(
+ "{ssrcs:[1,2];ssrc_groups:{semantics:XYZ;ssrcs:[1,2]};stream_ids:stream_"
+ "id;}",
+ sp.ToString().c_str());
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtxAndFlex) {
+ rtc::UniqueRandomIdGenerator generator;
+ cricket::StreamParams stream;
+ stream.GenerateSsrcs(1, true, true, &generator);
+ uint32_t primary_ssrc = stream.first_ssrc();
+ ASSERT_NE(0u, primary_ssrc);
+ uint32_t rtx_ssrc = 0;
+ uint32_t flex_ssrc = 0;
+ EXPECT_EQ(3u, stream.ssrcs.size());
+ EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+ EXPECT_NE(0u, rtx_ssrc);
+ EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+ EXPECT_NE(0u, flex_ssrc);
+ EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtx) {
+ rtc::UniqueRandomIdGenerator generator;
+ cricket::StreamParams stream;
+ stream.GenerateSsrcs(1, true, false, &generator);
+ uint32_t primary_ssrc = stream.first_ssrc();
+ ASSERT_NE(0u, primary_ssrc);
+ uint32_t rtx_ssrc = 0;
+ uint32_t flex_ssrc = 0;
+ EXPECT_EQ(2u, stream.ssrcs.size());
+ EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+ EXPECT_NE(0u, rtx_ssrc);
+ EXPECT_FALSE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+ EXPECT_EQ(0u, flex_ssrc);
+ EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithFlex) {
+ rtc::UniqueRandomIdGenerator generator;
+ cricket::StreamParams stream;
+ stream.GenerateSsrcs(1, false, true, &generator);
+ uint32_t primary_ssrc = stream.first_ssrc();
+ ASSERT_NE(0u, primary_ssrc);
+ uint32_t rtx_ssrc = 0;
+ uint32_t flex_ssrc = 0;
+ EXPECT_EQ(2u, stream.ssrcs.size());
+ EXPECT_FALSE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
+ EXPECT_EQ(0u, rtx_ssrc);
+ EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
+ EXPECT_NE(0u, flex_ssrc);
+ EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
+}
+
+TEST(StreamParams, TestGenerateSsrcs_SimulcastLayersAndRtx) {
+ const size_t kNumStreams = 3;
+ rtc::UniqueRandomIdGenerator generator;
+ cricket::StreamParams stream;
+ stream.GenerateSsrcs(kNumStreams, true, false, &generator);
+ EXPECT_EQ(kNumStreams * 2, stream.ssrcs.size());
+ std::vector<uint32_t> primary_ssrcs, rtx_ssrcs;
+ stream.GetPrimarySsrcs(&primary_ssrcs);
+ EXPECT_EQ(kNumStreams, primary_ssrcs.size());
+ EXPECT_THAT(primary_ssrcs, Each(Ne(0u)));
+ stream.GetFidSsrcs(primary_ssrcs, &rtx_ssrcs);
+ EXPECT_EQ(kNumStreams, rtx_ssrcs.size());
+ EXPECT_THAT(rtx_ssrcs, Each(Ne(0u)));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
+ EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
+}
diff --git a/third_party/libwebrtc/media/base/test_utils.cc b/third_party/libwebrtc/media/base/test_utils.cc
new file mode 100644
index 0000000000..a6d5f61c17
--- /dev/null
+++ b/third_party/libwebrtc/media/base/test_utils.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/test_utils.h"
+
+#include <cstdint>
+
+#include "api/video/video_frame.h"
+#include "api/video/video_source_interface.h"
+
+namespace cricket {
+
+cricket::StreamParams CreateSimStreamParams(
+ const std::string& cname,
+ const std::vector<uint32_t>& ssrcs) {
+ cricket::StreamParams sp;
+ cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs);
+ sp.ssrcs = ssrcs;
+ sp.ssrc_groups.push_back(sg);
+ sp.cname = cname;
+ return sp;
+}
+
+// There should be an rtx_ssrc per ssrc.
+cricket::StreamParams CreateSimWithRtxStreamParams(
+ const std::string& cname,
+ const std::vector<uint32_t>& ssrcs,
+ const std::vector<uint32_t>& rtx_ssrcs) {
+ cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs);
+ for (size_t i = 0; i < ssrcs.size(); ++i) {
+ sp.ssrcs.push_back(rtx_ssrcs[i]);
+ std::vector<uint32_t> fid_ssrcs;
+ fid_ssrcs.push_back(ssrcs[i]);
+ fid_ssrcs.push_back(rtx_ssrcs[i]);
+ cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs);
+ sp.ssrc_groups.push_back(fid_group);
+ }
+ return sp;
+}
+
+cricket::StreamParams CreatePrimaryWithFecFrStreamParams(
+ const std::string& cname,
+ uint32_t primary_ssrc,
+ uint32_t flexfec_ssrc) {
+ cricket::StreamParams sp;
+ cricket::SsrcGroup sg(cricket::kFecFrSsrcGroupSemantics,
+ {primary_ssrc, flexfec_ssrc});
+ sp.ssrcs = {primary_ssrc};
+ sp.ssrc_groups.push_back(sg);
+ sp.cname = cname;
+ return sp;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/test_utils.h b/third_party/libwebrtc/media/base/test_utils.h
new file mode 100644
index 0000000000..fb18485d32
--- /dev/null
+++ b/third_party/libwebrtc/media/base/test_utils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_TEST_UTILS_H_
+#define MEDIA_BASE_TEST_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "media/base/media_channel.h"
+#include "media/base/video_common.h"
+#include "rtc_base/arraysize.h"
+
+namespace webrtc {
+class VideoFrame;
+}
+
+namespace cricket {
+
+// Returns size of 420 image with rounding on chroma for odd sizes.
+#define I420_SIZE(w, h) (w * h + (((w + 1) / 2) * ((h + 1) / 2)) * 2)
+// Returns size of ARGB image.
+#define ARGB_SIZE(w, h) (w * h * 4)
+
+template <class T>
+inline std::vector<T> MakeVector(const T a[], size_t s) {
+ return std::vector<T>(a, a + s);
+}
+#define MAKE_VECTOR(a) cricket::MakeVector(a, arraysize(a))
+
+// Checks whether `codecs` contains `codec`; checks using Codec::Matches().
+template <class C>
+bool ContainsMatchingCodec(const std::vector<C>& codecs,
+ const C& codec,
+ const webrtc::FieldTrialsView* field_trials) {
+ typename std::vector<C>::const_iterator it;
+ for (it = codecs.begin(); it != codecs.end(); ++it) {
+ if (it->Matches(codec, field_trials)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Create Simulcast StreamParams with given `ssrcs` and `cname`.
+cricket::StreamParams CreateSimStreamParams(const std::string& cname,
+ const std::vector<uint32_t>& ssrcs);
+// Create Simulcast stream with given `ssrcs` and `rtx_ssrcs`.
+// The number of `rtx_ssrcs` must match number of `ssrcs`.
+cricket::StreamParams CreateSimWithRtxStreamParams(
+ const std::string& cname,
+ const std::vector<uint32_t>& ssrcs,
+ const std::vector<uint32_t>& rtx_ssrcs);
+
+// Create StreamParams with single primary SSRC and corresponding FlexFEC SSRC.
+cricket::StreamParams CreatePrimaryWithFecFrStreamParams(
+ const std::string& cname,
+ uint32_t primary_ssrc,
+ uint32_t flexfec_ssrc);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_TEST_UTILS_H_
diff --git a/third_party/libwebrtc/media/base/turn_utils.cc b/third_party/libwebrtc/media/base/turn_utils.cc
new file mode 100644
index 0000000000..c413117fb6
--- /dev/null
+++ b/third_party/libwebrtc/media/base/turn_utils.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/turn_utils.h"
+
+#include "api/transport/stun.h"
+#include "rtc_base/byte_order.h"
+
+namespace cricket {
+
+namespace {
+
+const size_t kTurnChannelHeaderLength = 4;
+
+bool IsTurnChannelData(const uint8_t* data, size_t length) {
+ return length >= kTurnChannelHeaderLength && ((*data & 0xC0) == 0x40);
+}
+
+bool IsTurnSendIndicationPacket(const uint8_t* data, size_t length) {
+ if (length < kStunHeaderSize) {
+ return false;
+ }
+
+ uint16_t type = rtc::GetBE16(data);
+ return (type == TURN_SEND_INDICATION);
+}
+
+} // namespace
+
+bool UnwrapTurnPacket(const uint8_t* packet,
+ size_t packet_size,
+ size_t* content_position,
+ size_t* content_size) {
+ if (IsTurnChannelData(packet, packet_size)) {
+ // Turn Channel Message header format.
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Channel Number | Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // / Application Data /
+ // / /
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ size_t length = rtc::GetBE16(&packet[2]);
+ if (length + kTurnChannelHeaderLength > packet_size) {
+ return false;
+ }
+
+ *content_position = kTurnChannelHeaderLength;
+ *content_size = length;
+ return true;
+ }
+
+ if (IsTurnSendIndicationPacket(packet, packet_size)) {
+ // Validate STUN message length.
+ const size_t stun_message_length = rtc::GetBE16(&packet[2]);
+ if (stun_message_length + kStunHeaderSize != packet_size) {
+ return false;
+ }
+
+ // First skip mandatory stun header which is of 20 bytes.
+ size_t pos = kStunHeaderSize;
+ // Loop through STUN attributes until we find STUN DATA attribute.
+ while (pos < packet_size) {
+ // Keep reading STUN attributes until we hit DATA attribute.
+ // Attribute will be a TLV structure.
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Type | Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Value (variable) ....
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // The value in the length field MUST contain the length of the Value
+ // part of the attribute, prior to padding, measured in bytes. Since
+ // STUN aligns attributes on 32-bit boundaries, attributes whose content
+ // is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of
+ // padding so that its value contains a multiple of 4 bytes. The
+ // padding bits are ignored, and may be any value.
+ uint16_t attr_type, attr_length;
+ const int kAttrHeaderLength = sizeof(attr_type) + sizeof(attr_length);
+
+ if (packet_size < pos + kAttrHeaderLength) {
+ return false;
+ }
+
+ // Getting attribute type and length.
+ attr_type = rtc::GetBE16(&packet[pos]);
+ attr_length = rtc::GetBE16(&packet[pos + sizeof(attr_type)]);
+
+ pos += kAttrHeaderLength; // Skip STUN_DATA_ATTR header.
+
+ // Checking for bogus attribute length.
+ if (pos + attr_length > packet_size) {
+ return false;
+ }
+
+ if (attr_type == STUN_ATTR_DATA) {
+ *content_position = pos;
+ *content_size = attr_length;
+ return true;
+ }
+
+ pos += attr_length;
+ if ((attr_length % 4) != 0) {
+ pos += (4 - (attr_length % 4));
+ }
+ }
+
+ // There is no data attribute present in the message.
+ return false;
+ }
+
+ // This is not a TURN packet.
+ *content_position = 0;
+ *content_size = packet_size;
+ return true;
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/turn_utils.h b/third_party/libwebrtc/media/base/turn_utils.h
new file mode 100644
index 0000000000..82e492c028
--- /dev/null
+++ b/third_party/libwebrtc/media/base/turn_utils.h
@@ -0,0 +1,30 @@
+/*
+ * 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 MEDIA_BASE_TURN_UTILS_H_
+#define MEDIA_BASE_TURN_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace cricket {
+
+// Finds data location within a TURN Channel Message or TURN Send Indication
+// message.
+bool RTC_EXPORT UnwrapTurnPacket(const uint8_t* packet,
+ size_t packet_size,
+ size_t* content_position,
+ size_t* content_size);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_TURN_UTILS_H_
diff --git a/third_party/libwebrtc/media/base/turn_utils_unittest.cc b/third_party/libwebrtc/media/base/turn_utils_unittest.cc
new file mode 100644
index 0000000000..f7bbf8b8d4
--- /dev/null
+++ b/third_party/libwebrtc/media/base/turn_utils_unittest.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 "media/base/turn_utils.h"
+
+#include "test/gtest.h"
+
+namespace cricket {
+
+// Invalid TURN send indication messages. Messages are proper STUN
+// messages with incorrect values in attributes.
+TEST(TurnUtilsTest, InvalidTurnSendIndicationMessages) {
+ size_t content_pos = SIZE_MAX;
+ size_t content_size = SIZE_MAX;
+
+ // Stun Indication message with Zero length
+ uint8_t kTurnSendIndicationMsgWithNoAttributes[] = {
+ 0x00, 0x16, 0x00, 0x00, // Zero length
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7', '8', '9', 'a', 'b',
+ };
+ EXPECT_FALSE(UnwrapTurnPacket(kTurnSendIndicationMsgWithNoAttributes,
+ sizeof(kTurnSendIndicationMsgWithNoAttributes),
+ &content_pos, &content_size));
+ EXPECT_EQ(SIZE_MAX, content_pos);
+ EXPECT_EQ(SIZE_MAX, content_size);
+
+ // Stun Send Indication message with invalid length in stun header.
+ const uint8_t kTurnSendIndicationMsgWithInvalidLength[] = {
+ 0x00, 0x16, 0xFF, 0x00, // length of 0xFF00
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7', '8', '9', 'a', 'b',
+ };
+ EXPECT_FALSE(UnwrapTurnPacket(kTurnSendIndicationMsgWithInvalidLength,
+ sizeof(kTurnSendIndicationMsgWithInvalidLength),
+ &content_pos, &content_size));
+ EXPECT_EQ(SIZE_MAX, content_pos);
+ EXPECT_EQ(SIZE_MAX, content_size);
+
+ // Stun Send Indication message with no DATA attribute in message.
+ const uint8_t kTurnSendIndicatinMsgWithNoDataAttribute[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x00, 0x16, 0x00, 0x08, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7', '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ // clang-format on
+ };
+ EXPECT_FALSE(
+ UnwrapTurnPacket(kTurnSendIndicatinMsgWithNoDataAttribute,
+ sizeof(kTurnSendIndicatinMsgWithNoDataAttribute),
+ &content_pos, &content_size));
+ EXPECT_EQ(SIZE_MAX, content_pos);
+ EXPECT_EQ(SIZE_MAX, content_size);
+}
+
+// Valid TURN Send Indication messages.
+TEST(TurnUtilsTest, ValidTurnSendIndicationMessage) {
+ size_t content_pos = SIZE_MAX;
+ size_t content_size = SIZE_MAX;
+ // A valid STUN indication message with a valid RTP header in data attribute
+ // payload field and no extension bit set.
+ const uint8_t kTurnSendIndicationMsgWithoutRtpExtension[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x00, 0x16, 0x00, 0x18, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7', '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x13, 0x00, 0x0C, // Data attribute.
+ 0x80, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // clang-format on
+ };
+ EXPECT_TRUE(
+ UnwrapTurnPacket(kTurnSendIndicationMsgWithoutRtpExtension,
+ sizeof(kTurnSendIndicationMsgWithoutRtpExtension),
+ &content_pos, &content_size));
+ EXPECT_EQ(12U, content_size);
+ EXPECT_EQ(32U, content_pos);
+}
+
+// Verify that parsing of valid TURN Channel Messages.
+TEST(TurnUtilsTest, ValidTurnChannelMessages) {
+ const uint8_t kTurnChannelMsgWithRtpPacket[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 0x40, 0x00, 0x00, 0x0C,
+ 0x80, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // clang-format on
+ };
+
+ size_t content_pos = 0, content_size = 0;
+ EXPECT_TRUE(UnwrapTurnPacket(kTurnChannelMsgWithRtpPacket,
+ sizeof(kTurnChannelMsgWithRtpPacket),
+ &content_pos, &content_size));
+ EXPECT_EQ(12U, content_size);
+ EXPECT_EQ(4U, content_pos);
+}
+
+TEST(TurnUtilsTest, ChannelMessageZeroLength) {
+ const uint8_t kTurnChannelMsgWithZeroLength[] = {0x40, 0x00, 0x00, 0x00};
+ size_t content_pos = SIZE_MAX;
+ size_t content_size = SIZE_MAX;
+ EXPECT_TRUE(UnwrapTurnPacket(kTurnChannelMsgWithZeroLength,
+ sizeof(kTurnChannelMsgWithZeroLength),
+ &content_pos, &content_size));
+ EXPECT_EQ(4u, content_pos);
+ EXPECT_EQ(0u, content_size);
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/video_adapter.cc b/third_party/libwebrtc/media/base/video_adapter.cc
new file mode 100644
index 0000000000..149071d153
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_adapter.cc
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/video_adapter.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <utility>
+
+#include "absl/types/optional.h"
+#include "media/base/video_common.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace {
+
+struct Fraction {
+ int numerator;
+ int denominator;
+
+ void DivideByGcd() {
+ int g = cricket::GreatestCommonDivisor(numerator, denominator);
+ numerator /= g;
+ denominator /= g;
+ }
+
+ // Determines number of output pixels if both width and height of an input of
+ // `input_pixels` pixels is scaled with the fraction numerator / denominator.
+ int scale_pixel_count(int input_pixels) {
+ return (numerator * numerator * input_pixels) / (denominator * denominator);
+ }
+};
+
+// Round `value_to_round` to a multiple of `multiple`. Prefer rounding upwards,
+// but never more than `max_value`.
+int roundUp(int value_to_round, int multiple, int max_value) {
+ const int rounded_value =
+ (value_to_round + multiple - 1) / multiple * multiple;
+ return rounded_value <= max_value ? rounded_value
+ : (max_value / multiple * multiple);
+}
+
+// Generates a scale factor that makes `input_pixels` close to `target_pixels`,
+// but no higher than `max_pixels`.
+Fraction FindScale(int input_width,
+ int input_height,
+ int target_pixels,
+ int max_pixels,
+ bool variable_start_scale_factor) {
+ // This function only makes sense for a positive target.
+ RTC_DCHECK_GT(target_pixels, 0);
+ RTC_DCHECK_GT(max_pixels, 0);
+ RTC_DCHECK_GE(max_pixels, target_pixels);
+
+ const int input_pixels = input_width * input_height;
+
+ // Don't scale up original.
+ if (target_pixels >= input_pixels)
+ return Fraction{1, 1};
+
+ Fraction current_scale = Fraction{1, 1};
+ Fraction best_scale = Fraction{1, 1};
+
+ if (variable_start_scale_factor) {
+ // Start scaling down by 2/3 depending on `input_width` and `input_height`.
+ if (input_width % 3 == 0 && input_height % 3 == 0) {
+ // 2/3 (then alternates 3/4, 2/3, 3/4,...).
+ current_scale = Fraction{6, 6};
+ }
+ if (input_width % 9 == 0 && input_height % 9 == 0) {
+ // 2/3, 2/3 (then alternates 3/4, 2/3, 3/4,...).
+ current_scale = Fraction{36, 36};
+ }
+ }
+
+ // The minimum (absolute) difference between the number of output pixels and
+ // the target pixel count.
+ int min_pixel_diff = std::numeric_limits<int>::max();
+ if (input_pixels <= max_pixels) {
+ // Start condition for 1/1 case, if it is less than max.
+ min_pixel_diff = std::abs(input_pixels - target_pixels);
+ }
+
+ // Alternately scale down by 3/4 and 2/3. This results in fractions which are
+ // effectively scalable. For instance, starting at 1280x720 will result in
+ // the series (3/4) => 960x540, (1/2) => 640x360, (3/8) => 480x270,
+ // (1/4) => 320x180, (3/16) => 240x125, (1/8) => 160x90.
+ while (current_scale.scale_pixel_count(input_pixels) > target_pixels) {
+ if (current_scale.numerator % 3 == 0 &&
+ current_scale.denominator % 2 == 0) {
+ // Multiply by 2/3.
+ current_scale.numerator /= 3;
+ current_scale.denominator /= 2;
+ } else {
+ // Multiply by 3/4.
+ current_scale.numerator *= 3;
+ current_scale.denominator *= 4;
+ }
+
+ int output_pixels = current_scale.scale_pixel_count(input_pixels);
+ if (output_pixels <= max_pixels) {
+ int diff = std::abs(target_pixels - output_pixels);
+ if (diff < min_pixel_diff) {
+ min_pixel_diff = diff;
+ best_scale = current_scale;
+ }
+ }
+ }
+ best_scale.DivideByGcd();
+
+ return best_scale;
+}
+
+absl::optional<std::pair<int, int>> Swap(
+ const absl::optional<std::pair<int, int>>& in) {
+ if (!in) {
+ return absl::nullopt;
+ }
+ return std::make_pair(in->second, in->first);
+}
+
+} // namespace
+
+namespace cricket {
+
+VideoAdapter::VideoAdapter(int source_resolution_alignment)
+ : frames_in_(0),
+ frames_out_(0),
+ frames_scaled_(0),
+ adaption_changes_(0),
+ previous_width_(0),
+ previous_height_(0),
+ variable_start_scale_factor_(!webrtc::field_trial::IsDisabled(
+ "WebRTC-Video-VariableStartScaleFactor")),
+ source_resolution_alignment_(source_resolution_alignment),
+ resolution_alignment_(source_resolution_alignment),
+ resolution_request_target_pixel_count_(std::numeric_limits<int>::max()),
+ resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
+ max_framerate_request_(std::numeric_limits<int>::max()) {}
+
+VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
+
+VideoAdapter::~VideoAdapter() {}
+
+bool VideoAdapter::DropFrame(int64_t in_timestamp_ns) {
+ int max_fps = max_framerate_request_;
+ if (output_format_request_.max_fps)
+ max_fps = std::min(max_fps, *output_format_request_.max_fps);
+
+ framerate_controller_.SetMaxFramerate(max_fps);
+ return framerate_controller_.ShouldDropFrame(in_timestamp_ns);
+}
+
+bool VideoAdapter::AdaptFrameResolution(int in_width,
+ int in_height,
+ int64_t in_timestamp_ns,
+ int* cropped_width,
+ int* cropped_height,
+ int* out_width,
+ int* out_height) {
+ webrtc::MutexLock lock(&mutex_);
+ ++frames_in_;
+
+ // The max output pixel count is the minimum of the requests from
+ // OnOutputFormatRequest and OnResolutionFramerateRequest.
+ int max_pixel_count = resolution_request_max_pixel_count_;
+
+ // Select target aspect ratio and max pixel count depending on input frame
+ // orientation.
+ absl::optional<std::pair<int, int>> target_aspect_ratio;
+ if (in_width > in_height) {
+ target_aspect_ratio = output_format_request_.target_landscape_aspect_ratio;
+ if (output_format_request_.max_landscape_pixel_count)
+ max_pixel_count = std::min(
+ max_pixel_count, *output_format_request_.max_landscape_pixel_count);
+ } else {
+ target_aspect_ratio = output_format_request_.target_portrait_aspect_ratio;
+ if (output_format_request_.max_portrait_pixel_count)
+ max_pixel_count = std::min(
+ max_pixel_count, *output_format_request_.max_portrait_pixel_count);
+ }
+
+ int target_pixel_count =
+ std::min(resolution_request_target_pixel_count_, max_pixel_count);
+
+ // Drop the input frame if necessary.
+ if (max_pixel_count <= 0 || DropFrame(in_timestamp_ns)) {
+ // Show VAdapt log every 90 frames dropped. (3 seconds)
+ if ((frames_in_ - frames_out_) % 90 == 0) {
+ // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
+ // in default calls.
+ RTC_LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
+ << " / out " << frames_out_ << " / in " << frames_in_
+ << " Changes: " << adaption_changes_
+ << " Input: " << in_width << "x" << in_height
+ << " timestamp: " << in_timestamp_ns
+ << " Output fps: " << max_framerate_request_ << "/"
+ << output_format_request_.max_fps.value_or(-1)
+ << " alignment: " << resolution_alignment_;
+ }
+
+ // Drop frame.
+ return false;
+ }
+
+ // Calculate how the input should be cropped.
+ if (!target_aspect_ratio || target_aspect_ratio->first <= 0 ||
+ target_aspect_ratio->second <= 0) {
+ *cropped_width = in_width;
+ *cropped_height = in_height;
+ } else {
+ const float requested_aspect =
+ target_aspect_ratio->first /
+ static_cast<float>(target_aspect_ratio->second);
+ *cropped_width =
+ std::min(in_width, static_cast<int>(in_height * requested_aspect));
+ *cropped_height =
+ std::min(in_height, static_cast<int>(in_width / requested_aspect));
+ }
+ const Fraction scale =
+ FindScale(*cropped_width, *cropped_height, target_pixel_count,
+ max_pixel_count, variable_start_scale_factor_);
+ // Adjust cropping slightly to get correctly aligned output size and a perfect
+ // scale factor.
+ *cropped_width = roundUp(*cropped_width,
+ scale.denominator * resolution_alignment_, in_width);
+ *cropped_height = roundUp(
+ *cropped_height, scale.denominator * resolution_alignment_, in_height);
+ RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
+ RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
+
+ // Calculate final output size.
+ *out_width = *cropped_width / scale.denominator * scale.numerator;
+ *out_height = *cropped_height / scale.denominator * scale.numerator;
+ RTC_DCHECK_EQ(0, *out_width % resolution_alignment_);
+ RTC_DCHECK_EQ(0, *out_height % resolution_alignment_);
+
+ ++frames_out_;
+ if (scale.numerator != scale.denominator)
+ ++frames_scaled_;
+
+ if (previous_width_ &&
+ (previous_width_ != *out_width || previous_height_ != *out_height)) {
+ ++adaption_changes_;
+ RTC_LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_
+ << " / out " << frames_out_ << " / in " << frames_in_
+ << " Changes: " << adaption_changes_
+ << " Input: " << in_width << "x" << in_height
+ << " Scale: " << scale.numerator << "/"
+ << scale.denominator << " Output: " << *out_width << "x"
+ << *out_height << " fps: " << max_framerate_request_ << "/"
+ << output_format_request_.max_fps.value_or(-1)
+ << " alignment: " << resolution_alignment_;
+ }
+
+ previous_width_ = *out_width;
+ previous_height_ = *out_height;
+
+ return true;
+}
+
+void VideoAdapter::OnOutputFormatRequest(
+ const absl::optional<VideoFormat>& format) {
+ absl::optional<std::pair<int, int>> target_aspect_ratio;
+ absl::optional<int> max_pixel_count;
+ absl::optional<int> max_fps;
+ if (format) {
+ target_aspect_ratio = std::make_pair(format->width, format->height);
+ max_pixel_count = format->width * format->height;
+ if (format->interval > 0)
+ max_fps = rtc::kNumNanosecsPerSec / format->interval;
+ }
+ OnOutputFormatRequest(target_aspect_ratio, max_pixel_count, max_fps);
+}
+
+void VideoAdapter::OnOutputFormatRequest(
+ const absl::optional<std::pair<int, int>>& target_aspect_ratio,
+ const absl::optional<int>& max_pixel_count,
+ const absl::optional<int>& max_fps) {
+ absl::optional<std::pair<int, int>> target_landscape_aspect_ratio;
+ absl::optional<std::pair<int, int>> target_portrait_aspect_ratio;
+ if (target_aspect_ratio && target_aspect_ratio->first > 0 &&
+ target_aspect_ratio->second > 0) {
+ // Maintain input orientation.
+ const int max_side =
+ std::max(target_aspect_ratio->first, target_aspect_ratio->second);
+ const int min_side =
+ std::min(target_aspect_ratio->first, target_aspect_ratio->second);
+ target_landscape_aspect_ratio = std::make_pair(max_side, min_side);
+ target_portrait_aspect_ratio = std::make_pair(min_side, max_side);
+ }
+ OnOutputFormatRequest(target_landscape_aspect_ratio, max_pixel_count,
+ target_portrait_aspect_ratio, max_pixel_count, max_fps);
+}
+
+void VideoAdapter::OnOutputFormatRequest(
+ const absl::optional<std::pair<int, int>>& target_landscape_aspect_ratio,
+ const absl::optional<int>& max_landscape_pixel_count,
+ const absl::optional<std::pair<int, int>>& target_portrait_aspect_ratio,
+ const absl::optional<int>& max_portrait_pixel_count,
+ const absl::optional<int>& max_fps) {
+ webrtc::MutexLock lock(&mutex_);
+
+ OutputFormatRequest request = {
+ .target_landscape_aspect_ratio = target_landscape_aspect_ratio,
+ .max_landscape_pixel_count = max_landscape_pixel_count,
+ .target_portrait_aspect_ratio = target_portrait_aspect_ratio,
+ .max_portrait_pixel_count = max_portrait_pixel_count,
+ .max_fps = max_fps};
+
+ if (stashed_output_format_request_) {
+ // Save the output format request for later use in case the encoder making
+ // this call would become active, because currently all active encoders use
+ // requested_resolution instead.
+ stashed_output_format_request_ = request;
+ RTC_LOG(LS_INFO) << "Stashing OnOutputFormatRequest: "
+ << stashed_output_format_request_->ToString();
+ } else {
+ output_format_request_ = request;
+ RTC_LOG(LS_INFO) << "Setting output_format_request_: "
+ << output_format_request_.ToString();
+ }
+
+ framerate_controller_.Reset();
+}
+
+void VideoAdapter::OnSinkWants(const rtc::VideoSinkWants& sink_wants) {
+ webrtc::MutexLock lock(&mutex_);
+ resolution_request_max_pixel_count_ = sink_wants.max_pixel_count;
+ resolution_request_target_pixel_count_ =
+ sink_wants.target_pixel_count.value_or(
+ resolution_request_max_pixel_count_);
+ max_framerate_request_ = sink_wants.max_framerate_fps;
+ resolution_alignment_ = cricket::LeastCommonMultiple(
+ source_resolution_alignment_, sink_wants.resolution_alignment);
+
+ if (!sink_wants.aggregates) {
+ RTC_LOG(LS_WARNING)
+ << "These should always be created by VideoBroadcaster!";
+ return;
+ }
+
+ // If requested_resolution is used, and there are no active encoders
+ // that are NOT using requested_resolution (aka newapi), then override
+ // calls to OnOutputFormatRequest and use values from requested_resolution
+ // instead (combined with qualityscaling based on pixel counts above).
+ if (webrtc::field_trial::IsDisabled(
+ "WebRTC-Video-RequestedResolutionOverrideOutputFormatRequest")) {
+ // kill-switch...
+ return;
+ }
+
+ if (!sink_wants.requested_resolution) {
+ if (stashed_output_format_request_) {
+ // because current active_output_format_request is based on
+ // requested_resolution logic, while current encoder(s) doesn't want that,
+ // we have to restore the stashed request.
+ RTC_LOG(LS_INFO) << "Unstashing OnOutputFormatRequest: "
+ << stashed_output_format_request_->ToString();
+ output_format_request_ = *stashed_output_format_request_;
+ stashed_output_format_request_.reset();
+ }
+ return;
+ }
+
+ if (sink_wants.aggregates->any_active_without_requested_resolution) {
+ return;
+ }
+
+ if (!stashed_output_format_request_) {
+ // The active output format request is about to be rewritten by
+ // request_resolution. We need to save it for later use in case the encoder
+ // which doesn't use request_resolution logic become active in the future.
+ stashed_output_format_request_ = output_format_request_;
+ RTC_LOG(LS_INFO) << "Stashing OnOutputFormatRequest: "
+ << stashed_output_format_request_->ToString();
+ }
+
+ auto res = *sink_wants.requested_resolution;
+ auto pixel_count = res.width * res.height;
+ output_format_request_.target_landscape_aspect_ratio =
+ std::make_pair(res.width, res.height);
+ output_format_request_.max_landscape_pixel_count = pixel_count;
+ output_format_request_.target_portrait_aspect_ratio =
+ std::make_pair(res.height, res.width);
+ output_format_request_.max_portrait_pixel_count = pixel_count;
+ output_format_request_.max_fps = max_framerate_request_;
+ RTC_LOG(LS_INFO) << "Setting output_format_request_ based on sink_wants: "
+ << output_format_request_.ToString();
+}
+
+int VideoAdapter::GetTargetPixels() const {
+ webrtc::MutexLock lock(&mutex_);
+ return resolution_request_target_pixel_count_;
+}
+
+float VideoAdapter::GetMaxFramerate() const {
+ webrtc::MutexLock lock(&mutex_);
+ // Minimum of `output_format_request_.max_fps` and `max_framerate_request_` is
+ // used to throttle frame-rate.
+ int framerate =
+ std::min(max_framerate_request_,
+ output_format_request_.max_fps.value_or(max_framerate_request_));
+ if (framerate == std::numeric_limits<int>::max()) {
+ return std::numeric_limits<float>::infinity();
+ } else {
+ return max_framerate_request_;
+ }
+}
+
+std::string VideoAdapter::OutputFormatRequest::ToString() const {
+ rtc::StringBuilder oss;
+ oss << "[ ";
+ if (target_landscape_aspect_ratio == Swap(target_portrait_aspect_ratio) &&
+ max_landscape_pixel_count == max_portrait_pixel_count) {
+ if (target_landscape_aspect_ratio) {
+ oss << target_landscape_aspect_ratio->first << "x"
+ << target_landscape_aspect_ratio->second;
+ } else {
+ oss << "unset-resolution";
+ }
+ if (max_landscape_pixel_count) {
+ oss << " max_pixel_count: " << *max_landscape_pixel_count;
+ }
+ } else {
+ oss << "[ landscape: ";
+ if (target_landscape_aspect_ratio) {
+ oss << target_landscape_aspect_ratio->first << "x"
+ << target_landscape_aspect_ratio->second;
+ } else {
+ oss << "unset";
+ }
+ if (max_landscape_pixel_count) {
+ oss << " max_pixel_count: " << *max_landscape_pixel_count;
+ }
+ oss << " ] [ portrait: ";
+ if (target_portrait_aspect_ratio) {
+ oss << target_portrait_aspect_ratio->first << "x"
+ << target_portrait_aspect_ratio->second;
+ }
+ if (max_portrait_pixel_count) {
+ oss << " max_pixel_count: " << *max_portrait_pixel_count;
+ }
+ oss << " ]";
+ }
+ oss << " max_fps: ";
+ if (max_fps) {
+ oss << *max_fps;
+ } else {
+ oss << "unset";
+ }
+ oss << " ]";
+ return oss.Release();
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/video_adapter.h b/third_party/libwebrtc/media/base/video_adapter.h
new file mode 100644
index 0000000000..b3e69c492b
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_adapter.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_VIDEO_ADAPTER_H_
+#define MEDIA_BASE_VIDEO_ADAPTER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "absl/types/optional.h"
+#include "api/video/video_source_interface.h"
+#include "common_video/framerate_controller.h"
+#include "media/base/video_common.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace cricket {
+
+// VideoAdapter adapts an input video frame to an output frame based on the
+// specified input and output formats. The adaptation includes dropping frames
+// to reduce frame rate and scaling frames.
+// VideoAdapter is thread safe.
+class RTC_EXPORT VideoAdapter {
+ public:
+ VideoAdapter();
+ // The source requests output frames whose width and height are divisible
+ // by `source_resolution_alignment`.
+ explicit VideoAdapter(int source_resolution_alignment);
+ virtual ~VideoAdapter();
+
+ VideoAdapter(const VideoAdapter&) = delete;
+ VideoAdapter& operator=(const VideoAdapter&) = delete;
+
+ // Return the adapted resolution and cropping parameters given the
+ // input resolution. The input frame should first be cropped, then
+ // scaled to the final output resolution. Returns true if the frame
+ // should be adapted, and false if it should be dropped.
+ bool AdaptFrameResolution(int in_width,
+ int in_height,
+ int64_t in_timestamp_ns,
+ int* cropped_width,
+ int* cropped_height,
+ int* out_width,
+ int* out_height) RTC_LOCKS_EXCLUDED(mutex_);
+
+ // DEPRECATED. Please use OnOutputFormatRequest below.
+ // TODO(asapersson): Remove this once it is no longer used.
+ // Requests the output frame size and frame interval from
+ // `AdaptFrameResolution` to not be larger than `format`. Also, the input
+ // frame size will be cropped to match the requested aspect ratio. The
+ // requested aspect ratio is orientation agnostic and will be adjusted to
+ // maintain the input orientation, so it doesn't matter if e.g. 1280x720 or
+ // 720x1280 is requested.
+ // Note: Should be called from the source only.
+ void OnOutputFormatRequest(const absl::optional<VideoFormat>& format)
+ RTC_LOCKS_EXCLUDED(mutex_);
+
+ // Requests output frame size and frame interval from `AdaptFrameResolution`.
+ // `target_aspect_ratio`: The input frame size will be cropped to match the
+ // requested aspect ratio. The aspect ratio is orientation agnostic and will
+ // be adjusted to maintain the input orientation (i.e. it doesn't matter if
+ // e.g. <1280,720> or <720,1280> is requested).
+ // `max_pixel_count`: The maximum output frame size.
+ // `max_fps`: The maximum output framerate.
+ // Note: Should be called from the source only.
+ void OnOutputFormatRequest(
+ const absl::optional<std::pair<int, int>>& target_aspect_ratio,
+ const absl::optional<int>& max_pixel_count,
+ const absl::optional<int>& max_fps) RTC_LOCKS_EXCLUDED(mutex_);
+
+ // Same as above, but allows setting two different target aspect ratios
+ // depending on incoming frame orientation. This gives more fine-grained
+ // control and can e.g. be used to force landscape video to be cropped to
+ // portrait video.
+ void OnOutputFormatRequest(
+ const absl::optional<std::pair<int, int>>& target_landscape_aspect_ratio,
+ const absl::optional<int>& max_landscape_pixel_count,
+ const absl::optional<std::pair<int, int>>& target_portrait_aspect_ratio,
+ const absl::optional<int>& max_portrait_pixel_count,
+ const absl::optional<int>& max_fps) RTC_LOCKS_EXCLUDED(mutex_);
+
+ // Requests the output frame size from `AdaptFrameResolution` to have as close
+ // as possible to `sink_wants.target_pixel_count` pixels (if set)
+ // but no more than `sink_wants.max_pixel_count`.
+ // `sink_wants.max_framerate_fps` is essentially analogous to
+ // `sink_wants.max_pixel_count`, but for framerate rather than resolution.
+ // Set `sink_wants.max_pixel_count` and/or `sink_wants.max_framerate_fps` to
+ // std::numeric_limit<int>::max() if no upper limit is desired.
+ // The sink resolution alignment requirement is given by
+ // `sink_wants.resolution_alignment`.
+ // Note: Should be called from the sink only.
+ void OnSinkWants(const rtc::VideoSinkWants& sink_wants)
+ RTC_LOCKS_EXCLUDED(mutex_);
+
+ // Returns maximum image area, which shouldn't impose any adaptations.
+ // Can return `numeric_limits<int>::max()` if no limit is set.
+ int GetTargetPixels() const;
+
+ // Returns current frame-rate limit.
+ // Can return `numeric_limits<float>::infinity()` if no limit is set.
+ float GetMaxFramerate() const;
+
+ private:
+ // Determine if frame should be dropped based on input fps and requested fps.
+ bool DropFrame(int64_t in_timestamp_ns) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ int frames_in_ RTC_GUARDED_BY(mutex_); // Number of input frames.
+ int frames_out_ RTC_GUARDED_BY(mutex_); // Number of output frames.
+ int frames_scaled_ RTC_GUARDED_BY(mutex_); // Number of frames scaled.
+ int adaption_changes_
+ RTC_GUARDED_BY(mutex_); // Number of changes in scale factor.
+ int previous_width_ RTC_GUARDED_BY(mutex_); // Previous adapter output width.
+ int previous_height_
+ RTC_GUARDED_BY(mutex_); // Previous adapter output height.
+ const bool variable_start_scale_factor_;
+
+ // The fixed source resolution alignment requirement.
+ const int source_resolution_alignment_;
+ // The currently applied resolution alignment, as given by the requirements:
+ // - the fixed `source_resolution_alignment_`; and
+ // - the latest `sink_wants.resolution_alignment`.
+ int resolution_alignment_ RTC_GUARDED_BY(mutex_);
+
+ // Max number of pixels/fps requested via calls to OnOutputFormatRequest,
+ // OnResolutionFramerateRequest respectively.
+ // The adapted output format is the minimum of these.
+ struct OutputFormatRequest {
+ absl::optional<std::pair<int, int>> target_landscape_aspect_ratio;
+ absl::optional<int> max_landscape_pixel_count;
+ absl::optional<std::pair<int, int>> target_portrait_aspect_ratio;
+ absl::optional<int> max_portrait_pixel_count;
+ absl::optional<int> max_fps;
+
+ // For logging.
+ std::string ToString() const;
+ };
+
+ OutputFormatRequest output_format_request_ RTC_GUARDED_BY(mutex_);
+ int resolution_request_target_pixel_count_ RTC_GUARDED_BY(mutex_);
+ int resolution_request_max_pixel_count_ RTC_GUARDED_BY(mutex_);
+ int max_framerate_request_ RTC_GUARDED_BY(mutex_);
+
+ // Stashed OutputFormatRequest that is used to save value of
+ // OnOutputFormatRequest in case all active encoders are using
+ // requested_resolution. I.e when all active encoders are using
+ // requested_resolution, the call to OnOutputFormatRequest is ignored
+ // and the value from requested_resolution is used instead (to scale/crop
+ // frame). This allows for an application to only use
+ // RtpEncodingParameters::request_resolution and get the same behavior as if
+ // it had used VideoAdapter::OnOutputFormatRequest.
+ absl::optional<OutputFormatRequest> stashed_output_format_request_
+ RTC_GUARDED_BY(mutex_);
+
+ webrtc::FramerateController framerate_controller_ RTC_GUARDED_BY(mutex_);
+
+ // The critical section to protect the above variables.
+ mutable webrtc::Mutex mutex_;
+};
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_VIDEO_ADAPTER_H_
diff --git a/third_party/libwebrtc/media/base/video_adapter_unittest.cc b/third_party/libwebrtc/media/base/video_adapter_unittest.cc
new file mode 100644
index 0000000000..3a8d2e6098
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_adapter_unittest.cc
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/video_adapter.h"
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "api/video/resolution.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_source_interface.h"
+#include "media/base/fake_frame_source.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/time_utils.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace cricket {
+namespace {
+const int kWidth = 1280;
+const int kHeight = 720;
+const int kDefaultFps = 30;
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Pair;
+using webrtc::Resolution;
+
+rtc::VideoSinkWants BuildSinkWants(absl::optional<int> target_pixel_count,
+ int max_pixel_count,
+ int max_framerate_fps,
+ int sink_alignment = 1) {
+ rtc::VideoSinkWants wants;
+ wants.target_pixel_count = target_pixel_count;
+ wants.max_pixel_count = max_pixel_count;
+ wants.max_framerate_fps = max_framerate_fps;
+ wants.resolution_alignment = sink_alignment;
+ wants.is_active = true;
+ wants.aggregates.emplace(rtc::VideoSinkWants::Aggregates());
+ wants.aggregates->any_active_without_requested_resolution = false;
+ return wants;
+}
+
+rtc::VideoSinkWants BuildSinkWants(
+ absl::optional<webrtc::Resolution> requested_resolution,
+ bool any_active_without_requested_resolution) {
+ rtc::VideoSinkWants wants;
+ wants.max_framerate_fps = kDefaultFps;
+ wants.resolution_alignment = 1;
+ wants.is_active = true;
+ if (requested_resolution) {
+ wants.target_pixel_count = requested_resolution->PixelCount();
+ wants.max_pixel_count = requested_resolution->PixelCount();
+ wants.requested_resolution.emplace(rtc::VideoSinkWants::FrameSize(
+ requested_resolution->width, requested_resolution->height));
+ } else {
+ wants.target_pixel_count = kWidth * kHeight;
+ wants.max_pixel_count = kWidth * kHeight;
+ }
+ wants.aggregates.emplace(rtc::VideoSinkWants::Aggregates());
+ wants.aggregates->any_active_without_requested_resolution =
+ any_active_without_requested_resolution;
+ return wants;
+}
+
+} // namespace
+
+class VideoAdapterTest : public ::testing::Test,
+ public ::testing::WithParamInterface<bool> {
+ public:
+ VideoAdapterTest() : VideoAdapterTest("", 1) {}
+ explicit VideoAdapterTest(const std::string& field_trials,
+ int source_resolution_alignment)
+ : override_field_trials_(field_trials),
+ frame_source_(std::make_unique<FakeFrameSource>(
+ kWidth,
+ kHeight,
+ VideoFormat::FpsToInterval(kDefaultFps) /
+ rtc::kNumNanosecsPerMicrosec)),
+ adapter_(source_resolution_alignment),
+ adapter_wrapper_(std::make_unique<VideoAdapterWrapper>(&adapter_)),
+ use_new_format_request_(GetParam()) {}
+
+ protected:
+ // Wrap a VideoAdapter and collect stats.
+ class VideoAdapterWrapper {
+ public:
+ struct Stats {
+ int captured_frames = 0;
+ int dropped_frames = 0;
+ bool last_adapt_was_no_op = false;
+
+ int cropped_width = 0;
+ int cropped_height = 0;
+ int out_width = 0;
+ int out_height = 0;
+ };
+
+ explicit VideoAdapterWrapper(VideoAdapter* adapter)
+ : video_adapter_(adapter) {}
+
+ void AdaptFrame(const webrtc::VideoFrame& frame) {
+ const int in_width = frame.width();
+ const int in_height = frame.height();
+ int cropped_width;
+ int cropped_height;
+ int out_width;
+ int out_height;
+ if (video_adapter_->AdaptFrameResolution(
+ in_width, in_height,
+ frame.timestamp_us() * rtc::kNumNanosecsPerMicrosec,
+ &cropped_width, &cropped_height, &out_width, &out_height)) {
+ stats_.cropped_width = cropped_width;
+ stats_.cropped_height = cropped_height;
+ stats_.out_width = out_width;
+ stats_.out_height = out_height;
+ stats_.last_adapt_was_no_op =
+ (in_width == cropped_width && in_height == cropped_height &&
+ in_width == out_width && in_height == out_height);
+ } else {
+ ++stats_.dropped_frames;
+ }
+ ++stats_.captured_frames;
+ }
+
+ Stats GetStats() const { return stats_; }
+
+ private:
+ VideoAdapter* video_adapter_;
+ Stats stats_;
+ };
+
+ void VerifyAdaptedResolution(const VideoAdapterWrapper::Stats& stats,
+ int cropped_width,
+ int cropped_height,
+ int out_width,
+ int out_height) {
+ EXPECT_EQ(cropped_width, stats.cropped_width);
+ EXPECT_EQ(cropped_height, stats.cropped_height);
+ EXPECT_EQ(out_width, stats.out_width);
+ EXPECT_EQ(out_height, stats.out_height);
+ }
+
+ void OnOutputFormatRequest(int width,
+ int height,
+ const absl::optional<int>& fps) {
+ if (use_new_format_request_) {
+ absl::optional<std::pair<int, int>> target_aspect_ratio =
+ std::make_pair(width, height);
+ absl::optional<int> max_pixel_count = width * height;
+ absl::optional<int> max_fps = fps;
+ adapter_.OnOutputFormatRequest(target_aspect_ratio, max_pixel_count,
+ max_fps);
+ return;
+ }
+ adapter_.OnOutputFormatRequest(
+ VideoFormat(width, height, fps ? VideoFormat::FpsToInterval(*fps) : 0,
+ cricket::FOURCC_I420));
+ }
+
+ // Return pair of <out resolution, cropping>
+ std::pair<webrtc::Resolution, webrtc::Resolution> AdaptFrameResolution(
+ webrtc::Resolution res) {
+ webrtc::Resolution out;
+ webrtc::Resolution cropped;
+ timestamp_ns_ += 1000000000;
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ res.width, res.height, timestamp_ns_, &cropped.width, &cropped.height,
+ &out.width, &out.height));
+ return std::make_pair(out, cropped);
+ }
+
+ webrtc::test::ScopedFieldTrials override_field_trials_;
+ const std::unique_ptr<FakeFrameSource> frame_source_;
+ VideoAdapter adapter_;
+ int64_t timestamp_ns_ = 0;
+ int cropped_width_;
+ int cropped_height_;
+ int out_width_;
+ int out_height_;
+ const std::unique_ptr<VideoAdapterWrapper> adapter_wrapper_;
+ const bool use_new_format_request_;
+};
+
+INSTANTIATE_TEST_SUITE_P(OnOutputFormatRequests,
+ VideoAdapterTest,
+ ::testing::Values(true, false));
+
+// Do not adapt the frame rate or the resolution. Expect no frame drop, no
+// cropping, and no resolution change.
+TEST_P(VideoAdapterTest, AdaptNothing) {
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop and no resolution change.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 10);
+ EXPECT_EQ(0, stats.dropped_frames);
+ VerifyAdaptedResolution(stats, kWidth, kHeight, kWidth, kHeight);
+ EXPECT_TRUE(stats.last_adapt_was_no_op);
+}
+
+TEST_P(VideoAdapterTest, AdaptZeroInterval) {
+ OnOutputFormatRequest(kWidth, kHeight, absl::nullopt);
+ for (int i = 0; i < 40; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no crash and that frames aren't dropped.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 40);
+ EXPECT_EQ(0, stats.dropped_frames);
+ VerifyAdaptedResolution(stats, kWidth, kHeight, kWidth, kHeight);
+}
+
+// Adapt the frame rate to be half of the capture rate at the beginning. Expect
+// the number of dropped frames to be half of the number the captured frames.
+TEST_P(VideoAdapterTest, AdaptFramerateToHalf) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps / 2);
+
+ // Capture 10 frames and verify that every other frame is dropped. The first
+ // frame should not be dropped.
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+ EXPECT_GE(adapter_wrapper_->GetStats().captured_frames, 10);
+ EXPECT_EQ(5, adapter_wrapper_->GetStats().dropped_frames);
+}
+
+// Adapt the frame rate to be two thirds of the capture rate at the beginning.
+// Expect the number of dropped frames to be one thirds of the number the
+// captured frames.
+TEST_P(VideoAdapterTest, AdaptFramerateToTwoThirds) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps * 2 / 3);
+
+ // Capture 10 frames and verify that every third frame is dropped. The first
+ // frame should not be dropped.
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+ EXPECT_GE(adapter_wrapper_->GetStats().captured_frames, 10);
+ EXPECT_EQ(3, adapter_wrapper_->GetStats().dropped_frames);
+}
+
+// Request frame rate twice as high as captured frame rate. Expect no frame
+// drop.
+TEST_P(VideoAdapterTest, AdaptFramerateHighLimit) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps * 2);
+
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop.
+ EXPECT_EQ(0, adapter_wrapper_->GetStats().dropped_frames);
+}
+
+// Adapt the frame rate to be half of the capture rate. No resolution limit set.
+// Expect the number of dropped frames to be half of the number the captured
+// frames.
+TEST_P(VideoAdapterTest, AdaptFramerateToHalfWithNoPixelLimit) {
+ adapter_.OnOutputFormatRequest(absl::nullopt, absl::nullopt, kDefaultFps / 2);
+
+ // Capture 10 frames and verify that every other frame is dropped. The first
+ // frame should not be dropped.
+ int expected_dropped_frames = 0;
+ for (int i = 0; i < 10; ++i) {
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+ EXPECT_GE(adapter_wrapper_->GetStats().captured_frames, i + 1);
+ if (i % 2 == 1)
+ ++expected_dropped_frames;
+ EXPECT_EQ(expected_dropped_frames,
+ adapter_wrapper_->GetStats().dropped_frames);
+ VerifyAdaptedResolution(adapter_wrapper_->GetStats(), kWidth, kHeight,
+ kWidth, kHeight);
+ }
+}
+
+// Adapt the frame rate to be half of the capture rate after capturing no less
+// than 10 frames. Expect no frame dropped before adaptation and frame dropped
+// after adaptation.
+TEST_P(VideoAdapterTest, AdaptFramerateOntheFly) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop before adaptation.
+ EXPECT_EQ(0, adapter_wrapper_->GetStats().dropped_frames);
+
+ // Adapt the frame rate.
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps / 2);
+ for (int i = 0; i < 20; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify frame drop after adaptation.
+ EXPECT_GT(adapter_wrapper_->GetStats().dropped_frames, 0);
+}
+
+// Do not adapt the frame rate or the resolution. Expect no frame drop, no
+// cropping, and no resolution change.
+TEST_P(VideoAdapterTest, AdaptFramerateRequestMax) {
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop and no resolution change.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 10);
+ EXPECT_EQ(0, stats.dropped_frames);
+ VerifyAdaptedResolution(stats, kWidth, kHeight, kWidth, kHeight);
+ EXPECT_TRUE(stats.last_adapt_was_no_op);
+}
+
+TEST_P(VideoAdapterTest, AdaptFramerateRequestZero) {
+ adapter_.OnSinkWants(
+ BuildSinkWants(absl::nullopt, std::numeric_limits<int>::max(), 0));
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no crash and that frames aren't dropped.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 10);
+ EXPECT_EQ(10, stats.dropped_frames);
+}
+
+// Adapt the frame rate to be half of the capture rate at the beginning. Expect
+// the number of dropped frames to be half of the number the captured frames.
+TEST_P(VideoAdapterTest, AdaptFramerateRequestHalf) {
+ adapter_.OnSinkWants(BuildSinkWants(
+ absl::nullopt, std::numeric_limits<int>::max(), kDefaultFps / 2));
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no crash and that frames aren't dropped.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 10);
+ EXPECT_EQ(5, stats.dropped_frames);
+ VerifyAdaptedResolution(stats, kWidth, kHeight, kWidth, kHeight);
+}
+
+// Set a very high output pixel resolution. Expect no cropping or resolution
+// change.
+TEST_P(VideoAdapterTest, AdaptFrameResolutionHighLimit) {
+ OnOutputFormatRequest(kWidth * 10, kHeight * 10, kDefaultFps);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(kWidth, cropped_width_);
+ EXPECT_EQ(kHeight, cropped_height_);
+ EXPECT_EQ(kWidth, out_width_);
+ EXPECT_EQ(kHeight, out_height_);
+}
+
+// Adapt the frame resolution to be the same as capture resolution. Expect no
+// cropping or resolution change.
+TEST_P(VideoAdapterTest, AdaptFrameResolutionIdentical) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(kWidth, cropped_width_);
+ EXPECT_EQ(kHeight, cropped_height_);
+ EXPECT_EQ(kWidth, out_width_);
+ EXPECT_EQ(kHeight, out_height_);
+}
+
+// Adapt the frame resolution to be a quarter of the capture resolution. Expect
+// no cropping, but a resolution change.
+TEST_P(VideoAdapterTest, AdaptFrameResolutionQuarter) {
+ OnOutputFormatRequest(kWidth / 2, kHeight / 2, kDefaultFps);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(kWidth, cropped_width_);
+ EXPECT_EQ(kHeight, cropped_height_);
+ EXPECT_EQ(kWidth / 2, out_width_);
+ EXPECT_EQ(kHeight / 2, out_height_);
+}
+
+// Adapt the pixel resolution to 0. Expect frame drop.
+TEST_P(VideoAdapterTest, AdaptFrameResolutionDrop) {
+ OnOutputFormatRequest(kWidth * 0, kHeight * 0, kDefaultFps);
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+}
+
+// Adapt the frame resolution to be a quarter of the capture resolution at the
+// beginning. Expect no cropping but a resolution change.
+TEST_P(VideoAdapterTest, AdaptResolution) {
+ OnOutputFormatRequest(kWidth / 2, kHeight / 2, kDefaultFps);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop, no cropping, and resolution change.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_EQ(0, stats.dropped_frames);
+ VerifyAdaptedResolution(stats, kWidth, kHeight, kWidth / 2, kHeight / 2);
+}
+
+// Adapt the frame resolution to be a quarter of the capture resolution after
+// capturing no less than 10 frames. Expect no resolution change before
+// adaptation and resolution change after adaptation.
+TEST_P(VideoAdapterTest, AdaptResolutionOnTheFly) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no resolution change before adaptation.
+ VerifyAdaptedResolution(adapter_wrapper_->GetStats(), kWidth, kHeight, kWidth,
+ kHeight);
+
+ // Adapt the frame resolution.
+ OnOutputFormatRequest(kWidth / 2, kHeight / 2, kDefaultFps);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify resolution change after adaptation.
+ VerifyAdaptedResolution(adapter_wrapper_->GetStats(), kWidth, kHeight,
+ kWidth / 2, kHeight / 2);
+}
+
+// Drop all frames for resolution 0x0.
+TEST_P(VideoAdapterTest, DropAllFrames) {
+ OnOutputFormatRequest(kWidth * 0, kHeight * 0, kDefaultFps);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify all frames are dropped.
+ VideoAdapterWrapper::Stats stats = adapter_wrapper_->GetStats();
+ EXPECT_GE(stats.captured_frames, 10);
+ EXPECT_EQ(stats.captured_frames, stats.dropped_frames);
+}
+
+TEST_P(VideoAdapterTest, TestOnOutputFormatRequest) {
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(400, out_height_);
+
+ // Format request 640x400.
+ OnOutputFormatRequest(640, 400, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(400, out_height_);
+
+ // Request 1280x720, higher than input, but aspect 16:9. Expect cropping but
+ // no scaling.
+ OnOutputFormatRequest(1280, 720, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Request 0x0.
+ OnOutputFormatRequest(0, 0, absl::nullopt);
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+
+ // Request 320x200. Expect scaling, but no cropping.
+ OnOutputFormatRequest(320, 200, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(320, out_width_);
+ EXPECT_EQ(200, out_height_);
+
+ // Request resolution close to 2/3 scale. Expect adapt down. Scaling to 2/3
+ // is not optimized and not allowed, therefore 1/2 scaling will be used
+ // instead.
+ OnOutputFormatRequest(424, 265, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(320, out_width_);
+ EXPECT_EQ(200, out_height_);
+
+ // Request resolution of 3 / 8. Expect adapt down.
+ OnOutputFormatRequest(640 * 3 / 8, 400 * 3 / 8, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(640 * 3 / 8, out_width_);
+ EXPECT_EQ(400 * 3 / 8, out_height_);
+
+ // Switch back up. Expect adapt.
+ OnOutputFormatRequest(320, 200, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(320, out_width_);
+ EXPECT_EQ(200, out_height_);
+
+ // Format request 480x300.
+ OnOutputFormatRequest(480, 300, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(300, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestViewRequestPlusCameraSwitch) {
+ // Start at HD.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+
+ // Format request for VGA.
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Now, the camera reopens at VGA.
+ // Both the frame and the output format should be 640x360.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 360, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // And another view request comes in for 640x360, which should have no
+ // real impact.
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 360, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestVgaWidth) {
+ // Requested output format is 640x360.
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ // Expect cropping.
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // But if frames come in at 640x360, we shouldn't adapt them down.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 360, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnResolutionRequestInSmallSteps) {
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+
+ // Adapt down one step.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 1280 * 720 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(960, out_width_);
+ EXPECT_EQ(540, out_height_);
+
+ // Adapt down one step more.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 960 * 540 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Adapt down one step more.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 360 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ // Adapt up one step.
+ adapter_.OnSinkWants(
+ BuildSinkWants(640 * 360, 960 * 540, std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Adapt up one step more.
+ adapter_.OnSinkWants(
+ BuildSinkWants(960 * 540, 1280 * 720, std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(960, out_width_);
+ EXPECT_EQ(540, out_height_);
+
+ // Adapt up one step more.
+ adapter_.OnSinkWants(
+ BuildSinkWants(1280 * 720, 1920 * 1080, std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnResolutionRequestMaxZero) {
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+
+ adapter_.OnSinkWants(
+ BuildSinkWants(absl::nullopt, 0, std::numeric_limits<int>::max()));
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+}
+
+TEST_P(VideoAdapterTest, TestOnResolutionRequestInLargeSteps) {
+ // Large step down.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 360 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ // Large step up.
+ adapter_.OnSinkWants(
+ BuildSinkWants(1280 * 720, 1920 * 1080, std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnOutputFormatRequestCapsMaxResolution) {
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 360 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 960 * 720,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnResolutionRequestReset) {
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 360 - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnOutputFormatRequestResolutionReset) {
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+
+ adapter_.OnOutputFormatRequest(absl::nullopt, 640 * 360 - 1, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ adapter_.OnOutputFormatRequest(absl::nullopt, absl::nullopt, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(1280, 720, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(1280, cropped_width_);
+ EXPECT_EQ(720, cropped_height_);
+ EXPECT_EQ(1280, out_width_);
+ EXPECT_EQ(720, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestOnOutputFormatRequestFpsReset) {
+ OnOutputFormatRequest(kWidth, kHeight, kDefaultFps / 2);
+ for (int i = 0; i < 10; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify frame drop.
+ const int dropped_frames = adapter_wrapper_->GetStats().dropped_frames;
+ EXPECT_GT(dropped_frames, 0);
+
+ // Reset frame rate.
+ OnOutputFormatRequest(kWidth, kHeight, absl::nullopt);
+ for (int i = 0; i < 20; ++i)
+ adapter_wrapper_->AdaptFrame(frame_source_->GetFrame());
+
+ // Verify no frame drop after reset.
+ EXPECT_EQ(dropped_frames, adapter_wrapper_->GetStats().dropped_frames);
+}
+
+TEST_P(VideoAdapterTest, RequestAspectRatio) {
+ // Request aspect ratio 320/180 (16:9), smaller than input, but no resolution
+ // limit. Expect cropping but no scaling.
+ adapter_.OnOutputFormatRequest(std::make_pair(320, 180), absl::nullopt,
+ absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, RequestAspectRatioWithDifferentOrientation) {
+ // Request 720x1280, higher than input, but aspect 16:9. Orientation should
+ // not matter, expect cropping but no scaling.
+ OnOutputFormatRequest(720, 1280, absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, InvalidAspectRatioIgnored) {
+ // Request aspect ratio 320/0. Expect no cropping.
+ adapter_.OnOutputFormatRequest(std::make_pair(320, 0), absl::nullopt,
+ absl::nullopt);
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 400, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(400, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(400, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestCroppingWithResolutionRequest) {
+ // Ask for 640x360 (16:9 aspect).
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+ // Send 640x480 (4:3 aspect).
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ // Expect cropping to 16:9 format and no scaling.
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Adapt down one step.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 360 - 1,
+ std::numeric_limits<int>::max()));
+ // Expect cropping to 16:9 format and 3/4 scaling.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ // Adapt down one step more.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 480 * 270 - 1,
+ std::numeric_limits<int>::max()));
+ // Expect cropping to 16:9 format and 1/2 scaling.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(320, out_width_);
+ EXPECT_EQ(180, out_height_);
+
+ // Adapt up one step.
+ adapter_.OnSinkWants(
+ BuildSinkWants(480 * 270, 640 * 360, std::numeric_limits<int>::max()));
+ // Expect cropping to 16:9 format and 3/4 scaling.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(480, out_width_);
+ EXPECT_EQ(270, out_height_);
+
+ // Adapt up one step more.
+ adapter_.OnSinkWants(
+ BuildSinkWants(640 * 360, 960 * 540, std::numeric_limits<int>::max()));
+ // Expect cropping to 16:9 format and no scaling.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+
+ // Try to adapt up one step more.
+ adapter_.OnSinkWants(
+ BuildSinkWants(960 * 540, 1280 * 720, std::numeric_limits<int>::max()));
+ // Expect cropping to 16:9 format and no scaling.
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(360, cropped_height_);
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestCroppingOddResolution) {
+ // Ask for 640x360 (16:9 aspect), with 3/16 scaling.
+ OnOutputFormatRequest(640, 360, absl::nullopt);
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ 640 * 360 * 3 / 16 * 3 / 16,
+ std::numeric_limits<int>::max()));
+
+ // Send 640x480 (4:3 aspect).
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 480, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+
+ // Instead of getting the exact aspect ratio with cropped resolution 640x360,
+ // the resolution should be adjusted to get a perfect scale factor instead.
+ EXPECT_EQ(640, cropped_width_);
+ EXPECT_EQ(368, cropped_height_);
+ EXPECT_EQ(120, out_width_);
+ EXPECT_EQ(69, out_height_);
+}
+
+TEST_P(VideoAdapterTest, TestAdaptToVerySmallResolution) {
+ // Ask for 1920x1080 (16:9 aspect), with 1/16 scaling.
+ const int w = 1920;
+ const int h = 1080;
+ OnOutputFormatRequest(w, h, absl::nullopt);
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, w * h * 1 / 16 * 1 / 16,
+ std::numeric_limits<int>::max()));
+
+ // Send 1920x1080 (16:9 aspect).
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ w, h, 0, &cropped_width_, &cropped_height_, &out_width_, &out_height_));
+
+ // Instead of getting the exact aspect ratio with cropped resolution 1920x1080
+ // the resolution should be adjusted to get a perfect scale factor instead.
+ EXPECT_EQ(1920, cropped_width_);
+ EXPECT_EQ(1072, cropped_height_);
+ EXPECT_EQ(120, out_width_);
+ EXPECT_EQ(67, out_height_);
+
+ // Adapt back up one step to 3/32.
+ adapter_.OnSinkWants(BuildSinkWants(w * h * 3 / 32 * 3 / 32,
+ w * h * 1 / 8 * 1 / 8,
+ std::numeric_limits<int>::max()));
+
+ // Send 1920x1080 (16:9 aspect).
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ w, h, 0, &cropped_width_, &cropped_height_, &out_width_, &out_height_));
+
+ EXPECT_EQ(160, out_width_);
+ EXPECT_EQ(90, out_height_);
+}
+
+TEST_P(VideoAdapterTest, AdaptFrameResolutionDropWithResolutionRequest) {
+ OnOutputFormatRequest(0, 0, kDefaultFps);
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+
+ adapter_.OnSinkWants(BuildSinkWants(960 * 540,
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ // Still expect all frames to be dropped
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, 640 * 480 - 1,
+ std::numeric_limits<int>::max()));
+
+ // Still expect all frames to be dropped
+ EXPECT_FALSE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+}
+
+// Test that we will adapt to max given a target pixel count close to max.
+TEST_P(VideoAdapterTest, TestAdaptToMax) {
+ OnOutputFormatRequest(640, 360, kDefaultFps);
+ adapter_.OnSinkWants(BuildSinkWants(640 * 360 - 1 /* target */,
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(640, 360, 0, &cropped_width_,
+ &cropped_height_, &out_width_,
+ &out_height_));
+ EXPECT_EQ(640, out_width_);
+ EXPECT_EQ(360, out_height_);
+}
+
+// Test adjusting to 16:9 in landscape, and 9:16 in portrait.
+TEST(VideoAdapterTestMultipleOrientation, TestNormal) {
+ VideoAdapter video_adapter;
+ video_adapter.OnOutputFormatRequest(std::make_pair(640, 360), 640 * 360,
+ std::make_pair(360, 640), 360 * 640, 30);
+
+ int cropped_width;
+ int cropped_height;
+ int out_width;
+ int out_height;
+ EXPECT_TRUE(video_adapter.AdaptFrameResolution(
+ /* in_width= */ 640, /* in_height= */ 480, /* in_timestamp_ns= */ 0,
+ &cropped_width, &cropped_height, &out_width, &out_height));
+ EXPECT_EQ(640, cropped_width);
+ EXPECT_EQ(360, cropped_height);
+ EXPECT_EQ(640, out_width);
+ EXPECT_EQ(360, out_height);
+
+ EXPECT_TRUE(video_adapter.AdaptFrameResolution(
+ /* in_width= */ 480, /* in_height= */ 640,
+ /* in_timestamp_ns= */ rtc::kNumNanosecsPerSec / 30, &cropped_width,
+ &cropped_height, &out_width, &out_height));
+ EXPECT_EQ(360, cropped_width);
+ EXPECT_EQ(640, cropped_height);
+ EXPECT_EQ(360, out_width);
+ EXPECT_EQ(640, out_height);
+}
+
+// Force output to be 9:16, even for landscape input.
+TEST(VideoAdapterTestMultipleOrientation, TestForcePortrait) {
+ VideoAdapter video_adapter;
+ video_adapter.OnOutputFormatRequest(std::make_pair(360, 640), 640 * 360,
+ std::make_pair(360, 640), 360 * 640, 30);
+
+ int cropped_width;
+ int cropped_height;
+ int out_width;
+ int out_height;
+ EXPECT_TRUE(video_adapter.AdaptFrameResolution(
+ /* in_width= */ 640, /* in_height= */ 480, /* in_timestamp_ns= */ 0,
+ &cropped_width, &cropped_height, &out_width, &out_height));
+ EXPECT_EQ(270, cropped_width);
+ EXPECT_EQ(480, cropped_height);
+ EXPECT_EQ(270, out_width);
+ EXPECT_EQ(480, out_height);
+
+ EXPECT_TRUE(video_adapter.AdaptFrameResolution(
+ /* in_width= */ 480, /* in_height= */ 640,
+ /* in_timestamp_ns= */ rtc::kNumNanosecsPerSec / 30, &cropped_width,
+ &cropped_height, &out_width, &out_height));
+ EXPECT_EQ(360, cropped_width);
+ EXPECT_EQ(640, cropped_height);
+ EXPECT_EQ(360, out_width);
+ EXPECT_EQ(640, out_height);
+}
+
+TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst3_4) {
+ const int kWidth = 1280;
+ const int kHeight = 720;
+ OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 16:9 aspect.
+
+ // Scale factors: 3/4, 2/3, 3/4, 2/3, ...
+ // Scale : 3/4, 1/2, 3/8, 1/4, 3/16, 1/8.
+ const int kExpectedWidths[] = {960, 640, 480, 320, 240, 160};
+ const int kExpectedHeights[] = {540, 360, 270, 180, 135, 90};
+
+ int request_width = kWidth;
+ int request_height = kHeight;
+
+ for (size_t i = 0; i < arraysize(kExpectedWidths); ++i) {
+ // Adapt down one step.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ request_width * request_height - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+ EXPECT_EQ(kExpectedWidths[i], out_width_);
+ EXPECT_EQ(kExpectedHeights[i], out_height_);
+ request_width = out_width_;
+ request_height = out_height_;
+ }
+}
+
+TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst2_3) {
+ const int kWidth = 1920;
+ const int kHeight = 1080;
+ OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 16:9 aspect.
+
+ // Scale factors: 2/3, 3/4, 2/3, 3/4, ...
+ // Scale: 2/3, 1/2, 1/3, 1/4, 1/6, 1/8, 1/12.
+ const int kExpectedWidths[] = {1280, 960, 640, 480, 320, 240, 160};
+ const int kExpectedHeights[] = {720, 540, 360, 270, 180, 135, 90};
+
+ int request_width = kWidth;
+ int request_height = kHeight;
+
+ for (size_t i = 0; i < arraysize(kExpectedWidths); ++i) {
+ // Adapt down one step.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ request_width * request_height - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+ EXPECT_EQ(kExpectedWidths[i], out_width_);
+ EXPECT_EQ(kExpectedHeights[i], out_height_);
+ request_width = out_width_;
+ request_height = out_height_;
+ }
+}
+
+TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst2x2_3) {
+ const int kWidth = 1440;
+ const int kHeight = 1080;
+ OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 4:3 aspect.
+
+ // Scale factors: 2/3, 2/3, 3/4, 2/3, 3/4, ...
+ // Scale : 2/3, 4/9, 1/3, 2/9, 1/6, 1/9, 1/12, 1/18, 1/24, 1/36.
+ const int kExpectedWidths[] = {960, 640, 480, 320, 240, 160, 120, 80, 60, 40};
+ const int kExpectedHeights[] = {720, 480, 360, 240, 180, 120, 90, 60, 45, 30};
+
+ int request_width = kWidth;
+ int request_height = kHeight;
+
+ for (size_t i = 0; i < arraysize(kExpectedWidths); ++i) {
+ // Adapt down one step.
+ adapter_.OnSinkWants(BuildSinkWants(absl::nullopt,
+ request_width * request_height - 1,
+ std::numeric_limits<int>::max()));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0,
+ &cropped_width_, &cropped_height_,
+ &out_width_, &out_height_));
+ EXPECT_EQ(kExpectedWidths[i], out_width_);
+ EXPECT_EQ(kExpectedHeights[i], out_height_);
+ request_width = out_width_;
+ request_height = out_height_;
+ }
+}
+
+TEST_P(VideoAdapterTest, AdaptResolutionWithSinkAlignment) {
+ constexpr int kSourceWidth = 1280;
+ constexpr int kSourceHeight = 720;
+ constexpr int kSourceFramerate = 30;
+ constexpr int kRequestedWidth = 480;
+ constexpr int kRequestedHeight = 270;
+ constexpr int kRequestedFramerate = 30;
+
+ OnOutputFormatRequest(kRequestedWidth, kRequestedHeight, kRequestedFramerate);
+
+ int frame_num = 1;
+ for (const int sink_alignment : {2, 3, 4, 5}) {
+ adapter_.OnSinkWants(
+ BuildSinkWants(absl::nullopt, std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max(), sink_alignment));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ kSourceWidth, kSourceHeight,
+ frame_num * rtc::kNumNanosecsPerSec / kSourceFramerate, &cropped_width_,
+ &cropped_height_, &out_width_, &out_height_));
+ EXPECT_EQ(out_width_ % sink_alignment, 0);
+ EXPECT_EQ(out_height_ % sink_alignment, 0);
+
+ ++frame_num;
+ }
+}
+
+// Verify the cases the OnOutputFormatRequest is ignored and
+// requested_resolution is used instead.
+TEST_P(VideoAdapterTest, UseRequestedResolutionInsteadOfOnOutputFormatRequest) {
+ {
+ // Both new and old API active => Use OnOutputFormatRequest
+ OnOutputFormatRequest(640, 360, kDefaultFps);
+ adapter_.OnSinkWants(
+ BuildSinkWants(Resolution{.width = 960, .height = 540},
+ /* any_active_without_requested_resolution= */ true));
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 640, .height = 360}));
+ }
+ {
+ // New API active, old API inactive, ignore OnOutputFormatRequest and use
+ // requested_resolution.
+ OnOutputFormatRequest(640, 360, kDefaultFps);
+ adapter_.OnSinkWants(
+ BuildSinkWants(Resolution{.width = 960, .height = 540},
+ /* any_active_without_requested_resolution= */ false));
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 960, .height = 540}));
+ }
+
+ {
+ // New API inactive, old API inactive, use OnOutputFormatRequest.
+ OnOutputFormatRequest(640, 360, kDefaultFps);
+ adapter_.OnSinkWants(
+ BuildSinkWants(absl::nullopt,
+ /* any_active_without_requested_resolution= */ false));
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 640, .height = 360}));
+ }
+
+ {
+ // New API active, old API inactive, remember OnOutputFormatRequest.
+ OnOutputFormatRequest(640, 360, kDefaultFps);
+ adapter_.OnSinkWants(
+ BuildSinkWants(Resolution{.width = 960, .height = 540},
+ /* any_active_without_requested_resolution= */ false));
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 960, .height = 540}));
+
+ // This is ignored since there is not any active NOT using
+ // requested_resolution.
+ OnOutputFormatRequest(320, 180, kDefaultFps);
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 960, .height = 540}));
+
+ // Disable new API => fallback to last OnOutputFormatRequest.
+ adapter_.OnSinkWants(
+ BuildSinkWants(absl::nullopt,
+ /* any_active_without_requested_resolution= */ false));
+
+ EXPECT_THAT(
+ AdaptFrameResolution(/* input frame */ {.width = 1280, .height = 720})
+ .first,
+ Eq(Resolution{.width = 320, .height = 180}));
+ }
+}
+
+class VideoAdapterWithSourceAlignmentTest : public VideoAdapterTest {
+ protected:
+ static constexpr int kSourceResolutionAlignment = 7;
+
+ VideoAdapterWithSourceAlignmentTest()
+ : VideoAdapterTest(/*field_trials=*/"", kSourceResolutionAlignment) {}
+};
+
+TEST_P(VideoAdapterWithSourceAlignmentTest, AdaptResolution) {
+ constexpr int kSourceWidth = 1280;
+ constexpr int kSourceHeight = 720;
+ constexpr int kRequestedWidth = 480;
+ constexpr int kRequestedHeight = 270;
+ constexpr int kRequestedFramerate = 30;
+
+ OnOutputFormatRequest(kRequestedWidth, kRequestedHeight, kRequestedFramerate);
+
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ kSourceWidth, kSourceHeight, /*in_timestamp_ns=*/0, &cropped_width_,
+ &cropped_height_, &out_width_, &out_height_));
+ EXPECT_EQ(out_width_ % kSourceResolutionAlignment, 0);
+ EXPECT_EQ(out_height_ % kSourceResolutionAlignment, 0);
+}
+
+TEST_P(VideoAdapterWithSourceAlignmentTest, AdaptResolutionWithSinkAlignment) {
+ constexpr int kSourceWidth = 1280;
+ constexpr int kSourceHeight = 720;
+ // 7 and 8 neither divide 480 nor 270.
+ constexpr int kRequestedWidth = 480;
+ constexpr int kRequestedHeight = 270;
+ constexpr int kRequestedFramerate = 30;
+ constexpr int kSinkResolutionAlignment = 8;
+
+ OnOutputFormatRequest(kRequestedWidth, kRequestedHeight, kRequestedFramerate);
+
+ adapter_.OnSinkWants(BuildSinkWants(
+ absl::nullopt, std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max(), kSinkResolutionAlignment));
+ EXPECT_TRUE(adapter_.AdaptFrameResolution(
+ kSourceWidth, kSourceHeight, /*in_timestamp_ns=*/0, &cropped_width_,
+ &cropped_height_, &out_width_, &out_height_));
+ EXPECT_EQ(out_width_ % kSourceResolutionAlignment, 0);
+ EXPECT_EQ(out_height_ % kSourceResolutionAlignment, 0);
+ EXPECT_EQ(out_width_ % kSinkResolutionAlignment, 0);
+ EXPECT_EQ(out_height_ % kSinkResolutionAlignment, 0);
+}
+
+INSTANTIATE_TEST_SUITE_P(OnOutputFormatRequests,
+ VideoAdapterWithSourceAlignmentTest,
+ ::testing::Values(true, false));
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/video_broadcaster.cc b/third_party/libwebrtc/media/base/video_broadcaster.cc
new file mode 100644
index 0000000000..43c17734e3
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_broadcaster.cc
@@ -0,0 +1,214 @@
+/*
+ * 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 "media/base/video_broadcaster.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_rotation.h"
+#include "media/base/video_common.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace rtc {
+
+VideoBroadcaster::VideoBroadcaster() = default;
+VideoBroadcaster::~VideoBroadcaster() = default;
+
+void VideoBroadcaster::AddOrUpdateSink(
+ VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) {
+ RTC_DCHECK(sink != nullptr);
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ if (!FindSinkPair(sink)) {
+ // `Sink` is a new sink, which didn't receive previous frame.
+ previous_frame_sent_to_all_sinks_ = false;
+
+ if (last_constraints_.has_value()) {
+ RTC_LOG(LS_INFO) << __func__ << " forwarding stored constraints min_fps "
+ << last_constraints_->min_fps.value_or(-1) << " max_fps "
+ << last_constraints_->max_fps.value_or(-1);
+ sink->OnConstraintsChanged(*last_constraints_);
+ }
+ }
+ VideoSourceBase::AddOrUpdateSink(sink, wants);
+ UpdateWants();
+}
+
+void VideoBroadcaster::RemoveSink(
+ VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK(sink != nullptr);
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ VideoSourceBase::RemoveSink(sink);
+ UpdateWants();
+}
+
+bool VideoBroadcaster::frame_wanted() const {
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ return !sink_pairs().empty();
+}
+
+VideoSinkWants VideoBroadcaster::wants() const {
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ return current_wants_;
+}
+
+void VideoBroadcaster::OnFrame(const webrtc::VideoFrame& frame) {
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ bool current_frame_was_discarded = false;
+ for (auto& sink_pair : sink_pairs()) {
+ if (sink_pair.wants.rotation_applied &&
+ frame.rotation() != webrtc::kVideoRotation_0) {
+ // Calls to OnFrame are not synchronized with changes to the sink wants.
+ // When rotation_applied is set to true, one or a few frames may get here
+ // with rotation still pending. Protect sinks that don't expect any
+ // pending rotation.
+ RTC_LOG(LS_VERBOSE) << "Discarding frame with unexpected rotation.";
+ sink_pair.sink->OnDiscardedFrame();
+ current_frame_was_discarded = true;
+ continue;
+ }
+ if (sink_pair.wants.black_frames) {
+ webrtc::VideoFrame black_frame =
+ webrtc::VideoFrame::Builder()
+ .set_video_frame_buffer(
+ GetBlackFrameBuffer(frame.width(), frame.height()))
+ .set_rotation(frame.rotation())
+ .set_timestamp_us(frame.timestamp_us())
+ .set_id(frame.id())
+ .build();
+ sink_pair.sink->OnFrame(black_frame);
+ } else if (!previous_frame_sent_to_all_sinks_ && frame.has_update_rect()) {
+ // Since last frame was not sent to some sinks, no reliable update
+ // information is available, so we need to clear the update rect.
+ webrtc::VideoFrame copy = frame;
+ copy.clear_update_rect();
+ sink_pair.sink->OnFrame(copy);
+ } else {
+ sink_pair.sink->OnFrame(frame);
+ }
+ }
+ previous_frame_sent_to_all_sinks_ = !current_frame_was_discarded;
+}
+
+void VideoBroadcaster::OnDiscardedFrame() {
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ for (auto& sink_pair : sink_pairs()) {
+ sink_pair.sink->OnDiscardedFrame();
+ }
+}
+
+void VideoBroadcaster::ProcessConstraints(
+ const webrtc::VideoTrackSourceConstraints& constraints) {
+ webrtc::MutexLock lock(&sinks_and_wants_lock_);
+ RTC_LOG(LS_INFO) << __func__ << " min_fps "
+ << constraints.min_fps.value_or(-1) << " max_fps "
+ << constraints.max_fps.value_or(-1) << " broadcasting to "
+ << sink_pairs().size() << " sinks.";
+ last_constraints_ = constraints;
+ for (auto& sink_pair : sink_pairs())
+ sink_pair.sink->OnConstraintsChanged(constraints);
+}
+
+void VideoBroadcaster::UpdateWants() {
+ VideoSinkWants wants;
+ wants.rotation_applied = false;
+ wants.resolution_alignment = 1;
+ wants.aggregates.emplace(VideoSinkWants::Aggregates());
+ wants.is_active = false;
+
+ // TODO(webrtc:14451) : I think it makes sense to always
+ // "ignore" encoders that are not active. But that would
+ // probably require a controlled roll out with a field trials?
+ // To play it safe, only ignore inactive encoders is there is an
+ // active encoder using the new api (requested_resolution),
+ // this means that there is only a behavioural change when using new
+ // api.
+ bool ignore_inactive_encoders_old_api = false;
+ for (auto& sink : sink_pairs()) {
+ if (sink.wants.is_active && sink.wants.requested_resolution.has_value()) {
+ ignore_inactive_encoders_old_api = true;
+ break;
+ }
+ }
+
+ for (auto& sink : sink_pairs()) {
+ if (!sink.wants.is_active &&
+ (sink.wants.requested_resolution || ignore_inactive_encoders_old_api)) {
+ continue;
+ }
+ // wants.rotation_applied == ANY(sink.wants.rotation_applied)
+ if (sink.wants.rotation_applied) {
+ wants.rotation_applied = true;
+ }
+ // wants.max_pixel_count == MIN(sink.wants.max_pixel_count)
+ if (sink.wants.max_pixel_count < wants.max_pixel_count) {
+ wants.max_pixel_count = sink.wants.max_pixel_count;
+ }
+ // Select the minimum requested target_pixel_count, if any, of all sinks so
+ // that we don't over utilize the resources for any one.
+ // TODO(sprang): Consider using the median instead, since the limit can be
+ // expressed by max_pixel_count.
+ if (sink.wants.target_pixel_count &&
+ (!wants.target_pixel_count ||
+ (*sink.wants.target_pixel_count < *wants.target_pixel_count))) {
+ wants.target_pixel_count = sink.wants.target_pixel_count;
+ }
+ // Select the minimum for the requested max framerates.
+ if (sink.wants.max_framerate_fps < wants.max_framerate_fps) {
+ wants.max_framerate_fps = sink.wants.max_framerate_fps;
+ }
+ wants.resolution_alignment = cricket::LeastCommonMultiple(
+ wants.resolution_alignment, sink.wants.resolution_alignment);
+
+ // Pick MAX(requested_resolution) since the actual can be downscaled
+ // in encoder instead.
+ if (sink.wants.requested_resolution) {
+ if (!wants.requested_resolution) {
+ wants.requested_resolution = sink.wants.requested_resolution;
+ } else {
+ wants.requested_resolution->width =
+ std::max(wants.requested_resolution->width,
+ sink.wants.requested_resolution->width);
+ wants.requested_resolution->height =
+ std::max(wants.requested_resolution->height,
+ sink.wants.requested_resolution->height);
+ }
+ } else if (sink.wants.is_active) {
+ wants.aggregates->any_active_without_requested_resolution = true;
+ }
+
+ wants.is_active |= sink.wants.is_active;
+ }
+
+ if (wants.target_pixel_count &&
+ *wants.target_pixel_count >= wants.max_pixel_count) {
+ wants.target_pixel_count.emplace(wants.max_pixel_count);
+ }
+ current_wants_ = wants;
+}
+
+const rtc::scoped_refptr<webrtc::VideoFrameBuffer>&
+VideoBroadcaster::GetBlackFrameBuffer(int width, int height) {
+ if (!black_frame_buffer_ || black_frame_buffer_->width() != width ||
+ black_frame_buffer_->height() != height) {
+ rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+ webrtc::I420Buffer::Create(width, height);
+ webrtc::I420Buffer::SetBlack(buffer.get());
+ black_frame_buffer_ = buffer;
+ }
+
+ return black_frame_buffer_;
+}
+
+} // namespace rtc
diff --git a/third_party/libwebrtc/media/base/video_broadcaster.h b/third_party/libwebrtc/media/base/video_broadcaster.h
new file mode 100644
index 0000000000..c253d44b09
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_broadcaster.h
@@ -0,0 +1,82 @@
+/*
+ * 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 MEDIA_BASE_VIDEO_BROADCASTER_H_
+#define MEDIA_BASE_VIDEO_BROADCASTER_H_
+
+#include "api/media_stream_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
+#include "api/video/video_frame_buffer.h"
+#include "api/video/video_source_interface.h"
+#include "media/base/video_source_base.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace rtc {
+
+// VideoBroadcaster broadcast video frames to sinks and combines VideoSinkWants
+// from its sinks. It does that by implementing rtc::VideoSourceInterface and
+// rtc::VideoSinkInterface. The class is threadsafe; methods may be called on
+// any thread. This is needed because VideoStreamEncoder calls AddOrUpdateSink
+// both on the worker thread and on the encoder task queue.
+class VideoBroadcaster : public VideoSourceBase,
+ public VideoSinkInterface<webrtc::VideoFrame> {
+ public:
+ VideoBroadcaster();
+ ~VideoBroadcaster() override;
+
+ // Adds a new, or updates an already existing sink. If the sink is new and
+ // ProcessConstraints has been called previously, the new sink's
+ // OnConstraintsCalled method will be invoked with the most recent
+ // constraints.
+ void AddOrUpdateSink(VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) override;
+ void RemoveSink(VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+
+ // Returns true if the next frame will be delivered to at least one sink.
+ bool frame_wanted() const;
+
+ // Returns VideoSinkWants a source is requested to fulfill. They are
+ // aggregated by all VideoSinkWants from all sinks.
+ VideoSinkWants wants() const;
+
+ // This method ensures that if a sink sets rotation_applied == true,
+ // it will never receive a frame with pending rotation. Our caller
+ // may pass in frames without precise synchronization with changes
+ // to the VideoSinkWants.
+ void OnFrame(const webrtc::VideoFrame& frame) override;
+
+ void OnDiscardedFrame() override;
+
+ // Called on the network thread when constraints change. Forwards the
+ // constraints to sinks added with AddOrUpdateSink via OnConstraintsChanged.
+ void ProcessConstraints(
+ const webrtc::VideoTrackSourceConstraints& constraints);
+
+ protected:
+ void UpdateWants() RTC_EXCLUSIVE_LOCKS_REQUIRED(sinks_and_wants_lock_);
+ const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& GetBlackFrameBuffer(
+ int width,
+ int height) RTC_EXCLUSIVE_LOCKS_REQUIRED(sinks_and_wants_lock_);
+
+ mutable webrtc::Mutex sinks_and_wants_lock_;
+
+ VideoSinkWants current_wants_ RTC_GUARDED_BY(sinks_and_wants_lock_);
+ rtc::scoped_refptr<webrtc::VideoFrameBuffer> black_frame_buffer_;
+ bool previous_frame_sent_to_all_sinks_ RTC_GUARDED_BY(sinks_and_wants_lock_) =
+ true;
+ absl::optional<webrtc::VideoTrackSourceConstraints> last_constraints_
+ RTC_GUARDED_BY(sinks_and_wants_lock_);
+};
+
+} // namespace rtc
+
+#endif // MEDIA_BASE_VIDEO_BROADCASTER_H_
diff --git a/third_party/libwebrtc/media/base/video_broadcaster_unittest.cc b/third_party/libwebrtc/media/base/video_broadcaster_unittest.cc
new file mode 100644
index 0000000000..c5dc24353c
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_broadcaster_unittest.cc
@@ -0,0 +1,436 @@
+/*
+ * Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/video_broadcaster.h"
+
+#include <limits>
+
+#include "absl/types/optional.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_rotation.h"
+#include "api/video/video_source_interface.h"
+#include "media/base/fake_video_renderer.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using cricket::FakeVideoRenderer;
+using rtc::VideoBroadcaster;
+using rtc::VideoSinkWants;
+using FrameSize = rtc::VideoSinkWants::FrameSize;
+
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Mock;
+using ::testing::Optional;
+
+class MockSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
+ public:
+ void OnFrame(const webrtc::VideoFrame&) override {}
+
+ MOCK_METHOD(void,
+ OnConstraintsChanged,
+ (const webrtc::VideoTrackSourceConstraints& constraints),
+ (override));
+};
+
+TEST(VideoBroadcasterTest, frame_wanted) {
+ VideoBroadcaster broadcaster;
+ EXPECT_FALSE(broadcaster.frame_wanted());
+
+ FakeVideoRenderer sink;
+ broadcaster.AddOrUpdateSink(&sink, rtc::VideoSinkWants());
+ EXPECT_TRUE(broadcaster.frame_wanted());
+
+ broadcaster.RemoveSink(&sink);
+ EXPECT_FALSE(broadcaster.frame_wanted());
+}
+
+TEST(VideoBroadcasterTest, OnFrame) {
+ VideoBroadcaster broadcaster;
+
+ FakeVideoRenderer sink1;
+ FakeVideoRenderer sink2;
+ broadcaster.AddOrUpdateSink(&sink1, rtc::VideoSinkWants());
+ broadcaster.AddOrUpdateSink(&sink2, rtc::VideoSinkWants());
+ static int kWidth = 100;
+ static int kHeight = 50;
+
+ rtc::scoped_refptr<webrtc::I420Buffer> buffer(
+ webrtc::I420Buffer::Create(kWidth, kHeight));
+ // Initialize, to avoid warnings on use of initialized values.
+ webrtc::I420Buffer::SetBlack(buffer.get());
+
+ webrtc::VideoFrame frame = webrtc::VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_rotation(webrtc::kVideoRotation_0)
+ .set_timestamp_us(0)
+ .build();
+
+ broadcaster.OnFrame(frame);
+ EXPECT_EQ(1, sink1.num_rendered_frames());
+ EXPECT_EQ(1, sink2.num_rendered_frames());
+
+ broadcaster.RemoveSink(&sink1);
+ broadcaster.OnFrame(frame);
+ EXPECT_EQ(1, sink1.num_rendered_frames());
+ EXPECT_EQ(2, sink2.num_rendered_frames());
+
+ broadcaster.AddOrUpdateSink(&sink1, rtc::VideoSinkWants());
+ broadcaster.OnFrame(frame);
+ EXPECT_EQ(2, sink1.num_rendered_frames());
+ EXPECT_EQ(3, sink2.num_rendered_frames());
+}
+
+TEST(VideoBroadcasterTest, AppliesRotationIfAnySinkWantsRotationApplied) {
+ VideoBroadcaster broadcaster;
+ EXPECT_FALSE(broadcaster.wants().rotation_applied);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.rotation_applied = false;
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_FALSE(broadcaster.wants().rotation_applied);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.rotation_applied = true;
+
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_TRUE(broadcaster.wants().rotation_applied);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_FALSE(broadcaster.wants().rotation_applied);
+}
+
+TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxPixelCount) {
+ VideoBroadcaster broadcaster;
+ EXPECT_EQ(std::numeric_limits<int>::max(),
+ broadcaster.wants().max_pixel_count);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.max_pixel_count = 1280 * 720;
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.max_pixel_count = 640 * 360;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(640 * 360, broadcaster.wants().max_pixel_count);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count);
+}
+
+TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxAndTargetPixelCount) {
+ VideoBroadcaster broadcaster;
+ EXPECT_TRUE(!broadcaster.wants().target_pixel_count);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.target_pixel_count = 1280 * 720;
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.target_pixel_count = 640 * 360;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(640 * 360, *broadcaster.wants().target_pixel_count);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count);
+}
+
+TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxFramerate) {
+ VideoBroadcaster broadcaster;
+ EXPECT_EQ(std::numeric_limits<int>::max(),
+ broadcaster.wants().max_framerate_fps);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.max_framerate_fps = 30;
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(30, broadcaster.wants().max_framerate_fps);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.max_framerate_fps = 15;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(15, broadcaster.wants().max_framerate_fps);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(30, broadcaster.wants().max_framerate_fps);
+}
+
+TEST(VideoBroadcasterTest,
+ AppliesLeastCommonMultipleOfSinkWantsResolutionAlignment) {
+ VideoBroadcaster broadcaster;
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 1);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.resolution_alignment = 2;
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.resolution_alignment = 3;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 6);
+
+ FakeVideoRenderer sink3;
+ VideoSinkWants wants3;
+ wants3.resolution_alignment = 4;
+ broadcaster.AddOrUpdateSink(&sink3, wants3);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 12);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 4);
+}
+
+TEST(VideoBroadcasterTest, SinkWantsBlackFrames) {
+ VideoBroadcaster broadcaster;
+ EXPECT_TRUE(!broadcaster.wants().black_frames);
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.black_frames = true;
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.black_frames = false;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+
+ rtc::scoped_refptr<webrtc::I420Buffer> buffer(
+ webrtc::I420Buffer::Create(100, 200));
+ // Makes it not all black.
+ buffer->InitializeData();
+
+ webrtc::VideoFrame frame1 = webrtc::VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_rotation(webrtc::kVideoRotation_0)
+ .set_timestamp_us(10)
+ .build();
+ broadcaster.OnFrame(frame1);
+ EXPECT_TRUE(sink1.black_frame());
+ EXPECT_EQ(10, sink1.timestamp_us());
+ EXPECT_FALSE(sink2.black_frame());
+ EXPECT_EQ(10, sink2.timestamp_us());
+
+ // Switch the sink wants.
+ wants1.black_frames = false;
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ wants2.black_frames = true;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+
+ webrtc::VideoFrame frame2 = webrtc::VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_rotation(webrtc::kVideoRotation_0)
+ .set_timestamp_us(30)
+ .build();
+ broadcaster.OnFrame(frame2);
+ EXPECT_FALSE(sink1.black_frame());
+ EXPECT_EQ(30, sink1.timestamp_us());
+ EXPECT_TRUE(sink2.black_frame());
+ EXPECT_EQ(30, sink2.timestamp_us());
+}
+
+TEST(VideoBroadcasterTest, ConstraintsChangedNotCalledOnSinkAddition) {
+ MockSink sink;
+ VideoBroadcaster broadcaster;
+ EXPECT_CALL(sink, OnConstraintsChanged).Times(0);
+ broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
+}
+
+TEST(VideoBroadcasterTest, ForwardsLastConstraintsOnAdd) {
+ MockSink sink;
+ VideoBroadcaster broadcaster;
+ broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3});
+ broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4});
+ EXPECT_CALL(
+ sink,
+ OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4)))));
+ broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
+}
+
+TEST(VideoBroadcasterTest, UpdatesOnlyNewSinksWithConstraints) {
+ MockSink sink1;
+ VideoBroadcaster broadcaster;
+ broadcaster.AddOrUpdateSink(&sink1, VideoSinkWants());
+ broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4});
+ Mock::VerifyAndClearExpectations(&sink1);
+ EXPECT_CALL(sink1, OnConstraintsChanged).Times(0);
+ MockSink sink2;
+ EXPECT_CALL(
+ sink2,
+ OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4)))));
+ broadcaster.AddOrUpdateSink(&sink2, VideoSinkWants());
+}
+
+TEST(VideoBroadcasterTest, ForwardsConstraintsToSink) {
+ MockSink sink;
+ VideoBroadcaster broadcaster;
+ EXPECT_CALL(sink, OnConstraintsChanged).Times(0);
+ broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
+ Mock::VerifyAndClearExpectations(&sink);
+
+ EXPECT_CALL(sink, OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps,
+ Eq(absl::nullopt)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps,
+ Eq(absl::nullopt)))));
+ broadcaster.ProcessConstraints(
+ webrtc::VideoTrackSourceConstraints{absl::nullopt, absl::nullopt});
+ Mock::VerifyAndClearExpectations(&sink);
+
+ EXPECT_CALL(
+ sink,
+ OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps,
+ Eq(absl::nullopt)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3)))));
+ broadcaster.ProcessConstraints(
+ webrtc::VideoTrackSourceConstraints{absl::nullopt, 3});
+ Mock::VerifyAndClearExpectations(&sink);
+
+ EXPECT_CALL(
+ sink,
+ OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps,
+ Eq(absl::nullopt)))));
+ broadcaster.ProcessConstraints(
+ webrtc::VideoTrackSourceConstraints{2, absl::nullopt});
+ Mock::VerifyAndClearExpectations(&sink);
+
+ EXPECT_CALL(
+ sink,
+ OnConstraintsChanged(AllOf(
+ Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)),
+ Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3)))));
+ broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3});
+}
+
+TEST(VideoBroadcasterTest, AppliesMaxOfSinkWantsRequestedResolution) {
+ VideoBroadcaster broadcaster;
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.requested_resolution = FrameSize(640, 360);
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.requested_resolution = FrameSize(650, 350);
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(FrameSize(650, 360), *broadcaster.wants().requested_resolution);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution);
+}
+
+TEST(VideoBroadcasterTest, AnyActive) {
+ VideoBroadcaster broadcaster;
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.is_active = false;
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(false, broadcaster.wants().is_active);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.is_active = true;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(true, broadcaster.wants().is_active);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(false, broadcaster.wants().is_active);
+}
+
+TEST(VideoBroadcasterTest, AnyActiveWithoutRequestedResolution) {
+ VideoBroadcaster broadcaster;
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.is_active = true;
+ wants1.requested_resolution = FrameSize(640, 360);
+
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(
+ false,
+ broadcaster.wants().aggregates->any_active_without_requested_resolution);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.is_active = true;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(
+ true,
+ broadcaster.wants().aggregates->any_active_without_requested_resolution);
+
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(
+ false,
+ broadcaster.wants().aggregates->any_active_without_requested_resolution);
+}
+
+// This verifies that the VideoSinkWants from a Sink that is_active = false
+// is ignored IF there is an active sink using new api (Requested_Resolution).
+// The uses resolution_alignment for verification.
+TEST(VideoBroadcasterTest, IgnoreInactiveSinkIfNewApiUsed) {
+ VideoBroadcaster broadcaster;
+
+ FakeVideoRenderer sink1;
+ VideoSinkWants wants1;
+ wants1.is_active = true;
+ wants1.requested_resolution = FrameSize(640, 360);
+ wants1.resolution_alignment = 2;
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
+
+ FakeVideoRenderer sink2;
+ VideoSinkWants wants2;
+ wants2.is_active = true;
+ wants2.resolution_alignment = 8;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 8);
+
+ // Now wants2 will be ignored.
+ wants2.is_active = false;
+ broadcaster.AddOrUpdateSink(&sink2, wants2);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
+
+ // But when wants1 is inactive, wants2 matters again.
+ wants1.is_active = false;
+ broadcaster.AddOrUpdateSink(&sink1, wants1);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 8);
+
+ // inactive wants1 (new api) is always ignored.
+ broadcaster.RemoveSink(&sink2);
+ EXPECT_EQ(broadcaster.wants().resolution_alignment, 1);
+}
diff --git a/third_party/libwebrtc/media/base/video_common.cc b/third_party/libwebrtc/media/base/video_common.cc
new file mode 100644
index 0000000000..0ac3b3790e
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_common.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/video_common.h"
+
+#include "api/array_view.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace cricket {
+
+struct FourCCAliasEntry {
+ uint32_t alias;
+ uint32_t canonical;
+};
+
+static const FourCCAliasEntry kFourCCAliases[] = {
+ {FOURCC_IYUV, FOURCC_I420},
+ {FOURCC_YU16, FOURCC_I422},
+ {FOURCC_YU24, FOURCC_I444},
+ {FOURCC_YUYV, FOURCC_YUY2},
+ {FOURCC_YUVS, FOURCC_YUY2},
+ {FOURCC_HDYC, FOURCC_UYVY},
+ {FOURCC_2VUY, FOURCC_UYVY},
+ {FOURCC_JPEG, FOURCC_MJPG}, // Note: JPEG has DHT while MJPG does not.
+ {FOURCC_DMB1, FOURCC_MJPG},
+ {FOURCC_BA81, FOURCC_BGGR},
+ {FOURCC_RGB3, FOURCC_RAW},
+ {FOURCC_BGR3, FOURCC_24BG},
+ {FOURCC_CM32, FOURCC_BGRA},
+ {FOURCC_CM24, FOURCC_RAW},
+};
+
+uint32_t CanonicalFourCC(uint32_t fourcc) {
+ for (uint32_t i = 0; i < arraysize(kFourCCAliases); ++i) {
+ if (kFourCCAliases[i].alias == fourcc) {
+ return kFourCCAliases[i].canonical;
+ }
+ }
+ // Not an alias, so return it as-is.
+ return fourcc;
+}
+
+// The C++ standard requires a namespace-scope definition of static const
+// integral types even when they are initialized in the declaration (see
+// [class.static.data]/4), but MSVC with /Ze is non-conforming and treats that
+// as a multiply defined symbol error. See Also:
+// http://msdn.microsoft.com/en-us/library/34h23df8.aspx
+#ifndef _MSC_EXTENSIONS
+const int64_t VideoFormat::kMinimumInterval; // Initialized in header.
+#endif
+
+std::string VideoFormat::ToString() const {
+ std::string fourcc_name = GetFourccName(fourcc) + " ";
+ for (std::string::const_iterator i = fourcc_name.begin();
+ i < fourcc_name.end(); ++i) {
+ // Test character is printable; Avoid isprint() which asserts on negatives.
+ if (*i < 32 || *i >= 127) {
+ fourcc_name = "";
+ break;
+ }
+ }
+
+ char buf[256];
+ rtc::SimpleStringBuilder sb(buf);
+ sb << fourcc_name << width << "x" << height << "x"
+ << IntervalToFpsFloat(interval);
+ return sb.str();
+}
+
+int GreatestCommonDivisor(int a, int b) {
+ RTC_DCHECK_GE(a, 0);
+ RTC_DCHECK_GT(b, 0);
+ int c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+ return b;
+}
+
+int LeastCommonMultiple(int a, int b) {
+ RTC_DCHECK_GT(a, 0);
+ RTC_DCHECK_GT(b, 0);
+ return a * (b / GreatestCommonDivisor(a, b));
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/video_common.h b/third_party/libwebrtc/media/base/video_common.h
new file mode 100644
index 0000000000..f27e008d26
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_common.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Common definition for video, including fourcc and VideoFormat.
+
+#ifndef MEDIA_BASE_VIDEO_COMMON_H_
+#define MEDIA_BASE_VIDEO_COMMON_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/time_utils.h"
+
+namespace cricket {
+
+//////////////////////////////////////////////////////////////////////////////
+// Definition of FourCC codes
+//////////////////////////////////////////////////////////////////////////////
+// Convert four characters to a FourCC code.
+// Needs to be a macro otherwise the OS X compiler complains when the kFormat*
+// constants are used in a switch.
+#define CRICKET_FOURCC(a, b, c, d) \
+ ((static_cast<uint32_t>(a)) | (static_cast<uint32_t>(b) << 8) | \
+ (static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24))
+// Some pages discussing FourCC codes:
+// http://www.fourcc.org/yuv.php
+// http://v4l2spec.bytesex.org/spec/book1.htm
+// http://developer.apple.com/quicktime/icefloe/dispatch020.html
+// http://msdn.microsoft.com/library/windows/desktop/dd206750.aspx#nv12
+// http://people.xiph.org/~xiphmont/containers/nut/nut4cc.txt
+
+// FourCC codes grouped according to implementation efficiency.
+// Primary formats should convert in 1 efficient step.
+// Secondary formats are converted in 2 steps.
+// Auxilliary formats call primary converters.
+enum FourCC {
+ // 9 Primary YUV formats: 5 planar, 2 biplanar, 2 packed.
+ FOURCC_I420 = CRICKET_FOURCC('I', '4', '2', '0'),
+ FOURCC_I422 = CRICKET_FOURCC('I', '4', '2', '2'),
+ FOURCC_I444 = CRICKET_FOURCC('I', '4', '4', '4'),
+ FOURCC_I411 = CRICKET_FOURCC('I', '4', '1', '1'),
+ FOURCC_I400 = CRICKET_FOURCC('I', '4', '0', '0'),
+ FOURCC_NV21 = CRICKET_FOURCC('N', 'V', '2', '1'),
+ FOURCC_NV12 = CRICKET_FOURCC('N', 'V', '1', '2'),
+ FOURCC_YUY2 = CRICKET_FOURCC('Y', 'U', 'Y', '2'),
+ FOURCC_UYVY = CRICKET_FOURCC('U', 'Y', 'V', 'Y'),
+
+ // 2 Secondary YUV formats: row biplanar.
+ FOURCC_M420 = CRICKET_FOURCC('M', '4', '2', '0'),
+
+ // 9 Primary RGB formats: 4 32 bpp, 2 24 bpp, 3 16 bpp.
+ FOURCC_ARGB = CRICKET_FOURCC('A', 'R', 'G', 'B'),
+ FOURCC_BGRA = CRICKET_FOURCC('B', 'G', 'R', 'A'),
+ FOURCC_ABGR = CRICKET_FOURCC('A', 'B', 'G', 'R'),
+ FOURCC_24BG = CRICKET_FOURCC('2', '4', 'B', 'G'),
+ FOURCC_RAW = CRICKET_FOURCC('r', 'a', 'w', ' '),
+ FOURCC_RGBA = CRICKET_FOURCC('R', 'G', 'B', 'A'),
+ FOURCC_RGBP = CRICKET_FOURCC('R', 'G', 'B', 'P'), // bgr565.
+ FOURCC_RGBO = CRICKET_FOURCC('R', 'G', 'B', 'O'), // abgr1555.
+ FOURCC_R444 = CRICKET_FOURCC('R', '4', '4', '4'), // argb4444.
+
+ // 4 Secondary RGB formats: 4 Bayer Patterns.
+ FOURCC_RGGB = CRICKET_FOURCC('R', 'G', 'G', 'B'),
+ FOURCC_BGGR = CRICKET_FOURCC('B', 'G', 'G', 'R'),
+ FOURCC_GRBG = CRICKET_FOURCC('G', 'R', 'B', 'G'),
+ FOURCC_GBRG = CRICKET_FOURCC('G', 'B', 'R', 'G'),
+
+ // 1 Primary Compressed YUV format.
+ FOURCC_MJPG = CRICKET_FOURCC('M', 'J', 'P', 'G'),
+
+ // 5 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias.
+ FOURCC_YV12 = CRICKET_FOURCC('Y', 'V', '1', '2'),
+ FOURCC_YV16 = CRICKET_FOURCC('Y', 'V', '1', '6'),
+ FOURCC_YV24 = CRICKET_FOURCC('Y', 'V', '2', '4'),
+ FOURCC_YU12 = CRICKET_FOURCC('Y', 'U', '1', '2'), // Linux version of I420.
+ FOURCC_J420 = CRICKET_FOURCC('J', '4', '2', '0'),
+ FOURCC_J400 = CRICKET_FOURCC('J', '4', '0', '0'),
+
+ // 14 Auxiliary aliases. CanonicalFourCC() maps these to canonical FOURCC.
+ FOURCC_IYUV = CRICKET_FOURCC('I', 'Y', 'U', 'V'), // Alias for I420.
+ FOURCC_YU16 = CRICKET_FOURCC('Y', 'U', '1', '6'), // Alias for I422.
+ FOURCC_YU24 = CRICKET_FOURCC('Y', 'U', '2', '4'), // Alias for I444.
+ FOURCC_YUYV = CRICKET_FOURCC('Y', 'U', 'Y', 'V'), // Alias for YUY2.
+ FOURCC_YUVS = CRICKET_FOURCC('y', 'u', 'v', 's'), // Alias for YUY2 on Mac.
+ FOURCC_HDYC = CRICKET_FOURCC('H', 'D', 'Y', 'C'), // Alias for UYVY.
+ FOURCC_2VUY = CRICKET_FOURCC('2', 'v', 'u', 'y'), // Alias for UYVY on Mac.
+ FOURCC_JPEG = CRICKET_FOURCC('J', 'P', 'E', 'G'), // Alias for MJPG.
+ FOURCC_DMB1 = CRICKET_FOURCC('d', 'm', 'b', '1'), // Alias for MJPG on Mac.
+ FOURCC_BA81 = CRICKET_FOURCC('B', 'A', '8', '1'), // Alias for BGGR.
+ FOURCC_RGB3 = CRICKET_FOURCC('R', 'G', 'B', '3'), // Alias for RAW.
+ FOURCC_BGR3 = CRICKET_FOURCC('B', 'G', 'R', '3'), // Alias for 24BG.
+ FOURCC_CM32 = CRICKET_FOURCC(0, 0, 0, 32), // BGRA kCMPixelFormat_32ARGB
+ FOURCC_CM24 = CRICKET_FOURCC(0, 0, 0, 24), // RAW kCMPixelFormat_24RGB
+
+ // 1 Auxiliary compressed YUV format set aside for capturer.
+ FOURCC_H264 = CRICKET_FOURCC('H', '2', '6', '4'),
+};
+
+#undef CRICKET_FOURCC
+
+// Match any fourcc.
+
+// We move this out of the enum because using it in many places caused
+// the compiler to get grumpy, presumably since the above enum is
+// backed by an int.
+static const uint32_t FOURCC_ANY = 0xFFFFFFFF;
+
+// Converts fourcc aliases into canonical ones.
+uint32_t CanonicalFourCC(uint32_t fourcc);
+
+// Get FourCC code as a string.
+inline std::string GetFourccName(uint32_t fourcc) {
+ std::string name;
+ name.push_back(static_cast<char>(fourcc & 0xFF));
+ name.push_back(static_cast<char>((fourcc >> 8) & 0xFF));
+ name.push_back(static_cast<char>((fourcc >> 16) & 0xFF));
+ name.push_back(static_cast<char>((fourcc >> 24) & 0xFF));
+ return name;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Definition of VideoFormat.
+//////////////////////////////////////////////////////////////////////////////
+
+// VideoFormat with Plain Old Data for global variables.
+struct VideoFormatPod {
+ int width; // Number of pixels.
+ int height; // Number of pixels.
+ int64_t interval; // Nanoseconds.
+ uint32_t fourcc; // Color space. FOURCC_ANY means that any color space is OK.
+};
+
+struct RTC_EXPORT VideoFormat : VideoFormatPod {
+ static const int64_t kMinimumInterval =
+ rtc::kNumNanosecsPerSec / 10000; // 10k fps.
+
+ VideoFormat() { Construct(0, 0, 0, 0); }
+
+ VideoFormat(int w, int h, int64_t interval_ns, uint32_t cc) {
+ Construct(w, h, interval_ns, cc);
+ }
+
+ explicit VideoFormat(const VideoFormatPod& format) {
+ Construct(format.width, format.height, format.interval, format.fourcc);
+ }
+
+ void Construct(int w, int h, int64_t interval_ns, uint32_t cc) {
+ width = w;
+ height = h;
+ interval = interval_ns;
+ fourcc = cc;
+ }
+
+ static int64_t FpsToInterval(int fps) {
+ return fps ? rtc::kNumNanosecsPerSec / fps : kMinimumInterval;
+ }
+
+ static int IntervalToFps(int64_t interval) {
+ if (!interval) {
+ return 0;
+ }
+ return static_cast<int>(rtc::kNumNanosecsPerSec / interval);
+ }
+
+ static float IntervalToFpsFloat(int64_t interval) {
+ if (!interval) {
+ return 0.f;
+ }
+ return static_cast<float>(rtc::kNumNanosecsPerSec) /
+ static_cast<float>(interval);
+ }
+
+ bool operator==(const VideoFormat& format) const {
+ return width == format.width && height == format.height &&
+ interval == format.interval && fourcc == format.fourcc;
+ }
+
+ bool operator!=(const VideoFormat& format) const {
+ return !(*this == format);
+ }
+
+ bool operator<(const VideoFormat& format) const {
+ return (fourcc < format.fourcc) ||
+ (fourcc == format.fourcc && width < format.width) ||
+ (fourcc == format.fourcc && width == format.width &&
+ height < format.height) ||
+ (fourcc == format.fourcc && width == format.width &&
+ height == format.height && interval > format.interval);
+ }
+
+ int framerate() const { return IntervalToFps(interval); }
+
+ // Check if both width and height are 0.
+ bool IsSize0x0() const { return 0 == width && 0 == height; }
+
+ // Check if this format is less than another one by comparing the resolution
+ // and frame rate.
+ bool IsPixelRateLess(const VideoFormat& format) const {
+ return width * height * framerate() <
+ format.width * format.height * format.framerate();
+ }
+
+ // Get a string presentation in the form of "fourcc width x height x fps"
+ std::string ToString() const;
+};
+
+// Returns the largest positive integer that divides both `a` and `b`.
+int GreatestCommonDivisor(int a, int b);
+
+// Returns the smallest positive integer that is divisible by both `a` and `b`.
+int LeastCommonMultiple(int a, int b);
+
+} // namespace cricket
+
+#endif // MEDIA_BASE_VIDEO_COMMON_H_
diff --git a/third_party/libwebrtc/media/base/video_common_unittest.cc b/third_party/libwebrtc/media/base/video_common_unittest.cc
new file mode 100644
index 0000000000..3f445c7769
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_common_unittest.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2008 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "media/base/video_common.h"
+
+#include "test/gtest.h"
+
+namespace cricket {
+
+TEST(VideoCommonTest, TestCanonicalFourCC) {
+ // Canonical fourccs are not changed.
+ EXPECT_EQ(FOURCC_I420, CanonicalFourCC(FOURCC_I420));
+ // The special FOURCC_ANY value is not changed.
+ EXPECT_EQ(FOURCC_ANY, CanonicalFourCC(FOURCC_ANY));
+ // Aliases are translated to the canonical equivalent.
+ EXPECT_EQ(FOURCC_I420, CanonicalFourCC(FOURCC_IYUV));
+ EXPECT_EQ(FOURCC_I422, CanonicalFourCC(FOURCC_YU16));
+ EXPECT_EQ(FOURCC_I444, CanonicalFourCC(FOURCC_YU24));
+ EXPECT_EQ(FOURCC_YUY2, CanonicalFourCC(FOURCC_YUYV));
+ EXPECT_EQ(FOURCC_YUY2, CanonicalFourCC(FOURCC_YUVS));
+ EXPECT_EQ(FOURCC_UYVY, CanonicalFourCC(FOURCC_HDYC));
+ EXPECT_EQ(FOURCC_UYVY, CanonicalFourCC(FOURCC_2VUY));
+ EXPECT_EQ(FOURCC_MJPG, CanonicalFourCC(FOURCC_JPEG));
+ EXPECT_EQ(FOURCC_MJPG, CanonicalFourCC(FOURCC_DMB1));
+ EXPECT_EQ(FOURCC_BGGR, CanonicalFourCC(FOURCC_BA81));
+ EXPECT_EQ(FOURCC_RAW, CanonicalFourCC(FOURCC_RGB3));
+ EXPECT_EQ(FOURCC_24BG, CanonicalFourCC(FOURCC_BGR3));
+ EXPECT_EQ(FOURCC_BGRA, CanonicalFourCC(FOURCC_CM32));
+ EXPECT_EQ(FOURCC_RAW, CanonicalFourCC(FOURCC_CM24));
+}
+
+// Test conversion between interval and fps
+TEST(VideoCommonTest, TestVideoFormatFps) {
+ EXPECT_EQ(VideoFormat::kMinimumInterval, VideoFormat::FpsToInterval(0));
+ EXPECT_EQ(rtc::kNumNanosecsPerSec / 20, VideoFormat::FpsToInterval(20));
+ EXPECT_EQ(20, VideoFormat::IntervalToFps(rtc::kNumNanosecsPerSec / 20));
+ EXPECT_EQ(0, VideoFormat::IntervalToFps(0));
+}
+
+// Test IsSize0x0
+TEST(VideoCommonTest, TestVideoFormatIsSize0x0) {
+ VideoFormat format;
+ EXPECT_TRUE(format.IsSize0x0());
+ format.width = 320;
+ EXPECT_FALSE(format.IsSize0x0());
+}
+
+// Test ToString: print fourcc when it is printable.
+TEST(VideoCommonTest, TestVideoFormatToString) {
+ VideoFormat format;
+ EXPECT_EQ("0x0x0", format.ToString());
+
+ format.fourcc = FOURCC_I420;
+ format.width = 640;
+ format.height = 480;
+ format.interval = VideoFormat::FpsToInterval(20);
+ EXPECT_EQ("I420 640x480x20", format.ToString());
+
+ format.fourcc = FOURCC_ANY;
+ format.width = 640;
+ format.height = 480;
+ format.interval = VideoFormat::FpsToInterval(20);
+ EXPECT_EQ("640x480x20", format.ToString());
+}
+
+// Test comparison.
+TEST(VideoCommonTest, TestVideoFormatCompare) {
+ VideoFormat format(640, 480, VideoFormat::FpsToInterval(20), FOURCC_I420);
+ VideoFormat format2;
+ EXPECT_NE(format, format2);
+
+ // Same pixelrate, different fourcc.
+ format2 = format;
+ format2.fourcc = FOURCC_YUY2;
+ EXPECT_NE(format, format2);
+ EXPECT_FALSE(format.IsPixelRateLess(format2) ||
+ format2.IsPixelRateLess(format2));
+
+ format2 = format;
+ format2.interval /= 2;
+ EXPECT_TRUE(format.IsPixelRateLess(format2));
+
+ format2 = format;
+ format2.width *= 2;
+ EXPECT_TRUE(format.IsPixelRateLess(format2));
+}
+
+TEST(VideoCommonTest, GreatestCommonDivisor) {
+ EXPECT_EQ(GreatestCommonDivisor(0, 1000), 1000);
+ EXPECT_EQ(GreatestCommonDivisor(1, 1), 1);
+ EXPECT_EQ(GreatestCommonDivisor(8, 12), 4);
+ EXPECT_EQ(GreatestCommonDivisor(24, 54), 6);
+}
+
+TEST(VideoCommonTest, LeastCommonMultiple) {
+ EXPECT_EQ(LeastCommonMultiple(1, 1), 1);
+ EXPECT_EQ(LeastCommonMultiple(2, 3), 6);
+ EXPECT_EQ(LeastCommonMultiple(16, 32), 32);
+}
+
+} // namespace cricket
diff --git a/third_party/libwebrtc/media/base/video_source_base.cc b/third_party/libwebrtc/media/base/video_source_base.cc
new file mode 100644
index 0000000000..2454902069
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_source_base.cc
@@ -0,0 +1,104 @@
+/*
+ * 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 "media/base/video_source_base.h"
+
+#include <algorithm>
+
+#include "absl/algorithm/container.h"
+#include "rtc_base/checks.h"
+
+namespace rtc {
+
+VideoSourceBase::VideoSourceBase() = default;
+VideoSourceBase::~VideoSourceBase() = default;
+
+void VideoSourceBase::AddOrUpdateSink(
+ VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) {
+ RTC_DCHECK(sink != nullptr);
+
+ SinkPair* sink_pair = FindSinkPair(sink);
+ if (!sink_pair) {
+ sinks_.push_back(SinkPair(sink, wants));
+ } else {
+ sink_pair->wants = wants;
+ }
+}
+
+void VideoSourceBase::RemoveSink(VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK(sink != nullptr);
+ RTC_DCHECK(FindSinkPair(sink));
+ sinks_.erase(std::remove_if(sinks_.begin(), sinks_.end(),
+ [sink](const SinkPair& sink_pair) {
+ return sink_pair.sink == sink;
+ }),
+ sinks_.end());
+}
+
+VideoSourceBase::SinkPair* VideoSourceBase::FindSinkPair(
+ const VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ auto sink_pair_it = absl::c_find_if(
+ sinks_,
+ [sink](const SinkPair& sink_pair) { return sink_pair.sink == sink; });
+ if (sink_pair_it != sinks_.end()) {
+ return &*sink_pair_it;
+ }
+ return nullptr;
+}
+
+VideoSourceBaseGuarded::VideoSourceBaseGuarded() = default;
+VideoSourceBaseGuarded::~VideoSourceBaseGuarded() = default;
+
+void VideoSourceBaseGuarded::AddOrUpdateSink(
+ VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) {
+ RTC_DCHECK_RUN_ON(&source_sequence_);
+ RTC_DCHECK(sink != nullptr);
+
+ SinkPair* sink_pair = FindSinkPair(sink);
+ if (!sink_pair) {
+ sinks_.push_back(SinkPair(sink, wants));
+ } else {
+ sink_pair->wants = wants;
+ }
+}
+
+void VideoSourceBaseGuarded::RemoveSink(
+ VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK_RUN_ON(&source_sequence_);
+ RTC_DCHECK(sink != nullptr);
+ RTC_DCHECK(FindSinkPair(sink));
+ sinks_.erase(std::remove_if(sinks_.begin(), sinks_.end(),
+ [sink](const SinkPair& sink_pair) {
+ return sink_pair.sink == sink;
+ }),
+ sinks_.end());
+}
+
+VideoSourceBaseGuarded::SinkPair* VideoSourceBaseGuarded::FindSinkPair(
+ const VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK_RUN_ON(&source_sequence_);
+ auto sink_pair_it = absl::c_find_if(
+ sinks_,
+ [sink](const SinkPair& sink_pair) { return sink_pair.sink == sink; });
+ if (sink_pair_it != sinks_.end()) {
+ return &*sink_pair_it;
+ }
+ return nullptr;
+}
+
+const std::vector<VideoSourceBaseGuarded::SinkPair>&
+VideoSourceBaseGuarded::sink_pairs() const {
+ RTC_DCHECK_RUN_ON(&source_sequence_);
+ return sinks_;
+}
+
+} // namespace rtc
diff --git a/third_party/libwebrtc/media/base/video_source_base.h b/third_party/libwebrtc/media/base/video_source_base.h
new file mode 100644
index 0000000000..2644723aa7
--- /dev/null
+++ b/third_party/libwebrtc/media/base/video_source_base.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_VIDEO_SOURCE_BASE_H_
+#define MEDIA_BASE_VIDEO_SOURCE_BASE_H_
+
+#include <vector>
+
+#include "api/sequence_checker.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_sink_interface.h"
+#include "api/video/video_source_interface.h"
+#include "rtc_base/system/no_unique_address.h"
+
+namespace rtc {
+
+// VideoSourceBase is not thread safe. Before using this class, consider using
+// VideoSourceBaseGuarded below instead, which is an identical implementation
+// but applies a sequence checker to help protect internal state.
+// TODO(bugs.webrtc.org/12780): Delete this class.
+class VideoSourceBase : public VideoSourceInterface<webrtc::VideoFrame> {
+ public:
+ VideoSourceBase();
+ ~VideoSourceBase() override;
+ void AddOrUpdateSink(VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) override;
+ void RemoveSink(VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+
+ protected:
+ struct SinkPair {
+ SinkPair(VideoSinkInterface<webrtc::VideoFrame>* sink, VideoSinkWants wants)
+ : sink(sink), wants(wants) {}
+ VideoSinkInterface<webrtc::VideoFrame>* sink;
+ VideoSinkWants wants;
+ };
+ SinkPair* FindSinkPair(const VideoSinkInterface<webrtc::VideoFrame>* sink);
+
+ const std::vector<SinkPair>& sink_pairs() const { return sinks_; }
+
+ private:
+ std::vector<SinkPair> sinks_;
+};
+
+// VideoSourceBaseGuarded assumes that operations related to sinks, occur on the
+// same TQ/thread that the object was constructed on.
+class VideoSourceBaseGuarded : public VideoSourceInterface<webrtc::VideoFrame> {
+ public:
+ VideoSourceBaseGuarded();
+ ~VideoSourceBaseGuarded() override;
+
+ void AddOrUpdateSink(VideoSinkInterface<webrtc::VideoFrame>* sink,
+ const VideoSinkWants& wants) override;
+ void RemoveSink(VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+
+ protected:
+ struct SinkPair {
+ SinkPair(VideoSinkInterface<webrtc::VideoFrame>* sink, VideoSinkWants wants)
+ : sink(sink), wants(wants) {}
+ VideoSinkInterface<webrtc::VideoFrame>* sink;
+ VideoSinkWants wants;
+ };
+
+ SinkPair* FindSinkPair(const VideoSinkInterface<webrtc::VideoFrame>* sink);
+ const std::vector<SinkPair>& sink_pairs() const;
+
+ // Keep the `source_sequence_` checker protected to allow sub classes the
+ // ability to call Detach() if/when appropriate.
+ RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker source_sequence_;
+
+ private:
+ std::vector<SinkPair> sinks_ RTC_GUARDED_BY(&source_sequence_);
+};
+
+} // namespace rtc
+
+#endif // MEDIA_BASE_VIDEO_SOURCE_BASE_H_