blob: b3a9c4e316ac61c62494a566a705c38ce9975765 [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_service.h"
#include <vector>
#include <base/logging.h>
#include <base/task/thread_pool.h>
#include <base/time/time.h>
namespace faced::testing {
namespace {
// Time between frames being sent.
//
// We schedule the frames to be sent at 30 FPS (i.e., 33ms), though we expect
// tests to be using a mock clock in practice.
constexpr base::TimeDelta kFrameInterval = base::Milliseconds(33);
} // namespace
FakeCameraService::FakeCameraService()
: ops_runner_(
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {}
FakeCameraService::~FakeCameraService() {
base::AutoLock lock(mutex_);
CHECK_NE(state_, State::kCapturing)
<< "Attempted to destroy FakeCameraService while a capture was in "
"progress.";
}
void FakeCameraService::AddCameraInfo(cros_cam_info_t cam_info,
bool is_removed) {
base::AutoLock lock(mutex_);
camera_infos_.push_back(cam_info);
camera_is_removed_.push_back(is_removed);
}
void FakeCameraService::AddResult(cros_cam_capture_result_t result) {
base::AutoLock lock(mutex_);
results_.push_back(result);
}
int FakeCameraService::Init() {
base::AutoLock lock(mutex_);
CHECK_EQ(state_, State::kCreated)
<< "Attempted to call Init() in incorrect state.";
state_ = State::kIdle;
return 0;
}
int FakeCameraService::Exit() {
base::AutoLock lock(mutex_);
CHECK_EQ(state_, State::kIdle)
<< "Attempted to call Exit() in incorrect state.";
state_ = State::kExit;
return 0;
}
int FakeCameraService::GetCameraInfo(cros_cam_get_cam_info_cb_t callback,
void* context) {
// Make a copy of our camera information data structures, so we can issue
// callbacks below without holding a lock.
std::vector<cros_cam_info_t> camera_infos;
std::vector<bool> camera_is_removed;
{
base::AutoLock lock(mutex_);
camera_infos = camera_infos_;
camera_is_removed = camera_is_removed_;
}
// Call the user's callback once for each camera info.
for (int i = 0; i < camera_infos.size(); i++) {
if ((*callback)(context, &camera_infos[i], camera_is_removed[i]) != 0) {
return 1;
}
}
return 0;
}
int FakeCameraService::StartCapture(const cros_cam_capture_request_t* request,
cros_cam_capture_cb_t callback,
void* context) {
base::AutoLock lock(mutex_);
// Update current state.
CHECK_EQ(state_, State::kIdle)
<< "Called StartCapture() when the camera service was not in an idle "
"state.";
state_ = State::kCapturing;
camera_id_ = request->id;
// Schedule the first frame.
ops_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeCameraService::DispatchNextFrame,
base::Unretained(this), request, callback, context),
kFrameInterval);
return 0;
}
int FakeCameraService::StopCapture(int id) {
base::AutoLock lock(mutex_);
CHECK_EQ(state_, State::kCapturing)
<< "Attempted to call StopCapture when no capture was in progress.";
CHECK_EQ(id, camera_id_)
<< "Called StopCapture() with the incorrect camera ID.";
// The camera service API guarantees that when StopCapture() returns,
// no new callsback will be issued.
state_ = State::kIdle;
return 0;
}
void FakeCameraService::DispatchNextFrame(
const cros_cam_capture_request_t* request,
cros_cam_capture_cb_t callback,
void* context) {
CHECK(ops_runner_->RunsTasksInCurrentSequence());
base::AutoLock lock(mutex_);
// If StopCapture() has been called, don't dispatch any more frames.
if (state_ != State::kCapturing) {
return;
}
// If we have run out of frames, just stall the stream.
if (results_.empty()) {
return;
}
// Fetch the next frame, and send it to the user's callback.
//
// If a non-zero value is returned, we stop the current capture.
cros_cam_capture_result_t result = results_.front();
results_.pop_front();
bool should_continue = ((*callback)(context, &result) == 0);
if (!should_continue) {
state_ = State::kIdle;
return;
}
// Schedule the next frame.
ops_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeCameraService::DispatchNextFrame,
base::Unretained(this), request, callback, context),
kFrameInterval);
}
} // namespace faced::testing