blob: c93b045398e4c286ced2237fab5a1fdc24731b4f [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 "hal/fake/camera_client.h"
#include <utility>
#include <vector>
#include <base/containers/contains.h>
#include <linux/videodev2.h>
#include <sync/sync.h>
#include "cros-camera/common.h"
#include "cros-camera/future.h"
#include "hal/fake/camera_hal.h"
#include "hal/fake/camera_hal_device_ops.h"
#include "hal/fake/metadata_handler.h"
namespace cros {
CameraClient::CameraClient(int id,
const android::CameraMetadata& static_metadata,
const android::CameraMetadata& request_template,
const hw_module_t* module,
hw_device_t** hw_device,
const CameraSpec& spec)
: id_(id),
// This clones the metadata.
static_metadata_(static_metadata),
request_template_(request_template),
request_thread_("FakeRequestThread"),
spec_(spec),
metadata_handler_(request_template_, spec_) {
camera3_device_ = {
.common =
{
.tag = HARDWARE_DEVICE_TAG,
.version = CAMERA_DEVICE_API_VERSION_3_5,
.module = const_cast<hw_module_t*>(module),
.close = cros::camera_device_close,
},
.ops = &g_camera_device_ops,
.priv = this,
};
*hw_device = &camera3_device_.common;
DETACH_FROM_SEQUENCE(ops_sequence_checker_);
}
CameraClient::~CameraClient() {
VLOGFID(1, id_);
}
int CameraClient::OpenDevice() {
VLOGFID(1, id_);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return 0;
}
int CameraClient::CloseDevice() {
VLOGFID(1, id_);
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
StreamOff();
return 0;
}
int CameraClient::Initialize(const camera3_callback_ops_t* callback_ops) {
VLOGFID(1, id_);
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
callback_ops_ = callback_ops;
return 0;
}
int CameraClient::ConfigureStreams(
camera3_stream_configuration_t* stream_config) {
VLOGFID(1, id_);
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
std::vector<camera3_stream_t*> streams(
stream_config->streams,
stream_config->streams + stream_config->num_streams);
for (auto stream : streams) {
if (stream->stream_type != CAMERA3_STREAM_OUTPUT) {
LOGFID(ERROR, id_) << "Non-output stream is not supported";
return -EINVAL;
}
// TODO(pihsun): Implement rotation.
if (stream->rotation != CAMERA3_STREAM_ROTATION_0) {
LOGFID(ERROR, id_) << "Rotations are not supported yet";
return -EINVAL;
}
if (!base::Contains(kSupportedHalFormats, stream->format)) {
LOGFID(ERROR, id_) << "Stream format " << stream->format
<< " is not supported";
return -EINVAL;
}
}
bool ret = StreamOn(streams);
if (!ret) {
return -ENODEV;
}
for (auto stream : streams) {
// TODO(pihsun): We likely need more than one max buffer here.
stream->max_buffers = 1;
stream->usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
}
return 0;
}
bool CameraClient::StreamOn(const std::vector<camera3_stream_t*>& streams) {
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
if (request_handler_ == nullptr) {
if (!request_thread_.Start()) {
LOGFID(ERROR, id_) << "Request thread failed to start";
return false;
}
request_task_runner_ = request_thread_.task_runner();
request_handler_ = std::make_unique<RequestHandler>(
id_, callback_ops_, static_metadata_, request_task_runner_, spec_);
}
auto future = cros::Future<bool>::Create(nullptr);
request_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RequestHandler::StreamOn,
base::Unretained(request_handler_.get()),
std::ref(streams), cros::GetFutureCallback(future)));
return future->Get();
}
void CameraClient::StreamOff() {
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
if (request_handler_) {
auto future = cros::Future<bool>::Create(nullptr);
request_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&RequestHandler::StreamOff,
base::Unretained(request_handler_.get()),
cros::GetFutureCallback(future)));
bool ret = future->Get();
if (!ret) {
LOGFID(ERROR, id_) << "StreamOff failed";
}
request_thread_.Stop();
request_handler_.reset();
}
}
const camera_metadata_t* CameraClient::ConstructDefaultRequestSettings(
int type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
return metadata_handler_.GetDefaultRequestSettings(type);
}
int CameraClient::ProcessCaptureRequest(camera3_capture_request_t* request) {
VLOGFID(1, id_);
DCHECK_CALLED_ON_VALID_SEQUENCE(ops_sequence_checker_);
DCHECK(request_handler_ != nullptr);
if (request == nullptr) {
LOGFID(ERROR, id_) << "NULL request recieved";
return -EINVAL;
}
VLOGFID(1, id_) << "Request Frame:" << request->frame_number;
if (request->input_buffer != nullptr) {
LOGFID(ERROR, id_) << "Input buffer is not supported";
return -EINVAL;
}
if (request->num_output_buffers == 0) {
LOGFID(ERROR, id_) << "Invalid number of output buffers: "
<< request->num_output_buffers;
return -EINVAL;
}
if (request->settings) {
latest_request_metadata_ = request->settings;
if (VLOG_IS_ON(2)) {
dump_camera_metadata(request->settings, 1, 1);
}
}
// TODO(pihsun): Check the requested stream format are supported and return
// error early if not.
// We cannot use |request| after this function returns. So we have to copy
// necessary information out to |capture_request|.
auto capture_request =
std::make_unique<CaptureRequest>(*request, latest_request_metadata_);
request_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&RequestHandler::HandleRequest,
base::Unretained(request_handler_.get()),
std::move(capture_request)));
return 0;
}
void CameraClient::Dump(int fd) {
VLOGFID(1, id_);
}
int CameraClient::Flush(const camera3_device_t* dev) {
VLOGFID(1, id_);
if (request_handler_ == nullptr) {
return 0;
}
base::WaitableEvent flushed;
request_handler_->HandleFlush(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&flushed)));
flushed.Wait();
return 0;
}
} // namespace cros