text: /*
   *  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 <utility>
  #include <vector>
  
  #include "api/test/fakeconstraints.h"
  #include "media/base/fakemediaengine.h"
  #include "media/base/fakevideocapturer.h"
  #include "media/base/fakevideorenderer.h"
  #include "pc/videocapturertracksource.h"
  #include "rtc_base/arraysize.h"
  #include "rtc_base/event.h"
  #include "rtc_base/gunit.h"
  #include "rtc_base/task_queue.h"
  
  using cricket::FOURCC_I420;
  using cricket::VideoFormat;
  using webrtc::FakeConstraints;
  using webrtc::MediaConstraintsInterface;
  using webrtc::MediaSourceInterface;
  using webrtc::ObserverInterface;
  using webrtc::VideoCapturerTrackSource;
  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) {
      static const auto fps = VideoFormat::FpsToInterval(30);
      static const VideoFormat formats[] = {
          {1280, 720, fps, FOURCC_I420}, {640, 480, fps, FOURCC_I420},
          {640, 400, fps, FOURCC_I420},  {320, 240, fps, FOURCC_I420},
          {352, 288, fps, FOURCC_I420},
      };
      ResetSupportedFormats({&formats[0], &formats[arraysize(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<VideoFormat> formats;
      ResetSupportedFormats(formats);
    }
  
    virtual cricket::CaptureState Start(const VideoFormat& capture_format) {
      if (test_without_formats_) {
        std::vector<VideoFormat> formats;
        formats.push_back(capture_format);
        ResetSupportedFormats(formats);
      }
      return FakeVideoCapturer::Start(capture_format);
    }
  
    virtual bool GetBestCaptureFormat(const VideoFormat& desired,
                                      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_ = new TestVideoCapturer(is_screencast);
      capturer_cleanup_.reset(capturer_);
    }
  
    void InitScreencast() { InitCapturer(true); }
  
    void CreateVideoCapturerSource() { CreateVideoCapturerSource(NULL); }
  
    void CreateVideoCapturerSource(
        const webrtc::MediaConstraintsInterface* constraints) {
      source_ = VideoCapturerTrackSource::Create(rtc::Thread::Current(),
                                                 std::move(capturer_cleanup_),
                                                 constraints, false);
  
      ASSERT_TRUE(source_.get() != NULL);
  
      state_observer_.reset(new StateObserver(source_));
      source_->RegisterObserver(state_observer_.get());
      source_->AddOrUpdateSink(&renderer_, rtc::VideoSinkWants());
    }
  
    void CaptureSingleFrame() {
      rtc::Event event(false, false);
      task_queue_.PostTask([this, &event]() {
        ASSERT_TRUE(capturer_->CaptureFrame());
        event.Set();
      });
      event.Wait(rtc::Event::kForever);
    }
  
    rtc::TaskQueue task_queue_{"VideoCapturerTrackSourceTest"};
    std::unique_ptr<cricket::VideoCapturer> 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);
  
    CaptureSingleFrame();
    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 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 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 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 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 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 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(false, source_->needs_denoising());
  }
  
  TEST_F(VideoCapturerTrackSourceTest, NoiseReductionConstraintNotSet) {
    FakeConstraints constraints;
    CreateVideoCapturerSource(&constraints);
    EXPECT_EQ(absl::nullopt, source_->needs_denoising());
  }
  
  TEST_F(VideoCapturerTrackSourceTest,
         MandatoryDenoisingConstraintOverridesOptional) {
    FakeConstraints constraints;
    constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, false);
    constraints.AddOptional(MediaConstraintsInterface::kNoiseReduction, true);
  
    CreateVideoCapturerSource(&constraints);
  
    EXPECT_EQ(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(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(absl::nullopt, 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(absl::nullopt, source_->needs_denoising());
  }
  
  TEST_F(VideoCapturerTrackSourceTest, InvalidDenoisingValueMandatory) {
    FakeConstraints constraints;
    // absl::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(absl::nullopt, 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 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(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 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 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);
  
    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);
  
    CreateVideoCapturerSource(&constraints);
    EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
                   kMaxWaitMs);
    const VideoFormat* format = capturer_->GetCaptureFormat();
    ASSERT_TRUE(format != NULL);
    EXPECT_EQ(1, format->framerate());
  }
  