1 /* 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 40 using webrtc::FakeConstraints; 41 using webrtc::LocalVideoSource; 42 using webrtc::MediaConstraintsInterface; 43 using webrtc::MediaSourceInterface; 44 using webrtc::ObserverInterface; 45 using webrtc::VideoSourceInterface; 46 47 namespace { 48 49 // Max wait time for a test. 50 const 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. 59 class 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 110 class 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 126 class 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. 169 TEST_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. 185 TEST_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. 197 TEST_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. 215 TEST_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. 236 TEST_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. 257 TEST_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. 267 TEST_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. 281 TEST_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. 297 TEST_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. 316 TEST_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. 326 TEST_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 335 TEST_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"); 342 343 CreateLocalVideoSource(&constraints); 344 345 bool value = true; 346 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value)); 347 EXPECT_FALSE(value); 348 EXPECT_TRUE(local_source_->options()-> 349 video_temporal_layer_screencast.Get(&value)); 350 EXPECT_FALSE(value); 351 EXPECT_TRUE(local_source_->options()->video_leaky_bucket.Get(&value)); 352 EXPECT_TRUE(value); 353 } 354 355 TEST_F(LocalVideoSourceTest, OptionNotSet) { 356 FakeConstraints constraints; 357 CreateLocalVideoSource(&constraints); 358 bool value; 359 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value)); 360 } 361 362 TEST_F(LocalVideoSourceTest, MandatoryOptionOverridesOptional) { 363 FakeConstraints constraints; 364 constraints.AddMandatory( 365 MediaConstraintsInterface::kNoiseReduction, true); 366 constraints.AddOptional( 367 MediaConstraintsInterface::kNoiseReduction, false); 368 369 CreateLocalVideoSource(&constraints); 370 371 bool value = false; 372 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value)); 373 EXPECT_TRUE(value); 374 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value)); 375 } 376 377 TEST_F(LocalVideoSourceTest, InvalidOptionKeyOptional) { 378 FakeConstraints constraints; 379 constraints.AddOptional( 380 MediaConstraintsInterface::kNoiseReduction, false); 381 constraints.AddOptional("invalidKey", false); 382 383 CreateLocalVideoSource(&constraints); 384 385 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 386 kMaxWaitMs); 387 bool value = true; 388 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value)); 389 EXPECT_FALSE(value); 390 } 391 392 TEST_F(LocalVideoSourceTest, InvalidOptionKeyMandatory) { 393 FakeConstraints constraints; 394 constraints.AddMandatory( 395 MediaConstraintsInterface::kNoiseReduction, false); 396 constraints.AddMandatory("invalidKey", false); 397 398 CreateLocalVideoSource(&constraints); 399 400 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 401 kMaxWaitMs); 402 bool value; 403 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value)); 404 } 405 406 TEST_F(LocalVideoSourceTest, InvalidOptionValueOptional) { 407 FakeConstraints constraints; 408 constraints.AddOptional( 409 MediaConstraintsInterface::kNoiseReduction, "true"); 410 constraints.AddOptional( 411 MediaConstraintsInterface::kLeakyBucket, "not boolean"); 412 413 CreateLocalVideoSource(&constraints); 414 415 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 416 kMaxWaitMs); 417 bool value = false; 418 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value)); 419 EXPECT_TRUE(value); 420 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value)); 421 } 422 423 TEST_F(LocalVideoSourceTest, InvalidOptionValueMandatory) { 424 FakeConstraints constraints; 425 // Optional constraints should be ignored if the mandatory constraints fail. 426 constraints.AddOptional( 427 MediaConstraintsInterface::kNoiseReduction, "false"); 428 // Values are case-sensitive and must be all lower-case. 429 constraints.AddMandatory( 430 MediaConstraintsInterface::kLeakyBucket, "True"); 431 432 CreateLocalVideoSource(&constraints); 433 434 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 435 kMaxWaitMs); 436 bool value; 437 EXPECT_FALSE(local_source_->options()->video_noise_reduction.Get(&value)); 438 } 439 440 TEST_F(LocalVideoSourceTest, MixedOptionsAndConstraints) { 441 FakeConstraints constraints; 442 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352); 443 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288); 444 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 5); 445 446 constraints.AddMandatory( 447 MediaConstraintsInterface::kNoiseReduction, false); 448 constraints.AddOptional( 449 MediaConstraintsInterface::kNoiseReduction, true); 450 451 CreateLocalVideoSource(&constraints); 452 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 453 kMaxWaitMs); 454 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 455 ASSERT_TRUE(format != NULL); 456 EXPECT_EQ(352, format->width); 457 EXPECT_EQ(288, format->height); 458 EXPECT_EQ(5, format->framerate()); 459 460 bool value = true; 461 EXPECT_TRUE(local_source_->options()->video_noise_reduction.Get(&value)); 462 EXPECT_FALSE(value); 463 EXPECT_FALSE(local_source_->options()->video_leaky_bucket.Get(&value)); 464 } 465 466 // Tests that the source starts video with the default resolution for 467 // screencast if no constraint is set. 468 TEST_F(LocalVideoSourceTest, ScreencastResolutionNoConstraint) { 469 capturer_->TestWithoutCameraFormats(); 470 capturer_->SetScreencast(true); 471 472 CreateLocalVideoSource(); 473 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 474 kMaxWaitMs); 475 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 476 ASSERT_TRUE(format != NULL); 477 EXPECT_EQ(640, format->width); 478 EXPECT_EQ(480, format->height); 479 EXPECT_EQ(30, format->framerate()); 480 } 481 482 // Tests that the source starts video with the max width and height set by 483 // constraints for screencast. 484 TEST_F(LocalVideoSourceTest, ScreencastResolutionWithConstraint) { 485 FakeConstraints constraints; 486 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 480); 487 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 270); 488 489 capturer_->TestWithoutCameraFormats(); 490 capturer_->SetScreencast(true); 491 492 CreateLocalVideoSource(&constraints); 493 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 494 kMaxWaitMs); 495 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 496 ASSERT_TRUE(format != NULL); 497 EXPECT_EQ(480, format->width); 498 EXPECT_EQ(270, format->height); 499 EXPECT_EQ(30, format->framerate()); 500 } 501 502 TEST_F(LocalVideoSourceTest, MandatorySubOneFpsConstraints) { 503 FakeConstraints constraints; 504 constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 0.5); 505 506 CreateLocalVideoSource(&constraints); 507 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 508 kMaxWaitMs); 509 ASSERT_TRUE(capturer_->GetCaptureFormat() == NULL); 510 } 511 512 TEST_F(LocalVideoSourceTest, OptionalSubOneFpsConstraints) { 513 FakeConstraints constraints; 514 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 0.5); 515 516 CreateLocalVideoSource(&constraints); 517 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 518 kMaxWaitMs); 519 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 520 ASSERT_TRUE(format != NULL); 521 EXPECT_EQ(1, format->framerate()); 522 } 523 524