RtpEncodingParameters::request_resolution patch 1
This patch adds RtpEncodingParameters::request_resolution
with documentation and plumming. No behaviour is changed yet.
Bug: webrtc:14451
Change-Id: I1f4f83a312ee8c293e3d8f02b950751e62048304
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/276262
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38172}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 9445e3c..5bf7396 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -454,6 +454,7 @@
"../rtc_base:checks",
"../rtc_base:stringutils",
"../rtc_base/system:rtc_export",
+ "video:resolution",
"video_codecs:scalability_mode",
]
absl_deps = [
diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h
index e311577..0d3c9df 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -23,6 +23,7 @@
#include "api/media_types.h"
#include "api/priority.h"
#include "api/rtp_transceiver_direction.h"
+#include "api/video/resolution.h"
#include "api/video_codecs/scalability_mode.h"
#include "rtc_base/system/rtc_export.h"
@@ -502,6 +503,24 @@
// https://w3c.github.io/webrtc-svc/#rtcrtpencodingparameters
absl::optional<std::string> scalability_mode;
+ // Requested encode resolution.
+ //
+ // This field provides an alternative to `scale_resolution_down_by`
+ // that is not dependent on the video source.
+ //
+ // When setting requested_resolution it is not necessary to adapt the
+ // video source using OnOutputFormatRequest, since the VideoStreamEncoder
+ // will apply downscaling if necessary. requested_resolution will also be
+ // propagated to the video source, this allows downscaling earlier in the
+ // pipeline which can be beneficial if the source is consumed by multiple
+ // encoders, but is not strictly necessary.
+ //
+ // The `requested_resolution` is subject to resource adaptation.
+ //
+ // It is an error to set both `requested_resolution` and
+ // `scale_resolution_down_by`.
+ absl::optional<Resolution> requested_resolution;
+
// For an RtpSender, set to true to cause this encoding to be encoded and
// sent, and false for it not to be encoded and sent. This allows control
// across multiple encodings of a sender for turning simulcast layers on and
@@ -527,7 +546,8 @@
num_temporal_layers == o.num_temporal_layers &&
scale_resolution_down_by == o.scale_resolution_down_by &&
active == o.active && rid == o.rid &&
- adaptive_ptime == o.adaptive_ptime;
+ adaptive_ptime == o.adaptive_ptime &&
+ requested_resolution == o.requested_resolution;
}
bool operator!=(const RtpEncodingParameters& o) const {
return !(*this == o);
diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn
index 060b9e4..ee62abd 100644
--- a/api/video/BUILD.gn
+++ b/api/video/BUILD.gn
@@ -131,6 +131,11 @@
public = [ "render_resolution.h" ]
}
+rtc_source_set("resolution") {
+ visibility = [ "*" ]
+ public = [ "resolution.h" ]
+}
+
rtc_library("encoded_image") {
visibility = [ "*" ]
sources = [
diff --git a/api/video/recordable_encoded_frame.h b/api/video/recordable_encoded_frame.h
index 702f4d7..47ea23f 100644
--- a/api/video/recordable_encoded_frame.h
+++ b/api/video/recordable_encoded_frame.h
@@ -24,6 +24,7 @@
class RecordableEncodedFrame {
public:
// Encoded resolution in pixels
+ // TODO(bugs.webrtc.org/12114) : remove in favor of Resolution.
struct EncodedResolution {
bool empty() const { return width == 0 && height == 0; }
diff --git a/api/video/render_resolution.h b/api/video/render_resolution.h
index edcf8f8..fcf4f12 100644
--- a/api/video/render_resolution.h
+++ b/api/video/render_resolution.h
@@ -13,6 +13,7 @@
namespace webrtc {
+// TODO(bugs.webrtc.org/12114) : remove in favor of Resolution.
class RenderResolution {
public:
constexpr RenderResolution() = default;
diff --git a/api/video/resolution.h b/api/video/resolution.h
new file mode 100644
index 0000000..99cb622
--- /dev/null
+++ b/api/video/resolution.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_VIDEO_RESOLUTION_H_
+#define API_VIDEO_RESOLUTION_H_
+
+namespace webrtc {
+
+// A struct representing a video resolution in pixels.
+struct Resolution {
+ int width = 0;
+ int height = 0;
+};
+
+inline bool operator==(const Resolution& lhs, const Resolution& rhs) {
+ return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Resolution& lhs, const Resolution& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace webrtc
+
+#endif // API_VIDEO_RESOLUTION_H_
diff --git a/api/video/video_source_interface.h b/api/video/video_source_interface.h
index 5eb4ebf..72937c7 100644
--- a/api/video/video_source_interface.h
+++ b/api/video/video_source_interface.h
@@ -80,6 +80,24 @@
// Note that the `resolutions` can change while frames are in flight and
// should only be used as a hint when constructing the webrtc::VideoFrame.
std::vector<FrameSize> resolutions;
+
+ // This is the resolution requested by the user using RtpEncodingParameters.
+ absl::optional<FrameSize> requested_resolution;
+
+ // `active` : is (any) of the layers/sink(s) active.
+ bool is_active = false;
+
+ // This sub-struct contains information computed by VideoBroadcaster
+ // that aggregates several VideoSinkWants (and sends them to
+ // AdaptedVideoTrackSource).
+ struct Aggregates {
+ // `active_without_requested_resolution` is set by VideoBroadcaster
+ // when aggregating sink wants if there exists any sink (encoder) that is
+ // active but has not set the `requested_resolution`, i.e is relying on
+ // OnOutputFormatRequest to handle encode resolution.
+ bool any_active_without_requested_resolution = false;
+ };
+ absl::optional<Aggregates> aggregates;
};
inline bool operator==(const VideoSinkWants::FrameSize& a,
@@ -87,6 +105,11 @@
return a.width == b.width && a.height == b.height;
}
+inline bool operator!=(const VideoSinkWants::FrameSize& a,
+ const VideoSinkWants::FrameSize& b) {
+ return !(a == b);
+}
+
template <typename VideoFrameT>
class VideoSourceInterface {
public:
diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn
index 3f933b9..d6b7392 100644
--- a/api/video_codecs/BUILD.gn
+++ b/api/video_codecs/BUILD.gn
@@ -84,6 +84,7 @@
"../units:data_rate",
"../video:encoded_image",
"../video:render_resolution",
+ "../video:resolution",
"../video:video_bitrate_allocation",
"../video:video_codec_constants",
"../video:video_frame",
diff --git a/api/video_codecs/video_encoder_config.h b/api/video_codecs/video_encoder_config.h
index 86d89d5..3d1b176 100644
--- a/api/video_codecs/video_encoder_config.h
+++ b/api/video_codecs/video_encoder_config.h
@@ -18,6 +18,7 @@
#include "absl/types/optional.h"
#include "api/scoped_refptr.h"
+#include "api/video/resolution.h"
#include "api/video_codecs/scalability_mode.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_codec.h"
@@ -32,10 +33,11 @@
VideoStream(const VideoStream& other);
std::string ToString() const;
- // Width in pixels.
+ // Width/Height in pixels.
+ // This is the actual width and height used to configure encoder,
+ // which might be less than `requested_resolution` due to adaptation
+ // or due to the source providing smaller frames than requested.
size_t width;
-
- // Height in pixels.
size_t height;
// Frame rate in fps.
@@ -69,6 +71,17 @@
// If this stream is enabled by the user, or not.
bool active;
+
+ // An optional user supplied max_frame_resolution
+ // than can be set independently of (adapted) VideoSource.
+ // This value is set from RtpEncodingParameters::requested_resolution
+ // (i.e. used for signaling app-level settings).
+ //
+ // The actual encode resolution is in `width` and `height`,
+ // which can be lower than requested_resolution,
+ // e.g. if source only provides lower resolution or
+ // if resource adaptation is active.
+ absl::optional<Resolution> requested_resolution;
};
class VideoEncoderConfig {
diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc
index 813657e..694e690 100644
--- a/media/base/media_engine.cc
+++ b/media/base/media_engine.cc
@@ -106,6 +106,13 @@
"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 webrtc::RTCError::OK();
diff --git a/media/base/video_broadcaster.cc b/media/base/video_broadcaster.cc
index 1167d7f..60c0e1c 100644
--- a/media/base/video_broadcaster.cc
+++ b/media/base/video_broadcaster.cc
@@ -10,6 +10,7 @@
#include "media/base/video_broadcaster.h"
+#include <algorithm>
#include <vector>
#include "absl/types/optional.h"
@@ -123,6 +124,7 @@
VideoSinkWants wants;
wants.rotation_applied = false;
wants.resolution_alignment = 1;
+ wants.aggregates.emplace(VideoSinkWants::Aggregates());
for (auto& sink : sink_pairs()) {
// wants.rotation_applied == ANY(sink.wants.rotation_applied)
if (sink.wants.rotation_applied) {
@@ -147,6 +149,25 @@
}
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 &&
diff --git a/media/base/video_broadcaster_unittest.cc b/media/base/video_broadcaster_unittest.cc
index fe1a1cb..52f015c 100644
--- a/media/base/video_broadcaster_unittest.cc
+++ b/media/base/video_broadcaster_unittest.cc
@@ -24,6 +24,7 @@
using cricket::FakeVideoRenderer;
using rtc::VideoBroadcaster;
using rtc::VideoSinkWants;
+using FrameSize = rtc::VideoSinkWants::FrameSize;
using ::testing::AllOf;
using ::testing::Eq;
@@ -330,3 +331,70 @@
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);
+}
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 5651f59..2a9c0a8 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -2350,7 +2350,9 @@
(new_parameters.encodings[i].scale_resolution_down_by !=
rtp_parameters_.encodings[i].scale_resolution_down_by) ||
(new_parameters.encodings[i].num_temporal_layers !=
- rtp_parameters_.encodings[i].num_temporal_layers)) {
+ rtp_parameters_.encodings[i].num_temporal_layers) ||
+ (new_parameters.encodings[i].requested_resolution !=
+ rtp_parameters_.encodings[i].requested_resolution)) {
new_param = true;
break;
}
@@ -2562,6 +2564,8 @@
encoder_config.simulcast_layers[i].num_temporal_layers =
*rtp_parameters_.encodings[i].num_temporal_layers;
}
+ encoder_config.simulcast_layers[i].requested_resolution =
+ rtp_parameters_.encodings[i].requested_resolution;
}
encoder_config.legacy_conference_mode = parameters_.conference_mode;
@@ -3705,6 +3709,7 @@
layer.width = width;
layer.height = height;
layer.max_framerate = max_framerate;
+ layer.active = encoder_config.simulcast_layers[0].active;
if (encoder_config.simulcast_layers[0].scale_resolution_down_by > 1.) {
layer.width = ScaleDownResolution(
diff --git a/video/video_receive_stream2_unittest.cc b/video/video_receive_stream2_unittest.cc
index 79c680f..a05524f 100644
--- a/video/video_receive_stream2_unittest.cc
+++ b/video/video_receive_stream2_unittest.cc
@@ -158,7 +158,7 @@
TimeController* const time_controller_;
};
-MATCHER_P2(Resolution, w, h, "") {
+MATCHER_P2(MatchResolution, w, h, "") {
return arg.resolution().width == w && arg.resolution().height == h;
}
@@ -740,8 +740,9 @@
/*generate_key_frame=*/false);
InSequence s;
- EXPECT_CALL(callback, Call(Resolution(test::FakeDecoder::kDefaultWidth,
- test::FakeDecoder::kDefaultHeight)));
+ EXPECT_CALL(callback,
+ Call(MatchResolution(test::FakeDecoder::kDefaultWidth,
+ test::FakeDecoder::kDefaultHeight)));
EXPECT_CALL(callback, Call);
video_receive_stream_->OnCompleteFrame(
@@ -763,7 +764,7 @@
/*generate_key_frame=*/false);
InSequence s;
- EXPECT_CALL(callback, Call(Resolution(1080u, 720u)));
+ EXPECT_CALL(callback, Call(MatchResolution(1080u, 720u)));
EXPECT_CALL(callback, Call);
video_receive_stream_->OnCompleteFrame(
diff --git a/video/video_source_sink_controller.cc b/video/video_source_sink_controller.cc
index cf3b649..2f7b375 100644
--- a/video/video_source_sink_controller.cc
+++ b/video/video_source_sink_controller.cc
@@ -20,29 +20,6 @@
namespace webrtc {
-namespace {
-
-std::string WantsToString(const rtc::VideoSinkWants& wants) {
- rtc::StringBuilder ss;
-
- ss << "max_fps=" << wants.max_framerate_fps
- << " max_pixel_count=" << wants.max_pixel_count << " target_pixel_count="
- << (wants.target_pixel_count.has_value()
- ? std::to_string(wants.target_pixel_count.value())
- : "null")
- << " resolutions={";
- for (size_t i = 0; i < wants.resolutions.size(); ++i) {
- if (i != 0)
- ss << ",";
- ss << wants.resolutions[i].width << "x" << wants.resolutions[i].height;
- }
- ss << "}";
-
- return ss.Release();
-}
-
-} // namespace
-
VideoSourceSinkController::VideoSourceSinkController(
rtc::VideoSinkInterface<VideoFrame>* sink,
rtc::VideoSourceInterface<VideoFrame>* source)
@@ -86,8 +63,6 @@
if (!source_)
return;
rtc::VideoSinkWants wants = CurrentSettingsToSinkWants();
- RTC_LOG(LS_INFO) << "Pushing SourceSink restrictions: "
- << WantsToString(wants);
source_->AddOrUpdateSink(sink_, wants);
}
@@ -124,6 +99,17 @@
return resolutions_;
}
+bool VideoSourceSinkController::active() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return active_;
+}
+
+absl::optional<rtc::VideoSinkWants::FrameSize>
+VideoSourceSinkController::requested_resolution() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return requested_resolution_;
+}
+
void VideoSourceSinkController::SetRestrictions(
VideoSourceRestrictions restrictions) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
@@ -159,6 +145,17 @@
resolutions_ = std::move(resolutions);
}
+void VideoSourceSinkController::SetActive(bool active) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ active_ = active;
+}
+
+void VideoSourceSinkController::SetRequestedResolution(
+ absl::optional<rtc::VideoSinkWants::FrameSize> requested_resolution) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ requested_resolution_ = std::move(requested_resolution);
+}
+
// RTC_EXCLUSIVE_LOCKS_REQUIRED(sequence_checker_)
rtc::VideoSinkWants VideoSourceSinkController::CurrentSettingsToSinkWants()
const {
@@ -188,6 +185,8 @@
? static_cast<int>(frame_rate_upper_limit_.value())
: std::numeric_limits<int>::max());
wants.resolutions = resolutions_;
+ wants.is_active = active_;
+ wants.requested_resolution = requested_resolution_;
return wants;
}
diff --git a/video/video_source_sink_controller.h b/video/video_source_sink_controller.h
index e2a7eb7..1bb6ef6 100644
--- a/video/video_source_sink_controller.h
+++ b/video/video_source_sink_controller.h
@@ -51,6 +51,8 @@
bool rotation_applied() const;
int resolution_alignment() const;
const std::vector<rtc::VideoSinkWants::FrameSize>& resolutions() const;
+ bool active() const;
+ absl::optional<rtc::VideoSinkWants::FrameSize> requested_resolution() const;
// Updates the settings stored internally. In order for these settings to be
// applied to the sink, PushSourceSinkSettings() must subsequently be called.
@@ -61,6 +63,9 @@
void SetRotationApplied(bool rotation_applied);
void SetResolutionAlignment(int resolution_alignment);
void SetResolutions(std::vector<rtc::VideoSinkWants::FrameSize> resolutions);
+ void SetActive(bool active);
+ void SetRequestedResolution(
+ absl::optional<rtc::VideoSinkWants::FrameSize> requested_resolution);
private:
rtc::VideoSinkWants CurrentSettingsToSinkWants() const
@@ -87,6 +92,9 @@
int resolution_alignment_ RTC_GUARDED_BY(&sequence_checker_) = 1;
std::vector<rtc::VideoSinkWants::FrameSize> resolutions_
RTC_GUARDED_BY(&sequence_checker_);
+ bool active_ RTC_GUARDED_BY(&sequence_checker_) = true;
+ absl::optional<rtc::VideoSinkWants::FrameSize> requested_resolution_
+ RTC_GUARDED_BY(&sequence_checker_);
};
} // namespace webrtc
diff --git a/video/video_source_sink_controller_unittest.cc b/video/video_source_sink_controller_unittest.cc
index 2db7b56..75cc52b 100644
--- a/video/video_source_sink_controller_unittest.cc
+++ b/video/video_source_sink_controller_unittest.cc
@@ -24,6 +24,7 @@
namespace {
+using FrameSize = rtc::VideoSinkWants::FrameSize;
constexpr int kIntUnconstrained = std::numeric_limits<int>::max();
class MockVideoSinkWithVideoFrame : public rtc::VideoSinkInterface<VideoFrame> {
@@ -61,6 +62,7 @@
EXPECT_FALSE(controller.pixels_per_frame_upper_limit().has_value());
EXPECT_FALSE(controller.frame_rate_upper_limit().has_value());
EXPECT_FALSE(controller.rotation_applied());
+ EXPECT_FALSE(controller.requested_resolution().has_value());
EXPECT_EQ(controller.resolution_alignment(), 1);
EXPECT_CALL(source, AddOrUpdateSink(_, _))
@@ -71,6 +73,7 @@
EXPECT_EQ(wants.target_pixel_count, absl::nullopt);
EXPECT_EQ(wants.max_framerate_fps, kIntUnconstrained);
EXPECT_EQ(wants.resolution_alignment, 1);
+ EXPECT_FALSE(wants.requested_resolution.has_value());
});
controller.PushSourceSinkSettings();
}
@@ -162,4 +165,35 @@
VideoSourceSinkController controller(&sink, nullptr);
controller.RequestRefreshFrame();
}
+
+TEST(VideoSourceSinkControllerTest, RequestedResolutionPropagatesToWants) {
+ MockVideoSinkWithVideoFrame sink;
+ MockVideoSourceWithVideoFrame source;
+ VideoSourceSinkController controller(&sink, &source);
+ controller.SetRequestedResolution(FrameSize(640, 360));
+ EXPECT_TRUE(controller.requested_resolution().has_value());
+
+ EXPECT_CALL(source, AddOrUpdateSink(_, _))
+ .WillOnce([](rtc::VideoSinkInterface<VideoFrame>* sink,
+ const rtc::VideoSinkWants& wants) {
+ EXPECT_EQ(*wants.requested_resolution, FrameSize(640, 360));
+ });
+ controller.PushSourceSinkSettings();
+}
+
+TEST(VideoSourceSinkControllerTest, ActivePropagatesToWants) {
+ MockVideoSinkWithVideoFrame sink;
+ MockVideoSourceWithVideoFrame source;
+ VideoSourceSinkController controller(&sink, &source);
+ controller.SetActive(true);
+ EXPECT_TRUE(controller.active());
+
+ EXPECT_CALL(source, AddOrUpdateSink(_, _))
+ .WillOnce([](rtc::VideoSinkInterface<VideoFrame>* sink,
+ const rtc::VideoSinkWants& wants) {
+ EXPECT_TRUE(wants.is_active);
+ });
+ controller.PushSourceSinkSettings();
+}
+
} // namespace webrtc
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index e320e43..1018df4 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -1130,10 +1130,30 @@
RTC_DCHECK_LE(codec.startBitrate, 1000000);
max_framerate_ = codec.maxFramerate;
- // Inform source about max configured framerate.
+ // Inform source about max configured framerate,
+ // requested_resolution and which layers are active.
int max_framerate = 0;
+ // Is any layer active.
+ bool active = false;
+ // The max requested_resolution.
+ absl::optional<rtc::VideoSinkWants::FrameSize> requested_resolution;
for (const auto& stream : streams) {
max_framerate = std::max(stream.max_framerate, max_framerate);
+ active |= stream.active;
+ // Note: we propagate the highest requested_resolution regardless
+ // if layer is active or not.
+ if (stream.requested_resolution) {
+ if (!requested_resolution) {
+ requested_resolution.emplace(stream.requested_resolution->width,
+ stream.requested_resolution->height);
+ } else {
+ requested_resolution.emplace(
+ std::max(stream.requested_resolution->width,
+ requested_resolution->width),
+ std::max(stream.requested_resolution->height,
+ requested_resolution->height));
+ }
+ }
}
// The resolutions that we're actually encoding with.
@@ -1146,20 +1166,28 @@
encoder_resolutions.emplace_back(simulcastStream.width,
simulcastStream.height);
}
+
worker_queue_->PostTask(SafeTask(
task_safety_.flag(),
[this, max_framerate, alignment,
- encoder_resolutions = std::move(encoder_resolutions)]() {
+ encoder_resolutions = std::move(encoder_resolutions),
+ requested_resolution = std::move(requested_resolution), active]() {
RTC_DCHECK_RUN_ON(worker_queue_);
if (max_framerate !=
video_source_sink_controller_.frame_rate_upper_limit() ||
alignment != video_source_sink_controller_.resolution_alignment() ||
encoder_resolutions !=
- video_source_sink_controller_.resolutions()) {
+ video_source_sink_controller_.resolutions() ||
+ (video_source_sink_controller_.requested_resolution() !=
+ requested_resolution) ||
+ (video_source_sink_controller_.active() != active)) {
video_source_sink_controller_.SetFrameRateUpperLimit(max_framerate);
video_source_sink_controller_.SetResolutionAlignment(alignment);
video_source_sink_controller_.SetResolutions(
std::move(encoder_resolutions));
+ video_source_sink_controller_.SetRequestedResolution(
+ requested_resolution);
+ video_source_sink_controller_.SetActive(active);
video_source_sink_controller_.PushSourceSinkSettings();
}
}));