webrtc/talk/app/webrtc/videosource_unittest.cc
perkj@webrtc.org 8f605e8911 Add VideoSource::Stop and Restart methods.
The purpose is to make sure that start and stop is called on the correct thread on Android. It also cleans up the Java VideoSource implementation.

BUG=4303
R=glaznev@webrtc.org, magjed@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/39989004

Cr-Commit-Position: refs/heads/master@{#8389}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8389 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-02-17 13:54:42 +00:00

564 lines
20 KiB
C++

/*
* libjingle
* Copyright 2012 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <vector>
#include "talk/app/webrtc/remotevideocapturer.h"
#include "talk/app/webrtc/test/fakeconstraints.h"
#include "talk/app/webrtc/videosource.h"
#include "talk/media/base/fakemediaengine.h"
#include "talk/media/base/fakevideorenderer.h"
#include "talk/media/devices/fakedevicemanager.h"
#include "talk/media/webrtc/webrtcvideoframe.h"
#include "talk/session/media/channelmanager.h"
#include "webrtc/base/gunit.h"
using webrtc::FakeConstraints;
using webrtc::VideoSource;
using webrtc::MediaConstraintsInterface;
using webrtc::MediaSourceInterface;
using webrtc::ObserverInterface;
using webrtc::VideoSourceInterface;
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:
TestVideoCapturer() : 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(VideoSourceInterface* 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<VideoSourceInterface> source_;
};
class VideoSourceTest : public testing::Test {
protected:
VideoSourceTest()
: capturer_cleanup_(new TestVideoCapturer()),
capturer_(capturer_cleanup_.get()),
channel_manager_(new cricket::ChannelManager(
new cricket::FakeMediaEngine(),
new cricket::FakeDeviceManager(), rtc::Thread::Current())) {
}
void SetUp() {
ASSERT_TRUE(channel_manager_->Init());
}
void CreateVideoSource() {
CreateVideoSource(NULL);
}
void CreateVideoSource(
const webrtc::MediaConstraintsInterface* constraints) {
// VideoSource take ownership of |capturer_|
source_ = VideoSource::Create(channel_manager_.get(),
capturer_cleanup_.release(),
constraints);
ASSERT_TRUE(source_.get() != NULL);
EXPECT_EQ(capturer_, source_->GetVideoCapturer());
state_observer_.reset(new StateObserver(source_));
source_->RegisterObserver(state_observer_.get());
source_->AddSink(&renderer_);
}
rtc::scoped_ptr<TestVideoCapturer> capturer_cleanup_;
TestVideoCapturer* capturer_;
cricket::FakeVideoRenderer renderer_;
rtc::scoped_ptr<cricket::ChannelManager> channel_manager_;
rtc::scoped_ptr<StateObserver> state_observer_;
rtc::scoped_refptr<VideoSource> 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(VideoSourceTest, CapturerStartStop) {
// Initialize without constraints.
CreateVideoSource();
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 can be stopped and restarted.
TEST_F(VideoSourceTest, StopRestart) {
// Initialize without constraints.
CreateVideoSource();
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
ASSERT_TRUE(capturer_->CaptureFrame());
EXPECT_EQ(1, renderer_.num_rendered_frames());
source_->Stop();
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
source_->Restart();
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
ASSERT_TRUE(capturer_->CaptureFrame());
EXPECT_EQ(2, renderer_.num_rendered_frames());
source_->Stop();
}
// Test start stop with a remote VideoSource - the video source that has a
// RemoteVideoCapturer and takes video frames from FrameInput.
TEST_F(VideoSourceTest, StartStopRemote) {
source_ = VideoSource::Create(channel_manager_.get(),
new webrtc::RemoteVideoCapturer(),
NULL);
ASSERT_TRUE(source_.get() != NULL);
EXPECT_TRUE(NULL != source_->GetVideoCapturer());
state_observer_.reset(new StateObserver(source_));
source_->RegisterObserver(state_observer_.get());
source_->AddSink(&renderer_);
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
cricket::VideoRenderer* frameinput = source_->FrameInput();
cricket::WebRtcVideoFrame test_frame;
frameinput->SetSize(1280, 720, 0);
frameinput->RenderFrame(&test_frame);
EXPECT_EQ(1, renderer_.num_rendered_frames());
source_->GetVideoCapturer()->Stop();
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
}
// Test that a VideoSource transition to kEnded if the capture device
// fails.
TEST_F(VideoSourceTest, CameraFailed) {
CreateVideoSource();
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(VideoSourceTest, MandatoryConstraintCif5Fps) {
FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 352);
constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 288);
constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 5);
CreateVideoSource(&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(30, format->framerate());
}
// Test that the capture output is 720P if the camera support it and the
// optional constraint is set to 720P.
TEST_F(VideoSourceTest, 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);
CreateVideoSource(&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(VideoSourceTest, 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);
CreateVideoSource(&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(VideoSourceTest, MandatoryAspectRatioTooHigh) {
FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio, 2);
CreateVideoSource(&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(VideoSourceTest, OptionalAspectRatioTooHigh) {
FakeConstraints constraints;
constraints.AddOptional(MediaConstraintsInterface::kMinAspectRatio, 2);
CreateVideoSource(&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(VideoSourceTest, NoCameraCapability) {
capturer_->TestWithoutCameraFormats();
CreateVideoSource();
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(VideoSourceTest, NoCameraCapability16To9Ratio) {
capturer_->TestWithoutCameraFormats();
FakeConstraints constraints;
double requested_aspect_ratio = 640.0 / 360;
constraints.AddMandatory(MediaConstraintsInterface::kMinWidth, 640);
constraints.AddMandatory(MediaConstraintsInterface::kMinAspectRatio,
requested_aspect_ratio);
CreateVideoSource(&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(VideoSourceTest, InvalidMandatoryConstraint) {
FakeConstraints constraints;
constraints.AddMandatory("weird key", 640);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
}
// Test that the source ignores an unknown optional constraint.
TEST_F(VideoSourceTest, InvalidOptionalConstraint) {
FakeConstraints constraints;
constraints.AddOptional("weird key", 640);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
}
TEST_F(VideoSourceTest, SetValidOptionValues) {
FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kNoiseReduction, "false");
CreateVideoSource(&constraints);
bool value = true;
EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value));
EXPECT_FALSE(value);
}
TEST_F(VideoSourceTest, OptionNotSet) {
FakeConstraints constraints;
CreateVideoSource(&constraints);
bool value;
EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value));
}
TEST_F(VideoSourceTest, MandatoryOptionOverridesOptional) {
FakeConstraints constraints;
constraints.AddMandatory(
MediaConstraintsInterface::kNoiseReduction, true);
constraints.AddOptional(
MediaConstraintsInterface::kNoiseReduction, false);
CreateVideoSource(&constraints);
bool value = false;
EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value));
EXPECT_TRUE(value);
}
TEST_F(VideoSourceTest, InvalidOptionKeyOptional) {
FakeConstraints constraints;
constraints.AddOptional(
MediaConstraintsInterface::kNoiseReduction, false);
constraints.AddOptional("invalidKey", false);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
bool value = true;
EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value));
EXPECT_FALSE(value);
}
TEST_F(VideoSourceTest, InvalidOptionKeyMandatory) {
FakeConstraints constraints;
constraints.AddMandatory(
MediaConstraintsInterface::kNoiseReduction, false);
constraints.AddMandatory("invalidKey", false);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
bool value;
EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value));
}
TEST_F(VideoSourceTest, InvalidOptionValueOptional) {
FakeConstraints constraints;
constraints.AddOptional(
MediaConstraintsInterface::kNoiseReduction, "not a boolean");
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
bool value = false;
EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value));
}
TEST_F(VideoSourceTest, InvalidOptionValueMandatory) {
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");
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
bool value;
EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value));
}
TEST_F(VideoSourceTest, 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);
CreateVideoSource(&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(30, format->framerate());
bool value = true;
EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value));
EXPECT_FALSE(value);
}
// Tests that the source starts video with the default resolution for
// screencast if no constraint is set.
TEST_F(VideoSourceTest, ScreencastResolutionNoConstraint) {
capturer_->TestWithoutCameraFormats();
capturer_->SetScreencast(true);
CreateVideoSource();
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(VideoSourceTest, ScreencastResolutionWithConstraint) {
FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kMaxWidth, 480);
constraints.AddMandatory(MediaConstraintsInterface::kMaxHeight, 270);
capturer_->TestWithoutCameraFormats();
capturer_->SetScreencast(true);
CreateVideoSource(&constraints);
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(VideoSourceTest, MandatorySubOneFpsConstraints) {
FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kMaxFrameRate, 0.5);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
kMaxWaitMs);
ASSERT_TRUE(capturer_->GetCaptureFormat() == NULL);
}
TEST_F(VideoSourceTest, OptionalSubOneFpsConstraints) {
FakeConstraints constraints;
constraints.AddOptional(MediaConstraintsInterface::kMaxFrameRate, 0.5);
CreateVideoSource(&constraints);
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
kMaxWaitMs);
const cricket::VideoFormat* format = capturer_->GetCaptureFormat();
ASSERT_TRUE(format != NULL);
EXPECT_EQ(30, format->framerate());
}