| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/capture/video/fake_video_capture_device.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <memory> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/command_line.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/run_loop.h" | |
| 17 #include "base/test/test_timeouts.h" | |
| 18 #include "base/threading/thread.h" | |
| 19 #include "build/build_config.h" | |
| 20 #include "media/base/media_switches.h" | |
| 21 #include "media/base/video_capture_types.h" | |
| 22 #include "media/capture/video/fake_video_capture_device_factory.h" | |
| 23 #include "media/capture/video/video_capture_device.h" | |
| 24 #include "mojo/public/cpp/bindings/string.h" | |
| 25 #include "testing/gmock/include/gmock/gmock.h" | |
| 26 #include "testing/gtest/include/gtest/gtest.h" | |
| 27 | |
| 28 using ::testing::_; | |
| 29 using ::testing::Bool; | |
| 30 using ::testing::Combine; | |
| 31 using ::testing::SaveArg; | |
| 32 using ::testing::Values; | |
| 33 | |
| 34 namespace media { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 // This class is a Client::Buffer that allocates and frees the requested |size|. | |
| 39 class MockBuffer : public VideoCaptureDevice::Client::Buffer { | |
| 40 public: | |
| 41 MockBuffer(int buffer_id, size_t mapped_size) | |
| 42 : id_(buffer_id), | |
| 43 mapped_size_(mapped_size), | |
| 44 data_(new uint8_t[mapped_size]) {} | |
| 45 ~MockBuffer() override { delete[] data_; } | |
| 46 | |
| 47 int id() const override { return id_; } | |
| 48 gfx::Size dimensions() const override { return gfx::Size(); } | |
| 49 size_t mapped_size() const override { return mapped_size_; } | |
| 50 void* data(int plane) override { return data_; } | |
| 51 ClientBuffer AsClientBuffer(int plane) override { return nullptr; } | |
| 52 #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) | |
| 53 base::FileDescriptor AsPlatformFile() override { | |
| 54 return base::FileDescriptor(); | |
| 55 } | |
| 56 #endif | |
| 57 | |
| 58 private: | |
| 59 const int id_; | |
| 60 const size_t mapped_size_; | |
| 61 uint8_t* const data_; | |
| 62 }; | |
| 63 | |
| 64 class MockClient : public VideoCaptureDevice::Client { | |
| 65 public: | |
| 66 MOCK_METHOD2(OnError, | |
| 67 void(const tracked_objects::Location& from_here, | |
| 68 const std::string& reason)); | |
| 69 | |
| 70 explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb) | |
| 71 : frame_cb_(frame_cb) {} | |
| 72 | |
| 73 // Client virtual methods for capturing using Device Buffers. | |
| 74 void OnIncomingCapturedData(const uint8_t* data, | |
| 75 int length, | |
| 76 const VideoCaptureFormat& format, | |
| 77 int rotation, | |
| 78 base::TimeTicks reference_time, | |
| 79 base::TimeDelta timestamp) override { | |
| 80 frame_cb_.Run(format); | |
| 81 } | |
| 82 // Virtual methods for capturing using Client's Buffers. | |
| 83 std::unique_ptr<Buffer> ReserveOutputBuffer( | |
| 84 const gfx::Size& dimensions, | |
| 85 media::VideoPixelFormat format, | |
| 86 media::VideoPixelStorage storage) { | |
| 87 EXPECT_TRUE((format == media::PIXEL_FORMAT_ARGB && | |
| 88 storage == media::PIXEL_STORAGE_CPU) || | |
| 89 (format == media::PIXEL_FORMAT_I420 && | |
| 90 storage == media::PIXEL_STORAGE_GPUMEMORYBUFFER)); | |
| 91 EXPECT_GT(dimensions.GetArea(), 0); | |
| 92 const VideoCaptureFormat frame_format(dimensions, 0.0, format); | |
| 93 return base::WrapUnique( | |
| 94 new MockBuffer(0, frame_format.ImageAllocationSize())); | |
| 95 } | |
| 96 void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer, | |
| 97 const VideoCaptureFormat& frame_format, | |
| 98 base::TimeTicks reference_time, | |
| 99 base::TimeDelta timestamp) { | |
| 100 frame_cb_.Run(frame_format); | |
| 101 } | |
| 102 void OnIncomingCapturedVideoFrame( | |
| 103 std::unique_ptr<Buffer> buffer, | |
| 104 const scoped_refptr<media::VideoFrame>& frame) { | |
| 105 VideoCaptureFormat format(frame->natural_size(), 30.0, | |
| 106 PIXEL_FORMAT_I420); | |
| 107 frame_cb_.Run(format); | |
| 108 } | |
| 109 std::unique_ptr<Buffer> ResurrectLastOutputBuffer( | |
| 110 const gfx::Size& dimensions, | |
| 111 media::VideoPixelFormat format, | |
| 112 media::VideoPixelStorage storage) { | |
| 113 return std::unique_ptr<Buffer>(); | |
| 114 } | |
| 115 double GetBufferPoolUtilization() const override { return 0.0; } | |
| 116 | |
| 117 private: | |
| 118 base::Callback<void(const VideoCaptureFormat&)> frame_cb_; | |
| 119 }; | |
| 120 | |
| 121 class DeviceEnumerationListener | |
| 122 : public base::RefCounted<DeviceEnumerationListener> { | |
| 123 public: | |
| 124 MOCK_METHOD1(OnEnumeratedDevicesCallbackPtr, | |
| 125 void(VideoCaptureDevice::Names* names)); | |
| 126 // GMock doesn't support move-only arguments, so we use this forward method. | |
| 127 void OnEnumeratedDevicesCallback( | |
| 128 std::unique_ptr<VideoCaptureDevice::Names> names) { | |
| 129 OnEnumeratedDevicesCallbackPtr(names.release()); | |
| 130 } | |
| 131 | |
| 132 private: | |
| 133 friend class base::RefCounted<DeviceEnumerationListener>; | |
| 134 virtual ~DeviceEnumerationListener() {} | |
| 135 }; | |
| 136 | |
| 137 class ImageCaptureClient : public base::RefCounted<ImageCaptureClient> { | |
| 138 public: | |
| 139 // GMock doesn't support move-only arguments, so we use this forward method. | |
| 140 void DoOnGetPhotoCapabilities(mojom::PhotoCapabilitiesPtr capabilities) { | |
| 141 capabilities_ = std::move(capabilities); | |
| 142 OnCorrectGetPhotoCapabilities(); | |
| 143 } | |
| 144 MOCK_METHOD0(OnCorrectGetPhotoCapabilities, void(void)); | |
| 145 MOCK_METHOD1(OnGetPhotoCapabilitiesFailure, | |
| 146 void(const base::Callback<void(mojom::PhotoCapabilitiesPtr)>&)); | |
| 147 | |
| 148 const mojom::PhotoCapabilities* capabilities() { return capabilities_.get(); } | |
| 149 | |
| 150 MOCK_METHOD1(OnCorrectSetPhotoOptions, void(bool)); | |
| 151 MOCK_METHOD1(OnSetPhotoOptionsFailure, | |
| 152 void(const base::Callback<void(bool)>&)); | |
| 153 | |
| 154 // GMock doesn't support move-only arguments, so we use this forward method. | |
| 155 void DoOnPhotoTaken(mojo::String mime_type, mojo::Array<uint8_t> data) { | |
| 156 // Only PNG images are supported right now. | |
| 157 EXPECT_STREQ("image/png", mime_type.storage().c_str()); | |
| 158 // Not worth decoding the incoming data. Just check that the header is PNG. | |
| 159 // http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-sign
ature | |
| 160 ASSERT_GT(data.size(), 4u); | |
| 161 EXPECT_EQ('P', data[1]); | |
| 162 EXPECT_EQ('N', data[2]); | |
| 163 EXPECT_EQ('G', data[3]); | |
| 164 OnCorrectPhotoTaken(); | |
| 165 } | |
| 166 MOCK_METHOD0(OnCorrectPhotoTaken, void(void)); | |
| 167 MOCK_METHOD1( | |
| 168 OnTakePhotoFailure, | |
| 169 void(const base::Callback<void(mojo::String, mojo::Array<uint8_t>)>&)); | |
| 170 | |
| 171 private: | |
| 172 friend class base::RefCounted<ImageCaptureClient>; | |
| 173 virtual ~ImageCaptureClient() {} | |
| 174 | |
| 175 mojom::PhotoCapabilitiesPtr capabilities_; | |
| 176 }; | |
| 177 | |
| 178 } // namespace | |
| 179 | |
| 180 class FakeVideoCaptureDeviceBase : public ::testing::Test { | |
| 181 protected: | |
| 182 FakeVideoCaptureDeviceBase() | |
| 183 : loop_(new base::MessageLoop()), | |
| 184 client_(new MockClient( | |
| 185 base::Bind(&FakeVideoCaptureDeviceBase::OnFrameCaptured, | |
| 186 base::Unretained(this)))), | |
| 187 device_enumeration_listener_(new DeviceEnumerationListener()), | |
| 188 image_capture_client_(new ImageCaptureClient()), | |
| 189 video_capture_device_factory_(new FakeVideoCaptureDeviceFactory()) {} | |
| 190 | |
| 191 void SetUp() override { EXPECT_CALL(*client_, OnError(_, _)).Times(0); } | |
| 192 | |
| 193 void OnFrameCaptured(const VideoCaptureFormat& format) { | |
| 194 last_format_ = format; | |
| 195 run_loop_->QuitClosure().Run(); | |
| 196 } | |
| 197 | |
| 198 void WaitForCapturedFrame() { | |
| 199 run_loop_.reset(new base::RunLoop()); | |
| 200 run_loop_->Run(); | |
| 201 } | |
| 202 | |
| 203 std::unique_ptr<VideoCaptureDevice::Names> EnumerateDevices() { | |
| 204 VideoCaptureDevice::Names* names; | |
| 205 EXPECT_CALL(*device_enumeration_listener_.get(), | |
| 206 OnEnumeratedDevicesCallbackPtr(_)).WillOnce(SaveArg<0>(&names)); | |
| 207 | |
| 208 video_capture_device_factory_->EnumerateDeviceNames( | |
| 209 base::Bind(&DeviceEnumerationListener::OnEnumeratedDevicesCallback, | |
| 210 device_enumeration_listener_)); | |
| 211 base::RunLoop().RunUntilIdle(); | |
| 212 return std::unique_ptr<VideoCaptureDevice::Names>(names); | |
| 213 } | |
| 214 | |
| 215 const VideoCaptureFormat& last_format() const { return last_format_; } | |
| 216 | |
| 217 VideoCaptureDevice::Names names_; | |
| 218 const std::unique_ptr<base::MessageLoop> loop_; | |
| 219 std::unique_ptr<base::RunLoop> run_loop_; | |
| 220 std::unique_ptr<MockClient> client_; | |
| 221 const scoped_refptr<DeviceEnumerationListener> device_enumeration_listener_; | |
| 222 const scoped_refptr<ImageCaptureClient> image_capture_client_; | |
| 223 VideoCaptureFormat last_format_; | |
| 224 const std::unique_ptr<VideoCaptureDeviceFactory> | |
| 225 video_capture_device_factory_; | |
| 226 }; | |
| 227 | |
| 228 class FakeVideoCaptureDeviceTest | |
| 229 : public FakeVideoCaptureDeviceBase, | |
| 230 public ::testing::WithParamInterface< | |
| 231 ::testing::tuple<FakeVideoCaptureDevice::BufferOwnership, float>> {}; | |
| 232 | |
| 233 struct CommandLineTestData { | |
| 234 // Command line argument | |
| 235 std::string argument; | |
| 236 // Expected values | |
| 237 float fps; | |
| 238 }; | |
| 239 | |
| 240 class FakeVideoCaptureDeviceCommandLineTest | |
| 241 : public FakeVideoCaptureDeviceBase, | |
| 242 public ::testing::WithParamInterface<CommandLineTestData> {}; | |
| 243 | |
| 244 TEST_P(FakeVideoCaptureDeviceTest, CaptureUsing) { | |
| 245 const std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); | |
| 246 ASSERT_FALSE(names->empty()); | |
| 247 | |
| 248 std::unique_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( | |
| 249 testing::get<0>(GetParam()), testing::get<1>(GetParam()))); | |
| 250 ASSERT_TRUE(device); | |
| 251 | |
| 252 VideoCaptureParams capture_params; | |
| 253 capture_params.requested_format.frame_size.SetSize(640, 480); | |
| 254 capture_params.requested_format.frame_rate = testing::get<1>(GetParam()); | |
| 255 device->AllocateAndStart(capture_params, std::move(client_)); | |
| 256 | |
| 257 WaitForCapturedFrame(); | |
| 258 EXPECT_EQ(last_format().frame_size.width(), 640); | |
| 259 EXPECT_EQ(last_format().frame_size.height(), 480); | |
| 260 EXPECT_EQ(last_format().frame_rate, testing::get<1>(GetParam())); | |
| 261 device->StopAndDeAllocate(); | |
| 262 } | |
| 263 | |
| 264 INSTANTIATE_TEST_CASE_P( | |
| 265 , | |
| 266 FakeVideoCaptureDeviceTest, | |
| 267 Combine(Values(FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, | |
| 268 FakeVideoCaptureDevice::BufferOwnership::CLIENT_BUFFERS), | |
| 269 Values(20, 29.97, 30, 50, 60))); | |
| 270 | |
| 271 TEST_F(FakeVideoCaptureDeviceTest, GetDeviceSupportedFormats) { | |
| 272 std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); | |
| 273 | |
| 274 VideoCaptureFormats supported_formats; | |
| 275 | |
| 276 for (const auto& names_iterator : *names) { | |
| 277 video_capture_device_factory_->GetDeviceSupportedFormats( | |
| 278 names_iterator, &supported_formats); | |
| 279 ASSERT_EQ(supported_formats.size(), 4u); | |
| 280 EXPECT_EQ(supported_formats[0].frame_size.width(), 320); | |
| 281 EXPECT_EQ(supported_formats[0].frame_size.height(), 240); | |
| 282 EXPECT_EQ(supported_formats[0].pixel_format, PIXEL_FORMAT_I420); | |
| 283 EXPECT_GE(supported_formats[0].frame_rate, 20.0); | |
| 284 EXPECT_EQ(supported_formats[1].frame_size.width(), 640); | |
| 285 EXPECT_EQ(supported_formats[1].frame_size.height(), 480); | |
| 286 EXPECT_EQ(supported_formats[1].pixel_format, PIXEL_FORMAT_I420); | |
| 287 EXPECT_GE(supported_formats[1].frame_rate, 20.0); | |
| 288 EXPECT_EQ(supported_formats[2].frame_size.width(), 1280); | |
| 289 EXPECT_EQ(supported_formats[2].frame_size.height(), 720); | |
| 290 EXPECT_EQ(supported_formats[2].pixel_format, PIXEL_FORMAT_I420); | |
| 291 EXPECT_GE(supported_formats[2].frame_rate, 20.0); | |
| 292 EXPECT_EQ(supported_formats[3].frame_size.width(), 1920); | |
| 293 EXPECT_EQ(supported_formats[3].frame_size.height(), 1080); | |
| 294 EXPECT_EQ(supported_formats[3].pixel_format, PIXEL_FORMAT_I420); | |
| 295 EXPECT_GE(supported_formats[3].frame_rate, 20.0); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 TEST_F(FakeVideoCaptureDeviceTest, GetAndSetCapabilities) { | |
| 300 std::unique_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( | |
| 301 FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, 30.0)); | |
| 302 ASSERT_TRUE(device); | |
| 303 | |
| 304 VideoCaptureParams capture_params; | |
| 305 capture_params.requested_format.frame_size.SetSize(640, 480); | |
| 306 capture_params.requested_format.frame_rate = 30.0; | |
| 307 device->AllocateAndStart(capture_params, std::move(client_)); | |
| 308 | |
| 309 VideoCaptureDevice::GetPhotoCapabilitiesCallback scoped_get_callback( | |
| 310 base::Bind(&ImageCaptureClient::DoOnGetPhotoCapabilities, | |
| 311 image_capture_client_), | |
| 312 base::Bind(&ImageCaptureClient::OnGetPhotoCapabilitiesFailure, | |
| 313 image_capture_client_)); | |
| 314 | |
| 315 EXPECT_CALL(*image_capture_client_.get(), OnCorrectGetPhotoCapabilities()) | |
| 316 .Times(1); | |
| 317 device->GetPhotoCapabilities(std::move(scoped_get_callback)); | |
| 318 run_loop_.reset(new base::RunLoop()); | |
| 319 run_loop_->Run(); | |
| 320 | |
| 321 auto* capabilities = image_capture_client_->capabilities(); | |
| 322 ASSERT_TRUE(capabilities); | |
| 323 EXPECT_EQ(100u, capabilities->zoom->min); | |
| 324 EXPECT_EQ(400u, capabilities->zoom->max); | |
| 325 EXPECT_GE(capabilities->zoom->current, capabilities->zoom->min); | |
| 326 EXPECT_GE(capabilities->zoom->max, capabilities->zoom->current); | |
| 327 | |
| 328 // Set options: zoom to the maximum value. | |
| 329 const unsigned int max_zoom_value = capabilities->zoom->max; | |
| 330 VideoCaptureDevice::SetPhotoOptionsCallback scoped_set_callback( | |
| 331 base::Bind(&ImageCaptureClient::OnCorrectSetPhotoOptions, | |
| 332 image_capture_client_), | |
| 333 base::Bind(&ImageCaptureClient::OnSetPhotoOptionsFailure, | |
| 334 image_capture_client_)); | |
| 335 | |
| 336 mojom::PhotoSettingsPtr settings = mojom::PhotoSettings::New(); | |
| 337 settings->zoom = max_zoom_value; | |
| 338 settings->has_zoom = true; | |
| 339 | |
| 340 EXPECT_CALL(*image_capture_client_.get(), OnCorrectSetPhotoOptions(true)) | |
| 341 .Times(1); | |
| 342 device->SetPhotoOptions(std::move(settings), std::move(scoped_set_callback)); | |
| 343 run_loop_.reset(new base::RunLoop()); | |
| 344 run_loop_->Run(); | |
| 345 | |
| 346 // Retrieve Capabilities again and check against the set values. | |
| 347 VideoCaptureDevice::GetPhotoCapabilitiesCallback scoped_get_callback2( | |
| 348 base::Bind(&ImageCaptureClient::DoOnGetPhotoCapabilities, | |
| 349 image_capture_client_), | |
| 350 base::Bind(&ImageCaptureClient::OnGetPhotoCapabilitiesFailure, | |
| 351 image_capture_client_)); | |
| 352 | |
| 353 EXPECT_CALL(*image_capture_client_.get(), OnCorrectGetPhotoCapabilities()) | |
| 354 .Times(1); | |
| 355 device->GetPhotoCapabilities(std::move(scoped_get_callback2)); | |
| 356 run_loop_.reset(new base::RunLoop()); | |
| 357 run_loop_->Run(); | |
| 358 EXPECT_EQ(max_zoom_value, | |
| 359 image_capture_client_->capabilities()->zoom->current); | |
| 360 | |
| 361 device->StopAndDeAllocate(); | |
| 362 } | |
| 363 | |
| 364 TEST_F(FakeVideoCaptureDeviceTest, TakePhoto) { | |
| 365 std::unique_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( | |
| 366 FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, 30.0)); | |
| 367 ASSERT_TRUE(device); | |
| 368 | |
| 369 VideoCaptureParams capture_params; | |
| 370 capture_params.requested_format.frame_size.SetSize(640, 480); | |
| 371 capture_params.requested_format.frame_rate = 30.0; | |
| 372 device->AllocateAndStart(capture_params, std::move(client_)); | |
| 373 | |
| 374 VideoCaptureDevice::TakePhotoCallback scoped_callback( | |
| 375 base::Bind(&ImageCaptureClient::DoOnPhotoTaken, image_capture_client_), | |
| 376 base::Bind(&ImageCaptureClient::OnTakePhotoFailure, | |
| 377 image_capture_client_)); | |
| 378 | |
| 379 EXPECT_CALL(*image_capture_client_.get(), OnCorrectPhotoTaken()).Times(1); | |
| 380 device->TakePhoto(std::move(scoped_callback)); | |
| 381 | |
| 382 run_loop_.reset(new base::RunLoop()); | |
| 383 run_loop_->Run(); | |
| 384 device->StopAndDeAllocate(); | |
| 385 } | |
| 386 | |
| 387 TEST_P(FakeVideoCaptureDeviceCommandLineTest, FrameRate) { | |
| 388 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( | |
| 389 switches::kUseFakeDeviceForMediaStream, GetParam().argument); | |
| 390 const std::unique_ptr<VideoCaptureDevice::Names> names(EnumerateDevices()); | |
| 391 ASSERT_FALSE(names->empty()); | |
| 392 | |
| 393 for (const auto& names_iterator : *names) { | |
| 394 std::unique_ptr<VideoCaptureDevice> device = | |
| 395 video_capture_device_factory_->Create(names_iterator); | |
| 396 ASSERT_TRUE(device); | |
| 397 | |
| 398 VideoCaptureParams capture_params; | |
| 399 capture_params.requested_format.frame_size.SetSize(1280, 720); | |
| 400 capture_params.requested_format.frame_rate = GetParam().fps; | |
| 401 device->AllocateAndStart(capture_params, std::move(client_)); | |
| 402 | |
| 403 WaitForCapturedFrame(); | |
| 404 EXPECT_EQ(last_format().frame_size.width(), 1280); | |
| 405 EXPECT_EQ(last_format().frame_size.height(), 720); | |
| 406 EXPECT_EQ(last_format().frame_rate, GetParam().fps); | |
| 407 device->StopAndDeAllocate(); | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 INSTANTIATE_TEST_CASE_P(, | |
| 412 FakeVideoCaptureDeviceCommandLineTest, | |
| 413 Values(CommandLineTestData{"fps=-1", 5}, | |
| 414 CommandLineTestData{"fps=29.97", 29.97f}, | |
| 415 CommandLineTestData{"fps=60", 60}, | |
| 416 CommandLineTestData{"fps=1000", 60})); | |
| 417 }; // namespace media | |
| OLD | NEW |