blob: e728a003555403c54012e6a6b166800142632bb2 [file] [log] [blame]
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +00001/*
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
pbos@webrtc.org69215d82013-07-10 15:02:02 +000015#include "testing/gtest/include/gtest/gtest.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000016
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +000017#include "webrtc/base/scoped_ptr.h"
pbos@webrtc.org38344ed2014-09-24 06:05:00 +000018#include "webrtc/base/thread_annotations.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000019#include "webrtc/call.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000020#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
21#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
22#include "webrtc/system_wrappers/interface/clock.h"
sprang@webrtc.org131bea82015-02-18 12:46:06 +000023#include "webrtc/system_wrappers/interface/cpu_info.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000024#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
25#include "webrtc/system_wrappers/interface/event_wrapper.h"
pbos@webrtc.org94015242013-10-16 11:05:37 +000026#include "webrtc/system_wrappers/interface/sleep.h"
pbos@webrtc.org994d0b72014-06-27 08:47:52 +000027#include "webrtc/test/call_test.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000028#include "webrtc/test/direct_transport.h"
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +000029#include "webrtc/test/encoder_settings.h"
30#include "webrtc/test/fake_encoder.h"
sprang@webrtc.org131bea82015-02-18 12:46:06 +000031#include "webrtc/test/frame_generator.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000032#include "webrtc/test/frame_generator_capturer.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000033#include "webrtc/test/statistics.h"
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +000034#include "webrtc/test/testsupport/fileutils.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000035#include "webrtc/typedefs.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000036
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000037namespace webrtc {
38
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +000039static const int kFullStackTestDurationSecs = 60;
pbos@webrtc.orgb613b5a2013-12-03 10:13:04 +000040
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000041struct FullStackTestParams {
42 const char* test_label;
43 struct {
44 const char* name;
pbos@webrtc.orgf3f13582013-07-09 14:04:46 +000045 size_t width, height;
46 int fps;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000047 } clip;
sprang@webrtc.org131bea82015-02-18 12:46:06 +000048 bool screenshare;
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +000049 int min_bitrate_bps;
50 int target_bitrate_bps;
51 int max_bitrate_bps;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000052 double avg_psnr_threshold;
53 double avg_ssim_threshold;
sprang@webrtc.org131bea82015-02-18 12:46:06 +000054 int test_durations_secs;
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +000055 FakeNetworkPipe::Config link;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000056};
57
pbos@webrtc.org994d0b72014-06-27 08:47:52 +000058class FullStackTest : public test::CallTest {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000059 protected:
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +000060 void RunTest(const FullStackTestParams& params);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000061};
62
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000063class VideoAnalyzer : public PacketReceiver,
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000064 public newapi::Transport,
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000065 public VideoRenderer,
66 public VideoSendStreamInput {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000067 public:
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000068 VideoAnalyzer(VideoSendStreamInput* input,
69 Transport* transport,
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000070 const char* test_label,
71 double avg_psnr_threshold,
72 double avg_ssim_threshold,
pbos@webrtc.org94015242013-10-16 11:05:37 +000073 int duration_frames)
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000074 : input_(input),
75 transport_(transport),
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000076 receiver_(NULL),
77 test_label_(test_label),
sprang@webrtc.org131bea82015-02-18 12:46:06 +000078 frames_to_process_(duration_frames),
79 frames_recorded_(0),
80 frames_processed_(0),
pbos@webrtc.org94015242013-10-16 11:05:37 +000081 dropped_frames_(0),
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000082 last_render_time_(0),
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +000083 rtp_timestamp_delta_(0),
84 crit_(CriticalSectionWrapper::CreateCriticalSection()),
85 first_send_frame_(NULL),
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000086 avg_psnr_threshold_(avg_psnr_threshold),
87 avg_ssim_threshold_(avg_ssim_threshold),
pbos@webrtc.org94015242013-10-16 11:05:37 +000088 comparison_lock_(CriticalSectionWrapper::CreateCriticalSection()),
sprang@webrtc.org131bea82015-02-18 12:46:06 +000089 comparison_available_event_(EventWrapper::Create()),
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +000090 done_(EventWrapper::Create()) {
sprang@webrtc.org131bea82015-02-18 12:46:06 +000091 // Create thread pool for CPU-expensive PSNR/SSIM calculations.
92
93 // Try to use about as many threads as cores, but leave kMinCoresLeft alone,
94 // so that we don't accidentally starve "real" worker threads (codec etc).
95 // Also, don't allocate more than kMaxComparisonThreads, even if there are
96 // spare cores.
97
98 uint32_t num_cores = CpuInfo::DetectNumberOfCores();
99 assert(num_cores >= 1);
100 static const uint32_t kMinCoresLeft = 4;
sprang@webrtc.org343096a2015-02-23 08:34:17 +0000101 static const uint32_t kMaxComparisonThreads = 8;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000102
103 if (num_cores <= kMinCoresLeft) {
104 num_cores = 1;
105 } else {
106 num_cores -= kMinCoresLeft;
107 num_cores = std::min(num_cores, kMaxComparisonThreads);
108 }
109
110 for (uint32_t i = 0; i < num_cores; ++i) {
111 ThreadWrapper* thread =
112 ThreadWrapper::CreateThread(&FrameComparisonThread, this);
113 comparison_thread_pool_.push_back(thread);
pbos@webrtc.org86639732015-03-13 00:06:21 +0000114 EXPECT_TRUE(thread->Start());
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000115 }
pbos@webrtc.org94015242013-10-16 11:05:37 +0000116 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000117
118 ~VideoAnalyzer() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000119 for (ThreadWrapper* thread : comparison_thread_pool_) {
120 EXPECT_TRUE(thread->Stop());
121 delete thread;
122 }
pbos@webrtc.org94015242013-10-16 11:05:37 +0000123
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000124 for (I420VideoFrame* frame : frames_)
125 delete frame;
126
127 for (I420VideoFrame* frame : frame_pool_)
128 delete frame;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000129 }
130
pbos@webrtc.org94015242013-10-16 11:05:37 +0000131 virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; }
132
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000133 DeliveryStatus DeliverPacket(const uint8_t* packet, size_t length) override {
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000134 rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000135 RTPHeader header;
pbos@webrtc.org62bafae2014-07-08 12:10:51 +0000136 parser->Parse(packet, length, &header);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000137 {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000138 CriticalSectionScoped lock(crit_.get());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000139 recv_times_[header.timestamp - rtp_timestamp_delta_] =
140 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
141 }
142
143 return receiver_->DeliverPacket(packet, length);
144 }
145
magjed@webrtc.orgd7452a02015-03-10 15:12:26 +0000146 void SwapFrame(I420VideoFrame* video_frame) override {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000147 I420VideoFrame* copy = NULL;
148 {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000149 CriticalSectionScoped lock(crit_.get());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000150 if (frame_pool_.size() > 0) {
151 copy = frame_pool_.front();
152 frame_pool_.pop_front();
153 }
154 }
155 if (copy == NULL)
156 copy = new I420VideoFrame();
157
magjed@webrtc.orgd7452a02015-03-10 15:12:26 +0000158 copy->CopyFrame(*video_frame);
159 copy->set_timestamp(copy->render_time_ms() * 90);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000160
161 {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000162 CriticalSectionScoped lock(crit_.get());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000163 if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0)
164 first_send_frame_ = copy;
165
166 frames_.push_back(copy);
167 }
168
magjed@webrtc.orgd7452a02015-03-10 15:12:26 +0000169 input_->SwapFrame(video_frame);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000170 }
171
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000172 bool SendRtp(const uint8_t* packet, size_t length) override {
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000173 rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000174 RTPHeader header;
pbos@webrtc.org62bafae2014-07-08 12:10:51 +0000175 parser->Parse(packet, length, &header);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000176
177 {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000178 CriticalSectionScoped lock(crit_.get());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000179 if (rtp_timestamp_delta_ == 0) {
180 rtp_timestamp_delta_ =
181 header.timestamp - first_send_frame_->timestamp();
182 first_send_frame_ = NULL;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000183 }
pbos@webrtc.org7fb9ce02013-08-05 09:29:50 +0000184 send_times_[header.timestamp - rtp_timestamp_delta_] =
185 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000186 }
187
pbos@webrtc.org27326b62013-11-20 12:17:04 +0000188 return transport_->SendRtp(packet, length);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000189 }
190
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000191 bool SendRtcp(const uint8_t* packet, size_t length) override {
pbos@webrtc.org27326b62013-11-20 12:17:04 +0000192 return transport_->SendRtcp(packet, length);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000193 }
194
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000195 void RenderFrame(const I420VideoFrame& video_frame,
196 int time_to_render_ms) override {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000197 int64_t render_time_ms =
198 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000199 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_;
200
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000201 CriticalSectionScoped lock(crit_.get());
202 while (frames_.front()->timestamp() < send_timestamp) {
203 AddFrameComparison(
204 frames_.front(), &last_rendered_frame_, true, render_time_ms);
205 frame_pool_.push_back(frames_.front());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000206 frames_.pop_front();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000207 }
208
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000209 I420VideoFrame* reference_frame = frames_.front();
210 frames_.pop_front();
211 assert(reference_frame != NULL);
212 EXPECT_EQ(reference_frame->timestamp(), send_timestamp);
213 assert(reference_frame->timestamp() == send_timestamp);
214
215 AddFrameComparison(reference_frame, &video_frame, false, render_time_ms);
216 frame_pool_.push_back(reference_frame);
217
pbos@webrtc.org94015242013-10-16 11:05:37 +0000218 last_rendered_frame_.CopyFrame(video_frame);
219 }
220
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000221 bool IsTextureSupported() const override { return false; }
pbos@webrtc.org0d852d52015-02-09 15:14:36 +0000222
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000223 void Wait() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000224 // Frame comparisons can be very expensive. Wait for test to be done, but
225 // at time-out check if frames_processed is going up. If so, give it more
226 // time, otherwise fail. Hopefully this will reduce test flakiness.
227
228 int last_frames_processed = -1;
229 EventTypeWrapper eventType;
230 while ((eventType = done_->Wait(FullStackTest::kDefaultTimeoutMs)) !=
231 kEventSignaled) {
232 int frames_processed;
233 {
234 CriticalSectionScoped crit(comparison_lock_.get());
235 frames_processed = frames_processed_;
236 }
237 if (last_frames_processed == -1) {
238 last_frames_processed = frames_processed;
239 continue;
240 }
241 ASSERT_GT(frames_processed, last_frames_processed)
242 << "Analyzer stalled while waiting for test to finish.";
243 last_frames_processed = frames_processed;
244 }
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000245 }
pbos@webrtc.org94015242013-10-16 11:05:37 +0000246
247 VideoSendStreamInput* input_;
248 Transport* transport_;
249 PacketReceiver* receiver_;
250
251 private:
252 struct FrameComparison {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000253 FrameComparison()
254 : dropped(false), send_time_ms(0), recv_time_ms(0), render_time_ms(0) {}
255
pbos@webrtc.org94015242013-10-16 11:05:37 +0000256 FrameComparison(const I420VideoFrame* reference,
257 const I420VideoFrame* render,
258 bool dropped,
259 int64_t send_time_ms,
260 int64_t recv_time_ms,
261 int64_t render_time_ms)
262 : dropped(dropped),
263 send_time_ms(send_time_ms),
264 recv_time_ms(recv_time_ms),
265 render_time_ms(render_time_ms) {
266 this->reference.CopyFrame(*reference);
267 this->render.CopyFrame(*render);
268 }
269
270 FrameComparison(const FrameComparison& compare)
271 : dropped(compare.dropped),
272 send_time_ms(compare.send_time_ms),
273 recv_time_ms(compare.recv_time_ms),
274 render_time_ms(compare.render_time_ms) {
275 this->reference.CopyFrame(compare.reference);
276 this->render.CopyFrame(compare.render);
277 }
278
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000279 void CloneStatsAndSwapFrames(FrameComparison* comparison) {
280 reference.SwapFrame(&comparison->reference);
281 render.SwapFrame(&comparison->render);
282 dropped = comparison->dropped;
283 send_time_ms = comparison->send_time_ms;
284 recv_time_ms = comparison->recv_time_ms;
285 render_time_ms = comparison->render_time_ms;
286 }
287
pbos@webrtc.org94015242013-10-16 11:05:37 +0000288 ~FrameComparison() {}
289
290 I420VideoFrame reference;
291 I420VideoFrame render;
292 bool dropped;
293 int64_t send_time_ms;
294 int64_t recv_time_ms;
295 int64_t render_time_ms;
296 };
297
298 void AddFrameComparison(const I420VideoFrame* reference,
299 const I420VideoFrame* render,
300 bool dropped,
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000301 int64_t render_time_ms)
302 EXCLUSIVE_LOCKS_REQUIRED(crit_) {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000303 int64_t send_time_ms = send_times_[reference->timestamp()];
304 send_times_.erase(reference->timestamp());
305 int64_t recv_time_ms = recv_times_[reference->timestamp()];
306 recv_times_.erase(reference->timestamp());
307
308 CriticalSectionScoped crit(comparison_lock_.get());
309 comparisons_.push_back(FrameComparison(reference,
310 render,
311 dropped,
312 send_time_ms,
313 recv_time_ms,
314 render_time_ms));
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000315 comparison_available_event_->Set();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000316 }
317
318 static bool FrameComparisonThread(void* obj) {
319 return static_cast<VideoAnalyzer*>(obj)->CompareFrames();
320 }
321
322 bool CompareFrames() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000323 if (AllFramesRecorded())
324 return false;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000325
326 I420VideoFrame reference;
327 I420VideoFrame render;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000328 FrameComparison comparison;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000329
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000330 if (!PopComparison(&comparison)) {
331 // Wait until new comparison task is available, or test is done.
332 // If done, wake up remaining threads waiting.
333 comparison_available_event_->Wait(1000);
334 if (AllFramesRecorded()) {
335 comparison_available_event_->Set();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000336 return false;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000337 }
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000338 return true; // Try again.
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000339 }
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000340
341 PerformFrameComparison(comparison);
342
343 if (FrameProcessed()) {
344 PrintResults();
345 done_->Set();
346 comparison_available_event_->Set();
347 return false;
348 }
349
350 return true;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000351 }
352
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000353 bool PopComparison(FrameComparison* comparison) {
354 CriticalSectionScoped crit(comparison_lock_.get());
355 // If AllFramesRecorded() is true, it means we have already popped
356 // frames_to_process_ frames from comparisons_, so there is no more work
357 // for this thread to be done. frames_processed_ might still be lower if
358 // all comparisons are not done, but those frames are currently being
359 // worked on by other threads.
360 if (comparisons_.empty() || AllFramesRecorded())
361 return false;
362
363 comparison->CloneStatsAndSwapFrames(&comparisons_.front());
364 comparisons_.pop_front();
365
366 FrameRecorded();
367 return true;
368 }
369
370 // Increment counter for number of frames received for comparison.
371 void FrameRecorded() {
372 CriticalSectionScoped crit(comparison_lock_.get());
373 ++frames_recorded_;
374 }
375
376 // Returns true if all frames to be compared have been taken from the queue.
377 bool AllFramesRecorded() {
378 CriticalSectionScoped crit(comparison_lock_.get());
379 assert(frames_recorded_ <= frames_to_process_);
380 return frames_recorded_ == frames_to_process_;
381 }
382
383 // Increase count of number of frames processed. Returns true if this was the
384 // last frame to be processed.
385 bool FrameProcessed() {
386 CriticalSectionScoped crit(comparison_lock_.get());
387 ++frames_processed_;
388 assert(frames_processed_ <= frames_to_process_);
389 return frames_processed_ == frames_to_process_;
390 }
391
392 void PrintResults() {
393 CriticalSectionScoped crit(comparison_lock_.get());
394 PrintResult("psnr", psnr_, " dB");
395 PrintResult("ssim", ssim_, "");
396 PrintResult("sender_time", sender_time_, " ms");
397 printf("RESULT dropped_frames: %s = %d frames\n", test_label_,
398 dropped_frames_);
399 PrintResult("receiver_time", receiver_time_, " ms");
400 PrintResult("total_delay_incl_network", end_to_end_, " ms");
401 PrintResult("time_between_rendered_frames", rendered_delta_, " ms");
402 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_);
403 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_);
404 }
405
406 void PerformFrameComparison(const FrameComparison& comparison) {
407 // Perform expensive psnr and ssim calculations while not holding lock.
408 double psnr = I420PSNR(&comparison.reference, &comparison.render);
409 double ssim = I420SSIM(&comparison.reference, &comparison.render);
410
411 CriticalSectionScoped crit(comparison_lock_.get());
412 psnr_.AddSample(psnr);
413 ssim_.AddSample(ssim);
414 if (comparison.dropped) {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000415 ++dropped_frames_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000416 return;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000417 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000418 if (last_render_time_ != 0)
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000419 rendered_delta_.AddSample(comparison.render_time_ms - last_render_time_);
420 last_render_time_ = comparison.render_time_ms;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000421
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000422 int64_t input_time_ms = comparison.reference.render_time_ms();
423 sender_time_.AddSample(comparison.send_time_ms - input_time_ms);
424 receiver_time_.AddSample(comparison.render_time_ms -
425 comparison.recv_time_ms);
426 end_to_end_.AddSample(comparison.render_time_ms - input_time_ms);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000427 }
428
429 void PrintResult(const char* result_type,
430 test::Statistics stats,
431 const char* unit) {
432 printf("RESULT %s: %s = {%f, %f}%s\n",
433 result_type,
434 test_label_,
435 stats.Mean(),
436 stats.StandardDeviation(),
437 unit);
438 }
439
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000440 const char* const test_label_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000441 test::Statistics sender_time_;
442 test::Statistics receiver_time_;
443 test::Statistics psnr_;
444 test::Statistics ssim_;
445 test::Statistics end_to_end_;
446 test::Statistics rendered_delta_;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000447 const int frames_to_process_;
448 int frames_recorded_;
449 int frames_processed_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000450 int dropped_frames_;
451 int64_t last_render_time_;
452 uint32_t rtp_timestamp_delta_;
453
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000454 const rtc::scoped_ptr<CriticalSectionWrapper> crit_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000455 std::deque<I420VideoFrame*> frames_ GUARDED_BY(crit_);
456 std::deque<I420VideoFrame*> frame_pool_ GUARDED_BY(crit_);
457 I420VideoFrame last_rendered_frame_ GUARDED_BY(crit_);
458 std::map<uint32_t, int64_t> send_times_ GUARDED_BY(crit_);
459 std::map<uint32_t, int64_t> recv_times_ GUARDED_BY(crit_);
460 I420VideoFrame* first_send_frame_ GUARDED_BY(crit_);
pbos@webrtc.orgb35b1362014-10-20 09:14:38 +0000461 const double avg_psnr_threshold_;
462 const double avg_ssim_threshold_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000463
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000464 const rtc::scoped_ptr<CriticalSectionWrapper> comparison_lock_;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000465 std::vector<ThreadWrapper*> comparison_thread_pool_;
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000466 const rtc::scoped_ptr<EventWrapper> comparison_available_event_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000467 std::deque<FrameComparison> comparisons_ GUARDED_BY(comparison_lock_);
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000468 const rtc::scoped_ptr<EventWrapper> done_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000469};
470
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000471void FullStackTest::RunTest(const FullStackTestParams& params) {
472 test::DirectTransport send_transport(params.link);
473 test::DirectTransport recv_transport(params.link);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000474 VideoAnalyzer analyzer(NULL, &send_transport, params.test_label,
475 params.avg_psnr_threshold, params.avg_ssim_threshold,
476 params.test_durations_secs * params.clip.fps);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000477
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000478 CreateCalls(Call::Config(&analyzer), Call::Config(&recv_transport));
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000479
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000480 analyzer.SetReceiver(receiver_call_->Receiver());
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000481 send_transport.SetReceiver(&analyzer);
482 recv_transport.SetReceiver(sender_call_->Receiver());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000483
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000484 CreateSendConfig(1);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000485
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000486 rtc::scoped_ptr<VideoEncoder> encoder(
pbos@webrtc.orgab990ae2014-09-17 09:02:25 +0000487 VideoEncoder::Create(VideoEncoder::kVp8));
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000488 send_config_.encoder_settings.encoder = encoder.get();
489 send_config_.encoder_settings.payload_name = "VP8";
490 send_config_.encoder_settings.payload_type = 124;
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000491 send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000492 send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
493 send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000494
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000495 VideoStream* stream = &encoder_config_.streams[0];
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000496 stream->width = params.clip.width;
497 stream->height = params.clip.height;
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000498 stream->min_bitrate_bps = params.min_bitrate_bps;
499 stream->target_bitrate_bps = params.target_bitrate_bps;
500 stream->max_bitrate_bps = params.max_bitrate_bps;
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000501 stream->max_framerate = params.clip.fps;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000502
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000503 if (params.screenshare) {
504 encoder_config_.content_type = VideoEncoderConfig::kScreenshare;
505 encoder_config_.min_transmit_bitrate_bps = 400 * 1000;
506 VideoCodecVP8 vp8_settings = VideoEncoder::GetDefaultVp8Settings();
507 vp8_settings.denoisingOn = false;
508 vp8_settings.frameDroppingOn = false;
509 vp8_settings.numberOfTemporalLayers = 2;
510 encoder_config_.encoder_specific_settings = &vp8_settings;
511
512 stream->temporal_layer_thresholds_bps.clear();
513 stream->temporal_layer_thresholds_bps.push_back(stream->target_bitrate_bps);
514 }
515
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000516 CreateMatchingReceiveConfigs();
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000517 receive_configs_[0].renderer = &analyzer;
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000518 receive_configs_[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000519 receive_configs_[0].rtp.rtx[kSendRtxPayloadType].ssrc = kSendRtxSsrcs[0];
520 receive_configs_[0].rtp.rtx[kSendRtxPayloadType].payload_type =
sprang@webrtc.org343096a2015-02-23 08:34:17 +0000521 kSendRtxPayloadType;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000522
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000523 CreateStreams();
524 analyzer.input_ = send_stream_->Input();
525
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000526 if (params.screenshare) {
527 std::vector<std::string> slides;
528 slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
529 slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
530 slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
531 slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000532
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000533 rtc::scoped_ptr<test::FrameGenerator> frame_generator(
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000534 test::FrameGenerator::CreateFromYuvFile(
535 slides, 1850, 1110,
536 10 * params.clip.fps) // Cycle image every 10 seconds.
537 );
538 frame_generator_capturer_.reset(new test::FrameGeneratorCapturer(
539 Clock::GetRealTimeClock(), &analyzer, frame_generator.release(),
540 params.clip.fps));
541 ASSERT_TRUE(frame_generator_capturer_->Init());
542 } else {
543 frame_generator_capturer_.reset(
544 test::FrameGeneratorCapturer::CreateFromYuvFile(
545 &analyzer, test::ResourcePath(params.clip.name, "yuv"),
546 params.clip.width, params.clip.height, params.clip.fps,
547 Clock::GetRealTimeClock()));
548
549 ASSERT_TRUE(frame_generator_capturer_.get() != NULL)
550 << "Could not create capturer for " << params.clip.name
551 << ".yuv. Is this resource file present?";
552 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000553
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000554 Start();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000555
556 analyzer.Wait();
557
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000558 send_transport.StopSending();
559 recv_transport.StopSending();
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000560
561 Stop();
562
563 DestroyStreams();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000564}
565
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000566TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) {
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000567 FullStackTestParams paris_qcif = {"net_delay_0_0_plr_0",
568 {"paris_qcif", 176, 144, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000569 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000570 300000,
571 300000,
572 300000,
573 36.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000574 0.96,
575 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000576 RunTest(paris_qcif);
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000577}
578
579TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) {
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000580 // TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
581 FullStackTestParams foreman_cif = {"foreman_cif_net_delay_0_0_plr_0",
582 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000583 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000584 700000,
585 700000,
586 700000,
587 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000588 0.0,
589 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000590 RunTest(foreman_cif);
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000591}
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000592
sprang@webrtc.org343096a2015-02-23 08:34:17 +0000593TEST_F(FullStackTest, ForemanCifPlr5) {
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000594 FullStackTestParams foreman_cif = {"foreman_cif_delay_50_0_plr_5",
595 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000596 false,
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000597 30000,
598 500000,
599 2000000,
600 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000601 0.0,
602 kFullStackTestDurationSecs};
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000603 foreman_cif.link.loss_percent = 5;
604 foreman_cif.link.queue_delay_ms = 50;
605 RunTest(foreman_cif);
606}
607
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000608TEST_F(FullStackTest, ForemanCif500kbps) {
609 FullStackTestParams foreman_cif = {"foreman_cif_500kbps",
610 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000611 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000612 30000,
613 500000,
614 2000000,
615 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000616 0.0,
617 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000618 foreman_cif.link.queue_length_packets = 0;
619 foreman_cif.link.queue_delay_ms = 0;
620 foreman_cif.link.link_capacity_kbps = 500;
621 RunTest(foreman_cif);
622}
623
624TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) {
625 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_32pkts_queue",
626 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000627 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000628 30000,
629 500000,
630 2000000,
631 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000632 0.0,
633 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000634 foreman_cif.link.queue_length_packets = 32;
635 foreman_cif.link.queue_delay_ms = 0;
636 foreman_cif.link.link_capacity_kbps = 500;
637 RunTest(foreman_cif);
638}
639
640TEST_F(FullStackTest, ForemanCif500kbps100ms) {
641 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms",
642 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000643 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000644 30000,
645 500000,
646 2000000,
647 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000648 0.0,
649 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000650 foreman_cif.link.queue_length_packets = 0;
651 foreman_cif.link.queue_delay_ms = 100;
652 foreman_cif.link.link_capacity_kbps = 500;
653 RunTest(foreman_cif);
654}
655
656TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) {
657 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms_32pkts_queue",
658 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000659 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000660 30000,
661 500000,
662 2000000,
663 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000664 0.0,
665 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000666 foreman_cif.link.queue_length_packets = 32;
667 foreman_cif.link.queue_delay_ms = 100;
668 foreman_cif.link.link_capacity_kbps = 500;
669 RunTest(foreman_cif);
670}
671
672TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) {
673 FullStackTestParams foreman_cif = {"foreman_cif_1000kbps_100ms_32pkts_queue",
674 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000675 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000676 30000,
677 2000000,
678 2000000,
679 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000680 0.0,
681 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000682 foreman_cif.link.queue_length_packets = 32;
683 foreman_cif.link.queue_delay_ms = 100;
684 foreman_cif.link.link_capacity_kbps = 1000;
685 RunTest(foreman_cif);
686}
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000687
688TEST_F(FullStackTest, ScreenshareSlides) {
689 FullStackTestParams screenshare_params = {
690 "screenshare_slides",
691 {"screenshare_slides", 1850, 1110, 5},
692 true,
693 50000,
694 100000,
695 1000000,
696 0.0,
697 0.0,
698 kFullStackTestDurationSecs};
699 RunTest(screenshare_params);
700}
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000701} // namespace webrtc