blob: 5ce963fb4a3af6e657fc1b5fbef6095c6be1321e [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/localvideosource.h"
29
30#include <string>
31#include <vector>
32
33#include "talk/app/webrtc/test/fakeconstraints.h"
34#include "talk/base/gunit.h"
35#include "talk/media/base/fakemediaengine.h"
36#include "talk/media/base/fakevideorenderer.h"
37#include "talk/media/devices/fakedevicemanager.h"
38#include "talk/session/media/channelmanager.h"
39
40using webrtc::FakeConstraints;
41using webrtc::LocalVideoSource;
42using webrtc::MediaConstraintsInterface;
43using webrtc::MediaSourceInterface;
44using webrtc::ObserverInterface;
45using webrtc::VideoSourceInterface;
46
47namespace {
48
49// Max wait time for a test.
50const int kMaxWaitMs = 100;
51
52} // anonymous namespace
53
54
55// TestVideoCapturer extends cricket::FakeVideoCapturer so it can be used for
56// testing without known camera formats.
57// It keeps its own lists of cricket::VideoFormats for the unit tests in this
58// file.
59class TestVideoCapturer : public cricket::FakeVideoCapturer {
60 public:
61 TestVideoCapturer() : test_without_formats_(false) {
62 std::vector<cricket::VideoFormat> formats;
63 formats.push_back(cricket::VideoFormat(1280, 720,
64 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
65 formats.push_back(cricket::VideoFormat(640, 480,
66 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
67 formats.push_back(cricket::VideoFormat(640, 400,
68 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
69 formats.push_back(cricket::VideoFormat(320, 240,
70 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
71 formats.push_back(cricket::VideoFormat(352, 288,
72 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
73 ResetSupportedFormats(formats);
74 }
75
76 // This function is used for resetting the supported capture formats and
77 // simulating a cricket::VideoCapturer implementation that don't support
78 // capture format enumeration. This is used to simulate the current
79 // Chrome implementation.
80 void TestWithoutCameraFormats() {
81 test_without_formats_ = true;
82 std::vector<cricket::VideoFormat> formats;
83 ResetSupportedFormats(formats);
84 }
85
86 virtual cricket::CaptureState Start(
87 const cricket::VideoFormat& capture_format) {
88 if (test_without_formats_) {
89 std::vector<cricket::VideoFormat> formats;
90 formats.push_back(capture_format);
91 ResetSupportedFormats(formats);
92 }
93 return FakeVideoCapturer::Start(capture_format);
94 }
95
96 virtual bool GetBestCaptureFormat(const cricket::VideoFormat& desired,
97 cricket::VideoFormat* best_format) {
98 if (test_without_formats_) {
99 *best_format = desired;
100 return true;
101 }
102 return FakeVideoCapturer::GetBestCaptureFormat(desired,
103 best_format);
104 }
105
106 private:
107 bool test_without_formats_;
108};
109
110class StateObserver : public ObserverInterface {
111 public:
112 explicit StateObserver(VideoSourceInterface* source)
113 : state_(source->state()),
114 source_(source) {
115 }
116 virtual void OnChanged() {
117 state_ = source_->state();
118 }
119 MediaSourceInterface::SourceState state() const { return state_; }
120
121 private:
122 MediaSourceInterface::SourceState state_;
123 talk_base::scoped_refptr<VideoSourceInterface> source_;
124};
125
126class LocalVideoSourceTest : public testing::Test {
127 protected:
128 LocalVideoSourceTest()
129 : channel_manager_(new cricket::ChannelManager(
130 new cricket::FakeMediaEngine(),
131 new cricket::FakeDeviceManager(), talk_base::Thread::Current())) {
132 }
133
134 void SetUp() {
135 ASSERT_TRUE(channel_manager_->Init());
136 capturer_ = new TestVideoCapturer();
137 }
138
139 void CreateLocalVideoSource() {
140 CreateLocalVideoSource(NULL);
141 }
142
143 void CreateLocalVideoSource(
144 const webrtc::MediaConstraintsInterface* constraints) {
145 // VideoSource take ownership of |capturer_|
146 local_source_ = LocalVideoSource::Create(channel_manager_.get(),
147 capturer_,
148 constraints);
149
150 ASSERT_TRUE(local_source_.get() != NULL);
151 EXPECT_EQ(capturer_, local_source_->GetVideoCapturer());
152
153 state_observer_.reset(new StateObserver(local_source_));
154 local_source_->RegisterObserver(state_observer_.get());
155 local_source_->AddSink(&renderer_);
156 }
157
158 TestVideoCapturer* capturer_; // Raw pointer. Owned by local_source_.
159 cricket::FakeVideoRenderer renderer_;
160 talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
161 talk_base::scoped_ptr<StateObserver> state_observer_;
162 talk_base::scoped_refptr<LocalVideoSource> local_source_;
163};
164
165
166// Test that a LocalVideoSource transition to kLive state when the capture
167// device have started and kEnded if it is stopped.
168// It also test that an output can receive video frames.
169TEST_F(LocalVideoSourceTest, StartStop) {
170 // Initialize without constraints.
171 CreateLocalVideoSource();
172 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
173 kMaxWaitMs);
174
175 ASSERT_TRUE(capturer_->CaptureFrame());
176 EXPECT_EQ(1, renderer_.num_rendered_frames());
177
178 capturer_->Stop();
179 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
180 kMaxWaitMs);
181}
182
183// Test that a LocalVideoSource transition to kEnded if the capture device
184// fails.
185TEST_F(LocalVideoSourceTest, CameraFailed) {
186 CreateLocalVideoSource();
187 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
188 kMaxWaitMs);
189
190 capturer_->SignalStateChange(capturer_, cricket::CS_FAILED);
191 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
192 kMaxWaitMs);
193}
194
195// Test that the capture output is CIF if we set max constraints to CIF.
196// and the capture device support CIF.
197TEST_F(LocalVideoSourceTest, MandatoryConstraintCif5Fps) {
198 FakeConstraints constraints;
199 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352);
200 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288);
201 constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 5);
202
203 CreateLocalVideoSource(&constraints);
204 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
205 kMaxWaitMs);
206 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
207 ASSERT_TRUE(format != NULL);
208 EXPECT_EQ(352, format->width);
209 EXPECT_EQ(288, format->height);
210 EXPECT_EQ(5, format->framerate());
211}
212
213// Test that the capture output is 720P if the camera support it and the
214// optional constraint is set to 720P.
215TEST_F(LocalVideoSourceTest, MandatoryMinVgaOptional720P) {
216 FakeConstraints constraints;
217 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640);
218 constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480);
219 constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280);
220 constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio,
221 1280.0 / 720);
222
223 CreateLocalVideoSource(&constraints);
224 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
225 kMaxWaitMs);
226 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
227 ASSERT_TRUE(format != NULL);
228 EXPECT_EQ(1280, format->width);
229 EXPECT_EQ(720, format->height);
230 EXPECT_EQ(30, format->framerate());
231}
232
233// Test that the capture output have aspect ratio 4:3 if a mandatory constraint
234// require it even if an optional constraint request a higher resolution
235// that don't have this aspect ratio.
236TEST_F(LocalVideoSourceTest, MandatoryAspectRatio4To3) {
237 FakeConstraints constraints;
238 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640);
239 constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480);
240 constraints.AddMandatory(MediaConstraintsInterface::kMaxAspectRatio,
241 640.0 / 480);
242 constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280);
243
244 CreateLocalVideoSource(&constraints);
245 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
246 kMaxWaitMs);
247 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
248 ASSERT_TRUE(format != NULL);
249 EXPECT_EQ(640, format->width);
250 EXPECT_EQ(480, format->height);
251 EXPECT_EQ(30, format->framerate());
252}
253
254
255// Test that the source state transition to kEnded if the mandatory aspect ratio
256// is set higher than supported.
257TEST_F(LocalVideoSourceTest, MandatoryAspectRatioTooHigh) {
258 FakeConstraints constraints;
259 constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, 2);
260 CreateLocalVideoSource(&constraints);
261 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
262 kMaxWaitMs);
263}
264
265// Test that the source ignores an optional aspect ratio that is higher than
266// supported.
267TEST_F(LocalVideoSourceTest, OptionalAspectRatioTooHigh) {
268 FakeConstraints constraints;
269 constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, 2);
270 CreateLocalVideoSource(&constraints);
271 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
272 kMaxWaitMs);
273 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
274 ASSERT_TRUE(format != NULL);
275 double aspect_ratio = static_cast<double>(format->width) / format->height;
276 EXPECT_LT(aspect_ratio, 2);
277}
278
279// Test that the source starts video with the default resolution if the
280// camera doesn't support capability enumeration and there are no constraints.
281TEST_F(LocalVideoSourceTest, NoCameraCapability) {
282 capturer_->TestWithoutCameraFormats();
283
284 CreateLocalVideoSource();
285 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
286 kMaxWaitMs);
287 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
288 ASSERT_TRUE(format != NULL);
289 EXPECT_EQ(640, format->width);
290 EXPECT_EQ(480, format->height);
291 EXPECT_EQ(30, format->framerate());
292}
293
294// Test that the source can start the video and get the requested aspect ratio
295// if the camera doesn't support capability enumeration and the aspect ratio is
296// set.
297TEST_F(LocalVideoSourceTest, NoCameraCapability16To9Ratio) {
298 capturer_->TestWithoutCameraFormats();
299
300 FakeConstraints constraints;
301 double requested_aspect_ratio = 640.0 / 360;
302 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640);
303 constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio,
304 requested_aspect_ratio);
305
306 CreateLocalVideoSource(&constraints);
307 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
308 kMaxWaitMs);
309 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
310 double aspect_ratio = static_cast<double>(format->width) / format->height;
311 EXPECT_LE(requested_aspect_ratio, aspect_ratio);
312}
313
314// Test that the source state transitions to kEnded if an unknown mandatory
315// constraint is found.
316TEST_F(LocalVideoSourceTest, InvalidMandatoryConstraint) {
317 FakeConstraints constraints;
318 constraints.AddMandatory("weird key", 640);
319
320 CreateLocalVideoSource(&constraints);
321 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
322 kMaxWaitMs);
323}
324
325// Test that the source ignores an unknown optional constraint.
326TEST_F(LocalVideoSourceTest, InvalidOptionalConstraint) {
327 FakeConstraints constraints;
328 constraints.AddOptional("weird key", 640);
329
330 CreateLocalVideoSource(&constraints);
331 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
332 kMaxWaitMs);
333}
334
335TEST_F(LocalVideoSourceTest, SetValidOptionValues) {
336 FakeConstraints constraints;
337 constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, "false");
338 constraints.AddMandatory(
339 MediaConstraintsInterface::kTemporalLayeredScreencast, "false");
340 constraints.AddOptional(
341 MediaConstraintsInterface::kLeakyBucket, "true");
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000342 constraints.AddOptional(
343 MediaConstraintsInterface::kCpuOveruseDetection, "true");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344
345 CreateLocalVideoSource(&constraints);
346
347 bool value = true;
348 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value));
349 EXPECT_FALSE(value);
350 EXPECT_TRUE(local_source_->options()->
351 video_temporal_layer_screencast.Get(&value));
352 EXPECT_FALSE(value);
353 EXPECT_TRUE(local_source_->options()->video_leaky_bucket.Get(&value));
354 EXPECT_TRUE(value);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000355 EXPECT_TRUE(local_source_->options()->
356 cpu_overuse_detection.GetWithDefaultIfUnset(false));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000357}
358
359TEST_F(LocalVideoSourceTest, OptionNotSet) {
360 FakeConstraints constraints;
361 CreateLocalVideoSource(&constraints);
362 bool value;
363 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value));
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000364 EXPECT_FALSE(local_source_->options()->cpu_overuse_detection.Get(&value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000365}
366
367TEST_F(LocalVideoSourceTest, MandatoryOptionOverridesOptional) {
368 FakeConstraints constraints;
369 constraints.AddMandatory(
370 MediaConstraintsInterface::kNoiseReduction, true);
371 constraints.AddOptional(
372 MediaConstraintsInterface::kNoiseReduction, false);
373
374 CreateLocalVideoSource(&constraints);
375
376 bool value = false;
377 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value));
378 EXPECT_TRUE(value);
379 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value));
380}
381
382TEST_F(LocalVideoSourceTest, InvalidOptionKeyOptional) {
383 FakeConstraints constraints;
384 constraints.AddOptional(
385 MediaConstraintsInterface::kNoiseReduction, false);
386 constraints.AddOptional("invalidKey", false);
387
388 CreateLocalVideoSource(&constraints);
389
390 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
391 kMaxWaitMs);
392 bool value = true;
393 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value));
394 EXPECT_FALSE(value);
395}
396
397TEST_F(LocalVideoSourceTest, InvalidOptionKeyMandatory) {
398 FakeConstraints constraints;
399 constraints.AddMandatory(
400 MediaConstraintsInterface::kNoiseReduction, false);
401 constraints.AddMandatory("invalidKey", false);
402
403 CreateLocalVideoSource(&constraints);
404
405 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
406 kMaxWaitMs);
407 bool value;
408 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value));
409}
410
411TEST_F(LocalVideoSourceTest, InvalidOptionValueOptional) {
412 FakeConstraints constraints;
413 constraints.AddOptional(
414 MediaConstraintsInterface::kNoiseReduction, "true");
415 constraints.AddOptional(
416 MediaConstraintsInterface::kLeakyBucket, "not boolean");
417
418 CreateLocalVideoSource(&constraints);
419
420 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
421 kMaxWaitMs);
422 bool value = false;
423 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value));
424 EXPECT_TRUE(value);
425 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value));
426}
427
428TEST_F(LocalVideoSourceTest, InvalidOptionValueMandatory) {
429 FakeConstraints constraints;
430 // Optional constraints should be ignored if the mandatory constraints fail.
431 constraints.AddOptional(
432 MediaConstraintsInterface::kNoiseReduction, "false");
433 // Values are case-sensitive and must be all lower-case.
434 constraints.AddMandatory(
435 MediaConstraintsInterface::kLeakyBucket, "True");
436
437 CreateLocalVideoSource(&constraints);
438
439 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
440 kMaxWaitMs);
441 bool value;
442 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value));
443}
444
445TEST_F(LocalVideoSourceTest, MixedOptionsAndConstraints) {
446 FakeConstraints constraints;
447 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352);
448 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288);
449 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 5);
450
451 constraints.AddMandatory(
452 MediaConstraintsInterface::kNoiseReduction, false);
453 constraints.AddOptional(
454 MediaConstraintsInterface::kNoiseReduction, true);
455
456 CreateLocalVideoSource(&constraints);
457 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
458 kMaxWaitMs);
459 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
460 ASSERT_TRUE(format != NULL);
461 EXPECT_EQ(352, format->width);
462 EXPECT_EQ(288, format->height);
463 EXPECT_EQ(5, format->framerate());
464
465 bool value = true;
466 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value));
467 EXPECT_FALSE(value);
468 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value));
469}
470
471// Tests that the source starts video with the default resolution for
472// screencast if no constraint is set.
473TEST_F(LocalVideoSourceTest, ScreencastResolutionNoConstraint) {
474 capturer_->TestWithoutCameraFormats();
475 capturer_->SetScreencast(true);
476
477 CreateLocalVideoSource();
478 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
479 kMaxWaitMs);
480 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
481 ASSERT_TRUE(format != NULL);
482 EXPECT_EQ(640, format->width);
483 EXPECT_EQ(480, format->height);
484 EXPECT_EQ(30, format->framerate());
485}
486
487// Tests that the source starts video with the max width and height set by
488// constraints for screencast.
489TEST_F(LocalVideoSourceTest, ScreencastResolutionWithConstraint) {
490 FakeConstraints constraints;
491 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 480);
492 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 270);
493
494 capturer_->TestWithoutCameraFormats();
495 capturer_->SetScreencast(true);
496
497 CreateLocalVideoSource(&constraints);
498 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
499 kMaxWaitMs);
500 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
501 ASSERT_TRUE(format != NULL);
502 EXPECT_EQ(480, format->width);
503 EXPECT_EQ(270, format->height);
504 EXPECT_EQ(30, format->framerate());
505}
506
507TEST_F(LocalVideoSourceTest, MandatorySubOneFpsConstraints) {
508 FakeConstraints constraints;
509 constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 0.5);
510
511 CreateLocalVideoSource(&constraints);
512 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
513 kMaxWaitMs);
514 ASSERT_TRUE(capturer_->GetCaptureFormat() == NULL);
515}
516
517TEST_F(LocalVideoSourceTest, OptionalSubOneFpsConstraints) {
518 FakeConstraints constraints;
519 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 0.5);
520
521 CreateLocalVideoSource(&constraints);
522 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
523 kMaxWaitMs);
524 const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
525 ASSERT_TRUE(format != NULL);
526 EXPECT_EQ(1, format->framerate());
527}
528