blob: 5ff37450c124da390ae11fade813cf178bac230d [file] [log] [blame]
// Copyright 2017 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/video_capture_system_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "build/build_config.h"
#include "media/base/bind_to_current_loop.h"
#include "media/capture/video/video_capture_metrics.h"
namespace {
// Compares two VideoCaptureFormat by checking smallest frame_size area, then
// by width, and then by _largest_ frame_rate. Used to order a
// VideoCaptureFormats vector so that the first entry for a given resolution has
// the largest frame rate.
bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
DCHECK(format1.frame_size.GetCheckedArea().IsValid());
DCHECK(format2.frame_size.GetCheckedArea().IsValid());
if (format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
format2.frame_size.GetCheckedArea().ValueOrDefault(0)) {
if (format1.frame_size.width() == format2.frame_size.width()) {
return format1.frame_rate > format2.frame_rate;
}
return format1.frame_size.width() > format2.frame_size.width();
}
return format1.frame_size.GetCheckedArea().ValueOrDefault(0) <
format2.frame_size.GetCheckedArea().ValueOrDefault(0);
}
bool IsCaptureFormatEqual(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
return format1.frame_size == format2.frame_size &&
format1.frame_rate == format2.frame_rate &&
format1.pixel_format == format2.pixel_format;
}
// This function receives a list of capture formats, sets all of them to I420
// (while keeping Y16 as is), and then removes duplicates.
void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
if (formats->empty())
return;
// Mark all formats as I420, since this is what the renderer side will get
// anyhow: the actual pixel format is decided at the device level.
// Don't do this for the Y16 or NV12 formats as they are handled separately.
for (auto& format : *formats) {
if (format.pixel_format != media::PIXEL_FORMAT_Y16 &&
format.pixel_format != media::PIXEL_FORMAT_NV12)
format.pixel_format = media::PIXEL_FORMAT_I420;
}
std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
// Remove duplicates
auto last =
std::unique(formats->begin(), formats->end(), IsCaptureFormatEqual);
formats->erase(last, formats->end());
}
} // anonymous namespace
namespace media {
VideoCaptureSystemImpl::VideoCaptureSystemImpl(
std::unique_ptr<VideoCaptureDeviceFactory> factory)
: factory_(std::move(factory)) {
thread_checker_.DetachFromThread();
}
VideoCaptureSystemImpl::~VideoCaptureSystemImpl() = default;
void VideoCaptureSystemImpl::GetDeviceInfosAsync(
DeviceInfoCallback result_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
device_enum_request_queue_.push_back(std::move(result_callback));
if (device_enum_request_queue_.size() == 1) {
// base::Unretained() is safe because |factory_| is owned and it guarantees
// not to call the callback after destruction.
factory_->GetDevicesInfo(base::BindOnce(
&VideoCaptureSystemImpl::DevicesInfoReady, base::Unretained(this)));
}
}
std::unique_ptr<VideoCaptureDevice> VideoCaptureSystemImpl::CreateDevice(
const std::string& device_id) {
DCHECK(thread_checker_.CalledOnValidThread());
const VideoCaptureDeviceInfo* device_info = LookupDeviceInfoFromId(device_id);
if (!device_info)
return nullptr;
return factory_->CreateDevice(device_info->descriptor);
}
const VideoCaptureDeviceInfo* VideoCaptureSystemImpl::LookupDeviceInfoFromId(
const std::string& device_id) {
DCHECK(thread_checker_.CalledOnValidThread());
auto iter =
std::find_if(devices_info_cache_.begin(), devices_info_cache_.end(),
[&device_id](const VideoCaptureDeviceInfo& device_info) {
return device_info.descriptor.device_id == device_id;
});
if (iter == devices_info_cache_.end())
return nullptr;
return &(*iter);
}
void VideoCaptureSystemImpl::DevicesInfoReady(
std::vector<VideoCaptureDeviceInfo> devices_info) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!device_enum_request_queue_.empty());
// Only save metrics the first time device infos are populated.
if (devices_info_cache_.empty()) {
LogCaptureDeviceMetrics(devices_info);
}
for (auto& device_info : devices_info) {
ConsolidateCaptureFormats(&device_info.supported_formats);
}
devices_info_cache_ = std::move(devices_info);
DeviceEnumQueue requests;
std::swap(requests, device_enum_request_queue_);
auto weak_this = weak_factory_.GetWeakPtr();
for (auto& request : requests) {
std::move(request).Run(devices_info_cache_);
// Callbacks may destroy |this|.
if (!weak_this)
return;
}
}
} // namespace media