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 <string> 29#include <vector> 30 31#include "talk/app/webrtc/remotevideocapturer.h" 32#include "talk/app/webrtc/test/fakeconstraints.h" 33#include "talk/app/webrtc/videosource.h" 34#include "talk/media/base/fakemediaengine.h" 35#include "talk/media/base/fakevideorenderer.h" 36#include "talk/media/devices/fakedevicemanager.h" 37#include "talk/media/webrtc/webrtcvideoframe.h" 38#include "talk/session/media/channelmanager.h" 39#include "webrtc/base/gunit.h" 40 41using webrtc::FakeConstraints; 42using webrtc::VideoSource; 43using webrtc::MediaConstraintsInterface; 44using webrtc::MediaSourceInterface; 45using webrtc::ObserverInterface; 46using webrtc::VideoSourceInterface; 47 48namespace { 49 50// Max wait time for a test. 51const int kMaxWaitMs = 100; 52 53} // anonymous namespace 54 55 56// TestVideoCapturer extends cricket::FakeVideoCapturer so it can be used for 57// testing without known camera formats. 58// It keeps its own lists of cricket::VideoFormats for the unit tests in this 59// file. 60class TestVideoCapturer : public cricket::FakeVideoCapturer { 61 public: 62 TestVideoCapturer() : test_without_formats_(false) { 63 std::vector<cricket::VideoFormat> formats; 64 formats.push_back(cricket::VideoFormat(1280, 720, 65 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); 66 formats.push_back(cricket::VideoFormat(640, 480, 67 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); 68 formats.push_back(cricket::VideoFormat(640, 400, 69 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); 70 formats.push_back(cricket::VideoFormat(320, 240, 71 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); 72 formats.push_back(cricket::VideoFormat(352, 288, 73 cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); 74 ResetSupportedFormats(formats); 75 } 76 77 // This function is used for resetting the supported capture formats and 78 // simulating a cricket::VideoCapturer implementation that don't support 79 // capture format enumeration. This is used to simulate the current 80 // Chrome implementation. 81 void TestWithoutCameraFormats() { 82 test_without_formats_ = true; 83 std::vector<cricket::VideoFormat> formats; 84 ResetSupportedFormats(formats); 85 } 86 87 virtual cricket::CaptureState Start( 88 const cricket::VideoFormat& capture_format) { 89 if (test_without_formats_) { 90 std::vector<cricket::VideoFormat> formats; 91 formats.push_back(capture_format); 92 ResetSupportedFormats(formats); 93 } 94 return FakeVideoCapturer::Start(capture_format); 95 } 96 97 virtual bool GetBestCaptureFormat(const cricket::VideoFormat& desired, 98 cricket::VideoFormat* best_format) { 99 if (test_without_formats_) { 100 *best_format = desired; 101 return true; 102 } 103 return FakeVideoCapturer::GetBestCaptureFormat(desired, 104 best_format); 105 } 106 107 private: 108 bool test_without_formats_; 109}; 110 111class StateObserver : public ObserverInterface { 112 public: 113 explicit StateObserver(VideoSourceInterface* source) 114 : state_(source->state()), 115 source_(source) { 116 } 117 virtual void OnChanged() { 118 state_ = source_->state(); 119 } 120 MediaSourceInterface::SourceState state() const { return state_; } 121 122 private: 123 MediaSourceInterface::SourceState state_; 124 rtc::scoped_refptr<VideoSourceInterface> source_; 125}; 126 127class VideoSourceTest : public testing::Test { 128 protected: 129 VideoSourceTest() 130 : capturer_cleanup_(new TestVideoCapturer()), 131 capturer_(capturer_cleanup_.get()), 132 channel_manager_(new cricket::ChannelManager( 133 new cricket::FakeMediaEngine(), 134 new cricket::FakeDeviceManager(), rtc::Thread::Current())) { 135 } 136 137 void SetUp() { 138 ASSERT_TRUE(channel_manager_->Init()); 139 } 140 141 void CreateVideoSource() { 142 CreateVideoSource(NULL); 143 } 144 145 void CreateVideoSource( 146 const webrtc::MediaConstraintsInterface* constraints) { 147 // VideoSource take ownership of |capturer_| 148 source_ = VideoSource::Create(channel_manager_.get(), 149 capturer_cleanup_.release(), 150 constraints); 151 152 ASSERT_TRUE(source_.get() != NULL); 153 EXPECT_EQ(capturer_, source_->GetVideoCapturer()); 154 155 state_observer_.reset(new StateObserver(source_)); 156 source_->RegisterObserver(state_observer_.get()); 157 source_->AddSink(&renderer_); 158 } 159 160 rtc::scoped_ptr<TestVideoCapturer> capturer_cleanup_; 161 TestVideoCapturer* capturer_; 162 cricket::FakeVideoRenderer renderer_; 163 rtc::scoped_ptr<cricket::ChannelManager> channel_manager_; 164 rtc::scoped_ptr<StateObserver> state_observer_; 165 rtc::scoped_refptr<VideoSource> source_; 166}; 167 168 169// Test that a VideoSource transition to kLive state when the capture 170// device have started and kEnded if it is stopped. 171// It also test that an output can receive video frames. 172TEST_F(VideoSourceTest, StartStop) { 173 // Initialize without constraints. 174 CreateVideoSource(); 175 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 176 kMaxWaitMs); 177 178 ASSERT_TRUE(capturer_->CaptureFrame()); 179 EXPECT_EQ(1, renderer_.num_rendered_frames()); 180 181 capturer_->Stop(); 182 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 183 kMaxWaitMs); 184} 185 186// Test start stop with a remote VideoSource - the video source that has a 187// RemoteVideoCapturer and takes video frames from FrameInput. 188TEST_F(VideoSourceTest, StartStopRemote) { 189 source_ = VideoSource::Create(channel_manager_.get(), 190 new webrtc::RemoteVideoCapturer(), 191 NULL); 192 193 ASSERT_TRUE(source_.get() != NULL); 194 EXPECT_TRUE(NULL != source_->GetVideoCapturer()); 195 196 state_observer_.reset(new StateObserver(source_)); 197 source_->RegisterObserver(state_observer_.get()); 198 source_->AddSink(&renderer_); 199 200 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 201 kMaxWaitMs); 202 203 cricket::VideoRenderer* frameinput = source_->FrameInput(); 204 cricket::WebRtcVideoFrame test_frame; 205 frameinput->SetSize(1280, 720, 0); 206 frameinput->RenderFrame(&test_frame); 207 EXPECT_EQ(1, renderer_.num_rendered_frames()); 208 209 source_->GetVideoCapturer()->Stop(); 210 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 211 kMaxWaitMs); 212} 213 214// Test that a VideoSource transition to kEnded if the capture device 215// fails. 216TEST_F(VideoSourceTest, CameraFailed) { 217 CreateVideoSource(); 218 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 219 kMaxWaitMs); 220 221 capturer_->SignalStateChange(capturer_, cricket::CS_FAILED); 222 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 223 kMaxWaitMs); 224} 225 226// Test that the capture output is CIF if we set max constraints to CIF. 227// and the capture device support CIF. 228TEST_F(VideoSourceTest, MandatoryConstraintCif5Fps) { 229 FakeConstraints constraints; 230 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352); 231 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288); 232 constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 5); 233 234 CreateVideoSource(&constraints); 235 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 236 kMaxWaitMs); 237 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 238 ASSERT_TRUE(format != NULL); 239 EXPECT_EQ(352, format->width); 240 EXPECT_EQ(288, format->height); 241 EXPECT_EQ(30, format->framerate()); 242} 243 244// Test that the capture output is 720P if the camera support it and the 245// optional constraint is set to 720P. 246TEST_F(VideoSourceTest, MandatoryMinVgaOptional720P) { 247 FakeConstraints constraints; 248 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); 249 constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480); 250 constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280); 251 constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, 252 1280.0 / 720); 253 254 CreateVideoSource(&constraints); 255 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 256 kMaxWaitMs); 257 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 258 ASSERT_TRUE(format != NULL); 259 EXPECT_EQ(1280, format->width); 260 EXPECT_EQ(720, format->height); 261 EXPECT_EQ(30, format->framerate()); 262} 263 264// Test that the capture output have aspect ratio 4:3 if a mandatory constraint 265// require it even if an optional constraint request a higher resolution 266// that don't have this aspect ratio. 267TEST_F(VideoSourceTest, MandatoryAspectRatio4To3) { 268 FakeConstraints constraints; 269 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); 270 constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480); 271 constraints.AddMandatory(MediaConstraintsInterface::kMaxAspectRatio, 272 640.0 / 480); 273 constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280); 274 275 CreateVideoSource(&constraints); 276 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 277 kMaxWaitMs); 278 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 279 ASSERT_TRUE(format != NULL); 280 EXPECT_EQ(640, format->width); 281 EXPECT_EQ(480, format->height); 282 EXPECT_EQ(30, format->framerate()); 283} 284 285 286// Test that the source state transition to kEnded if the mandatory aspect ratio 287// is set higher than supported. 288TEST_F(VideoSourceTest, MandatoryAspectRatioTooHigh) { 289 FakeConstraints constraints; 290 constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, 2); 291 CreateVideoSource(&constraints); 292 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 293 kMaxWaitMs); 294} 295 296// Test that the source ignores an optional aspect ratio that is higher than 297// supported. 298TEST_F(VideoSourceTest, OptionalAspectRatioTooHigh) { 299 FakeConstraints constraints; 300 constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, 2); 301 CreateVideoSource(&constraints); 302 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 303 kMaxWaitMs); 304 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 305 ASSERT_TRUE(format != NULL); 306 double aspect_ratio = static_cast<double>(format->width) / format->height; 307 EXPECT_LT(aspect_ratio, 2); 308} 309 310// Test that the source starts video with the default resolution if the 311// camera doesn't support capability enumeration and there are no constraints. 312TEST_F(VideoSourceTest, NoCameraCapability) { 313 capturer_->TestWithoutCameraFormats(); 314 315 CreateVideoSource(); 316 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 317 kMaxWaitMs); 318 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 319 ASSERT_TRUE(format != NULL); 320 EXPECT_EQ(640, format->width); 321 EXPECT_EQ(480, format->height); 322 EXPECT_EQ(30, format->framerate()); 323} 324 325// Test that the source can start the video and get the requested aspect ratio 326// if the camera doesn't support capability enumeration and the aspect ratio is 327// set. 328TEST_F(VideoSourceTest, NoCameraCapability16To9Ratio) { 329 capturer_->TestWithoutCameraFormats(); 330 331 FakeConstraints constraints; 332 double requested_aspect_ratio = 640.0 / 360; 333 constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); 334 constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, 335 requested_aspect_ratio); 336 337 CreateVideoSource(&constraints); 338 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 339 kMaxWaitMs); 340 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 341 double aspect_ratio = static_cast<double>(format->width) / format->height; 342 EXPECT_LE(requested_aspect_ratio, aspect_ratio); 343} 344 345// Test that the source state transitions to kEnded if an unknown mandatory 346// constraint is found. 347TEST_F(VideoSourceTest, InvalidMandatoryConstraint) { 348 FakeConstraints constraints; 349 constraints.AddMandatory("weird key", 640); 350 351 CreateVideoSource(&constraints); 352 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 353 kMaxWaitMs); 354} 355 356// Test that the source ignores an unknown optional constraint. 357TEST_F(VideoSourceTest, InvalidOptionalConstraint) { 358 FakeConstraints constraints; 359 constraints.AddOptional("weird key", 640); 360 361 CreateVideoSource(&constraints); 362 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 363 kMaxWaitMs); 364} 365 366TEST_F(VideoSourceTest, SetValidOptionValues) { 367 FakeConstraints constraints; 368 constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, "false"); 369 constraints.AddMandatory( 370 MediaConstraintsInterface::kTemporalLayeredScreencast, "false"); 371 constraints.AddOptional( 372 MediaConstraintsInterface::kLeakyBucket, "true"); 373 374 CreateVideoSource(&constraints); 375 376 bool value = true; 377 EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); 378 EXPECT_FALSE(value); 379 EXPECT_TRUE(source_->options()-> 380 video_temporal_layer_screencast.Get(&value)); 381 EXPECT_FALSE(value); 382 EXPECT_TRUE(source_->options()->video_leaky_bucket.Get(&value)); 383 EXPECT_TRUE(value); 384} 385 386TEST_F(VideoSourceTest, OptionNotSet) { 387 FakeConstraints constraints; 388 CreateVideoSource(&constraints); 389 bool value; 390 EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value)); 391} 392 393TEST_F(VideoSourceTest, MandatoryOptionOverridesOptional) { 394 FakeConstraints constraints; 395 constraints.AddMandatory( 396 MediaConstraintsInterface::kNoiseReduction, true); 397 constraints.AddOptional( 398 MediaConstraintsInterface::kNoiseReduction, false); 399 400 CreateVideoSource(&constraints); 401 402 bool value = false; 403 EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); 404 EXPECT_TRUE(value); 405 EXPECT_FALSE(source_->options()->video_leaky_bucket.Get(&value)); 406} 407 408TEST_F(VideoSourceTest, InvalidOptionKeyOptional) { 409 FakeConstraints constraints; 410 constraints.AddOptional( 411 MediaConstraintsInterface::kNoiseReduction, false); 412 constraints.AddOptional("invalidKey", false); 413 414 CreateVideoSource(&constraints); 415 416 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 417 kMaxWaitMs); 418 bool value = true; 419 EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); 420 EXPECT_FALSE(value); 421} 422 423TEST_F(VideoSourceTest, InvalidOptionKeyMandatory) { 424 FakeConstraints constraints; 425 constraints.AddMandatory( 426 MediaConstraintsInterface::kNoiseReduction, false); 427 constraints.AddMandatory("invalidKey", false); 428 429 CreateVideoSource(&constraints); 430 431 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 432 kMaxWaitMs); 433 bool value; 434 EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value)); 435} 436 437TEST_F(VideoSourceTest, InvalidOptionValueOptional) { 438 FakeConstraints constraints; 439 constraints.AddOptional( 440 MediaConstraintsInterface::kNoiseReduction, "true"); 441 constraints.AddOptional( 442 MediaConstraintsInterface::kLeakyBucket, "not boolean"); 443 444 CreateVideoSource(&constraints); 445 446 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 447 kMaxWaitMs); 448 bool value = false; 449 EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); 450 EXPECT_TRUE(value); 451 EXPECT_FALSE(source_->options()->video_leaky_bucket.Get(&value)); 452} 453 454TEST_F(VideoSourceTest, InvalidOptionValueMandatory) { 455 FakeConstraints constraints; 456 // Optional constraints should be ignored if the mandatory constraints fail. 457 constraints.AddOptional( 458 MediaConstraintsInterface::kNoiseReduction, "false"); 459 // Values are case-sensitive and must be all lower-case. 460 constraints.AddMandatory( 461 MediaConstraintsInterface::kLeakyBucket, "True"); 462 463 CreateVideoSource(&constraints); 464 465 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 466 kMaxWaitMs); 467 bool value; 468 EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value)); 469} 470 471TEST_F(VideoSourceTest, MixedOptionsAndConstraints) { 472 FakeConstraints constraints; 473 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352); 474 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288); 475 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 5); 476 477 constraints.AddMandatory( 478 MediaConstraintsInterface::kNoiseReduction, false); 479 constraints.AddOptional( 480 MediaConstraintsInterface::kNoiseReduction, true); 481 482 CreateVideoSource(&constraints); 483 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 484 kMaxWaitMs); 485 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 486 ASSERT_TRUE(format != NULL); 487 EXPECT_EQ(352, format->width); 488 EXPECT_EQ(288, format->height); 489 EXPECT_EQ(30, format->framerate()); 490 491 bool value = true; 492 EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); 493 EXPECT_FALSE(value); 494 EXPECT_FALSE(source_->options()->video_leaky_bucket.Get(&value)); 495} 496 497// Tests that the source starts video with the default resolution for 498// screencast if no constraint is set. 499TEST_F(VideoSourceTest, ScreencastResolutionNoConstraint) { 500 capturer_->TestWithoutCameraFormats(); 501 capturer_->SetScreencast(true); 502 503 CreateVideoSource(); 504 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 505 kMaxWaitMs); 506 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 507 ASSERT_TRUE(format != NULL); 508 EXPECT_EQ(640, format->width); 509 EXPECT_EQ(480, format->height); 510 EXPECT_EQ(30, format->framerate()); 511} 512 513// Tests that the source starts video with the max width and height set by 514// constraints for screencast. 515TEST_F(VideoSourceTest, ScreencastResolutionWithConstraint) { 516 FakeConstraints constraints; 517 constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 480); 518 constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 270); 519 520 capturer_->TestWithoutCameraFormats(); 521 capturer_->SetScreencast(true); 522 523 CreateVideoSource(&constraints); 524 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 525 kMaxWaitMs); 526 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 527 ASSERT_TRUE(format != NULL); 528 EXPECT_EQ(480, format->width); 529 EXPECT_EQ(270, format->height); 530 EXPECT_EQ(30, format->framerate()); 531} 532 533TEST_F(VideoSourceTest, MandatorySubOneFpsConstraints) { 534 FakeConstraints constraints; 535 constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 0.5); 536 537 CreateVideoSource(&constraints); 538 EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), 539 kMaxWaitMs); 540 ASSERT_TRUE(capturer_->GetCaptureFormat() == NULL); 541} 542 543TEST_F(VideoSourceTest, OptionalSubOneFpsConstraints) { 544 FakeConstraints constraints; 545 constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 0.5); 546 547 CreateVideoSource(&constraints); 548 EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), 549 kMaxWaitMs); 550 const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); 551 ASSERT_TRUE(format != NULL); 552 EXPECT_EQ(30, format->framerate()); 553} 554 555