blob: f88845dd1c824b3e09df2aa49de6b0d02a93d279 [file] [log] [blame] [edit]
/* Copyright 2022 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <string>
#include <vector>
#include <base/containers/contains.h>
#include <base/strings/string_util.h>
#include <base/strings/string_number_conversions.h>
#include "cros-camera/common.h"
#include "hal/fake/hal_spec.h"
#include "hal/fake/value_util.h"
namespace cros {
namespace {
constexpr char kCamerasKey[] = "cameras";
constexpr char kIdKey[] = "id";
constexpr char kConnectedKey[] = "connected";
constexpr char kSupportedFormatsKey[] = "supported_formats";
constexpr char kWidthKey[] = "width";
constexpr char kHeightKey[] = "height";
constexpr char kFrameRatesKey[] = "frame_rates";
constexpr char kFramesKey[] = "frames";
constexpr char kPathKey[] = "path";
constexpr char kScaleModeKey[] = "scale_mode";
constexpr char kScaleModeStretch[] = "stretch";
constexpr char kScaleModeCover[] = "cover";
constexpr char kScaleModeContain[] = "contain";
constexpr char kLoopModeKey[] = "loop_mode";
constexpr char kLoopModeLoop[] = "loop";
constexpr char kLoopModePingPong[] = "ping_pong";
constexpr char kSolidColorBlob[] = "solid_color_blob";
// Default fps ranges, this conform to the minimal required fps ranges as in
// https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
const std::vector<std::pair<int, int>> kDefaultFpsRanges = {{15, 60}, {60, 60}};
FramesSpec ParseFramesSpec(const DictWithPath& frames_value) {
if (auto path = GetRequiredValue<std::string>(frames_value, kPathKey)) {
ScaleMode scale_mode = ScaleMode::kStretch;
if (auto scale_mode_str =
GetValue<std::string>(frames_value, kScaleModeKey)) {
if (*scale_mode_str == kScaleModeCover) {
scale_mode = ScaleMode::kCover;
} else if (*scale_mode_str == kScaleModeContain) {
scale_mode = ScaleMode::kContain;
} else if (*scale_mode_str == kScaleModeStretch) {
scale_mode = ScaleMode::kStretch;
} else {
LOGF(WARNING) << "invalid scale mode " << *scale_mode_str << " at "
<< frames_value.path << "." << kScaleModeKey
<< ", fallback to stretch";
}
}
LoopMode loop_mode = LoopMode::kLoop;
if (auto loop_mode_str =
GetValue<std::string>(frames_value, kLoopModeKey)) {
if (*loop_mode_str == kLoopModeLoop) {
loop_mode = LoopMode::kLoop;
} else if (*loop_mode_str == kLoopModePingPong) {
loop_mode = LoopMode::kPingPong;
} else {
LOGF(WARNING) << "invalid loop mode " << *loop_mode_str << " at "
<< frames_value.path << "." << kScaleModeKey
<< ", fallback to loop";
}
}
return FramesFileSpec{base::FilePath(*path), scale_mode, loop_mode};
}
return FramesTestPatternSpec();
}
// Parses an entry in frame_rates array of the supported formats.
// The supported formats are: 60 (int), [60] (list of one int), [60, 120] (list
// of two ints).
std::optional<std::pair<int, int>> ParseFrameRate(
const ValueWithPath& frame_rate) {
int low, high;
if (auto fps_range_value = GetIfList(frame_rate)) {
std::vector<int> values;
bool valid = true;
for (const auto& raw_value : *fps_range_value) {
auto value = raw_value->GetIfInt();
if (!value) {
valid = false;
break;
}
values.push_back(*value);
if (values.size() > 2) {
valid = false;
break;
}
}
if (!valid || values.size() == 0) {
LOGF(WARNING) << "invalid frame_rate range " << fps_range_value->value
<< " at " << fps_range_value->path << ", ignore";
return std::nullopt;
}
// We accept either [fps] or [low, high] for fps range.
if (values.size() == 1) {
low = values[0];
high = values[0];
} else if (values.size() == 2) {
low = values[0];
high = values[1];
} else {
NOTREACHED();
return std::nullopt;
}
} else if (auto fps_value = frame_rate->GetIfInt()) {
low = *fps_value;
high = *fps_value;
} else {
LOGF(WARNING) << "unknown type of frame_rate " << frame_rate.value << " at "
<< frame_rate.path << ", ignore";
return std::nullopt;
}
if (low <= 0 || high < low) {
LOGF(WARNING) << "invalid frame_rate range: " << low << " " << high
<< " at " << frame_rate.path << ", ignore";
return std::nullopt;
}
return std::make_pair(low, high);
}
std::vector<SupportedFormatSpec> ParseSupportedFormatSpecs(
const ListWithPath& supported_formats_value) {
std::vector<SupportedFormatSpec> supported_formats;
// TODO(pihsun): This currently might not satisfy the requirement, since
// 240p, 480p, 720p might be missing.
for (const auto& c : supported_formats_value) {
auto supported_format_value = GetIfDict(c);
if (!supported_format_value.has_value()) {
continue;
}
SupportedFormatSpec supported_format;
if (auto width =
GetRequiredValue<int>(*supported_format_value, kWidthKey)) {
supported_format.width = *width;
} else {
continue;
}
if (auto height =
GetRequiredValue<int>(*supported_format_value, kHeightKey)) {
supported_format.height = *height;
} else {
continue;
}
if (auto frame_rates =
GetValue<ListWithPath>(*supported_format_value, kFrameRatesKey)) {
for (const auto& frame_rate : *frame_rates) {
if (const auto& parsed_frame_rate = ParseFrameRate(frame_rate)) {
supported_format.fps_ranges.push_back(*parsed_frame_rate);
}
}
if (supported_format.fps_ranges.empty()) {
LOGF(WARNING) << "empty frame_rates at " << frame_rates->path
<< ", ignore";
continue;
}
} else {
supported_format.fps_ranges = kDefaultFpsRanges;
}
// TODO(pihsun): Support actual format.
supported_formats.push_back(supported_format);
}
return supported_formats;
}
std::vector<CameraSpec> ParseCameraSpecs(const ListWithPath& cameras_value) {
std::vector<CameraSpec> camera_specs;
for (auto c : cameras_value) {
auto spec_value = GetIfDict(c);
if (!spec_value.has_value()) {
continue;
}
CameraSpec camera_spec;
if (auto id = GetRequiredValue<int>(*spec_value, kIdKey)) {
if (base::Contains(camera_specs, *id,
[](const CameraSpec& spec) { return spec.id; })) {
LOGF(WARNING) << "duplicated id " << *id << " at " << spec_value->path
<< ".id, ignore";
continue;
}
camera_spec.id = *id;
} else {
// TODO(pihsun): Use generated ID for this case?
continue;
}
camera_spec.connected =
GetValue<bool>(*spec_value, kConnectedKey).value_or(false);
if (auto frames = GetValue<DictWithPath>(*spec_value, kFramesKey)) {
camera_spec.frames = ParseFramesSpec(*frames);
} else {
camera_spec.frames = FramesTestPatternSpec();
}
if (auto supported_formats =
GetValue<ListWithPath>(*spec_value, kSupportedFormatsKey)) {
camera_spec.supported_formats =
ParseSupportedFormatSpecs(*supported_formats);
if (camera_spec.supported_formats.empty()) {
LOGF(WARNING) << "empty supported_formats at "
<< supported_formats->path << ", ignore";
continue;
}
} else {
// Using default supported formats.
// Resolutions are the required ones in
// https://chromeos.google.com/partner/dlm/docs/latest-requirements/chromebook.html#cam-sw-0003-v01
camera_spec.supported_formats = {
{
.width = 320,
.height = 240,
.fps_ranges = kDefaultFpsRanges,
},
{
.width = 640,
.height = 360,
.fps_ranges = kDefaultFpsRanges,
},
{
.width = 640,
.height = 480,
.fps_ranges = kDefaultFpsRanges,
},
{
.width = 1280,
.height = 720,
.fps_ranges = kDefaultFpsRanges,
},
{
.width = 1280,
.height = 960,
.fps_ranges = kDefaultFpsRanges,
},
{
.width = 1920,
.height = 1080,
.fps_ranges = kDefaultFpsRanges,
},
};
}
camera_spec.solid_color_blob =
GetValue<bool>(*spec_value, kSolidColorBlob).value_or(false);
camera_specs.push_back(camera_spec);
}
return camera_specs;
}
} // namespace
std::optional<HalSpec> ParseHalSpecFromJsonValue(
const base::Value::Dict& value) {
HalSpec spec;
DictWithPath root = {&value, {}};
if (auto cameras = GetRequiredValue<ListWithPath>(root, kCamerasKey)) {
spec.cameras = ParseCameraSpecs(*cameras);
}
return spec;
}
} // namespace cros