Add cropping to VIEEncoder to match simulcast streams resolution
Detect when simulcaststreamfactory adjust resolution and remeber cropping
parameters in VIEEncoder.
Expose EncoderStreamFactory in webrtcvideoengine2.
BUG=webrtc:7375, webrtc:6958
Review-Url: https://codereview.webrtc.org/2936393002
Cr-Commit-Position: refs/heads/master@{#18632}
diff --git a/webrtc/media/engine/webrtcvideoengine.cc b/webrtc/media/engine/webrtcvideoengine.cc
index 25afbb6..54d7103 100644
--- a/webrtc/media/engine/webrtcvideoengine.cc
+++ b/webrtc/media/engine/webrtcvideoengine.cc
@@ -286,69 +286,6 @@
}
return 1;
}
-
-class EncoderStreamFactory
- : public webrtc::VideoEncoderConfig::VideoStreamFactoryInterface {
- public:
- EncoderStreamFactory(std::string codec_name,
- int max_qp,
- int max_framerate,
- bool is_screencast,
- bool conference_mode)
- : codec_name_(codec_name),
- max_qp_(max_qp),
- max_framerate_(max_framerate),
- is_screencast_(is_screencast),
- conference_mode_(conference_mode) {}
-
- private:
- std::vector<webrtc::VideoStream> CreateEncoderStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config) override {
- if (is_screencast_ &&
- (!conference_mode_ || !cricket::UseSimulcastScreenshare())) {
- RTC_DCHECK_EQ(1, encoder_config.number_of_streams);
- }
- if (encoder_config.number_of_streams > 1 ||
- (CodecNamesEq(codec_name_, kVp8CodecName) && is_screencast_ &&
- conference_mode_)) {
- return GetSimulcastConfig(encoder_config.number_of_streams, width, height,
- encoder_config.max_bitrate_bps, max_qp_,
- max_framerate_, is_screencast_);
- }
-
- // For unset max bitrates set default bitrate for non-simulcast.
- int max_bitrate_bps =
- (encoder_config.max_bitrate_bps > 0)
- ? encoder_config.max_bitrate_bps
- : GetMaxDefaultVideoBitrateKbps(width, height) * 1000;
-
- webrtc::VideoStream stream;
- stream.width = width;
- stream.height = height;
- stream.max_framerate = max_framerate_;
- stream.min_bitrate_bps = kMinVideoBitrateKbps * 1000;
- stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate_bps;
- stream.max_qp = max_qp_;
-
- if (CodecNamesEq(codec_name_, kVp9CodecName) && !is_screencast_) {
- stream.temporal_layer_thresholds_bps.resize(
- GetDefaultVp9TemporalLayers() - 1);
- }
-
- std::vector<webrtc::VideoStream> streams;
- streams.push_back(stream);
- return streams;
- }
-
- const std::string codec_name_;
- const int max_qp_;
- const int max_framerate_;
- const bool is_screencast_;
- const bool conference_mode_;
-};
-
} // namespace
// Constants defined in webrtc/media/engine/constants.h
@@ -2676,4 +2613,55 @@
return video_codecs;
}
+EncoderStreamFactory::EncoderStreamFactory(std::string codec_name,
+ int max_qp,
+ int max_framerate,
+ bool is_screencast,
+ bool conference_mode)
+ : codec_name_(codec_name),
+ max_qp_(max_qp),
+ max_framerate_(max_framerate),
+ is_screencast_(is_screencast),
+ conference_mode_(conference_mode) {}
+
+std::vector<webrtc::VideoStream> EncoderStreamFactory::CreateEncoderStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config) {
+ if (is_screencast_ &&
+ (!conference_mode_ || !cricket::UseSimulcastScreenshare())) {
+ RTC_DCHECK_EQ(1, encoder_config.number_of_streams);
+ }
+ if (encoder_config.number_of_streams > 1 ||
+ (CodecNamesEq(codec_name_, kVp8CodecName) && is_screencast_ &&
+ conference_mode_)) {
+ return GetSimulcastConfig(encoder_config.number_of_streams, width, height,
+ encoder_config.max_bitrate_bps, max_qp_,
+ max_framerate_, is_screencast_);
+ }
+
+ // For unset max bitrates set default bitrate for non-simulcast.
+ int max_bitrate_bps =
+ (encoder_config.max_bitrate_bps > 0)
+ ? encoder_config.max_bitrate_bps
+ : GetMaxDefaultVideoBitrateKbps(width, height) * 1000;
+
+ webrtc::VideoStream stream;
+ stream.width = width;
+ stream.height = height;
+ stream.max_framerate = max_framerate_;
+ stream.min_bitrate_bps = kMinVideoBitrateKbps * 1000;
+ stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate_bps;
+ stream.max_qp = max_qp_;
+
+ if (CodecNamesEq(codec_name_, kVp9CodecName) && !is_screencast_) {
+ stream.temporal_layer_thresholds_bps.resize(GetDefaultVp9TemporalLayers() -
+ 1);
+ }
+
+ std::vector<webrtc::VideoStream> streams;
+ streams.push_back(stream);
+ return streams;
+}
+
} // namespace cricket
diff --git a/webrtc/media/engine/webrtcvideoengine.h b/webrtc/media/engine/webrtcvideoengine.h
index 8928b41..c51aa69 100644
--- a/webrtc/media/engine/webrtcvideoengine.h
+++ b/webrtc/media/engine/webrtcvideoengine.h
@@ -525,6 +525,28 @@
int64_t last_stats_log_ms_;
};
+class EncoderStreamFactory
+ : public webrtc::VideoEncoderConfig::VideoStreamFactoryInterface {
+ public:
+ EncoderStreamFactory(std::string codec_name,
+ int max_qp,
+ int max_framerate,
+ bool is_screencast,
+ bool conference_mode);
+
+ private:
+ std::vector<webrtc::VideoStream> CreateEncoderStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config) override;
+
+ const std::string codec_name_;
+ const int max_qp_;
+ const int max_framerate_;
+ const bool is_screencast_;
+ const bool conference_mode_;
+};
+
} // namespace cricket
#endif // WEBRTC_MEDIA_ENGINE_WEBRTCVIDEOENGINE_H_
diff --git a/webrtc/video/full_stack_tests.cc b/webrtc/video/full_stack_tests.cc
index 1e3b279..8299771 100644
--- a/webrtc/video/full_stack_tests.cc
+++ b/webrtc/video/full_stack_tests.cc
@@ -331,7 +331,7 @@
std::vector<VideoStream> streams = {
DefaultVideoStream(screenshare_params_low),
DefaultVideoStream(screenshare_params_high)};
- screenshare.ss = {streams, 1, 1, 0};
+ screenshare.ss = {streams, 1, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(screenshare);
}
@@ -423,7 +423,8 @@
screenshare.analyzer = {"screenshare_slides_vp9_2sl", 0.0, 0.0,
kFullStackTestDurationSecs};
screenshare.logs = false;
- screenshare.ss = {std::vector<VideoStream>(), 0, 2, 1};
+ screenshare.ss = {std::vector<VideoStream>(), 0, 2, 1,
+ std::vector<SpatialLayer>(), false};
RunTest(screenshare);
}
@@ -434,7 +435,8 @@
simulcast.analyzer = {"vp9svc_3sl_high", 0.0, 0.0,
kFullStackTestDurationSecs};
simulcast.logs = false;
- simulcast.ss = {std::vector<VideoStream>(), 0, 3, 2};
+ simulcast.ss = {std::vector<VideoStream>(), 0, 3, 2,
+ std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
@@ -445,7 +447,8 @@
simulcast.analyzer = {"vp9svc_3sl_medium", 0.0, 0.0,
kFullStackTestDurationSecs};
simulcast.logs = false;
- simulcast.ss = {std::vector<VideoStream>(), 0, 3, 1};
+ simulcast.ss = {std::vector<VideoStream>(), 0, 3, 1,
+ std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
@@ -455,11 +458,38 @@
simulcast.video = kSvcVp9Video;
simulcast.analyzer = {"vp9svc_3sl_low", 0.0, 0.0, kFullStackTestDurationSecs};
simulcast.logs = false;
- simulcast.ss = {std::vector<VideoStream>(), 0, 3, 0};
+ simulcast.ss = {std::vector<VideoStream>(), 0, 3, 0,
+ std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
#endif // !defined(RTC_DISABLE_VP9)
+// Android bots can't handle FullHD, so disable the test.
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_SimulcastFullHdOveruse DISABLED_SimulcastFullHdOveruse
+#else
+#define MAYBE_SimulcastFullHdOveruse SimulcastFullHdOveruse
+#endif
+
+TEST_F(FullStackTest, MAYBE_SimulcastFullHdOveruse) {
+ VideoQualityTest::Params simulcast;
+ simulcast.call.send_side_bwe = true;
+ simulcast.video = {true, 1920, 1080, 30, 800000, 2500000,
+ 2500000, false, "VP8", 3, 2, 400000,
+ false, false, "", "Generator"};
+ simulcast.analyzer = {"simulcast_HD_high", 0.0, 0.0,
+ kFullStackTestDurationSecs};
+ simulcast.pipe.loss_percent = 0;
+ simulcast.pipe.queue_delay_ms = 100;
+ std::vector<VideoStream> streams = {DefaultVideoStream(simulcast),
+ DefaultVideoStream(simulcast),
+ DefaultVideoStream(simulcast)};
+ simulcast.ss = {streams, 2, 1, 0, std::vector<SpatialLayer>(), true};
+ webrtc::test::ScopedFieldTrials override_trials(
+ "WebRTC-ForceSimulatedOveruseIntervalMs/1000-50000-300/");
+ RunTest(simulcast);
+}
+
TEST_F(FullStackTest, SimulcastVP8_3SL_High) {
VideoQualityTest::Params simulcast;
simulcast.call.send_side_bwe = true;
@@ -478,7 +508,7 @@
std::vector<VideoStream> streams = {DefaultVideoStream(video_params_low),
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
- simulcast.ss = {streams, 2, 1, 0};
+ simulcast.ss = {streams, 2, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
@@ -500,7 +530,7 @@
std::vector<VideoStream> streams = {DefaultVideoStream(video_params_low),
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
- simulcast.ss = {streams, 1, 1, 0};
+ simulcast.ss = {streams, 1, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
@@ -522,7 +552,7 @@
std::vector<VideoStream> streams = {DefaultVideoStream(video_params_low),
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
- simulcast.ss = {streams, 0, 1, 0};
+ simulcast.ss = {streams, 0, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(simulcast);
}
@@ -545,7 +575,7 @@
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
large_room.num_thumbnails = 5;
- large_room.ss = {streams, 2, 1, 0};
+ large_room.ss = {streams, 2, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(large_room);
}
@@ -578,7 +608,7 @@
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
large_room.num_thumbnails = 15;
- large_room.ss = {streams, 2, 1, 0};
+ large_room.ss = {streams, 2, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(large_room);
}
@@ -601,7 +631,7 @@
DefaultVideoStream(video_params_medium),
DefaultVideoStream(video_params_high)};
large_room.num_thumbnails = 50;
- large_room.ss = {streams, 2, 1, 0};
+ large_room.ss = {streams, 2, 1, 0, std::vector<SpatialLayer>(), false};
RunTest(large_room);
}
diff --git a/webrtc/video/video_quality_test.cc b/webrtc/video/video_quality_test.cc
index 0ccc6b8..d19ecbe 100644
--- a/webrtc/video/video_quality_test.cc
+++ b/webrtc/video/video_quality_test.cc
@@ -148,12 +148,13 @@
size_t selected_stream,
int selected_sl,
int selected_tl,
- bool is_quick_test_enabled)
+ bool is_quick_test_enabled,
+ Clock* clock)
: transport_(transport),
receiver_(nullptr),
send_stream_(nullptr),
receive_stream_(nullptr),
- captured_frame_forwarder_(this),
+ captured_frame_forwarder_(this, clock),
test_label_(test_label),
graph_data_output_file_(graph_data_output_file),
graph_title_(graph_title),
@@ -219,6 +220,13 @@
virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; }
+ void SetSource(test::VideoCapturer* video_capturer, bool respect_sink_wants) {
+ if (respect_sink_wants)
+ captured_frame_forwarder_.SetSource(video_capturer);
+ rtc::VideoSinkWants wants;
+ video_capturer->AddOrUpdateSink(InputInterface(), wants);
+ }
+
void SetSendStream(VideoSendStream* stream) {
rtc::CritScope lock(&crit_);
RTC_DCHECK(!send_stream_);
@@ -782,7 +790,7 @@
// Perform expensive psnr and ssim calculations while not holding lock.
double psnr = -1.0;
double ssim = -1.0;
- if (comparison.reference) {
+ if (comparison.reference && !comparison.dropped) {
psnr = I420PSNR(&*comparison.reference, &*comparison.render);
ssim = I420SSIM(&*comparison.reference, &*comparison.render);
}
@@ -901,8 +909,15 @@
class CapturedFrameForwarder : public rtc::VideoSinkInterface<VideoFrame>,
public rtc::VideoSourceInterface<VideoFrame> {
public:
- explicit CapturedFrameForwarder(VideoAnalyzer* analyzer)
- : analyzer_(analyzer), send_stream_input_(nullptr) {}
+ explicit CapturedFrameForwarder(VideoAnalyzer* analyzer, Clock* clock)
+ : analyzer_(analyzer),
+ send_stream_input_(nullptr),
+ video_capturer_(nullptr),
+ clock_(clock) {}
+
+ void SetSource(test::VideoCapturer* video_capturer) {
+ video_capturer_ = video_capturer;
+ }
private:
void OnFrame(const VideoFrame& video_frame) override {
@@ -910,8 +925,8 @@
// Frames from the capturer does not have a rtp timestamp.
// Create one so it can be used for comparison.
RTC_DCHECK_EQ(0, video_frame.timestamp());
- if (copy.ntp_time_ms() == 0)
- copy.set_ntp_time_ms(rtc::TimeMillis());
+ if (video_frame.ntp_time_ms() == 0)
+ copy.set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
copy.set_timestamp(copy.ntp_time_ms() * 90);
analyzer_->AddCapturedFrameForComparison(copy);
rtc::CritScope lock(&crit_);
@@ -925,6 +940,9 @@
rtc::CritScope lock(&crit_);
RTC_DCHECK(!send_stream_input_ || send_stream_input_ == sink);
send_stream_input_ = sink;
+ if (video_capturer_) {
+ video_capturer_->AddOrUpdateSink(this, wants);
+ }
}
// Called by |send_stream_| when |send_stream_.SetSource()| is called.
@@ -937,6 +955,8 @@
VideoAnalyzer* const analyzer_;
rtc::CriticalSection crit_;
rtc::VideoSinkInterface<VideoFrame>* send_stream_input_ GUARDED_BY(crit_);
+ test::VideoCapturer* video_capturer_;
+ Clock* clock_;
};
void AddCapturedFrameForComparison(const VideoFrame& video_frame) {
@@ -1238,6 +1258,7 @@
params->ss.streams.push_back(stream);
}
params->ss.selected_stream = selected_stream;
+ params->ss.infer_streams = false;
params->ss.num_spatial_layers = num_spatial_layers ? num_spatial_layers : 1;
params->ss.selected_sl = selected_sl;
@@ -1319,8 +1340,15 @@
video_encoder_config_.max_bitrate_bps +=
params_.ss.streams[i].max_bitrate_bps;
}
- video_encoder_config_.video_stream_factory =
- new rtc::RefCountedObject<VideoStreamFactory>(params_.ss.streams);
+ if (params_.ss.infer_streams) {
+ video_encoder_config_.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ params_.video.codec, params_.ss.streams[0].max_qp,
+ params_.video.fps, params_.screenshare.enabled, true);
+ } else {
+ video_encoder_config_.video_stream_factory =
+ new rtc::RefCountedObject<VideoStreamFactory>(params_.ss.streams);
+ }
video_encoder_config_.spatial_layers = params_.ss.spatial_layers;
@@ -1423,9 +1451,15 @@
params_.video.suspend_below_min_bitrate;
thumbnail_encoder_config.number_of_streams = 1;
thumbnail_encoder_config.max_bitrate_bps = 50000;
- thumbnail_encoder_config.video_stream_factory =
- new rtc::RefCountedObject<VideoStreamFactory>(
- std::vector<webrtc::VideoStream>{DefaultThumbnailStream()});
+ if (params_.ss.infer_streams) {
+ thumbnail_encoder_config.video_stream_factory =
+ new rtc::RefCountedObject<VideoStreamFactory>(params_.ss.streams);
+ } else {
+ thumbnail_encoder_config.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ params_.video.codec, params_.ss.streams[0].max_qp,
+ params_.video.fps, params_.screenshare.enabled, true);
+ }
thumbnail_encoder_config.spatial_layers = params_.ss.spatial_layers;
VideoReceiveStream::Config thumbnail_receive_config(send_transport);
@@ -1565,7 +1599,11 @@
EXPECT_TRUE(frame_generator_capturer->Init());
video_capturer_.reset(frame_generator_capturer);
} else {
- if (params_.video.clip_name.empty()) {
+ if (params_.video.clip_name == "Generator") {
+ video_capturer_.reset(test::FrameGeneratorCapturer::Create(
+ static_cast<int>(params_.video.width),
+ static_cast<int>(params_.video.height), params_.video.fps, clock_));
+ } else if (params_.video.clip_name.empty()) {
video_capturer_.reset(test::VcmCapturer::Create(
params_.video.width, params_.video.height, params_.video.fps,
params_.video.capture_device_index));
@@ -1631,7 +1669,7 @@
kVideoSendSsrcs[params_.ss.selected_stream],
kSendRtxSsrcs[params_.ss.selected_stream],
static_cast<size_t>(params_.ss.selected_stream), params.ss.selected_sl,
- params_.video.selected_tl, is_quick_test_enabled);
+ params_.video.selected_tl, is_quick_test_enabled, clock_);
analyzer.SetReceiver(receiver_call_->Receiver());
send_transport.SetReceiver(&analyzer);
recv_transport.SetReceiver(sender_call_->Receiver());
@@ -1662,8 +1700,7 @@
CreateCapturer();
- rtc::VideoSinkWants wants;
- video_capturer_->AddOrUpdateSink(analyzer.InputInterface(), wants);
+ analyzer.SetSource(video_capturer_.get(), params_.ss.infer_streams);
StartEncodedFrameLogs(video_send_stream_);
StartEncodedFrameLogs(video_receive_streams_[0]);
diff --git a/webrtc/video/video_quality_test.h b/webrtc/video/video_quality_test.h
index 5cf54bd..d2fa2ed 100644
--- a/webrtc/video/video_quality_test.h
+++ b/webrtc/video/video_quality_test.h
@@ -51,7 +51,7 @@
bool ulpfec;
bool flexfec;
std::string encoded_frame_base_path;
- std::string clip_name;
+ std::string clip_name; // "Generator" to generate frames instead.
size_t capture_device_index;
} video;
struct Audio {
@@ -82,6 +82,8 @@
int selected_sl;
// If empty, bitrates are generated in VP9Impl automatically.
std::vector<SpatialLayer> spatial_layers;
+ // If set, default parameters will be used instead of |streams|.
+ bool infer_streams;
} ss;
int num_thumbnails;
};
diff --git a/webrtc/video/vie_encoder.cc b/webrtc/video/vie_encoder.cc
index d82e4fc..3308961 100644
--- a/webrtc/video/vie_encoder.cc
+++ b/webrtc/video/vie_encoder.cc
@@ -15,6 +15,7 @@
#include <numeric>
#include <utility>
+#include "webrtc/api/video/i420_buffer.h"
#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/location.h"
@@ -575,6 +576,20 @@
encoder_config_.video_stream_factory->CreateEncoderStreams(
last_frame_info_->width, last_frame_info_->height, encoder_config_);
+ // TODO(ilnik): If configured resolution is significantly less than provided,
+ // e.g. because there are not enough SSRCs for all simulcast streams,
+ // signal new resolutions via SinkWants to video source.
+
+ // Stream dimensions may be not equal to given because of a simulcast
+ // restrictions.
+ int highest_stream_width = static_cast<int>(streams.back().width);
+ int highest_stream_height = static_cast<int>(streams.back().height);
+ // Dimension may be reduced to be, e.g. divisible by 4.
+ RTC_CHECK_GE(last_frame_info_->width, highest_stream_width);
+ RTC_CHECK_GE(last_frame_info_->height, highest_stream_height);
+ crop_width_ = last_frame_info_->width - highest_stream_width;
+ crop_height_ = last_frame_info_->height - highest_stream_height;
+
VideoCodec codec;
if (!VideoCodecInitializer::SetupCodec(encoder_config_, settings_, streams,
nack_enabled_, &codec,
@@ -770,12 +785,35 @@
}
TraceFrameDropEnd();
+ VideoFrame out_frame(video_frame);
+ // Crop frame if needed.
+ if (crop_width_ > 0 || crop_height_ > 0) {
+ int cropped_width = video_frame.width() - crop_width_;
+ int cropped_height = video_frame.height() - crop_height_;
+ rtc::scoped_refptr<I420Buffer> cropped_buffer =
+ I420Buffer::Create(cropped_width, cropped_height);
+ // TODO(ilnik): Remove scaling if cropping is too big, as it should never
+ // happen after SinkWants signaled correctly from ReconfigureEncoder.
+ if (crop_width_ < 4 && crop_height_ < 4) {
+ cropped_buffer->CropAndScaleFrom(
+ *video_frame.video_frame_buffer()->ToI420(), crop_width_ / 2,
+ crop_height_ / 2, cropped_width, cropped_height);
+ } else {
+ cropped_buffer->ScaleFrom(
+ *video_frame.video_frame_buffer()->ToI420().get());
+ }
+ out_frame =
+ VideoFrame(cropped_buffer, video_frame.timestamp(),
+ video_frame.render_time_ms(), video_frame.rotation());
+ out_frame.set_ntp_time_ms(video_frame.ntp_time_ms());
+ }
+
TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame.render_time_ms(),
"Encode");
- overuse_detector_->FrameCaptured(video_frame, time_when_posted_us);
+ overuse_detector_->FrameCaptured(out_frame, time_when_posted_us);
- video_sender_.AddVideoFrame(video_frame, nullptr);
+ video_sender_.AddVideoFrame(out_frame, nullptr);
}
void ViEEncoder::SendKeyFrame() {
diff --git a/webrtc/video/vie_encoder.h b/webrtc/video/vie_encoder.h
index 77a6bc4..91c6ca4 100644
--- a/webrtc/video/vie_encoder.h
+++ b/webrtc/video/vie_encoder.h
@@ -257,6 +257,8 @@
// encoder on the next frame.
bool pending_encoder_reconfiguration_ ACCESS_ON(&encoder_queue_);
rtc::Optional<VideoFrameInfo> last_frame_info_ ACCESS_ON(&encoder_queue_);
+ int crop_width_ ACCESS_ON(&encoder_queue_);
+ int crop_height_ ACCESS_ON(&encoder_queue_);
uint32_t encoder_start_bitrate_bps_ ACCESS_ON(&encoder_queue_);
size_t max_data_payload_length_ ACCESS_ON(&encoder_queue_);
bool nack_enabled_ ACCESS_ON(&encoder_queue_);
diff --git a/webrtc/video/vie_encoder_unittest.cc b/webrtc/video/vie_encoder_unittest.cc
index b143e4d..976cf47 100644
--- a/webrtc/video/vie_encoder_unittest.cc
+++ b/webrtc/video/vie_encoder_unittest.cc
@@ -166,6 +166,7 @@
const int framerate_;
};
+
class AdaptingFrameForwarder : public test::FrameForwarder {
public:
AdaptingFrameForwarder() : adaptation_enabled_(false) {}
@@ -2944,4 +2945,71 @@
vie_encoder_->Stop();
}
+TEST_F(ViEEncoderTest, AcceptsFullHdAdaptedDownSimulcastFrames) {
+ // Simulates simulcast behavior and makes highest stream resolutions divisible
+ // by 4.
+ class CroppingVideoStreamFactory
+ : public VideoEncoderConfig::VideoStreamFactoryInterface {
+ public:
+ explicit CroppingVideoStreamFactory(size_t num_temporal_layers,
+ int framerate)
+ : num_temporal_layers_(num_temporal_layers), framerate_(framerate) {
+ EXPECT_GT(num_temporal_layers, 0u);
+ EXPECT_GT(framerate, 0);
+ }
+
+ private:
+ std::vector<VideoStream> CreateEncoderStreams(
+ int width,
+ int height,
+ const VideoEncoderConfig& encoder_config) override {
+ std::vector<VideoStream> streams =
+ test::CreateVideoStreams(width - width % 4, height - height % 4,
+ encoder_config);
+ for (VideoStream& stream : streams) {
+ stream.temporal_layer_thresholds_bps.resize(num_temporal_layers_ - 1);
+ stream.max_framerate = framerate_;
+ }
+ return streams;
+ }
+
+ const size_t num_temporal_layers_;
+ const int framerate_;
+ };
+
+ const int kFrameWidth = 1920;
+ const int kFrameHeight = 1080;
+ // 3/4 of 1920.
+ const int kAdaptedFrameWidth = 1440;
+ // 3/4 of 1080 rounded down to multiple of 4.
+ const int kAdaptedFrameHeight = 808;
+ const int kFramerate = 24;
+
+ vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
+ // Trigger reconfigure encoder (without resetting the entire instance).
+ VideoEncoderConfig video_encoder_config;
+ video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
+ video_encoder_config.number_of_streams = 1;
+ video_encoder_config.video_stream_factory =
+ new rtc::RefCountedObject<CroppingVideoStreamFactory>(1, kFramerate);
+ vie_encoder_->ConfigureEncoder(std::move(video_encoder_config),
+ kMaxPayloadLength, false);
+ vie_encoder_->WaitUntilTaskQueueIsIdle();
+
+ video_source_.set_adaptation_enabled(true);
+
+ video_source_.IncomingCapturedFrame(
+ CreateFrame(1, kFrameWidth, kFrameHeight));
+ sink_.WaitForEncodedFrame(kFrameWidth, kFrameHeight);
+
+ // Trigger CPU overuse, downscale by 3/4.
+ vie_encoder_->TriggerCpuOveruse();
+ video_source_.IncomingCapturedFrame(
+ CreateFrame(2, kFrameWidth, kFrameHeight));
+ sink_.WaitForEncodedFrame(kAdaptedFrameWidth, kAdaptedFrameHeight);
+
+ vie_encoder_->Stop();
+}
+
+
} // namespace webrtc