|  | /* | 
|  | *  Copyright 2012 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "webrtc/api/test/fakeconstraints.h" | 
|  | #include "webrtc/api/videocapturertracksource.h" | 
|  | #include "webrtc/base/gunit.h" | 
|  | #include "webrtc/media/base/fakemediaengine.h" | 
|  | #include "webrtc/media/base/fakevideocapturer.h" | 
|  | #include "webrtc/media/base/fakevideorenderer.h" | 
|  |  | 
|  | using webrtc::FakeConstraints; | 
|  | using webrtc::VideoCapturerTrackSource; | 
|  | using webrtc::MediaConstraintsInterface; | 
|  | using webrtc::MediaSourceInterface; | 
|  | using webrtc::ObserverInterface; | 
|  | using webrtc::VideoTrackSourceInterface; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Max wait time for a test. | 
|  | const int kMaxWaitMs = 100; | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | // TestVideoCapturer extends cricket::FakeVideoCapturer so it can be used for | 
|  | // testing without known camera formats. | 
|  | // It keeps its own lists of cricket::VideoFormats for the unit tests in this | 
|  | // file. | 
|  | class TestVideoCapturer : public cricket::FakeVideoCapturer { | 
|  | public: | 
|  | explicit TestVideoCapturer(bool is_screencast) | 
|  | : FakeVideoCapturer(is_screencast), test_without_formats_(false) { | 
|  | std::vector<cricket::VideoFormat> formats; | 
|  | formats.push_back( | 
|  | cricket::VideoFormat(1280, 720, cricket::VideoFormat::FpsToInterval(30), | 
|  | cricket::FOURCC_I420)); | 
|  | formats.push_back( | 
|  | cricket::VideoFormat(640, 480, cricket::VideoFormat::FpsToInterval(30), | 
|  | cricket::FOURCC_I420)); | 
|  | formats.push_back( | 
|  | cricket::VideoFormat(640, 400, cricket::VideoFormat::FpsToInterval(30), | 
|  | cricket::FOURCC_I420)); | 
|  | formats.push_back( | 
|  | cricket::VideoFormat(320, 240, cricket::VideoFormat::FpsToInterval(30), | 
|  | cricket::FOURCC_I420)); | 
|  | formats.push_back( | 
|  | cricket::VideoFormat(352, 288, cricket::VideoFormat::FpsToInterval(30), | 
|  | cricket::FOURCC_I420)); | 
|  | ResetSupportedFormats(formats); | 
|  | } | 
|  |  | 
|  | // This function is used for resetting the supported capture formats and | 
|  | // simulating a cricket::VideoCapturer implementation that don't support | 
|  | // capture format enumeration. This is used to simulate the current | 
|  | // Chrome implementation. | 
|  | void TestWithoutCameraFormats() { | 
|  | test_without_formats_ = true; | 
|  | std::vector<cricket::VideoFormat> formats; | 
|  | ResetSupportedFormats(formats); | 
|  | } | 
|  |  | 
|  | virtual cricket::CaptureState Start( | 
|  | const cricket::VideoFormat& capture_format) { | 
|  | if (test_without_formats_) { | 
|  | std::vector<cricket::VideoFormat> formats; | 
|  | formats.push_back(capture_format); | 
|  | ResetSupportedFormats(formats); | 
|  | } | 
|  | return FakeVideoCapturer::Start(capture_format); | 
|  | } | 
|  |  | 
|  | virtual bool GetBestCaptureFormat(const cricket::VideoFormat& desired, | 
|  | cricket::VideoFormat* best_format) { | 
|  | if (test_without_formats_) { | 
|  | *best_format = desired; | 
|  | return true; | 
|  | } | 
|  | return FakeVideoCapturer::GetBestCaptureFormat(desired, best_format); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool test_without_formats_; | 
|  | }; | 
|  |  | 
|  | class StateObserver : public ObserverInterface { | 
|  | public: | 
|  | explicit StateObserver(VideoTrackSourceInterface* source) | 
|  | : state_(source->state()), source_(source) {} | 
|  | virtual void OnChanged() { state_ = source_->state(); } | 
|  | MediaSourceInterface::SourceState state() const { return state_; } | 
|  |  | 
|  | private: | 
|  | MediaSourceInterface::SourceState state_; | 
|  | rtc::scoped_refptr<VideoTrackSourceInterface> source_; | 
|  | }; | 
|  |  | 
|  | class VideoCapturerTrackSourceTest : public testing::Test { | 
|  | protected: | 
|  | VideoCapturerTrackSourceTest() { InitCapturer(false); } | 
|  | void InitCapturer(bool is_screencast) { | 
|  | capturer_cleanup_ = std::unique_ptr<TestVideoCapturer>( | 
|  | new TestVideoCapturer(is_screencast)); | 
|  | capturer_ = capturer_cleanup_.get(); | 
|  | } | 
|  |  | 
|  | void InitScreencast() { InitCapturer(true); } | 
|  |  | 
|  | void CreateVideoCapturerSource() { CreateVideoCapturerSource(NULL); } | 
|  |  | 
|  | void CreateVideoCapturerSource( | 
|  | const webrtc::MediaConstraintsInterface* constraints) { | 
|  | // VideoSource take ownership of |capturer_| | 
|  | source_ = VideoCapturerTrackSource::Create(rtc::Thread::Current(), | 
|  | capturer_cleanup_.release(), | 
|  | constraints, false); | 
|  |  | 
|  | ASSERT_TRUE(source_.get() != NULL); | 
|  |  | 
|  | state_observer_.reset(new StateObserver(source_)); | 
|  | source_->RegisterObserver(state_observer_.get()); | 
|  | source_->AddOrUpdateSink(&renderer_, rtc::VideoSinkWants()); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<TestVideoCapturer> capturer_cleanup_; | 
|  | TestVideoCapturer* capturer_; | 
|  | cricket::FakeVideoRenderer renderer_; | 
|  | std::unique_ptr<StateObserver> state_observer_; | 
|  | rtc::scoped_refptr<VideoTrackSourceInterface> source_; | 
|  | }; | 
|  |  | 
|  | // Test that a VideoSource transition to kLive state when the capture | 
|  | // device have started and kEnded if it is stopped. | 
|  | // It also test that an output can receive video frames. | 
|  | TEST_F(VideoCapturerTrackSourceTest, CapturerStartStop) { | 
|  | // Initialize without constraints. | 
|  | CreateVideoCapturerSource(); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  |  | 
|  | ASSERT_TRUE(capturer_->CaptureFrame()); | 
|  | EXPECT_EQ(1, renderer_.num_rendered_frames()); | 
|  |  | 
|  | capturer_->Stop(); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | } | 
|  |  | 
|  | // Test that a VideoSource transition to kEnded if the capture device | 
|  | // fails. | 
|  | TEST_F(VideoCapturerTrackSourceTest, CameraFailed) { | 
|  | CreateVideoCapturerSource(); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  |  | 
|  | capturer_->SignalStateChange(capturer_, cricket::CS_FAILED); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | } | 
|  |  | 
|  | // Test that the capture output is CIF if we set max constraints to CIF. | 
|  | // and the capture device support CIF. | 
|  | TEST_F(VideoCapturerTrackSourceTest, MandatoryConstraintCif5Fps) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 5); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(352, format->width); | 
|  | EXPECT_EQ(288, format->height); | 
|  | EXPECT_EQ(5, format->framerate()); | 
|  | } | 
|  |  | 
|  | // Test that the capture output is 720P if the camera support it and the | 
|  | // optional constraint is set to 720P. | 
|  | TEST_F(VideoCapturerTrackSourceTest, MandatoryMinVgaOptional720P) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, | 
|  | 1280.0 / 720); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(1280, format->width); | 
|  | EXPECT_EQ(720, format->height); | 
|  | EXPECT_EQ(30, format->framerate()); | 
|  | } | 
|  |  | 
|  | // Test that the capture output have aspect ratio 4:3 if a mandatory constraint | 
|  | // require it even if an optional constraint request a higher resolution | 
|  | // that don't have this aspect ratio. | 
|  | TEST_F(VideoCapturerTrackSourceTest, MandatoryAspectRatio4To3) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinHeight, 480); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxAspectRatio, | 
|  | 640.0 / 480); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMinWidth, 1280); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(640, format->width); | 
|  | EXPECT_EQ(480, format->height); | 
|  | EXPECT_EQ(30, format->framerate()); | 
|  | } | 
|  |  | 
|  | // Test that the source state transition to kEnded if the mandatory aspect ratio | 
|  | // is set higher than supported. | 
|  | TEST_F(VideoCapturerTrackSourceTest, MandatoryAspectRatioTooHigh) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, 2); | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | } | 
|  |  | 
|  | // Test that the source ignores an optional aspect ratio that is higher than | 
|  | // supported. | 
|  | TEST_F(VideoCapturerTrackSourceTest, OptionalAspectRatioTooHigh) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, 2); | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | double aspect_ratio = static_cast<double>(format->width) / format->height; | 
|  | EXPECT_LT(aspect_ratio, 2); | 
|  | } | 
|  |  | 
|  | // Test that the source starts video with the default resolution if the | 
|  | // camera doesn't support capability enumeration and there are no constraints. | 
|  | TEST_F(VideoCapturerTrackSourceTest, NoCameraCapability) { | 
|  | capturer_->TestWithoutCameraFormats(); | 
|  |  | 
|  | CreateVideoCapturerSource(); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(640, format->width); | 
|  | EXPECT_EQ(480, format->height); | 
|  | EXPECT_EQ(30, format->framerate()); | 
|  | } | 
|  |  | 
|  | // Test that the source can start the video and get the requested aspect ratio | 
|  | // if the camera doesn't support capability enumeration and the aspect ratio is | 
|  | // set. | 
|  | TEST_F(VideoCapturerTrackSourceTest, NoCameraCapability16To9Ratio) { | 
|  | capturer_->TestWithoutCameraFormats(); | 
|  |  | 
|  | FakeConstraints constraints; | 
|  | double requested_aspect_ratio = 640.0 / 360; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, | 
|  | requested_aspect_ratio); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | double aspect_ratio = static_cast<double>(format->width) / format->height; | 
|  | EXPECT_LE(requested_aspect_ratio, aspect_ratio); | 
|  | } | 
|  |  | 
|  | // Test that the source state transitions to kEnded if an unknown mandatory | 
|  | // constraint is found. | 
|  | TEST_F(VideoCapturerTrackSourceTest, InvalidMandatoryConstraint) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory("weird key", 640); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | } | 
|  |  | 
|  | // Test that the source ignores an unknown optional constraint. | 
|  | TEST_F(VideoCapturerTrackSourceTest, InvalidOptionalConstraint) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddOptional("weird key", 640); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, SetValidDenoisingConstraint) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, "false"); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ(rtc::Optional<bool>(false), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, NoiseReductionConstraintNotSet) { | 
|  | FakeConstraints constraints; | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ(rtc::Optional<bool>(), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, | 
|  | MandatoryDenoisingConstraintOverridesOptional) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, false); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, true); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ(rtc::Optional<bool>(false), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, NoiseReductionAndInvalidKeyOptional) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, true); | 
|  | constraints.AddOptional("invalidKey", false); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | EXPECT_EQ(rtc::Optional<bool>(true), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, NoiseReductionAndInvalidKeyMandatory) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, false); | 
|  | constraints.AddMandatory("invalidKey", false); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | EXPECT_EQ(rtc::Optional<bool>(), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, InvalidDenoisingValueOptional) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, | 
|  | "not a boolean"); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  |  | 
|  | EXPECT_EQ(rtc::Optional<bool>(), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, InvalidDenoisingValueMandatory) { | 
|  | FakeConstraints constraints; | 
|  | // Optional constraints should be ignored if the mandatory constraints fail. | 
|  | constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, "false"); | 
|  | // Values are case-sensitive and must be all lower-case. | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, "True"); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  |  | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | EXPECT_EQ(rtc::Optional<bool>(), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, MixedOptionsAndConstraints) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 5); | 
|  |  | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, false); | 
|  | constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, true); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(352, format->width); | 
|  | EXPECT_EQ(288, format->height); | 
|  | EXPECT_EQ(5, format->framerate()); | 
|  |  | 
|  | EXPECT_EQ(rtc::Optional<bool>(false), source_->needs_denoising()); | 
|  | } | 
|  |  | 
|  | // Tests that the source starts video with the default resolution for | 
|  | // screencast if no constraint is set. | 
|  | TEST_F(VideoCapturerTrackSourceTest, ScreencastResolutionNoConstraint) { | 
|  | InitScreencast(); | 
|  | capturer_->TestWithoutCameraFormats(); | 
|  |  | 
|  | CreateVideoCapturerSource(); | 
|  | ASSERT_TRUE(source_->is_screencast()); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(640, format->width); | 
|  | EXPECT_EQ(480, format->height); | 
|  | EXPECT_EQ(30, format->framerate()); | 
|  | } | 
|  |  | 
|  | // Tests that the source starts video with the max width and height set by | 
|  | // constraints for screencast. | 
|  | TEST_F(VideoCapturerTrackSourceTest, ScreencastResolutionWithConstraint) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 480); | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 270); | 
|  |  | 
|  | InitScreencast(); | 
|  | capturer_->TestWithoutCameraFormats(); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | ASSERT_TRUE(source_->is_screencast()); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(480, format->width); | 
|  | EXPECT_EQ(270, format->height); | 
|  | EXPECT_EQ(30, format->framerate()); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, MandatorySubOneFpsConstraints) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 0.5); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | ASSERT_TRUE(capturer_->GetCaptureFormat() == NULL); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoCapturerTrackSourceTest, OptionalSubOneFpsConstraints) { | 
|  | FakeConstraints constraints; | 
|  | constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 0.5); | 
|  |  | 
|  | CreateVideoCapturerSource(&constraints); | 
|  | EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(), | 
|  | kMaxWaitMs); | 
|  | const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); | 
|  | ASSERT_TRUE(format != NULL); | 
|  | EXPECT_EQ(1, format->framerate()); | 
|  | } |