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 <stdio.h> 11 12 #include <deque> 13 #include <map> 14 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 #include "webrtc/base/thread_annotations.h" 18 #include "webrtc/call.h" 19 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 20 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" 21 #include "webrtc/system_wrappers/interface/clock.h" 22 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 23 #include "webrtc/system_wrappers/interface/event_wrapper.h" 24 #include "webrtc/system_wrappers/interface/scoped_ptr.h" 25 #include "webrtc/system_wrappers/interface/sleep.h" 26 #include "webrtc/test/call_test.h" 27 #include "webrtc/test/direct_transport.h" 28 #include "webrtc/test/encoder_settings.h" 29 #include "webrtc/test/fake_encoder.h" 30 #include "webrtc/test/frame_generator_capturer.h" 31 #include "webrtc/test/statistics.h" 32 #include "webrtc/test/testsupport/fileutils.h" 33 #include "webrtc/typedefs.h" 34 35 namespace webrtc { 36 37 static const int kFullStackTestDurationSecs = 60; 38 39 struct FullStackTestParams { 40 const char* test_label; 41 struct { 42 const char* name; 43 size_t width, height; 44 int fps; 45 } clip; 46 int min_bitrate_bps; 47 int target_bitrate_bps; 48 int max_bitrate_bps; 49 double avg_psnr_threshold; 50 double avg_ssim_threshold; 51 FakeNetworkPipe::Config link; 52 }; 53 54 class FullStackTest : public test::CallTest { 55 protected: 56 void RunTest(const FullStackTestParams& params); 57 }; 58 59 class VideoAnalyzer : public PacketReceiver, 60 public newapi::Transport, 61 public VideoRenderer, 62 public VideoSendStreamInput { 63 public: 64 VideoAnalyzer(VideoSendStreamInput* input, 65 Transport* transport, 66 const char* test_label, 67 double avg_psnr_threshold, 68 double avg_ssim_threshold, 69 int duration_frames) 70 : input_(input), 71 transport_(transport), 72 receiver_(NULL), 73 test_label_(test_label), 74 frames_left_(duration_frames), 75 dropped_frames_(0), 76 last_render_time_(0), 77 rtp_timestamp_delta_(0), 78 crit_(CriticalSectionWrapper::CreateCriticalSection()), 79 first_send_frame_(NULL), 80 avg_psnr_threshold_(avg_psnr_threshold), 81 avg_ssim_threshold_(avg_ssim_threshold), 82 comparison_lock_(CriticalSectionWrapper::CreateCriticalSection()), 83 comparison_thread_(ThreadWrapper::CreateThread(&FrameComparisonThread, 84 this)), 85 done_(EventWrapper::Create()) { 86 unsigned int id; 87 EXPECT_TRUE(comparison_thread_->Start(id)); 88 } 89 90 ~VideoAnalyzer() { 91 EXPECT_TRUE(comparison_thread_->Stop()); 92 93 while (!frames_.empty()) { 94 delete frames_.back(); 95 frames_.pop_back(); 96 } 97 while (!frame_pool_.empty()) { 98 delete frame_pool_.back(); 99 frame_pool_.pop_back(); 100 } 101 } 102 103 virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; } 104 105 virtual DeliveryStatus DeliverPacket(const uint8_t* packet, 106 size_t length) OVERRIDE { 107 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); 108 RTPHeader header; 109 parser->Parse(packet, length, &header); 110 { 111 CriticalSectionScoped lock(crit_.get()); 112 recv_times_[header.timestamp - rtp_timestamp_delta_] = 113 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 114 } 115 116 return receiver_->DeliverPacket(packet, length); 117 } 118 119 virtual void SwapFrame(I420VideoFrame* video_frame) OVERRIDE { 120 I420VideoFrame* copy = NULL; 121 { 122 CriticalSectionScoped lock(crit_.get()); 123 if (frame_pool_.size() > 0) { 124 copy = frame_pool_.front(); 125 frame_pool_.pop_front(); 126 } 127 } 128 if (copy == NULL) 129 copy = new I420VideoFrame(); 130 131 copy->CopyFrame(*video_frame); 132 copy->set_timestamp(copy->render_time_ms() * 90); 133 134 { 135 CriticalSectionScoped lock(crit_.get()); 136 if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0) 137 first_send_frame_ = copy; 138 139 frames_.push_back(copy); 140 } 141 142 input_->SwapFrame(video_frame); 143 } 144 145 virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { 146 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); 147 RTPHeader header; 148 parser->Parse(packet, length, &header); 149 150 { 151 CriticalSectionScoped lock(crit_.get()); 152 if (rtp_timestamp_delta_ == 0) { 153 rtp_timestamp_delta_ = 154 header.timestamp - first_send_frame_->timestamp(); 155 first_send_frame_ = NULL; 156 } 157 send_times_[header.timestamp - rtp_timestamp_delta_] = 158 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 159 } 160 161 return transport_->SendRtp(packet, length); 162 } 163 164 virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { 165 return transport_->SendRtcp(packet, length); 166 } 167 168 virtual void RenderFrame(const I420VideoFrame& video_frame, 169 int time_to_render_ms) OVERRIDE { 170 int64_t render_time_ms = 171 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 172 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_; 173 174 CriticalSectionScoped lock(crit_.get()); 175 while (frames_.front()->timestamp() < send_timestamp) { 176 AddFrameComparison( 177 frames_.front(), &last_rendered_frame_, true, render_time_ms); 178 frame_pool_.push_back(frames_.front()); 179 frames_.pop_front(); 180 } 181 182 I420VideoFrame* reference_frame = frames_.front(); 183 frames_.pop_front(); 184 assert(reference_frame != NULL); 185 EXPECT_EQ(reference_frame->timestamp(), send_timestamp); 186 assert(reference_frame->timestamp() == send_timestamp); 187 188 AddFrameComparison(reference_frame, &video_frame, false, render_time_ms); 189 frame_pool_.push_back(reference_frame); 190 191 last_rendered_frame_.CopyFrame(video_frame); 192 } 193 194 void Wait() { 195 EXPECT_EQ(kEventSignaled, done_->Wait(FullStackTest::kLongTimeoutMs)); 196 } 197 198 VideoSendStreamInput* input_; 199 Transport* transport_; 200 PacketReceiver* receiver_; 201 202 private: 203 struct FrameComparison { 204 FrameComparison(const I420VideoFrame* reference, 205 const I420VideoFrame* render, 206 bool dropped, 207 int64_t send_time_ms, 208 int64_t recv_time_ms, 209 int64_t render_time_ms) 210 : dropped(dropped), 211 send_time_ms(send_time_ms), 212 recv_time_ms(recv_time_ms), 213 render_time_ms(render_time_ms) { 214 this->reference.CopyFrame(*reference); 215 this->render.CopyFrame(*render); 216 } 217 218 FrameComparison(const FrameComparison& compare) 219 : dropped(compare.dropped), 220 send_time_ms(compare.send_time_ms), 221 recv_time_ms(compare.recv_time_ms), 222 render_time_ms(compare.render_time_ms) { 223 this->reference.CopyFrame(compare.reference); 224 this->render.CopyFrame(compare.render); 225 } 226 227 ~FrameComparison() {} 228 229 I420VideoFrame reference; 230 I420VideoFrame render; 231 bool dropped; 232 int64_t send_time_ms; 233 int64_t recv_time_ms; 234 int64_t render_time_ms; 235 }; 236 237 void AddFrameComparison(const I420VideoFrame* reference, 238 const I420VideoFrame* render, 239 bool dropped, 240 int64_t render_time_ms) 241 EXCLUSIVE_LOCKS_REQUIRED(crit_) { 242 int64_t send_time_ms = send_times_[reference->timestamp()]; 243 send_times_.erase(reference->timestamp()); 244 int64_t recv_time_ms = recv_times_[reference->timestamp()]; 245 recv_times_.erase(reference->timestamp()); 246 247 CriticalSectionScoped crit(comparison_lock_.get()); 248 comparisons_.push_back(FrameComparison(reference, 249 render, 250 dropped, 251 send_time_ms, 252 recv_time_ms, 253 render_time_ms)); 254 } 255 256 static bool FrameComparisonThread(void* obj) { 257 return static_cast<VideoAnalyzer*>(obj)->CompareFrames(); 258 } 259 260 bool CompareFrames() { 261 assert(frames_left_ > 0); 262 263 I420VideoFrame reference; 264 I420VideoFrame render; 265 bool dropped; 266 int64_t send_time_ms; 267 int64_t recv_time_ms; 268 int64_t render_time_ms; 269 270 SleepMs(10); 271 272 while (true) { 273 { 274 CriticalSectionScoped crit(comparison_lock_.get()); 275 if (comparisons_.empty()) 276 return true; 277 reference.SwapFrame(&comparisons_.front().reference); 278 render.SwapFrame(&comparisons_.front().render); 279 dropped = comparisons_.front().dropped; 280 send_time_ms = comparisons_.front().send_time_ms; 281 recv_time_ms = comparisons_.front().recv_time_ms; 282 render_time_ms = comparisons_.front().render_time_ms; 283 comparisons_.pop_front(); 284 } 285 286 PerformFrameComparison(&reference, 287 &render, 288 dropped, 289 send_time_ms, 290 recv_time_ms, 291 render_time_ms); 292 293 if (--frames_left_ == 0) { 294 PrintResult("psnr", psnr_, " dB"); 295 PrintResult("ssim", ssim_, ""); 296 PrintResult("sender_time", sender_time_, " ms"); 297 printf( 298 "RESULT dropped_frames: %s = %d\n", test_label_, dropped_frames_); 299 PrintResult("receiver_time", receiver_time_, " ms"); 300 PrintResult("total_delay_incl_network", end_to_end_, " ms"); 301 PrintResult("time_between_rendered_frames", rendered_delta_, " ms"); 302 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_); 303 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_); 304 done_->Set(); 305 306 return false; 307 } 308 } 309 } 310 311 void PerformFrameComparison(const I420VideoFrame* reference, 312 const I420VideoFrame* render, 313 bool dropped, 314 int64_t send_time_ms, 315 int64_t recv_time_ms, 316 int64_t render_time_ms) { 317 psnr_.AddSample(I420PSNR(reference, render)); 318 ssim_.AddSample(I420SSIM(reference, render)); 319 if (dropped) { 320 ++dropped_frames_; 321 return; 322 } 323 if (last_render_time_ != 0) 324 rendered_delta_.AddSample(render_time_ms - last_render_time_); 325 last_render_time_ = render_time_ms; 326 327 int64_t input_time_ms = reference->render_time_ms(); 328 sender_time_.AddSample(send_time_ms - input_time_ms); 329 receiver_time_.AddSample(render_time_ms - recv_time_ms); 330 end_to_end_.AddSample(render_time_ms - input_time_ms); 331 } 332 333 void PrintResult(const char* result_type, 334 test::Statistics stats, 335 const char* unit) { 336 printf("RESULT %s: %s = {%f, %f}%s\n", 337 result_type, 338 test_label_, 339 stats.Mean(), 340 stats.StandardDeviation(), 341 unit); 342 } 343 344 const char* const test_label_; 345 test::Statistics sender_time_; 346 test::Statistics receiver_time_; 347 test::Statistics psnr_; 348 test::Statistics ssim_; 349 test::Statistics end_to_end_; 350 test::Statistics rendered_delta_; 351 int frames_left_; 352 int dropped_frames_; 353 int64_t last_render_time_; 354 uint32_t rtp_timestamp_delta_; 355 356 const scoped_ptr<CriticalSectionWrapper> crit_; 357 std::deque<I420VideoFrame*> frames_ GUARDED_BY(crit_); 358 std::deque<I420VideoFrame*> frame_pool_ GUARDED_BY(crit_); 359 I420VideoFrame last_rendered_frame_ GUARDED_BY(crit_); 360 std::map<uint32_t, int64_t> send_times_ GUARDED_BY(crit_); 361 std::map<uint32_t, int64_t> recv_times_ GUARDED_BY(crit_); 362 I420VideoFrame* first_send_frame_ GUARDED_BY(crit_); 363 double avg_psnr_threshold_ GUARDED_BY(crit_); 364 double avg_ssim_threshold_ GUARDED_BY(crit_); 365 366 const scoped_ptr<CriticalSectionWrapper> comparison_lock_; 367 const scoped_ptr<ThreadWrapper> comparison_thread_; 368 std::deque<FrameComparison> comparisons_ GUARDED_BY(comparison_lock_); 369 const scoped_ptr<EventWrapper> done_; 370 }; 371 372 void FullStackTest::RunTest(const FullStackTestParams& params) { 373 test::DirectTransport send_transport(params.link); 374 test::DirectTransport recv_transport(params.link); 375 VideoAnalyzer analyzer(NULL, 376 &send_transport, 377 params.test_label, 378 params.avg_psnr_threshold, 379 params.avg_ssim_threshold, 380 kFullStackTestDurationSecs * params.clip.fps); 381 382 CreateCalls(Call::Config(&analyzer), Call::Config(&recv_transport)); 383 384 analyzer.SetReceiver(receiver_call_->Receiver()); 385 send_transport.SetReceiver(&analyzer); 386 recv_transport.SetReceiver(sender_call_->Receiver()); 387 388 CreateSendConfig(1); 389 390 scoped_ptr<VideoEncoder> encoder( 391 VideoEncoder::Create(VideoEncoder::kVp8)); 392 send_config_.encoder_settings.encoder = encoder.get(); 393 send_config_.encoder_settings.payload_name = "VP8"; 394 send_config_.encoder_settings.payload_type = 124; 395 396 VideoStream* stream = &encoder_config_.streams[0]; 397 stream->width = params.clip.width; 398 stream->height = params.clip.height; 399 stream->min_bitrate_bps = params.min_bitrate_bps; 400 stream->target_bitrate_bps = params.target_bitrate_bps; 401 stream->max_bitrate_bps = params.max_bitrate_bps; 402 stream->max_framerate = params.clip.fps; 403 404 CreateMatchingReceiveConfigs(); 405 receive_configs_[0].renderer = &analyzer; 406 407 CreateStreams(); 408 analyzer.input_ = send_stream_->Input(); 409 410 frame_generator_capturer_.reset( 411 test::FrameGeneratorCapturer::CreateFromYuvFile( 412 &analyzer, 413 test::ResourcePath(params.clip.name, "yuv").c_str(), 414 params.clip.width, 415 params.clip.height, 416 params.clip.fps, 417 Clock::GetRealTimeClock())); 418 419 ASSERT_TRUE(frame_generator_capturer_.get() != NULL) 420 << "Could not create capturer for " << params.clip.name 421 << ".yuv. Is this resource file present?"; 422 423 Start(); 424 425 analyzer.Wait(); 426 427 send_transport.StopSending(); 428 recv_transport.StopSending(); 429 430 Stop(); 431 432 DestroyStreams(); 433 } 434 435 TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) { 436 FullStackTestParams paris_qcif = {"net_delay_0_0_plr_0", 437 {"paris_qcif", 176, 144, 30}, 438 300000, 439 300000, 440 300000, 441 36.0, 442 0.96 443 }; 444 RunTest(paris_qcif); 445 } 446 447 TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) { 448 // TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif. 449 FullStackTestParams foreman_cif = {"foreman_cif_net_delay_0_0_plr_0", 450 {"foreman_cif", 352, 288, 30}, 451 700000, 452 700000, 453 700000, 454 0.0, 455 0.0 456 }; 457 RunTest(foreman_cif); 458 } 459 460 TEST_F(FullStackTest, ForemanCif500kbps) { 461 FullStackTestParams foreman_cif = {"foreman_cif_500kbps", 462 {"foreman_cif", 352, 288, 30}, 463 30000, 464 500000, 465 2000000, 466 0.0, 467 0.0 468 }; 469 foreman_cif.link.queue_length_packets = 0; 470 foreman_cif.link.queue_delay_ms = 0; 471 foreman_cif.link.link_capacity_kbps = 500; 472 RunTest(foreman_cif); 473 } 474 475 TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) { 476 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_32pkts_queue", 477 {"foreman_cif", 352, 288, 30}, 478 30000, 479 500000, 480 2000000, 481 0.0, 482 0.0 483 }; 484 foreman_cif.link.queue_length_packets = 32; 485 foreman_cif.link.queue_delay_ms = 0; 486 foreman_cif.link.link_capacity_kbps = 500; 487 RunTest(foreman_cif); 488 } 489 490 TEST_F(FullStackTest, ForemanCif500kbps100ms) { 491 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms", 492 {"foreman_cif", 352, 288, 30}, 493 30000, 494 500000, 495 2000000, 496 0.0, 497 0.0 498 }; 499 foreman_cif.link.queue_length_packets = 0; 500 foreman_cif.link.queue_delay_ms = 100; 501 foreman_cif.link.link_capacity_kbps = 500; 502 RunTest(foreman_cif); 503 } 504 505 TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) { 506 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms_32pkts_queue", 507 {"foreman_cif", 352, 288, 30}, 508 30000, 509 500000, 510 2000000, 511 0.0, 512 0.0 513 }; 514 foreman_cif.link.queue_length_packets = 32; 515 foreman_cif.link.queue_delay_ms = 100; 516 foreman_cif.link.link_capacity_kbps = 500; 517 RunTest(foreman_cif); 518 } 519 520 TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) { 521 FullStackTestParams foreman_cif = {"foreman_cif_1000kbps_100ms_32pkts_queue", 522 {"foreman_cif", 352, 288, 30}, 523 30000, 524 2000000, 525 2000000, 526 0.0, 527 0.0 528 }; 529 foreman_cif.link.queue_length_packets = 32; 530 foreman_cif.link.queue_delay_ms = 100; 531 foreman_cif.link.link_capacity_kbps = 1000; 532 RunTest(foreman_cif); 533 } 534 } // namespace webrtc 535