mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 22:30:40 +01:00

Bug: webrtc:8982 Change-Id: I3977435b035fdebef449732301d6e77fc899e7ba Reviewed-on: https://webrtc-review.googlesource.com/86941 Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24319}
470 lines
17 KiB
C++
470 lines
17 KiB
C++
/*
|
|
* 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());
|
|
}
|