blob: dbce51d631cf8170a23e71cab63d5f808015c237 [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 "faced/camera/fake_camera_client.h"
#include <memory>
#include <utility>
#include <vector>
#include <absl/status/status.h>
#include <base/functional/bind.h>
#include <base/functional/callback_forward.h>
#include <cros-camera/camera_service_connector.h>
#include <linux/videodev2.h>
#include "faced/camera/camera_client.h"
#include "faced/camera/frame.h"
#include "faced/util/blocking_future.h"
#include "faced/util/task.h"
namespace faced::testing {
FakeCameraClient::FakeCameraClient() {
// Set up a simple 1920x1080 YUV camera.
device_ = CameraClient::DeviceInfo{
.id = 0,
.name = "Fake Camera",
.formats = {cros_cam_format_info_t{
.fourcc = V4L2_PIX_FMT_NV12,
.width = 1920,
.height = 1080,
.fps = 30,
}},
};
}
FakeCameraClient::FakeCameraClient(DeviceInfo device) : device_(device) {}
CameraClient::CaptureFramesConfig FakeCameraClient::DefaultConfig() const {
CHECK(!device_.formats.empty()) << "Fake device has no supported formats.";
return CaptureFramesConfig{
.camera_id = device_.id,
.format = device_.formats[0],
};
}
bool FakeCameraClient::Capturing() const {
return frame_processor_ != nullptr;
}
void FakeCameraClient::WriteFrame(
Frame frame, base::OnceCallback<void(absl::Status)> frame_done) {
// Ensure a capture is in progress.
if (frame_processor_ == nullptr) {
PostToCurrentSequence(base::BindOnce(
std::move(frame_done),
absl::FailedPreconditionError("No capture in process.")));
return;
}
// Drop the frame if one is already in flight.
if (frame_in_flight_) {
PostToCurrentSequence(base::BindOnce(
std::move(frame_done),
absl::UnavailableError("Frame already being processed.")));
return;
}
// Set up clean up required after the frame has been processed.
frame_in_flight_ = true;
FrameProcessor::ProcessFrameDoneCallback done =
base::BindOnce(&FakeCameraClient::WriteFrameComplete,
base::Unretained(this), std::move(frame_done));
// Process the frame.
PostToCurrentSequence(base::BindOnce(
&FrameProcessor::ProcessFrame, frame_processor_,
std::make_unique<Frame>(std::move(frame)), std::move(done)));
}
absl::Status FakeCameraClient::WriteFrameAndWait(Frame frame) {
BlockingFuture<absl::Status> future;
WriteFrame(std::move(frame), future.PromiseCallback());
return future.Wait();
}
void FakeCameraClient::WriteFrameComplete(
base::OnceCallback<void(absl::Status)> done,
std::optional<absl::Status> status) {
CHECK(frame_in_flight_)
<< "FakeCameraClient::WriteFrameComplete callback called when "
"no frame was in flight.";
frame_in_flight_ = false;
// Notify the caller of `WriteFrame` the frame has been processed.
PostToCurrentSequence(base::BindOnce(std::move(done), absl::OkStatus()));
// If an error was given by the process frame callback, stop the camera, and
// notify the caller.
if (status.has_value()) {
frame_processor_.reset();
PostToCurrentSequence(
base::BindOnce(std::move(capture_complete_), *status));
}
}
void FakeCameraClient::CaptureFrames(
const CaptureFramesConfig& config,
const scoped_refptr<FrameProcessor>& frame_processor,
StopCaptureCallback capture_complete) {
capture_complete_ = std::move(capture_complete);
frame_processor_ = frame_processor;
}
absl::StatusOr<std::vector<CameraClient::DeviceInfo>>
FakeCameraClient::GetDevices() {
return std::vector<CameraClient::DeviceInfo>({device_});
}
absl::StatusOr<CameraClient::DeviceInfo> FakeCameraClient::GetDevice(int id) {
if (id != 0) {
return absl::NotFoundError("No such device.");
}
return device_;
}
} // namespace faced::testing