diff --git a/tools/emulator/system/camera/Android.mk b/tools/emulator/system/camera/Android.mk index e6d106da7..80154178d 100644 --- a/tools/emulator/system/camera/Android.mk +++ b/tools/emulator/system/camera/Android.mk @@ -29,15 +29,18 @@ LOCAL_SHARED_LIBRARIES:= \ libui \ LOCAL_SRC_FILES := \ - emulated_camera_hal.cpp \ + emulated_camera_hal.cpp \ emulated_camera_factory.cpp \ emulated_camera.cpp \ - emulated_camera_device.cpp \ - emulated_fake_camera.cpp \ - emulated_fake_camera_device.cpp \ - converters.cpp \ - preview_window.cpp \ - callback_notifier.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 \ + QemuClient.cpp ifeq ($(TARGET_PRODUCT),vbox_x86) LOCAL_MODULE := camera.vbox_x86 diff --git a/tools/emulator/system/camera/QemuClient.cpp b/tools/emulator/system/camera/QemuClient.cpp new file mode 100644 index 000000000..78ec121a9 --- /dev/null +++ b/tools/emulator/system/camera/QemuClient.cpp @@ -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 +#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(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(&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(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 */ diff --git a/tools/emulator/system/camera/QemuClient.h b/tools/emulator/system/camera/QemuClient.h new file mode 100644 index 000000000..737c28fbc --- /dev/null +++ b/tools/emulator/system/camera/QemuClient.h @@ -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 + +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: + * + * "[ ]", + * + * where is a string representing query name, and 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: + * + * "= = ... =" + * + * 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= [inp_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= channel= pix= framedims=\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 'x', 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 */ diff --git a/tools/emulator/system/camera/callback_notifier.cpp b/tools/emulator/system/camera/callback_notifier.cpp index 69c94a6fc..aa21a0a0a 100644 --- a/tools/emulator/system/camera/callback_notifier.cpp +++ b/tools/emulator/system/camera/callback_notifier.cpp @@ -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(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, @@ -247,31 +216,16 @@ void CallbackNotifier::OnNextFrameAvailable(const void* frame, Mutex::Autolock locker(&object_lock_); 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(holder->data); - meta->type = kMetadataBufferTypeCameraSource; - meta->frame = frame; - meta->offset = 0; - meta->holder = holder; - data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME, - holder, 0, cb_opaque_); - /* Allocated holder will be released by release_recording_frame - * call. */ - } else { - holder->data = const_cast(frame); - data_cb_timestamp_(timestamp, CAMERA_MSG_VIDEO_FRAME, - holder, 0, cb_opaque_); - holder->release(holder); - } + data_cb_timestamp_ != NULL && video_recording_enabled_ && + 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, + cam_buff, 0, cb_opaque_); } 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; diff --git a/tools/emulator/system/camera/callback_notifier.h b/tools/emulator/system/camera/callback_notifier.h index eb6598d8e..48dfb02df 100644 --- a/tools/emulator/system/camera/callback_notifier.h +++ b/tools/emulator/system/camera/callback_notifier.h @@ -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 */ diff --git a/tools/emulator/system/camera/converters.cpp b/tools/emulator/system/camera/converters.cpp index 37b7caf86..da6a0b26c 100644 --- a/tools/emulator/system/camera/converters.cpp +++ b/tools/emulator/system/camera/converters.cpp @@ -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(rgb); + const uint8_t* Y = reinterpret_cast(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) { diff --git a/tools/emulator/system/camera/converters.h b/tools/emulator/system/camera/converters.h index 8d7709f1e..450ecbd8e 100644 --- a/tools/emulator/system/camera/converters.h +++ b/tools/emulator/system/camera/converters.h @@ -17,103 +17,234 @@ #ifndef HW_EMULATOR_CAMERA_CONVERTERS_H #define HW_EMULATOR_CAMERA_CONVERTERS_H +#include + +#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(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(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(kR0*R + kR1*G + kR2*B) -#define RGB2Cb(R,G,B) static_cast(-kR3*R - kR4*G + kR5*B + 128) -#define RGB2Cr(R,G,B) static_cast(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(rgb & kRed5) +#define G16(rgb) static_cast((rgb & kGreen6) >> 5) +#define B16(rgb) static_cast((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(rgb & kRed8) +#define G32(rgb) static_cast(((rgb & kGreen8) >> 8) & 0xff) +#define B32(rgb) static_cast(((rgb & kBlue8) >> 16) & 0xff) +/* Build RGB565 word from red, green, and blue bytes. */ +#define RGB565(r, g, b) static_cast((((static_cast(b) << 6) | g) << 5) | r) +/* Build RGB32 dword from red, green, and blue bytes. */ +#define RGB32(r, g, b) static_cast((((static_cast(b) << 8) | g) << 8) | r) +#else // __BYTE_ORDER +/* Extract red, green, and blue bytes from RGB565 word. */ +#define R16(rgb) static_cast((rgb & kRed5) >> 11) +#define G16(rgb) static_cast((rgb & kGreen6) >> 5) +#define B16(rgb) static_cast(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((rgb & kRed8) >> 16) +#define G32(rgb) static_cast((rgb & kGreen8) >> 8) +#define B32(rgb) static_cast(rgb & kBlue8) +/* Build RGB565 word from red, green, and blue bytes. */ +#define RGB565(r, g, b) static_cast((((static_cast(r) << 6) | g) << 5) | b) +/* Build RGB32 dword from red, green, and blue bytes. */ +#define RGB32(r, g, b) static_cast((((static_cast(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 */ - diff --git a/tools/emulator/system/camera/emulated_camera.cpp b/tools/emulator/system/camera/emulated_camera.cpp index 8808e87c8..6d590fa00 100644 --- a/tools/emulator/system/camera/emulated_camera.cpp +++ b/tools/emulator/system/camera/emulated_camera.cpp @@ -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; diff --git a/tools/emulator/system/camera/emulated_camera_common.h b/tools/emulator/system/camera/emulated_camera_common.h index 1b8b7404b..22cddbd08 100644 --- a/tools/emulator/system/camera/emulated_camera_common.h +++ b/tools/emulator/system/camera/emulated_camera_common.h @@ -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 */ diff --git a/tools/emulator/system/camera/emulated_camera_device.cpp b/tools/emulator/system/camera/emulated_camera_device.cpp index 02d18aecc..cef56b330 100644 --- a/tools/emulator/system/camera/emulated_camera_device.cpp +++ b/tools/emulator/system/camera/emulated_camera_device.cpp @@ -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: diff --git a/tools/emulator/system/camera/emulated_camera_device.h b/tools/emulator/system/camera/emulated_camera_device.h index 5b81cdc8f..6b140f9db 100644 --- a/tools/emulator/system/camera/emulated_camera_device.h +++ b/tools/emulator/system/camera/emulated_camera_device.h @@ -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 worker_thread_; + /* Timestamp of the current frame. */ + nsecs_t timestamp_; + /* Emulated camera object containing this instance. */ EmulatedCamera* camera_hal_; diff --git a/tools/emulator/system/camera/emulated_camera_factory.cpp b/tools/emulator/system/camera/emulated_camera_factory.cpp index 8a7658588..098f82fca 100644 --- a/tools/emulator/system/camera/emulated_camera_factory.cpp +++ b/tools/emulator/system/camera/emulated_camera_factory.cpp @@ -22,6 +22,7 @@ #define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_Factory" #include +#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; - } - 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_); - - 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. */ - 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; + /* 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(); } - /* Setup the 'qemud' cameras. */ - if (!CreateQemudCameras(qemud_num)) { - return; + if (IsFakeCameraEmulationOn()) { + /* ID fake camera with the number of created 'qemud' cameras. */ + fake_camera_id_ = emulated_camera_num_; + emulated_camera_num_++; + + /* 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; + } + 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) { + if (emulated_cameras_[fake_camera_id_]->Initialize() != NO_ERROR) { + 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__); + } } - /* 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_]->Initialize() != NO_ERROR) { - return; - } + 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; + } + + /* + * Calculate number of connected cameras. Number of EOLs in the camera list + * is the number of the connected cameras. + */ + + int num = 0; + const char* eol = strchr(camera_list, '\n'); + while (eol != NULL) { + num++; + eol = strchr(eol + 1, '\n'); + } + + /* Allocate the array for emulated camera instances. Note that we allocate + * one more entry for the fake camera emulation. */ + emulated_cameras_ = new EmulatedCamera*[num + 1]; + if (emulated_cameras_ == NULL) { + LOGE("%s: Unable to allocate emulated camera array for %d entries", + __FUNCTION__, num + 1); + free(camera_list); + return; + } + memset(emulated_cameras_, 0, sizeof(EmulatedCamera*) * (num + 1)); + + /* + * Iterate the list, creating, and initializin emulated qemu cameras for each + * entry (line) in the list. + */ + + int index = 0; + char* cur_entry = camera_list; + while (cur_entry != NULL && *cur_entry != '\0' && index < num) { + /* Find the end of the current camera entry, and terminate it with zero + * for simpler string manipulation. */ + char* next_entry = strchr(cur_entry, '\n'); + if (next_entry != NULL) { + *next_entry = '\0'; + next_entry++; // Start of the next entry. + } + + /* Find 'name', and 'framedims' tokens that are required here. */ + char* name_start = strstr(cur_entry, _list_name_token); + char* dim_start = strstr(cur_entry, _list_dims_token); + if (name_start != NULL && dim_start != NULL) { + /* Advance to the token values. */ + name_start += strlen(_list_name_token); + dim_start += strlen(_list_dims_token); + + /* Terminate token values with zero. */ + char* s = strchr(name_start, ' '); + if (s != NULL) { + *s = '\0'; + } + s = strchr(dim_start, ' '); + if (s != NULL) { + *s = '\0'; + } + + /* Create and initialize qemu camera. */ + EmulatedQemuCamera* qemu_cam = + new EmulatedQemuCamera(index, &HAL_MODULE_INFO_SYM.common); + if (NULL != qemu_cam) { + res = qemu_cam->Initialize(name_start, dim_start); + if (res == NO_ERROR) { + emulated_cameras_[index] = qemu_cam; + index++; + } else { + delete qemu_cam; + } + } else { + LOGE("%s: Unable to instantiate EmulatedQemuCamera", + __FUNCTION__); + } + } else { + LOGW("%s: Bad camera information: %s", __FUNCTION__, cur_entry); + } + + cur_entry = next_entry; + } + + emulated_camera_num_ = index; } -bool EmulatedCameraFactory::CreateQemudCameras(int num) +bool EmulatedCameraFactory::IsQemuCameraEmulationOn() { - // TODO: Implement! + /* TODO: Have a boot property that controls that! */ + return true; +} + +bool EmulatedCameraFactory::IsFakeCameraEmulationOn() +{ + /* TODO: Have a boot property that controls that! */ return true; } diff --git a/tools/emulator/system/camera/emulated_camera_factory.h b/tools/emulator/system/camera/emulated_camera_factory.h index 68d0ed039..368652e62 100644 --- a/tools/emulator/system/camera/emulated_camera_factory.h +++ b/tools/emulator/system/camera/emulated_camera_factory.h @@ -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_; diff --git a/tools/emulator/system/camera/emulated_fake_camera.cpp b/tools/emulator/system/camera/emulated_fake_camera.cpp index f1861a4c1..80dbe2d46 100644 --- a/tools/emulator/system/camera/emulated_fake_camera.cpp +++ b/tools/emulator/system/camera/emulated_fake_camera.cpp @@ -23,6 +23,7 @@ #define LOG_TAG "EmulatedCamera_FakeCamera" #include #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; } diff --git a/tools/emulator/system/camera/emulated_fake_camera_device.cpp b/tools/emulator/system/camera/emulated_fake_camera_device.cpp index 9c1698fb4..1957c54de 100644 --- a/tools/emulator/system/camera/emulated_fake_camera_device.cpp +++ b/tools/emulator/system/camera/emulated_fake_camera_device.cpp @@ -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); diff --git a/tools/emulator/system/camera/emulated_fake_camera_device.h b/tools/emulator/system/camera/emulated_fake_camera_device.h index 76c34f97d..fd65ed3f2 100644 --- a/tools/emulator/system/camera/emulated_fake_camera_device.h +++ b/tools/emulator/system/camera/emulated_fake_camera_device.h @@ -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 */ diff --git a/tools/emulator/system/camera/emulated_qemu_camera.cpp b/tools/emulator/system/camera/emulated_qemu_camera.cpp new file mode 100644 index 000000000..9e573a6a7 --- /dev/null +++ b/tools/emulator/system/camera/emulated_qemu_camera.cpp @@ -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 +#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 */ diff --git a/tools/emulator/system/camera/emulated_qemu_camera.h b/tools/emulator/system/camera/emulated_qemu_camera.h new file mode 100644 index 000000000..3883f26b6 --- /dev/null +++ b/tools/emulator/system/camera/emulated_qemu_camera.h @@ -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 */ diff --git a/tools/emulator/system/camera/emulated_qemu_camera_device.cpp b/tools/emulator/system/camera/emulated_qemu_camera_device.cpp new file mode 100644 index 000000000..01207ee53 --- /dev/null +++ b/tools/emulator/system/camera/emulated_qemu_camera_device.cpp @@ -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 +#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 */ diff --git a/tools/emulator/system/camera/emulated_qemu_camera_device.h b/tools/emulator/system/camera/emulated_qemu_camera_device.h new file mode 100644 index 000000000..83d692512 --- /dev/null +++ b/tools/emulator/system/camera/emulated_qemu_camera_device.h @@ -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 */ diff --git a/tools/emulator/system/camera/preview_window.cpp b/tools/emulator/system/camera/preview_window.cpp index 151e466c6..18ecf478d 100644 --- a/tools/emulator/system/camera/preview_window.cpp +++ b/tools/emulator/system/camera/preview_window.cpp @@ -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));