Home | History | Annotate | Download | only in webrtc
      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