| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/capture/video/fake_video_capture_device.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/thread.h" |
| #include "build/build_config.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/video_capture_types.h" |
| #include "media/capture/video/fake_video_capture_device_factory.h" |
| #include "media/capture/video/video_capture_device.h" |
| #include "mojo/public/cpp/bindings/string.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::Bool; |
| using ::testing::Combine; |
| using ::testing::SaveArg; |
| using ::testing::Values; |
| |
| namespace media { |
| |
| namespace { |
| |
| // This class is a Client::Buffer that allocates and frees the requested |size|. |
| class MockBuffer : public VideoCaptureDevice::Client::Buffer { |
| public: |
| MockBuffer(int buffer_id, size_t mapped_size) |
| : id_(buffer_id), |
| mapped_size_(mapped_size), |
| data_(new uint8_t[mapped_size]) {} |
| ~MockBuffer() override { delete[] data_; } |
| |
| int id() const override { return id_; } |
| gfx::Size dimensions() const override { return gfx::Size(); } |
| size_t mapped_size() const override { return mapped_size_; } |
| void* data(int plane) override { return data_; } |
| ClientBuffer AsClientBuffer(int plane) override { return nullptr; } |
| #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) |
| base::FileDescriptor AsPlatformFile() override { |
| return base::FileDescriptor(); |
| } |
| #endif |
| |
| private: |
| const int id_; |
| const size_t mapped_size_; |
| uint8_t* const data_; |
| }; |
| |
| class MockClient : public VideoCaptureDevice::Client { |
| public: |
| MOCK_METHOD2(OnError, |
| void(const tracked_objects::Location& from_here, |
| const std::string& reason)); |
| |
| explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb) |
| : frame_cb_(frame_cb) {} |
| |
| // Client virtual methods for capturing using Device Buffers. |
| void OnIncomingCapturedData(const uint8_t* data, |
| int length, |
| const VideoCaptureFormat& format, |
| int rotation, |
| base::TimeTicks reference_time, |
| base::TimeDelta timestamp) override { |
| frame_cb_.Run(format); |
| } |
| // Virtual methods for capturing using Client's Buffers. |
| std::unique_ptr<Buffer> ReserveOutputBuffer( |
| const gfx::Size& dimensions, |
| media::VideoPixelFormat format, |
| media::VideoPixelStorage storage) { |
| EXPECT_TRUE((format == media::PIXEL_FORMAT_ARGB && |
| storage == media::PIXEL_STORAGE_CPU) || |
| (format == media::PIXEL_FORMAT_I420 && |
| storage == media::PIXEL_STORAGE_GPUMEMORYBUFFER)); |
| EXPECT_GT(dimensions.GetArea(), 0); |
| const VideoCaptureFormat frame_format(dimensions, 0.0, format); |
| return base::WrapUnique( |
| new MockBuffer(0, frame_format.ImageAllocationSize())); |
| } |
| void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer, |
| const VideoCaptureFormat& frame_format, |
| base::TimeTicks reference_time, |
| base::TimeDelta timestamp) { |
| frame_cb_.Run(frame_format); |
| } |
| void OnIncomingCapturedVideoFrame( |
| std::unique_ptr<Buffer> buffer, |
| const scoped_refptr<media::VideoFrame>& frame) { |
| VideoCaptureFormat format(frame->natural_size(), 30.0, |
| PIXEL_FORMAT_I420); |
| frame_cb_.Run(format); |
| } |
| std::unique_ptr<Buffer> ResurrectLastOutputBuffer( |
| const gfx::Size& dimensions, |
| media::VideoPixelFormat format, |
| media::VideoPixelStorage storage) { |
| return std::unique_ptr<Buffer>(); |
| } |
| double GetBufferPoolUtilization() const override { return 0.0; } |
| |
| private: |
| base::Callback<void(const VideoCaptureFormat&)> frame_cb_; |
| }; |
| |
| class DeviceEnumerationListener |
| : public base::RefCounted<DeviceEnumerationListener> { |
| public: |
| MOCK_METHOD1(OnEnumeratedDevicesCallbackPtr, |
| void(VideoCaptureDevice::Names* names)); |
| // GMock doesn't support move-only arguments, so we use this forward method. |
| void OnEnumeratedDevicesCallback( |
| std::unique_ptr<VideoCaptureDevice::Names> names) { |
| OnEnumeratedDevicesCallbackPtr(names.release()); |
| } |
| |
| private: |
| friend class base::RefCounted<DeviceEnumerationListener>; |
| virtual ~DeviceEnumerationListener() {} |
| }; |
| |
| class PhotoTakenListener : public base::RefCounted<PhotoTakenListener> { |
| public: |
| MOCK_METHOD0(OnCorrectPhotoTaken, void(void)); |
| // GMock doesn't support move-only arguments, so we use this forward method. |
| void DoOnPhotoTaken(mojo::String mime_type, mojo::Array<uint8_t> data) { |
| // Only PNG images are supported right now. |
| EXPECT_STREQ("image/png", mime_type.storage().c_str()); |
| // Not worth decoding the incoming data. Just check that the header is PNG. |
| // http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature |
| ASSERT_GT(data.size(), 4u); |
| EXPECT_EQ('P', data[1]); |
| EXPECT_EQ('N', data[2]); |
| EXPECT_EQ('G', data[3]); |
| OnCorrectPhotoTaken(); |
| } |
| MOCK_METHOD1(OnTakePhotoFailure, |
| void(const VideoCaptureDevice::TakePhotoCallback&)); |
| |
| private: |
| friend class base::RefCounted<PhotoTakenListener>; |
| virtual ~PhotoTakenListener() {} |
| }; |
| |
| } // namespace |
| |
| class FakeVideoCaptureDeviceBase : public ::testing::Test { |
| protected: |
| FakeVideoCaptureDeviceBase() |
| : loop_(new base::MessageLoop()), |
| client_(new MockClient( |
| base::Bind(&FakeVideoCaptureDeviceBase::OnFrameCaptured, |
| base::Unretained(this)))), |
| device_enumeration_listener_(new DeviceEnumerationListener()), |
| photo_taken_listener_(new PhotoTakenListener()), |
| video_capture_device_factory_(new FakeVideoCaptureDeviceFactory()) {} |
| |
| void SetUp() override { EXPECT_CALL(*client_, OnError(_, _)).Times(0); } |
| |
| void OnFrameCaptured(const VideoCaptureFormat& format) { |
| last_format_ = format; |
| run_loop_->QuitClosure().Run(); |
| } |
| |
| void WaitForCapturedFrame() { |
| run_loop_.reset(new base::RunLoop()); |
| run_loop_->Run(); |
| } |
| |
| std::unique_ptr<VideoCaptureDevice::Names> EnumerateDevices() { |
| VideoCaptureDevice::Names* names; |
| EXPECT_CALL(*device_enumeration_listener_.get(), |
| OnEnumeratedDevicesCallbackPtr(_)).WillOnce(SaveArg<0>(&names)); |
| |
| video_capture_device_factory_->EnumerateDeviceNames( |
| base::Bind(&DeviceEnumerationListener::OnEnumeratedDevicesCallback, |
| device_enumeration_listener_)); |
| base::RunLoop().RunUntilIdle(); |
| return std::unique_ptr<VideoCaptureDevice::Names>(names); |
| } |
| |
| const VideoCaptureFormat& last_format() const { return last_format_; } |
| |
| VideoCaptureDevice::Names names_; |
| const std::unique_ptr<base::MessageLoop> loop_; |
| std::unique_ptr<base::RunLoop> run_loop_; |
| std::unique_ptr<MockClient> client_; |
| const scoped_refptr<DeviceEnumerationListener> device_enumeration_listener_; |
| const scoped_refptr<PhotoTakenListener> photo_taken_listener_; |
| VideoCaptureFormat last_format_; |
| const std::unique_ptr<VideoCaptureDeviceFactory> |
| video_capture_device_factory_; |
| }; |
| |
| class FakeVideoCaptureDeviceTest |
| : public FakeVideoCaptureDeviceBase, |
| public ::testing::WithParamInterface< |
| ::testing::tuple<FakeVideoCaptureDevice::BufferOwnership, float>> {}; |
| |
| struct CommandLineTestData { |
| // Command line argument |
| std::string argument; |
| // Expected values |
| float fps; |
| }; |
| |
| class FakeVideoCaptureDeviceCommandLineTest |
| : public FakeVideoCaptureDeviceBase, |
| public ::testing::WithParamInterface<CommandLineTestData> {}; |
| |
| TEST_P(FakeVideoCaptureDeviceTest, CaptureUsing) { |
| const std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); |
| ASSERT_FALSE(names->empty()); |
| |
| std::unique_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( |
| testing::get<0>(GetParam()), testing::get<1>(GetParam()))); |
| ASSERT_TRUE(device); |
| |
| VideoCaptureParams capture_params; |
| capture_params.requested_format.frame_size.SetSize(640, 480); |
| capture_params.requested_format.frame_rate = testing::get<1>(GetParam()); |
| device->AllocateAndStart(capture_params, std::move(client_)); |
| |
| WaitForCapturedFrame(); |
| EXPECT_EQ(last_format().frame_size.width(), 640); |
| EXPECT_EQ(last_format().frame_size.height(), 480); |
| EXPECT_EQ(last_format().frame_rate, testing::get<1>(GetParam())); |
| device->StopAndDeAllocate(); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| , |
| FakeVideoCaptureDeviceTest, |
| Combine(Values(FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, |
| FakeVideoCaptureDevice::BufferOwnership::CLIENT_BUFFERS), |
| Values(20, 29.97, 30, 50, 60))); |
| |
| TEST_F(FakeVideoCaptureDeviceTest, GetDeviceSupportedFormats) { |
| std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); |
| |
| VideoCaptureFormats supported_formats; |
| |
| for (const auto& names_iterator : *names) { |
| video_capture_device_factory_->GetDeviceSupportedFormats( |
| names_iterator, &supported_formats); |
| ASSERT_EQ(supported_formats.size(), 4u); |
| EXPECT_EQ(supported_formats[0].frame_size.width(), 320); |
| EXPECT_EQ(supported_formats[0].frame_size.height(), 240); |
| EXPECT_EQ(supported_formats[0].pixel_format, PIXEL_FORMAT_I420); |
| EXPECT_GE(supported_formats[0].frame_rate, 20.0); |
| EXPECT_EQ(supported_formats[1].frame_size.width(), 640); |
| EXPECT_EQ(supported_formats[1].frame_size.height(), 480); |
| EXPECT_EQ(supported_formats[1].pixel_format, PIXEL_FORMAT_I420); |
| EXPECT_GE(supported_formats[1].frame_rate, 20.0); |
| EXPECT_EQ(supported_formats[2].frame_size.width(), 1280); |
| EXPECT_EQ(supported_formats[2].frame_size.height(), 720); |
| EXPECT_EQ(supported_formats[2].pixel_format, PIXEL_FORMAT_I420); |
| EXPECT_GE(supported_formats[2].frame_rate, 20.0); |
| EXPECT_EQ(supported_formats[3].frame_size.width(), 1920); |
| EXPECT_EQ(supported_formats[3].frame_size.height(), 1080); |
| EXPECT_EQ(supported_formats[3].pixel_format, PIXEL_FORMAT_I420); |
| EXPECT_GE(supported_formats[3].frame_rate, 20.0); |
| } |
| } |
| |
| TEST_F(FakeVideoCaptureDeviceTest, TakePhoto) { |
| std::unique_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( |
| FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, 30.0)); |
| ASSERT_TRUE(device); |
| |
| VideoCaptureParams capture_params; |
| capture_params.requested_format.frame_size.SetSize(640, 480); |
| capture_params.requested_format.frame_rate = 30.0; |
| device->AllocateAndStart(capture_params, std::move(client_)); |
| |
| ScopedResultCallback<VideoCaptureDevice::TakePhotoCallback> scoped_callback( |
| base::Bind(&PhotoTakenListener::DoOnPhotoTaken, photo_taken_listener_), |
| base::Bind(&PhotoTakenListener::OnTakePhotoFailure, |
| photo_taken_listener_)); |
| |
| EXPECT_CALL(*photo_taken_listener_.get(), OnCorrectPhotoTaken()).Times(1); |
| device->TakePhoto(std::move(scoped_callback)); |
| |
| run_loop_.reset(new base::RunLoop()); |
| run_loop_->Run(); |
| device->StopAndDeAllocate(); |
| } |
| |
| TEST_P(FakeVideoCaptureDeviceCommandLineTest, FrameRate) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, GetParam().argument); |
| const std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); |
| ASSERT_FALSE(names->empty()); |
| |
| for (const auto& names_iterator : *names) { |
| std::unique_ptr<VideoCaptureDevice> device = |
| video_capture_device_factory_->Create(names_iterator); |
| ASSERT_TRUE(device); |
| |
| VideoCaptureParams capture_params; |
| capture_params.requested_format.frame_size.SetSize(1280, 720); |
| capture_params.requested_format.frame_rate = GetParam().fps; |
| device->AllocateAndStart(capture_params, std::move(client_)); |
| |
| WaitForCapturedFrame(); |
| EXPECT_EQ(last_format().frame_size.width(), 1280); |
| EXPECT_EQ(last_format().frame_size.height(), 720); |
| EXPECT_EQ(last_format().frame_rate, GetParam().fps); |
| device->StopAndDeAllocate(); |
| } |
| } |
| |
| INSTANTIATE_TEST_CASE_P(, |
| FakeVideoCaptureDeviceCommandLineTest, |
| Values(CommandLineTestData{"fps=-1", 5}, |
| CommandLineTestData{"fps=29.97", 29.97f}, |
| CommandLineTestData{"fps=60", 60}, |
| CommandLineTestData{"fps=1000", 60})); |
| }; // namespace media |