Implement dual stream full stack test and loopback tool
Bug: webrtc:8588
Change-Id: I0abec4891a723c98001f4580f0cfa57a5d6d6bdb
Reviewed-on: https://webrtc-review.googlesource.com/34441
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21416}diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 2392e9f..bf2ec8c 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -1121,13 +1121,16 @@
VideoQualityTest::Params::Params()
: call({false, Call::Config::BitrateConfig(), 0}),
- video({false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
- false, ""}),
+ video{{false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
+ false, ""},
+ {false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
+ false, ""}},
audio({false, false, false}),
- screenshare({false, false, 10, 0}),
+ screenshare{{false, false, 10, 0}, {false, false, 10, 0}},
analyzer({"", 0.0, 0.0, 0, "", ""}),
pipe(),
- ss({std::vector<VideoStream>(), 0, 0, -1, std::vector<SpatialLayer>()}),
+ ss{{std::vector<VideoStream>(), 0, 0, -1, std::vector<SpatialLayer>()},
+ {std::vector<VideoStream>(), 0, 0, -1, std::vector<SpatialLayer>()}},
logging({false, "", "", ""}) {}
VideoQualityTest::Params::~Params() = default;
@@ -1136,70 +1139,84 @@
std::string VideoQualityTest::GenerateGraphTitle() const {
std::stringstream ss;
- ss << params_.video.codec;
- ss << " (" << params_.video.target_bitrate_bps / 1000 << "kbps";
- ss << ", " << params_.video.fps << " FPS";
- if (params_.screenshare.scroll_duration)
- ss << ", " << params_.screenshare.scroll_duration << "s scroll";
- if (params_.ss.streams.size() > 1)
- ss << ", Stream #" << params_.ss.selected_stream;
- if (params_.ss.num_spatial_layers > 1)
- ss << ", Layer #" << params_.ss.selected_sl;
+ ss << params_.video[0].codec;
+ ss << " (" << params_.video[0].target_bitrate_bps / 1000 << "kbps";
+ ss << ", " << params_.video[0].fps << " FPS";
+ if (params_.screenshare[0].scroll_duration)
+ ss << ", " << params_.screenshare[0].scroll_duration << "s scroll";
+ if (params_.ss[0].streams.size() > 1)
+ ss << ", Stream #" << params_.ss[0].selected_stream;
+ if (params_.ss[0].num_spatial_layers > 1)
+ ss << ", Layer #" << params_.ss[0].selected_sl;
ss << ")";
return ss.str();
}
void VideoQualityTest::CheckParams() {
- if (!params_.video.enabled)
- return;
- // Add a default stream in none specified.
- if (params_.ss.streams.empty())
- params_.ss.streams.push_back(VideoQualityTest::DefaultVideoStream(params_));
- if (params_.ss.num_spatial_layers == 0)
- params_.ss.num_spatial_layers = 1;
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ // Iterate over primary and secondary video streams.
+ if (!params_.video[video_idx].enabled)
+ return;
+ // Add a default stream in none specified.
+ if (params_.ss[video_idx].streams.empty())
+ params_.ss[video_idx].streams.push_back(
+ VideoQualityTest::DefaultVideoStream(params_, video_idx));
+ if (params_.ss[video_idx].num_spatial_layers == 0)
+ params_.ss[video_idx].num_spatial_layers = 1;
- if (params_.pipe.loss_percent != 0 ||
- params_.pipe.queue_length_packets != 0) {
- // Since LayerFilteringTransport changes the sequence numbers, we can't
- // use that feature with pack loss, since the NACK request would end up
- // retransmitting the wrong packets.
- RTC_CHECK(params_.ss.selected_sl == -1 ||
- params_.ss.selected_sl == params_.ss.num_spatial_layers - 1);
- RTC_CHECK(params_.video.selected_tl == -1 ||
- params_.video.selected_tl ==
- params_.video.num_temporal_layers - 1);
- }
+ if (params_.pipe.loss_percent != 0 ||
+ params_.pipe.queue_length_packets != 0) {
+ // Since LayerFilteringTransport changes the sequence numbers, we can't
+ // use that feature with pack loss, since the NACK request would end up
+ // retransmitting the wrong packets.
+ RTC_CHECK(params_.ss[video_idx].selected_sl == -1 ||
+ params_.ss[video_idx].selected_sl ==
+ params_.ss[video_idx].num_spatial_layers - 1);
+ RTC_CHECK(params_.video[video_idx].selected_tl == -1 ||
+ params_.video[video_idx].selected_tl ==
+ params_.video[video_idx].num_temporal_layers - 1);
+ }
- // TODO(ivica): Should max_bitrate_bps == -1 represent inf max bitrate, as it
- // does in some parts of the code?
- RTC_CHECK_GE(params_.video.max_bitrate_bps, params_.video.target_bitrate_bps);
- RTC_CHECK_GE(params_.video.target_bitrate_bps, params_.video.min_bitrate_bps);
- RTC_CHECK_LT(params_.video.selected_tl, params_.video.num_temporal_layers);
- RTC_CHECK_LE(params_.ss.selected_stream, params_.ss.streams.size());
- for (const VideoStream& stream : params_.ss.streams) {
- RTC_CHECK_GE(stream.min_bitrate_bps, 0);
- RTC_CHECK_GE(stream.target_bitrate_bps, stream.min_bitrate_bps);
- RTC_CHECK_GE(stream.max_bitrate_bps, stream.target_bitrate_bps);
- }
- // TODO(ivica): Should we check if the sum of all streams/layers is equal to
- // the total bitrate? We anyway have to update them in the case bitrate
- // estimator changes the total bitrates.
- RTC_CHECK_GE(params_.ss.num_spatial_layers, 1);
- RTC_CHECK_LE(params_.ss.selected_sl, params_.ss.num_spatial_layers);
- RTC_CHECK(params_.ss.spatial_layers.empty() ||
- params_.ss.spatial_layers.size() ==
- static_cast<size_t>(params_.ss.num_spatial_layers));
- if (params_.video.codec == "VP8") {
- RTC_CHECK_EQ(params_.ss.num_spatial_layers, 1);
- } else if (params_.video.codec == "VP9") {
- RTC_CHECK_EQ(params_.ss.streams.size(), 1);
- }
- RTC_CHECK_GE(params_.call.num_thumbnails, 0);
- if (params_.call.num_thumbnails > 0) {
- RTC_CHECK_EQ(params_.ss.num_spatial_layers, 1);
- RTC_CHECK_EQ(params_.ss.streams.size(), 3);
- RTC_CHECK_EQ(params_.video.num_temporal_layers, 3);
- RTC_CHECK_EQ(params_.video.codec, "VP8");
+ // TODO(ivica): Should max_bitrate_bps == -1 represent inf max bitrate, as
+ // it does in some parts of the code?
+ RTC_CHECK_GE(params_.video[video_idx].max_bitrate_bps,
+ params_.video[video_idx].target_bitrate_bps);
+ RTC_CHECK_GE(params_.video[video_idx].target_bitrate_bps,
+ params_.video[video_idx].min_bitrate_bps);
+ RTC_CHECK_LT(params_.video[video_idx].selected_tl,
+ params_.video[video_idx].num_temporal_layers);
+ RTC_CHECK_LE(params_.ss[video_idx].selected_stream,
+ params_.ss[video_idx].streams.size());
+ for (const VideoStream& stream : params_.ss[video_idx].streams) {
+ RTC_CHECK_GE(stream.min_bitrate_bps, 0);
+ RTC_CHECK_GE(stream.target_bitrate_bps, stream.min_bitrate_bps);
+ RTC_CHECK_GE(stream.max_bitrate_bps, stream.target_bitrate_bps);
+ }
+ // TODO(ivica): Should we check if the sum of all streams/layers is equal to
+ // the total bitrate? We anyway have to update them in the case bitrate
+ // estimator changes the total bitrates.
+ RTC_CHECK_GE(params_.ss[video_idx].num_spatial_layers, 1);
+ RTC_CHECK_LE(params_.ss[video_idx].selected_sl,
+ params_.ss[video_idx].num_spatial_layers);
+ RTC_CHECK(
+ params_.ss[video_idx].spatial_layers.empty() ||
+ params_.ss[video_idx].spatial_layers.size() ==
+ static_cast<size_t>(params_.ss[video_idx].num_spatial_layers));
+ if (params_.video[video_idx].codec == "VP8") {
+ RTC_CHECK_EQ(params_.ss[video_idx].num_spatial_layers, 1);
+ } else if (params_.video[video_idx].codec == "VP9") {
+ RTC_CHECK_EQ(params_.ss[video_idx].streams.size(), 1);
+ }
+ RTC_CHECK_GE(params_.call.num_thumbnails, 0);
+ if (params_.call.num_thumbnails > 0) {
+ RTC_CHECK_EQ(params_.ss[video_idx].num_spatial_layers, 1);
+ RTC_CHECK_EQ(params_.ss[video_idx].streams.size(), 3);
+ RTC_CHECK_EQ(params_.video[video_idx].num_temporal_layers, 3);
+ RTC_CHECK_EQ(params_.video[video_idx].codec, "VP8");
+ }
+ // Dual streams with FEC not supported in tests yet.
+ RTC_CHECK(!params_.video[video_idx].flexfec || num_video_streams_ == 1);
+ RTC_CHECK(!params_.video[video_idx].ulpfec || num_video_streams_ == 1);
}
}
@@ -1232,24 +1249,26 @@
}
// Static.
-VideoStream VideoQualityTest::DefaultVideoStream(const Params& params) {
+VideoStream VideoQualityTest::DefaultVideoStream(const Params& params,
+ size_t video_idx) {
VideoStream stream;
- stream.width = params.video.width;
- stream.height = params.video.height;
- stream.max_framerate = params.video.fps;
- stream.min_bitrate_bps = params.video.min_bitrate_bps;
- stream.target_bitrate_bps = params.video.target_bitrate_bps;
- stream.max_bitrate_bps = params.video.max_bitrate_bps;
+ stream.width = params.video[video_idx].width;
+ stream.height = params.video[video_idx].height;
+ stream.max_framerate = params.video[video_idx].fps;
+ stream.min_bitrate_bps = params.video[video_idx].min_bitrate_bps;
+ stream.target_bitrate_bps = params.video[video_idx].target_bitrate_bps;
+ stream.max_bitrate_bps = params.video[video_idx].max_bitrate_bps;
stream.max_qp = kDefaultMaxQp;
// TODO(sprang): Can we make this less of a hack?
- if (params.video.num_temporal_layers == 2) {
+ if (params.video[video_idx].num_temporal_layers == 2) {
stream.temporal_layer_thresholds_bps.push_back(stream.target_bitrate_bps);
- } else if (params.video.num_temporal_layers == 3) {
+ } else if (params.video[video_idx].num_temporal_layers == 3) {
stream.temporal_layer_thresholds_bps.push_back(stream.max_bitrate_bps / 4);
stream.temporal_layer_thresholds_bps.push_back(stream.target_bitrate_bps);
} else {
- RTC_CHECK_LE(params.video.num_temporal_layers, kMaxTemporalStreams);
- for (int i = 0; i < params.video.num_temporal_layers - 1; ++i) {
+ RTC_CHECK_LE(params.video[video_idx].num_temporal_layers,
+ kMaxTemporalStreams);
+ for (int i = 0; i < params.video[video_idx].num_temporal_layers - 1; ++i) {
stream.temporal_layer_thresholds_bps.push_back(static_cast<int>(
stream.max_bitrate_bps * kVp8LayerRateAlloction[0][i] + 0.5));
}
@@ -1273,39 +1292,44 @@
// Static.
void VideoQualityTest::FillScalabilitySettings(
Params* params,
+ size_t video_idx,
const std::vector<std::string>& stream_descriptors,
int num_streams,
size_t selected_stream,
int num_spatial_layers,
int selected_sl,
const std::vector<std::string>& sl_descriptors) {
- if (params->ss.streams.empty() && params->ss.infer_streams) {
+ if (params->ss[video_idx].streams.empty() &&
+ params->ss[video_idx].infer_streams) {
webrtc::VideoEncoderConfig encoder_config;
encoder_config.content_type =
- params->screenshare.enabled
+ params->screenshare[video_idx].enabled
? webrtc::VideoEncoderConfig::ContentType::kScreen
: webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo;
- encoder_config.max_bitrate_bps = params->video.max_bitrate_bps;
- encoder_config.min_transmit_bitrate_bps = params->video.min_transmit_bps;
+ encoder_config.max_bitrate_bps = params->video[video_idx].max_bitrate_bps;
+ encoder_config.min_transmit_bitrate_bps =
+ params->video[video_idx].min_transmit_bps;
encoder_config.number_of_streams = num_streams;
- encoder_config.spatial_layers = params->ss.spatial_layers;
+ encoder_config.spatial_layers = params->ss[video_idx].spatial_layers;
encoder_config.video_stream_factory =
new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
- params->video.codec, kDefaultMaxQp, params->video.fps,
- params->screenshare.enabled, true);
- params->ss.streams =
+ params->video[video_idx].codec, kDefaultMaxQp,
+ params->video[video_idx].fps,
+ params->screenshare[video_idx].enabled, true);
+ params->ss[video_idx].streams =
encoder_config.video_stream_factory->CreateEncoderStreams(
- static_cast<int>(params->video.width),
- static_cast<int>(params->video.height), encoder_config);
+ static_cast<int>(params->video[video_idx].width),
+ static_cast<int>(params->video[video_idx].height), encoder_config);
} else {
// Read VideoStream and SpatialLayer elements from a list of comma separated
// lists. To use a default value for an element, use -1 or leave empty.
// Validity checks performed in CheckParams.
- RTC_CHECK(params->ss.streams.empty());
+ RTC_CHECK(params->ss[video_idx].streams.empty());
for (auto descriptor : stream_descriptors) {
if (descriptor.empty())
continue;
- VideoStream stream = VideoQualityTest::DefaultVideoStream(*params);
+ VideoStream stream =
+ VideoQualityTest::DefaultVideoStream(*params, video_idx);
std::vector<int> v = VideoQualityTest::ParseCSV(descriptor);
if (v[0] != -1)
stream.width = static_cast<size_t>(v[0]);
@@ -1327,17 +1351,17 @@
stream.temporal_layer_thresholds_bps.end(), v.begin() + 7, v.end());
} else {
// Automatic TL thresholds for more than two layers not supported.
- RTC_CHECK_LE(params->video.num_temporal_layers, 2);
+ RTC_CHECK_LE(params->video[video_idx].num_temporal_layers, 2);
}
- params->ss.streams.push_back(stream);
+ params->ss[video_idx].streams.push_back(stream);
}
}
- params->ss.num_spatial_layers = std::max(1, num_spatial_layers);
- params->ss.selected_stream = selected_stream;
+ params->ss[video_idx].num_spatial_layers = std::max(1, num_spatial_layers);
+ params->ss[video_idx].selected_stream = selected_stream;
- params->ss.selected_sl = selected_sl;
- RTC_CHECK(params->ss.spatial_layers.empty());
+ params->ss[video_idx].selected_sl = selected_sl;
+ RTC_CHECK(params->ss[video_idx].spatial_layers.empty());
for (auto descriptor : sl_descriptors) {
if (descriptor.empty())
continue;
@@ -1348,120 +1372,202 @@
layer.scaling_factor_num = v[0] == -1 ? 1 : v[0];
layer.scaling_factor_den = v[1] == -1 ? 1 : v[1];
layer.target_bitrate_bps = v[2];
- params->ss.spatial_layers.push_back(layer);
+ params->ss[video_idx].spatial_layers.push_back(layer);
}
}
void VideoQualityTest::SetupVideo(Transport* send_transport,
Transport* recv_transport) {
- size_t num_video_streams = params_.ss.streams.size();
- size_t num_flexfec_streams = params_.video.flexfec ? 1 : 0;
- CreateSendConfig(num_video_streams, 0, num_flexfec_streams, send_transport);
+ size_t total_streams_used = 0;
+ size_t num_flexfec_streams = params_.video[0].flexfec ? 1 : 0;
+ CreateAudioAndFecSendConfigs(0, num_flexfec_streams, send_transport);
+ CreateMatchingAudioAndFecConfigs(recv_transport);
+ video_receive_configs_.clear();
+ video_send_configs_.clear();
+ video_encoders_.clear();
+ video_encoder_configs_.clear();
+ allocated_decoders_.clear();
+ bool decode_all_receive_streams = true;
+ size_t num_video_substreams = params_.ss[0].streams.size();
+ RTC_CHECK(num_video_streams_ > 0);
+ video_encoder_configs_.resize(num_video_streams_);
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ video_send_configs_.push_back(VideoSendStream::Config(send_transport));
+ video_encoders_.push_back(nullptr);
+ video_encoder_configs_.push_back(VideoEncoderConfig());
+ num_video_substreams = params_.ss[video_idx].streams.size();
+ RTC_CHECK_GT(num_video_substreams, 0);
+ CreateVideoSendConfig(&video_send_configs_[video_idx], num_video_substreams,
+ total_streams_used, send_transport);
- int payload_type;
- if (params_.video.codec == "H264") {
- video_encoder_ = H264Encoder::Create(cricket::VideoCodec("H264"));
- payload_type = kPayloadTypeH264;
- } else if (params_.video.codec == "VP8") {
- if (params_.screenshare.enabled && params_.ss.streams.size() > 1) {
- // Simulcast screenshare needs a simulcast encoder adapter to work, since
- // encoders usually can't natively do simulcast with different frame rates
- // for the different layers.
- video_encoder_.reset(
- new SimulcastEncoderAdapter(new InternalEncoderFactory()));
+ int payload_type;
+ if (params_.video[video_idx].codec == "H264") {
+ video_encoders_[video_idx] =
+ H264Encoder::Create(cricket::VideoCodec("H264"));
+ payload_type = kPayloadTypeH264;
+ } else if (params_.video[video_idx].codec == "VP8") {
+ if (params_.screenshare[video_idx].enabled &&
+ params_.ss[video_idx].streams.size() > 1) {
+ // Simulcast screenshare needs a simulcast encoder adapter to work,
+ // since encoders usually can't natively do simulcast with different
+ // frame rates for the different layers.
+ video_encoders_[video_idx].reset(
+ new SimulcastEncoderAdapter(new InternalEncoderFactory()));
+ } else {
+ video_encoders_[video_idx] = VP8Encoder::Create();
+ }
+ payload_type = kPayloadTypeVP8;
+ } else if (params_.video[video_idx].codec == "VP9") {
+ video_encoders_[video_idx] = VP9Encoder::Create();
+ payload_type = kPayloadTypeVP9;
} else {
- video_encoder_ = VP8Encoder::Create();
+ RTC_NOTREACHED() << "Codec not supported!";
+ return;
}
- payload_type = kPayloadTypeVP8;
- } else if (params_.video.codec == "VP9") {
- video_encoder_ = VP9Encoder::Create();
- payload_type = kPayloadTypeVP9;
- } else {
- RTC_NOTREACHED() << "Codec not supported!";
- return;
- }
- video_send_config_.encoder_settings.encoder = video_encoder_.get();
- video_send_config_.encoder_settings.payload_name = params_.video.codec;
- video_send_config_.encoder_settings.payload_type = payload_type;
- video_send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
- video_send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
- for (size_t i = 0; i < num_video_streams; ++i)
- video_send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]);
-
- video_send_config_.rtp.extensions.clear();
- if (params_.call.send_side_bwe) {
- video_send_config_.rtp.extensions.push_back(
- RtpExtension(RtpExtension::kTransportSequenceNumberUri,
- test::kTransportSequenceNumberExtensionId));
- } else {
- video_send_config_.rtp.extensions.push_back(RtpExtension(
- RtpExtension::kAbsSendTimeUri, test::kAbsSendTimeExtensionId));
- }
- video_send_config_.rtp.extensions.push_back(RtpExtension(
- RtpExtension::kVideoContentTypeUri, test::kVideoContentTypeExtensionId));
- video_send_config_.rtp.extensions.push_back(RtpExtension(
- RtpExtension::kVideoTimingUri, test::kVideoTimingExtensionId));
-
- video_encoder_config_.min_transmit_bitrate_bps =
- params_.video.min_transmit_bps;
-
- video_send_config_.suspend_below_min_bitrate =
- params_.video.suspend_below_min_bitrate;
-
- video_encoder_config_.number_of_streams = params_.ss.streams.size();
- video_encoder_config_.max_bitrate_bps = 0;
- for (size_t i = 0; i < params_.ss.streams.size(); ++i) {
- video_encoder_config_.max_bitrate_bps +=
- params_.ss.streams[i].max_bitrate_bps;
- }
- 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;
-
- CreateMatchingReceiveConfigs(recv_transport);
-
- const bool decode_all_receive_streams =
- params_.ss.selected_stream == params_.ss.streams.size();
-
- for (size_t i = 0; i < num_video_streams; ++i) {
- video_receive_configs_[i].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
- video_receive_configs_[i].rtp.rtx_ssrc = kSendRtxSsrcs[i];
- video_receive_configs_[i]
- .rtp.rtx_associated_payload_types[kSendRtxPayloadType] = payload_type;
- video_receive_configs_[i].rtp.transport_cc = params_.call.send_side_bwe;
- video_receive_configs_[i].rtp.remb = !params_.call.send_side_bwe;
- // Enable RTT calculation so NTP time estimator will work.
- video_receive_configs_[i].rtp.rtcp_xr.receiver_reference_time_report = true;
- // Force fake decoders on non-selected simulcast streams.
- if (!decode_all_receive_streams && i != params_.ss.selected_stream) {
- VideoReceiveStream::Decoder decoder;
- decoder.decoder = new test::FakeDecoder();
- decoder.payload_type = video_send_config_.encoder_settings.payload_type;
- decoder.payload_name = video_send_config_.encoder_settings.payload_name;
- video_receive_configs_[i].decoders.clear();
- allocated_decoders_.emplace_back(decoder.decoder);
- video_receive_configs_[i].decoders.push_back(decoder);
+ video_send_configs_[video_idx].encoder_settings.encoder =
+ video_encoders_[video_idx].get();
+ video_send_configs_[video_idx].encoder_settings.payload_name =
+ params_.video[video_idx].codec;
+ video_send_configs_[video_idx].encoder_settings.payload_type = payload_type;
+ video_send_configs_[video_idx].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+ video_send_configs_[video_idx].rtp.rtx.payload_type = kSendRtxPayloadType;
+ for (size_t i = 0; i < num_video_substreams; ++i) {
+ video_send_configs_[video_idx].rtp.rtx.ssrcs.push_back(
+ kSendRtxSsrcs[i + total_streams_used]);
}
+ video_send_configs_[video_idx].rtp.extensions.clear();
+ if (params_.call.send_side_bwe) {
+ video_send_configs_[video_idx].rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kTransportSequenceNumberUri,
+ test::kTransportSequenceNumberExtensionId));
+ } else {
+ video_send_configs_[video_idx].rtp.extensions.push_back(RtpExtension(
+ RtpExtension::kAbsSendTimeUri, test::kAbsSendTimeExtensionId));
+ }
+ video_send_configs_[video_idx].rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kVideoContentTypeUri,
+ test::kVideoContentTypeExtensionId));
+ video_send_configs_[video_idx].rtp.extensions.push_back(RtpExtension(
+ RtpExtension::kVideoTimingUri, test::kVideoTimingExtensionId));
+
+ video_encoder_configs_[video_idx].min_transmit_bitrate_bps =
+ params_.video[video_idx].min_transmit_bps;
+
+ video_send_configs_[video_idx].suspend_below_min_bitrate =
+ params_.video[video_idx].suspend_below_min_bitrate;
+
+ video_encoder_configs_[video_idx].number_of_streams =
+ params_.ss[video_idx].streams.size();
+ video_encoder_configs_[video_idx].max_bitrate_bps = 0;
+ for (size_t i = 0; i < params_.ss[video_idx].streams.size(); ++i) {
+ video_encoder_configs_[video_idx].max_bitrate_bps +=
+ params_.ss[video_idx].streams[i].max_bitrate_bps;
+ }
+ if (params_.ss[video_idx].infer_streams) {
+ video_encoder_configs_[video_idx].video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ params_.video[video_idx].codec,
+ params_.ss[video_idx].streams[0].max_qp,
+ params_.video[video_idx].fps,
+ params_.screenshare[video_idx].enabled, true);
+ } else {
+ video_encoder_configs_[video_idx].video_stream_factory =
+ new rtc::RefCountedObject<VideoStreamFactory>(
+ params_.ss[video_idx].streams);
+ }
+
+ video_encoder_configs_[video_idx].spatial_layers =
+ params_.ss[video_idx].spatial_layers;
+
+ std::vector<VideoReceiveStream::Config> new_receive_configs =
+ CreateMatchingVideoReceiveConfigs(video_send_configs_[video_idx],
+ recv_transport);
+
+ decode_all_receive_streams = params_.ss[video_idx].selected_stream ==
+ params_.ss[video_idx].streams.size();
+
+ for (size_t i = 0; i < num_video_substreams; ++i) {
+ new_receive_configs[i].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+ new_receive_configs[i].rtp.rtx_ssrc =
+ kSendRtxSsrcs[i + total_streams_used];
+ new_receive_configs[i]
+ .rtp.rtx_associated_payload_types[kSendRtxPayloadType] = payload_type;
+ new_receive_configs[i].rtp.transport_cc = params_.call.send_side_bwe;
+ new_receive_configs[i].rtp.remb = !params_.call.send_side_bwe;
+ // Enable RTT calculation so NTP time estimator will work.
+ new_receive_configs[i].rtp.rtcp_xr.receiver_reference_time_report = true;
+ // Force fake decoders on non-selected simulcast streams.
+ if (!decode_all_receive_streams &&
+ i != params_.ss[video_idx].selected_stream) {
+ VideoReceiveStream::Decoder decoder;
+ decoder.decoder = new test::FakeDecoder();
+ decoder.payload_type =
+ video_send_configs_[video_idx].encoder_settings.payload_type;
+ decoder.payload_name =
+ video_send_configs_[video_idx].encoder_settings.payload_name;
+ new_receive_configs[i].decoders.clear();
+ allocated_decoders_.emplace_back(decoder.decoder);
+ new_receive_configs[i].decoders.push_back(decoder);
+ }
+ }
+
+ for (VideoReceiveStream::Config& config : new_receive_configs) {
+ video_receive_configs_.push_back(config.Copy());
+ }
+
+ if (params_.screenshare[video_idx].enabled) {
+ // Fill out codec settings.
+ video_encoder_configs_[video_idx].content_type =
+ VideoEncoderConfig::ContentType::kScreen;
+ degradation_preference_ =
+ VideoSendStream::DegradationPreference::kMaintainResolution;
+ if (params_.video[video_idx].codec == "VP8") {
+ VideoCodecVP8 vp8_settings = VideoEncoder::GetDefaultVp8Settings();
+ vp8_settings.denoisingOn = false;
+ vp8_settings.frameDroppingOn = false;
+ vp8_settings.numberOfTemporalLayers = static_cast<unsigned char>(
+ params_.video[video_idx].num_temporal_layers);
+ video_encoder_configs_[video_idx].encoder_specific_settings =
+ new rtc::RefCountedObject<
+ VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
+ } else if (params_.video[video_idx].codec == "VP9") {
+ VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
+ vp9_settings.denoisingOn = false;
+ vp9_settings.frameDroppingOn = false;
+ vp9_settings.numberOfTemporalLayers = static_cast<unsigned char>(
+ params_.video[video_idx].num_temporal_layers);
+ vp9_settings.numberOfSpatialLayers = static_cast<unsigned char>(
+ params_.ss[video_idx].num_spatial_layers);
+ video_encoder_configs_[video_idx].encoder_specific_settings =
+ new rtc::RefCountedObject<
+ VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
+ }
+ } else if (params_.ss[video_idx].num_spatial_layers > 1) {
+ // If SVC mode without screenshare, still need to set codec specifics.
+ RTC_CHECK(params_.video[video_idx].codec == "VP9");
+ VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
+ vp9_settings.numberOfTemporalLayers = static_cast<unsigned char>(
+ params_.video[video_idx].num_temporal_layers);
+ vp9_settings.numberOfSpatialLayers =
+ static_cast<unsigned char>(params_.ss[video_idx].num_spatial_layers);
+ video_encoder_configs_[video_idx].encoder_specific_settings =
+ new rtc::RefCountedObject<
+ VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
+ }
+ total_streams_used += num_video_substreams;
}
- if (params_.video.flexfec) {
+ // FEC supported only for single video stream mode yet.
+ if (params_.video[0].flexfec) {
// Override send config constructed by CreateSendConfig.
if (decode_all_receive_streams) {
- for (uint32_t media_ssrc : video_send_config_.rtp.ssrcs) {
- video_send_config_.rtp.flexfec.protected_media_ssrcs.push_back(
+ for (uint32_t media_ssrc : video_send_configs_[0].rtp.ssrcs) {
+ video_send_configs_[0].rtp.flexfec.protected_media_ssrcs.push_back(
media_ssrc);
}
} else {
- video_send_config_.rtp.flexfec.protected_media_ssrcs = {
- kVideoSendSsrcs[params_.ss.selected_stream]};
+ video_send_configs_[0].rtp.flexfec.protected_media_ssrcs = {
+ kVideoSendSsrcs[params_.ss[0].selected_stream]};
}
// The matching receive config is _not_ created by
@@ -1469,10 +1575,11 @@
// Set up the receive config manually instead.
FlexfecReceiveStream::Config flexfec_receive_config(recv_transport);
flexfec_receive_config.payload_type =
- video_send_config_.rtp.flexfec.payload_type;
- flexfec_receive_config.remote_ssrc = video_send_config_.rtp.flexfec.ssrc;
+ video_send_configs_[0].rtp.flexfec.payload_type;
+ flexfec_receive_config.remote_ssrc =
+ video_send_configs_[0].rtp.flexfec.ssrc;
flexfec_receive_config.protected_media_ssrcs =
- video_send_config_.rtp.flexfec.protected_media_ssrcs;
+ video_send_configs_[0].rtp.flexfec.protected_media_ssrcs;
flexfec_receive_config.local_ssrc = kReceiverLocalVideoSsrc;
flexfec_receive_config.transport_cc = params_.call.send_side_bwe;
if (params_.call.send_side_bwe) {
@@ -1484,37 +1591,38 @@
RtpExtension::kAbsSendTimeUri, test::kAbsSendTimeExtensionId));
}
flexfec_receive_configs_.push_back(flexfec_receive_config);
- if (num_video_streams > 0) {
+ if (num_video_substreams > 0) {
video_receive_configs_[0].rtp.protected_by_flexfec = true;
}
}
- if (params_.video.ulpfec) {
- video_send_config_.rtp.ulpfec.red_payload_type = kRedPayloadType;
- video_send_config_.rtp.ulpfec.ulpfec_payload_type = kUlpfecPayloadType;
- video_send_config_.rtp.ulpfec.red_rtx_payload_type = kRtxRedPayloadType;
+ if (params_.video[0].ulpfec) {
+ video_send_configs_[0].rtp.ulpfec.red_payload_type = kRedPayloadType;
+ video_send_configs_[0].rtp.ulpfec.ulpfec_payload_type = kUlpfecPayloadType;
+ video_send_configs_[0].rtp.ulpfec.red_rtx_payload_type = kRtxRedPayloadType;
if (decode_all_receive_streams) {
for (auto it = video_receive_configs_.begin();
it != video_receive_configs_.end(); ++it) {
it->rtp.red_payload_type =
- video_send_config_.rtp.ulpfec.red_payload_type;
+ video_send_configs_[0].rtp.ulpfec.red_payload_type;
it->rtp.ulpfec_payload_type =
- video_send_config_.rtp.ulpfec.ulpfec_payload_type;
- it->rtp.rtx_associated_payload_types[video_send_config_.rtp.ulpfec
- .red_rtx_payload_type] =
- video_send_config_.rtp.ulpfec.red_payload_type;
+ video_send_configs_[0].rtp.ulpfec.ulpfec_payload_type;
+ it->rtp.rtx_associated_payload_types
+ [video_send_configs_[0].rtp.ulpfec.red_rtx_payload_type] =
+ video_send_configs_[0].rtp.ulpfec.red_payload_type;
}
} else {
- video_receive_configs_[params_.ss.selected_stream].rtp.red_payload_type =
- video_send_config_.rtp.ulpfec.red_payload_type;
- video_receive_configs_[params_.ss.selected_stream]
+ video_receive_configs_[params_.ss[0].selected_stream]
+ .rtp.red_payload_type =
+ video_send_configs_[0].rtp.ulpfec.red_payload_type;
+ video_receive_configs_[params_.ss[0].selected_stream]
.rtp.ulpfec_payload_type =
- video_send_config_.rtp.ulpfec.ulpfec_payload_type;
- video_receive_configs_[params_.ss.selected_stream]
- .rtp.rtx_associated_payload_types[video_send_config_.rtp.ulpfec
- .red_rtx_payload_type] =
- video_send_config_.rtp.ulpfec.red_payload_type;
+ video_send_configs_[0].rtp.ulpfec.ulpfec_payload_type;
+ video_receive_configs_[params_.ss[0].selected_stream]
+ .rtp.rtx_associated_payload_types
+ [video_send_configs_[0].rtp.ulpfec.red_rtx_payload_type] =
+ video_send_configs_[0].rtp.ulpfec.red_payload_type;
}
}
}
@@ -1530,7 +1638,8 @@
thumbnail_send_config.rtp.ssrcs.push_back(kThumbnailSendSsrcStart + i);
thumbnail_send_config.encoder_settings.encoder =
thumbnail_encoders_.back().get();
- thumbnail_send_config.encoder_settings.payload_name = params_.video.codec;
+ thumbnail_send_config.encoder_settings.payload_name =
+ params_.video[0].codec;
thumbnail_send_config.encoder_settings.payload_type = kPayloadTypeVP8;
thumbnail_send_config.rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
thumbnail_send_config.rtp.rtx.payload_type = kSendRtxPayloadType;
@@ -1548,19 +1657,19 @@
VideoEncoderConfig thumbnail_encoder_config;
thumbnail_encoder_config.min_transmit_bitrate_bps = 7500;
thumbnail_send_config.suspend_below_min_bitrate =
- params_.video.suspend_below_min_bitrate;
+ params_.video[0].suspend_below_min_bitrate;
thumbnail_encoder_config.number_of_streams = 1;
thumbnail_encoder_config.max_bitrate_bps = 50000;
- if (params_.ss.infer_streams) {
+ if (params_.ss[0].infer_streams) {
thumbnail_encoder_config.video_stream_factory =
- new rtc::RefCountedObject<VideoStreamFactory>(params_.ss.streams);
+ new rtc::RefCountedObject<VideoStreamFactory>(params_.ss[0].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);
+ params_.video[0].codec, params_.ss[0].streams[0].max_qp,
+ params_.video[0].fps, params_.screenshare[0].enabled, true);
}
- thumbnail_encoder_config.spatial_layers = params_.ss.spatial_layers;
+ thumbnail_encoder_config.spatial_layers = params_.ss[0].spatial_layers;
VideoReceiveStream::Config thumbnail_receive_config(send_transport);
thumbnail_receive_config.rtp.remb = false;
@@ -1601,12 +1710,14 @@
}
void VideoQualityTest::DestroyThumbnailStreams() {
- for (VideoSendStream* thumbnail_send_stream : thumbnail_send_streams_)
+ for (VideoSendStream* thumbnail_send_stream : thumbnail_send_streams_) {
receiver_call_->DestroyVideoSendStream(thumbnail_send_stream);
+ }
thumbnail_send_streams_.clear();
for (VideoReceiveStream* thumbnail_receive_stream :
- thumbnail_receive_streams_)
+ thumbnail_receive_streams_) {
sender_call_->DestroyVideoReceiveStream(thumbnail_receive_stream);
+ }
thumbnail_send_streams_.clear();
thumbnail_receive_streams_.clear();
for (std::unique_ptr<test::VideoCapturer>& video_caputurer :
@@ -1615,85 +1726,6 @@
}
}
-void VideoQualityTest::SetupScreenshareOrSVC() {
- if (params_.screenshare.enabled) {
- // Fill out codec settings.
- video_encoder_config_.content_type =
- VideoEncoderConfig::ContentType::kScreen;
- degradation_preference_ =
- VideoSendStream::DegradationPreference::kMaintainResolution;
- if (params_.video.codec == "VP8") {
- VideoCodecVP8 vp8_settings = VideoEncoder::GetDefaultVp8Settings();
- vp8_settings.denoisingOn = false;
- vp8_settings.frameDroppingOn = false;
- vp8_settings.numberOfTemporalLayers =
- static_cast<unsigned char>(params_.video.num_temporal_layers);
- video_encoder_config_.encoder_specific_settings =
- new rtc::RefCountedObject<
- VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
- } else if (params_.video.codec == "VP9") {
- VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
- vp9_settings.denoisingOn = false;
- vp9_settings.frameDroppingOn = false;
- vp9_settings.numberOfTemporalLayers =
- static_cast<unsigned char>(params_.video.num_temporal_layers);
- vp9_settings.numberOfSpatialLayers =
- static_cast<unsigned char>(params_.ss.num_spatial_layers);
- video_encoder_config_.encoder_specific_settings =
- new rtc::RefCountedObject<
- VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
- }
- // Setup frame generator.
- const size_t kWidth = 1850;
- const size_t kHeight = 1110;
- if (params_.screenshare.generate_slides) {
- frame_generator_ = test::FrameGenerator::CreateSlideGenerator(
- kWidth, kHeight,
- params_.screenshare.slide_change_interval * params_.video.fps);
- } else {
- std::vector<std::string> slides = params_.screenshare.slides;
- if (slides.size() == 0) {
- slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
- slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
- slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
- slides.push_back(
- test::ResourcePath("difficult_photo_1850_1110", "yuv"));
- }
- if (params_.screenshare.scroll_duration == 0) {
- // Cycle image every slide_change_interval seconds.
- frame_generator_ = test::FrameGenerator::CreateFromYuvFile(
- slides, kWidth, kHeight,
- params_.screenshare.slide_change_interval * params_.video.fps);
- } else {
- RTC_CHECK_LE(params_.video.width, kWidth);
- RTC_CHECK_LE(params_.video.height, kHeight);
- RTC_CHECK_GT(params_.screenshare.slide_change_interval, 0);
- const int kPauseDurationMs =
- (params_.screenshare.slide_change_interval -
- params_.screenshare.scroll_duration) *
- 1000;
- RTC_CHECK_LE(params_.screenshare.scroll_duration,
- params_.screenshare.slide_change_interval);
-
- frame_generator_ =
- test::FrameGenerator::CreateScrollingInputFromYuvFiles(
- clock_, slides, kWidth, kHeight, params_.video.width,
- params_.video.height,
- params_.screenshare.scroll_duration * 1000, kPauseDurationMs);
- }
- }
- } else if (params_.ss.num_spatial_layers > 1) { // For non-screenshare case.
- RTC_CHECK(params_.video.codec == "VP9");
- VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
- vp9_settings.numberOfTemporalLayers =
- static_cast<unsigned char>(params_.video.num_temporal_layers);
- vp9_settings.numberOfSpatialLayers =
- static_cast<unsigned char>(params_.ss.num_spatial_layers);
- video_encoder_config_.encoder_specific_settings = new rtc::RefCountedObject<
- VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
- }
-}
-
void VideoQualityTest::SetupThumbnailCapturers(size_t num_thumbnail_streams) {
VideoStream thumbnail = DefaultThumbnailStream();
for (size_t i = 0; i < num_thumbnail_streams; ++i) {
@@ -1704,47 +1736,106 @@
}
}
-void VideoQualityTest::CreateCapturer() {
- if (params_.screenshare.enabled) {
- test::FrameGeneratorCapturer* frame_generator_capturer =
- new test::FrameGeneratorCapturer(clock_, std::move(frame_generator_),
- params_.video.fps);
- EXPECT_TRUE(frame_generator_capturer->Init());
- video_capturer_.reset(frame_generator_capturer);
+std::unique_ptr<test::FrameGenerator> VideoQualityTest::CreateFrameGenerator(
+ size_t video_idx) {
+ // Setup frame generator.
+ const size_t kWidth = 1850;
+ const size_t kHeight = 1110;
+ std::unique_ptr<test::FrameGenerator> frame_generator;
+ if (params_.screenshare[video_idx].generate_slides) {
+ frame_generator = test::FrameGenerator::CreateSlideGenerator(
+ kWidth, kHeight,
+ params_.screenshare[video_idx].slide_change_interval *
+ params_.video[video_idx].fps);
} else {
- 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));
- if (!video_capturer_) {
- // Failed to get actual camera, use chroma generator as backup.
- video_capturer_.reset(test::FrameGeneratorCapturer::Create(
- static_cast<int>(params_.video.width),
- static_cast<int>(params_.video.height), params_.video.fps, clock_));
- }
+ std::vector<std::string> slides = params_.screenshare[video_idx].slides;
+ if (slides.size() == 0) {
+ slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
+ slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
+ slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
+ slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
+ }
+ if (params_.screenshare[video_idx].scroll_duration == 0) {
+ // Cycle image every slide_change_interval seconds.
+ frame_generator = test::FrameGenerator::CreateFromYuvFile(
+ slides, kWidth, kHeight,
+ params_.screenshare[video_idx].slide_change_interval *
+ params_.video[video_idx].fps);
} else {
- video_capturer_.reset(test::FrameGeneratorCapturer::CreateFromYuvFile(
- test::ResourcePath(params_.video.clip_name, "yuv"),
- params_.video.width, params_.video.height, params_.video.fps,
- clock_));
- ASSERT_TRUE(video_capturer_) << "Could not create capturer for "
- << params_.video.clip_name
- << ".yuv. Is this resource file present?";
+ RTC_CHECK_LE(params_.video[video_idx].width, kWidth);
+ RTC_CHECK_LE(params_.video[video_idx].height, kHeight);
+ RTC_CHECK_GT(params_.screenshare[video_idx].slide_change_interval, 0);
+ const int kPauseDurationMs =
+ (params_.screenshare[video_idx].slide_change_interval -
+ params_.screenshare[video_idx].scroll_duration) *
+ 1000;
+ RTC_CHECK_LE(params_.screenshare[video_idx].scroll_duration,
+ params_.screenshare[video_idx].slide_change_interval);
+
+ frame_generator = test::FrameGenerator::CreateScrollingInputFromYuvFiles(
+ clock_, slides, kWidth, kHeight, params_.video[video_idx].width,
+ params_.video[video_idx].height,
+ params_.screenshare[video_idx].scroll_duration * 1000,
+ kPauseDurationMs);
}
}
- RTC_DCHECK(video_capturer_.get());
+ return frame_generator;
+}
+
+void VideoQualityTest::CreateCapturers() {
+ video_capturers_.resize(num_video_streams_);
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ if (params_.screenshare[video_idx].enabled) {
+ std::unique_ptr<test::FrameGenerator> frame_generator =
+ CreateFrameGenerator(video_idx);
+ test::FrameGeneratorCapturer* frame_generator_capturer =
+ new test::FrameGeneratorCapturer(clock_, std::move(frame_generator),
+ params_.video[video_idx].fps);
+ EXPECT_TRUE(frame_generator_capturer->Init());
+ video_capturers_[video_idx].reset(frame_generator_capturer);
+ } else {
+ if (params_.video[video_idx].clip_name == "Generator") {
+ video_capturers_[video_idx].reset(test::FrameGeneratorCapturer::Create(
+ static_cast<int>(params_.video[video_idx].width),
+ static_cast<int>(params_.video[video_idx].height),
+ params_.video[video_idx].fps, clock_));
+ } else if (params_.video[video_idx].clip_name.empty()) {
+ video_capturers_[video_idx].reset(test::VcmCapturer::Create(
+ params_.video[video_idx].width, params_.video[video_idx].height,
+ params_.video[video_idx].fps,
+ params_.video[video_idx].capture_device_index));
+ if (!video_capturers_[video_idx]) {
+ // Failed to get actual camera, use chroma generator as backup.
+ video_capturers_[video_idx].reset(
+ test::FrameGeneratorCapturer::Create(
+ static_cast<int>(params_.video[video_idx].width),
+ static_cast<int>(params_.video[video_idx].height),
+ params_.video[video_idx].fps, clock_));
+ }
+ } else {
+ video_capturers_[video_idx].reset(
+ test::FrameGeneratorCapturer::CreateFromYuvFile(
+ test::ResourcePath(params_.video[video_idx].clip_name, "yuv"),
+ params_.video[video_idx].width, params_.video[video_idx].height,
+ params_.video[video_idx].fps, clock_));
+ ASSERT_TRUE(video_capturers_[video_idx])
+ << "Could not create capturer for "
+ << params_.video[video_idx].clip_name
+ << ".yuv. Is this resource file present?";
+ }
+ }
+ RTC_DCHECK(video_capturers_[video_idx].get());
+ }
}
std::unique_ptr<test::LayerFilteringTransport>
VideoQualityTest::CreateSendTransport() {
return rtc::MakeUnique<test::LayerFilteringTransport>(
&task_queue_, params_.pipe, sender_call_.get(), kPayloadTypeVP8,
- kPayloadTypeVP9, params_.video.selected_tl, params_.ss.selected_sl,
- payload_type_map_);
+ kPayloadTypeVP9, params_.video[0].selected_tl, params_.ss[0].selected_sl,
+ payload_type_map_, kVideoSendSsrcs[0],
+ static_cast<uint32_t>(kVideoSendSsrcs[0] + params_.ss[0].streams.size() -
+ 1));
}
std::unique_ptr<test::DirectTransport>
@@ -1753,7 +1844,31 @@
&task_queue_, params_.pipe, receiver_call_.get(), payload_type_map_);
}
+void VideoQualityTest::CreateVideoStreams() {
+ RTC_DCHECK(video_send_streams_.empty());
+ RTC_DCHECK(video_receive_streams_.empty());
+ RTC_DCHECK_EQ(video_send_configs_.size(), num_video_streams_);
+ for (size_t i = 0; i < video_send_configs_.size(); ++i) {
+ video_send_streams_.push_back(sender_call_->CreateVideoSendStream(
+ video_send_configs_[i].Copy(), video_encoder_configs_[i].Copy()));
+ }
+ for (size_t i = 0; i < video_receive_configs_.size(); ++i) {
+ video_receive_streams_.push_back(receiver_call_->CreateVideoReceiveStream(
+ video_receive_configs_[i].Copy()));
+ }
+
+ AssociateFlexfecStreamsWithVideoStreams();
+}
+
+void VideoQualityTest::DestroyStreams() {
+ CallTest::DestroyStreams();
+
+ for (VideoSendStream* video_send_stream : video_send_streams_)
+ sender_call_->DestroyVideoSendStream(video_send_stream);
+}
+
void VideoQualityTest::RunWithAnalyzer(const Params& params) {
+ num_video_streams_ = params.call.dual_video ? 2 : 1;
std::unique_ptr<test::LayerFilteringTransport> send_transport;
std::unique_ptr<test::DirectTransport> recv_transport;
FILE* graph_data_output_file = nullptr;
@@ -1819,13 +1934,13 @@
params_.analyzer.avg_psnr_threshold, params_.analyzer.avg_ssim_threshold,
is_quick_test_enabled
? kFramesSentInQuickTest
- : params_.analyzer.test_durations_secs * params_.video.fps,
+ : params_.analyzer.test_durations_secs * params_.video[0].fps,
graph_data_output_file, graph_title,
- 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, clock_,
- params_.logging.rtp_dump_name);
+ kVideoSendSsrcs[params_.ss[0].selected_stream],
+ kSendRtxSsrcs[params_.ss[0].selected_stream],
+ static_cast<size_t>(params_.ss[0].selected_stream),
+ params.ss[0].selected_sl, params_.video[0].selected_tl,
+ is_quick_test_enabled, clock_, params_.logging.rtp_dump_name);
task_queue_.SendTask([&]() {
analyzer->SetCall(sender_call_.get());
@@ -1835,36 +1950,41 @@
SetupVideo(analyzer.get(), recv_transport.get());
SetupThumbnails(analyzer.get(), recv_transport.get());
- video_receive_configs_[params_.ss.selected_stream].renderer =
+ video_receive_configs_[params_.ss[0].selected_stream].renderer =
analyzer.get();
- video_send_config_.pre_encode_callback = analyzer->pre_encode_proxy();
- RTC_DCHECK(!video_send_config_.post_encode_callback);
- video_send_config_.post_encode_callback = analyzer->encode_timing_proxy();
-
- SetupScreenshareOrSVC();
+ video_send_configs_[0].pre_encode_callback = analyzer->pre_encode_proxy();
+ RTC_DCHECK(!video_send_configs_[0].post_encode_callback);
+ video_send_configs_[0].post_encode_callback =
+ analyzer->encode_timing_proxy();
CreateFlexfecStreams();
CreateVideoStreams();
- analyzer->SetSendStream(video_send_stream_);
+ analyzer->SetSendStream(video_send_streams_[0]);
if (video_receive_streams_.size() == 1)
analyzer->SetReceiveStream(video_receive_streams_[0]);
- video_send_stream_->SetSource(analyzer->OutputInterface(),
- degradation_preference_);
-
+ video_send_streams_[0]->SetSource(analyzer->OutputInterface(),
+ degradation_preference_);
SetupThumbnailCapturers(params_.call.num_thumbnails);
for (size_t i = 0; i < thumbnail_send_streams_.size(); ++i) {
thumbnail_send_streams_[i]->SetSource(thumbnail_capturers_[i].get(),
degradation_preference_);
}
- CreateCapturer();
+ CreateCapturers();
- analyzer->SetSource(video_capturer_.get(), params_.ss.infer_streams);
+ analyzer->SetSource(video_capturers_[0].get(), params_.ss[0].infer_streams);
- StartEncodedFrameLogs(video_send_stream_);
- StartEncodedFrameLogs(video_receive_streams_[params_.ss.selected_stream]);
- video_send_stream_->Start();
+ for (size_t video_idx = 1; video_idx < num_video_streams_; ++video_idx) {
+ video_send_streams_[video_idx]->SetSource(
+ video_capturers_[video_idx].get(), degradation_preference_);
+ }
+
+ StartEncodedFrameLogs(video_send_streams_[0]);
+ StartEncodedFrameLogs(
+ video_receive_streams_[params_.ss[0].selected_stream]);
+ for (VideoSendStream* video_send_stream : video_send_streams_)
+ video_send_stream->Start();
for (VideoSendStream* thumbnail_send_stream : thumbnail_send_streams_)
thumbnail_send_stream->Start();
for (VideoReceiveStream* receive_stream : video_receive_streams_)
@@ -1875,7 +1995,9 @@
analyzer->StartMeasuringCpuProcessTime();
- video_capturer_->Start();
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ video_capturers_[video_idx]->Start();
+ }
for (std::unique_ptr<test::VideoCapturer>& video_caputurer :
thumbnail_capturers_) {
video_caputurer->Start();
@@ -1890,7 +2012,9 @@
for (std::unique_ptr<test::VideoCapturer>& video_caputurer :
thumbnail_capturers_)
video_caputurer->Stop();
- video_capturer_->Stop();
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ video_capturers_[video_idx]->Stop();
+ }
for (VideoReceiveStream* thumbnail_receive_stream :
thumbnail_receive_streams_)
thumbnail_receive_stream->Stop();
@@ -1898,7 +2022,8 @@
receive_stream->Stop();
for (VideoSendStream* thumbnail_send_stream : thumbnail_send_streams_)
thumbnail_send_stream->Stop();
- video_send_stream_->Stop();
+ for (VideoSendStream* video_send_stream : video_send_streams_)
+ video_send_stream->Stop();
DestroyStreams();
DestroyThumbnailStreams();
@@ -1906,7 +2031,7 @@
if (graph_data_output_file)
fclose(graph_data_output_file);
- video_capturer_.reset();
+ video_capturers_.clear();
send_transport.reset();
recv_transport.reset();
@@ -1950,7 +2075,7 @@
audio_config.rtp.extensions = audio_send_config_.rtp.extensions;
audio_config.decoder_factory = decoder_factory_;
audio_config.decoder_map = {{kAudioSendPayloadType, {"OPUS", 48000, 2}}};
- if (params_.video.enabled && params_.audio.sync_video)
+ if (params_.video[0].enabled && params_.audio.sync_video)
audio_config.sync_group = kSyncGroup;
*audio_receive_stream =
@@ -1958,6 +2083,7 @@
}
void VideoQualityTest::RunWithRenderers(const Params& params) {
+ num_video_streams_ = params.call.dual_video ? 2 : 1;
std::unique_ptr<test::LayerFilteringTransport> send_transport;
std::unique_ptr<test::DirectTransport> recv_transport;
::VoiceEngineState voe;
@@ -1996,13 +2122,9 @@
// TODO(minyue): consider if this is a good transport even for audio only
// calls.
- send_transport = rtc::MakeUnique<test::LayerFilteringTransport>(
- &task_queue_, params.pipe, sender_call_.get(), kPayloadTypeVP8,
- kPayloadTypeVP9, params.video.selected_tl, params_.ss.selected_sl,
- payload_type_map_);
+ send_transport = CreateSendTransport();
- recv_transport = rtc::MakeUnique<test::DirectTransport>(
- &task_queue_, params_.pipe, receiver_call_.get(), payload_type_map_);
+ recv_transport = CreateReceiveTransport();
// TODO(ivica): Use two calls to be able to merge with RunWithAnalyzer or at
// least share as much code as possible. That way this test would also match
@@ -2010,53 +2132,56 @@
send_transport->SetReceiver(receiver_call_->Receiver());
recv_transport->SetReceiver(sender_call_->Receiver());
- if (params_.video.enabled) {
+ if (params_.video[0].enabled) {
// Create video renderers.
local_preview.reset(test::VideoRenderer::Create(
- "Local Preview", params_.video.width, params_.video.height));
-
- const size_t selected_stream_id = params_.ss.selected_stream;
- const size_t num_streams = params_.ss.streams.size();
-
- if (selected_stream_id == num_streams) {
- for (size_t stream_id = 0; stream_id < num_streams; ++stream_id) {
- std::ostringstream oss;
- oss << "Loopback Video - Stream #" << static_cast<int>(stream_id);
- loopback_renderers.emplace_back(test::VideoRenderer::Create(
- oss.str().c_str(), params_.ss.streams[stream_id].width,
- params_.ss.streams[stream_id].height));
- }
- } else {
- loopback_renderers.emplace_back(test::VideoRenderer::Create(
- "Loopback Video", params_.ss.streams[selected_stream_id].width,
- params_.ss.streams[selected_stream_id].height));
- }
+ "Local Preview", params_.video[0].width, params_.video[0].height));
SetupVideo(send_transport.get(), recv_transport.get());
+ video_send_configs_[0].pre_encode_callback = local_preview.get();
- video_send_config_.pre_encode_callback = local_preview.get();
- if (selected_stream_id == num_streams) {
- for (size_t stream_id = 0; stream_id < num_streams; ++stream_id) {
- video_receive_configs_[stream_id].renderer =
- loopback_renderers[stream_id].get();
+ size_t num_streams_processed = 0;
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ const size_t selected_stream_id = params_.ss[video_idx].selected_stream;
+ const size_t num_streams = params_.ss[video_idx].streams.size();
+ if (selected_stream_id == num_streams) {
+ for (size_t stream_id = 0; stream_id < num_streams; ++stream_id) {
+ std::ostringstream oss;
+ oss << "Loopback Video #" << video_idx << " - Stream #"
+ << static_cast<int>(stream_id);
+ loopback_renderers.emplace_back(test::VideoRenderer::Create(
+ oss.str().c_str(),
+ params_.ss[video_idx].streams[stream_id].width,
+ params_.ss[video_idx].streams[stream_id].height));
+ video_receive_configs_[stream_id + num_streams_processed].renderer =
+ loopback_renderers.back().get();
+ if (params_.audio.enabled && params_.audio.sync_video)
+ video_receive_configs_[stream_id + num_streams_processed]
+ .sync_group = kSyncGroup;
+ }
+ } else {
+ std::ostringstream oss;
+ oss << "Loopback Video #" << video_idx;
+ loopback_renderers.emplace_back(test::VideoRenderer::Create(
+ oss.str().c_str(),
+ params_.ss[video_idx].streams[selected_stream_id].width,
+ params_.ss[video_idx].streams[selected_stream_id].height));
+ video_receive_configs_[selected_stream_id + num_streams_processed]
+ .renderer = loopback_renderers.back().get();
if (params_.audio.enabled && params_.audio.sync_video)
- video_receive_configs_[stream_id].sync_group = kSyncGroup;
+ video_receive_configs_[num_streams_processed + selected_stream_id]
+ .sync_group = kSyncGroup;
}
- } else {
- video_receive_configs_[selected_stream_id].renderer =
- loopback_renderers.back().get();
- if (params_.audio.enabled && params_.audio.sync_video)
- video_receive_configs_[selected_stream_id].sync_group = kSyncGroup;
+ num_streams_processed += num_streams;
}
-
- SetupScreenshareOrSVC();
-
CreateFlexfecStreams();
CreateVideoStreams();
- CreateCapturer();
- video_send_stream_->SetSource(video_capturer_.get(),
- degradation_preference_);
+ CreateCapturers();
+ for (size_t video_idx = 0; video_idx < num_video_streams_; ++video_idx) {
+ video_send_streams_[video_idx]->SetSource(
+ video_capturers_[video_idx].get(), degradation_preference_);
+ }
}
if (params_.audio.enabled) {
@@ -2066,15 +2191,16 @@
for (VideoReceiveStream* receive_stream : video_receive_streams_)
StartEncodedFrameLogs(receive_stream);
- StartEncodedFrameLogs(video_send_stream_);
+ StartEncodedFrameLogs(video_send_streams_[0]);
// Start sending and receiving video.
- if (params_.video.enabled) {
+ if (params_.video[0].enabled) {
for (VideoReceiveStream* video_receive_stream : video_receive_streams_)
video_receive_stream->Start();
-
- video_send_stream_->Start();
- video_capturer_->Start();
+ for (VideoSendStream* video_send_stream : video_send_streams_)
+ video_send_stream->Start();
+ for (auto& video_capturer : video_capturers_)
+ video_capturer->Start();
}
if (params_.audio.enabled) {
@@ -2095,30 +2221,18 @@
// Stop receiving audio.
audio_receive_stream->Stop();
- sender_call_->DestroyAudioSendStream(audio_send_stream_);
- receiver_call_->DestroyAudioReceiveStream(audio_receive_stream);
}
// Stop receiving and sending video.
- if (params_.video.enabled) {
- video_capturer_->Stop();
- video_send_stream_->Stop();
- for (FlexfecReceiveStream* flexfec_receive_stream :
- flexfec_receive_streams_) {
- for (VideoReceiveStream* video_receive_stream :
- video_receive_streams_) {
- video_receive_stream->RemoveSecondarySink(flexfec_receive_stream);
- }
- receiver_call_->DestroyFlexfecReceiveStream(flexfec_receive_stream);
- }
- for (VideoReceiveStream* receive_stream : video_receive_streams_) {
- receive_stream->Stop();
- receiver_call_->DestroyVideoReceiveStream(receive_stream);
- }
- sender_call_->DestroyVideoSendStream(video_send_stream_);
+ if (params_.video[0].enabled) {
+ for (auto& video_capturer : video_capturers_)
+ video_capturer->Stop();
+ for (VideoSendStream* video_send_stream : video_send_streams_)
+ video_send_stream->Stop();
+ DestroyStreams();
}
- video_capturer_.reset();
+ video_capturers_.clear();
send_transport.reset();
recv_transport.reset();