blob: 222e0ab158291780a638cdef12cfb57b2471942c [file] [log] [blame]
// 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_factory.h"
#include <array>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "media/base/media_switches.h"
namespace {
// Cap the frame rate command line input to reasonable values.
static const float kFakeCaptureMinFrameRate = 5.0f;
static const float kFakeCaptureMaxFrameRate = 60.0f;
// Cap the device count command line input to reasonable values.
static const int kFakeCaptureMinDeviceCount = 0;
static const int kFakeCaptureMaxDeviceCount = 10;
static const int kDefaultDeviceCount = 1;
static const char* kDefaultDeviceIdMask = "/dev/video%d";
static const media::FakeVideoCaptureDevice::DeliveryMode kDefaultDeliveryMode =
media::FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS;
static constexpr std::array<gfx::Size, 6> kDefaultResolutions{
{gfx::Size(96, 96), gfx::Size(320, 240), gfx::Size(640, 480),
gfx::Size(1280, 720), gfx::Size(1920, 1080), gfx::Size(3840, 2160)}};
static constexpr std::array<float, 1> kDefaultFrameRates{{20.0f}};
static const double kInitialPan = 100.0;
static const double kInitialTilt = 100.0;
static const double kInitialZoom = 100.0;
static const double kInitialExposureTime = 50.0;
static const double kInitialFocusDistance = 50.0;
static const media::VideoPixelFormat kSupportedPixelFormats[] = {
media::PIXEL_FORMAT_I420, media::PIXEL_FORMAT_Y16,
media::PIXEL_FORMAT_MJPEG};
template <typename TElement, size_t TSize>
std::vector<TElement> ArrayToVector(const std::array<TElement, TSize>& arr) {
return std::vector<TElement>(arr.begin(), arr.end());
}
media::VideoPixelFormat GetPixelFormatFromDeviceIndex(int device_index) {
if (device_index == 1)
return media::PIXEL_FORMAT_Y16;
if (device_index == 2)
return media::PIXEL_FORMAT_MJPEG;
return media::PIXEL_FORMAT_I420;
}
void AppendAllCombinationsToFormatsContainer(
const std::vector<media::VideoPixelFormat>& pixel_formats,
const std::vector<gfx::Size>& resolutions,
const std::vector<float>& frame_rates,
media::VideoCaptureFormats* output) {
for (const auto& pixel_format : pixel_formats) {
for (const auto& resolution : resolutions) {
for (const auto& frame_rate : frame_rates)
output->emplace_back(resolution, frame_rate, pixel_format);
}
}
}
class ErrorFakeDevice : public media::VideoCaptureDevice {
public:
// VideoCaptureDevice implementation.
void AllocateAndStart(const media::VideoCaptureParams& params,
std::unique_ptr<Client> client) override {
client->OnError(media::VideoCaptureError::
kErrorFakeDeviceIntentionallyEmittingErrorEvent,
FROM_HERE, "Device has no supported formats.");
}
void StopAndDeAllocate() override {}
void GetPhotoState(GetPhotoStateCallback callback) override {}
void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings,
SetPhotoOptionsCallback callback) override {}
void TakePhoto(TakePhotoCallback callback) override {}
};
} // anonymous namespace
namespace media {
FakeVideoCaptureDeviceSettings::FakeVideoCaptureDeviceSettings() = default;
FakeVideoCaptureDeviceSettings::~FakeVideoCaptureDeviceSettings() = default;
FakeVideoCaptureDeviceSettings::FakeVideoCaptureDeviceSettings(
const FakeVideoCaptureDeviceSettings& other) = default;
constexpr char
FakeVideoCaptureDeviceFactory::kDeviceConfigForGetPhotoStateFails[];
constexpr char
FakeVideoCaptureDeviceFactory::kDeviceConfigForSetPhotoOptionsFails[];
constexpr char FakeVideoCaptureDeviceFactory::kDeviceConfigForTakePhotoFails[];
FakeVideoCaptureDeviceFactory::FakeVideoCaptureDeviceFactory() {
// The default |devices_config_| is the one obtained from an empty options
// string.
ParseFakeDevicesConfigFromOptionsString("", &devices_config_);
}
FakeVideoCaptureDeviceFactory::~FakeVideoCaptureDeviceFactory() = default;
// static
std::unique_ptr<VideoCaptureDevice>
FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings(
const FakeVideoCaptureDeviceSettings& settings,
std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support) {
if (settings.supported_formats.empty())
return CreateErrorDevice();
for (const auto& entry : settings.supported_formats) {
bool pixel_format_supported = false;
for (const auto& supported_pixel_format : kSupportedPixelFormats) {
if (entry.pixel_format == supported_pixel_format) {
pixel_format_supported = true;
break;
}
}
if (!pixel_format_supported) {
DLOG(ERROR) << "Requested an unsupported pixel format "
<< VideoPixelFormatToString(entry.pixel_format);
return nullptr;
}
}
const VideoCaptureFormat& initial_format = settings.supported_formats.front();
auto device_state = std::make_unique<FakeDeviceState>(
kInitialPan, kInitialTilt, kInitialZoom, kInitialExposureTime,
kInitialFocusDistance, initial_format.frame_rate,
initial_format.pixel_format);
auto photo_frame_painter = std::make_unique<PacmanFramePainter>(
PacmanFramePainter::Format::SK_N32, device_state.get());
auto photo_device = std::make_unique<FakePhotoDevice>(
std::move(photo_frame_painter), device_state.get(),
settings.photo_device_config);
return std::make_unique<FakeVideoCaptureDevice>(
settings.supported_formats,
std::make_unique<FrameDelivererFactory>(
settings.delivery_mode, device_state.get(), std::move(gmb_support)),
std::move(photo_device), std::move(device_state));
}
// static
std::unique_ptr<VideoCaptureDevice>
FakeVideoCaptureDeviceFactory::CreateDeviceWithDefaultResolutions(
VideoPixelFormat pixel_format,
FakeVideoCaptureDevice::DeliveryMode delivery_mode,
float frame_rate,
std::unique_ptr<gpu::GpuMemoryBufferSupport> gmb_support) {
FakeVideoCaptureDeviceSettings settings;
settings.delivery_mode = delivery_mode;
for (const gfx::Size& resolution : kDefaultResolutions)
settings.supported_formats.emplace_back(resolution, frame_rate,
pixel_format);
return CreateDeviceWithSettings(settings, std::move(gmb_support));
}
// static
std::unique_ptr<VideoCaptureDevice>
FakeVideoCaptureDeviceFactory::CreateErrorDevice() {
return std::make_unique<ErrorFakeDevice>();
}
void FakeVideoCaptureDeviceFactory::SetToDefaultDevicesConfig(
int device_count) {
devices_config_.clear();
ParseFakeDevicesConfigFromOptionsString(
base::StringPrintf("device-count=%d", device_count), &devices_config_);
}
void FakeVideoCaptureDeviceFactory::SetToCustomDevicesConfig(
const std::vector<FakeVideoCaptureDeviceSettings>& config) {
devices_config_ = config;
}
std::unique_ptr<VideoCaptureDevice> FakeVideoCaptureDeviceFactory::CreateDevice(
const VideoCaptureDeviceDescriptor& device_descriptor) {
DCHECK(thread_checker_.CalledOnValidThread());
for (const auto& entry : devices_config_) {
if (device_descriptor.device_id != entry.device_id)
continue;
return CreateDeviceWithSettings(entry);
}
return nullptr;
}
void FakeVideoCaptureDeviceFactory::GetDevicesInfo(
GetDevicesInfoCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<VideoCaptureDeviceInfo> devices_info;
int entry_index = 0;
for (const auto& entry : devices_config_) {
VideoCaptureApi api =
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
VideoCaptureApi::LINUX_V4L2_SINGLE_PLANE;
#elif defined(OS_MAC)
VideoCaptureApi::MACOSX_AVFOUNDATION;
#elif defined(OS_WIN)
VideoCaptureApi::WIN_DIRECT_SHOW;
#elif defined(OS_ANDROID)
VideoCaptureApi::ANDROID_API2_LEGACY;
#elif defined(OS_FUCHSIA)
VideoCaptureApi::FUCHSIA_CAMERA3;
#else
#error Unsupported platform
#endif
devices_info.emplace_back(VideoCaptureDeviceDescriptor(
base::StringPrintf("fake_device_%d", entry_index), entry.device_id, api,
entry.photo_device_config.control_support,
VideoCaptureTransportType::OTHER_TRANSPORT));
devices_info.back().supported_formats =
GetSupportedFormats(entry.device_id);
entry_index++;
}
std::move(callback).Run(std::move(devices_info));
}
VideoCaptureFormats FakeVideoCaptureDeviceFactory::GetSupportedFormats(
const std::string& device_id) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureFormats supported_formats;
for (const auto& entry : devices_config_) {
if (device_id != entry.device_id)
continue;
supported_formats.insert(supported_formats.end(),
entry.supported_formats.begin(),
entry.supported_formats.end());
}
return supported_formats;
}
// static
void FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(
const std::string options_string,
std::vector<FakeVideoCaptureDeviceSettings>* config) {
base::StringTokenizer option_tokenizer(options_string, ", ");
option_tokenizer.set_quote_chars("\"");
FakeVideoCaptureDevice::DeliveryMode delivery_mode = kDefaultDeliveryMode;
std::vector<gfx::Size> resolutions = ArrayToVector(kDefaultResolutions);
std::vector<float> frame_rates = ArrayToVector(kDefaultFrameRates);
int device_count = kDefaultDeviceCount;
FakePhotoDeviceConfig photo_device_config;
FakeVideoCaptureDevice::DisplayMediaType display_media_type =
FakeVideoCaptureDevice::DisplayMediaType::ANY;
while (option_tokenizer.GetNext()) {
std::vector<base::StringPiece> param = base::SplitStringPiece(
option_tokenizer.token_piece(), "=", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
if (param.size() != 2u) {
LOG(WARNING) << "Forget a value '" << options_string
<< "'? Use name=value for "
<< switches::kUseFakeDeviceForMediaStream << ".";
return;
}
if (base::EqualsCaseInsensitiveASCII(param.front(), "ownership") &&
base::EqualsCaseInsensitiveASCII(param.back(), "client")) {
delivery_mode =
FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS;
} else if (base::EqualsCaseInsensitiveASCII(param.front(), "fps")) {
double parsed_fps = 0;
if (base::StringToDouble(param.back(), &parsed_fps)) {
float capped_frame_rate =
std::max(kFakeCaptureMinFrameRate, static_cast<float>(parsed_fps));
capped_frame_rate =
std::min(kFakeCaptureMaxFrameRate, capped_frame_rate);
frame_rates.clear();
frame_rates.push_back(capped_frame_rate);
}
} else if (base::EqualsCaseInsensitiveASCII(param.front(),
"device-count")) {
unsigned int count = 0;
if (base::StringToUint(param.back(), &count)) {
device_count = std::min(
kFakeCaptureMaxDeviceCount,
std::max(kFakeCaptureMinDeviceCount, static_cast<int>(count)));
}
} else if (base::EqualsCaseInsensitiveASCII(param.front(), "config")) {
const int device_index = 0;
std::vector<VideoPixelFormat> pixel_formats;
pixel_formats.push_back(GetPixelFormatFromDeviceIndex(device_index));
FakeVideoCaptureDeviceSettings settings;
settings.delivery_mode = delivery_mode;
settings.device_id =
base::StringPrintf(kDefaultDeviceIdMask, device_index);
AppendAllCombinationsToFormatsContainer(
pixel_formats, resolutions, frame_rates, &settings.supported_formats);
if (param.back() == kDeviceConfigForGetPhotoStateFails) {
settings.photo_device_config.should_fail_get_photo_capabilities = true;
config->push_back(settings);
return;
}
if (param.back() == kDeviceConfigForSetPhotoOptionsFails) {
settings.photo_device_config.should_fail_set_photo_options = true;
config->push_back(settings);
return;
}
if (param.back() == kDeviceConfigForTakePhotoFails) {
settings.photo_device_config.should_fail_take_photo = true;
config->push_back(settings);
return;
}
LOG(WARNING) << "Unknown config " << param.back();
return;
} else if (base::EqualsCaseInsensitiveASCII(param.front(),
"display-media-type")) {
if (base::EqualsCaseInsensitiveASCII(param.back(), "any")) {
display_media_type = FakeVideoCaptureDevice::DisplayMediaType::ANY;
} else if (base::EqualsCaseInsensitiveASCII(param.back(), "monitor")) {
display_media_type = FakeVideoCaptureDevice::DisplayMediaType::MONITOR;
} else if (base::EqualsCaseInsensitiveASCII(param.back(), "window")) {
display_media_type = FakeVideoCaptureDevice::DisplayMediaType::WINDOW;
} else if (base::EqualsCaseInsensitiveASCII(param.back(), "browser")) {
display_media_type = FakeVideoCaptureDevice::DisplayMediaType::BROWSER;
}
} else if (base::EqualsCaseInsensitiveASCII(param.front(),
"hardware-support")) {
photo_device_config.control_support = VideoCaptureControlSupport();
if (!base::EqualsCaseInsensitiveASCII(param.back(), "none")) {
for (const std::string& support :
base::SplitString(param.back(), "-", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
if (base::EqualsCaseInsensitiveASCII(support, "pan"))
photo_device_config.control_support.pan = true;
else if (base::EqualsCaseInsensitiveASCII(support, "tilt"))
photo_device_config.control_support.tilt = true;
else if (base::EqualsCaseInsensitiveASCII(support, "zoom"))
photo_device_config.control_support.zoom = true;
else
LOG(WARNING) << "Unsupported hardware support " << support;
}
}
}
}
for (int device_index = 0; device_index < device_count; device_index++) {
std::vector<VideoPixelFormat> pixel_formats;
pixel_formats.push_back(GetPixelFormatFromDeviceIndex(device_index));
FakeVideoCaptureDeviceSettings settings;
settings.delivery_mode = delivery_mode;
settings.device_id = base::StringPrintf(kDefaultDeviceIdMask, device_index);
AppendAllCombinationsToFormatsContainer(
pixel_formats, resolutions, frame_rates, &settings.supported_formats);
settings.photo_device_config = photo_device_config;
settings.display_media_type = display_media_type;
config->push_back(settings);
}
}
} // namespace media