blob: e7f41f8e9834855973f359435a04a4b00fcffff8 [file] [log] [blame]
// 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/cros_camera_manager.h"
#include <utility>
#include <absl/status/status.h>
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "faced/camera/camera_client.h"
#include "faced/camera/camera_manager.h"
#include "faced/util/queueing_stream.h"
#include "faced/util/task.h"
namespace faced {
namespace {
// Queue at most two frames.
//
// We need at least two frames for QueueingStream's "expedite" feature to work,
// where it detects a backlog of work and requests new frames to be expedited.
// However, a large queue introduces lag: that is, the visible UI represents the
// state of the camera _N_ frames ago.
constexpr int kMaxQueuedFrames = 2;
// A `FrameProcessor` that enqueues frames to a QueueingStream.
class StreamingFrameProcessor : public faced::FrameProcessor {
public:
explicit StreamingFrameProcessor(
QueueingStream<absl::StatusOr<std::unique_ptr<Frame>>>& stream)
: stream_(stream) {}
// Process a frame from the camera.
void ProcessFrame(std::unique_ptr<Frame> frame,
ProcessFrameDoneCallback done) override {
// Attempt to write the frame to the stream. If the stream has closed, stop
// the camera.
std::optional<absl::Status> result;
if (!stream_.Write(std::move(frame))) {
result = absl::CancelledError();
}
PostToCurrentSequence(base::BindOnce(std::move(done), result));
}
private:
~StreamingFrameProcessor() override = default;
QueueingStream<absl::StatusOr<std::unique_ptr<Frame>>> stream_;
};
} // namespace
CrosCameraManager::CrosCameraManager(
CameraClient& client, const CameraClient::CaptureFramesConfig& config)
: camera_(client), config_(config) {}
CrosCameraManager::~CrosCameraManager() {
CHECK(!stream_.has_value())
<< "CrosCameraManager destroyed while still active.";
}
absl::StatusOr<std::unique_ptr<CameraStreamReader>> CrosCameraManager::Open() {
// If a reader already exists, abort with an error.
if (stream_.has_value()) {
return absl::AlreadyExistsError("Camera already open.");
}
// Create a queue for the camera frames.
stream_.emplace(/*max_queue_size=*/kMaxQueuedFrames);
// Start streaming frames.
auto processor = base::MakeRefCounted<StreamingFrameProcessor>(*stream_);
camera_.CaptureFrames(config_, std::move(processor),
base::BindOnce(&CrosCameraManager::OnCameraClosed,
base::Unretained(this)));
return stream_->GetReader();
}
void CrosCameraManager::Close(base::OnceClosure close_complete) {
CHECK(close_complete_.is_null()) << "Close already in progress.";
// If no session is open, simply call the callback.
if (!stream_.has_value()) {
PostToCurrentSequence(std::move(close_complete));
return;
}
// Close the stream.
//
// Next time a frame arrives, the camera client should detect that the stream
// is closed, clean up, and trigger the callback.
//
// TODO(b/254429209): It would be preferable if we could just stop the camera
// directly here, and not need to perform the async wait.
close_complete_ = std::move(close_complete);
stream_->Write(absl::CancelledError());
stream_->Close();
}
void CrosCameraManager::OnCameraClosed(absl::Status final_status) {
DCHECK(stream_.has_value());
// The camera may have closed because of an error, because the reader
// closed the stream, or because Close() was called on us.
//
// In any case, we must:
//
// * Write out an error to the stream, if it is still open.
// * Close the stream.
// * Complete any pending close callback.
// Write out the error to the stream, and close it.
//
// It may already be closed: that's fine. We assume that an error was
// already written out.
stream_->Write(final_status);
stream_->Close();
stream_.reset();
// Notify any potential waiters that the close is finished.
//
// If this cleanup was not triggered by a Close(), there may not be
// any one waiting. That's fine: any future `Close()` will just be
// a no-op.
if (!close_complete_.is_null()) {
PostToCurrentSequence(std::move(close_complete_));
}
}
} // namespace faced