blob: f0c5d7985e7e7b0cc54562a5168e1f8ea8e4a091 [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.org2b4ce3a2015-03-23 13:12:24 +000076 receiver_(nullptr),
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000077 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),
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000084 avg_psnr_threshold_(avg_psnr_threshold),
85 avg_ssim_threshold_(avg_ssim_threshold),
sprang@webrtc.org131bea82015-02-18 12:46:06 +000086 comparison_available_event_(EventWrapper::Create()),
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +000087 done_(EventWrapper::Create()) {
sprang@webrtc.org131bea82015-02-18 12:46:06 +000088 // Create thread pool for CPU-expensive PSNR/SSIM calculations.
89
90 // Try to use about as many threads as cores, but leave kMinCoresLeft alone,
91 // so that we don't accidentally starve "real" worker threads (codec etc).
92 // Also, don't allocate more than kMaxComparisonThreads, even if there are
93 // spare cores.
94
95 uint32_t num_cores = CpuInfo::DetectNumberOfCores();
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +000096 DCHECK_GE(num_cores, 1u);
sprang@webrtc.org131bea82015-02-18 12:46:06 +000097 static const uint32_t kMinCoresLeft = 4;
sprang@webrtc.org343096a2015-02-23 08:34:17 +000098 static const uint32_t kMaxComparisonThreads = 8;
sprang@webrtc.org131bea82015-02-18 12:46:06 +000099
100 if (num_cores <= kMinCoresLeft) {
101 num_cores = 1;
102 } else {
103 num_cores -= kMinCoresLeft;
104 num_cores = std::min(num_cores, kMaxComparisonThreads);
105 }
106
107 for (uint32_t i = 0; i < num_cores; ++i) {
tommi@webrtc.org361981f2015-03-19 14:44:18 +0000108 rtc::scoped_ptr<ThreadWrapper> thread =
tommi@webrtc.org38492c52015-03-22 14:41:46 +0000109 ThreadWrapper::CreateThread(&FrameComparisonThread, this, "Analyzer");
pbos@webrtc.org86639732015-03-13 00:06:21 +0000110 EXPECT_TRUE(thread->Start());
tommi@webrtc.org361981f2015-03-19 14:44:18 +0000111 comparison_thread_pool_.push_back(thread.release());
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000112 }
pbos@webrtc.org94015242013-10-16 11:05:37 +0000113 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000114
115 ~VideoAnalyzer() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000116 for (ThreadWrapper* thread : comparison_thread_pool_) {
117 EXPECT_TRUE(thread->Stop());
118 delete thread;
119 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000120 }
121
pbos@webrtc.org94015242013-10-16 11:05:37 +0000122 virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; }
123
Fredrik Solenberg23fba1f2015-04-29 15:24:01 +0200124 DeliveryStatus DeliverPacket(MediaType media_type, const uint8_t* packet,
125 size_t length) override {
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000126 rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000127 RTPHeader header;
pbos@webrtc.org62bafae2014-07-08 12:10:51 +0000128 parser->Parse(packet, length, &header);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000129 {
Peter Boströmf2f82832015-05-01 13:00:41 +0200130 rtc::CritScope lock(&crit_);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000131 recv_times_[header.timestamp - rtp_timestamp_delta_] =
132 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
133 }
134
Fredrik Solenberg23fba1f2015-04-29 15:24:01 +0200135 return receiver_->DeliverPacket(media_type, packet, length);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000136 }
137
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700138 void IncomingCapturedFrame(const VideoFrame& video_frame) override {
139 VideoFrame copy = video_frame;
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000140 copy.set_timestamp(copy.ntp_time_ms() * 90);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000141
142 {
Peter Boströmf2f82832015-05-01 13:00:41 +0200143 rtc::CritScope lock(&crit_);
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000144 if (first_send_frame_.IsZeroSize() && rtp_timestamp_delta_ == 0)
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000145 first_send_frame_ = copy;
146
147 frames_.push_back(copy);
148 }
149
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000150 input_->IncomingCapturedFrame(video_frame);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000151 }
152
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000153 bool SendRtp(const uint8_t* packet, size_t length) override {
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000154 rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000155 RTPHeader header;
pbos@webrtc.org62bafae2014-07-08 12:10:51 +0000156 parser->Parse(packet, length, &header);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000157
158 {
Peter Boströmf2f82832015-05-01 13:00:41 +0200159 rtc::CritScope lock(&crit_);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000160 if (rtp_timestamp_delta_ == 0) {
161 rtp_timestamp_delta_ =
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000162 header.timestamp - first_send_frame_.timestamp();
163 first_send_frame_.Reset();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000164 }
pbos@webrtc.org7fb9ce02013-08-05 09:29:50 +0000165 send_times_[header.timestamp - rtp_timestamp_delta_] =
166 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000167 }
168
pbos@webrtc.org27326b62013-11-20 12:17:04 +0000169 return transport_->SendRtp(packet, length);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000170 }
171
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000172 bool SendRtcp(const uint8_t* packet, size_t length) override {
pbos@webrtc.org27326b62013-11-20 12:17:04 +0000173 return transport_->SendRtcp(packet, length);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000174 }
175
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700176 void RenderFrame(const VideoFrame& video_frame,
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000177 int time_to_render_ms) override {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000178 int64_t render_time_ms =
179 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000180 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_;
181
Peter Boströmf2f82832015-05-01 13:00:41 +0200182 rtc::CritScope lock(&crit_);
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000183
184 while (frames_.front().timestamp() < send_timestamp) {
185 AddFrameComparison(frames_.front(), last_rendered_frame_, true,
186 render_time_ms);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000187 frames_.pop_front();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000188 }
189
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700190 VideoFrame reference_frame = frames_.front();
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000191 frames_.pop_front();
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000192 assert(!reference_frame.IsZeroSize());
193 EXPECT_EQ(reference_frame.timestamp(), send_timestamp);
194 assert(reference_frame.timestamp() == send_timestamp);
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000195
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000196 AddFrameComparison(reference_frame, video_frame, false, render_time_ms);
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000197
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000198 last_rendered_frame_ = video_frame;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000199 }
200
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000201 bool IsTextureSupported() const override { return false; }
pbos@webrtc.org0d852d52015-02-09 15:14:36 +0000202
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000203 void Wait() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000204 // Frame comparisons can be very expensive. Wait for test to be done, but
205 // at time-out check if frames_processed is going up. If so, give it more
206 // time, otherwise fail. Hopefully this will reduce test flakiness.
207
208 int last_frames_processed = -1;
209 EventTypeWrapper eventType;
210 while ((eventType = done_->Wait(FullStackTest::kDefaultTimeoutMs)) !=
211 kEventSignaled) {
212 int frames_processed;
213 {
Peter Boströmf2f82832015-05-01 13:00:41 +0200214 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000215 frames_processed = frames_processed_;
216 }
217 if (last_frames_processed == -1) {
218 last_frames_processed = frames_processed;
219 continue;
220 }
221 ASSERT_GT(frames_processed, last_frames_processed)
222 << "Analyzer stalled while waiting for test to finish.";
223 last_frames_processed = frames_processed;
224 }
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000225 }
pbos@webrtc.org94015242013-10-16 11:05:37 +0000226
227 VideoSendStreamInput* input_;
228 Transport* transport_;
229 PacketReceiver* receiver_;
230
231 private:
232 struct FrameComparison {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000233 FrameComparison()
234 : dropped(false), send_time_ms(0), recv_time_ms(0), render_time_ms(0) {}
235
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700236 FrameComparison(const VideoFrame& reference,
237 const VideoFrame& render,
pbos@webrtc.org94015242013-10-16 11:05:37 +0000238 bool dropped,
239 int64_t send_time_ms,
240 int64_t recv_time_ms,
241 int64_t render_time_ms)
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000242 : reference(reference),
243 render(render),
244 dropped(dropped),
pbos@webrtc.org94015242013-10-16 11:05:37 +0000245 send_time_ms(send_time_ms),
246 recv_time_ms(recv_time_ms),
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000247 render_time_ms(render_time_ms) {}
pbos@webrtc.org94015242013-10-16 11:05:37 +0000248
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700249 VideoFrame reference;
250 VideoFrame render;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000251 bool dropped;
252 int64_t send_time_ms;
253 int64_t recv_time_ms;
254 int64_t render_time_ms;
255 };
256
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700257 void AddFrameComparison(const VideoFrame& reference,
258 const VideoFrame& render,
pbos@webrtc.org94015242013-10-16 11:05:37 +0000259 bool dropped,
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000260 int64_t render_time_ms)
261 EXCLUSIVE_LOCKS_REQUIRED(crit_) {
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000262 int64_t send_time_ms = send_times_[reference.timestamp()];
263 send_times_.erase(reference.timestamp());
264 int64_t recv_time_ms = recv_times_[reference.timestamp()];
265 recv_times_.erase(reference.timestamp());
pbos@webrtc.org94015242013-10-16 11:05:37 +0000266
Peter Boströmf2f82832015-05-01 13:00:41 +0200267 rtc::CritScope crit(&comparison_lock_);
pbos@webrtc.org94015242013-10-16 11:05:37 +0000268 comparisons_.push_back(FrameComparison(reference,
269 render,
270 dropped,
271 send_time_ms,
272 recv_time_ms,
273 render_time_ms));
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000274 comparison_available_event_->Set();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000275 }
276
277 static bool FrameComparisonThread(void* obj) {
278 return static_cast<VideoAnalyzer*>(obj)->CompareFrames();
279 }
280
281 bool CompareFrames() {
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000282 if (AllFramesRecorded())
283 return false;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000284
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700285 VideoFrame reference;
286 VideoFrame render;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000287 FrameComparison comparison;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000288
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000289 if (!PopComparison(&comparison)) {
290 // Wait until new comparison task is available, or test is done.
291 // If done, wake up remaining threads waiting.
292 comparison_available_event_->Wait(1000);
293 if (AllFramesRecorded()) {
294 comparison_available_event_->Set();
pbos@webrtc.org94015242013-10-16 11:05:37 +0000295 return false;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000296 }
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000297 return true; // Try again.
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000298 }
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000299
300 PerformFrameComparison(comparison);
301
302 if (FrameProcessed()) {
303 PrintResults();
304 done_->Set();
305 comparison_available_event_->Set();
306 return false;
307 }
308
309 return true;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000310 }
311
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000312 bool PopComparison(FrameComparison* comparison) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200313 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000314 // If AllFramesRecorded() is true, it means we have already popped
315 // frames_to_process_ frames from comparisons_, so there is no more work
316 // for this thread to be done. frames_processed_ might still be lower if
317 // all comparisons are not done, but those frames are currently being
318 // worked on by other threads.
319 if (comparisons_.empty() || AllFramesRecorded())
320 return false;
321
pbos@webrtc.org443ad402015-03-20 07:34:28 +0000322 *comparison = comparisons_.front();
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000323 comparisons_.pop_front();
324
325 FrameRecorded();
326 return true;
327 }
328
329 // Increment counter for number of frames received for comparison.
330 void FrameRecorded() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200331 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000332 ++frames_recorded_;
333 }
334
335 // Returns true if all frames to be compared have been taken from the queue.
336 bool AllFramesRecorded() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200337 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000338 assert(frames_recorded_ <= frames_to_process_);
339 return frames_recorded_ == frames_to_process_;
340 }
341
342 // Increase count of number of frames processed. Returns true if this was the
343 // last frame to be processed.
344 bool FrameProcessed() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200345 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000346 ++frames_processed_;
347 assert(frames_processed_ <= frames_to_process_);
348 return frames_processed_ == frames_to_process_;
349 }
350
351 void PrintResults() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200352 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000353 PrintResult("psnr", psnr_, " dB");
354 PrintResult("ssim", ssim_, "");
355 PrintResult("sender_time", sender_time_, " ms");
356 printf("RESULT dropped_frames: %s = %d frames\n", test_label_,
357 dropped_frames_);
358 PrintResult("receiver_time", receiver_time_, " ms");
359 PrintResult("total_delay_incl_network", end_to_end_, " ms");
360 PrintResult("time_between_rendered_frames", rendered_delta_, " ms");
361 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_);
362 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_);
363 }
364
365 void PerformFrameComparison(const FrameComparison& comparison) {
366 // Perform expensive psnr and ssim calculations while not holding lock.
magjed@webrtc.org2056ee32015-03-16 13:46:52 +0000367 double psnr = I420PSNR(&comparison.reference, &comparison.render);
368 double ssim = I420SSIM(&comparison.reference, &comparison.render);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000369
Peter Boströmf2f82832015-05-01 13:00:41 +0200370 rtc::CritScope crit(&comparison_lock_);
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000371 psnr_.AddSample(psnr);
372 ssim_.AddSample(ssim);
373 if (comparison.dropped) {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000374 ++dropped_frames_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000375 return;
pbos@webrtc.org94015242013-10-16 11:05:37 +0000376 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000377 if (last_render_time_ != 0)
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000378 rendered_delta_.AddSample(comparison.render_time_ms - last_render_time_);
379 last_render_time_ = comparison.render_time_ms;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000380
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000381 int64_t input_time_ms = comparison.reference.ntp_time_ms();
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000382 sender_time_.AddSample(comparison.send_time_ms - input_time_ms);
383 receiver_time_.AddSample(comparison.render_time_ms -
384 comparison.recv_time_ms);
385 end_to_end_.AddSample(comparison.render_time_ms - input_time_ms);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000386 }
387
388 void PrintResult(const char* result_type,
389 test::Statistics stats,
390 const char* unit) {
391 printf("RESULT %s: %s = {%f, %f}%s\n",
392 result_type,
393 test_label_,
394 stats.Mean(),
395 stats.StandardDeviation(),
396 unit);
397 }
398
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000399 const char* const test_label_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000400 test::Statistics sender_time_;
401 test::Statistics receiver_time_;
402 test::Statistics psnr_;
403 test::Statistics ssim_;
404 test::Statistics end_to_end_;
405 test::Statistics rendered_delta_;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000406 const int frames_to_process_;
407 int frames_recorded_;
408 int frames_processed_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000409 int dropped_frames_;
410 int64_t last_render_time_;
411 uint32_t rtp_timestamp_delta_;
412
Peter Boströmf2f82832015-05-01 13:00:41 +0200413 rtc::CriticalSection crit_;
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700414 std::deque<VideoFrame> frames_ GUARDED_BY(crit_);
415 VideoFrame last_rendered_frame_ GUARDED_BY(crit_);
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000416 std::map<uint32_t, int64_t> send_times_ GUARDED_BY(crit_);
417 std::map<uint32_t, int64_t> recv_times_ GUARDED_BY(crit_);
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700418 VideoFrame first_send_frame_ GUARDED_BY(crit_);
pbos@webrtc.orgb35b1362014-10-20 09:14:38 +0000419 const double avg_psnr_threshold_;
420 const double avg_ssim_threshold_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000421
Peter Boströmf2f82832015-05-01 13:00:41 +0200422 rtc::CriticalSection comparison_lock_;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000423 std::vector<ThreadWrapper*> comparison_thread_pool_;
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000424 const rtc::scoped_ptr<EventWrapper> comparison_available_event_;
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000425 std::deque<FrameComparison> comparisons_ GUARDED_BY(comparison_lock_);
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000426 const rtc::scoped_ptr<EventWrapper> done_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000427};
428
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000429void FullStackTest::RunTest(const FullStackTestParams& params) {
430 test::DirectTransport send_transport(params.link);
431 test::DirectTransport recv_transport(params.link);
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000432 VideoAnalyzer analyzer(nullptr, &send_transport, params.test_label,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000433 params.avg_psnr_threshold, params.avg_ssim_threshold,
434 params.test_durations_secs * params.clip.fps);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000435
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000436 CreateCalls(Call::Config(&analyzer), Call::Config(&recv_transport));
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000437
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000438 analyzer.SetReceiver(receiver_call_->Receiver());
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000439 send_transport.SetReceiver(&analyzer);
440 recv_transport.SetReceiver(sender_call_->Receiver());
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000441
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000442 CreateSendConfig(1);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000443
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000444 rtc::scoped_ptr<VideoEncoder> encoder(
pbos@webrtc.orgab990ae2014-09-17 09:02:25 +0000445 VideoEncoder::Create(VideoEncoder::kVp8));
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000446 send_config_.encoder_settings.encoder = encoder.get();
447 send_config_.encoder_settings.payload_name = "VP8";
448 send_config_.encoder_settings.payload_type = 124;
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000449 send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000450 send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
451 send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000452
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000453 VideoStream* stream = &encoder_config_.streams[0];
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000454 stream->width = params.clip.width;
455 stream->height = params.clip.height;
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000456 stream->min_bitrate_bps = params.min_bitrate_bps;
457 stream->target_bitrate_bps = params.target_bitrate_bps;
458 stream->max_bitrate_bps = params.max_bitrate_bps;
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000459 stream->max_framerate = params.clip.fps;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000460
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000461 if (params.screenshare) {
Erik Språng143cec12015-04-28 10:01:41 +0200462 encoder_config_.content_type = VideoEncoderConfig::ContentType::kScreen;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000463 encoder_config_.min_transmit_bitrate_bps = 400 * 1000;
464 VideoCodecVP8 vp8_settings = VideoEncoder::GetDefaultVp8Settings();
465 vp8_settings.denoisingOn = false;
466 vp8_settings.frameDroppingOn = false;
467 vp8_settings.numberOfTemporalLayers = 2;
468 encoder_config_.encoder_specific_settings = &vp8_settings;
469
470 stream->temporal_layer_thresholds_bps.clear();
471 stream->temporal_layer_thresholds_bps.push_back(stream->target_bitrate_bps);
472 }
473
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000474 CreateMatchingReceiveConfigs();
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000475 receive_configs_[0].renderer = &analyzer;
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000476 receive_configs_[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000477 receive_configs_[0].rtp.rtx[kSendRtxPayloadType].ssrc = kSendRtxSsrcs[0];
478 receive_configs_[0].rtp.rtx[kSendRtxPayloadType].payload_type =
sprang@webrtc.org343096a2015-02-23 08:34:17 +0000479 kSendRtxPayloadType;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000480
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000481 CreateStreams();
482 analyzer.input_ = send_stream_->Input();
483
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000484 if (params.screenshare) {
485 std::vector<std::string> slides;
486 slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
487 slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
488 slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
489 slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000490
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000491 rtc::scoped_ptr<test::FrameGenerator> frame_generator(
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000492 test::FrameGenerator::CreateFromYuvFile(
493 slides, 1850, 1110,
494 10 * params.clip.fps) // Cycle image every 10 seconds.
495 );
496 frame_generator_capturer_.reset(new test::FrameGeneratorCapturer(
497 Clock::GetRealTimeClock(), &analyzer, frame_generator.release(),
498 params.clip.fps));
499 ASSERT_TRUE(frame_generator_capturer_->Init());
500 } else {
501 frame_generator_capturer_.reset(
502 test::FrameGeneratorCapturer::CreateFromYuvFile(
503 &analyzer, test::ResourcePath(params.clip.name, "yuv"),
504 params.clip.width, params.clip.height, params.clip.fps,
505 Clock::GetRealTimeClock()));
506
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000507 ASSERT_TRUE(frame_generator_capturer_.get() != nullptr)
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000508 << "Could not create capturer for " << params.clip.name
509 << ".yuv. Is this resource file present?";
510 }
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000511
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000512 Start();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000513
514 analyzer.Wait();
515
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000516 send_transport.StopSending();
517 recv_transport.StopSending();
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000518
519 Stop();
520
521 DestroyStreams();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000522}
523
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000524TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) {
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000525 FullStackTestParams paris_qcif = {"net_delay_0_0_plr_0",
526 {"paris_qcif", 176, 144, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000527 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000528 300000,
529 300000,
530 300000,
531 36.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000532 0.96,
533 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000534 RunTest(paris_qcif);
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000535}
536
537TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) {
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000538 // TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
539 FullStackTestParams foreman_cif = {"foreman_cif_net_delay_0_0_plr_0",
540 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000541 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000542 700000,
543 700000,
544 700000,
545 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000546 0.0,
547 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000548 RunTest(foreman_cif);
pbos@webrtc.org994d0b72014-06-27 08:47:52 +0000549}
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000550
sprang@webrtc.org343096a2015-02-23 08:34:17 +0000551TEST_F(FullStackTest, ForemanCifPlr5) {
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000552 FullStackTestParams foreman_cif = {"foreman_cif_delay_50_0_plr_5",
553 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000554 false,
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000555 30000,
556 500000,
557 2000000,
558 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000559 0.0,
560 kFullStackTestDurationSecs};
stefan@webrtc.orgc216b9a2014-10-14 10:38:49 +0000561 foreman_cif.link.loss_percent = 5;
562 foreman_cif.link.queue_delay_ms = 50;
563 RunTest(foreman_cif);
564}
565
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000566TEST_F(FullStackTest, ForemanCif500kbps) {
567 FullStackTestParams foreman_cif = {"foreman_cif_500kbps",
568 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000569 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000570 30000,
571 500000,
572 2000000,
573 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000574 0.0,
575 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000576 foreman_cif.link.queue_length_packets = 0;
577 foreman_cif.link.queue_delay_ms = 0;
578 foreman_cif.link.link_capacity_kbps = 500;
579 RunTest(foreman_cif);
580}
581
582TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) {
583 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_32pkts_queue",
584 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000585 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000586 30000,
587 500000,
588 2000000,
589 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000590 0.0,
591 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000592 foreman_cif.link.queue_length_packets = 32;
593 foreman_cif.link.queue_delay_ms = 0;
594 foreman_cif.link.link_capacity_kbps = 500;
595 RunTest(foreman_cif);
596}
597
598TEST_F(FullStackTest, ForemanCif500kbps100ms) {
599 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms",
600 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000601 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000602 30000,
603 500000,
604 2000000,
605 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000606 0.0,
607 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000608 foreman_cif.link.queue_length_packets = 0;
609 foreman_cif.link.queue_delay_ms = 100;
610 foreman_cif.link.link_capacity_kbps = 500;
611 RunTest(foreman_cif);
612}
613
614TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) {
615 FullStackTestParams foreman_cif = {"foreman_cif_500kbps_100ms_32pkts_queue",
616 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000617 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000618 30000,
619 500000,
620 2000000,
621 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000622 0.0,
623 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000624 foreman_cif.link.queue_length_packets = 32;
625 foreman_cif.link.queue_delay_ms = 100;
626 foreman_cif.link.link_capacity_kbps = 500;
627 RunTest(foreman_cif);
628}
629
630TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) {
631 FullStackTestParams foreman_cif = {"foreman_cif_1000kbps_100ms_32pkts_queue",
632 {"foreman_cif", 352, 288, 30},
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000633 false,
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000634 30000,
635 2000000,
636 2000000,
637 0.0,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000638 0.0,
639 kFullStackTestDurationSecs};
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000640 foreman_cif.link.queue_length_packets = 32;
641 foreman_cif.link.queue_delay_ms = 100;
642 foreman_cif.link.link_capacity_kbps = 1000;
643 RunTest(foreman_cif);
644}
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000645
646TEST_F(FullStackTest, ScreenshareSlides) {
647 FullStackTestParams screenshare_params = {
648 "screenshare_slides",
649 {"screenshare_slides", 1850, 1110, 5},
650 true,
651 50000,
Erik Språng2c4c9142015-06-24 11:24:44 +0200652 200000,
653 2000000,
sprang@webrtc.org131bea82015-02-18 12:46:06 +0000654 0.0,
655 0.0,
656 kFullStackTestDurationSecs};
657 RunTest(screenshare_params);
658}
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000659} // namespace webrtc