| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2013 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 <algorithm> | |
| 11 #include <sstream> | |
| 12 #include <string> | |
| 13 | |
| 14 #include "testing/gtest/include/gtest/gtest.h" | |
| 15 | |
| 16 #include "webrtc/base/checks.h" | |
| 17 #include "webrtc/base/scoped_ptr.h" | |
| 18 #include "webrtc/base/thread_annotations.h" | |
| 19 #include "webrtc/call.h" | |
| 20 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" | |
| 21 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" | |
| 22 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" | |
| 23 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | |
| 24 #include "webrtc/system_wrappers/interface/rtp_to_ntp.h" | |
| 25 #include "webrtc/test/call_test.h" | |
| 26 #include "webrtc/test/direct_transport.h" | |
| 27 #include "webrtc/test/encoder_settings.h" | |
| 28 #include "webrtc/test/fake_audio_device.h" | |
| 29 #include "webrtc/test/fake_decoder.h" | |
| 30 #include "webrtc/test/fake_encoder.h" | |
| 31 #include "webrtc/test/frame_generator.h" | |
| 32 #include "webrtc/test/frame_generator_capturer.h" | |
| 33 #include "webrtc/test/rtp_rtcp_observer.h" | |
| 34 #include "webrtc/test/testsupport/fileutils.h" | |
| 35 #include "webrtc/test/testsupport/perf_test.h" | |
| 36 #include "webrtc/video/transport_adapter.h" | |
| 37 #include "webrtc/voice_engine/include/voe_base.h" | |
| 38 #include "webrtc/voice_engine/include/voe_codec.h" | |
| 39 #include "webrtc/voice_engine/include/voe_network.h" | |
| 40 #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" | |
| 41 #include "webrtc/voice_engine/include/voe_video_sync.h" | |
| 42 | |
| 43 namespace webrtc { | |
| 44 | |
| 45 class CallPerfTest : public test::CallTest { | |
| 46 protected: | |
| 47 void TestAudioVideoSync(bool fec, bool create_audio_first); | |
| 48 | |
| 49 void TestCpuOveruse(LoadObserver::Load tested_load, int encode_delay_ms); | |
| 50 | |
| 51 void TestMinTransmitBitrate(bool pad_to_min_bitrate); | |
| 52 | |
| 53 void TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, | |
| 54 int threshold_ms, | |
| 55 int start_time_ms, | |
| 56 int run_time_ms); | |
| 57 }; | |
| 58 | |
| 59 class SyncRtcpObserver : public test::RtpRtcpObserver { | |
| 60 public: | |
| 61 explicit SyncRtcpObserver(const FakeNetworkPipe::Config& config) | |
| 62 : test::RtpRtcpObserver(CallPerfTest::kLongTimeoutMs, config) {} | |
| 63 | |
| 64 Action OnSendRtcp(const uint8_t* packet, size_t length) override { | |
| 65 RTCPUtility::RTCPParserV2 parser(packet, length, true); | |
| 66 EXPECT_TRUE(parser.IsValid()); | |
| 67 | |
| 68 for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); | |
| 69 packet_type != RTCPUtility::RTCPPacketTypes::kInvalid; | |
| 70 packet_type = parser.Iterate()) { | |
| 71 if (packet_type == RTCPUtility::RTCPPacketTypes::kSr) { | |
| 72 const RTCPUtility::RTCPPacket& packet = parser.Packet(); | |
| 73 RtcpMeasurement ntp_rtp_pair( | |
| 74 packet.SR.NTPMostSignificant, | |
| 75 packet.SR.NTPLeastSignificant, | |
| 76 packet.SR.RTPTimestamp); | |
| 77 StoreNtpRtpPair(ntp_rtp_pair); | |
| 78 } | |
| 79 } | |
| 80 return SEND_PACKET; | |
| 81 } | |
| 82 | |
| 83 int64_t RtpTimestampToNtp(uint32_t timestamp) const { | |
| 84 rtc::CritScope lock(&crit_); | |
| 85 int64_t timestamp_in_ms = -1; | |
| 86 if (ntp_rtp_pairs_.size() == 2) { | |
| 87 // TODO(stefan): We can't EXPECT_TRUE on this call due to a bug in the | |
| 88 // RTCP sender where it sends RTCP SR before any RTP packets, which leads | |
| 89 // to a bogus NTP/RTP mapping. | |
| 90 RtpToNtpMs(timestamp, ntp_rtp_pairs_, ×tamp_in_ms); | |
| 91 return timestamp_in_ms; | |
| 92 } | |
| 93 return -1; | |
| 94 } | |
| 95 | |
| 96 private: | |
| 97 void StoreNtpRtpPair(RtcpMeasurement ntp_rtp_pair) { | |
| 98 rtc::CritScope lock(&crit_); | |
| 99 for (RtcpList::iterator it = ntp_rtp_pairs_.begin(); | |
| 100 it != ntp_rtp_pairs_.end(); | |
| 101 ++it) { | |
| 102 if (ntp_rtp_pair.ntp_secs == it->ntp_secs && | |
| 103 ntp_rtp_pair.ntp_frac == it->ntp_frac) { | |
| 104 // This RTCP has already been added to the list. | |
| 105 return; | |
| 106 } | |
| 107 } | |
| 108 // We need two RTCP SR reports to map between RTP and NTP. More than two | |
| 109 // will not improve the mapping. | |
| 110 if (ntp_rtp_pairs_.size() == 2) { | |
| 111 ntp_rtp_pairs_.pop_back(); | |
| 112 } | |
| 113 ntp_rtp_pairs_.push_front(ntp_rtp_pair); | |
| 114 } | |
| 115 | |
| 116 mutable rtc::CriticalSection crit_; | |
| 117 RtcpList ntp_rtp_pairs_ GUARDED_BY(crit_); | |
| 118 }; | |
| 119 | |
| 120 class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { | |
| 121 static const int kInSyncThresholdMs = 50; | |
| 122 static const int kStartupTimeMs = 2000; | |
| 123 static const int kMinRunTimeMs = 30000; | |
| 124 | |
| 125 public: | |
| 126 VideoRtcpAndSyncObserver(Clock* clock, | |
| 127 int voe_channel, | |
| 128 VoEVideoSync* voe_sync, | |
| 129 SyncRtcpObserver* audio_observer) | |
| 130 : SyncRtcpObserver(FakeNetworkPipe::Config()), | |
| 131 clock_(clock), | |
| 132 voe_channel_(voe_channel), | |
| 133 voe_sync_(voe_sync), | |
| 134 audio_observer_(audio_observer), | |
| 135 creation_time_ms_(clock_->TimeInMilliseconds()), | |
| 136 first_time_in_sync_(-1) {} | |
| 137 | |
| 138 void RenderFrame(const VideoFrame& video_frame, | |
| 139 int time_to_render_ms) override { | |
| 140 int64_t now_ms = clock_->TimeInMilliseconds(); | |
| 141 uint32_t playout_timestamp = 0; | |
| 142 if (voe_sync_->GetPlayoutTimestamp(voe_channel_, playout_timestamp) != 0) | |
| 143 return; | |
| 144 int64_t latest_audio_ntp = | |
| 145 audio_observer_->RtpTimestampToNtp(playout_timestamp); | |
| 146 int64_t latest_video_ntp = RtpTimestampToNtp(video_frame.timestamp()); | |
| 147 if (latest_audio_ntp < 0 || latest_video_ntp < 0) | |
| 148 return; | |
| 149 int time_until_render_ms = | |
| 150 std::max(0, static_cast<int>(video_frame.render_time_ms() - now_ms)); | |
| 151 latest_video_ntp += time_until_render_ms; | |
| 152 int64_t stream_offset = latest_audio_ntp - latest_video_ntp; | |
| 153 std::stringstream ss; | |
| 154 ss << stream_offset; | |
| 155 webrtc::test::PrintResult("stream_offset", | |
| 156 "", | |
| 157 "synchronization", | |
| 158 ss.str(), | |
| 159 "ms", | |
| 160 false); | |
| 161 int64_t time_since_creation = now_ms - creation_time_ms_; | |
| 162 // During the first couple of seconds audio and video can falsely be | |
| 163 // estimated as being synchronized. We don't want to trigger on those. | |
| 164 if (time_since_creation < kStartupTimeMs) | |
| 165 return; | |
| 166 if (std::abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) { | |
| 167 if (first_time_in_sync_ == -1) { | |
| 168 first_time_in_sync_ = now_ms; | |
| 169 webrtc::test::PrintResult("sync_convergence_time", | |
| 170 "", | |
| 171 "synchronization", | |
| 172 time_since_creation, | |
| 173 "ms", | |
| 174 false); | |
| 175 } | |
| 176 if (time_since_creation > kMinRunTimeMs) | |
| 177 observation_complete_->Set(); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 bool IsTextureSupported() const override { return false; } | |
| 182 | |
| 183 private: | |
| 184 Clock* const clock_; | |
| 185 int voe_channel_; | |
| 186 VoEVideoSync* voe_sync_; | |
| 187 SyncRtcpObserver* audio_observer_; | |
| 188 int64_t creation_time_ms_; | |
| 189 int64_t first_time_in_sync_; | |
| 190 }; | |
| 191 | |
| 192 void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { | |
| 193 const char* kSyncGroup = "av_sync"; | |
| 194 class AudioPacketReceiver : public PacketReceiver { | |
| 195 public: | |
| 196 AudioPacketReceiver(int channel, VoENetwork* voe_network) | |
| 197 : channel_(channel), | |
| 198 voe_network_(voe_network), | |
| 199 parser_(RtpHeaderParser::Create()) {} | |
| 200 DeliveryStatus DeliverPacket(MediaType media_type, | |
| 201 const uint8_t* packet, | |
| 202 size_t length, | |
| 203 const PacketTime& packet_time) override { | |
| 204 EXPECT_TRUE(media_type == MediaType::ANY || | |
| 205 media_type == MediaType::AUDIO); | |
| 206 int ret; | |
| 207 if (parser_->IsRtcp(packet, length)) { | |
| 208 ret = voe_network_->ReceivedRTCPPacket(channel_, packet, length); | |
| 209 } else { | |
| 210 ret = voe_network_->ReceivedRTPPacket(channel_, packet, length, | |
| 211 PacketTime()); | |
| 212 } | |
| 213 return ret == 0 ? DELIVERY_OK : DELIVERY_PACKET_ERROR; | |
| 214 } | |
| 215 | |
| 216 private: | |
| 217 int channel_; | |
| 218 VoENetwork* voe_network_; | |
| 219 rtc::scoped_ptr<RtpHeaderParser> parser_; | |
| 220 }; | |
| 221 | |
| 222 VoiceEngine* voice_engine = VoiceEngine::Create(); | |
| 223 VoEBase* voe_base = VoEBase::GetInterface(voice_engine); | |
| 224 VoECodec* voe_codec = VoECodec::GetInterface(voice_engine); | |
| 225 VoENetwork* voe_network = VoENetwork::GetInterface(voice_engine); | |
| 226 VoEVideoSync* voe_sync = VoEVideoSync::GetInterface(voice_engine); | |
| 227 const std::string audio_filename = | |
| 228 test::ResourcePath("voice_engine/audio_long16", "pcm"); | |
| 229 ASSERT_STRNE("", audio_filename.c_str()); | |
| 230 test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(), | |
| 231 audio_filename); | |
| 232 EXPECT_EQ(0, voe_base->Init(&fake_audio_device, nullptr)); | |
| 233 int channel = voe_base->CreateChannel(); | |
| 234 | |
| 235 FakeNetworkPipe::Config net_config; | |
| 236 net_config.queue_delay_ms = 500; | |
| 237 net_config.loss_percent = 5; | |
| 238 SyncRtcpObserver audio_observer(net_config); | |
| 239 VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock(), | |
| 240 channel, | |
| 241 voe_sync, | |
| 242 &audio_observer); | |
| 243 | |
| 244 Call::Config receiver_config; | |
| 245 receiver_config.voice_engine = voice_engine; | |
| 246 CreateCalls(Call::Config(), receiver_config); | |
| 247 | |
| 248 CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000}; | |
| 249 EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac)); | |
| 250 | |
| 251 AudioPacketReceiver voe_packet_receiver(channel, voe_network); | |
| 252 audio_observer.SetReceivers(&voe_packet_receiver, &voe_packet_receiver); | |
| 253 | |
| 254 internal::TransportAdapter transport_adapter(audio_observer.SendTransport()); | |
| 255 transport_adapter.Enable(); | |
| 256 EXPECT_EQ(0, | |
| 257 voe_network->RegisterExternalTransport(channel, transport_adapter)); | |
| 258 | |
| 259 observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); | |
| 260 | |
| 261 test::FakeDecoder fake_decoder; | |
| 262 | |
| 263 CreateSendConfig(1, observer.SendTransport()); | |
| 264 CreateMatchingReceiveConfigs(observer.ReceiveTransport()); | |
| 265 | |
| 266 send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; | |
| 267 if (fec) { | |
| 268 send_config_.rtp.fec.red_payload_type = kRedPayloadType; | |
| 269 send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; | |
| 270 receive_configs_[0].rtp.fec.red_payload_type = kRedPayloadType; | |
| 271 receive_configs_[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; | |
| 272 } | |
| 273 receive_configs_[0].rtp.nack.rtp_history_ms = 1000; | |
| 274 receive_configs_[0].renderer = &observer; | |
| 275 receive_configs_[0].sync_group = kSyncGroup; | |
| 276 | |
| 277 AudioReceiveStream::Config audio_config; | |
| 278 audio_config.voe_channel_id = channel; | |
| 279 audio_config.sync_group = kSyncGroup; | |
| 280 | |
| 281 AudioReceiveStream* audio_receive_stream = nullptr; | |
| 282 | |
| 283 if (create_audio_first) { | |
| 284 audio_receive_stream = | |
| 285 receiver_call_->CreateAudioReceiveStream(audio_config); | |
| 286 CreateStreams(); | |
| 287 } else { | |
| 288 CreateStreams(); | |
| 289 audio_receive_stream = | |
| 290 receiver_call_->CreateAudioReceiveStream(audio_config); | |
| 291 } | |
| 292 | |
| 293 CreateFrameGeneratorCapturer(); | |
| 294 | |
| 295 Start(); | |
| 296 | |
| 297 fake_audio_device.Start(); | |
| 298 EXPECT_EQ(0, voe_base->StartPlayout(channel)); | |
| 299 EXPECT_EQ(0, voe_base->StartReceive(channel)); | |
| 300 EXPECT_EQ(0, voe_base->StartSend(channel)); | |
| 301 | |
| 302 EXPECT_EQ(kEventSignaled, observer.Wait()) | |
| 303 << "Timed out while waiting for audio and video to be synchronized."; | |
| 304 | |
| 305 EXPECT_EQ(0, voe_base->StopSend(channel)); | |
| 306 EXPECT_EQ(0, voe_base->StopReceive(channel)); | |
| 307 EXPECT_EQ(0, voe_base->StopPlayout(channel)); | |
| 308 fake_audio_device.Stop(); | |
| 309 | |
| 310 Stop(); | |
| 311 observer.StopSending(); | |
| 312 audio_observer.StopSending(); | |
| 313 | |
| 314 voe_base->DeleteChannel(channel); | |
| 315 voe_base->Release(); | |
| 316 voe_codec->Release(); | |
| 317 voe_network->Release(); | |
| 318 voe_sync->Release(); | |
| 319 | |
| 320 DestroyStreams(); | |
| 321 | |
| 322 receiver_call_->DestroyAudioReceiveStream(audio_receive_stream); | |
| 323 | |
| 324 VoiceEngine::Delete(voice_engine); | |
| 325 } | |
| 326 | |
| 327 TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithAudioCreatedFirst) { | |
| 328 TestAudioVideoSync(false, true); | |
| 329 } | |
| 330 | |
| 331 TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithVideoCreatedFirst) { | |
| 332 TestAudioVideoSync(false, false); | |
| 333 } | |
| 334 | |
| 335 TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSyncWithFec) { | |
| 336 TestAudioVideoSync(true, false); | |
| 337 } | |
| 338 | |
| 339 void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, | |
| 340 int threshold_ms, | |
| 341 int start_time_ms, | |
| 342 int run_time_ms) { | |
| 343 class CaptureNtpTimeObserver : public test::EndToEndTest, | |
| 344 public VideoRenderer { | |
| 345 public: | |
| 346 CaptureNtpTimeObserver(const FakeNetworkPipe::Config& config, | |
| 347 int threshold_ms, | |
| 348 int start_time_ms, | |
| 349 int run_time_ms) | |
| 350 : EndToEndTest(kLongTimeoutMs, config), | |
| 351 clock_(Clock::GetRealTimeClock()), | |
| 352 threshold_ms_(threshold_ms), | |
| 353 start_time_ms_(start_time_ms), | |
| 354 run_time_ms_(run_time_ms), | |
| 355 creation_time_ms_(clock_->TimeInMilliseconds()), | |
| 356 capturer_(nullptr), | |
| 357 rtp_start_timestamp_set_(false), | |
| 358 rtp_start_timestamp_(0) {} | |
| 359 | |
| 360 private: | |
| 361 void RenderFrame(const VideoFrame& video_frame, | |
| 362 int time_to_render_ms) override { | |
| 363 if (video_frame.ntp_time_ms() <= 0) { | |
| 364 // Haven't got enough RTCP SR in order to calculate the capture ntp | |
| 365 // time. | |
| 366 return; | |
| 367 } | |
| 368 | |
| 369 int64_t now_ms = clock_->TimeInMilliseconds(); | |
| 370 int64_t time_since_creation = now_ms - creation_time_ms_; | |
| 371 if (time_since_creation < start_time_ms_) { | |
| 372 // Wait for |start_time_ms_| before start measuring. | |
| 373 return; | |
| 374 } | |
| 375 | |
| 376 if (time_since_creation > run_time_ms_) { | |
| 377 observation_complete_->Set(); | |
| 378 } | |
| 379 | |
| 380 FrameCaptureTimeList::iterator iter = | |
| 381 capture_time_list_.find(video_frame.timestamp()); | |
| 382 EXPECT_TRUE(iter != capture_time_list_.end()); | |
| 383 | |
| 384 // The real capture time has been wrapped to uint32_t before converted | |
| 385 // to rtp timestamp in the sender side. So here we convert the estimated | |
| 386 // capture time to a uint32_t 90k timestamp also for comparing. | |
| 387 uint32_t estimated_capture_timestamp = | |
| 388 90 * static_cast<uint32_t>(video_frame.ntp_time_ms()); | |
| 389 uint32_t real_capture_timestamp = iter->second; | |
| 390 int time_offset_ms = real_capture_timestamp - estimated_capture_timestamp; | |
| 391 time_offset_ms = time_offset_ms / 90; | |
| 392 std::stringstream ss; | |
| 393 ss << time_offset_ms; | |
| 394 | |
| 395 webrtc::test::PrintResult( | |
| 396 "capture_ntp_time", "", "real - estimated", ss.str(), "ms", true); | |
| 397 EXPECT_TRUE(std::abs(time_offset_ms) < threshold_ms_); | |
| 398 } | |
| 399 | |
| 400 bool IsTextureSupported() const override { return false; } | |
| 401 | |
| 402 virtual Action OnSendRtp(const uint8_t* packet, size_t length) { | |
| 403 RTPHeader header; | |
| 404 EXPECT_TRUE(parser_->Parse(packet, length, &header)); | |
| 405 | |
| 406 if (!rtp_start_timestamp_set_) { | |
| 407 // Calculate the rtp timestamp offset in order to calculate the real | |
| 408 // capture time. | |
| 409 uint32_t first_capture_timestamp = | |
| 410 90 * static_cast<uint32_t>(capturer_->first_frame_capture_time()); | |
| 411 rtp_start_timestamp_ = header.timestamp - first_capture_timestamp; | |
| 412 rtp_start_timestamp_set_ = true; | |
| 413 } | |
| 414 | |
| 415 uint32_t capture_timestamp = header.timestamp - rtp_start_timestamp_; | |
| 416 capture_time_list_.insert( | |
| 417 capture_time_list_.end(), | |
| 418 std::make_pair(header.timestamp, capture_timestamp)); | |
| 419 return SEND_PACKET; | |
| 420 } | |
| 421 | |
| 422 void OnFrameGeneratorCapturerCreated( | |
| 423 test::FrameGeneratorCapturer* frame_generator_capturer) override { | |
| 424 capturer_ = frame_generator_capturer; | |
| 425 } | |
| 426 | |
| 427 void ModifyConfigs(VideoSendStream::Config* send_config, | |
| 428 std::vector<VideoReceiveStream::Config>* receive_configs, | |
| 429 VideoEncoderConfig* encoder_config) override { | |
| 430 (*receive_configs)[0].renderer = this; | |
| 431 // Enable the receiver side rtt calculation. | |
| 432 (*receive_configs)[0].rtp.rtcp_xr.receiver_reference_time_report = true; | |
| 433 } | |
| 434 | |
| 435 void PerformTest() override { | |
| 436 EXPECT_EQ(kEventSignaled, Wait()) << "Timed out while waiting for " | |
| 437 "estimated capture NTP time to be " | |
| 438 "within bounds."; | |
| 439 } | |
| 440 | |
| 441 Clock* clock_; | |
| 442 int threshold_ms_; | |
| 443 int start_time_ms_; | |
| 444 int run_time_ms_; | |
| 445 int64_t creation_time_ms_; | |
| 446 test::FrameGeneratorCapturer* capturer_; | |
| 447 bool rtp_start_timestamp_set_; | |
| 448 uint32_t rtp_start_timestamp_; | |
| 449 typedef std::map<uint32_t, uint32_t> FrameCaptureTimeList; | |
| 450 FrameCaptureTimeList capture_time_list_; | |
| 451 } test(net_config, threshold_ms, start_time_ms, run_time_ms); | |
| 452 | |
| 453 RunBaseTest(&test); | |
| 454 } | |
| 455 | |
| 456 TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkDelay) { | |
| 457 FakeNetworkPipe::Config net_config; | |
| 458 net_config.queue_delay_ms = 100; | |
| 459 // TODO(wu): lower the threshold as the calculation/estimatation becomes more | |
| 460 // accurate. | |
| 461 const int kThresholdMs = 100; | |
| 462 const int kStartTimeMs = 10000; | |
| 463 const int kRunTimeMs = 20000; | |
| 464 TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); | |
| 465 } | |
| 466 | |
| 467 TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkJitter) { | |
| 468 FakeNetworkPipe::Config net_config; | |
| 469 net_config.queue_delay_ms = 100; | |
| 470 net_config.delay_standard_deviation_ms = 10; | |
| 471 // TODO(wu): lower the threshold as the calculation/estimatation becomes more | |
| 472 // accurate. | |
| 473 const int kThresholdMs = 100; | |
| 474 const int kStartTimeMs = 10000; | |
| 475 const int kRunTimeMs = 20000; | |
| 476 TestCaptureNtpTime(net_config, kThresholdMs, kStartTimeMs, kRunTimeMs); | |
| 477 } | |
| 478 | |
| 479 void CallPerfTest::TestCpuOveruse(LoadObserver::Load tested_load, | |
| 480 int encode_delay_ms) { | |
| 481 class LoadObserver : public test::SendTest, public webrtc::LoadObserver { | |
| 482 public: | |
| 483 LoadObserver(LoadObserver::Load tested_load, int encode_delay_ms) | |
| 484 : SendTest(kLongTimeoutMs), | |
| 485 tested_load_(tested_load), | |
| 486 encoder_(Clock::GetRealTimeClock(), encode_delay_ms) {} | |
| 487 | |
| 488 void OnLoadUpdate(Load load) override { | |
| 489 if (load == tested_load_) | |
| 490 observation_complete_->Set(); | |
| 491 } | |
| 492 | |
| 493 void ModifyConfigs(VideoSendStream::Config* send_config, | |
| 494 std::vector<VideoReceiveStream::Config>* receive_configs, | |
| 495 VideoEncoderConfig* encoder_config) override { | |
| 496 send_config->overuse_callback = this; | |
| 497 send_config->encoder_settings.encoder = &encoder_; | |
| 498 } | |
| 499 | |
| 500 void PerformTest() override { | |
| 501 EXPECT_EQ(kEventSignaled, Wait()) | |
| 502 << "Timed out before receiving an overuse callback."; | |
| 503 } | |
| 504 | |
| 505 LoadObserver::Load tested_load_; | |
| 506 test::DelayedEncoder encoder_; | |
| 507 } test(tested_load, encode_delay_ms); | |
| 508 | |
| 509 RunBaseTest(&test); | |
| 510 } | |
| 511 | |
| 512 TEST_F(CallPerfTest, ReceivesCpuUnderuse) { | |
| 513 const int kEncodeDelayMs = 2; | |
| 514 TestCpuOveruse(LoadObserver::kUnderuse, kEncodeDelayMs); | |
| 515 } | |
| 516 | |
| 517 TEST_F(CallPerfTest, ReceivesCpuOveruse) { | |
| 518 const int kEncodeDelayMs = 35; | |
| 519 TestCpuOveruse(LoadObserver::kOveruse, kEncodeDelayMs); | |
| 520 } | |
| 521 | |
| 522 void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { | |
| 523 static const int kMaxEncodeBitrateKbps = 30; | |
| 524 static const int kMinTransmitBitrateBps = 150000; | |
| 525 static const int kMinAcceptableTransmitBitrate = 130; | |
| 526 static const int kMaxAcceptableTransmitBitrate = 170; | |
| 527 static const int kNumBitrateObservationsInRange = 100; | |
| 528 static const int kAcceptableBitrateErrorMargin = 15; // +- 7 | |
| 529 class BitrateObserver : public test::EndToEndTest, public PacketReceiver { | |
| 530 public: | |
| 531 explicit BitrateObserver(bool using_min_transmit_bitrate) | |
| 532 : EndToEndTest(kLongTimeoutMs), | |
| 533 send_stream_(nullptr), | |
| 534 send_transport_receiver_(nullptr), | |
| 535 pad_to_min_bitrate_(using_min_transmit_bitrate), | |
| 536 num_bitrate_observations_in_range_(0) {} | |
| 537 | |
| 538 private: | |
| 539 void SetReceivers(PacketReceiver* send_transport_receiver, | |
| 540 PacketReceiver* receive_transport_receiver) override { | |
| 541 send_transport_receiver_ = send_transport_receiver; | |
| 542 test::RtpRtcpObserver::SetReceivers(this, receive_transport_receiver); | |
| 543 } | |
| 544 | |
| 545 DeliveryStatus DeliverPacket(MediaType media_type, | |
| 546 const uint8_t* packet, | |
| 547 size_t length, | |
| 548 const PacketTime& packet_time) override { | |
| 549 VideoSendStream::Stats stats = send_stream_->GetStats(); | |
| 550 if (stats.substreams.size() > 0) { | |
| 551 RTC_DCHECK_EQ(1u, stats.substreams.size()); | |
| 552 int bitrate_kbps = | |
| 553 stats.substreams.begin()->second.total_bitrate_bps / 1000; | |
| 554 if (bitrate_kbps > 0) { | |
| 555 test::PrintResult( | |
| 556 "bitrate_stats_", | |
| 557 (pad_to_min_bitrate_ ? "min_transmit_bitrate" | |
| 558 : "without_min_transmit_bitrate"), | |
| 559 "bitrate_kbps", | |
| 560 static_cast<size_t>(bitrate_kbps), | |
| 561 "kbps", | |
| 562 false); | |
| 563 if (pad_to_min_bitrate_) { | |
| 564 if (bitrate_kbps > kMinAcceptableTransmitBitrate && | |
| 565 bitrate_kbps < kMaxAcceptableTransmitBitrate) { | |
| 566 ++num_bitrate_observations_in_range_; | |
| 567 } | |
| 568 } else { | |
| 569 // Expect bitrate stats to roughly match the max encode bitrate. | |
| 570 if (bitrate_kbps > (kMaxEncodeBitrateKbps - | |
| 571 kAcceptableBitrateErrorMargin / 2) && | |
| 572 bitrate_kbps < (kMaxEncodeBitrateKbps + | |
| 573 kAcceptableBitrateErrorMargin / 2)) { | |
| 574 ++num_bitrate_observations_in_range_; | |
| 575 } | |
| 576 } | |
| 577 if (num_bitrate_observations_in_range_ == | |
| 578 kNumBitrateObservationsInRange) | |
| 579 observation_complete_->Set(); | |
| 580 } | |
| 581 } | |
| 582 return send_transport_receiver_->DeliverPacket(media_type, packet, length, | |
| 583 packet_time); | |
| 584 } | |
| 585 | |
| 586 void OnStreamsCreated( | |
| 587 VideoSendStream* send_stream, | |
| 588 const std::vector<VideoReceiveStream*>& receive_streams) override { | |
| 589 send_stream_ = send_stream; | |
| 590 } | |
| 591 | |
| 592 void ModifyConfigs(VideoSendStream::Config* send_config, | |
| 593 std::vector<VideoReceiveStream::Config>* receive_configs, | |
| 594 VideoEncoderConfig* encoder_config) override { | |
| 595 if (pad_to_min_bitrate_) { | |
| 596 encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps; | |
| 597 } else { | |
| 598 RTC_DCHECK_EQ(0, encoder_config->min_transmit_bitrate_bps); | |
| 599 } | |
| 600 } | |
| 601 | |
| 602 void PerformTest() override { | |
| 603 EXPECT_EQ(kEventSignaled, Wait()) | |
| 604 << "Timeout while waiting for send-bitrate stats."; | |
| 605 } | |
| 606 | |
| 607 VideoSendStream* send_stream_; | |
| 608 PacketReceiver* send_transport_receiver_; | |
| 609 const bool pad_to_min_bitrate_; | |
| 610 int num_bitrate_observations_in_range_; | |
| 611 } test(pad_to_min_bitrate); | |
| 612 | |
| 613 fake_encoder_.SetMaxBitrate(kMaxEncodeBitrateKbps); | |
| 614 RunBaseTest(&test); | |
| 615 } | |
| 616 | |
| 617 TEST_F(CallPerfTest, PadsToMinTransmitBitrate) { TestMinTransmitBitrate(true); } | |
| 618 | |
| 619 TEST_F(CallPerfTest, NoPadWithoutMinTransmitBitrate) { | |
| 620 TestMinTransmitBitrate(false); | |
| 621 } | |
| 622 | |
| 623 TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { | |
| 624 static const uint32_t kInitialBitrateKbps = 400; | |
| 625 static const uint32_t kReconfigureThresholdKbps = 600; | |
| 626 static const uint32_t kPermittedReconfiguredBitrateDiffKbps = 100; | |
| 627 | |
| 628 class BitrateObserver : public test::EndToEndTest, public test::FakeEncoder { | |
| 629 public: | |
| 630 BitrateObserver() | |
| 631 : EndToEndTest(kDefaultTimeoutMs), | |
| 632 FakeEncoder(Clock::GetRealTimeClock()), | |
| 633 time_to_reconfigure_(webrtc::EventWrapper::Create()), | |
| 634 encoder_inits_(0), | |
| 635 last_set_bitrate_(0), | |
| 636 send_stream_(nullptr) {} | |
| 637 | |
| 638 int32_t InitEncode(const VideoCodec* config, | |
| 639 int32_t number_of_cores, | |
| 640 size_t max_payload_size) override { | |
| 641 if (encoder_inits_ == 0) { | |
| 642 EXPECT_EQ(kInitialBitrateKbps, config->startBitrate) | |
| 643 << "Encoder not initialized at expected bitrate."; | |
| 644 } | |
| 645 ++encoder_inits_; | |
| 646 if (encoder_inits_ == 2) { | |
| 647 EXPECT_GE(last_set_bitrate_, kReconfigureThresholdKbps); | |
| 648 EXPECT_NEAR(config->startBitrate, | |
| 649 last_set_bitrate_, | |
| 650 kPermittedReconfiguredBitrateDiffKbps) | |
| 651 << "Encoder reconfigured with bitrate too far away from last set."; | |
| 652 observation_complete_->Set(); | |
| 653 } | |
| 654 return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); | |
| 655 } | |
| 656 | |
| 657 int32_t SetRates(uint32_t new_target_bitrate_kbps, | |
| 658 uint32_t framerate) override { | |
| 659 last_set_bitrate_ = new_target_bitrate_kbps; | |
| 660 if (encoder_inits_ == 1 && | |
| 661 new_target_bitrate_kbps > kReconfigureThresholdKbps) { | |
| 662 time_to_reconfigure_->Set(); | |
| 663 } | |
| 664 return FakeEncoder::SetRates(new_target_bitrate_kbps, framerate); | |
| 665 } | |
| 666 | |
| 667 Call::Config GetSenderCallConfig() override { | |
| 668 Call::Config config = EndToEndTest::GetSenderCallConfig(); | |
| 669 config.bitrate_config.start_bitrate_bps = kInitialBitrateKbps * 1000; | |
| 670 return config; | |
| 671 } | |
| 672 | |
| 673 void ModifyConfigs(VideoSendStream::Config* send_config, | |
| 674 std::vector<VideoReceiveStream::Config>* receive_configs, | |
| 675 VideoEncoderConfig* encoder_config) override { | |
| 676 send_config->encoder_settings.encoder = this; | |
| 677 encoder_config->streams[0].min_bitrate_bps = 50000; | |
| 678 encoder_config->streams[0].target_bitrate_bps = | |
| 679 encoder_config->streams[0].max_bitrate_bps = 2000000; | |
| 680 | |
| 681 encoder_config_ = *encoder_config; | |
| 682 } | |
| 683 | |
| 684 void OnStreamsCreated( | |
| 685 VideoSendStream* send_stream, | |
| 686 const std::vector<VideoReceiveStream*>& receive_streams) override { | |
| 687 send_stream_ = send_stream; | |
| 688 } | |
| 689 | |
| 690 void PerformTest() override { | |
| 691 ASSERT_EQ(kEventSignaled, time_to_reconfigure_->Wait(kDefaultTimeoutMs)) | |
| 692 << "Timed out before receiving an initial high bitrate."; | |
| 693 encoder_config_.streams[0].width *= 2; | |
| 694 encoder_config_.streams[0].height *= 2; | |
| 695 EXPECT_TRUE(send_stream_->ReconfigureVideoEncoder(encoder_config_)); | |
| 696 EXPECT_EQ(kEventSignaled, Wait()) | |
| 697 << "Timed out while waiting for a couple of high bitrate estimates " | |
| 698 "after reconfiguring the send stream."; | |
| 699 } | |
| 700 | |
| 701 private: | |
| 702 rtc::scoped_ptr<webrtc::EventWrapper> time_to_reconfigure_; | |
| 703 int encoder_inits_; | |
| 704 uint32_t last_set_bitrate_; | |
| 705 VideoSendStream* send_stream_; | |
| 706 VideoEncoderConfig encoder_config_; | |
| 707 } test; | |
| 708 | |
| 709 RunBaseTest(&test); | |
| 710 } | |
| 711 | |
| 712 } // namespace webrtc | |
| OLD | NEW |