blob: 317f126ad5f519a497dd3f73485f7402b2501fcf [file] [log] [blame]
pbos@webrtc.org2f02da82013-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.org2f02da82013-07-09 08:02:33 +000015#include "gflags/gflags.h"
pbos@webrtc.orge8350192013-07-10 15:02:02 +000016#include "testing/gtest/include/gtest/gtest.h"
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000017
pbos@webrtc.org24e20892013-10-28 16:32:01 +000018#include "webrtc/call.h"
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000019#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"
pbos@webrtc.orga24c3562013-10-16 11:05:37 +000025#include "webrtc/system_wrappers/interface/sleep.h"
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000026#include "webrtc/test/testsupport/fileutils.h"
pbos@webrtc.org24e20892013-10-28 16:32:01 +000027#include "webrtc/test/direct_transport.h"
28#include "webrtc/test/frame_generator_capturer.h"
29#include "webrtc/test/generate_ssrcs.h"
30#include "webrtc/test/statistics.h"
31#include "webrtc/test/video_renderer.h"
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000032#include "webrtc/typedefs.h"
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000033
34DEFINE_int32(seconds, 10, "Seconds to run each clip.");
35
36namespace webrtc {
37
38struct FullStackTestParams {
39 const char* test_label;
40 struct {
41 const char* name;
pbos@webrtc.orge9bd2992013-07-09 14:04:46 +000042 size_t width, height;
43 int fps;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000044 } clip;
pbos@webrtc.orge9bd2992013-07-09 14:04:46 +000045 unsigned int bitrate;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000046 double avg_psnr_threshold;
47 double avg_ssim_threshold;
48};
49
pbos@webrtc.orgbf6d5722013-09-09 15:04:25 +000050FullStackTestParams paris_qcif = {
51 "net_delay_0_0_plr_0", {"paris_qcif", 176, 144, 30}, 300, 36.0, 0.96};
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000052
53// TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
pbos@webrtc.orgbf6d5722013-09-09 15:04:25 +000054FullStackTestParams foreman_cif = {
55 "foreman_cif_net_delay_0_0_plr_0",
56 {"foreman_cif", 352, 288, 30},
57 700,
58 0.0,
59 0.0};
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000060
61class FullStackTest : public ::testing::TestWithParam<FullStackTestParams> {
62 protected:
63 std::map<uint32_t, bool> reserved_ssrcs_;
64};
65
pbos@webrtc.orgc1797062013-08-23 09:19:30 +000066class VideoAnalyzer : public PacketReceiver,
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000067 public newapi::Transport,
pbos@webrtc.orgc1797062013-08-23 09:19:30 +000068 public VideoRenderer,
69 public VideoSendStreamInput {
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000070 public:
pbos@webrtc.orgc1797062013-08-23 09:19:30 +000071 VideoAnalyzer(VideoSendStreamInput* input,
72 Transport* transport,
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000073 const char* test_label,
74 double avg_psnr_threshold,
75 double avg_ssim_threshold,
pbos@webrtc.orga24c3562013-10-16 11:05:37 +000076 int duration_frames)
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000077 : input_(input),
78 transport_(transport),
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000079 receiver_(NULL),
80 test_label_(test_label),
pbos@webrtc.orga24c3562013-10-16 11:05:37 +000081 dropped_frames_(0),
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000082 rtp_timestamp_delta_(0),
83 first_send_frame_(NULL),
84 last_render_time_(0),
85 avg_psnr_threshold_(avg_psnr_threshold),
86 avg_ssim_threshold_(avg_ssim_threshold),
87 frames_left_(duration_frames),
88 crit_(CriticalSectionWrapper::CreateCriticalSection()),
pbos@webrtc.orga24c3562013-10-16 11:05:37 +000089 comparison_lock_(CriticalSectionWrapper::CreateCriticalSection()),
90 comparison_thread_(ThreadWrapper::CreateThread(&FrameComparisonThread,
91 this)),
92 trigger_(EventWrapper::Create()) {
93 unsigned int id;
94 EXPECT_TRUE(comparison_thread_->Start(id));
95 }
pbos@webrtc.org2f02da82013-07-09 08:02:33 +000096
97 ~VideoAnalyzer() {
pbos@webrtc.orga24c3562013-10-16 11:05:37 +000098 EXPECT_TRUE(comparison_thread_->Stop());
99
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000100 while (!frames_.empty()) {
101 delete frames_.back();
102 frames_.pop_back();
103 }
104 while (!frame_pool_.empty()) {
105 delete frame_pool_.back();
106 frame_pool_.pop_back();
107 }
108 }
109
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000110 virtual void SetReceiver(PacketReceiver* receiver) { receiver_ = receiver; }
111
pbos@webrtc.org78ab5112013-08-05 12:49:22 +0000112 virtual bool DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE {
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000113 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
114 RTPHeader header;
pbos@webrtc.org78ab5112013-08-05 12:49:22 +0000115 parser->Parse(packet, static_cast<int>(length), &header);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000116 {
117 CriticalSectionScoped cs(crit_.get());
118 recv_times_[header.timestamp - rtp_timestamp_delta_] =
119 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
120 }
121
122 return receiver_->DeliverPacket(packet, length);
123 }
124
125 virtual void PutFrame(const I420VideoFrame& video_frame,
126 uint32_t delta_capture_ms) OVERRIDE {
127 I420VideoFrame* copy = NULL;
128 {
129 CriticalSectionScoped cs(crit_.get());
130 if (frame_pool_.size() > 0) {
131 copy = frame_pool_.front();
132 frame_pool_.pop_front();
133 }
134 }
135 if (copy == NULL)
136 copy = new I420VideoFrame();
137
138 copy->CopyFrame(video_frame);
139 copy->set_timestamp(copy->render_time_ms() * 90);
140
141 {
142 CriticalSectionScoped cs(crit_.get());
143 if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0)
144 first_send_frame_ = copy;
145
146 frames_.push_back(copy);
147 }
148
149 input_->PutFrame(video_frame, delta_capture_ms);
150 }
151
152 virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
153 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
154 RTPHeader header;
155 parser->Parse(packet, static_cast<int>(length), &header);
156
157 {
158 CriticalSectionScoped cs(crit_.get());
159 if (rtp_timestamp_delta_ == 0) {
160 rtp_timestamp_delta_ =
161 header.timestamp - first_send_frame_->timestamp();
162 first_send_frame_ = NULL;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000163 }
pbos@webrtc.org3a74d402013-08-05 09:29:50 +0000164 send_times_[header.timestamp - rtp_timestamp_delta_] =
165 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000166 }
167
168 return transport_->SendRTP(packet, length);
169 }
170
171 virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
172 return transport_->SendRTCP(packet, length);
173 }
174
175 virtual void RenderFrame(const I420VideoFrame& video_frame,
176 int time_to_render_ms) OVERRIDE {
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000177 int64_t render_time_ms =
178 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000179 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_;
180
181 {
182 CriticalSectionScoped cs(crit_.get());
183 while (frames_.front()->timestamp() < send_timestamp) {
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000184 AddFrameComparison(
185 frames_.front(), &last_rendered_frame_, true, render_time_ms);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000186 frame_pool_.push_back(frames_.front());
187 frames_.pop_front();
188 }
189
190 I420VideoFrame* reference_frame = frames_.front();
191 frames_.pop_front();
192 assert(reference_frame != NULL);
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000193 EXPECT_EQ(reference_frame->timestamp(), send_timestamp);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000194 assert(reference_frame->timestamp() == send_timestamp);
195
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000196 AddFrameComparison(reference_frame, &video_frame, false, render_time_ms);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000197 frame_pool_.push_back(reference_frame);
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000198 }
199
200 last_rendered_frame_.CopyFrame(video_frame);
201 }
202
203 void Wait() { trigger_->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 int64_t send_time_ms = send_times_[reference->timestamp()];
249 send_times_.erase(reference->timestamp());
250 int64_t recv_time_ms = recv_times_[reference->timestamp()];
251 recv_times_.erase(reference->timestamp());
252
253 CriticalSectionScoped crit(comparison_lock_.get());
254 comparisons_.push_back(FrameComparison(reference,
255 render,
256 dropped,
257 send_time_ms,
258 recv_time_ms,
259 render_time_ms));
260 }
261
262 static bool FrameComparisonThread(void* obj) {
263 return static_cast<VideoAnalyzer*>(obj)->CompareFrames();
264 }
265
266 bool CompareFrames() {
267 assert(frames_left_ > 0);
268
269 I420VideoFrame reference;
270 I420VideoFrame render;
271 bool dropped;
272 int64_t send_time_ms;
273 int64_t recv_time_ms;
274 int64_t render_time_ms;
275
276 SleepMs(10);
277
278 while (true) {
279 {
280 CriticalSectionScoped crit(comparison_lock_.get());
281 if (comparisons_.empty())
282 return true;
283 reference.SwapFrame(&comparisons_.front().reference);
284 render.SwapFrame(&comparisons_.front().render);
285 dropped = comparisons_.front().dropped;
286 send_time_ms = comparisons_.front().send_time_ms;
287 recv_time_ms = comparisons_.front().recv_time_ms;
288 render_time_ms = comparisons_.front().render_time_ms;
289 comparisons_.pop_front();
290 }
291
292 PerformFrameComparison(&reference,
293 &render,
294 dropped,
295 send_time_ms,
296 recv_time_ms,
297 render_time_ms);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000298
299 if (--frames_left_ == 0) {
300 PrintResult("psnr", psnr_, " dB");
301 PrintResult("ssim", ssim_, "");
302 PrintResult("sender_time", sender_time_, " ms");
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000303 printf(
304 "RESULT dropped_frames: %s = %d\n", test_label_, dropped_frames_);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000305 PrintResult("receiver_time", receiver_time_, " ms");
306 PrintResult("total_delay_incl_network", end_to_end_, " ms");
307 PrintResult("time_between_rendered_frames", rendered_delta_, " ms");
308 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_);
309 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_);
310 trigger_->Set();
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000311
312 return false;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000313 }
314 }
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000315 }
316
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000317 void PerformFrameComparison(const I420VideoFrame* reference,
318 const I420VideoFrame* render,
319 bool dropped,
320 int64_t send_time_ms,
321 int64_t recv_time_ms,
322 int64_t render_time_ms) {
323 psnr_.AddSample(I420PSNR(reference, render));
324 ssim_.AddSample(I420SSIM(reference, render));
325 if (dropped) {
326 ++dropped_frames_;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000327 return;
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000328 }
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000329 if (last_render_time_ != 0)
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000330 rendered_delta_.AddSample(render_time_ms - last_render_time_);
331 last_render_time_ = render_time_ms;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000332
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000333 int64_t input_time_ms = reference->render_time_ms();
334 sender_time_.AddSample(send_time_ms - input_time_ms);
335 receiver_time_.AddSample(render_time_ms - recv_time_ms);
336 end_to_end_.AddSample(render_time_ms - input_time_ms);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000337 }
338
339 void PrintResult(const char* result_type,
340 test::Statistics stats,
341 const char* unit) {
342 printf("RESULT %s: %s = {%f, %f}%s\n",
343 result_type,
344 test_label_,
345 stats.Mean(),
346 stats.StandardDeviation(),
347 unit);
348 }
349
350 const char* test_label_;
351 test::Statistics sender_time_;
352 test::Statistics receiver_time_;
353 test::Statistics psnr_;
354 test::Statistics ssim_;
355 test::Statistics end_to_end_;
356 test::Statistics rendered_delta_;
357
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000358 int dropped_frames_;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000359 std::deque<I420VideoFrame*> frames_;
360 std::deque<I420VideoFrame*> frame_pool_;
361 I420VideoFrame last_rendered_frame_;
362 std::map<uint32_t, int64_t> send_times_;
363 std::map<uint32_t, int64_t> recv_times_;
364 uint32_t rtp_timestamp_delta_;
365 I420VideoFrame* first_send_frame_;
366 int64_t last_render_time_;
367 double avg_psnr_threshold_;
368 double avg_ssim_threshold_;
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000369 int frames_left_;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000370 scoped_ptr<CriticalSectionWrapper> crit_;
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000371 scoped_ptr<CriticalSectionWrapper> comparison_lock_;
372 scoped_ptr<ThreadWrapper> comparison_thread_;
373 std::deque<FrameComparison> comparisons_;
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000374 scoped_ptr<EventWrapper> trigger_;
375};
376
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000377TEST_P(FullStackTest, NoPacketLoss) {
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000378 FullStackTestParams params = GetParam();
379
pbos@webrtc.orgfe881f62013-08-12 12:59:04 +0000380 test::DirectTransport transport;
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000381 VideoAnalyzer analyzer(NULL,
382 &transport,
383 params.test_label,
384 params.avg_psnr_threshold,
385 params.avg_ssim_threshold,
386 FLAGS_seconds * params.clip.fps);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000387
pbos@webrtc.orgbf6d5722013-09-09 15:04:25 +0000388 Call::Config call_config(&analyzer);
mflodman@webrtc.orgbf76ae22013-07-23 11:35:00 +0000389
pbos@webrtc.orgbf6d5722013-09-09 15:04:25 +0000390 scoped_ptr<Call> call(Call::Create(call_config));
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000391 analyzer.SetReceiver(call->Receiver());
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000392 transport.SetReceiver(&analyzer);
393
pbos@webrtc.orgc1797062013-08-23 09:19:30 +0000394 VideoSendStream::Config send_config = call->GetDefaultSendConfig();
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000395 test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
396
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000397 // TODO(pbos): static_cast shouldn't be required after mflodman refactors the
398 // VideoCodec struct.
399 send_config.codec.width = static_cast<uint16_t>(params.clip.width);
400 send_config.codec.height = static_cast<uint16_t>(params.clip.height);
401 send_config.codec.minBitrate = params.bitrate;
402 send_config.codec.startBitrate = params.bitrate;
403 send_config.codec.maxBitrate = params.bitrate;
404
pbos@webrtc.org964d78e2013-11-20 10:40:25 +0000405 VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000406 analyzer.input_ = send_stream->Input();
407
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000408 scoped_ptr<test::FrameGeneratorCapturer> file_capturer(
andresp@webrtc.org28631e72013-09-19 12:14:03 +0000409 test::FrameGeneratorCapturer::CreateFromYuvFile(
pbos@webrtc.org7deb3352013-08-21 12:07:37 +0000410 &analyzer,
andresp@webrtc.org28631e72013-09-19 12:14:03 +0000411 test::ResourcePath(params.clip.name, "yuv").c_str(),
412 params.clip.width,
413 params.clip.height,
414 params.clip.fps,
415 Clock::GetRealTimeClock()));
pbos@webrtc.orga24c3562013-10-16 11:05:37 +0000416 ASSERT_TRUE(file_capturer.get() != NULL)
417 << "Could not create capturer for " << params.clip.name
418 << ".yuv. Is this resource file present?";
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000419
pbos@webrtc.orgbf6d5722013-09-09 15:04:25 +0000420 VideoReceiveStream::Config receive_config = call->GetDefaultReceiveConfig();
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000421 receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
422 receive_config.renderer = &analyzer;
423
pbos@webrtc.orgc1797062013-08-23 09:19:30 +0000424 VideoReceiveStream* receive_stream =
pbos@webrtc.org964d78e2013-11-20 10:40:25 +0000425 call->CreateVideoReceiveStream(receive_config);
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000426
427 receive_stream->StartReceive();
428 send_stream->StartSend();
429
430 file_capturer->Start();
431
432 analyzer.Wait();
433
434 file_capturer->Stop();
435 send_stream->StopSend();
436 receive_stream->StopReceive();
437
438 call->DestroyReceiveStream(receive_stream);
439 call->DestroySendStream(send_stream);
pbos@webrtc.orgfe881f62013-08-12 12:59:04 +0000440
441 transport.StopSending();
pbos@webrtc.org2f02da82013-07-09 08:02:33 +0000442}
443
444INSTANTIATE_TEST_CASE_P(FullStack,
445 FullStackTest,
446 ::testing::Values(paris_qcif, foreman_cif));
447
448} // namespace webrtc