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/call.h" 18 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 19 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" 20 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.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/system_wrappers/interface/thread_annotations.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 uint32_t kSendSsrc = 0x654321; 38 static const int kFullStackTestDurationSecs = 10; 39 40 struct FullStackTestParams { 41 const char* test_label; 42 struct { 43 const char* name; 44 size_t width, height; 45 int fps; 46 } clip; 47 unsigned int bitrate; 48 double avg_psnr_threshold; 49 double avg_ssim_threshold; 50 }; 51 52 FullStackTestParams paris_qcif = { 53 "net_delay_0_0_plr_0", {"paris_qcif", 176, 144, 30}, 300, 36.0, 0.96}; 54 55 // TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif. 56 FullStackTestParams foreman_cif = { 57 "foreman_cif_net_delay_0_0_plr_0", 58 {"foreman_cif", 352, 288, 30}, 59 700, 60 0.0, 61 0.0}; 62 63 class FullStackTest : public ::testing::TestWithParam<FullStackTestParams> { 64 protected: 65 std::map<uint32_t, bool> reserved_ssrcs_; 66 }; 67 68 class VideoAnalyzer : public PacketReceiver, 69 public newapi::Transport, 70 public VideoRenderer, 71 public VideoSendStreamInput { 72 public: 73 VideoAnalyzer(VideoSendStreamInput* input, 74 Transport* transport, 75 const char* test_label, 76 double avg_psnr_threshold, 77 double avg_ssim_threshold, 78 int duration_frames) 79 : input_(input), 80 transport_(transport), 81 receiver_(NULL), 82 test_label_(test_label), 83 frames_left_(duration_frames), 84 dropped_frames_(0), 85 last_render_time_(0), 86 rtp_timestamp_delta_(0), 87 crit_(CriticalSectionWrapper::CreateCriticalSection()), 88 first_send_frame_(NULL), 89 avg_psnr_threshold_(avg_psnr_threshold), 90 avg_ssim_threshold_(avg_ssim_threshold), 91 comparison_lock_(CriticalSectionWrapper::CreateCriticalSection()), 92 comparison_thread_(ThreadWrapper::CreateThread(&FrameComparisonThread, 93 this)), 94 done_(EventWrapper::Create()) { 95 unsigned int id; 96 EXPECT_TRUE(comparison_thread_->Start(id)); 97 } 98 99 ~VideoAnalyzer() { 100 EXPECT_TRUE(comparison_thread_->Stop()); 101 102 while (!frames_.empty()) { 103 delete frames_.back(); 104 frames_.pop_back(); 105 } 106 while (!frame_pool_.empty()) { 107 delete frame_pool_.back(); 108 frame_pool_.pop_back(); 109 } 110 } 111 112 virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; } 113 114 virtual DeliveryStatus DeliverPacket(const uint8_t* packet, 115 size_t length) OVERRIDE { 116 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); 117 RTPHeader header; 118 parser->Parse(packet, static_cast<int>(length), &header); 119 { 120 CriticalSectionScoped lock(crit_.get()); 121 recv_times_[header.timestamp - rtp_timestamp_delta_] = 122 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 123 } 124 125 return receiver_->DeliverPacket(packet, length); 126 } 127 128 virtual void SwapFrame(I420VideoFrame* video_frame) OVERRIDE { 129 I420VideoFrame* copy = NULL; 130 { 131 CriticalSectionScoped lock(crit_.get()); 132 if (frame_pool_.size() > 0) { 133 copy = frame_pool_.front(); 134 frame_pool_.pop_front(); 135 } 136 } 137 if (copy == NULL) 138 copy = new I420VideoFrame(); 139 140 copy->CopyFrame(*video_frame); 141 copy->set_timestamp(copy->render_time_ms() * 90); 142 143 { 144 CriticalSectionScoped lock(crit_.get()); 145 if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0) 146 first_send_frame_ = copy; 147 148 frames_.push_back(copy); 149 } 150 151 input_->SwapFrame(video_frame); 152 } 153 154 virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { 155 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); 156 RTPHeader header; 157 parser->Parse(packet, static_cast<int>(length), &header); 158 159 { 160 CriticalSectionScoped lock(crit_.get()); 161 if (rtp_timestamp_delta_ == 0) { 162 rtp_timestamp_delta_ = 163 header.timestamp - first_send_frame_->timestamp(); 164 first_send_frame_ = NULL; 165 } 166 send_times_[header.timestamp - rtp_timestamp_delta_] = 167 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 168 } 169 170 return transport_->SendRtp(packet, length); 171 } 172 173 virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { 174 return transport_->SendRtcp(packet, length); 175 } 176 177 virtual void RenderFrame(const I420VideoFrame& video_frame, 178 int time_to_render_ms) OVERRIDE { 179 int64_t render_time_ms = 180 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); 181 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_; 182 183 CriticalSectionScoped lock(crit_.get()); 184 while (frames_.front()->timestamp() < send_timestamp) { 185 AddFrameComparison( 186 frames_.front(), &last_rendered_frame_, true, render_time_ms); 187 frame_pool_.push_back(frames_.front()); 188 frames_.pop_front(); 189 } 190 191 I420VideoFrame* reference_frame = frames_.front(); 192 frames_.pop_front(); 193 assert(reference_frame != NULL); 194 EXPECT_EQ(reference_frame->timestamp(), send_timestamp); 195 assert(reference_frame->timestamp() == send_timestamp); 196 197 AddFrameComparison(reference_frame, &video_frame, false, render_time_ms); 198 frame_pool_.push_back(reference_frame); 199 200 last_rendered_frame_.CopyFrame(video_frame); 201 } 202 203 void Wait() { done_->Wait(120 * 1000); } 204 205 VideoSendStreamInput* input_; 206 Transport* transport_; 207 PacketReceiver* receiver_; 208 209 private: 210 struct FrameComparison { 211 FrameComparison(const I420VideoFrame* reference, 212 const I420VideoFrame* render, 213 bool dropped, 214 int64_t send_time_ms, 215 int64_t recv_time_ms, 216 int64_t render_time_ms) 217 : dropped(dropped), 218 send_time_ms(send_time_ms), 219 recv_time_ms(recv_time_ms), 220 render_time_ms(render_time_ms) { 221 this->reference.CopyFrame(*reference); 222 this->render.CopyFrame(*render); 223 } 224 225 FrameComparison(const FrameComparison& compare) 226 : dropped(compare.dropped), 227 send_time_ms(compare.send_time_ms), 228 recv_time_ms(compare.recv_time_ms), 229 render_time_ms(compare.render_time_ms) { 230 this->reference.CopyFrame(compare.reference); 231 this->render.CopyFrame(compare.render); 232 } 233 234 ~FrameComparison() {} 235 236 I420VideoFrame reference; 237 I420VideoFrame render; 238 bool dropped; 239 int64_t send_time_ms; 240 int64_t recv_time_ms; 241 int64_t render_time_ms; 242 }; 243 244 void AddFrameComparison(const I420VideoFrame* reference, 245 const I420VideoFrame* render, 246 bool dropped, 247 int64_t render_time_ms) 248 EXCLUSIVE_LOCKS_REQUIRED(crit_) { 249 int64_t send_time_ms = send_times_[reference->timestamp()]; 250 send_times_.erase(reference->timestamp()); 251 int64_t recv_time_ms = recv_times_[reference->timestamp()]; 252 recv_times_.erase(reference->timestamp()); 253 254 CriticalSectionScoped crit(comparison_lock_.get()); 255 comparisons_.push_back(FrameComparison(reference, 256 render, 257 dropped, 258 send_time_ms, 259 recv_time_ms, 260 render_time_ms)); 261 } 262 263 static bool FrameComparisonThread(void* obj) { 264 return static_cast<VideoAnalyzer*>(obj)->CompareFrames(); 265 } 266 267 bool CompareFrames() { 268 assert(frames_left_ > 0); 269 270 I420VideoFrame reference; 271 I420VideoFrame render; 272 bool dropped; 273 int64_t send_time_ms; 274 int64_t recv_time_ms; 275 int64_t render_time_ms; 276 277 SleepMs(10); 278 279 while (true) { 280 { 281 CriticalSectionScoped crit(comparison_lock_.get()); 282 if (comparisons_.empty()) 283 return true; 284 reference.SwapFrame(&comparisons_.front().reference); 285 render.SwapFrame(&comparisons_.front().render); 286 dropped = comparisons_.front().dropped; 287 send_time_ms = comparisons_.front().send_time_ms; 288 recv_time_ms = comparisons_.front().recv_time_ms; 289 render_time_ms = comparisons_.front().render_time_ms; 290 comparisons_.pop_front(); 291 } 292 293 PerformFrameComparison(&reference, 294 &render, 295 dropped, 296 send_time_ms, 297 recv_time_ms, 298 render_time_ms); 299 300 if (--frames_left_ == 0) { 301 PrintResult("psnr", psnr_, " dB"); 302 PrintResult("ssim", ssim_, ""); 303 PrintResult("sender_time", sender_time_, " ms"); 304 printf( 305 "RESULT dropped_frames: %s = %d\n", test_label_, dropped_frames_); 306 PrintResult("receiver_time", receiver_time_, " ms"); 307 PrintResult("total_delay_incl_network", end_to_end_, " ms"); 308 PrintResult("time_between_rendered_frames", rendered_delta_, " ms"); 309 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_); 310 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_); 311 done_->Set(); 312 313 return false; 314 } 315 } 316 } 317 318 void PerformFrameComparison(const I420VideoFrame* reference, 319 const I420VideoFrame* render, 320 bool dropped, 321 int64_t send_time_ms, 322 int64_t recv_time_ms, 323 int64_t render_time_ms) { 324 psnr_.AddSample(I420PSNR(reference, render)); 325 ssim_.AddSample(I420SSIM(reference, render)); 326 if (dropped) { 327 ++dropped_frames_; 328 return; 329 } 330 if (last_render_time_ != 0) 331 rendered_delta_.AddSample(render_time_ms - last_render_time_); 332 last_render_time_ = render_time_ms; 333 334 int64_t input_time_ms = reference->render_time_ms(); 335 sender_time_.AddSample(send_time_ms - input_time_ms); 336 receiver_time_.AddSample(render_time_ms - recv_time_ms); 337 end_to_end_.AddSample(render_time_ms - input_time_ms); 338 } 339 340 void PrintResult(const char* result_type, 341 test::Statistics stats, 342 const char* unit) { 343 printf("RESULT %s: %s = {%f, %f}%s\n", 344 result_type, 345 test_label_, 346 stats.Mean(), 347 stats.StandardDeviation(), 348 unit); 349 } 350 351 const char* const test_label_; 352 test::Statistics sender_time_; 353 test::Statistics receiver_time_; 354 test::Statistics psnr_; 355 test::Statistics ssim_; 356 test::Statistics end_to_end_; 357 test::Statistics rendered_delta_; 358 int frames_left_; 359 int dropped_frames_; 360 int64_t last_render_time_; 361 uint32_t rtp_timestamp_delta_; 362 363 const scoped_ptr<CriticalSectionWrapper> crit_; 364 std::deque<I420VideoFrame*> frames_ GUARDED_BY(crit_); 365 std::deque<I420VideoFrame*> frame_pool_ GUARDED_BY(crit_); 366 I420VideoFrame last_rendered_frame_ GUARDED_BY(crit_); 367 std::map<uint32_t, int64_t> send_times_ GUARDED_BY(crit_); 368 std::map<uint32_t, int64_t> recv_times_ GUARDED_BY(crit_); 369 I420VideoFrame* first_send_frame_ GUARDED_BY(crit_); 370 double avg_psnr_threshold_ GUARDED_BY(crit_); 371 double avg_ssim_threshold_ GUARDED_BY(crit_); 372 373 const scoped_ptr<CriticalSectionWrapper> comparison_lock_; 374 const scoped_ptr<ThreadWrapper> comparison_thread_; 375 std::deque<FrameComparison> comparisons_ GUARDED_BY(comparison_lock_); 376 const scoped_ptr<EventWrapper> done_; 377 }; 378 379 TEST_P(FullStackTest, NoPacketLoss) { 380 static const uint32_t kReceiverLocalSsrc = 0x123456; 381 FullStackTestParams params = GetParam(); 382 383 test::DirectTransport transport; 384 VideoAnalyzer analyzer(NULL, 385 &transport, 386 params.test_label, 387 params.avg_psnr_threshold, 388 params.avg_ssim_threshold, 389 kFullStackTestDurationSecs * params.clip.fps); 390 391 Call::Config call_config(&analyzer); 392 393 scoped_ptr<Call> call(Call::Create(call_config)); 394 analyzer.SetReceiver(call->Receiver()); 395 transport.SetReceiver(&analyzer); 396 397 VideoSendStream::Config send_config = call->GetDefaultSendConfig(); 398 send_config.rtp.ssrcs.push_back(kSendSsrc); 399 400 scoped_ptr<VP8Encoder> encoder(VP8Encoder::Create()); 401 send_config.encoder_settings.encoder = encoder.get(); 402 send_config.encoder_settings.payload_name = "VP8"; 403 send_config.encoder_settings.payload_type = 124; 404 std::vector<VideoStream> video_streams = test::CreateVideoStreams(1); 405 VideoStream* stream = &video_streams[0]; 406 stream->width = params.clip.width; 407 stream->height = params.clip.height; 408 stream->min_bitrate_bps = stream->target_bitrate_bps = 409 stream->max_bitrate_bps = params.bitrate * 1000; 410 stream->max_framerate = params.clip.fps; 411 412 VideoSendStream* send_stream = 413 call->CreateVideoSendStream(send_config, video_streams, NULL); 414 analyzer.input_ = send_stream->Input(); 415 416 scoped_ptr<test::FrameGeneratorCapturer> file_capturer( 417 test::FrameGeneratorCapturer::CreateFromYuvFile( 418 &analyzer, 419 test::ResourcePath(params.clip.name, "yuv").c_str(), 420 params.clip.width, 421 params.clip.height, 422 params.clip.fps, 423 Clock::GetRealTimeClock())); 424 ASSERT_TRUE(file_capturer.get() != NULL) 425 << "Could not create capturer for " << params.clip.name 426 << ".yuv. Is this resource file present?"; 427 428 VideoReceiveStream::Config receive_config = call->GetDefaultReceiveConfig(); 429 VideoCodec codec = 430 test::CreateDecoderVideoCodec(send_config.encoder_settings); 431 receive_config.codecs.push_back(codec); 432 receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0]; 433 receive_config.rtp.local_ssrc = kReceiverLocalSsrc; 434 receive_config.renderer = &analyzer; 435 436 VideoReceiveStream* receive_stream = 437 call->CreateVideoReceiveStream(receive_config); 438 439 receive_stream->Start(); 440 send_stream->Start(); 441 file_capturer->Start(); 442 443 analyzer.Wait(); 444 445 file_capturer->Stop(); 446 send_stream->Stop(); 447 receive_stream->Stop(); 448 449 call->DestroyVideoReceiveStream(receive_stream); 450 call->DestroyVideoSendStream(send_stream); 451 452 transport.StopSending(); 453 } 454 455 INSTANTIATE_TEST_CASE_P(FullStack, 456 FullStackTest, 457 ::testing::Values(paris_qcif, foreman_cif)); 458 459 } // namespace webrtc 460