1 /*
2 * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "test/scenario/video_stream.h"
11
12 #include <algorithm>
13 #include <memory>
14 #include <utility>
15
16 #include "absl/strings/match.h"
17 #include "api/test/create_frame_generator.h"
18 #include "api/test/frame_generator_interface.h"
19 #include "api/test/video/function_video_encoder_factory.h"
20 #include "api/video/builtin_video_bitrate_allocator_factory.h"
21 #include "media/base/media_constants.h"
22 #include "media/engine/internal_decoder_factory.h"
23 #include "media/engine/internal_encoder_factory.h"
24 #include "media/engine/webrtc_video_engine.h"
25 #include "test/call_test.h"
26 #include "test/fake_encoder.h"
27 #include "test/scenario/hardware_codecs.h"
28 #include "test/testsupport/file_utils.h"
29
30 namespace webrtc {
31 namespace test {
32 namespace {
33 enum : int { // The first valid value is 1.
34 kTransportSequenceNumberExtensionId = 1,
35 kAbsSendTimeExtensionId,
36 kVideoContentTypeExtensionId,
37 kVideoRotationRtpExtensionId,
38 };
39
40 constexpr int kDefaultMaxQp = cricket::WebRtcVideoChannel::kDefaultQpMax;
CodecTypeToPayloadType(VideoCodecType codec_type)41 uint8_t CodecTypeToPayloadType(VideoCodecType codec_type) {
42 switch (codec_type) {
43 case VideoCodecType::kVideoCodecGeneric:
44 return CallTest::kFakeVideoSendPayloadType;
45 case VideoCodecType::kVideoCodecVP8:
46 return CallTest::kPayloadTypeVP8;
47 case VideoCodecType::kVideoCodecVP9:
48 return CallTest::kPayloadTypeVP9;
49 case VideoCodecType::kVideoCodecH264:
50 return CallTest::kPayloadTypeH264;
51 default:
52 RTC_NOTREACHED();
53 }
54 return {};
55 }
CodecTypeToCodecName(VideoCodecType codec_type)56 std::string CodecTypeToCodecName(VideoCodecType codec_type) {
57 switch (codec_type) {
58 case VideoCodecType::kVideoCodecGeneric:
59 return "";
60 case VideoCodecType::kVideoCodecVP8:
61 return cricket::kVp8CodecName;
62 case VideoCodecType::kVideoCodecVP9:
63 return cricket::kVp9CodecName;
64 case VideoCodecType::kVideoCodecH264:
65 return cricket::kH264CodecName;
66 default:
67 RTC_NOTREACHED();
68 }
69 return {};
70 }
ConvertContentType(VideoStreamConfig::Encoder::ContentType content_type)71 VideoEncoderConfig::ContentType ConvertContentType(
72 VideoStreamConfig::Encoder::ContentType content_type) {
73 switch (content_type) {
74 case VideoStreamConfig::Encoder::ContentType::kVideo:
75 return VideoEncoderConfig::ContentType::kRealtimeVideo;
76 break;
77 case VideoStreamConfig::Encoder::ContentType::kScreen:
78 return VideoEncoderConfig::ContentType::kScreen;
79 }
80 }
ToInterLayerPredMode(VideoStreamConfig::Encoder::Layers::Prediction value)81 InterLayerPredMode ToInterLayerPredMode(
82 VideoStreamConfig::Encoder::Layers::Prediction value) {
83 using Pred = VideoStreamConfig::Encoder::Layers::Prediction;
84 switch (value) {
85 case Pred::kTemporalOnly:
86 return InterLayerPredMode::kOff;
87 case Pred::kSpatialOnKey:
88 return InterLayerPredMode::kOnKeyPic;
89 case Pred::kFull:
90 return InterLayerPredMode::kOn;
91 }
92 }
GetVideoRtpExtensions(const VideoStreamConfig config)93 std::vector<RtpExtension> GetVideoRtpExtensions(
94 const VideoStreamConfig config) {
95 std::vector<RtpExtension> res = {
96 RtpExtension(RtpExtension::kVideoContentTypeUri,
97 kVideoContentTypeExtensionId),
98 RtpExtension(RtpExtension::kVideoRotationUri,
99 kVideoRotationRtpExtensionId)};
100 if (config.stream.packet_feedback) {
101 res.push_back(RtpExtension(RtpExtension::kTransportSequenceNumberUri,
102 kTransportSequenceNumberExtensionId));
103 }
104 if (config.stream.abs_send_time) {
105 res.push_back(
106 RtpExtension(RtpExtension::kAbsSendTimeUri, kAbsSendTimeExtensionId));
107 }
108 return res;
109 }
110
TransformFilePath(std::string path)111 std::string TransformFilePath(std::string path) {
112 static const std::string resource_prefix = "res://";
113 int ext_pos = path.rfind(".");
114 if (ext_pos < 0) {
115 return test::ResourcePath(path, "yuv");
116 } else if (absl::StartsWith(path, resource_prefix)) {
117 std::string name = path.substr(resource_prefix.length(), ext_pos);
118 std::string ext = path.substr(ext_pos, path.size());
119 return test::ResourcePath(name, ext);
120 }
121 return path;
122 }
123
CreateVideoSendStreamConfig(VideoStreamConfig config,std::vector<uint32_t> ssrcs,std::vector<uint32_t> rtx_ssrcs,Transport * send_transport)124 VideoSendStream::Config CreateVideoSendStreamConfig(
125 VideoStreamConfig config,
126 std::vector<uint32_t> ssrcs,
127 std::vector<uint32_t> rtx_ssrcs,
128 Transport* send_transport) {
129 VideoSendStream::Config send_config(send_transport);
130 send_config.rtp.payload_name = CodecTypeToPayloadString(config.encoder.codec);
131 send_config.rtp.payload_type = CodecTypeToPayloadType(config.encoder.codec);
132 send_config.rtp.nack.rtp_history_ms =
133 config.stream.nack_history_time.ms<int>();
134
135 send_config.rtp.ssrcs = ssrcs;
136 send_config.rtp.extensions = GetVideoRtpExtensions(config);
137
138 if (config.stream.use_rtx) {
139 send_config.rtp.rtx.payload_type = CallTest::kSendRtxPayloadType;
140 send_config.rtp.rtx.ssrcs = rtx_ssrcs;
141 }
142 if (config.stream.use_flexfec) {
143 send_config.rtp.flexfec.payload_type = CallTest::kFlexfecPayloadType;
144 send_config.rtp.flexfec.ssrc = CallTest::kFlexfecSendSsrc;
145 send_config.rtp.flexfec.protected_media_ssrcs = ssrcs;
146 }
147 if (config.stream.use_ulpfec) {
148 send_config.rtp.ulpfec.red_payload_type = CallTest::kRedPayloadType;
149 send_config.rtp.ulpfec.ulpfec_payload_type = CallTest::kUlpfecPayloadType;
150 send_config.rtp.ulpfec.red_rtx_payload_type = CallTest::kRtxRedPayloadType;
151 }
152 return send_config;
153 }
154 rtc::scoped_refptr<VideoEncoderConfig::EncoderSpecificSettings>
CreateVp9SpecificSettings(VideoStreamConfig video_config)155 CreateVp9SpecificSettings(VideoStreamConfig video_config) {
156 constexpr auto kScreen = VideoStreamConfig::Encoder::ContentType::kScreen;
157 VideoStreamConfig::Encoder conf = video_config.encoder;
158 VideoCodecVP9 vp9 = VideoEncoder::GetDefaultVp9Settings();
159 vp9.frameDroppingOn = conf.frame_dropping;
160 vp9.keyFrameInterval = conf.key_frame_interval.value_or(0);
161 vp9.numberOfTemporalLayers = static_cast<uint8_t>(conf.layers.temporal);
162 vp9.numberOfSpatialLayers = static_cast<uint8_t>(conf.layers.spatial);
163 vp9.interLayerPred = ToInterLayerPredMode(conf.layers.prediction);
164
165 if (conf.content_type == kScreen &&
166 (video_config.source.framerate > 5 || conf.layers.spatial >= 3)) {
167 vp9.flexibleMode = true;
168 }
169
170 if (conf.content_type == kScreen ||
171 conf.layers.temporal * conf.layers.spatial) {
172 vp9.automaticResizeOn = false;
173 vp9.denoisingOn = false;
174 } else {
175 vp9.automaticResizeOn = conf.single.automatic_scaling;
176 vp9.denoisingOn = conf.single.denoising;
177 }
178 return new rtc::RefCountedObject<
179 VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9);
180 }
181
182 rtc::scoped_refptr<VideoEncoderConfig::EncoderSpecificSettings>
CreateVp8SpecificSettings(VideoStreamConfig config)183 CreateVp8SpecificSettings(VideoStreamConfig config) {
184 VideoCodecVP8 vp8_settings = VideoEncoder::GetDefaultVp8Settings();
185 vp8_settings.frameDroppingOn = config.encoder.frame_dropping;
186 vp8_settings.keyFrameInterval = config.encoder.key_frame_interval.value_or(0);
187 vp8_settings.numberOfTemporalLayers = config.encoder.layers.temporal;
188 if (config.encoder.layers.spatial * config.encoder.layers.temporal > 1) {
189 vp8_settings.automaticResizeOn = false;
190 vp8_settings.denoisingOn = false;
191 } else {
192 vp8_settings.automaticResizeOn = config.encoder.single.automatic_scaling;
193 vp8_settings.denoisingOn = config.encoder.single.denoising;
194 }
195 return new rtc::RefCountedObject<
196 VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
197 }
198
199 rtc::scoped_refptr<VideoEncoderConfig::EncoderSpecificSettings>
CreateH264SpecificSettings(VideoStreamConfig config)200 CreateH264SpecificSettings(VideoStreamConfig config) {
201 RTC_DCHECK_EQ(config.encoder.layers.temporal, 1);
202 RTC_DCHECK_EQ(config.encoder.layers.spatial, 1);
203
204 VideoCodecH264 h264_settings = VideoEncoder::GetDefaultH264Settings();
205 h264_settings.frameDroppingOn = config.encoder.frame_dropping;
206 h264_settings.keyFrameInterval =
207 config.encoder.key_frame_interval.value_or(0);
208 return new rtc::RefCountedObject<
209 VideoEncoderConfig::H264EncoderSpecificSettings>(h264_settings);
210 }
211
212 rtc::scoped_refptr<VideoEncoderConfig::EncoderSpecificSettings>
CreateEncoderSpecificSettings(VideoStreamConfig config)213 CreateEncoderSpecificSettings(VideoStreamConfig config) {
214 using Codec = VideoStreamConfig::Encoder::Codec;
215 switch (config.encoder.codec) {
216 case Codec::kVideoCodecH264:
217 return CreateH264SpecificSettings(config);
218 case Codec::kVideoCodecVP8:
219 return CreateVp8SpecificSettings(config);
220 case Codec::kVideoCodecVP9:
221 return CreateVp9SpecificSettings(config);
222 case Codec::kVideoCodecGeneric:
223 case Codec::kVideoCodecAV1:
224 return nullptr;
225 case Codec::kVideoCodecMultiplex:
226 RTC_NOTREACHED();
227 return nullptr;
228 }
229 }
230
CreateVideoEncoderConfig(VideoStreamConfig config)231 VideoEncoderConfig CreateVideoEncoderConfig(VideoStreamConfig config) {
232 VideoEncoderConfig encoder_config;
233 encoder_config.codec_type = config.encoder.codec;
234 encoder_config.content_type = ConvertContentType(config.encoder.content_type);
235 encoder_config.video_format =
236 SdpVideoFormat(CodecTypeToPayloadString(config.encoder.codec), {});
237
238 encoder_config.number_of_streams = 1;
239 if (config.encoder.codec == VideoStreamConfig::Encoder::Codec::kVideoCodecVP8)
240 encoder_config.number_of_streams =
241 static_cast<size_t>(config.encoder.layers.spatial);
242 encoder_config.simulcast_layers =
243 std::vector<VideoStream>(config.encoder.layers.spatial);
244 encoder_config.min_transmit_bitrate_bps = config.stream.pad_to_rate.bps();
245
246 std::string cricket_codec = CodecTypeToCodecName(config.encoder.codec);
247 if (!cricket_codec.empty()) {
248 bool screenshare = config.encoder.content_type ==
249 VideoStreamConfig::Encoder::ContentType::kScreen;
250 encoder_config.video_stream_factory =
251 new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
252 cricket_codec, kDefaultMaxQp, screenshare, screenshare);
253 } else {
254 encoder_config.video_stream_factory =
255 new rtc::RefCountedObject<DefaultVideoStreamFactory>();
256 }
257
258 // TODO(srte): Base this on encoder capabilities.
259 encoder_config.max_bitrate_bps =
260 config.encoder.max_data_rate.value_or(DataRate::KilobitsPerSec(10000))
261 .bps();
262
263 encoder_config.encoder_specific_settings =
264 CreateEncoderSpecificSettings(config);
265 if (config.encoder.max_framerate) {
266 for (auto& layer : encoder_config.simulcast_layers) {
267 layer.max_framerate = *config.encoder.max_framerate;
268 }
269 }
270
271 return encoder_config;
272 }
273
CreateImageSlideGenerator(Clock * clock,VideoStreamConfig::Source::Slides slides,int framerate)274 std::unique_ptr<FrameGeneratorInterface> CreateImageSlideGenerator(
275 Clock* clock,
276 VideoStreamConfig::Source::Slides slides,
277 int framerate) {
278 std::vector<std::string> paths = slides.images.paths;
279 for (std::string& path : paths)
280 path = TransformFilePath(path);
281 if (slides.images.crop.width || slides.images.crop.height) {
282 TimeDelta pause_duration =
283 slides.change_interval - slides.images.crop.scroll_duration;
284 RTC_CHECK_GE(pause_duration, TimeDelta::Zero());
285 int crop_width = slides.images.crop.width.value_or(slides.images.width);
286 int crop_height = slides.images.crop.height.value_or(slides.images.height);
287 RTC_CHECK_LE(crop_width, slides.images.width);
288 RTC_CHECK_LE(crop_height, slides.images.height);
289 return CreateScrollingInputFromYuvFilesFrameGenerator(
290 clock, paths, slides.images.width, slides.images.height, crop_width,
291 crop_height, slides.images.crop.scroll_duration.ms(),
292 pause_duration.ms());
293 } else {
294 return CreateFromYuvFileFrameGenerator(
295 paths, slides.images.width, slides.images.height,
296 slides.change_interval.seconds<double>() * framerate);
297 }
298 }
299
CreateFrameGenerator(Clock * clock,VideoStreamConfig::Source source)300 std::unique_ptr<FrameGeneratorInterface> CreateFrameGenerator(
301 Clock* clock,
302 VideoStreamConfig::Source source) {
303 using Capture = VideoStreamConfig::Source::Capture;
304 switch (source.capture) {
305 case Capture::kGenerator:
306 return CreateSquareFrameGenerator(
307 source.generator.width, source.generator.height,
308 source.generator.pixel_format, /*num_squares*/ absl::nullopt);
309 case Capture::kVideoFile:
310 RTC_CHECK(source.video_file.width && source.video_file.height);
311 return CreateFromYuvFileFrameGenerator(
312 {TransformFilePath(source.video_file.name)}, source.video_file.width,
313 source.video_file.height, /*frame_repeat_count*/ 1);
314 case Capture::kGenerateSlides:
315 return CreateSlideFrameGenerator(
316 source.slides.generator.width, source.slides.generator.height,
317 source.slides.change_interval.seconds<double>() * source.framerate);
318 case Capture::kImageSlides:
319 return CreateImageSlideGenerator(clock, source.slides, source.framerate);
320 }
321 }
322
CreateVideoReceiveStreamConfig(VideoStreamConfig config,Transport * feedback_transport,VideoReceiveStream::Decoder decoder,rtc::VideoSinkInterface<VideoFrame> * renderer,uint32_t local_ssrc,uint32_t ssrc,uint32_t rtx_ssrc)323 VideoReceiveStream::Config CreateVideoReceiveStreamConfig(
324 VideoStreamConfig config,
325 Transport* feedback_transport,
326 VideoReceiveStream::Decoder decoder,
327 rtc::VideoSinkInterface<VideoFrame>* renderer,
328 uint32_t local_ssrc,
329 uint32_t ssrc,
330 uint32_t rtx_ssrc) {
331 VideoReceiveStream::Config recv(feedback_transport);
332 recv.rtp.transport_cc = config.stream.packet_feedback;
333 recv.rtp.local_ssrc = local_ssrc;
334 recv.rtp.extensions = GetVideoRtpExtensions(config);
335
336 RTC_DCHECK(!config.stream.use_rtx ||
337 config.stream.nack_history_time > TimeDelta::Zero());
338 recv.rtp.nack.rtp_history_ms = config.stream.nack_history_time.ms();
339 recv.rtp.protected_by_flexfec = config.stream.use_flexfec;
340 recv.rtp.remote_ssrc = ssrc;
341 recv.decoders.push_back(decoder);
342 recv.renderer = renderer;
343 if (config.stream.use_rtx) {
344 recv.rtp.rtx_ssrc = rtx_ssrc;
345 recv.rtp.rtx_associated_payload_types[CallTest::kSendRtxPayloadType] =
346 CodecTypeToPayloadType(config.encoder.codec);
347 }
348 if (config.stream.use_ulpfec) {
349 recv.rtp.red_payload_type = CallTest::kRedPayloadType;
350 recv.rtp.ulpfec_payload_type = CallTest::kUlpfecPayloadType;
351 recv.rtp.rtx_associated_payload_types[CallTest::kRtxRedPayloadType] =
352 CallTest::kRedPayloadType;
353 }
354 recv.sync_group = config.render.sync_group;
355 return recv;
356 }
357 } // namespace
358
SendVideoStream(CallClient * sender,VideoStreamConfig config,Transport * send_transport,VideoFrameMatcher * matcher)359 SendVideoStream::SendVideoStream(CallClient* sender,
360 VideoStreamConfig config,
361 Transport* send_transport,
362 VideoFrameMatcher* matcher)
363 : sender_(sender), config_(config) {
364 video_capturer_ = std::make_unique<FrameGeneratorCapturer>(
365 sender_->clock_, CreateFrameGenerator(sender_->clock_, config.source),
366 config.source.framerate,
367 *sender->time_controller_->GetTaskQueueFactory());
368 video_capturer_->Init();
369
370 using Encoder = VideoStreamConfig::Encoder;
371 using Codec = VideoStreamConfig::Encoder::Codec;
372 switch (config.encoder.implementation) {
373 case Encoder::Implementation::kFake:
374 encoder_factory_ =
375 std::make_unique<FunctionVideoEncoderFactory>([this]() {
376 MutexLock lock(&mutex_);
377 std::unique_ptr<FakeEncoder> encoder;
378 if (config_.encoder.codec == Codec::kVideoCodecVP8) {
379 encoder = std::make_unique<test::FakeVp8Encoder>(sender_->clock_);
380 } else if (config_.encoder.codec == Codec::kVideoCodecGeneric) {
381 encoder = std::make_unique<test::FakeEncoder>(sender_->clock_);
382 } else {
383 RTC_NOTREACHED();
384 }
385 fake_encoders_.push_back(encoder.get());
386 if (config_.encoder.fake.max_rate.IsFinite())
387 encoder->SetMaxBitrate(config_.encoder.fake.max_rate.kbps());
388 return encoder;
389 });
390 break;
391 case VideoStreamConfig::Encoder::Implementation::kSoftware:
392 encoder_factory_.reset(new InternalEncoderFactory());
393 break;
394 case VideoStreamConfig::Encoder::Implementation::kHardware:
395 encoder_factory_ = CreateHardwareEncoderFactory();
396 break;
397 }
398 RTC_CHECK(encoder_factory_);
399
400 bitrate_allocator_factory_ = CreateBuiltinVideoBitrateAllocatorFactory();
401 RTC_CHECK(bitrate_allocator_factory_);
402
403 VideoEncoderConfig encoder_config = CreateVideoEncoderConfig(config);
404 for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
405 ssrcs_.push_back(sender->GetNextVideoSsrc());
406 rtx_ssrcs_.push_back(sender->GetNextRtxSsrc());
407 }
408 VideoSendStream::Config send_config =
409 CreateVideoSendStreamConfig(config, ssrcs_, rtx_ssrcs_, send_transport);
410 send_config.encoder_settings.encoder_factory = encoder_factory_.get();
411 send_config.encoder_settings.bitrate_allocator_factory =
412 bitrate_allocator_factory_.get();
413
414 sender_->SendTask([&] {
415 if (config.stream.fec_controller_factory) {
416 send_stream_ = sender_->call_->CreateVideoSendStream(
417 std::move(send_config), std::move(encoder_config),
418 config.stream.fec_controller_factory->CreateFecController());
419 } else {
420 send_stream_ = sender_->call_->CreateVideoSendStream(
421 std::move(send_config), std::move(encoder_config));
422 }
423
424 if (matcher->Active()) {
425 frame_tap_ = std::make_unique<ForwardingCapturedFrameTap>(
426 sender_->clock_, matcher, video_capturer_.get());
427 send_stream_->SetSource(frame_tap_.get(),
428 config.encoder.degradation_preference);
429 } else {
430 send_stream_->SetSource(video_capturer_.get(),
431 config.encoder.degradation_preference);
432 }
433 });
434 }
435
~SendVideoStream()436 SendVideoStream::~SendVideoStream() {
437 sender_->SendTask(
438 [this] { sender_->call_->DestroyVideoSendStream(send_stream_); });
439 }
440
Start()441 void SendVideoStream::Start() {
442 sender_->SendTask([this] {
443 send_stream_->Start();
444 sender_->call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp);
445 });
446 }
447
Stop()448 void SendVideoStream::Stop() {
449 sender_->SendTask([this] { send_stream_->Stop(); });
450 }
451
UpdateConfig(std::function<void (VideoStreamConfig *)> modifier)452 void SendVideoStream::UpdateConfig(
453 std::function<void(VideoStreamConfig*)> modifier) {
454 sender_->SendTask([&] {
455 MutexLock lock(&mutex_);
456 VideoStreamConfig prior_config = config_;
457 modifier(&config_);
458 if (prior_config.encoder.fake.max_rate != config_.encoder.fake.max_rate) {
459 for (auto* encoder : fake_encoders_) {
460 encoder->SetMaxBitrate(config_.encoder.fake.max_rate.kbps());
461 }
462 }
463 // TODO(srte): Add more conditions that should cause reconfiguration.
464 if (prior_config.encoder.max_framerate != config_.encoder.max_framerate) {
465 VideoEncoderConfig encoder_config = CreateVideoEncoderConfig(config_);
466 send_stream_->ReconfigureVideoEncoder(std::move(encoder_config));
467 }
468 if (prior_config.source.framerate != config_.source.framerate) {
469 SetCaptureFramerate(config_.source.framerate);
470 }
471 });
472 }
473
UpdateActiveLayers(std::vector<bool> active_layers)474 void SendVideoStream::UpdateActiveLayers(std::vector<bool> active_layers) {
475 sender_->task_queue_.PostTask([=] {
476 MutexLock lock(&mutex_);
477 if (config_.encoder.codec ==
478 VideoStreamConfig::Encoder::Codec::kVideoCodecVP8) {
479 send_stream_->UpdateActiveSimulcastLayers(active_layers);
480 } else {
481 VideoEncoderConfig encoder_config = CreateVideoEncoderConfig(config_);
482 RTC_CHECK_EQ(encoder_config.simulcast_layers.size(),
483 active_layers.size());
484 for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i)
485 encoder_config.simulcast_layers[i].active = active_layers[i];
486 send_stream_->ReconfigureVideoEncoder(std::move(encoder_config));
487 }
488 });
489 }
490
UsingSsrc(uint32_t ssrc) const491 bool SendVideoStream::UsingSsrc(uint32_t ssrc) const {
492 for (uint32_t owned : ssrcs_) {
493 if (owned == ssrc)
494 return true;
495 }
496 return false;
497 }
498
UsingRtxSsrc(uint32_t ssrc) const499 bool SendVideoStream::UsingRtxSsrc(uint32_t ssrc) const {
500 for (uint32_t owned : rtx_ssrcs_) {
501 if (owned == ssrc)
502 return true;
503 }
504 return false;
505 }
506
SetCaptureFramerate(int framerate)507 void SendVideoStream::SetCaptureFramerate(int framerate) {
508 sender_->SendTask([&] { video_capturer_->ChangeFramerate(framerate); });
509 }
510
GetStats() const511 VideoSendStream::Stats SendVideoStream::GetStats() const {
512 return send_stream_->GetStats();
513 }
514
StatsPrinter()515 ColumnPrinter SendVideoStream::StatsPrinter() {
516 return ColumnPrinter::Lambda(
517 "video_target_rate video_sent_rate width height",
518 [this](rtc::SimpleStringBuilder& sb) {
519 VideoSendStream::Stats video_stats = send_stream_->GetStats();
520 int width = 0;
521 int height = 0;
522 for (const auto& stream_stat : video_stats.substreams) {
523 width = std::max(width, stream_stat.second.width);
524 height = std::max(height, stream_stat.second.height);
525 }
526 sb.AppendFormat("%.0lf %.0lf %i %i",
527 video_stats.target_media_bitrate_bps / 8.0,
528 video_stats.media_bitrate_bps / 8.0, width, height);
529 },
530 64);
531 }
532
ReceiveVideoStream(CallClient * receiver,VideoStreamConfig config,SendVideoStream * send_stream,size_t chosen_stream,Transport * feedback_transport,VideoFrameMatcher * matcher)533 ReceiveVideoStream::ReceiveVideoStream(CallClient* receiver,
534 VideoStreamConfig config,
535 SendVideoStream* send_stream,
536 size_t chosen_stream,
537 Transport* feedback_transport,
538 VideoFrameMatcher* matcher)
539 : receiver_(receiver), config_(config) {
540 if (config.encoder.codec ==
541 VideoStreamConfig::Encoder::Codec::kVideoCodecGeneric ||
542 config.encoder.implementation == VideoStreamConfig::Encoder::kFake) {
543 decoder_factory_ = std::make_unique<FunctionVideoDecoderFactory>(
544 []() { return std::make_unique<FakeDecoder>(); });
545 } else {
546 decoder_factory_ = std::make_unique<InternalDecoderFactory>();
547 }
548
549 VideoReceiveStream::Decoder decoder =
550 CreateMatchingDecoder(CodecTypeToPayloadType(config.encoder.codec),
551 CodecTypeToPayloadString(config.encoder.codec));
552 decoder.decoder_factory = decoder_factory_.get();
553 size_t num_streams = 1;
554 if (config.encoder.codec == VideoStreamConfig::Encoder::Codec::kVideoCodecVP8)
555 num_streams = config.encoder.layers.spatial;
556 for (size_t i = 0; i < num_streams; ++i) {
557 rtc::VideoSinkInterface<VideoFrame>* renderer = &fake_renderer_;
558 if (matcher->Active()) {
559 render_taps_.emplace_back(
560 std::make_unique<DecodedFrameTap>(receiver_->clock_, matcher, i));
561 renderer = render_taps_.back().get();
562 }
563 auto recv_config = CreateVideoReceiveStreamConfig(
564 config, feedback_transport, decoder, renderer,
565 receiver_->GetNextVideoLocalSsrc(), send_stream->ssrcs_[i],
566 send_stream->rtx_ssrcs_[i]);
567 if (config.stream.use_flexfec) {
568 RTC_DCHECK(num_streams == 1);
569 FlexfecReceiveStream::Config flexfec(feedback_transport);
570 flexfec.payload_type = CallTest::kFlexfecPayloadType;
571 flexfec.remote_ssrc = CallTest::kFlexfecSendSsrc;
572 flexfec.protected_media_ssrcs = send_stream->rtx_ssrcs_;
573 flexfec.local_ssrc = recv_config.rtp.local_ssrc;
574 receiver_->ssrc_media_types_[flexfec.remote_ssrc] = MediaType::VIDEO;
575
576 receiver_->SendTask([this, &flexfec] {
577 flecfec_stream_ = receiver_->call_->CreateFlexfecReceiveStream(flexfec);
578 });
579 }
580 receiver_->ssrc_media_types_[recv_config.rtp.remote_ssrc] =
581 MediaType::VIDEO;
582 if (config.stream.use_rtx)
583 receiver_->ssrc_media_types_[recv_config.rtp.rtx_ssrc] = MediaType::VIDEO;
584 receiver_->SendTask([this, &recv_config] {
585 receive_streams_.push_back(
586 receiver_->call_->CreateVideoReceiveStream(std::move(recv_config)));
587 });
588 }
589 }
590
~ReceiveVideoStream()591 ReceiveVideoStream::~ReceiveVideoStream() {
592 receiver_->SendTask([this] {
593 for (auto* recv_stream : receive_streams_)
594 receiver_->call_->DestroyVideoReceiveStream(recv_stream);
595 if (flecfec_stream_)
596 receiver_->call_->DestroyFlexfecReceiveStream(flecfec_stream_);
597 });
598 }
599
Start()600 void ReceiveVideoStream::Start() {
601 receiver_->SendTask([this] {
602 for (auto* recv_stream : receive_streams_)
603 recv_stream->Start();
604 receiver_->call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp);
605 });
606 }
607
Stop()608 void ReceiveVideoStream::Stop() {
609 receiver_->SendTask([this] {
610 for (auto* recv_stream : receive_streams_)
611 recv_stream->Stop();
612 });
613 }
614
GetStats() const615 VideoReceiveStream::Stats ReceiveVideoStream::GetStats() const {
616 if (receive_streams_.empty())
617 return VideoReceiveStream::Stats();
618 // TODO(srte): Handle multiple receive streams.
619 return receive_streams_.back()->GetStats();
620 }
621
622 VideoStreamPair::~VideoStreamPair() = default;
623
VideoStreamPair(CallClient * sender,CallClient * receiver,VideoStreamConfig config)624 VideoStreamPair::VideoStreamPair(CallClient* sender,
625 CallClient* receiver,
626 VideoStreamConfig config)
627 : config_(config),
628 matcher_(config.hooks.frame_pair_handlers),
629 send_stream_(sender, config, sender->transport_.get(), &matcher_),
630 receive_stream_(receiver,
631 config,
632 &send_stream_,
633 /*chosen_stream=*/0,
634 receiver->transport_.get(),
635 &matcher_) {}
636
637 } // namespace test
638 } // namespace webrtc
639