blob: fc860e458f7bb82b6f9c3f0ed99c907b092bec82 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "camera3_test/camera3_service.h"
#include <unistd.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/command_line.h>
#include <base/strings/string_number_conversions.h>
#include "camera3_test/camera3_perf_log.h"
namespace camera3_test {
namespace {
constexpr int kWaitForStopPreviewTimeoutMs = 3000;
constexpr int kWaitForStopRecordingTimeoutMs = 3000;
double Get3ATimeoutMultiplier() {
static double multiplier = [] {
constexpr char kSwitch[] = "3a_timeout_multiplier";
const auto* cl = base::CommandLine::ForCurrentProcess();
std::string val = cl->GetSwitchValueASCII(kSwitch);
double out;
if (val.empty() || !base::StringToDouble(val, &out)) {
return 1.0;
}
return out;
}();
return multiplier;
}
int GetWaitForFocusDoneTimeoutMs() {
return static_cast<int>(6000 * Get3ATimeoutMultiplier());
}
int GetWaitForAWBConvergedTimeoutMs() {
return static_cast<int>(3000 * Get3ATimeoutMultiplier());
}
} // namespace
Camera3Service::~Camera3Service() {}
int Camera3Service::Initialize(
ProcessStillCaptureResultCallback still_capture_cb,
ProcessRecordingResultCallback recording_cb) {
base::AutoLock l(lock_);
if (initialized_) {
LOG(ERROR) << "Camera service is already initialized";
return -EINVAL;
}
for (const auto& it : cam_ids_) {
cam_dev_service_map_[it].reset(new Camera3Service::Camera3DeviceService(
it, still_capture_cb, recording_cb));
int result = cam_dev_service_map_[it]->Initialize();
if (result != 0) {
LOG(ERROR) << "Camera device " << it << " service initialization fails";
cam_dev_service_map_.clear();
return result;
}
}
initialized_ = true;
return 0;
}
void Camera3Service::Destroy() {
base::AutoLock l(lock_);
if (!initialized_) {
return;
}
for (auto& it : cam_dev_service_map_) {
it.second->Destroy();
}
cam_dev_service_map_.clear();
initialized_ = false;
}
int Camera3Service::StartPreview(int cam_id,
const ResolutionInfo& preview_resolution,
const ResolutionInfo& still_capture_resolution,
const ResolutionInfo& recording_resolution) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->StartPreview(
preview_resolution, still_capture_resolution, recording_resolution);
}
void Camera3Service::StopPreview(int cam_id) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
cam_dev_service_map_[cam_id]->StopPreview();
}
}
void Camera3Service::StartAutoFocus(int cam_id) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
cam_dev_service_map_[cam_id]->StartAutoFocus();
}
}
int Camera3Service::WaitForAutoFocusDone(int cam_id) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->WaitForAutoFocusDone();
}
int Camera3Service::WaitForAWBConvergedAndLock(int cam_id) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->WaitForAWBConvergedAndLock();
}
void Camera3Service::StartAEPrecapture(int cam_id) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
cam_dev_service_map_[cam_id]->StartAEPrecapture();
}
}
int Camera3Service::WaitForAEStable(int cam_id) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->WaitForAEStable();
}
void Camera3Service::TakeStillCapture(int cam_id,
const camera_metadata_t* metadata) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
cam_dev_service_map_[cam_id]->TakeStillCapture(metadata);
}
}
int Camera3Service::StartRecording(int cam_id,
const camera_metadata_t* metadata) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->StartRecording(metadata);
}
void Camera3Service::StopRecording(int cam_id) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
cam_dev_service_map_[cam_id]->StopRecording();
}
}
int Camera3Service::WaitForPreviewFrames(int cam_id,
uint32_t num_frames,
uint32_t timeout_ms) {
base::AutoLock l(lock_);
if (!initialized_ ||
cam_dev_service_map_.find(cam_id) == cam_dev_service_map_.end()) {
return -ENODEV;
}
return cam_dev_service_map_[cam_id]->WaitForPreviewFrames(num_frames,
timeout_ms);
}
const Camera3Device::StaticInfo* Camera3Service::GetStaticInfo(int cam_id) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
return cam_dev_service_map_[cam_id]->GetStaticInfo();
}
return nullptr;
}
const camera_metadata_t* Camera3Service::ConstructDefaultRequestSettings(
int cam_id, int type) {
base::AutoLock l(lock_);
if (initialized_ &&
cam_dev_service_map_.find(cam_id) != cam_dev_service_map_.end()) {
return cam_dev_service_map_[cam_id]->ConstructDefaultRequestSettings(type);
}
return nullptr;
}
int Camera3Service::Camera3DeviceService::Initialize() {
Camera3Module cam_module;
if (cam_device_.Initialize(&cam_module) != 0) {
LOG(ERROR) << "Camera device initialization fails";
return -ENODEV;
}
if (!service_thread_.Start()) {
LOG(ERROR) << "Failed to start thread";
return -EINVAL;
}
cam_device_.RegisterResultMetadataOutputBufferCallback(base::Bind(
&Camera3Service::Camera3DeviceService::ProcessResultMetadataOutputBuffers,
base::Unretained(this)));
repeating_preview_metadata_.reset(clone_camera_metadata(
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW)));
if (!repeating_preview_metadata_) {
LOG(ERROR) << "Failed to create preview metadata";
return -ENOMEM;
}
sem_init(&preview_frame_sem_, 0, 0);
return 0;
}
void Camera3Service::Camera3DeviceService::Destroy() {
service_thread_.Stop();
sem_destroy(&preview_frame_sem_);
cam_device_.Destroy();
}
int Camera3Service::Camera3DeviceService::StartPreview(
const ResolutionInfo& preview_resolution,
const ResolutionInfo& still_capture_resolution,
const ResolutionInfo& recording_resolution) {
VLOGF_ENTER();
int result = -EIO;
service_thread_.PostTaskSync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::StartPreviewOnServiceThread,
base::Unretained(this), preview_resolution, still_capture_resolution,
recording_resolution, &result));
return result;
}
void Camera3Service::Camera3DeviceService::StopPreview() {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
service_thread_.PostTaskAsync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::StopPreviewOnServiceThread,
base::Unretained(this), cros::GetFutureCallback(future)));
if (!future->Wait(kWaitForStopPreviewTimeoutMs)) {
LOG(ERROR) << "Timeout stopping preview";
}
}
void Camera3Service::Camera3DeviceService::StartAutoFocus() {
service_thread_.PostTaskAsync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::StartAutoFocusOnServiceThread,
base::Unretained(this)));
}
int Camera3Service::Camera3DeviceService::WaitForAutoFocusDone() {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
int32_t result;
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
AddMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AF_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED,
ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED}),
cros::GetFutureCallback(future), &result));
if (!future->Wait(GetWaitForFocusDoneTimeoutMs())) {
service_thread_.PostTaskSync(
FROM_HERE,
base::Bind(&Camera3Service::Camera3DeviceService::
DeleteMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AF_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED,
ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED})));
return -ETIMEDOUT;
}
if (result == ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
VLOGF(1) << "Auto-focus did not lock";
}
return 0;
}
int Camera3Service::Camera3DeviceService::WaitForAWBConvergedAndLock() {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
AddMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AWB_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AWB_STATE_CONVERGED}),
cros::GetFutureCallback(future), nullptr));
if (!future->Wait(GetWaitForAWBConvergedTimeoutMs())) {
service_thread_.PostTaskSync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
DeleteMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AWB_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AWB_STATE_CONVERGED})));
return -ETIMEDOUT;
}
if (cam_device_.GetStaticInfo()->IsAWBLockSupported()) {
service_thread_.PostTaskAsync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::LockAWBOnServiceThread,
base::Unretained(this)));
}
return 0;
}
void Camera3Service::Camera3DeviceService::StartAEPrecapture() {
VLOGF_ENTER();
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
StartAEPrecaptureOnServiceThread,
base::Unretained(this)));
}
int Camera3Service::Camera3DeviceService::WaitForAEStable() {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
int32_t result;
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
AddMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AE_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AE_STATE_CONVERGED,
ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED}),
cros::GetFutureCallback(future), &result));
if (!future->Wait(GetWaitForFocusDoneTimeoutMs())) {
service_thread_.PostTaskSync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
DeleteMetadataListenerOnServiceThread,
base::Unretained(this), ANDROID_CONTROL_AE_STATE,
std::unordered_set<int32_t>(
{ANDROID_CONTROL_AE_STATE_CONVERGED,
ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED})));
return -ETIMEDOUT;
}
if (result == ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED) {
VLOGF(1) << "Flash needs to be fired for good quality still capture";
}
return 0;
}
void Camera3Service::Camera3DeviceService::TakeStillCapture(
const camera_metadata_t* metadata) {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
TakeStillCaptureOnServiceThread,
base::Unretained(this), metadata,
cros::GetFutureCallback(future)));
// Wait for ProcessPreviewRequestOnServiceThread() to finish processing
// |metadata|
future->Wait();
}
int Camera3Service::Camera3DeviceService::StartRecording(
const camera_metadata_t* metadata) {
VLOGF_ENTER();
auto future = cros::Future<int>::Create(nullptr);
service_thread_.PostTaskSync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::StartRecordingOnServiceThread,
base::Unretained(this), metadata, cros::GetFutureCallback(future)));
return future->Wait();
}
void Camera3Service::Camera3DeviceService::StopRecording() {
VLOGF_ENTER();
auto future = cros::Future<void>::Create(nullptr);
service_thread_.PostTaskAsync(
FROM_HERE,
base::Bind(
&Camera3Service::Camera3DeviceService::StopRecordingOnServiceThread,
base::Unretained(this), cros::GetFutureCallback(future)));
if (!future->Wait(kWaitForStopRecordingTimeoutMs)) {
LOG(ERROR) << "Timeout stopping preview";
}
}
int Camera3Service::Camera3DeviceService::WaitForPreviewFrames(
uint32_t num_frames, uint32_t timeout_ms) {
VLOGF_ENTER();
while (sem_trywait(&preview_frame_sem_) == 0) {
}
for (uint32_t i = 0; i < num_frames; ++i) {
struct timespec timeout = {};
if (clock_gettime(CLOCK_REALTIME, &timeout)) {
LOGF(ERROR) << "Failed to get clock time";
return -errno;
}
timeout.tv_sec += timeout_ms / 1000;
timeout.tv_nsec += (timeout_ms % 1000) * 1000;
if (sem_timedwait(&preview_frame_sem_, &timeout) != 0) {
return -errno;
}
}
return 0;
}
const Camera3Device::StaticInfo*
Camera3Service::Camera3DeviceService::GetStaticInfo() const {
return cam_device_.GetStaticInfo();
}
const camera_metadata_t*
Camera3Service::Camera3DeviceService::ConstructDefaultRequestSettings(
int type) {
return cam_device_.ConstructDefaultRequestSettings(type);
}
void Camera3Service::Camera3DeviceService::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<ScopedBufferHandle> buffers) {
VLOGF_ENTER();
service_thread_.PostTaskAsync(
FROM_HERE,
base::Bind(&Camera3Service::Camera3DeviceService::
ProcessResultMetadataOutputBuffersOnServiceThread,
base::Unretained(this), frame_number, base::Passed(&metadata),
base::Passed(&buffers)));
}
void Camera3Service::Camera3DeviceService::StartPreviewOnServiceThread(
const ResolutionInfo preview_resolution,
const ResolutionInfo still_capture_resolution,
const ResolutionInfo recording_resolution,
int* result) {
DCHECK(service_thread_.IsCurrentThread());
VLOGF_ENTER();
if (preview_state_ != PREVIEW_STOPPED) {
LOG(ERROR) << "Failed to start preview because it is not stopped";
*result = -EAGAIN;
return;
}
if (still_capture_resolution.Area()) {
cam_device_.AddOutputStream(
HAL_PIXEL_FORMAT_BLOB, still_capture_resolution.Width(),
still_capture_resolution.Height(), CAMERA3_STREAM_ROTATION_0);
}
if (recording_resolution.Area()) {
cam_device_.AddOutputStream(
HAL_PIXEL_FORMAT_YCbCr_420_888, recording_resolution.Width(),
recording_resolution.Height(), CAMERA3_STREAM_ROTATION_0);
}
cam_device_.AddOutputStream(
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, preview_resolution.Width(),
preview_resolution.Height(), CAMERA3_STREAM_ROTATION_0);
if (cam_device_.ConfigureStreams(&streams_) != 0) {
ADD_FAILURE() << "Configuring stream fails";
*result = -EINVAL;
return;
}
auto GetStream = [this](int format) {
auto it = std::find_if(streams_.begin(), streams_.end(),
[&](const camera3_stream_t* stream) {
return (stream->format == format);
});
return (it == streams_.end()) ? nullptr : *it;
};
const camera3_stream_t* preview_stream =
GetStream(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
const camera3_stream_t* record_stream =
GetStream(HAL_PIXEL_FORMAT_YCbCr_420_888);
if (!preview_stream || (recording_resolution.Area() && !record_stream)) {
ADD_FAILURE() << "Failed to find configured stream";
*result = -EINVAL;
return;
}
number_of_capture_requests_ = preview_stream->max_buffers;
if (recording_resolution.Area()) {
number_of_capture_requests_ =
std::min(number_of_capture_requests_, record_stream->max_buffers);
}
capture_requests_.resize(number_of_capture_requests_);
output_stream_buffers_ = std::vector<std::vector<camera3_stream_buffer_t>>(
number_of_capture_requests_,
std::vector<camera3_stream_buffer_t>(kNumberOfOutputStreamBuffers));
// Submit initial preview capture requests to fill the HAL pipeline first.
// Then when a result callback is processed, the corresponding capture
// request (and output buffer) is recycled and submitted again.
for (uint32_t i = 0; i < number_of_capture_requests_; i++) {
std::vector<const camera3_stream_t*> streams(1, preview_stream);
std::vector<camera3_stream_buffer_t> output_buffers;
if (cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers) !=
0) {
ADD_FAILURE() << "Failed to allocate output buffer";
*result = -EINVAL;
return;
}
output_stream_buffers_[i][kPreviewOutputStreamIdx] = output_buffers.front();
capture_requests_[i] = std::make_pair(
camera3_capture_request_t{
.frame_number =
UINT32_MAX, // Will be overwritten with correct value
.settings = repeating_preview_metadata_.get(),
.input_buffer = NULL,
.num_output_buffers = 1,
.output_buffers = output_stream_buffers_[i].data()},
false);
ProcessPreviewRequestOnServiceThread();
}
preview_state_ = PREVIEW_STARTING;
*result = 0;
}
void Camera3Service::Camera3DeviceService::StopPreviewOnServiceThread(
base::Callback<void()> cb) {
DCHECK(service_thread_.IsCurrentThread());
VLOGF_ENTER();
if (preview_state_ != PREVIEW_STARTED && preview_state_ != PREVIEW_STARTING) {
return;
}
preview_state_ = PREVIEW_STOPPING;
stop_preview_cb_ = cb;
}
void Camera3Service::Camera3DeviceService::StartAutoFocusOnServiceThread() {
DCHECK(service_thread_.IsCurrentThread());
uint8_t af_mode = ANDROID_CONTROL_AF_MODE_AUTO;
EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AF_MODE, &af_mode, 1,
&repeating_preview_metadata_));
if (!oneshot_preview_metadata_.get()) {
oneshot_preview_metadata_.reset(
clone_camera_metadata(repeating_preview_metadata_.get()));
}
uint8_t af_trigger = ANDROID_CONTROL_AF_TRIGGER_START;
EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AF_TRIGGER, &af_trigger, 1,
&oneshot_preview_metadata_));
}
void Camera3Service::Camera3DeviceService::AddMetadataListenerOnServiceThread(
int32_t key,
std::unordered_set<int32_t> values,
base::Callback<void()> cb,
int32_t* result) {
DCHECK(service_thread_.IsCurrentThread());
metadata_listener_list_.emplace_back(key, values, cb, result);
}
void Camera3Service::Camera3DeviceService::
DeleteMetadataListenerOnServiceThread(int32_t key,
std::unordered_set<int32_t> values) {
DCHECK(service_thread_.IsCurrentThread());
for (auto it = metadata_listener_list_.begin();
it != metadata_listener_list_.end(); it++) {
if (it->key == key && it->values == values) {
it = metadata_listener_list_.erase(it);
break;
}
}
}
void Camera3Service::Camera3DeviceService::LockAWBOnServiceThread() {
DCHECK(service_thread_.IsCurrentThread());
uint8_t awb_lock = ANDROID_CONTROL_AWB_LOCK_ON;
EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AWB_LOCK, &awb_lock, 1,
&repeating_preview_metadata_));
}
void Camera3Service::Camera3DeviceService::StartAEPrecaptureOnServiceThread() {
DCHECK(service_thread_.IsCurrentThread());
if (!oneshot_preview_metadata_.get()) {
oneshot_preview_metadata_.reset(
clone_camera_metadata(repeating_preview_metadata_.get()));
}
uint8_t ae_trigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START;
EXPECT_EQ(0, UpdateMetadata(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
&ae_trigger, 1, &oneshot_preview_metadata_));
}
void Camera3Service::Camera3DeviceService::TakeStillCaptureOnServiceThread(
const camera_metadata_t* metadata, base::Callback<void()> cb) {
DCHECK(service_thread_.IsCurrentThread());
still_capture_metadata_ = metadata;
still_capture_cb_ = cb;
}
void Camera3Service::Camera3DeviceService::StartRecordingOnServiceThread(
const camera_metadata_t* metadata, base::Callback<void(int)> cb) {
DCHECK(service_thread_.IsCurrentThread());
VLOGF_ENTER();
if (!metadata) {
LOG(ERROR) << "Invalid metadata settings";
cb.Run(-EINVAL);
return;
}
if (preview_state_ != PREVIEW_STARTED) {
VLOGF(2) << "Preview is not started yet. Retrying...";
usleep(500);
service_thread_.PostTaskAsync(
FROM_HERE, base::Bind(&Camera3Service::Camera3DeviceService::
StartRecordingOnServiceThread,
base::Unretained(this), metadata, cb));
return;
}
auto it = std::find_if(
streams_.begin(), streams_.end(), [](const camera3_stream_t* stream) {
return (stream->format == HAL_PIXEL_FORMAT_YCbCr_420_888);
});
if (it == streams_.end()) {
ADD_FAILURE() << "Failed to find configured recording stream";
cb.Run(-EINVAL);
return;
}
const camera3_stream_t* record_stream = *it;
for (uint32_t i = 0; i < number_of_capture_requests_; i++) {
std::vector<const camera3_stream_t*> streams(1, record_stream);
std::vector<camera3_stream_buffer_t> output_buffers;
if (cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers) !=
0) {
ADD_FAILURE() << "Failed to allocate output buffer";
cb.Run(-EINVAL);
return;
}
output_stream_buffers_[i][kRecordingOutputStreamIdx] =
output_buffers.front();
}
recording_metadata_ = metadata;
cb.Run(0);
}
void Camera3Service::Camera3DeviceService::StopRecordingOnServiceThread(
base::Callback<void()> cb) {
VLOGF_ENTER();
recording_metadata_ = nullptr;
stop_recording_cb_ = cb;
}
void Camera3Service::Camera3DeviceService::
ProcessPreviewRequestOnServiceThread() {
DCHECK(service_thread_.IsCurrentThread());
size_t capture_request_idx = 0;
for (; capture_request_idx < capture_requests_.size();
capture_request_idx++) {
if (!capture_requests_[capture_request_idx].second) {
break;
}
}
ASSERT_NE(capture_request_idx, capture_requests_.size())
<< "Out of captures requests";
capture_requests_[capture_request_idx].second = true;
camera3_capture_request_t* request =
&capture_requests_[capture_request_idx].first;
// Initially there is preview stream only
request->num_output_buffers = 1;
if (recording_metadata_) {
request->settings = recording_metadata_;
++request->num_output_buffers;
}
if (still_capture_metadata_) {
auto it = std::find_if(streams_.begin(), streams_.end(),
[](const camera3_stream_t* stream) {
return (stream->format == HAL_PIXEL_FORMAT_BLOB);
});
ASSERT_NE(streams_.end(), it)
<< "Failed to find configured still capture stream";
const camera3_stream_t* still_capture_stream = *it;
std::vector<const camera3_stream_t*> streams(1, still_capture_stream);
std::vector<camera3_stream_buffer_t> output_buffers;
ASSERT_EQ(
0, cam_device_.AllocateOutputBuffersByStreams(streams, &output_buffers))
<< "Failed to allocate output buffer";
request->settings = still_capture_metadata_;
output_stream_buffers_[capture_request_idx][request->num_output_buffers] =
output_buffers.front();
++request->num_output_buffers;
}
if (!recording_metadata_ && !still_capture_metadata_) {
// Request with one-shot metadata if there is one
request->settings = oneshot_preview_metadata_.get()
? oneshot_preview_metadata_.get()
: repeating_preview_metadata_.get();
}
ASSERT_EQ(0, cam_device_.ProcessCaptureRequest(request))
<< "Failed to process capture request";
++number_of_in_flight_requests_;
VLOGF(1) << "Capture request";
VLOGF(1) << " Frame " << request->frame_number;
VLOGF(1) << " Index " << capture_request_idx;
for (size_t i = 0; i < request->num_output_buffers; i++) {
VLOGF(1) << " Buffer " << *request->output_buffers[i].buffer
<< " (format:" << request->output_buffers[i].stream->format << ","
<< request->output_buffers[i].stream->width << "x"
<< request->output_buffers[i].stream->height << ")";
}
VLOGF(1) << " Settings " << request->settings;
if (still_capture_metadata_) {
still_capture_metadata_ = nullptr;
still_capture_cb_.Run();
} else if (!recording_metadata_ && !still_capture_metadata_ &&
oneshot_preview_metadata_.get()) {
oneshot_preview_metadata_.reset(nullptr);
}
}
void Camera3Service::Camera3DeviceService::
ProcessResultMetadataOutputBuffersOnServiceThread(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<ScopedBufferHandle> buffers) {
DCHECK(service_thread_.IsCurrentThread());
--number_of_in_flight_requests_;
size_t capture_request_idx = 0;
for (; capture_request_idx < capture_requests_.size();
capture_request_idx++) {
if (capture_requests_[capture_request_idx].first.frame_number ==
frame_number) {
break;
}
}
ASSERT_NE(capture_request_idx, capture_requests_.size())
<< "Failed to find frame " << frame_number << " in the requests";
VLOGF(1) << "Capture result";
VLOGF(1) << " Frame " << frame_number;
VLOGF(1) << " Index " << capture_request_idx;
// Process result metadata according to listeners
for (auto it = metadata_listener_list_.begin();
it != metadata_listener_list_.end();) {
camera_metadata_ro_entry_t entry;
if (find_camera_metadata_ro_entry(metadata.get(), it->key, &entry) == 0 &&
it->values.find(entry.data.i32[0]) != it->values.end()) {
VLOGF(1) << "Metadata listener gets tag "
<< get_camera_metadata_tag_name(it->key) << " value "
<< entry.data.i32[0];
if (it->result) {
*it->result = entry.data.i32[0];
}
it->cb.Run();
it = metadata_listener_list_.erase(it);
} else {
it++;
}
}
// Process output buffers and record perf logs. We record preview and video
// perf logs only if there's no still capture in this result since it is
// expected to take longer time.
const bool has_still_capture = std::any_of(
buffers.begin(), buffers.end(), [](const ScopedBufferHandle& buffer) {
return Camera3TestGralloc::GetFormat(*buffer) == HAL_PIXEL_FORMAT_BLOB;
});
const bool stopping_preview =
(preview_state_ == PREVIEW_STOPPING) && !still_capture_metadata_;
bool have_yuv_buffer = false;
for (auto& it : buffers) {
VLOGF(1) << " Buffer " << *it
<< " (format:" << Camera3TestGralloc::GetFormat(*it) << ")";
switch (Camera3TestGralloc::GetFormat(*it)) {
case HAL_PIXEL_FORMAT_BLOB:
Camera3PerfLog::GetInstance()->UpdateFrameEvent(
cam_id_, frame_number, FrameEvent::STILL_CAPTURE_RESULT,
base::TimeTicks::Now());
if (!process_still_capture_result_cb_.is_null()) {
process_still_capture_result_cb_.Run(
cam_id_, frame_number, std::move(metadata), std::move(it));
}
break;
case HAL_PIXEL_FORMAT_YCbCr_420_888:
have_yuv_buffer = true;
if (!has_still_capture) {
Camera3PerfLog::GetInstance()->UpdateFrameEvent(
cam_id_, frame_number, FrameEvent::VIDEO_RECORD_RESULT,
base::TimeTicks::Now());
}
if (!process_recording_result_cb_.is_null()) {
process_recording_result_cb_.Run(cam_id_, frame_number,
std::move(metadata));
}
if (recording_metadata_ && !stopping_preview) {
// Register buffer back to be used by future requests
cam_device_.RegisterOutputBuffer(
*output_stream_buffers_[capture_request_idx]
[kRecordingOutputStreamIdx]
.stream,
std::move(it));
}
break;
default:
if (!has_still_capture) {
Camera3PerfLog::GetInstance()->UpdateFrameEvent(
cam_id_, frame_number, FrameEvent::PREVIEW_RESULT,
base::TimeTicks::Now());
}
if (!stopping_preview) {
// Register buffer back to be used by future requests
cam_device_.RegisterOutputBuffer(
*output_stream_buffers_[capture_request_idx]
[kPreviewOutputStreamIdx]
.stream,
std::move(it));
}
}
}
if (preview_state_ == PREVIEW_STARTING) {
preview_state_ = PREVIEW_STARTED;
Camera3PerfLog::GetInstance()->UpdateDeviceEvent(
cam_id_, DeviceEvent::PREVIEW_STARTED, base::TimeTicks::Now());
}
sem_post(&preview_frame_sem_);
if (!stop_recording_cb_.is_null() && !have_yuv_buffer) {
stop_recording_cb_.Run();
stop_recording_cb_.Reset();
}
if (stopping_preview) {
VLOGF(1) << "Stopping preview ... (" << number_of_in_flight_requests_
<< " requests in flight";
if (number_of_in_flight_requests_ == 0) {
preview_state_ = PREVIEW_STOPPED;
stop_preview_cb_.Run();
stop_preview_cb_.Reset();
}
return;
}
capture_requests_[capture_request_idx].second = false;
ProcessPreviewRequestOnServiceThread();
}
} // namespace camera3_test