Implements connection with the emulator
Change-Id: Ie3910a98e927f7b8ea556c403f8ced27e06bcdec
This commit is contained in:
@@ -33,11 +33,14 @@ LOCAL_SRC_FILES := \
|
||||
emulated_camera_factory.cpp \
|
||||
emulated_camera.cpp \
|
||||
emulated_camera_device.cpp \
|
||||
emulated_qemu_camera.cpp \
|
||||
emulated_qemu_camera_device.cpp \
|
||||
emulated_fake_camera.cpp \
|
||||
emulated_fake_camera_device.cpp \
|
||||
converters.cpp \
|
||||
preview_window.cpp \
|
||||
callback_notifier.cpp
|
||||
callback_notifier.cpp \
|
||||
QemuClient.cpp
|
||||
|
||||
ifeq ($(TARGET_PRODUCT),vbox_x86)
|
||||
LOCAL_MODULE := camera.vbox_x86
|
||||
|
||||
521
tools/emulator/system/camera/QemuClient.cpp
Normal file
521
tools/emulator/system/camera/QemuClient.cpp
Normal 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 */
|
||||
418
tools/emulator/system/camera/QemuClient.h
Normal file
418
tools/emulator/system/camera/QemuClient.h
Normal 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 */
|
||||
@@ -28,22 +28,6 @@
|
||||
|
||||
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. */
|
||||
static const char* _camera_messages[] =
|
||||
{
|
||||
@@ -108,8 +92,7 @@ CallbackNotifier::CallbackNotifier()
|
||||
last_frame_(0),
|
||||
frame_after_(0),
|
||||
message_enabler_(0),
|
||||
video_recording_enabled_(false),
|
||||
store_meta_data_in_buffers_(true)
|
||||
video_recording_enabled_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -173,7 +156,7 @@ status_t CallbackNotifier::EnableVideoRecording(int fps)
|
||||
Mutex::Autolock locker(&object_lock_);
|
||||
video_recording_enabled_ = true;
|
||||
last_frame_ = 0;
|
||||
frame_after_ = 1000000 / fps;
|
||||
frame_after_ = 1000000000LL / fps;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -196,25 +179,12 @@ bool CallbackNotifier::IsVideoRecordingEnabled()
|
||||
|
||||
void CallbackNotifier::ReleaseRecordingFrame(const void* opaque)
|
||||
{
|
||||
LOGV("%s: frame = %p", __FUNCTION__, opaque);
|
||||
|
||||
if (opaque != NULL) {
|
||||
const VideoFrameMetadata* meta =
|
||||
reinterpret_cast<const VideoFrameMetadata*>(opaque);
|
||||
if (meta->type == kMetadataBufferTypeCameraSource &&
|
||||
meta->holder != NULL) {
|
||||
meta->holder->release(meta->holder);
|
||||
}
|
||||
}
|
||||
/* We don't really have anything to release here, since we report video
|
||||
* frames by copying them directly to the camera memory. */
|
||||
}
|
||||
|
||||
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 actual frame data with CAMERA_MSG_VIDEO_FRRAME. Return
|
||||
* INVALID_OPERATION to mean metadata is not supported. */
|
||||
@@ -237,7 +207,6 @@ void CallbackNotifier::Cleanup()
|
||||
last_frame_ = 0;
|
||||
frame_after_ = 0;
|
||||
video_recording_enabled_ = false;
|
||||
store_meta_data_in_buffers_ = true;
|
||||
}
|
||||
|
||||
void CallbackNotifier::OnNextFrameAvailable(const void* frame,
|
||||
@@ -248,30 +217,15 @@ void CallbackNotifier::OnNextFrameAvailable(const void* frame,
|
||||
|
||||
if ((message_enabler_ & CAMERA_MSG_VIDEO_FRAME) != 0 &&
|
||||
data_cb_timestamp_ != NULL && video_recording_enabled_ &&
|
||||
IsTimeForNewVideoFrame()) {
|
||||
/* Ready for new video frame. Allocate frame holder. */
|
||||
camera_memory_t* holder =
|
||||
get_memory_(-1, sizeof(VideoFrameMetadata), 1, NULL);
|
||||
if (NULL != holder && NULL != holder->data) {
|
||||
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;
|
||||
IsTimeForNewVideoFrame(timestamp)) {
|
||||
camera_memory_t* cam_buff =
|
||||
get_memory_(-1, camera_dev->GetFrameBufferSize(), 1, NULL);
|
||||
if (NULL != cam_buff && NULL != cam_buff->data) {
|
||||
memcpy(cam_buff->data, frame, camera_dev->GetFrameBufferSize());
|
||||
data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME,
|
||||
holder, 0, cb_opaque_);
|
||||
/* Allocated holder will be released by release_recording_frame
|
||||
* call. */
|
||||
cam_buff, 0, cb_opaque_);
|
||||
} else {
|
||||
holder->data = const_cast<void*>(frame);
|
||||
data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME,
|
||||
holder, 0, cb_opaque_);
|
||||
holder->release(holder);
|
||||
}
|
||||
} else {
|
||||
LOGE("%s: Memory failure", __FUNCTION__);
|
||||
LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,13 +234,10 @@ void CallbackNotifier::OnNextFrameAvailable(const void* frame,
|
||||
* Private API
|
||||
***************************************************************************/
|
||||
|
||||
bool CallbackNotifier::IsTimeForNewVideoFrame()
|
||||
bool CallbackNotifier::IsTimeForNewVideoFrame(nsecs_t timestamp)
|
||||
{
|
||||
timeval cur_time;
|
||||
gettimeofday(&cur_time, NULL);
|
||||
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;
|
||||
if ((timestamp - last_frame_) >= frame_after_) {
|
||||
last_frame_ = timestamp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -149,8 +149,10 @@ public:
|
||||
|
||||
protected:
|
||||
/* Checks if it's time to push new video frame.
|
||||
* Note that this method must be called while object is locked. */
|
||||
bool IsTimeForNewVideoFrame();
|
||||
* Note that this method must be called while object is locked.
|
||||
* Param:
|
||||
* timestamp - Timestamp for the new frame. */
|
||||
bool IsTimeForNewVideoFrame(nsecs_t timestamp);
|
||||
|
||||
/****************************************************************************
|
||||
* Data members
|
||||
@@ -170,21 +172,17 @@ protected:
|
||||
camera_request_memory get_memory_;
|
||||
void* cb_opaque_;
|
||||
|
||||
/* Timestamp (abs. microseconds) when last frame has been delivered to the
|
||||
* framework. */
|
||||
uint64_t last_frame_;
|
||||
/* Timestamp when last frame has been delivered to the framework. */
|
||||
nsecs_t last_frame_;
|
||||
|
||||
/* Video frequency in microseconds. */
|
||||
uint32_t frame_after_;
|
||||
/* Video frequency in nanosec. */
|
||||
nsecs_t frame_after_;
|
||||
|
||||
/* Message enabler. */
|
||||
uint32_t message_enabler_;
|
||||
|
||||
/* Video recording status. */
|
||||
bool video_recording_enabled_;
|
||||
|
||||
/* Status of the metadata buffering. */
|
||||
bool store_meta_data_in_buffers_;
|
||||
};
|
||||
|
||||
}; /* namespace android */
|
||||
|
||||
@@ -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* 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 = YCbCrToRGB565(*Y, nCb, nCr);
|
||||
*rgb_buf = YUVToRGB565(*Y, nCb, nCr);
|
||||
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++;
|
||||
}
|
||||
if (y & 0x1) {
|
||||
|
||||
@@ -17,103 +17,234 @@
|
||||
#ifndef 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.
|
||||
*
|
||||
* 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 {
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
static const int kRed = 0xf800;
|
||||
static const int kGreen = 0x07e0;
|
||||
static const int kBlue = 0x001f;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
static const uint16_t kRed5 = 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;
|
||||
static const double kR1 = 0.587;
|
||||
static const double kR2 = 0.114;
|
||||
static const double kR3 = 0.169;
|
||||
static const double kR4 = 0.331;
|
||||
static const double kR5 = 0.499;
|
||||
static const double kR6 = 0.499;
|
||||
static const double kR7 = 0.418;
|
||||
static const double kR8 = 0.0813;
|
||||
|
||||
#define RGB2Y(R,G,B) static_cast<uint8_t>(kR0*R + kR1*G + kR2*B)
|
||||
#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);
|
||||
}
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
static const uint32_t kRed8 = 0x000000ff;
|
||||
static const uint32_t kGreen8 = 0x0000ff00;
|
||||
static const uint32_t kBlue8 = 0x00ff0000;
|
||||
#else // __BYTE_ORDER
|
||||
static const uint32_t kRed8 = 0x00ff0000;
|
||||
static const uint32_t kGreen8 = 0x0000ff00;
|
||||
static const uint32_t kBlue8 = 0x000000ff;
|
||||
#endif // __BYTE_ORDER
|
||||
static const uint32_t kBlack32 = 0x00000000;
|
||||
static const uint32_t kWhite32 = kRed8 | kGreen8 | kBlue8;
|
||||
|
||||
/*
|
||||
* 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))
|
||||
#define YCbCr2G(Y, Cb, Cr) clamp(Y - kY1*(Cb-128) - kY2*(Cr-128))
|
||||
#define YCbCr2B(Y, Cb, Cr) clamp(Y + kY3*(Cb-128))
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
/* Extract red, green, and blue bytes from RGB565 word. */
|
||||
#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. */
|
||||
inline uint16_t YCbCrToRGB565(uint8_t y, uint8_t Cb, uint8_t Cr)
|
||||
/* An union that simplifies breaking 32 bit RGB into separate R, G, and B colors.
|
||||
*/
|
||||
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;
|
||||
const uint16_t g = YCbCr2G(y, Cb, Cr) & 0x3f;
|
||||
const uint16_t b = YCbCr2B(y, Cb, Cr) & 0x1f;
|
||||
if (x > 255) return 255;
|
||||
if (x < 0) return 0;
|
||||
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. */
|
||||
struct YCbCrPixel {
|
||||
struct YUVPixel {
|
||||
uint8_t Y;
|
||||
uint8_t Cb;
|
||||
uint8_t Cr;
|
||||
|
||||
inline YCbCrPixel()
|
||||
inline YUVPixel()
|
||||
: 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
|
||||
@@ -130,7 +261,14 @@ struct YCbCrPixel {
|
||||
*/
|
||||
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 */
|
||||
|
||||
#endif /* HW_EMULATOR_CAMERA_CONVERTERS_H */
|
||||
|
||||
|
||||
@@ -90,17 +90,12 @@ status_t EmulatedCamera::Initialize()
|
||||
{
|
||||
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.
|
||||
*/
|
||||
|
||||
/* 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_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_VERTICAL_VIEW_ANGLE, "42.5");
|
||||
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
|
||||
@@ -504,10 +517,10 @@ status_t EmulatedCamera::StartCamera()
|
||||
return EINVAL;
|
||||
}
|
||||
uint32_t org_fmt;
|
||||
if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGB565) == 0) {
|
||||
org_fmt = V4L2_PIX_FMT_RGB565;
|
||||
} else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
|
||||
if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
|
||||
org_fmt = V4L2_PIX_FMT_YVU420;
|
||||
} else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
|
||||
org_fmt = V4L2_PIX_FMT_RGB32;
|
||||
} else {
|
||||
LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
|
||||
return EINVAL;
|
||||
|
||||
@@ -50,16 +50,6 @@ private:
|
||||
/* Logs an execution of a routine / method. */
|
||||
#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
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace android {
|
||||
|
||||
EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal)
|
||||
: object_lock_(),
|
||||
timestamp_(0),
|
||||
camera_hal_(camera_hal),
|
||||
current_frame_(NULL),
|
||||
state_(ECDS_CONSTRUCTED)
|
||||
@@ -98,7 +99,7 @@ status_t EmulatedCameraDevice::StartCapturing(int width,
|
||||
total_pixels_ = width * height;
|
||||
|
||||
/* Allocate framebuffer. */
|
||||
current_frame_ = new uint8_t[GetFrameBufferSize()];
|
||||
current_frame_ = new uint8_t[framebuffer_size_];
|
||||
if (current_frame_ == NULL) {
|
||||
LOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
|
||||
return ENOMEM;
|
||||
@@ -150,7 +151,7 @@ status_t EmulatedCameraDevice::GetCurrentFrame(void* buffer)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memcpy(buffer, current_frame_, GetFrameBufferSize());
|
||||
memcpy(buffer, current_frame_, framebuffer_size_);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -168,7 +169,7 @@ status_t EmulatedCameraDevice::GetCurrentPreviewFrame(void* buffer)
|
||||
/* In emulation the framebuffer is never RGB. */
|
||||
switch (pixel_format_) {
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
YV12ToRGB565(current_frame_, buffer, frame_width_, frame_height_);
|
||||
YV12ToRGB32(current_frame_, buffer, frame_width_, frame_height_);
|
||||
return NO_ERROR;
|
||||
|
||||
default:
|
||||
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
* must be large enough to contain the entire preview frame, as defined
|
||||
* by frame's width, height, and preview pixel format. Note also, that
|
||||
* 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);
|
||||
|
||||
@@ -380,6 +380,9 @@ protected:
|
||||
/* Worker thread that is used in frame capturing. */
|
||||
sp<WorkerThread> worker_thread_;
|
||||
|
||||
/* Timestamp of the current frame. */
|
||||
nsecs_t timestamp_;
|
||||
|
||||
/* Emulated camera object containing this instance. */
|
||||
EmulatedCamera* camera_hal_;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "EmulatedCamera_Factory"
|
||||
#include <cutils/log.h>
|
||||
#include "emulated_qemu_camera.h"
|
||||
#include "emulated_fake_camera.h"
|
||||
#include "emulated_camera_factory.h"
|
||||
|
||||
@@ -35,49 +36,56 @@ android::EmulatedCameraFactory _emulated_camera_factory;
|
||||
namespace android {
|
||||
|
||||
EmulatedCameraFactory::EmulatedCameraFactory()
|
||||
: emulated_cameras_(NULL),
|
||||
: qemu_client_(),
|
||||
emulated_cameras_(NULL),
|
||||
emulated_camera_num_(0),
|
||||
fake_camera_id_(-1),
|
||||
constructed_ok_(false)
|
||||
|
||||
{
|
||||
/* Obtain number of 'qemud' cameras from the emulator. */
|
||||
const int qemud_num = GetQemudCameraNumber();
|
||||
if (qemud_num < 0) {
|
||||
return;
|
||||
/* If qemu camera emulation is on, try to connect to the factory service in
|
||||
* the emulator. */
|
||||
if (IsQemuCameraEmulationOn() && qemu_client_.Connect(NULL) == NO_ERROR) {
|
||||
/* 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. */
|
||||
fake_camera_id_ = qemud_num;
|
||||
LOGI("Fake camera ID is %d", fake_camera_id_);
|
||||
if (IsFakeCameraEmulationOn()) {
|
||||
/* ID fake camera with the number of created 'qemud' cameras. */
|
||||
fake_camera_id_ = emulated_camera_num_;
|
||||
emulated_camera_num_++;
|
||||
|
||||
emulated_camera_num_ = qemud_num + 1; // Including the 'fake' camera.
|
||||
LOGI("%d cameras are being emulated", emulated_camera_num_);
|
||||
|
||||
/* Allocate the array for emulated camera instances. */
|
||||
/* Make sure that array is allocated (in case there were no 'qemu'
|
||||
* cameras created. */
|
||||
if (emulated_cameras_ == NULL) {
|
||||
emulated_cameras_ = new EmulatedCamera*[emulated_camera_num_];
|
||||
if (emulated_cameras_ == NULL) {
|
||||
LOGE("%s: Unable to allocate emulated camera array for %d entries",
|
||||
__FUNCTION__, emulated_camera_num_);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setup the 'qemud' cameras. */
|
||||
if (!CreateQemudCameras(qemud_num)) {
|
||||
return;
|
||||
memset(emulated_cameras_, 0, emulated_camera_num_ * sizeof(EmulatedCamera*));
|
||||
}
|
||||
|
||||
/* Create, and initialize the fake camera */
|
||||
emulated_cameras_[fake_camera_id_] =
|
||||
new EmulatedFakeCamera(fake_camera_id_, &HAL_MODULE_INFO_SYM.common);
|
||||
if (emulated_cameras_[fake_camera_id_] == NULL) {
|
||||
LOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
if (emulated_cameras_[fake_camera_id_] != NULL) {
|
||||
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;
|
||||
}
|
||||
@@ -177,15 +185,121 @@ int EmulatedCameraFactory::get_camera_info(int camera_id,
|
||||
* 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!
|
||||
return 0;
|
||||
/* Obtain camera list. */
|
||||
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;
|
||||
}
|
||||
|
||||
bool EmulatedCameraFactory::CreateQemudCameras(int num)
|
||||
/*
|
||||
* 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::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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define HW_EMULATOR_CAMERA_EMULATED_CAMERA_FACTORY_H
|
||||
|
||||
#include "emulated_camera.h"
|
||||
#include "QemuClient.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -93,6 +94,30 @@ private:
|
||||
***************************************************************************/
|
||||
|
||||
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.
|
||||
*/
|
||||
int emulated_camera_num() const {
|
||||
@@ -110,30 +135,27 @@ public:
|
||||
***************************************************************************/
|
||||
|
||||
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
|
||||
* 'camera' service in the emulator. For each such camera and instance of
|
||||
* the EmulatedCameraQemud will be created and added to the emulated_cameras_
|
||||
* 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.
|
||||
***************************************************************************/
|
||||
|
||||
private:
|
||||
/* Connection to the camera service in the emulator. */
|
||||
FactoryQemuClient qemu_client_;
|
||||
|
||||
/* Array of cameras available for the emulation. */
|
||||
EmulatedCamera** emulated_cameras_;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define LOG_TAG "EmulatedCamera_FakeCamera"
|
||||
#include <cutils/log.h>
|
||||
#include "emulated_fake_camera.h"
|
||||
#include "emulated_camera_factory.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -49,6 +50,15 @@ status_t EmulatedFakeCamera::Initialize()
|
||||
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();
|
||||
if (res != NO_ERROR) {
|
||||
return res;
|
||||
@@ -60,15 +70,8 @@ status_t EmulatedFakeCamera::Initialize()
|
||||
|
||||
parameters_.set(CameraParameters::KEY_SUPPORTED_PICTURE_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_.setPictureSize(640, 480);
|
||||
parameters_.setPictureFormat(CameraParameters::PIXEL_FORMAT_YUV420P);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,11 @@ namespace android {
|
||||
|
||||
EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal)
|
||||
: EmulatedCameraDevice(camera_hal),
|
||||
last_redrawn_(0),
|
||||
black_YCbCr_(0),
|
||||
white_YCbCr_(0xffff),
|
||||
red_YCbCr_(kRed),
|
||||
green_YCbCr_(kGreen),
|
||||
blue_YCbCr_(kBlue),
|
||||
black_YCbCr_(kBlack32),
|
||||
white_YCbCr_(kWhite32),
|
||||
red_YCbCr_(kRed8),
|
||||
green_YCbCr_(kGreen8),
|
||||
blue_YCbCr_(kBlue8),
|
||||
check_x_(0),
|
||||
check_y_(0),
|
||||
counter_(0)
|
||||
@@ -117,7 +116,6 @@ status_t EmulatedFakeCameraDevice::StopCamera()
|
||||
{
|
||||
LOGV("%s", __FUNCTION__);
|
||||
|
||||
Mutex::Autolock locker(&object_lock_);
|
||||
if (!IsCapturing()) {
|
||||
LOGW("%s: Fake camera device is not capturing.", __FUNCTION__);
|
||||
return NO_ERROR;
|
||||
@@ -147,10 +145,7 @@ bool EmulatedFakeCameraDevice::InWorkerThread()
|
||||
}
|
||||
|
||||
/* Lets see if we need to generate a new frame. */
|
||||
timeval cur_time;
|
||||
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_) {
|
||||
if ((systemTime(SYSTEM_TIME_MONOTONIC) - timestamp_) >= redraw_after_) {
|
||||
/*
|
||||
* Time to generate a new frame.
|
||||
*/
|
||||
@@ -167,12 +162,11 @@ bool EmulatedFakeCameraDevice::InWorkerThread()
|
||||
DrawSquare(x * size / 32, y * size / 32, (size * 5) >> 1,
|
||||
(counter_ & 0x100) ? &red_YCbCr_ : &green_YCbCr_);
|
||||
counter_++;
|
||||
last_redrawn_ = cur_mks;
|
||||
}
|
||||
|
||||
/* Notify the camera HAL about new frame. */
|
||||
camera_hal_->OnNextFrameAvailable(current_frame_,
|
||||
systemTime(SYSTEM_TIME_MONOTONIC), this);
|
||||
/* Timestamp the current frame, and notify the camera HAL about new frame. */
|
||||
timestamp_ = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
camera_hal_->OnNextFrameAvailable(current_frame_, timestamp_, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -235,7 +229,7 @@ void EmulatedFakeCameraDevice::DrawCheckerboard()
|
||||
void EmulatedFakeCameraDevice::DrawSquare(int x,
|
||||
int y,
|
||||
int size,
|
||||
const YCbCrPixel* color)
|
||||
const YUVPixel* color)
|
||||
{
|
||||
const int half_x = x / 2;
|
||||
const int square_xstop = min(frame_width_, x+size);
|
||||
|
||||
@@ -103,25 +103,22 @@ private:
|
||||
* size - Size of the square's side.
|
||||
* 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
|
||||
***************************************************************************/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
YCbCrPixel black_YCbCr_;
|
||||
YCbCrPixel white_YCbCr_;
|
||||
YCbCrPixel red_YCbCr_;
|
||||
YCbCrPixel green_YCbCr_;
|
||||
YCbCrPixel blue_YCbCr_;
|
||||
YUVPixel black_YCbCr_;
|
||||
YUVPixel white_YCbCr_;
|
||||
YUVPixel red_YCbCr_;
|
||||
YUVPixel green_YCbCr_;
|
||||
YUVPixel blue_YCbCr_;
|
||||
|
||||
/*
|
||||
* Drawing related stuff
|
||||
@@ -133,12 +130,12 @@ private:
|
||||
int half_width_;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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. */
|
||||
static const uint32_t redraw_after_ = 15000;
|
||||
static const nsecs_t redraw_after_ = 15000000LL;
|
||||
};
|
||||
|
||||
}; /* namespace android */
|
||||
|
||||
120
tools/emulator/system/camera/emulated_qemu_camera.cpp
Normal file
120
tools/emulator/system/camera/emulated_qemu_camera.cpp
Normal 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 */
|
||||
75
tools/emulator/system/camera/emulated_qemu_camera.h
Normal file
75
tools/emulator/system/camera/emulated_qemu_camera.h
Normal 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 */
|
||||
243
tools/emulator/system/camera/emulated_qemu_camera_device.cpp
Normal file
243
tools/emulator/system/camera/emulated_qemu_camera_device.cpp
Normal 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 */
|
||||
120
tools/emulator/system/camera/emulated_qemu_camera_device.h
Normal file
120
tools/emulator/system/camera/emulated_qemu_camera_device.h
Normal 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 */
|
||||
@@ -129,7 +129,7 @@ void PreviewWindow::OnNextFrameAvailable(const void* frame,
|
||||
res = preview_window_->set_buffers_geometry(preview_window_,
|
||||
preview_frame_width_,
|
||||
preview_frame_height_,
|
||||
HAL_PIXEL_FORMAT_RGB_565);
|
||||
HAL_PIXEL_FORMAT_RGBA_8888);
|
||||
if (res != NO_ERROR) {
|
||||
LOGE("%s: Error in set_buffers_geometry %d -> %s",
|
||||
__FUNCTION__, -res, strerror(-res));
|
||||
|
||||
Reference in New Issue
Block a user