Implements connection with the emulator

Change-Id: Ie3910a98e927f7b8ea556c403f8ced27e06bcdec
This commit is contained in:
Vladimir Chtchetkine
2011-09-13 05:27:29 -07:00
parent 455e444a1f
commit 33bda47528
21 changed files with 2015 additions and 261 deletions

View File

@@ -33,11 +33,14 @@ LOCAL_SRC_FILES := \
emulated_camera_factory.cpp \ emulated_camera_factory.cpp \
emulated_camera.cpp \ emulated_camera.cpp \
emulated_camera_device.cpp \ emulated_camera_device.cpp \
emulated_qemu_camera.cpp \
emulated_qemu_camera_device.cpp \
emulated_fake_camera.cpp \ emulated_fake_camera.cpp \
emulated_fake_camera_device.cpp \ emulated_fake_camera_device.cpp \
converters.cpp \ converters.cpp \
preview_window.cpp \ preview_window.cpp \
callback_notifier.cpp callback_notifier.cpp \
QemuClient.cpp
ifeq ($(TARGET_PRODUCT),vbox_x86) ifeq ($(TARGET_PRODUCT),vbox_x86)
LOCAL_MODULE := camera.vbox_x86 LOCAL_MODULE := camera.vbox_x86

View File

@@ -0,0 +1,521 @@
/*
* 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 classes that encapsulate connection to camera
* services in the emulator via qemu pipe.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_QemuClient"
#include <cutils/log.h>
#include "emulated_camera.h"
#include "QemuClient.h"
namespace android {
/****************************************************************************
* Qemu query
***************************************************************************/
QemuQuery::QemuQuery()
: query_(query_prealloc_),
query_status_(NO_ERROR),
reply_buffer_(NULL),
reply_data_(NULL),
reply_size_(0),
reply_data_size_(0),
reply_status_(0)
{
*query_ = '\0';
}
QemuQuery::QemuQuery(const char* query_string)
: query_(query_prealloc_),
query_status_(NO_ERROR),
reply_buffer_(NULL),
reply_data_(NULL),
reply_size_(0),
reply_data_size_(0),
reply_status_(0)
{
query_status_ = QemuQuery::Create(query_string, NULL);
}
QemuQuery::QemuQuery(const char* query_name, const char* query_param)
: query_(query_prealloc_),
query_status_(NO_ERROR),
reply_buffer_(NULL),
reply_data_(NULL),
reply_size_(0),
reply_data_size_(0),
reply_status_(0)
{
query_status_ = QemuQuery::Create(query_name, query_param);
}
QemuQuery::~QemuQuery()
{
QemuQuery::Reset();
}
status_t QemuQuery::Create(const char* name, const char* param)
{
/* Reset from the previous use. */
Reset();
/* Query name cannot be NULL or an empty string. */
if (name == NULL || *name == '\0') {
LOGE("%s: NULL or an empty string is passed as query name.",
__FUNCTION__);
return EINVAL;
}
const size_t name_len = strlen(name);
const size_t param_len = (param != NULL) ? strlen(param) : 0;
const size_t required = strlen(name) + (param_len ? (param_len + 2) : 1);
if (required > sizeof(query_prealloc_)) {
/* Preallocated buffer was too small. Allocate a bigger query buffer. */
query_ = new char[required];
if (query_ == NULL) {
LOGE("%s: Unable to allocate %d bytes for query buffer",
__FUNCTION__, required);
query_status_ = ENOMEM;
return ENOMEM;
}
}
/* At this point query_ buffer is big enough for the query. */
if (param_len) {
sprintf(query_, "%s %s", name, param);
} else {
memcpy(query_, name, name_len + 1);
}
return NO_ERROR;
}
status_t QemuQuery::Completed(status_t status)
{
/* Save query completion status. */
query_status_ = status;
if (query_status_ != NO_ERROR) {
return query_status_;
}
/* Make sure reply buffer contains at least 'ok', or 'ko'.
* Note that 'ok', or 'ko' prefixes are always 3 characters long: in case
* there are more data in the reply, that data will be separated from 'ok'/'ko'
* with a ':'. If there is no more data in the reply, the prefix will be
* zero-terminated, and the terminator will be inculded in the reply. */
if (reply_buffer_ == NULL || reply_size_ < 3) {
LOGE("%s: Invalid reply to the query", __FUNCTION__);
query_status_ = EINVAL;
return EINVAL;
}
/* Lets see the reply status. */
if (!memcmp(reply_buffer_, "ok", 2)) {
reply_status_ = 1;
} else if (!memcmp(reply_buffer_, "ko", 2)) {
reply_status_ = 0;
} else {
LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, reply_buffer_);
query_status_ = EINVAL;
return EINVAL;
}
/* Lets see if there are reply data that follow. */
if (reply_size_ > 3) {
/* There are extra data. Make sure they are separated from the status
* with a ':' */
if (reply_buffer_[2] != ':') {
LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, reply_buffer_);
query_status_ = EINVAL;
return EINVAL;
}
reply_data_ = reply_buffer_ + 3;
reply_data_size_ = reply_size_ - 3;
} else {
/* Make sure reply buffer containing just 'ok'/'ko' ends with
* zero-terminator. */
if (reply_buffer_[2] != '\0') {
LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, reply_buffer_);
query_status_ = EINVAL;
return EINVAL;
}
}
return NO_ERROR;
}
void QemuQuery::Reset()
{
if (query_ != NULL && query_ != query_prealloc_) {
delete[] query_;
}
query_ = query_prealloc_;
query_status_ = NO_ERROR;
if (reply_buffer_ != NULL) {
free(reply_buffer_);
reply_buffer_ = NULL;
}
reply_data_ = NULL;
reply_size_ = 0;
reply_data_size_ = 0;
reply_status_ = 0;
}
/****************************************************************************
* Qemu client base
***************************************************************************/
/* Camera service name. */
const char QemuClient::camera_service_name_[] = "camera";
QemuClient::QemuClient()
: fd_(-1)
{
}
QemuClient::~QemuClient()
{
if (fd_ >= 0) {
close(fd_);
}
}
/****************************************************************************
* Qemu client API
***************************************************************************/
status_t QemuClient::Connect(const char* param)
{
LOGV("%s: '%s'", __FUNCTION__, param ? param : "");
/* Make sure that client is not connected already. */
if (fd_ >= 0) {
LOGE("%s: Qemu client is already connected", __FUNCTION__);
return EINVAL;
}
/* Select one of the two: 'factory', or 'emulated camera' service */
if (param == NULL || *param == '\0') {
/* No parameters: connect to the factory service. */
char pipe_name[512];
snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", camera_service_name_);
fd_ = qemu_pipe_open(pipe_name);
} else {
/* One extra char ':' that separates service name and parameters + six
* characters for 'qemud:'. This is required by qemu pipe protocol. */
char* connection_str = new char[strlen(camera_service_name_) +
strlen(param) + 8];
sprintf(connection_str, "qemud:%s:%s", camera_service_name_, param);
fd_ = qemu_pipe_open(connection_str);
delete[] connection_str;
}
if (fd_ < 0) {
LOGE("%s: Unable to connect to the camera service '%s': %s",
__FUNCTION__, param ? param : "Factory", strerror(errno));
return errno ? errno : EINVAL;
}
return NO_ERROR;
}
void QemuClient::Disconnect()
{
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
}
status_t QemuClient::Send(const void* data, size_t data_size)
{
if (fd_ < 0) {
LOGE("%s: Qemu client is not connected", __FUNCTION__);
return EINVAL;
}
/* Note that we don't use here qemud_client_send, since with qemu pipes we
* don't need to provide payload size prior to payload when we're writing to
* the pipe. So, we can use simple write, and qemu pipe will take care of the
* rest, calling the receiving end with the number of bytes transferred. */
const size_t written = qemud_fd_write(fd_, data, data_size);
if (written == data_size) {
return NO_ERROR;
} else {
LOGE("%s: Error sending data via qemu pipe: %s",
__FUNCTION__, strerror(errno));
return errno != NO_ERROR ? errno : EIO;
}
}
status_t QemuClient::Receive(void** data, size_t* data_size)
{
*data = NULL;
*data_size = 0;
if (fd_ < 0) {
LOGE("%s: Qemu client is not connected", __FUNCTION__);
return EINVAL;
}
/* The way the service replies to a query, it sends payload size first, and
* then it sends the payload itself. Note that payload size is sent as a
* string, containing 8 characters representing a hexadecimal payload size
* value. Note also, that the string doesn't contain zero-terminator. */
size_t payload_size;
char payload_size_str[9];
int rd_res = qemud_fd_read(fd_, payload_size_str, 8);
if (rd_res != 8) {
LOGE("%s: Unable to obtain payload size: %s",
__FUNCTION__, strerror(errno));
return errno ? errno : EIO;
}
/* Convert payload size. */
errno = 0;
payload_size_str[8] = '\0';
payload_size = strtol(payload_size_str, NULL, 16);
if (errno) {
LOGE("%s: Invalid payload size '%s'", __FUNCTION__, payload_size_str);
return EIO;
}
/* Allocate payload data buffer, and read the payload there. */
*data = malloc(payload_size);
if (*data == NULL) {
LOGE("%s: Unable to allocate %d bytes payload buffer",
__FUNCTION__, payload_size);
return ENOMEM;
}
rd_res = qemud_fd_read(fd_, *data, payload_size);
if (static_cast<size_t>(rd_res) == payload_size) {
*data_size = payload_size;
return NO_ERROR;
} else {
LOGE("%s: Read size %d doesnt match expected payload size %d: %s",
__FUNCTION__, rd_res, payload_size, strerror(errno));
free(*data);
*data = NULL;
return errno ? errno : EIO;
}
}
status_t QemuClient::Query(QemuQuery* query)
{
/* Make sure that query has been successfuly constructed. */
if (query->query_status_ != NO_ERROR) {
LOGE("%s: Query is invalid", __FUNCTION__);
return query->query_status_;
}
/* Send the query. */
status_t res = Send(query->query_, strlen(query->query_) + 1);
if (res == NO_ERROR) {
/* Read the response. */
res = Receive(reinterpret_cast<void**>(&query->reply_buffer_),
&query->reply_size_);
if (res != NO_ERROR) {
LOGE("%s Response to query '%s' has failed: %s",
__FUNCTION__, query->query_, strerror(res));
}
} else {
LOGE("%s: Send query '%s' failed: %s",
__FUNCTION__, query->query_, strerror(res));
}
/* Complete the query, and return its completion handling status. */
return query->Completed(res);
}
/****************************************************************************
* Qemu client for the 'factory' service.
***************************************************************************/
/*
* Factory service queries.
*/
/* Queries list of cameras connected to the host. */
const char FactoryQemuClient::query_list_[] = "list";
FactoryQemuClient::FactoryQemuClient()
: QemuClient()
{
}
FactoryQemuClient::~FactoryQemuClient()
{
}
status_t FactoryQemuClient::ListCameras(char** list)
{
QemuQuery query(query_list_);
Query(&query);
if (!query.IsSucceeded()) {
return query.GetCompletionStatus();
}
/* Make sure there is a list returned. */
if (query.reply_data_size_ == 0) {
LOGE("%s: No camera list is returned.", __FUNCTION__);
return EINVAL;
}
/* Copy the list over. */
*list = (char*)malloc(query.reply_data_size_);
if (*list != NULL) {
memcpy(*list, query.reply_data_, query.reply_data_size_);
LOGD("Emulated camera list: %s", *list);
return NO_ERROR;
} else {
LOGE("%s: Unable to allocate %d bytes",
__FUNCTION__, query.reply_data_size_);
return ENOMEM;
}
}
/****************************************************************************
* Qemu client for an 'emulated camera' service.
***************************************************************************/
/*
* Emulated camera queries
*/
/* Connect to the camera device. */
const char CameraQemuClient::query_connect_[] = "connect";
/* Disconect from the camera device. */
const char CameraQemuClient::query_disconnect_[] = "disconnect";
/* Start capturing video from the camera device. */
const char CameraQemuClient::query_start_[] = "start";
/* Stop capturing video from the camera device. */
const char CameraQemuClient::query_stop_[] = "stop";
/* Get next video frame from the camera device. */
const char CameraQemuClient::query_frame_[] = "frame";
CameraQemuClient::CameraQemuClient()
: QemuClient()
{
}
CameraQemuClient::~CameraQemuClient()
{
}
status_t CameraQemuClient::QueryConnect()
{
QemuQuery query(query_connect_);
Query(&query);
const status_t res = query.GetCompletionStatus();
LOGE_IF(res != NO_ERROR, "%s failed: %s",
__FUNCTION__, query.reply_data_ ? query.reply_data_ :
"No error message");
return res;
}
status_t CameraQemuClient::QueryDisconnect()
{
QemuQuery query(query_disconnect_);
Query(&query);
const status_t res = query.GetCompletionStatus();
LOGE_IF(res != NO_ERROR, "%s failed: %s",
__FUNCTION__, query.reply_data_ ? query.reply_data_ :
"No error message");
return res;
}
status_t CameraQemuClient::QueryStart(uint32_t pixel_format,
int width,
int height)
{
char query_str[256];
snprintf(query_str, sizeof(query_str), "%s dim=%dx%d pix=%d",
query_start_, width, height, pixel_format);
QemuQuery query(query_str);
Query(&query);
const status_t res = query.GetCompletionStatus();
LOGE_IF(res != NO_ERROR, "%s failed: %s",
__FUNCTION__, query.reply_data_ ? query.reply_data_ :
"No error message");
return res;
}
status_t CameraQemuClient::QueryStop()
{
QemuQuery query(query_stop_);
Query(&query);
const status_t res = query.GetCompletionStatus();
LOGE_IF(res != NO_ERROR, "%s failed: %s",
__FUNCTION__, query.reply_data_ ? query.reply_data_ :
"No error message");
return res;
}
status_t CameraQemuClient::QueryFrame(void* vframe,
void* pframe,
size_t vframe_size,
size_t pframe_size)
{
char query_str[256];
snprintf(query_str, sizeof(query_str), "%s video=%d preview=%d",
query_frame_, (vframe && vframe_size) ? vframe_size : 0,
(pframe && pframe_size) ? pframe_size : 0);
QemuQuery query(query_str);
Query(&query);
const status_t res = query.GetCompletionStatus();
LOGE_IF(res != NO_ERROR, "%s failed: %s",
__FUNCTION__, query.reply_data_ ? query.reply_data_ :
"No error message");
if (res == NO_ERROR) {
/* Copy requested frames. */
size_t cur_offset = 0;
const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.reply_data_);
/* Video frame is always first. */
if (vframe != NULL && vframe_size != 0) {
/* Make sure that video frame is in. */
if ((query.reply_data_size_ - cur_offset) >= vframe_size) {
memcpy(vframe, frame, vframe_size);
cur_offset += vframe_size;
} else {
LOGE("%s: Reply (%d bytes) is to small to contain video frame (%d bytes)",
__FUNCTION__, query.reply_data_size_ - cur_offset, vframe_size);
return EINVAL;
}
}
if (pframe != NULL && pframe_size != 0) {
/* Make sure that preview frame is in. */
if ((query.reply_data_size_ - cur_offset) >= pframe_size) {
memcpy(pframe, frame + cur_offset, pframe_size);
cur_offset += pframe_size;
} else {
LOGE("%s: Reply (%d bytes) is to small to contain preview frame (%d bytes)",
__FUNCTION__, query.reply_data_size_ - cur_offset, pframe_size);
return EINVAL;
}
}
}
return res;
}
}; /* namespace android */

View File

@@ -0,0 +1,418 @@
/*
* 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.
*/
#ifndef HW_EMULATOR_CAMERA_QEMU_CLIENT_H
#define HW_EMULATOR_CAMERA_QEMU_CLIENT_H
/*
* Contains declaration of classes that encapsulate connection to camera services
* in the emulator via qemu pipe.
*/
#include <hardware/qemud.h>
namespace android {
/****************************************************************************
* Qemu query
***************************************************************************/
/* Encapsulates a query to the emulator.
* Guest exchanges data with the emulator via queries sent over the qemu pipe.
* The queries as well as replies to the queries are all strings (except for the
* 'frame' query where reply is a framebuffer).
* Each query is formatted as such:
*
* "<query name>[ <parameters>]",
*
* where <query name> is a string representing query name, and <parameters> are
* optional parameters for the query. If parameters are present, they must be
* separated from the query name with a single space, and they must be formatted
* as such:
*
* "<name1>=<value1> <name2>=<value2> ... <nameN>=<valueN>"
*
* I.e.:
* - Every parameter must have a name, and a value.
* - Name and value must be separated with '='.
* - No spaces are allowed around '=' separating name and value.
* - Parameters must be separated with a single space character.
* - No '=' character is allowed in name and in value.
*
* There are certain restrictions on strings used in the query:
* - Spaces are allowed only as separators.
* - '=' are allowed only to divide parameter names from parameter values.
*
* Emulator replies to each query in two chunks:
* - 4 bytes encoding the payload size
* - Payload, whose size is defined by the first chunk.
*
* Every payload always begins with two characters, encoding the result of the
* query:
* - 'ok' Encoding the success
* - 'ko' Encoding a failure.
* After that payload may have optional data. If payload has more data following
* the query result, there is a ':' character separating them. If payload carries
* only the result, it always ends with a zero-terminator.
*/
class QemuQuery {
public:
/* Constructs an uninitialized QemuQuery instance. */
QemuQuery();
/* Constructs and initializes QemuQuery instance for a query.
* Param:
* query_string - Query string. This constructor can also be used to
* construct a query that doesn't have parameters. In this case query
* name can be passed as a parameter here.
*/
explicit QemuQuery(const char* query_string);
/* Constructs and initializes QemuQuery instance for a query.
* Param:
* query_name - Query name.
* query_param - Query parameters. Can be NULL.
*/
QemuQuery(const char* query_name, const char* query_param);
/* Destructs QemuQuery instance. */
~QemuQuery();
/****************************************************************************
* Public API
***************************************************************************/
/* Creates new query.
* This method will reset this instance prior to creating a new query.
* Param:
* query_name - Query name.
* query_param - Query parameters. Can be NULL.
* Return:
* NO_ERROR on success, or an appropriate error status.
*/
status_t Create(const char* name, const char* param);
/* Completes the query after a reply from the emulator.
* This method will parse the reply buffer, and calculate the final query
* status, which depends not only on the transport success / failure, but
* also on 'ok' / 'ko' in the query reply.
* Param:
* status - Query delivery status. This status doesn't necessarily reflects
* the final query status (which is defined by 'ok'/'ko' in the reply buffer).
* This status simply states whether or not the query has been sent, and a
* reply has been received successfuly. However, if status indicates a
* failure, the entire query has failed. If status indicates a success, the
* reply will be checked here to calculate the final query status.
* Return:
* NO_ERROR on success, or an appropriate error status on failure. Note that
* status returned here just signals whether or not the method has succeeded.
* Use IsSucceeded() / GetCompletionStatus() methods to check the final
* query status.
*/
status_t Completed(status_t status);
/* Resets the query from a previous use. */
void Reset();
/* Checks if query has succeeded.
* Note that this method must be called after Completed() method of this
* class has been executed.
*/
inline bool IsSucceeded() const {
return query_status_ == NO_ERROR && reply_status_ != 0;
}
/* Gets final completion status of the query.
* Note that this method must be called after Completed() method of this
* class has been executed.
* NO_ERROR on success, or an appropriate error status on failure.
*/
inline status_t GetCompletionStatus() const {
if (IsSucceeded()) {
return NO_ERROR;
}
return (query_status_ != NO_ERROR) ? query_status_ : EINVAL;
}
/****************************************************************************
* Public data memebers
***************************************************************************/
public:
/* Query string. */
char* query_;
/* Query status. */
status_t query_status_;
/* Reply buffer */
char* reply_buffer_;
/* Reply data (past 'ok'/'ko'). If NULL, there were no data in reply. */
char* reply_data_;
/* Reply buffer size. */
size_t reply_size_;
/* Reply data size. */
size_t reply_data_size_;
/* Reply status: 1 - ok, 0 - ko. */
int reply_status_;
/****************************************************************************
* Private data memebers
***************************************************************************/
protected:
/* Preallocated buffer for small queries. */
char query_prealloc_[256];
};
/****************************************************************************
* Qemu client base
***************************************************************************/
/* Encapsulates a connection to the 'camera' service in the emulator via qemu
* pipe.
*/
class QemuClient {
public:
/* Constructs QemuClient instance. */
QemuClient();
/* Destructs QemuClient instance. */
virtual ~QemuClient();
/****************************************************************************
* Qemu client API
***************************************************************************/
public:
/* Connects to the 'camera' service in the emulator via qemu pipe.
* Param:
* param - Parameters to pass to the camera service. There are two types of
* camera services implemented by the emulator. The first one is a
* 'camera factory' type of service that provides list of cameras
* connected to the host. Another one is an 'emulated camera' type of
* service that provides interface to a camera connected to the host. At
* the connection time emulator makes distinction between the two by
* looking at connection parameters: no parameters means connection to
* the 'factory' service, while connection with parameters means
* connection to an 'emulated camera' service, where camera is identified
* by one of the connection parameters. So, passing NULL, or an empty
* string to this method will establish connection with a 'factory'
* service, while not empty string passed here will establish connection
* with an 'emulated camera' service. Parameters defining the emulated
* camera must be formatted as such:
*
* "name=<device name> [inp_channel=<input channel #>]",
*
* where 'device name' is a required parameter defining name of the
* camera device, 'input channel' is an optional parameter (positive
* integer), defining input channel to use on the camera device. Note
* that device name passed here must have been previously obtained from
* the factory service.
* Return:
* NO_ERROR on success, or an appropriate error status.
*/
virtual status_t Connect(const char* param);
/* Disconnects from the service. */
virtual void Disconnect();
/* Sends data to the service.
* Param:
* data, data_size - Data to send.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
virtual status_t Send(const void* data, size_t data_size);
/* Receives data from the service.
* This method assumes that data to receive will come in two chunks: 8
* characters encoding the payload size in hexadecimal string, followed by
* the paylod (if any).
* This method will allocate data buffer where to receive the response.
* Param:
* data - Upon success contains address of the allocated data buffer with
* the data received from the service. The caller is responsible for
* freeing allocated data buffer.
* data_size - Upon success contains size of the data received from the
* service.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
virtual status_t Receive(void** data, size_t* data_size);
/* Sends a query, and receives a response from the service.
* Param:
* query - Query to send to the service. When this method returns, the query
* is completed, and all its relevant data members are properly initialized.
* Return:
* NO_ERROR on success, or an appropriate error status on failure. Note that
* status returned here is not the final query status. Use IsSucceeded(), or
* GetCompletionStatus() method on the query to see if it has succeeded.
* However, if this method returns a failure, it means that the query has
* failed, and there is no guarantee that its data members are properly
* initialized (except for the 'query_status_', which is always in the
* proper state).
*/
virtual status_t Query(QemuQuery* query);
/****************************************************************************
* Data members
***************************************************************************/
protected:
/* Qemu pipe handle. */
int fd_;
private:
/* Camera service name. */
static const char camera_service_name_[];
};
/****************************************************************************
* Qemu client for the 'factory' service.
***************************************************************************/
/* Encapsulates QemuClient for the 'factory' service. */
class FactoryQemuClient : public QemuClient {
public:
/* Constructs FactoryQemuClient instance. */
FactoryQemuClient();
/* Destructs FactoryQemuClient instance. */
~FactoryQemuClient();
/****************************************************************************
* Public API
***************************************************************************/
public:
/* Lists camera devices connected to the host.
* Param:
* list - Upon success contains list of cameras connected to the host. The
* list returned here is represented as a string, containing multiple
* lines, separated with '\n', where each line represents a camera. Each
* camera line is formatted as such:
*
* "name=<device name> channel=<num> pix=<num> framedims=<dimensions>\n"
*
* Where:
* - 'name' is the name of camera device attached to the host. This name
* must be used for subsequent connection to the 'emulated camera'
* service for that camera.
* - 'channel' - input channel number (positive int) to use to communicate
* with the camera.
* - 'pix' - pixel format (a "fourcc" int), chosen for the video frames.
* - 'framedims' contains a list of frame dimensions supported by the
* camera. Each etry in the list is in form '<width>x<height>', where
* 'width' and 'height' are numeric values for width and height of a
* supported frame dimension. Entries in this list are separated with
* ','.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t ListCameras(char** list);
/****************************************************************************
* Names of the queries available for the emulated camera factory.
***************************************************************************/
private:
/* List cameras connected to the host. */
static const char query_list_[];
};
/****************************************************************************
* Qemu client for an 'emulated camera' service.
***************************************************************************/
/* Encapsulates QemuClient for an 'emulated camera' service.
*/
class CameraQemuClient : public QemuClient {
public:
/* Constructs CameraQemuClient instance. */
CameraQemuClient();
/* Destructs CameraQemuClient instance. */
~CameraQemuClient();
/****************************************************************************
* Public API
***************************************************************************/
public:
/* Queries camera connection.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t QueryConnect();
/* Queries camera disconnection.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t QueryDisconnect();
/* Queries camera to start capturing video.
* Param:
* pixel_format - Pixel format that is used by the client to push video
* frames to the camera framework.
* width, height - Frame dimensions, requested by the framework.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t QueryStart(uint32_t pixel_format, int width, int height);
/* Queries camera to stop capturing video.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t QueryStop();
/* Queries camera for the next video frame.
* Param:
* vframe, vframe_size - Define buffer, allocated to receive a video frame.
* Any of these parameters can be 0, indicating that the caller is
* interested only in preview frame.
* pframe, pframe_size - Define buffer, allocated to receive a preview frame.
* Any of these parameters can be 0, indicating that the caller is
* interested only in video frame.
* Return:
* NO_ERROR on success, or an appropriate error status on failure.
*/
status_t QueryFrame(void* vframe,
void* pframe,
size_t vframe_size,
size_t pframe_size);
/****************************************************************************
* Names of the queries available for the emulated camera.
***************************************************************************/
private:
/* Connect to the camera. */
static const char query_connect_[];
/* Disconnect from the camera. */
static const char query_disconnect_[];
/* Start video capturing. */
static const char query_start_[];
/* Stop video capturing. */
static const char query_stop_[];
/* Query frame(s). */
static const char query_frame_[];
};
}; /* namespace android */
#endif /* HW_EMULATOR_CAMERA_QEMU_CLIENT_H */

View File

@@ -28,22 +28,6 @@
namespace android { namespace android {
/* Descriptor for video frame metadata. */
struct VideoFrameMetadata
{
/* Required field that defines metadata buffer type. */
MetadataBufferType type;
/*
* TODO: This is taken from a sample code. It seems to work, but requires
* clarifications on what metadata structure should look like!
*/
const void* frame;
int offset;
camera_memory_t* holder;
};
/* String representation of camera messages. */ /* String representation of camera messages. */
static const char* _camera_messages[] = static const char* _camera_messages[] =
{ {
@@ -108,8 +92,7 @@ CallbackNotifier::CallbackNotifier()
last_frame_(0), last_frame_(0),
frame_after_(0), frame_after_(0),
message_enabler_(0), message_enabler_(0),
video_recording_enabled_(false), video_recording_enabled_(false)
store_meta_data_in_buffers_(true)
{ {
} }
@@ -173,7 +156,7 @@ status_t CallbackNotifier::EnableVideoRecording(int fps)
Mutex::Autolock locker(&object_lock_); Mutex::Autolock locker(&object_lock_);
video_recording_enabled_ = true; video_recording_enabled_ = true;
last_frame_ = 0; last_frame_ = 0;
frame_after_ = 1000000 / fps; frame_after_ = 1000000000LL / fps;
return NO_ERROR; return NO_ERROR;
} }
@@ -196,25 +179,12 @@ bool CallbackNotifier::IsVideoRecordingEnabled()
void CallbackNotifier::ReleaseRecordingFrame(const void* opaque) void CallbackNotifier::ReleaseRecordingFrame(const void* opaque)
{ {
LOGV("%s: frame = %p", __FUNCTION__, opaque); /* We don't really have anything to release here, since we report video
* frames by copying them directly to the camera memory. */
if (opaque != NULL) {
const VideoFrameMetadata* meta =
reinterpret_cast<const VideoFrameMetadata*>(opaque);
if (meta->type == kMetadataBufferTypeCameraSource &&
meta->holder != NULL) {
meta->holder->release(meta->holder);
}
}
} }
status_t CallbackNotifier::StoreMetaDataInBuffers(bool enable) status_t CallbackNotifier::StoreMetaDataInBuffers(bool enable)
{ {
LOGV("%s: %s", __FUNCTION__, enable ? "true" : "false");
Mutex::Autolock locker(&object_lock_);
store_meta_data_in_buffers_ = enable;
/* Return INVALID_OPERATION means HAL does not support metadata. So HAL will /* Return INVALID_OPERATION means HAL does not support metadata. So HAL will
* return actual frame data with CAMERA_MSG_VIDEO_FRRAME. Return * return actual frame data with CAMERA_MSG_VIDEO_FRRAME. Return
* INVALID_OPERATION to mean metadata is not supported. */ * INVALID_OPERATION to mean metadata is not supported. */
@@ -237,7 +207,6 @@ void CallbackNotifier::Cleanup()
last_frame_ = 0; last_frame_ = 0;
frame_after_ = 0; frame_after_ = 0;
video_recording_enabled_ = false; video_recording_enabled_ = false;
store_meta_data_in_buffers_ = true;
} }
void CallbackNotifier::OnNextFrameAvailable(const void* frame, void CallbackNotifier::OnNextFrameAvailable(const void* frame,
@@ -248,30 +217,15 @@ void CallbackNotifier::OnNextFrameAvailable(const void* frame,
if ((message_enabler_ & CAMERA_MSG_VIDEO_FRAME) != 0 && if ((message_enabler_ & CAMERA_MSG_VIDEO_FRAME) != 0 &&
data_cb_timestamp_ != NULL && video_recording_enabled_ && data_cb_timestamp_ != NULL && video_recording_enabled_ &&
IsTimeForNewVideoFrame()) { IsTimeForNewVideoFrame(timestamp)) {
/* Ready for new video frame. Allocate frame holder. */ camera_memory_t* cam_buff =
camera_memory_t* holder = get_memory_(-1, camera_dev->GetFrameBufferSize(), 1, NULL);
get_memory_(-1, sizeof(VideoFrameMetadata), 1, NULL); if (NULL != cam_buff && NULL != cam_buff->data) {
if (NULL != holder && NULL != holder->data) { memcpy(cam_buff->data, frame, camera_dev->GetFrameBufferSize());
if (store_meta_data_in_buffers_) {
VideoFrameMetadata* meta =
reinterpret_cast<VideoFrameMetadata*>(holder->data);
meta->type = kMetadataBufferTypeCameraSource;
meta->frame = frame;
meta->offset = 0;
meta->holder = holder;
data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME, data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME,
holder, 0, cb_opaque_); cam_buff, 0, cb_opaque_);
/* Allocated holder will be released by release_recording_frame
* call. */
} else { } else {
holder->data = const_cast<void*>(frame); LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME,
holder, 0, cb_opaque_);
holder->release(holder);
}
} else {
LOGE("%s: Memory failure", __FUNCTION__);
} }
} }
} }
@@ -280,13 +234,10 @@ void CallbackNotifier::OnNextFrameAvailable(const void* frame,
* Private API * Private API
***************************************************************************/ ***************************************************************************/
bool CallbackNotifier::IsTimeForNewVideoFrame() bool CallbackNotifier::IsTimeForNewVideoFrame(nsecs_t timestamp)
{ {
timeval cur_time; if ((timestamp - last_frame_) >= frame_after_) {
gettimeofday(&cur_time, NULL); last_frame_ = timestamp;
const uint64_t cur_mks = cur_time.tv_sec * 1000000LL + cur_time.tv_usec;
if ((cur_mks - last_frame_) >= frame_after_) {
last_frame_ = cur_mks;
return true; return true;
} }
return false; return false;

View File

@@ -149,8 +149,10 @@ public:
protected: protected:
/* Checks if it's time to push new video frame. /* Checks if it's time to push new video frame.
* Note that this method must be called while object is locked. */ * Note that this method must be called while object is locked.
bool IsTimeForNewVideoFrame(); * Param:
* timestamp - Timestamp for the new frame. */
bool IsTimeForNewVideoFrame(nsecs_t timestamp);
/**************************************************************************** /****************************************************************************
* Data members * Data members
@@ -170,21 +172,17 @@ protected:
camera_request_memory get_memory_; camera_request_memory get_memory_;
void* cb_opaque_; void* cb_opaque_;
/* Timestamp (abs. microseconds) when last frame has been delivered to the /* Timestamp when last frame has been delivered to the framework. */
* framework. */ nsecs_t last_frame_;
uint64_t last_frame_;
/* Video frequency in microseconds. */ /* Video frequency in nanosec. */
uint32_t frame_after_; nsecs_t frame_after_;
/* Message enabler. */ /* Message enabler. */
uint32_t message_enabler_; uint32_t message_enabler_;
/* Video recording status. */ /* Video recording status. */
bool video_recording_enabled_; bool video_recording_enabled_;
/* Status of the metadata buffering. */
bool store_meta_data_in_buffers_;
}; };
}; /* namespace android */ }; /* namespace android */

View File

@@ -34,13 +34,43 @@ void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
const uint8_t* Cr_pos = Cb_pos + pix_total / 4; const uint8_t* Cr_pos = Cb_pos + pix_total / 4;
const uint8_t* Cb = Cb_pos; const uint8_t* Cb = Cb_pos;
const uint8_t* Cr = Cr_pos; const uint8_t* Cr = Cr_pos;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x += 2) { for (int x = 0; x < width; x += 2) {
const uint8_t nCb = *Cb; Cb++; const uint8_t nCb = *Cb; Cb++;
const uint8_t nCr = *Cr; Cr++; const uint8_t nCr = *Cr; Cr++;
*rgb_buf = YCbCrToRGB565(*Y, nCb, nCr); *rgb_buf = YUVToRGB565(*Y, nCb, nCr);
Y++; rgb_buf++; Y++; rgb_buf++;
*rgb_buf = YCbCrToRGB565(*Y, nCb, nCr); *rgb_buf = YUVToRGB565(*Y, nCb, nCr);
Y++; rgb_buf++;
}
if (y & 0x1) {
Cb_pos = Cb;
Cr_pos = Cr;
} else {
Cb = Cb_pos;
Cr = Cr_pos;
}
}
}
void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
{
const int pix_total = width * height;
uint32_t* rgb_buf = reinterpret_cast<uint32_t*>(rgb);
const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
const uint8_t* Cb_pos = Y + pix_total;
const uint8_t* Cr_pos = Cb_pos + pix_total / 4;
const uint8_t* Cb = Cb_pos;
const uint8_t* Cr = Cr_pos;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x += 2) {
const uint8_t nCb = *Cb; Cb++;
const uint8_t nCr = *Cr; Cr++;
*rgb_buf = YUVToRGB32(*Y, nCb, nCr);
Y++; rgb_buf++;
*rgb_buf = YUVToRGB32(*Y, nCb, nCr);
Y++; rgb_buf++; Y++; rgb_buf++;
} }
if (y & 0x1) { if (y & 0x1) {

View File

@@ -17,103 +17,234 @@
#ifndef HW_EMULATOR_CAMERA_CONVERTERS_H #ifndef HW_EMULATOR_CAMERA_CONVERTERS_H
#define HW_EMULATOR_CAMERA_CONVERTERS_H #define HW_EMULATOR_CAMERA_CONVERTERS_H
#include <sys/endian.h>
#ifndef __BYTE_ORDER
#error "could not determine byte order"
#endif
/* /*
* Contains declaration of framebuffer conversion routines. * Contains declaration of framebuffer conversion routines.
*
* NOTE: RGB and big/little endian considerations. Wherewer in this code RGB
* pixels are represented as WORD, or DWORD, the color order inside the
* WORD / DWORD matches the one that would occur if that WORD / DWORD would have
* been read from the typecasted framebuffer:
*
* const uint32_t rgb = *reinterpret_cast<const uint32_t*>(framebuffer);
*
* So, if this code runs on the little endian CPU, red color in 'rgb' would be
* masked as 0x000000ff, and blue color would be masked as 0x00ff0000, while if
* the code runs on a big endian CPU, the red color in 'rgb' would be masked as
* 0xff000000, and blue color would be masked as 0x0000ff00,
*/ */
namespace android { namespace android {
inline uint8_t clamp(int x)
{
if (x > 255) return 255;
if (x < 0) return 0;
return static_cast<uint8_t>(x);
}
/* /*
* RGB565 color masks * RGB565 color masks
*/ */
static const int kRed = 0xf800; #if __BYTE_ORDER == __LITTLE_ENDIAN
static const int kGreen = 0x07e0; static const uint16_t kRed5 = 0x001f;
static const int kBlue = 0x001f; static const uint16_t kGreen6 = 0x07e0;
static const uint16_t kBlue5 = 0xf800;
#else // __BYTE_ORDER
static const uint16_t kRed5 = 0xf800;
static const uint16_t kGreen6 = 0x07e0;
static const uint16_t kBlue5 = 0x001f;
#endif // __BYTE_ORDER
static const uint32_t kBlack16 = 0x0000;
static const uint32_t kWhite16 = kRed5 | kGreen6 | kBlue5;
/* /*
* RGB -> YCbCr conversion constants and macros * RGB32 color masks
*/ */
static const double kR0 = 0.299; #if __BYTE_ORDER == __LITTLE_ENDIAN
static const double kR1 = 0.587; static const uint32_t kRed8 = 0x000000ff;
static const double kR2 = 0.114; static const uint32_t kGreen8 = 0x0000ff00;
static const double kR3 = 0.169; static const uint32_t kBlue8 = 0x00ff0000;
static const double kR4 = 0.331; #else // __BYTE_ORDER
static const double kR5 = 0.499; static const uint32_t kRed8 = 0x00ff0000;
static const double kR6 = 0.499; static const uint32_t kGreen8 = 0x0000ff00;
static const double kR7 = 0.418; static const uint32_t kBlue8 = 0x000000ff;
static const double kR8 = 0.0813; #endif // __BYTE_ORDER
static const uint32_t kBlack32 = 0x00000000;
#define RGB2Y(R,G,B) static_cast<uint8_t>(kR0*R + kR1*G + kR2*B) static const uint32_t kWhite32 = kRed8 | kGreen8 | kBlue8;
#define RGB2Cb(R,G,B) static_cast<uint8_t>(-kR3*R - kR4*G + kR5*B + 128)
#define RGB2Cr(R,G,B) static_cast<uint8_t>(kR6*R - kR7*G - kR8*B + 128)
/* Converts RGB565 color to YCbCr */
inline void RGB565ToYCbCr(uint16_t rgb, uint8_t* y, uint8_t* Cb, uint8_t* Cr)
{
const uint32_t r = (rgb & kRed) >> 11;
const uint32_t g = (rgb & kGreen) >> 5;
const uint32_t b = rgb & kBlue;
*y = RGB2Y(r,g,b);
*Cb = RGB2Cb(r,g,b);
*Cr = RGB2Cr(r,g,b);
}
/* Gets a 'Y' value for RGB565 color. */
inline uint8_t RGB565ToY(uint16_t rgb)
{
const uint32_t r = (rgb & kRed) >> 11;
const uint32_t g = (rgb & kGreen) >> 5;
const uint32_t b = rgb & kBlue;
return RGB2Y(r,g,b);
}
/* /*
* YCbCr -> RGB conversion constants and macros * Extracting, and saving color bytes from / to WORD / DWORD RGB.
*/ */
static const double kY0 = 1.402;
static const double kY1 = 0.344;
static const double kY2 = 0.714;
static const double kY3 = 1.772;
#define YCbCr2R(Y, Cb, Cr) clamp(Y + kY0*(Cr-128)) #if __BYTE_ORDER == __LITTLE_ENDIAN
#define YCbCr2G(Y, Cb, Cr) clamp(Y - kY1*(Cb-128) - kY2*(Cr-128)) /* Extract red, green, and blue bytes from RGB565 word. */
#define YCbCr2B(Y, Cb, Cr) clamp(Y + kY3*(Cb-128)) #define R16(rgb) static_cast<uint8_t>(rgb & kRed5)
#define G16(rgb) static_cast<uint8_t>((rgb & kGreen6) >> 5)
#define B16(rgb) static_cast<uint8_t>((rgb & kBlue5) >> 11)
/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
#define R16_32(rgb) (uint8_t)(((rgb & kRed5) << 3) | ((rgb & kRed5) >> 2))
#define G16_32(rgb) (uint8_t)(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
#define B16_32(rgb) (uint8_t)(((rgb & kBlue5) >> 8) | ((rgb & kBlue5) >> 14))
/* Extract red, green, and blue bytes from RGB32 dword. */
#define R32(rgb) static_cast<uint8_t>(rgb & kRed8)
#define G32(rgb) static_cast<uint8_t>(((rgb & kGreen8) >> 8) & 0xff)
#define B32(rgb) static_cast<uint8_t>(((rgb & kBlue8) >> 16) & 0xff)
/* Build RGB565 word from red, green, and blue bytes. */
#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(b) << 6) | g) << 5) | r)
/* Build RGB32 dword from red, green, and blue bytes. */
#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(b) << 8) | g) << 8) | r)
#else // __BYTE_ORDER
/* Extract red, green, and blue bytes from RGB565 word. */
#define R16(rgb) static_cast<uint8_t>((rgb & kRed5) >> 11)
#define G16(rgb) static_cast<uint8_t>((rgb & kGreen6) >> 5)
#define B16(rgb) static_cast<uint8_t>(rgb & kBlue5)
/* Make 8 bits red, green, and blue, extracted from RGB565 word. */
#define R16_32(rgb) (uint8_t)(((rgb & kRed5) >> 8) | ((rgb & kRed5) >> 14))
#define G16_32(rgb) (uint8_t)(((rgb & kGreen6) >> 3) | ((rgb & kGreen6) >> 9))
#define B16_32(rgb) (uint8_t)(((rgb & kBlue5) << 3) | ((rgb & kBlue5) >> 2))
/* Extract red, green, and blue bytes from RGB32 dword. */
#define R32(rgb) static_cast<uint8_t>((rgb & kRed8) >> 16)
#define G32(rgb) static_cast<uint8_t>((rgb & kGreen8) >> 8)
#define B32(rgb) static_cast<uint8_t>(rgb & kBlue8)
/* Build RGB565 word from red, green, and blue bytes. */
#define RGB565(r, g, b) static_cast<uint16_t>((((static_cast<uint16_t>(r) << 6) | g) << 5) | b)
/* Build RGB32 dword from red, green, and blue bytes. */
#define RGB32(r, g, b) static_cast<uint32_t>((((static_cast<uint32_t>(r) << 8) | g) << 8) | b)
#endif // __BYTE_ORDER
/* Converts YCbCr color to RGB565. */ /* An union that simplifies breaking 32 bit RGB into separate R, G, and B colors.
inline uint16_t YCbCrToRGB565(uint8_t y, uint8_t Cb, uint8_t Cr) */
typedef union RGB32_t {
uint32_t color;
struct {
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t r; uint8_t g; uint8_t b; uint8_t a;
#else // __BYTE_ORDER
uint8_t a; uint8_t b; uint8_t g; uint8_t r;
#endif // __BYTE_ORDER
};
} RGB32_t;
/* Clips a value to the unsigned 0-255 range, treating negative values as zero.
*/
static __inline__ int
clamp(int x)
{ {
const uint16_t r = YCbCr2R(y, Cb, Cr) & 0x1f; if (x > 255) return 255;
const uint16_t g = YCbCr2G(y, Cb, Cr) & 0x3f; if (x < 0) return 0;
const uint16_t b = YCbCr2B(y, Cb, Cr) & 0x1f; return x;
}
return b | (g << 5) | (r << 11); /********************************************************************************
* Basics of RGB -> YUV conversion
*******************************************************************************/
/*
* RGB -> YUV conversion macros
*/
#define RGB2Y(r, g, b) (uint8_t)(((66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16)
#define RGB2U(r, g, b) (uint8_t)(((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
#define RGB2V(r, g, b) (uint8_t)(((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128)
/* Converts R8 G8 B8 color to YUV. */
static __inline__ void
R8G8B8ToYUV(uint8_t r, uint8_t g, uint8_t b, uint8_t* y, uint8_t* u, uint8_t* v)
{
*y = RGB2Y((int)r, (int)g, (int)b);
*u = RGB2U((int)r, (int)g, (int)b);
*v = RGB2V((int)r, (int)g, (int)b);
}
/* Converts RGB565 color to YUV. */
static __inline__ void
RGB565ToYUV(uint16_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
{
R8G8B8ToYUV(R16_32(rgb), G16_32(rgb), B16_32(rgb), y, u, v);
}
/* Converts RGB32 color to YUV. */
static __inline__ void
RGB32ToYUV(uint32_t rgb, uint8_t* y, uint8_t* u, uint8_t* v)
{
RGB32_t rgb_c;
rgb_c.color = rgb;
R8G8B8ToYUV(rgb_c.r, rgb_c.g, rgb_c.b, y, u, v);
}
/********************************************************************************
* Basics of YUV -> RGB conversion.
* Note that due to the fact that guest uses RGB only on preview window, and the
* RGB format that is used is RGB565, we can limit YUV -> RGB conversions to
* RGB565 only.
*******************************************************************************/
/*
* YUV -> RGB conversion macros
*/
/* "Optimized" macros that take specialy prepared Y, U, and V values:
* C = Y - 16
* D = U - 128
* E = V - 128
*/
#define YUV2RO(C, D, E) clamp((298 * (C) + 409 * (E) + 128) >> 8)
#define YUV2GO(C, D, E) clamp((298 * (C) - 100 * (D) - 208 * (E) + 128) >> 8)
#define YUV2BO(C, D, E) clamp((298 * (C) + 516 * (D) + 128) >> 8)
/*
* Main macros that take the original Y, U, and V values
*/
#define YUV2R(y, u, v) clamp((298 * ((y)-16) + 409 * ((v)-128) + 128) >> 8)
#define YUV2G(y, u, v) clamp((298 * ((y)-16) - 100 * ((u)-128) - 208 * ((v)-128) + 128) >> 8)
#define YUV2B(y, u, v) clamp((298 * ((y)-16) + 516 * ((u)-128) + 128) >> 8)
/* Converts YUV color to RGB565. */
static __inline__ uint16_t
YUVToRGB565(int y, int u, int v)
{
/* Calculate C, D, and E values for the optimized macro. */
y -= 16; u -= 128; v -= 128;
const uint16_t r = (YUV2RO(y,u,v) >> 3) & 0x1f;
const uint16_t g = (YUV2GO(y,u,v) >> 2) & 0x3f;
const uint16_t b = (YUV2BO(y,u,v) >> 3) & 0x1f;
return RGB565(r, g, b);
}
/* Converts YUV color to RGB32. */
static __inline__ uint32_t
YUVToRGB32(int y, int u, int v)
{
/* Calculate C, D, and E values for the optimized macro. */
y -= 16; u -= 128; v -= 128;
RGB32_t rgb;
rgb.r = YUV2RO(y,u,v) & 0xff;
rgb.g = YUV2GO(y,u,v) & 0xff;
rgb.b = YUV2BO(y,u,v) & 0xff;
return rgb.color;
} }
/* YCbCr pixel descriptor. */ /* YCbCr pixel descriptor. */
struct YCbCrPixel { struct YUVPixel {
uint8_t Y; uint8_t Y;
uint8_t Cb; uint8_t Cb;
uint8_t Cr; uint8_t Cr;
inline YCbCrPixel() inline YUVPixel()
: Y(0), Cb(0), Cr(0) : Y(0), Cb(0), Cr(0)
{ {
} }
inline explicit YCbCrPixel(uint16_t rgb565) inline explicit YUVPixel(uint16_t rgb565)
{ {
RGB565ToYCbCr(rgb565, &Y, &Cb, &Cr); RGB565ToYUV(rgb565, &Y, &Cb, &Cr);
}
inline explicit YUVPixel(uint32_t rgb32)
{
RGB32ToYUV(rgb32, &Y, &Cb, &Cr);
} }
inline void get(uint8_t* pY, uint8_t* pCb, uint8_t* pCr) const inline void get(uint8_t* pY, uint8_t* pCb, uint8_t* pCr) const
@@ -130,7 +261,14 @@ struct YCbCrPixel {
*/ */
void YV12ToRGB565(const void* yv12, void* rgb, int width, int height); void YV12ToRGB565(const void* yv12, void* rgb, int width, int height);
/* Converts an YV12 framebuffer to RGB32 framebuffer.
* Param:
* yv12 - YV12 framebuffer.
* rgb - RGB32 framebuffer.
* width, height - Dimensions for both framebuffers.
*/
void YV12ToRGB32(const void* yv12, void* rgb, int width, int height);
}; /* namespace android */ }; /* namespace android */
#endif /* HW_EMULATOR_CAMERA_CONVERTERS_H */ #endif /* HW_EMULATOR_CAMERA_CONVERTERS_H */

View File

@@ -90,17 +90,12 @@ status_t EmulatedCamera::Initialize()
{ {
LOGV("%s", __FUNCTION__); LOGV("%s", __FUNCTION__);
/* Emulated camera is facing back. */
parameters_.set(EmulatedCamera::FACING_KEY, EmulatedCamera::FACING_BACK);
/* Portrait orientation. */
parameters_.set(EmulatedCamera::ORIENTATION_KEY, 90);
/* /*
* Fake required parameters. * Fake required parameters.
*/ */
/* Only RGBX are supported by the framework for preview window in the emulator! */ /* Only RGBX are supported by the framework for preview window in the emulator! */
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS, CameraParameters::PIXEL_FORMAT_RGB565); parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS, CameraParameters::PIXEL_FORMAT_RGBA8888);
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, "60,50,25,15,10"); parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, "60,50,25,15,10");
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(10,60)"); parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(10,60)");
parameters_.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "10,60"); parameters_.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "10,60");
@@ -115,7 +110,25 @@ status_t EmulatedCamera::Initialize()
parameters_.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "54.8"); parameters_.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "54.8");
parameters_.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "42.5"); parameters_.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "42.5");
parameters_.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90"); parameters_.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90");
parameters_.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGB565);
/* Only RGB formats are supported by preview window in emulator. */
parameters_.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGBA8888);
/* We don't relay on the actual frame rates supported by the camera device,
* since we will emulate them through timeouts in the emulated camera device
* worker thread. */
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES,
"30,24,20,15,10,5");
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(5,30)");
parameters_.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "5,30");
parameters_.setPreviewFrameRate(24);
/* Only PIXEL_FORMAT_YUV420P is accepted by camera framework in emulator! */
parameters_.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
CameraParameters::PIXEL_FORMAT_YUV420P);
parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
CameraParameters::PIXEL_FORMAT_YUV420P);
parameters_.setPictureFormat(CameraParameters::PIXEL_FORMAT_YUV420P);
/* /*
* Not supported features * Not supported features
@@ -504,10 +517,10 @@ status_t EmulatedCamera::StartCamera()
return EINVAL; return EINVAL;
} }
uint32_t org_fmt; uint32_t org_fmt;
if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGB565) == 0) { if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
org_fmt = V4L2_PIX_FMT_RGB565;
} else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
org_fmt = V4L2_PIX_FMT_YVU420; org_fmt = V4L2_PIX_FMT_YVU420;
} else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
org_fmt = V4L2_PIX_FMT_RGB32;
} else { } else {
LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt); LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
return EINVAL; return EINVAL;

View File

@@ -50,16 +50,6 @@ private:
/* Logs an execution of a routine / method. */ /* Logs an execution of a routine / method. */
#define LOGRE() HWERoutineTracker hwertracker_##__LINE__(__FUNCTION__) #define LOGRE() HWERoutineTracker hwertracker_##__LINE__(__FUNCTION__)
static __inline__ void Sleep(int millisec)
{
if (millisec != 0) {
timeval to;
to.tv_sec = millisec / 1000;
to.tv_usec = (millisec % 1000) * 1000;
select(0, NULL, NULL, NULL, &to);
}
}
/* /*
* min / max macros * min / max macros
*/ */

View File

@@ -34,6 +34,7 @@ namespace android {
EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal) EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal)
: object_lock_(), : object_lock_(),
timestamp_(0),
camera_hal_(camera_hal), camera_hal_(camera_hal),
current_frame_(NULL), current_frame_(NULL),
state_(ECDS_CONSTRUCTED) state_(ECDS_CONSTRUCTED)
@@ -98,7 +99,7 @@ status_t EmulatedCameraDevice::StartCapturing(int width,
total_pixels_ = width * height; total_pixels_ = width * height;
/* Allocate framebuffer. */ /* Allocate framebuffer. */
current_frame_ = new uint8_t[GetFrameBufferSize()]; current_frame_ = new uint8_t[framebuffer_size_];
if (current_frame_ == NULL) { if (current_frame_ == NULL) {
LOGE("%s: Unable to allocate framebuffer", __FUNCTION__); LOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
return ENOMEM; return ENOMEM;
@@ -150,7 +151,7 @@ status_t EmulatedCameraDevice::GetCurrentFrame(void* buffer)
return EINVAL; return EINVAL;
} }
memcpy(buffer, current_frame_, GetFrameBufferSize()); memcpy(buffer, current_frame_, framebuffer_size_);
return NO_ERROR; return NO_ERROR;
} }
@@ -168,7 +169,7 @@ status_t EmulatedCameraDevice::GetCurrentPreviewFrame(void* buffer)
/* In emulation the framebuffer is never RGB. */ /* In emulation the framebuffer is never RGB. */
switch (pixel_format_) { switch (pixel_format_) {
case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YVU420:
YV12ToRGB565(current_frame_, buffer, frame_width_, frame_height_); YV12ToRGB32(current_frame_, buffer, frame_width_, frame_height_);
return NO_ERROR; return NO_ERROR;
default: default:

View File

@@ -156,7 +156,7 @@ public:
* must be large enough to contain the entire preview frame, as defined * must be large enough to contain the entire preview frame, as defined
* by frame's width, height, and preview pixel format. Note also, that * by frame's width, height, and preview pixel format. Note also, that
* due to the the limitations of the camera framework in emulator, the * due to the the limitations of the camera framework in emulator, the
* preview frame is always formatted with RGB565. * preview frame is always formatted with RGBA8888.
*/ */
virtual status_t GetCurrentPreviewFrame(void* buffer); virtual status_t GetCurrentPreviewFrame(void* buffer);
@@ -380,6 +380,9 @@ protected:
/* Worker thread that is used in frame capturing. */ /* Worker thread that is used in frame capturing. */
sp<WorkerThread> worker_thread_; sp<WorkerThread> worker_thread_;
/* Timestamp of the current frame. */
nsecs_t timestamp_;
/* Emulated camera object containing this instance. */ /* Emulated camera object containing this instance. */
EmulatedCamera* camera_hal_; EmulatedCamera* camera_hal_;

View File

@@ -22,6 +22,7 @@
#define LOG_NDEBUG 0 #define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_Factory" #define LOG_TAG "EmulatedCamera_Factory"
#include <cutils/log.h> #include <cutils/log.h>
#include "emulated_qemu_camera.h"
#include "emulated_fake_camera.h" #include "emulated_fake_camera.h"
#include "emulated_camera_factory.h" #include "emulated_camera_factory.h"
@@ -35,49 +36,56 @@ android::EmulatedCameraFactory _emulated_camera_factory;
namespace android { namespace android {
EmulatedCameraFactory::EmulatedCameraFactory() EmulatedCameraFactory::EmulatedCameraFactory()
: emulated_cameras_(NULL), : qemu_client_(),
emulated_cameras_(NULL),
emulated_camera_num_(0), emulated_camera_num_(0),
fake_camera_id_(-1), fake_camera_id_(-1),
constructed_ok_(false) constructed_ok_(false)
{ {
/* Obtain number of 'qemud' cameras from the emulator. */ /* If qemu camera emulation is on, try to connect to the factory service in
const int qemud_num = GetQemudCameraNumber(); * the emulator. */
if (qemud_num < 0) { if (IsQemuCameraEmulationOn() && qemu_client_.Connect(NULL) == NO_ERROR) {
return; /* Connection has succeeded. Create emulated cameras for each camera
* device, reported by the service. */
CreateQemuCameras();
} }
LOGI("Emulating %d QEMUD cameras.", qemud_num);
/* ID fake camera with the number of 'qemud' cameras. */ if (IsFakeCameraEmulationOn()) {
fake_camera_id_ = qemud_num; /* ID fake camera with the number of created 'qemud' cameras. */
LOGI("Fake camera ID is %d", fake_camera_id_); fake_camera_id_ = emulated_camera_num_;
emulated_camera_num_++;
emulated_camera_num_ = qemud_num + 1; // Including the 'fake' camera. /* Make sure that array is allocated (in case there were no 'qemu'
LOGI("%d cameras are being emulated", emulated_camera_num_); * cameras created. */
if (emulated_cameras_ == NULL) {
/* Allocate the array for emulated camera instances. */
emulated_cameras_ = new EmulatedCamera*[emulated_camera_num_]; emulated_cameras_ = new EmulatedCamera*[emulated_camera_num_];
if (emulated_cameras_ == NULL) { if (emulated_cameras_ == NULL) {
LOGE("%s: Unable to allocate emulated camera array for %d entries", LOGE("%s: Unable to allocate emulated camera array for %d entries",
__FUNCTION__, emulated_camera_num_); __FUNCTION__, emulated_camera_num_);
return; return;
} }
memset(emulated_cameras_, 0, emulated_camera_num_ * sizeof(EmulatedCamera*));
/* Setup the 'qemud' cameras. */
if (!CreateQemudCameras(qemud_num)) {
return;
} }
/* Create, and initialize the fake camera */ /* Create, and initialize the fake camera */
emulated_cameras_[fake_camera_id_] = emulated_cameras_[fake_camera_id_] =
new EmulatedFakeCamera(fake_camera_id_, &HAL_MODULE_INFO_SYM.common); new EmulatedFakeCamera(fake_camera_id_, &HAL_MODULE_INFO_SYM.common);
if (emulated_cameras_[fake_camera_id_] == NULL) { if (emulated_cameras_[fake_camera_id_] != NULL) {
LOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
return;
}
if (emulated_cameras_[fake_camera_id_]->Initialize() != NO_ERROR) { if (emulated_cameras_[fake_camera_id_]->Initialize() != NO_ERROR) {
return; delete emulated_cameras_[fake_camera_id_];
emulated_cameras_--;
fake_camera_id_ = -1;
} }
} else {
emulated_cameras_--;
fake_camera_id_ = -1;
LOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
}
}
LOGV("%d cameras are being emulated. Fake camera ID is %d",
emulated_camera_num_, fake_camera_id_);
constructed_ok_ = true; constructed_ok_ = true;
} }
@@ -177,15 +185,121 @@ int EmulatedCameraFactory::get_camera_info(int camera_id,
* Internal API * Internal API
*******************************************************************************/ *******************************************************************************/
int EmulatedCameraFactory::GetQemudCameraNumber() /*
* Camera information tokens passed in response to the "list" factory query.
*/
/* Device name token. */
static const char _list_name_token[] = "name=";
/* Frame dimensions token. */
static const char _list_dims_token[] = "framedims=";
void EmulatedCameraFactory::CreateQemuCameras()
{ {
// TODO: Implement! /* Obtain camera list. */
return 0; char* camera_list = NULL;
status_t res = qemu_client_.ListCameras(&camera_list);
/* Empty list, or list containing just an EOL means that there were no
* connected cameras found. */
if (res != NO_ERROR || camera_list == NULL || *camera_list == '\0' ||
*camera_list == '\n') {
if (camera_list != NULL) {
free(camera_list);
}
return;
}
/*
* Calculate number of connected cameras. Number of EOLs in the camera list
* is the number of the connected cameras.
*/
int num = 0;
const char* eol = strchr(camera_list, '\n');
while (eol != NULL) {
num++;
eol = strchr(eol + 1, '\n');
}
/* Allocate the array for emulated camera instances. Note that we allocate
* one more entry for the fake camera emulation. */
emulated_cameras_ = new EmulatedCamera*[num + 1];
if (emulated_cameras_ == NULL) {
LOGE("%s: Unable to allocate emulated camera array for %d entries",
__FUNCTION__, num + 1);
free(camera_list);
return;
}
memset(emulated_cameras_, 0, sizeof(EmulatedCamera*) * (num + 1));
/*
* Iterate the list, creating, and initializin emulated qemu cameras for each
* entry (line) in the list.
*/
int index = 0;
char* cur_entry = camera_list;
while (cur_entry != NULL && *cur_entry != '\0' && index < num) {
/* Find the end of the current camera entry, and terminate it with zero
* for simpler string manipulation. */
char* next_entry = strchr(cur_entry, '\n');
if (next_entry != NULL) {
*next_entry = '\0';
next_entry++; // Start of the next entry.
}
/* Find 'name', and 'framedims' tokens that are required here. */
char* name_start = strstr(cur_entry, _list_name_token);
char* dim_start = strstr(cur_entry, _list_dims_token);
if (name_start != NULL && dim_start != NULL) {
/* Advance to the token values. */
name_start += strlen(_list_name_token);
dim_start += strlen(_list_dims_token);
/* Terminate token values with zero. */
char* s = strchr(name_start, ' ');
if (s != NULL) {
*s = '\0';
}
s = strchr(dim_start, ' ');
if (s != NULL) {
*s = '\0';
}
/* Create and initialize qemu camera. */
EmulatedQemuCamera* qemu_cam =
new EmulatedQemuCamera(index, &HAL_MODULE_INFO_SYM.common);
if (NULL != qemu_cam) {
res = qemu_cam->Initialize(name_start, dim_start);
if (res == NO_ERROR) {
emulated_cameras_[index] = qemu_cam;
index++;
} else {
delete qemu_cam;
}
} else {
LOGE("%s: Unable to instantiate EmulatedQemuCamera",
__FUNCTION__);
}
} else {
LOGW("%s: Bad camera information: %s", __FUNCTION__, cur_entry);
}
cur_entry = next_entry;
}
emulated_camera_num_ = index;
} }
bool EmulatedCameraFactory::CreateQemudCameras(int num) bool EmulatedCameraFactory::IsQemuCameraEmulationOn()
{ {
// TODO: Implement! /* TODO: Have a boot property that controls that! */
return true;
}
bool EmulatedCameraFactory::IsFakeCameraEmulationOn()
{
/* TODO: Have a boot property that controls that! */
return true; return true;
} }

View File

@@ -18,6 +18,7 @@
#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H #define HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
#include "emulated_camera.h" #include "emulated_camera.h"
#include "QemuClient.h"
namespace android { namespace android {
@@ -93,6 +94,30 @@ private:
***************************************************************************/ ***************************************************************************/
public: public:
/* Gets fake camera facing. */
int GetFakeCameraFacing() {
/* TODO: Have a boot property that controls that. */
return CAMERA_FACING_BACK;
}
/* Gets fake camera orientation. */
int GetFakeCameraOrientation() {
/* TODO: Have a boot property that controls that. */
return 90;
}
/* Gets qemu camera facing. */
int GetQemuCameraFacing() {
/* TODO: Have a boot property that controls that. */
return CAMERA_FACING_FRONT;
}
/* Gets qemu camera orientation. */
int GetQemuCameraOrientation() {
/* TODO: Have a boot property that controls that. */
return 270;
}
/* Gets number of emulated cameras. /* Gets number of emulated cameras.
*/ */
int emulated_camera_num() const { int emulated_camera_num() const {
@@ -110,30 +135,27 @@ public:
***************************************************************************/ ***************************************************************************/
private: private:
/* Gets number of cameras that are available via 'camera' service in the
* emulator.
* Return:
* Number of 'qemud' cameras reported by the 'camera' service, or -1 on
* failure.
*/
int GetQemudCameraNumber();
/* Populates emulated cameras array with cameras that are available via /* Populates emulated cameras array with cameras that are available via
* 'camera' service in the emulator. For each such camera and instance of * 'camera' service in the emulator. For each such camera and instance of
* the EmulatedCameraQemud will be created and added to the emulated_cameras_ * the EmulatedCameraQemud will be created and added to the emulated_cameras_
* array. * array.
* Param:
* num - Number of cameras returned by GetQemudCameraNumber method.
* Return:
* true on success, or false on failure.
*/ */
bool CreateQemudCameras(int num); void CreateQemuCameras();
/* Checks if qemu camera emulation is on. */
bool IsQemuCameraEmulationOn();
/* Checks if fake camera emulation is on. */
bool IsFakeCameraEmulationOn();
/**************************************************************************** /****************************************************************************
* Data members. * Data members.
***************************************************************************/ ***************************************************************************/
private: private:
/* Connection to the camera service in the emulator. */
FactoryQemuClient qemu_client_;
/* Array of cameras available for the emulation. */ /* Array of cameras available for the emulation. */
EmulatedCamera** emulated_cameras_; EmulatedCamera** emulated_cameras_;

View File

@@ -23,6 +23,7 @@
#define LOG_TAG "EmulatedCamera_FakeCamera" #define LOG_TAG "EmulatedCamera_FakeCamera"
#include <cutils/log.h> #include <cutils/log.h>
#include "emulated_fake_camera.h" #include "emulated_fake_camera.h"
#include "emulated_camera_factory.h"
namespace android { namespace android {
@@ -49,6 +50,15 @@ status_t EmulatedFakeCamera::Initialize()
return res; return res;
} }
const char* facing = EmulatedCamera::FACING_BACK;
if (_emulated_camera_factory.GetFakeCameraOrientation() == CAMERA_FACING_FRONT) {
facing = EmulatedCamera::FACING_FRONT;
}
parameters_.set(EmulatedCamera::FACING_KEY, facing);
parameters_.set(EmulatedCamera::ORIENTATION_KEY,
_emulated_camera_factory.GetFakeCameraOrientation());
res = EmulatedCamera::Initialize(); res = EmulatedCamera::Initialize();
if (res != NO_ERROR) { if (res != NO_ERROR) {
return res; return res;
@@ -60,15 +70,8 @@ status_t EmulatedFakeCamera::Initialize()
parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "640x480"); parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "640x480");
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "640x480"); parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "640x480");
parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
CameraParameters::PIXEL_FORMAT_YUV420P);
parameters_.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
CameraParameters::PIXEL_FORMAT_YUV420P);
parameters_.setPreviewFrameRate(25);
parameters_.setPreviewSize(640, 480); parameters_.setPreviewSize(640, 480);
parameters_.setPictureSize(640, 480); parameters_.setPictureSize(640, 480);
parameters_.setPictureFormat(CameraParameters::PIXEL_FORMAT_YUV420P);
return NO_ERROR; return NO_ERROR;
} }

View File

@@ -29,12 +29,11 @@ namespace android {
EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal) EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal)
: EmulatedCameraDevice(camera_hal), : EmulatedCameraDevice(camera_hal),
last_redrawn_(0), black_YCbCr_(kBlack32),
black_YCbCr_(0), white_YCbCr_(kWhite32),
white_YCbCr_(0xffff), red_YCbCr_(kRed8),
red_YCbCr_(kRed), green_YCbCr_(kGreen8),
green_YCbCr_(kGreen), blue_YCbCr_(kBlue8),
blue_YCbCr_(kBlue),
check_x_(0), check_x_(0),
check_y_(0), check_y_(0),
counter_(0) counter_(0)
@@ -117,7 +116,6 @@ status_t EmulatedFakeCameraDevice::StopCamera()
{ {
LOGV("%s", __FUNCTION__); LOGV("%s", __FUNCTION__);
Mutex::Autolock locker(&object_lock_);
if (!IsCapturing()) { if (!IsCapturing()) {
LOGW("%s: Fake camera device is not capturing.", __FUNCTION__); LOGW("%s: Fake camera device is not capturing.", __FUNCTION__);
return NO_ERROR; return NO_ERROR;
@@ -147,10 +145,7 @@ bool EmulatedFakeCameraDevice::InWorkerThread()
} }
/* Lets see if we need to generate a new frame. */ /* Lets see if we need to generate a new frame. */
timeval cur_time; if ((systemTime(SYSTEM_TIME_MONOTONIC) - timestamp_) >= redraw_after_) {
gettimeofday(&cur_time, NULL);
const uint64_t cur_mks = cur_time.tv_sec * 1000000LL + cur_time.tv_usec;
if ((cur_mks - last_redrawn_) >= redraw_after_) {
/* /*
* Time to generate a new frame. * Time to generate a new frame.
*/ */
@@ -167,12 +162,11 @@ bool EmulatedFakeCameraDevice::InWorkerThread()
DrawSquare(x * size / 32, y * size / 32, (size * 5) >> 1, DrawSquare(x * size / 32, y * size / 32, (size * 5) >> 1,
(counter_ & 0x100) ? &red_YCbCr_ : &green_YCbCr_); (counter_ & 0x100) ? &red_YCbCr_ : &green_YCbCr_);
counter_++; counter_++;
last_redrawn_ = cur_mks;
} }
/* Notify the camera HAL about new frame. */ /* Timestamp the current frame, and notify the camera HAL about new frame. */
camera_hal_->OnNextFrameAvailable(current_frame_, timestamp_ = systemTime(SYSTEM_TIME_MONOTONIC);
systemTime(SYSTEM_TIME_MONOTONIC), this); camera_hal_->OnNextFrameAvailable(current_frame_, timestamp_, this);
return true; return true;
} }
@@ -235,7 +229,7 @@ void EmulatedFakeCameraDevice::DrawCheckerboard()
void EmulatedFakeCameraDevice::DrawSquare(int x, void EmulatedFakeCameraDevice::DrawSquare(int x,
int y, int y,
int size, int size,
const YCbCrPixel* color) const YUVPixel* color)
{ {
const int half_x = x / 2; const int half_x = x / 2;
const int square_xstop = min(frame_width_, x+size); const int square_xstop = min(frame_width_, x+size);

View File

@@ -103,25 +103,22 @@ private:
* size - Size of the square's side. * size - Size of the square's side.
* color - Square's color. * color - Square's color.
*/ */
void DrawSquare(int x, int y, int size, const YCbCrPixel* color); void DrawSquare(int x, int y, int size, const YUVPixel* color);
/**************************************************************************** /****************************************************************************
* Fake camera device data members * Fake camera device data members
***************************************************************************/ ***************************************************************************/
private: private:
/* Last time (absoulte microsec) when the checker board has been redrawn. */
uint64_t last_redrawn_;
/* /*
* Pixel colors in YCbCr format used when drawing the checker board. * Pixel colors in YCbCr format used when drawing the checker board.
*/ */
YCbCrPixel black_YCbCr_; YUVPixel black_YCbCr_;
YCbCrPixel white_YCbCr_; YUVPixel white_YCbCr_;
YCbCrPixel red_YCbCr_; YUVPixel red_YCbCr_;
YCbCrPixel green_YCbCr_; YUVPixel green_YCbCr_;
YCbCrPixel blue_YCbCr_; YUVPixel blue_YCbCr_;
/* /*
* Drawing related stuff * Drawing related stuff
@@ -133,12 +130,12 @@ private:
int half_width_; int half_width_;
/* Emulated FPS (frames per second). /* Emulated FPS (frames per second).
* We will emulate the "semi-high end" 50 FPS. */ * We will emulate 50 FPS. */
static const int emulated_fps_ = 50; static const int emulated_fps_ = 50;
/* Defines time (in microseconds) between redrawing the checker board. /* Defines time (in nanoseconds) between redrawing the checker board.
* We will redraw the checker board every 15 milliseconds. */ * We will redraw the checker board every 15 milliseconds. */
static const uint32_t redraw_after_ = 15000; static const nsecs_t redraw_after_ = 15000000LL;
}; };
}; /* namespace android */ }; /* namespace android */

View File

@@ -0,0 +1,120 @@
/*
* 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 a class EmulatedQemuCamera that encapsulates
* functionality of an emulated camera connected to the host.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_QemuCamera"
#include <cutils/log.h>
#include "emulated_qemu_camera.h"
#include "emulated_camera_factory.h"
namespace android {
EmulatedQemuCamera::EmulatedQemuCamera(int cameraId, struct hw_module_t* module)
: EmulatedCamera(cameraId, module),
qemu_camera_dev_(this)
{
}
EmulatedQemuCamera::~EmulatedQemuCamera()
{
}
/****************************************************************************
* EmulatedCamera virtual overrides.
***************************************************************************/
status_t EmulatedQemuCamera::Initialize(const char* device_name,
const char* frame_dims)
{
/* Save dimensions. */
frame_dims_ = frame_dims;
/* Initialize camera device. */
status_t res = qemu_camera_dev_.Initialize(device_name);
if (res != NO_ERROR) {
return res;
}
/* Initialize base class. */
res = EmulatedCamera::Initialize();
if (res != NO_ERROR) {
return res;
}
/*
* Set customizable parameters.
*/
const char* facing = EmulatedCamera::FACING_FRONT;
if (_emulated_camera_factory.GetQemuCameraOrientation() == CAMERA_FACING_BACK) {
facing = EmulatedCamera::FACING_BACK;
}
parameters_.set(EmulatedCamera::FACING_KEY, facing);
parameters_.set(EmulatedCamera::ORIENTATION_KEY,
_emulated_camera_factory.GetQemuCameraOrientation());
parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, frame_dims);
parameters_.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, frame_dims);
/*
* Use first dimension reported by the device to set current preview and
* picture sizes.
*/
char first_dim[128];
/* Dimensions are separated with ',' */
const char* c = strchr(frame_dims, ',');
if (c == NULL) {
strncpy(first_dim, frame_dims, sizeof(first_dim));
first_dim[sizeof(first_dim)-1] = '\0';
} else if ((c - frame_dims) < sizeof(first_dim)) {
memcpy(first_dim, frame_dims, c - frame_dims);
first_dim[c - frame_dims] = '\0';
} else {
memcpy(first_dim, frame_dims, sizeof(first_dim));
first_dim[sizeof(first_dim)-1] = '\0';
}
/* Width and height are separated with 'x' */
char* sep = strchr(first_dim, 'x');
if (sep == NULL) {
LOGE("%s: Invalid first dimension format in %s",
__FUNCTION__, frame_dims);
return EINVAL;
}
*sep = '\0';
const int x = atoi(first_dim);
const int y = atoi(sep + 1);
parameters_.setPreviewSize(x, y);
parameters_.setPictureSize(x, y);
LOGV("%s: Qemu camera %s is initialized. Current frame is %dx%d",
__FUNCTION__, device_name, x, y);
return NO_ERROR;
}
EmulatedCameraDevice* EmulatedQemuCamera::GetCameraDevice()
{
return &qemu_camera_dev_;
}
}; /* namespace android */

View File

@@ -0,0 +1,75 @@
/*
* 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.
*/
#ifndef HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H
/*
* Contains declaration of a class EmulatedQemuCamera that encapsulates
* functionality of an emulated camera connected to the host.
*/
#include "emulated_camera.h"
#include "emulated_qemu_camera_device.h"
namespace android {
/* Encapsulates functionality of an emulated camera connected to the host.
*/
class EmulatedQemuCamera : public EmulatedCamera {
public:
/* Constructs EmulatedQemuCamera instance. */
EmulatedQemuCamera(int cameraId, struct hw_module_t* module);
/* Destructs EmulatedQemuCamera instance. */
~EmulatedQemuCamera();
/****************************************************************************
* EmulatedCamera virtual overrides.
***************************************************************************/
public:
/* Initializes EmulatedQemuCamera instance.
* The contained EmulatedQemuCameraDevice will be initialized in this method.
* Return:
* NO_ERROR on success, or an appropriate error status.
*/
status_t Initialize(const char* device_name, const char* frame_dims);
/****************************************************************************
* EmulatedCamera abstract API implementation.
***************************************************************************/
protected:
/* Gets emulated camera device ised by this instance of the emulated camera.
*/
EmulatedCameraDevice* GetCameraDevice();
/****************************************************************************
* Data memebers.
***************************************************************************/
protected:
/* Contained qemu camera device object. */
EmulatedQemuCameraDevice qemu_camera_dev_;
/* Supported frame dimensions reported by the camera device. */
String8 frame_dims_;
};
}; /* namespace android */
#endif /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_H */

View File

@@ -0,0 +1,243 @@
/*
* 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 a class EmulatedQemuCameraDevice that encapsulates
* an emulated camera device connected to the host.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_QemuDevice"
#include <cutils/log.h>
#include "emulated_qemu_camera.h"
#include "emulated_qemu_camera_device.h"
namespace android {
EmulatedQemuCameraDevice::EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal)
: EmulatedCameraDevice(camera_hal),
qemu_client_(),
preview_frame_(NULL)
{
}
EmulatedQemuCameraDevice::~EmulatedQemuCameraDevice()
{
if (preview_frame_ != NULL) {
delete[] preview_frame_;
}
}
/****************************************************************************
* Public API
***************************************************************************/
status_t EmulatedQemuCameraDevice::Initialize(const char* device_name)
{
/* Connect to the service. */
char connect_str[256];
snprintf(connect_str, sizeof(connect_str), "name=%s", device_name);
status_t res = qemu_client_.Connect(connect_str);
if (res != NO_ERROR) {
return res;
}
/* Initialize base class. */
res = EmulatedCameraDevice::Initialize();
if (res == NO_ERROR) {
LOGV("%s: Connected to the emulated camera service '%s'",
__FUNCTION__, device_name);
device_name_ = device_name;
} else {
qemu_client_.Disconnect();
}
return res;
}
/****************************************************************************
* Emulated camera device abstract interface implementation.
***************************************************************************/
status_t EmulatedQemuCameraDevice::Connect()
{
LOGV("%s", __FUNCTION__);
Mutex::Autolock locker(&object_lock_);
if (!IsInitialized()) {
LOGE("%s: Qemu camera device is not initialized.", __FUNCTION__);
return EINVAL;
}
if (IsConnected()) {
LOGW("%s: Qemu camera device is already connected.", __FUNCTION__);
return NO_ERROR;
}
const status_t res = qemu_client_.QueryConnect();
if (res == NO_ERROR) {
LOGV("%s: Connected", __FUNCTION__);
state_ = ECDS_CONNECTED;
} else {
LOGE("%s: Connection failed", __FUNCTION__);
}
return res;
}
status_t EmulatedQemuCameraDevice::Disconnect()
{
LOGV("%s", __FUNCTION__);
Mutex::Autolock locker(&object_lock_);
if (!IsConnected()) {
LOGW("%s: Qemu camera device is already disconnected.", __FUNCTION__);
return NO_ERROR;
}
if (IsCapturing()) {
LOGE("%s: Cannot disconnect while in the capturing state.", __FUNCTION__);
return EINVAL;
}
const status_t res = qemu_client_.QueryDisconnect();
if (res == NO_ERROR) {
LOGV("%s: Disonnected", __FUNCTION__);
state_ = ECDS_INITIALIZED;
} else {
LOGE("%s: Disconnection failed", __FUNCTION__);
}
return res;
}
status_t EmulatedQemuCameraDevice::StartCamera()
{
LOGV("%s", __FUNCTION__);
Mutex::Autolock locker(&object_lock_);
if (!IsConnected()) {
LOGE("%s: Qemu camera device is not connected.", __FUNCTION__);
return EINVAL;
}
if (IsCapturing()) {
LOGW("%s: Qemu camera device is already capturing.", __FUNCTION__);
return NO_ERROR;
}
/* Allocate preview frame buffer. */
/* TODO: Watch out for preview format changes! At this point we implement
* RGB32 only.*/
preview_frame_ = new uint16_t[total_pixels_ * 4];
if (preview_frame_ == NULL) {
LOGE("%s: Unable to allocate %d bytes for preview frame",
__FUNCTION__, total_pixels_ * 4);
return ENOMEM;
}
memset(preview_frame_, 0, total_pixels_ * 4);
/* Start the actual camera device. */
status_t res =
qemu_client_.QueryStart(pixel_format_, frame_width_, frame_height_);
if (res == NO_ERROR) {
/* Start the worker thread. */
res = StartWorkerThread();
if (res == NO_ERROR) {
state_ = ECDS_CAPTURING;
} else {
qemu_client_.QueryStop();
}
} else {
LOGE("%s: Start failed", __FUNCTION__);
}
return res;
}
status_t EmulatedQemuCameraDevice::StopCamera()
{
LOGV("%s", __FUNCTION__);
Mutex::Autolock locker(&object_lock_);
if (!IsCapturing()) {
LOGW("%s: Qemu camera device is not capturing.", __FUNCTION__);
return NO_ERROR;
}
/* Stop the actual camera device. */
status_t res = qemu_client_.QueryStop();
if (res == NO_ERROR) {
/* Stop the worker thread. */
res = StopWorkerThread();
if (res == NO_ERROR) {
if (preview_frame_ == NULL) {
delete[] preview_frame_;
preview_frame_ = NULL;
}
state_ = ECDS_CONNECTED;
LOGV("%s: Stopped", __FUNCTION__);
}
} else {
LOGE("%s: Stop failed", __FUNCTION__);
}
return res;
}
/****************************************************************************
* EmulatedCameraDevice virtual overrides
***************************************************************************/
status_t EmulatedQemuCameraDevice::GetCurrentPreviewFrame(void* buffer)
{
LOGW_IF(preview_frame_ == NULL, "%s: No preview frame", __FUNCTION__);
if (preview_frame_ != NULL) {
memcpy(buffer, preview_frame_, total_pixels_ * 4);
return 0;
} else {
return EmulatedCameraDevice::GetCurrentPreviewFrame(buffer);
}
}
/****************************************************************************
* Worker thread management overrides.
***************************************************************************/
bool EmulatedQemuCameraDevice::InWorkerThread()
{
/* Wait till FPS timeout expires, or thread exit message is received. */
WorkerThread::SelectRes res =
worker_thread()->Select(-1, 1000000 / emulated_fps_);
if (res == WorkerThread::EXIT_THREAD) {
LOGV("%s: Worker thread has been terminated.", __FUNCTION__);
return false;
}
/* Query frames from the service. */
status_t query_res = qemu_client_.QueryFrame(current_frame_, preview_frame_,
framebuffer_size_,
total_pixels_ * 4);
if (query_res == NO_ERROR) {
/* Timestamp the current frame, and notify the camera HAL. */
timestamp_ = systemTime(SYSTEM_TIME_MONOTONIC);
camera_hal_->OnNextFrameAvailable(current_frame_, timestamp_, this);
} else {
LOGE("%s: Unable to get current video frame: %s",
__FUNCTION__, strerror(query_res));
}
return true;
}
}; /* namespace android */

View File

@@ -0,0 +1,120 @@
/*
* 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.
*/
#ifndef HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
#define HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H
/*
* Contains declaration of a class EmulatedQemuCameraDevice that encapsulates
* an emulated camera device connected to the host.
*/
#include "emulated_camera_device.h"
#include "QemuClient.h"
namespace android {
class EmulatedQemuCamera;
/* Encapsulates an emulated camera device connected to the host.
*/
class EmulatedQemuCameraDevice : public EmulatedCameraDevice {
public:
/* Constructs EmulatedQemuCameraDevice instance. */
explicit EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal);
/* Destructs EmulatedQemuCameraDevice instance. */
~EmulatedQemuCameraDevice();
/****************************************************************************
* Public API
***************************************************************************/
public:
/* Initializes EmulatedQemuCameraDevice instance.
* Param:
* device_name - Name of the camera device connected to the host. The name
* that is used here must have been reported by the 'factory' camera
* service when it listed camera devices connected to the host.
* Return:
* NO_ERROR on success, or an appropriate error status.
*/
status_t Initialize(const char* device_name);
/****************************************************************************
* Emulated camera device abstract interface implementation.
* See declarations of these methods in EmulatedCameraDevice class for
* information on each of these methods.
***************************************************************************/
public:
/* Connects to the camera device. */
status_t Connect();
/* Disconnects from the camera device. */
status_t Disconnect();
protected:
/* Starts capturing frames from the camera device. */
status_t StartCamera();
/* Stops capturing frames from the camera device. */
status_t StopCamera();
/****************************************************************************
* EmulatedCameraDevice virtual overrides
***************************************************************************/
public:
/* Gets current preview fame into provided buffer.
* We override this method in order to provide preview frames cached in this
* object.
*/
status_t GetCurrentPreviewFrame(void* buffer);
/****************************************************************************
* Worker thread management overrides.
* See declarations of these methods in EmulatedCameraDevice class for
* information on each of these methods.
***************************************************************************/
protected:
/* Implementation of the worker thread routine. */
bool InWorkerThread();
/****************************************************************************
* Qemu camera device data members
***************************************************************************/
private:
/* Qemu client that is used to communicate with the 'emulated camera' service,
* created for this instance in the emulator. */
CameraQemuClient qemu_client_;
/* Name of the camera device connected to the host. */
String8 device_name_;
/* Current preview framebuffer. */
uint16_t* preview_frame_;
/* Emulated FPS (frames per second).
* We will emulate 50 FPS. */
static const int emulated_fps_ = 50;
};
}; /* namespace android */
#endif /* HW_EMULATOR_CAMERA_EMULATED_QEMU_CAMERA_DEVICE_H */

View File

@@ -129,7 +129,7 @@ void PreviewWindow::OnNextFrameAvailable(const void* frame,
res = preview_window_->set_buffers_geometry(preview_window_, res = preview_window_->set_buffers_geometry(preview_window_,
preview_frame_width_, preview_frame_width_,
preview_frame_height_, preview_frame_height_,
HAL_PIXEL_FORMAT_RGB_565); HAL_PIXEL_FORMAT_RGBA_8888);
if (res != NO_ERROR) { if (res != NO_ERROR) {
LOGE("%s: Error in set_buffers_geometry %d -> %s", LOGE("%s: Error in set_buffers_geometry %d -> %s",
__FUNCTION__, -res, strerror(-res)); __FUNCTION__, -res, strerror(-res));