blob: 5f1240fd7cc0971492cda0c14db48b03dbe56882 [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.orgaf8d5af2013-07-09 08:02:33 +000015#include "gflags/gflags.h"
pbos@webrtc.org69215d82013-07-10 15:02:02 +000016#include "testing/gtest/include/gtest/gtest.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000017
18#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
19#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
20#include "webrtc/system_wrappers/interface/clock.h"
21#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
22#include "webrtc/system_wrappers/interface/event_wrapper.h"
23#include "webrtc/system_wrappers/interface/scoped_ptr.h"
24#include "webrtc/test/testsupport/fileutils.h"
25#include "webrtc/typedefs.h"
pbos@webrtc.org841c8a42013-09-09 15:04:25 +000026#include "webrtc/video_engine/new_include/call.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000027#include "webrtc/video_engine/test/common/direct_transport.h"
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000028#include "webrtc/video_engine/test/common/frame_generator_capturer.h"
29#include "webrtc/video_engine/test/common/generate_ssrcs.h"
30#include "webrtc/video_engine/test/common/statistics.h"
31#include "webrtc/video_engine/test/common/video_renderer.h"
32
33DEFINE_int32(seconds, 10, "Seconds to run each clip.");
34
35namespace webrtc {
36
37struct FullStackTestParams {
38 const char* test_label;
39 struct {
40 const char* name;
pbos@webrtc.orgf3f13582013-07-09 14:04:46 +000041 size_t width, height;
42 int fps;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000043 } clip;
pbos@webrtc.orgf3f13582013-07-09 14:04:46 +000044 unsigned int bitrate;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000045 double avg_psnr_threshold;
46 double avg_ssim_threshold;
47};
48
pbos@webrtc.org841c8a42013-09-09 15:04:25 +000049FullStackTestParams paris_qcif = {
50 "net_delay_0_0_plr_0", {"paris_qcif", 176, 144, 30}, 300, 36.0, 0.96};
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000051
52// TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
pbos@webrtc.org841c8a42013-09-09 15:04:25 +000053FullStackTestParams foreman_cif = {
54 "foreman_cif_net_delay_0_0_plr_0",
55 {"foreman_cif", 352, 288, 30},
56 700,
57 0.0,
58 0.0};
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000059
60class FullStackTest : public ::testing::TestWithParam<FullStackTestParams> {
61 protected:
62 std::map<uint32_t, bool> reserved_ssrcs_;
63};
64
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000065class VideoAnalyzer : public PacketReceiver,
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000066 public newapi::Transport,
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000067 public VideoRenderer,
68 public VideoSendStreamInput {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000069 public:
pbos@webrtc.org74fa4892013-08-23 09:19:30 +000070 VideoAnalyzer(VideoSendStreamInput* input,
71 Transport* transport,
72 VideoRenderer* loopback_video,
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +000073 const char* test_label,
74 double avg_psnr_threshold,
75 double avg_ssim_threshold,
76 uint64_t duration_frames)
77 : input_(input),
78 transport_(transport),
79 renderer_(loopback_video),
80 receiver_(NULL),
81 test_label_(test_label),
82 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()),
89 trigger_(EventWrapper::Create()) {}
90
91 ~VideoAnalyzer() {
92 while (!frames_.empty()) {
93 delete frames_.back();
94 frames_.pop_back();
95 }
96 while (!frame_pool_.empty()) {
97 delete frame_pool_.back();
98 frame_pool_.pop_back();
99 }
100 }
101
pbos@webrtc.org40523702013-08-05 12:49:22 +0000102 virtual bool DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000103 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
104 RTPHeader header;
pbos@webrtc.org40523702013-08-05 12:49:22 +0000105 parser->Parse(packet, static_cast<int>(length), &header);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000106 {
107 CriticalSectionScoped cs(crit_.get());
108 recv_times_[header.timestamp - rtp_timestamp_delta_] =
109 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
110 }
111
112 return receiver_->DeliverPacket(packet, length);
113 }
114
115 virtual void PutFrame(const I420VideoFrame& video_frame,
116 uint32_t delta_capture_ms) OVERRIDE {
117 I420VideoFrame* copy = NULL;
118 {
119 CriticalSectionScoped cs(crit_.get());
120 if (frame_pool_.size() > 0) {
121 copy = frame_pool_.front();
122 frame_pool_.pop_front();
123 }
124 }
125 if (copy == NULL)
126 copy = new I420VideoFrame();
127
128 copy->CopyFrame(video_frame);
129 copy->set_timestamp(copy->render_time_ms() * 90);
130
131 {
132 CriticalSectionScoped cs(crit_.get());
133 if (first_send_frame_ == NULL && rtp_timestamp_delta_ == 0)
134 first_send_frame_ = copy;
135
136 frames_.push_back(copy);
137 }
138
139 input_->PutFrame(video_frame, delta_capture_ms);
140 }
141
142 virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
143 scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
144 RTPHeader header;
145 parser->Parse(packet, static_cast<int>(length), &header);
146
147 {
148 CriticalSectionScoped cs(crit_.get());
149 if (rtp_timestamp_delta_ == 0) {
150 rtp_timestamp_delta_ =
151 header.timestamp - first_send_frame_->timestamp();
152 first_send_frame_ = NULL;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000153 }
pbos@webrtc.org7fb9ce02013-08-05 09:29:50 +0000154 send_times_[header.timestamp - rtp_timestamp_delta_] =
155 Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000156 }
157
158 return transport_->SendRTP(packet, length);
159 }
160
161 virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
162 return transport_->SendRTCP(packet, length);
163 }
164
165 virtual void RenderFrame(const I420VideoFrame& video_frame,
166 int time_to_render_ms) OVERRIDE {
167 uint32_t send_timestamp = video_frame.timestamp() - rtp_timestamp_delta_;
168
169 {
170 CriticalSectionScoped cs(crit_.get());
171 while (frames_.front()->timestamp() < send_timestamp) {
172 AddFrameComparison(frames_.front(), &last_rendered_frame_, true);
173 frame_pool_.push_back(frames_.front());
174 frames_.pop_front();
175 }
176
177 I420VideoFrame* reference_frame = frames_.front();
178 frames_.pop_front();
179 assert(reference_frame != NULL);
180 assert(reference_frame->timestamp() == send_timestamp);
181
182 AddFrameComparison(reference_frame, &video_frame, false);
183 frame_pool_.push_back(reference_frame);
184
185 if (--frames_left_ == 0) {
186 PrintResult("psnr", psnr_, " dB");
187 PrintResult("ssim", ssim_, "");
188 PrintResult("sender_time", sender_time_, " ms");
189 PrintResult("receiver_time", receiver_time_, " ms");
190 PrintResult("total_delay_incl_network", end_to_end_, " ms");
191 PrintResult("time_between_rendered_frames", rendered_delta_, " ms");
192 EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_);
193 EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_);
194 trigger_->Set();
195 }
196 }
197
198 renderer_->RenderFrame(video_frame, time_to_render_ms);
199 last_rendered_frame_.CopyFrame(video_frame);
200 }
201
202 void Wait() { trigger_->Wait(WEBRTC_EVENT_INFINITE); }
203
pbos@webrtc.org74fa4892013-08-23 09:19:30 +0000204 VideoSendStreamInput* input_;
205 Transport* transport_;
206 VideoRenderer* renderer_;
207 PacketReceiver* receiver_;
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000208
209 private:
210 void AddFrameComparison(const I420VideoFrame* reference_frame,
211 const I420VideoFrame* render,
212 bool dropped) {
213 int64_t render_time = Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
214 psnr_.AddSample(I420PSNR(reference_frame, render));
215 ssim_.AddSample(I420SSIM(reference_frame, render));
216 if (dropped)
217 return;
218 if (last_render_time_ != 0)
219 rendered_delta_.AddSample(render_time - last_render_time_);
220 last_render_time_ = render_time;
221
222 int64_t input_time = reference_frame->render_time_ms();
223 int64_t send_time = send_times_[reference_frame->timestamp()];
224 send_times_.erase(reference_frame->timestamp());
225 sender_time_.AddSample(send_time - input_time);
226 int64_t recv_time = recv_times_[reference_frame->timestamp()];
227 recv_times_.erase(reference_frame->timestamp());
228 receiver_time_.AddSample(render_time - recv_time);
229 end_to_end_.AddSample(render_time - input_time);
230 }
231
232 void PrintResult(const char* result_type,
233 test::Statistics stats,
234 const char* unit) {
235 printf("RESULT %s: %s = {%f, %f}%s\n",
236 result_type,
237 test_label_,
238 stats.Mean(),
239 stats.StandardDeviation(),
240 unit);
241 }
242
243 const char* test_label_;
244 test::Statistics sender_time_;
245 test::Statistics receiver_time_;
246 test::Statistics psnr_;
247 test::Statistics ssim_;
248 test::Statistics end_to_end_;
249 test::Statistics rendered_delta_;
250
251 std::deque<I420VideoFrame*> frames_;
252 std::deque<I420VideoFrame*> frame_pool_;
253 I420VideoFrame last_rendered_frame_;
254 std::map<uint32_t, int64_t> send_times_;
255 std::map<uint32_t, int64_t> recv_times_;
256 uint32_t rtp_timestamp_delta_;
257 I420VideoFrame* first_send_frame_;
258 int64_t last_render_time_;
259 double avg_psnr_threshold_;
260 double avg_ssim_threshold_;
261 uint32_t frames_left_;
262 scoped_ptr<CriticalSectionWrapper> crit_;
263 scoped_ptr<EventWrapper> trigger_;
264};
265
henrike@webrtc.org60bdb072013-08-21 19:55:53 +0000266TEST_P(FullStackTest, DISABLED_NoPacketLoss) {
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000267 FullStackTestParams params = GetParam();
268
269 scoped_ptr<test::VideoRenderer> local_preview(test::VideoRenderer::Create(
270 "Local Preview", params.clip.width, params.clip.height));
271 scoped_ptr<test::VideoRenderer> loopback_video(test::VideoRenderer::Create(
272 "Loopback Video", params.clip.width, params.clip.height));
273
pbos@webrtc.org96684672013-08-12 12:59:04 +0000274 test::DirectTransport transport;
pbos@webrtc.orgf3f13582013-07-09 14:04:46 +0000275 VideoAnalyzer analyzer(
276 NULL,
277 &transport,
278 loopback_video.get(),
279 params.test_label,
280 params.avg_psnr_threshold,
281 params.avg_ssim_threshold,
282 static_cast<uint64_t>(FLAGS_seconds * params.clip.fps));
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000283
pbos@webrtc.org841c8a42013-09-09 15:04:25 +0000284 Call::Config call_config(&analyzer);
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000285
pbos@webrtc.org841c8a42013-09-09 15:04:25 +0000286 scoped_ptr<Call> call(Call::Create(call_config));
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000287 analyzer.receiver_ = call->Receiver();
288 transport.SetReceiver(&analyzer);
289
pbos@webrtc.org74fa4892013-08-23 09:19:30 +0000290 VideoSendStream::Config send_config = call->GetDefaultSendConfig();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000291 test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
292
293 send_config.local_renderer = local_preview.get();
294
295 // TODO(pbos): static_cast shouldn't be required after mflodman refactors the
296 // VideoCodec struct.
297 send_config.codec.width = static_cast<uint16_t>(params.clip.width);
298 send_config.codec.height = static_cast<uint16_t>(params.clip.height);
299 send_config.codec.minBitrate = params.bitrate;
300 send_config.codec.startBitrate = params.bitrate;
301 send_config.codec.maxBitrate = params.bitrate;
302
pbos@webrtc.org74fa4892013-08-23 09:19:30 +0000303 VideoSendStream* send_stream = call->CreateSendStream(send_config);
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000304 analyzer.input_ = send_stream->Input();
305
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000306 scoped_ptr<test::FrameGeneratorCapturer> file_capturer(
andresp@webrtc.orgab654952013-09-19 12:14:03 +0000307 test::FrameGeneratorCapturer::CreateFromYuvFile(
pbos@webrtc.org4c966012013-08-21 12:07:37 +0000308 &analyzer,
andresp@webrtc.orgab654952013-09-19 12:14:03 +0000309 test::ResourcePath(params.clip.name, "yuv").c_str(),
310 params.clip.width,
311 params.clip.height,
312 params.clip.fps,
313 Clock::GetRealTimeClock()));
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000314
pbos@webrtc.org841c8a42013-09-09 15:04:25 +0000315 VideoReceiveStream::Config receive_config = call->GetDefaultReceiveConfig();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000316 receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
317 receive_config.renderer = &analyzer;
318
pbos@webrtc.org74fa4892013-08-23 09:19:30 +0000319 VideoReceiveStream* receive_stream =
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000320 call->CreateReceiveStream(receive_config);
321
322 receive_stream->StartReceive();
323 send_stream->StartSend();
324
325 file_capturer->Start();
326
327 analyzer.Wait();
328
329 file_capturer->Stop();
330 send_stream->StopSend();
331 receive_stream->StopReceive();
332
333 call->DestroyReceiveStream(receive_stream);
334 call->DestroySendStream(send_stream);
pbos@webrtc.org96684672013-08-12 12:59:04 +0000335
336 transport.StopSending();
pbos@webrtc.orgaf8d5af2013-07-09 08:02:33 +0000337}
338
339INSTANTIATE_TEST_CASE_P(FullStack,
340 FullStackTest,
341 ::testing::Values(paris_qcif, foreman_cif));
342
343} // namespace webrtc