0

[Reland][Fuchsia] Implement camera device enumeration

Added camera device enumeration in VideoCaptureDeviceFactoryFuchsia
using fuchsia.camera3 API.
Also updated CMX files for runners to list fuchsia.camera3.DeviceWatcher
service.

Originally review
https://chromium-review.googlesource.com/c/chromium/src/+/2157870

TBR=ddorwin@chromium.org, nasko@chromium.org, guidou@chromium.org

Bug: 1064731
Change-Id: I884c0335ff10b2b4445d1037d8794590914facae
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2166342
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#762560}
This commit is contained in:
Sergey Ulanov
2020-04-24 22:50:51 +00:00
committed by Commit Bot
parent a7cb0cf42e
commit 1901bee30d
14 changed files with 273 additions and 9 deletions

@ -13,6 +13,7 @@
],
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",

@ -13,6 +13,7 @@
],
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",

@ -559,10 +559,7 @@ void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
device_dict->SetString("id", descriptor.device_id);
device_dict->SetString("name", descriptor.GetNameAndModel());
device_dict->Set("formats", std::move(format_list));
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_ANDROID)
device_dict->SetString("captureApi", descriptor.GetCaptureApiTypeString());
#endif
video_capture_capabilities_cached_data_.Append(std::move(device_dict));
}

@ -48,7 +48,7 @@ namespace content {
// Mainline routine for running as the utility process.
int UtilityMain(const MainFunctionParams& parameters) {
const base::MessagePumpType message_pump_type =
base::MessagePumpType message_pump_type =
parameters.command_line.HasSwitch(switches::kMessageLoopTypeUi)
? base::MessagePumpType::UI
: base::MessagePumpType::DEFAULT;
@ -66,6 +66,12 @@ int UtilityMain(const MainFunctionParams& parameters) {
});
#endif
#if defined(OS_FUCHSIA)
// On Fuchsia always use IO threads to allow FIDL calls.
if (message_pump_type == base::MessagePumpType::DEFAULT)
message_pump_type = base::MessagePumpType::IO;
#endif // defined(OS_FUCHSIA)
// The main task executor of the utility process.
base::SingleThreadTaskExecutor main_thread_task_executor(message_pump_type);
base::PlatformThread::SetName("CrUtilityMain");

@ -6,6 +6,7 @@
],
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",

@ -6,6 +6,7 @@
"services": [
"chromium.cast.ApplicationConfigManager",
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",

@ -6,6 +6,7 @@
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.device.NameProvider",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",

@ -310,6 +310,10 @@ jumbo_component("capture_lib") {
"video/fuchsia/video_capture_device_factory_fuchsia.cc",
"video/fuchsia/video_capture_device_factory_fuchsia.h",
]
deps += [
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
]
}
}

@ -74,6 +74,7 @@ enum VideoCaptureApi {
ANDROID_API2_LEGACY,
ANDROID_API2_FULL,
ANDROID_API2_LIMITED,
FUCHSIA_CAMERA3,
VIRTUAL_DEVICE,
UNKNOWN
};

@ -1510,6 +1510,8 @@ EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi>::ToMojom(
return media::mojom::VideoCaptureApi::ANDROID_API2_FULL;
case media::VideoCaptureApi::ANDROID_API2_LIMITED:
return media::mojom::VideoCaptureApi::ANDROID_API2_LIMITED;
case media::VideoCaptureApi::FUCHSIA_CAMERA3:
return media::mojom::VideoCaptureApi::FUCHSIA_CAMERA3;
case media::VideoCaptureApi::VIRTUAL_DEVICE:
return media::mojom::VideoCaptureApi::VIRTUAL_DEVICE;
case media::VideoCaptureApi::UNKNOWN:
@ -1554,6 +1556,9 @@ bool EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi>::
case media::mojom::VideoCaptureApi::ANDROID_API2_LIMITED:
*output = media::VideoCaptureApi::ANDROID_API2_LIMITED;
return true;
case media::mojom::VideoCaptureApi::FUCHSIA_CAMERA3:
*output = media::VideoCaptureApi::FUCHSIA_CAMERA3;
return true;
case media::mojom::VideoCaptureApi::VIRTUAL_DEVICE:
*output = media::VideoCaptureApi::VIRTUAL_DEVICE;
return true;

@ -4,13 +4,113 @@
#include "media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h"
#include <lib/sys/cpp/component_context.h>
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
namespace media {
VideoCaptureDeviceFactoryFuchsia::VideoCaptureDeviceFactoryFuchsia() = default;
VideoCaptureDeviceFactoryFuchsia::~VideoCaptureDeviceFactoryFuchsia() = default;
class VideoCaptureDeviceFactoryFuchsia::DeviceInfoFetcher {
public:
DeviceInfoFetcher(uint64_t device_id, fuchsia::camera3::DevicePtr device)
: device_id_(device_id), device_(std::move(device)) {
device_.set_error_handler(
fit::bind_member(this, &DeviceInfoFetcher::OnError));
device_->GetIdentifier(
fit::bind_member(this, &DeviceInfoFetcher::OnDescription));
device_->GetConfigurations(
fit::bind_member(this, &DeviceInfoFetcher::OnConfigurations));
}
~DeviceInfoFetcher() = default;
DeviceInfoFetcher(const DeviceInfoFetcher&) = delete;
const DeviceInfoFetcher& operator=(const DeviceInfoFetcher&) = delete;
void WaitResults() {
DCHECK(!wait_results_run_loop_);
if (have_results())
return;
if (!device_)
return;
wait_results_run_loop_.emplace();
wait_results_run_loop_->Run();
wait_results_run_loop_.reset();
}
bool have_results() const { return description_ && formats_; }
bool is_usable() const { return device_ || have_results(); }
uint64_t device_id() const { return device_id_; }
const std::string& description() const { return description_.value(); }
const VideoCaptureFormats& formats() const { return formats_.value(); }
private:
void OnError(zx_status_t status) {
ZX_LOG(ERROR, status) << "fuchsia.camera3.Device disconnected";
if (wait_results_run_loop_)
wait_results_run_loop_->Quit();
}
void OnDescription(fidl::StringPtr identifier) {
description_ = identifier.value_or("");
MaybeSignalDone();
}
void OnConfigurations(std::vector<fuchsia::camera3::Configuration> configs) {
VideoCaptureFormats formats;
for (auto& config : configs) {
for (auto& props : config.streams) {
VideoCaptureFormat format;
format.frame_size = gfx::Size(props.image_format.display_width,
props.image_format.display_height);
format.frame_rate = static_cast<float>(props.frame_rate.numerator) /
props.frame_rate.denominator;
// VideoFrameCapturerFuchsia converts all formats to I420.
format.pixel_format = PIXEL_FORMAT_I420;
formats.push_back(format);
}
}
formats_ = std::move(formats);
MaybeSignalDone();
}
void MaybeSignalDone() {
if (!have_results())
return;
device_.Unbind();
if (wait_results_run_loop_)
wait_results_run_loop_->Quit();
}
uint64_t device_id_;
fuchsia::camera3::DevicePtr device_;
base::Optional<std::string> description_;
base::Optional<VideoCaptureFormats> formats_;
base::Optional<base::RunLoop> wait_results_run_loop_;
};
VideoCaptureDeviceFactoryFuchsia::VideoCaptureDeviceFactoryFuchsia() {
DETACH_FROM_THREAD(thread_checker_);
}
VideoCaptureDeviceFactoryFuchsia::~VideoCaptureDeviceFactoryFuchsia() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
std::unique_ptr<VideoCaptureDevice>
VideoCaptureDeviceFactoryFuchsia::CreateDevice(
@ -23,14 +123,125 @@ VideoCaptureDeviceFactoryFuchsia::CreateDevice(
void VideoCaptureDeviceFactoryFuchsia::GetDeviceDescriptors(
VideoCaptureDeviceDescriptors* device_descriptors) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
device_descriptors->clear();
if (!device_watcher_) {
DCHECK(!first_update_run_loop_);
DCHECK(devices_.empty());
Initialize();
// The RunLoop will quit when either we've received the first WatchDevices()
// response or DeviceWatcher fails. |devices_| will be empty in case of a
// failure.
first_update_run_loop_.emplace();
first_update_run_loop_->Run();
first_update_run_loop_.reset();
}
for (auto& d : devices_) {
d.second->WaitResults();
if (d.second->is_usable()) {
device_descriptors->push_back(VideoCaptureDeviceDescriptor(
d.second->description(), base::NumberToString(d.first),
VideoCaptureApi::FUCHSIA_CAMERA3));
}
}
}
void VideoCaptureDeviceFactoryFuchsia::GetSupportedFormats(
const VideoCaptureDeviceDescriptor& device,
VideoCaptureFormats* capture_formats) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
capture_formats->clear();
uint64_t device_id;
bool converted = base::StringToUint64(device.device_id, &device_id);
DCHECK(converted);
auto it = devices_.find(device_id);
if (it == devices_.end()) {
capture_formats->clear();
} else {
it->second->WaitResults();
*capture_formats = it->second->formats();
}
}
void VideoCaptureDeviceFactoryFuchsia::Initialize() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!device_watcher_);
DCHECK(devices_.empty());
base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect(
device_watcher_.NewRequest());
device_watcher_.set_error_handler(fit::bind_member(
this, &VideoCaptureDeviceFactoryFuchsia::OnDeviceWatcherDisconnected));
WatchDevices();
}
void VideoCaptureDeviceFactoryFuchsia::OnDeviceWatcherDisconnected(
zx_status_t status) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ZX_LOG(ERROR, status) << "fuchsia.camera3.DeviceWatcher disconnected.";
devices_.clear();
if (first_update_run_loop_)
first_update_run_loop_->Quit();
}
void VideoCaptureDeviceFactoryFuchsia::WatchDevices() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
device_watcher_->WatchDevices(fit::bind_member(
this, &VideoCaptureDeviceFactoryFuchsia::OnWatchDevicesResult));
}
void VideoCaptureDeviceFactoryFuchsia::OnWatchDevicesResult(
std::vector<fuchsia::camera3::WatchDevicesEvent> events) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto& e : events) {
if (e.is_removed()) {
int erased = devices_.erase(e.removed());
if (!erased) {
LOG(WARNING) << "Received device removed event for a device that "
"wasn't previously registered.";
}
continue;
}
uint64_t id;
if (e.is_added()) {
id = e.added();
if (devices_.find(id) != devices_.end()) {
LOG(WARNING) << "Received device added event for a device that was "
"previously registered.";
continue;
}
} else {
id = e.existing();
if (devices_.find(id) != devices_.end()) {
continue;
}
LOG(WARNING) << "Received device exists event for a device that wasn't "
"previously registered.";
}
fuchsia::camera3::DevicePtr device;
device_watcher_->ConnectToDevice(id, device.NewRequest());
devices_.emplace(
id, std::make_unique<DeviceInfoFetcher>(id, std::move(device)));
}
if (first_update_run_loop_)
first_update_run_loop_->Quit();
// Watch for further updates.
WatchDevices();
}
} // namespace media

@ -5,6 +5,13 @@
#ifndef MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FACTORY_FUCHSIA_H_
#define MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FACTORY_FUCHSIA_H_
#include <fuchsia/camera3/cpp/fidl.h>
#include <map>
#include "base/containers/small_map.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "media/capture/video/video_capture_device_factory.h"
namespace media {
@ -15,6 +22,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryFuchsia
VideoCaptureDeviceFactoryFuchsia();
~VideoCaptureDeviceFactoryFuchsia() override;
VideoCaptureDeviceFactoryFuchsia(const VideoCaptureDeviceFactoryFuchsia&) =
delete;
VideoCaptureDeviceFactoryFuchsia& operator=(
const VideoCaptureDeviceFactoryFuchsia&) = delete;
// VideoCaptureDeviceFactory implementation.
std::unique_ptr<VideoCaptureDevice> CreateDevice(
const VideoCaptureDeviceDescriptor& device_descriptor) override;
void GetDeviceDescriptors(
@ -23,7 +36,26 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryFuchsia
VideoCaptureFormats* supported_formats) override;
private:
DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactoryFuchsia);
// Helper class used to fetch per-device information.
class DeviceInfoFetcher;
void Initialize();
void OnDeviceWatcherDisconnected(zx_status_t status);
void WatchDevices();
void OnWatchDevicesResult(
std::vector<fuchsia::camera3::WatchDevicesEvent> events);
fuchsia::camera3::DeviceWatcherPtr device_watcher_;
base::small_map<std::map<uint64_t, std::unique_ptr<DeviceInfoFetcher>>>
devices_;
// RunLoop used to wait for the first WatchDevices() response. Currently
// required because GetDeviceDescriptors() is synchronous.
// TODO(crbug.com/1072932) Refactor interface to allow asynchronous
// enumeration and remove this hack.
base::Optional<base::RunLoop> first_update_run_loop_;
};
} // namespace media

@ -89,6 +89,8 @@ const char* VideoCaptureDeviceDescriptor::GetCaptureApiTypeString() const {
return "Camera API2 Full";
case VideoCaptureApi::ANDROID_API2_LIMITED:
return "Camera API2 Limited";
case VideoCaptureApi::FUCHSIA_CAMERA3:
return "fuchsia.camera3 API";
case VideoCaptureApi::VIRTUAL_DEVICE:
return "Virtual Device";
case VideoCaptureApi::UNKNOWN:

@ -26,6 +26,7 @@ enum class VideoCaptureApi {
ANDROID_API2_LEGACY,
ANDROID_API2_FULL,
ANDROID_API2_LIMITED,
FUCHSIA_CAMERA3,
VIRTUAL_DEVICE,
UNKNOWN
};