Files
android_development/tools/emulator/system/camera/emulated_camera_device.cpp
Vladimir Chtchetkine b97c2f0b59 Fake camera implementation
The code submitted here builds a camera.goldfish.so module that encapsulates a camera HAL.

The major components of the camera HAL implementation are:
* Generic HAL module implemented in emulated_camera_hal.cpp There is nothing much
  to it: just exporting the required HAL header.
* EmulatedCameraFactory class that manages emulated cameras, and provides handling for
  camera_module_methods_ methods. There is only one object of this class, that is statically
  instantiated when camera.goldfish.so module is loaded.
* EmulatedCamera class that implements camera_device_ops_t API. Objects of this class are
  instantiated during EmulatedCameraFactory construction, and they interact with objects
  of EmulatedCameraDevice class to get frames.
* EmulatedCameraDevice class encapsulates an actual camera device. Objects of this class
  are contained in EmulatedCameraDevice objects, and interact with them as required by the
  API.

The fake camera implementation is shared between EmulatedFakeCamera, and EmulatedFakeCameraDevice
classes. They are pretty light. In fact, EmulatedFakeCamera is nothing more than just a
placeholder for EmulatedFakeCameraDevice instance, and EmulatedFakeCameraDevice does nothing
more, than just drawing a checker board with a bouncing square.

Other components / routines are minor: helpers, wrappers, etc. The code is heavily commented,
so there will be plenty of explanations between the lines.

Change-Id: I4463e14c255c6e3b1dcca17bed5f4efde32d9879
2011-09-12 08:37:48 -07:00

336 lines
9.9 KiB
C++

/*
* Copyright (C) 2011 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.
*/
/*
* Contains implementation of an abstract class EmulatedCameraDevice that defines
* functionality expected from an emulated physical camera device:
* - Obtaining and setting camera parameters
* - Capturing frames
* - Streaming video
* - etc.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_Device"
#include <cutils/log.h>
#include <sys/select.h>
#include "emulated_camera_device.h"
#include "converters.h"
namespace android {
EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal)
: object_lock_(),
camera_hal_(camera_hal),
current_frame_(NULL),
state_(ECDS_CONSTRUCTED)
{
}
EmulatedCameraDevice::~EmulatedCameraDevice()
{
if (current_frame_ != NULL) {
delete[] current_frame_;
}
}
/****************************************************************************
* Emulated camera device public API
***************************************************************************/
status_t EmulatedCameraDevice::Initialize()
{
LOGV("%s", __FUNCTION__);
if (IsInitialized()) {
LOGW("%s: Emulated camera device is already initialized: state_ = %d",
__FUNCTION__, state_);
return NO_ERROR;
}
/* Instantiate worker thread object. */
worker_thread_ = new WorkerThread(this);
if (worker_thread() == NULL) {
LOGE("%s: Unable to instantiate worker thread object", __FUNCTION__);
return ENOMEM;
}
state_ = ECDS_INITIALIZED;
return NO_ERROR;
}
status_t EmulatedCameraDevice::StartCapturing(int width,
int height,
uint32_t pix_fmt)
{
LOGV("%s", __FUNCTION__);
/* Validate pixel format, and calculate framebuffer size at the same time. */
switch (pix_fmt) {
case V4L2_PIX_FMT_YVU420:
framebuffer_size_ = (width * height * 12) / 8;
break;
default:
LOGE("%s: Unknown pixel format %.4s",
__FUNCTION__, reinterpret_cast<const char*>(&pix_fmt));
return EINVAL;
}
/* Cache framebuffer info. */
frame_width_ = width;
frame_height_ = height;
pixel_format_ = pix_fmt;
total_pixels_ = width * height;
/* Allocate framebuffer. */
current_frame_ = new uint8_t[GetFrameBufferSize()];
if (current_frame_ == NULL) {
LOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
return ENOMEM;
}
/* Calculate Cb/Cr panes inside the framebuffer. */
frame_Cb_ = current_frame_ + total_pixels_;
frame_Cr_ = frame_Cb_ + total_pixels_ / 4;
/* Start the camera. */
const status_t res = StartCamera();
if (res == NO_ERROR) {
LOGD("Camera device is started:\n"
" Framebuffer dimensions: %dx%d.\n"
" Pixel format: %.4s",
frame_width_, frame_height_,
reinterpret_cast<const char*>(&pixel_format_));
} else {
delete[] current_frame_;
current_frame_ = NULL;
}
return res;
}
status_t EmulatedCameraDevice::StopCapturing()
{
LOGV("%s", __FUNCTION__);
/* Stop the camera. */
const status_t res = StopCamera();
if (res == NO_ERROR) {
/* Release resources allocated for capturing. */
if (current_frame_ != NULL) {
delete[] current_frame_;
current_frame_ = NULL;
}
}
return res;
}
status_t EmulatedCameraDevice::GetCurrentFrame(void* buffer)
{
Mutex::Autolock locker(&object_lock_);
if (!IsCapturing() || current_frame_ == NULL) {
LOGE("%s is called on a device that is not in the capturing state",
__FUNCTION__);
return EINVAL;
}
memcpy(buffer, current_frame_, GetFrameBufferSize());
return NO_ERROR;
}
status_t EmulatedCameraDevice::GetCurrentPreviewFrame(void* buffer)
{
Mutex::Autolock locker(&object_lock_);
if (!IsCapturing() || current_frame_ == NULL) {
LOGE("%s is called on a device that is not in the capturing state",
__FUNCTION__);
return EINVAL;
}
/* In emulation the framebuffer is never RGB. */
switch (pixel_format_) {
case V4L2_PIX_FMT_YVU420:
YV12ToRGB565(current_frame_, buffer, frame_width_, frame_height_);
return NO_ERROR;
default:
LOGE("%s: Unknown pixel format %d", __FUNCTION__, pixel_format_);
return EINVAL;
}
}
/****************************************************************************
* Worker thread management.
***************************************************************************/
status_t EmulatedCameraDevice::StartWorkerThread()
{
LOGV("%s", __FUNCTION__);
if (!IsInitialized()) {
LOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
return EINVAL;
}
const status_t ret = worker_thread()->Start();
LOGE_IF(ret != NO_ERROR, "%s: Unable to start worker thread: %d -> %s",
__FUNCTION__, ret, strerror(ret));
return ret;
}
status_t EmulatedCameraDevice::StopWorkerThread()
{
LOGV("%s", __FUNCTION__);
if (!IsInitialized()) {
LOGE("%s: Emulated camera device is not initialized", __FUNCTION__);
return EINVAL;
}
worker_thread()->Stop();
return NO_ERROR;
}
bool EmulatedCameraDevice::InWorkerThread()
{
/* This will end the thread loop, and will terminate the thread. */
return false;
}
/****************************************************************************
* Worker thread implementation.
***************************************************************************/
status_t EmulatedCameraDevice::WorkerThread::readyToRun()
{
LOGV("Starting emulated camera device worker thread...");
LOGW_IF(thread_control_ >= 0 || control_fd_ >= 0,
"%s: Thread control FDs are opened", __FUNCTION__);
/* Create a pair of FDs that would be used to control the thread. */
int thread_fds[2];
if (pipe(thread_fds) == 0) {
thread_control_ = thread_fds[1];
control_fd_ = thread_fds[0];
LOGV("Emulated device's worker thread has been started.");
return NO_ERROR;
} else {
LOGE("%s: Unable to create thread control FDs: %d -> %s",
__FUNCTION__, errno, strerror(errno));
return errno;
}
}
status_t EmulatedCameraDevice::WorkerThread::Stop()
{
LOGV("Stopping emulated camera device's worker thread...");
status_t res = EINVAL;
if (thread_control_ >= 0) {
/* Send "stop" message to the thread loop. */
const ControlMessage msg = THREAD_STOP;
const int wres =
TEMP_FAILURE_RETRY(write(thread_control_, &msg, sizeof(msg)));
if (wres == sizeof(msg)) {
/* Stop the thread, and wait till it's terminated. */
res = requestExitAndWait();
if (res == NO_ERROR) {
/* Close control FDs. */
if (thread_control_ >= 0) {
close(thread_control_);
thread_control_ = -1;
}
if (control_fd_ >= 0) {
close(control_fd_);
control_fd_ = -1;
}
LOGV("Emulated camera device's worker thread has been stopped.");
} else {
LOGE("%s: requestExitAndWait failed: %d -> %s",
__FUNCTION__, res, strerror(res));
}
} else {
LOGE("%s: Unable to send THREAD_STOP: %d -> %s",
__FUNCTION__, errno, strerror(errno));
res = errno ? errno : EINVAL;
}
} else {
LOGE("%s: Thread control FDs are not opened", __FUNCTION__);
}
return res;
}
EmulatedCameraDevice::WorkerThread::SelectRes
EmulatedCameraDevice::WorkerThread::Select(int fd, int timeout)
{
fd_set fds[1];
struct timeval tv, *tvp = NULL;
const int fd_num = (fd >= 0) ? max(fd, control_fd_) + 1 :
control_fd_ + 1;
FD_ZERO(fds);
FD_SET(control_fd_, fds);
if (fd >= 0) {
FD_SET(fd, fds);
}
if (timeout) {
tv.tv_sec = timeout / 1000000;
tv.tv_usec = timeout % 1000000;
tvp = &tv;
}
int res = TEMP_FAILURE_RETRY(select(fd_num, fds, NULL, NULL, tvp));
if (res < 0) {
LOGE("%s: select returned %d and failed: %d -> %s",
__FUNCTION__, res, errno, strerror(errno));
return ERROR;
} else if (res == 0) {
/* Timeout. */
return TIMEOUT;
} else if (FD_ISSET(control_fd_, fds)) {
/* A control event. Lets read the message. */
ControlMessage msg;
res = TEMP_FAILURE_RETRY(read(control_fd_, &msg, sizeof(msg)));
if (res != sizeof(msg)) {
LOGE("%s: Unexpected message size %d, or an error %d -> %s",
__FUNCTION__, res, errno, strerror(errno));
return ERROR;
}
/* THREAD_STOP is the only message expected here. */
if (msg == THREAD_STOP) {
LOGV("%s: THREAD_STOP message is received", __FUNCTION__);
return EXIT_THREAD;
} else {
LOGE("Unknown worker thread message %d", msg);
return ERROR;
}
} else {
/* Must be an FD. */
LOGW_IF(fd < 0 || !FD_ISSET(fd, fds), "%s: Undefined 'select' result",
__FUNCTION__);
return READY;
}
}
}; /* namespace android */