| // 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 |