blob: 6d5a3f804bacacc8fd9a7ae04d668e896e768a5b [file] [log] [blame]
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include <string>
#include <base/at_exit.h>
#include <base/bind.h>
#include <base/logging.h>
#include <binder/IServiceManager.h>
#include <brillo/binder_watcher.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/syslog_logging.h>
#include <camera/CameraMetadata.h>
#include <camera/ICameraService.h>
#include <camera/camera2/CaptureRequest.h>
#include <camera/camera2/ICameraDeviceCallbacks.h>
#include <camera/camera2/ICameraDeviceUser.h>
#include <camera/camera2/OutputConfiguration.h>
#include <gui/BufferQueue.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
#include <gui/CpuConsumer.h>
#include <gui/Surface.h>
#include <hardware/camera3.h>
#include <system/camera_metadata.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/StrongPointer.h>
using android::BnCameraDeviceCallbacks;
using android::BufferQueue;
using android::CameraMetadata;
using android::CaptureRequest;
using android::CaptureResultExtras;
using android::CpuConsumer;
using android::ICameraDeviceCallbacks;
using android::ICameraDeviceUser;
using android::ICameraService;
using android::IGraphicBufferConsumer;
using android::IGraphicBufferProducer;
using android::OK;
using android::String16;
using android::Surface;
using android::sp;
namespace {
// TODO(wiley) This constant should probably come out that client library, but
// where is not obvious.
const char kCameraServiceName[] = "media.camera";
// TODO(wiley) This happens to be a format supported by both gralloc.default and
// the golfish camera HAL, but I have no idea how to get this into
// an actual viewable image.
const int kHalPixelFormat = HAL_PIXEL_FORMAT_RAW16;
class CameraDeviceCallbacks : public BnCameraDeviceCallbacks {
public:
explicit CameraDeviceCallbacks(brillo::MessageLoop* loop) : loop_(loop) {}
void onDeviceError(ICameraDeviceCallbacks::CameraErrorCode error_code,
const CaptureResultExtras& /* result_extras */) override {
LOG(ERROR) << "Received camera error = " << error_code;
loop_->BreakLoop();
}
void onDeviceIdle() override {
LOG(INFO) << "Camera device is idle";
}
void onCaptureStarted(
const CaptureResultExtras& /* result_extras */,
int64_t /* timestamp */) override {
LOG(INFO) << "Capture started.";
}
void onResultReceived(
const CameraMetadata& /* metadata */,
const CaptureResultExtras& /* result_extras */) override {
LOG(INFO) << "Capture result received";
result_received_ = true;
loop_->BreakLoop();
}
void onPrepared(int stream_id) override {
LOG(INFO) << "Camera is prepared for stream " << stream_id;
}
bool result_received() { return result_received_; }
private:
brillo::MessageLoop* loop_ = nullptr;
bool result_received_ = false;
};
} // namespace
int main() {
base::AtExitManager at_exit_manager;
brillo::InitLog(brillo::kLogToStderr);
// Create a message loop.
brillo::BaseMessageLoop message_loop;
// Initialize a binder watcher.
brillo::BinderWatcher watcher(&message_loop);
watcher.Init();
LOG(INFO) << "Retrieving a binder for the camera service.";
sp<ICameraService> camera_service;
android::status_t status = android::getService(
String16(kCameraServiceName), &camera_service);
CHECK(status == OK)
<< "Failed to get ICameraService binder from service manager!";
LOG(INFO) << "Asking how many cameras we have.";
const int32_t num_cameras = camera_service->getNumberOfCameras(
ICameraService::CAMERA_TYPE_ALL);
CHECK(num_cameras > 0)
<< "ICameraService reports no cameras available";
const int camera_id = 0;
LOG(INFO) << "Connecting to camera " << camera_id;
sp<CameraDeviceCallbacks> camera_cb =
new CameraDeviceCallbacks(&message_loop);
sp<ICameraDeviceCallbacks> camera_cb_alias = camera_cb;
const String16 client_package_name("brillo");
sp<ICameraDeviceUser> camera_device_user;
status = camera_service->connectDevice(
camera_cb_alias, camera_id, client_package_name,
ICameraService::USE_CALLING_UID,
camera_device_user);
CHECK(status == OK) << "Failed to connect to camera id=" << camera_id
<< " error=" << status;
LOG(INFO) << "Obtaining camera info";
CameraMetadata camera_metadata;
status = camera_device_user->getCameraInfo(&camera_metadata);
CHECK(status == OK) << "Failed to get camera info, error=" << status;
LOG(INFO) << "Calculating smallest stream configuration";
camera_metadata_entry streamConfigs =
camera_metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
int32_t width = std::numeric_limits<int32_t>::max();
int32_t height = 1;
for (size_t i = 0; i < streamConfigs.count; i += 4) {
int32_t fmt = streamConfigs.data.i32[i];
int32_t inout = streamConfigs.data.i32[i + 3];
if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_RAW16 &&
inout == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
int32_t w = streamConfigs.data.i32[i + 1];
int32_t h = streamConfigs.data.i32[i + 2];
if (width * height > w * h) {
width = w;
height = h;
}
}
}
LOG(INFO) << "width = " << width << " height = " << height;
CHECK(width != std::numeric_limits<int32_t>::max())
<< "Unable to configure stream dimensions";
LOG(INFO) << "Obtaining buffer queue";
sp<IGraphicBufferProducer> buffer_producer = nullptr;
sp<IGraphicBufferConsumer> buffer_consumer = nullptr;
BufferQueue::createBufferQueue(&buffer_producer, &buffer_consumer);
CHECK(buffer_producer.get() != nullptr && buffer_consumer.get() != nullptr);
LOG(INFO) << "Creating buffer consumer";
sp<CpuConsumer> consumer = new CpuConsumer(
buffer_consumer, 1 /* one frame only */, true /* controlled by app */);
consumer->setDefaultBufferSize(width, height);
consumer->setDefaultBufferFormat(kHalPixelFormat);
LOG(INFO) << "Configuring the camera";
status = camera_device_user->beginConfigure();
CHECK(status == OK) << "Failed calling ICameraDeviceUser::beginConfigure()"
<< " error = " << status;
android::OutputConfiguration output_configuration(
buffer_producer, CAMERA3_STREAM_ROTATION_0);
status = camera_device_user->createStream(output_configuration);
CHECK(status == OK) << "Failed calling ICameraDeviceUser::createStream()"
<< " error = " << status;
status = camera_device_user->endConfigure(false /* isConstrainedHighSpeed */);
CHECK(status == OK) << "Failed calling ICameraDeviceUser::endConfigure()"
<< " error = " << status;
LOG(INFO) << "Creating capture_request";
sp<CaptureRequest> capture_request = new CaptureRequest;
status = camera_device_user->createDefaultRequest(
CAMERA3_TEMPLATE_STILL_CAPTURE, &capture_request->mMetadata);
sp<Surface> surface = new Surface(
buffer_producer, true /* controlled by app */);
capture_request->mSurfaceList.push_back(surface);
capture_request->mIsReprocess = false;
LOG(INFO) << "Submitting capture request";
int64_t last_frame_number = 0;
status = camera_device_user->submitRequest(
capture_request, false, &last_frame_number);
CHECK(status == OK) << "Got error=" << status
<< " while submitting capture request.";
LOG(INFO) << "Waiting for the camera to take a picture.";
// If we don't hear back from the camera for long enough, just time out.
message_loop.PostDelayedTask(
base::Bind(&brillo::BaseMessageLoop::BreakLoop,
base::Unretained(&message_loop)),
base::TimeDelta::FromSeconds(30));
message_loop.Run(); // We'll exit on a callback from the camera.
CHECK(camera_cb->result_received());
// TODO(wiley) render this image to a file to save somewhere.
return 0;
}