blob: 173b6f55cd7bea5e5f0726587c5113577f686422 [file] [log] [blame]
/*
* 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 emulated camera service implementation.
*/
#include "qemu-common.h"
#include "android/globals.h" /* for android_hw */
#include "android/hw-qemud.h"
#include "android/utils/misc.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
#include "android/camera/camera-capture.h"
#include "android/camera/camera-format-converters.h"
#include "android/camera/camera-service.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(camera)
/* the T(...) macro is used to dump traffic */
#define T_ACTIVE 0
#if T_ACTIVE
#define T(...) VERBOSE_PRINT(camera,__VA_ARGS__)
#else
#define T(...) ((void)0)
#endif
/* Defines name of the camera service. */
#define SERVICE_NAME "camera"
/* Maximum number of supported emulated cameras. */
#define MAX_CAMERA 8
/* Camera sevice descriptor. */
typedef struct CameraServiceDesc CameraServiceDesc;
struct CameraServiceDesc {
/* Information about camera devices connected to the host.
* Note that once initialized, entries in this array are considered to be
* constant. */
CameraInfo camera_info[MAX_CAMERA];
/* Number of camera devices connected to the host. */
int camera_count;
};
/* One and only one camera service. */
static CameraServiceDesc _camera_service_desc;
/********************************************************************************
* Helper routines
*******************************************************************************/
/* Extracts query name, and (optionally) query parameters from the query string.
* Param:
* query - Query string. Query string in the camera service are formatted as such:
* "<query name>[ <parameters>]",
* where parameters are optional, and if present, must be separated from the
* query name with a single ' '. See comments to get_token_value routine
* for the format of the parameters string.
* query_name - Upon success contains query name extracted from the query
* string.
* query_name_size - Buffer size for 'query_name' string.
* query_param - Upon success contains a pointer to the beginning of the query
* parameters. If query has no parameters, NULL will be passed back with
* this parameter. This parameter is optional and can be NULL.
* Return:
* 0 on success, or number of bytes required for query name if 'query_name'
* string buffer was too small to contain it.
*/
static int
_parse_query(const char* query,
char* query_name,
int query_name_size,
const char** query_param)
{
/* Extract query name. */
const char* qend = strchr(query, ' ');
if (qend == NULL) {
qend = query + strlen(query);
}
if ((qend - query) >= query_name_size) {
return qend - query + 1;
}
memcpy(query_name, query, qend - query);
query_name[qend - query] = '\0';
/* Calculate query parameters pointer (if needed) */
if (query_param != NULL) {
if (*qend == ' ') {
qend++;
}
*query_param = (*qend == '\0') ? NULL : qend;
}
return 0;
}
/* Appends one string to another, growing the destination string buffer if
* needed.
* Param:
* str_buffer - Contains pointer to the destination string buffer. Content of
* this parameter can be NULL. Note that content of this parameter will
* change if string buffer has been reallocated.
* str_buf_size - Contains current buffer size of the string, addressed by
* 'str_buffer' parameter. Note that content of this parameter will change
* if string buffer has been reallocated.
* str - String to append.
* Return:
* 0 on success, or -1 on failure (memory allocation).
*/
static int
_append_string(char** str_buf, size_t* str_buf_size, const char* str)
{
const size_t offset = (*str_buf != NULL) ? strlen(*str_buf) : 0;
const size_t append_bytes = strlen(str) + 1;
/* Make sure these two match. */
if (*str_buf == NULL) {
*str_buf_size = 0;
}
if ((offset + append_bytes) > *str_buf_size) {
/* Reallocate string, so it can fit what's being append to it. Note that
* we reallocate a bit bigger buffer than is needed in order to minimize
* number of memory allocation calls in case there are more "appends"
* coming. */
const size_t required_mem = offset + append_bytes + 256;
char* new_buf = (char*)realloc(*str_buf, required_mem);
if (new_buf == NULL) {
E("%s: Unable to allocate %d bytes for a string",
__FUNCTION__, required_mem);
return -1;
}
*str_buf = new_buf;
*str_buf_size = required_mem;
}
memcpy(*str_buf + offset, str, append_bytes);
return 0;
}
/* Represents camera information as a string formatted as follows:
* 'name=<devname> channel=<num> pix=<format> facing=<direction> framedims=<widh1xheight1,...>\n'
* Param:
* ci - Camera information descriptor to convert into a string.
* str - Pointer to the string buffer where to save the converted camera
* information descriptor. On entry, content of this parameter can be NULL.
* Note that string buffer addressed with this parameter may be reallocated
* in this routine, so (if not NULL) it must contain a buffer allocated with
* malloc. The caller is responsible for freeing string buffer returned in
* this parameter.
* str_size - Contains byte size of the buffer addressed by 'str' parameter.
* Return:
* 0 on success, or != 0 on failure.
*/
static int
_camera_info_to_string(const CameraInfo* ci, char** str, size_t* str_size) {
int res;
int n;
char tmp[128];
/* Append device name. */
snprintf(tmp, sizeof(tmp), "name=%s ", ci->device_name);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
/* Append input channel. */
snprintf(tmp, sizeof(tmp), "channel=%d ", ci->inp_channel);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
/* Append pixel format. */
snprintf(tmp, sizeof(tmp), "pix=%d ", ci->pixel_format);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
/* Append direction. */
snprintf(tmp, sizeof(tmp), "dir=%s ", ci->direction);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
/* Append supported frame sizes. */
snprintf(tmp, sizeof(tmp), "framedims=%dx%d",
ci->frame_sizes[0].width, ci->frame_sizes[0].height);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
for (n = 1; n < ci->frame_sizes_num; n++) {
snprintf(tmp, sizeof(tmp), ",%dx%d",
ci->frame_sizes[n].width, ci->frame_sizes[n].height);
res = _append_string(str, str_size, tmp);
if (res) {
return res;
}
}
/* Stringified camera properties should end with EOL. */
return _append_string(str, str_size, "\n");
}
/* Gets camera information matching a display name.
* Param:
* disp_name - Display name to match.
* arr - Array of camera informations.
* num - Number of elements in the array.
* Return:
* Matching camera information, or NULL if matching camera information for the
* given display name has not been found in the array.
*/
static CameraInfo*
_camera_info_get_by_display_name(const char* disp_name, CameraInfo* arr, int num)
{
int n;
for (n = 0; n < num; n++) {
if (!arr[n].in_use && arr[n].display_name != NULL &&
!strcmp(arr[n].display_name, disp_name)) {
return &arr[n];
}
}
return NULL;
}
/* Gets camera information matching a device name.
* Param:
* device_name - Device name to match.
* arr - Array of camera informations.
* num - Number of elements in the array.
* Return:
* Matching camera information, or NULL if matching camera information for the
* given device name has not been found in the array.
*/
static CameraInfo*
_camera_info_get_by_device_name(const char* device_name, CameraInfo* arr, int num)
{
int n;
for (n = 0; n < num; n++) {
if (arr[n].device_name != NULL && !strcmp(arr[n].device_name, device_name)) {
return &arr[n];
}
}
return NULL;
}
/********************************************************************************
* CameraServiceDesc API
*******************************************************************************/
/* Initialized webcam emulation record in camera service descriptor.
* Param:
* csd - Camera service descriptor to initialize a record in.
* disp_name - Display name of a web camera ('webcam<N>') to use for emulation.
* dir - Direction ('back', or 'front') that emulated camera is facing.
* ci, ci_cnt - Array of webcam information for enumerated web cameras connected
* to the host.
*/
static void
_wecam_setup(CameraServiceDesc* csd,
const char* disp_name,
const char* dir,
CameraInfo* ci,
int ci_cnt)
{
/* Find webcam record in the list of enumerated web cameras. */
CameraInfo* found = _camera_info_get_by_display_name(disp_name, ci, ci_cnt);
if (found == NULL) {
W("Camera name '%s' is not found in the list of connected cameras.\n"
"Use '-webcam-list' emulator option to obtain the list of connected camera names.\n",
disp_name);
return;
}
/* Save to the camera info array that will be used by the service. */
memcpy(csd->camera_info + csd->camera_count, found, sizeof(CameraInfo));
/* This camera is taken. */
found->in_use = 1;
/* Update direction parameter. */
if (csd->camera_info[csd->camera_count].direction != NULL) {
free(csd->camera_info[csd->camera_count].direction);
}
csd->camera_info[csd->camera_count].direction = ASTRDUP(dir);
D("Camera %d '%s' connected to '%s' facing %s using %.4s pixel format",
csd->camera_count, csd->camera_info[csd->camera_count].display_name,
csd->camera_info[csd->camera_count].device_name,
csd->camera_info[csd->camera_count].direction,
(const char*)(&csd->camera_info[csd->camera_count].pixel_format));
csd->camera_count++;
}
/* Initializes camera service descriptor.
*/
static void
_camera_service_init(CameraServiceDesc* csd)
{
CameraInfo ci[MAX_CAMERA];
int connected_cnt;
/* Enumerate camera devices connected to the host. */
memset(ci, 0, sizeof(CameraInfo) * MAX_CAMERA);
memset(csd->camera_info, 0, sizeof(CameraInfo) * MAX_CAMERA);
csd->camera_count = 0;
/* Lets see if HW config uses web cameras. */
if (memcmp(android_hw->hw_camera_back, "webcam", 6) &&
memcmp(android_hw->hw_camera_front, "webcam", 6)) {
/* Web camera emulation is disabled. Skip enumeration of webcameras. */
return;
}
/* Enumerate web cameras connected to the host. */
connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
if (connected_cnt <= 0) {
/* Nothing is connected - nothing to emulate. */
return;
}
/* Set up back camera emulation. */
if (!memcmp(android_hw->hw_camera_back, "webcam", 6)) {
_wecam_setup(csd, android_hw->hw_camera_back, "back", ci, connected_cnt);
}
/* Set up front camera emulation. */
if (!memcmp(android_hw->hw_camera_front, "webcam", 6)) {
_wecam_setup(csd, android_hw->hw_camera_front, "front", ci, connected_cnt);
}
}
/* Gets camera information for the given camera device name.
* Param:
* cs - Initialized camera service descriptor.
* device_name - Camera's device name to look up the information for.
* Return:
* Camera information pointer on success, or NULL if no camera information has
* been found for the given device name.
*/
static CameraInfo*
_camera_service_get_camera_info_by_device_name(CameraServiceDesc* cs,
const char* device_name)
{
return _camera_info_get_by_device_name(device_name, cs->camera_info,
cs->camera_count);
}
/********************************************************************************
* Helpers for handling camera client queries
*******************************************************************************/
/* Formats paload size according to the protocol, and sends it to the client.
* To simplify endianess handling we convert payload size to an eight characters
* string, representing payload size value in hexadecimal format.
* Param:
* qc - Qemu client to send the payload size to.
* payload_size - Payload size to report to the client.
*/
static void
_qemu_client_reply_payload(QemudClient* qc, size_t payload_size)
{
char payload_size_str[9];
snprintf(payload_size_str, sizeof(payload_size_str), "%08zx", payload_size);
qemud_client_send(qc, (const uint8_t*)payload_size_str, 8);
}
/*
* Prefixes for replies to camera client queries.
*/
/* Success, no data to send in reply. */
#define OK_REPLY "ok"
/* Failure, no data to send in reply. */
#define KO_REPLY "ko"
/* Success, there are data to send in reply. */
#define OK_REPLY_DATA OK_REPLY ":"
/* Failure, there are data to send in reply. */
#define KO_REPLY_DATA KO_REPLY ":"
/* Builds and sends a reply to a query.
* All replies to a query in camera service have a prefix indicating whether the
* query has succeeded ("ok"), or failed ("ko"). The prefix can be followed by
* extra data, containing response to the query. In case there are extra data,
* they are separated from the prefix with a ':' character.
* Param:
* qc - Qemu client to send the reply to.
* ok_ko - An "ok", or "ko" selector, where 0 is for "ko", and !0 is for "ok".
* extra - Optional extra query data. Can be NULL.
* extra_size - Extra data size.
*/
static void
_qemu_client_query_reply(QemudClient* qc,
int ok_ko,
const void* extra,
size_t extra_size)
{
const char* ok_ko_str;
size_t payload_size;
/* Make sure extra_size is 0 if extra is NULL. */
if (extra == NULL && extra_size != 0) {
W("%s: 'extra' = NULL, while 'extra_size' = %d",
__FUNCTION__, (int)extra_size);
extra_size = 0;
}
/* Calculate total payload size, and select appropriate 'ok'/'ko' prefix */
if (extra_size) {
/* 'extra' size + 2 'ok'/'ko' bytes + 1 ':' separator byte. */
payload_size = extra_size + 3;
ok_ko_str = ok_ko ? OK_REPLY_DATA : KO_REPLY_DATA;
} else {
/* No extra data: just zero-terminated 'ok'/'ko'. */
payload_size = 3;
ok_ko_str = ok_ko ? OK_REPLY : KO_REPLY;
}
/* Send payload size first. */
_qemu_client_reply_payload(qc, payload_size);
/* Send 'ok[:]'/'ko[:]' next. Note that if there is no extra data, we still
* need to send a zero-terminator for 'ok'/'ko' string instead of the ':'
* separator. So, one way or another, the prefix is always 3 bytes. */
qemud_client_send(qc, (const uint8_t*)ok_ko_str, 3);
/* Send extra data (if present). */
if (extra != NULL) {
qemud_client_send(qc, (const uint8_t*)extra, extra_size);
}
}
/* Replies query success ("OK") back to the client.
* Param:
* qc - Qemu client to send the reply to.
* ok_str - An optional string containing query results. Can be NULL.
*/
static void
_qemu_client_reply_ok(QemudClient* qc, const char* ok_str)
{
_qemu_client_query_reply(qc, 1, ok_str,
(ok_str != NULL) ? (strlen(ok_str) + 1) : 0);
}
/* Replies query failure ("KO") back to the client.
* Param:
* qc - Qemu client to send the reply to.
* ko_str - An optional string containing reason for failure. Can be NULL.
*/
static void
_qemu_client_reply_ko(QemudClient* qc, const char* ko_str)
{
_qemu_client_query_reply(qc, 0, ko_str,
(ko_str != NULL) ? (strlen(ko_str) + 1) : 0);
}
/********************************************************************************
* Camera Factory API
*******************************************************************************/
/* Handles 'list' query received from the Factory client.
* Response to this query is a string that represents each connected camera in
* this format: 'name=devname framedims=widh1xheight1,widh2xheight2,widhNxheightN\n'
* Strings, representing each camera are separated with EOL symbol.
* Param:
* csd, client - Factory serivice, and client.
* Return:
* 0 on success, or != 0 on failure.
*/
static int
_factory_client_list_cameras(CameraServiceDesc* csd, QemudClient* client)
{
int n;
size_t reply_size = 0;
char* reply = NULL;
/* Lets see if there was anything found... */
if (csd->camera_count == 0) {
/* No cameras connected to the host. Reply with "\n" */
_qemu_client_reply_ok(client, "\n");
return 0;
}
/* "Stringify" each camera information into the reply string. */
for (n = 0; n < csd->camera_count; n++) {
const int res =
_camera_info_to_string(csd->camera_info + n, &reply, &reply_size);
if (res) {
if (reply != NULL) {
free(reply);
}
_qemu_client_reply_ko(client, "Memory allocation error");
return res;
}
}
D("%s Replied: %s", __FUNCTION__, reply);
_qemu_client_reply_ok(client, reply);
free(reply);
return 0;
}
/* Handles a message received from the emulated camera factory client.
* Queries received here are represented as strings:
* 'list' - Queries list of cameras connected to the host.
* Param:
* opaque - Camera service descriptor.
* msg, msglen - Message received from the camera factory client.
* client - Camera factory client pipe.
*/
static void
_factory_client_recv(void* opaque,
uint8_t* msg,
int msglen,
QemudClient* client)
{
/*
* Emulated camera factory client queries.
*/
/* List cameras connected to the host. */
static const char _query_list[] = "list";
CameraServiceDesc* csd = (CameraServiceDesc*)opaque;
char query_name[64];
const char* query_param = NULL;
/* Parse the query, extracting query name and parameters. */
if (_parse_query((const char*)msg, query_name, sizeof(query_name),
&query_param)) {
E("%s: Invalid format in query '%s'", __FUNCTION__, (const char*)msg);
_qemu_client_reply_ko(client, "Invalid query format");
return;
}
D("%s Camera factory query '%s'", __FUNCTION__, query_name);
/* Dispatch the query to an appropriate handler. */
if (!strcmp(query_name, _query_list)) {
/* This is a "list" query. */
_factory_client_list_cameras(csd, client);
} else {
E("%s: Unknown camera factory query name in '%s'",
__FUNCTION__, (const char*)msg);
_qemu_client_reply_ko(client, "Unknown query name");
}
}
/* Emulated camera factory client has been disconnected from the service. */
static void
_factory_client_close(void* opaque)
{
/* There is nothing to clean up here: factory service is just an alias for
* the "root" camera service, that doesn't require anything more, than camera
* dervice descriptor already provides. */
}
/********************************************************************************
* Camera client API
*******************************************************************************/
/* Describes an emulated camera client.
*/
typedef struct CameraClient CameraClient;
struct CameraClient
{
/* Client name.
* On Linux this is the name of the camera device.
* On Windows this is the name of capturing window.
*/
char* device_name;
/* Input channel to use to connect to the camera. */
int inp_channel;
/* Camera information. */
const CameraInfo* camera_info;
/* Emulated camera device descriptor. */
CameraDevice* camera;
/* Buffer allocated for video frames.
* Note that memory allocated for this buffer
* also contains preview framebuffer. */
uint8_t* video_frame;
/* Preview frame buffer.
* This address points inside the 'video_frame' buffer. */
uint16_t* preview_frame;
/* Byte size of the videoframe buffer. */
size_t video_frame_size;
/* Byte size of the preview frame buffer. */
size_t preview_frame_size;
/* Pixel format required by the guest. */
uint32_t pixel_format;
/* Frame width. */
int width;
/* Frame height. */
int height;
/* Number of pixels in a frame buffer. */
int pixel_num;
/* Status of video and preview frame cache. */
int frames_cached;
};
/* Frees emulated camera client descriptor. */
static void
_camera_client_free(CameraClient* cc)
{
/* The only exception to the "read only" rule: we have to mark the camera
* as being not used when we destroy a service for it. */
if (cc->camera_info != NULL) {
((CameraInfo*)cc->camera_info)->in_use = 0;
}
if (cc->camera != NULL) {
camera_device_close(cc->camera);
}
if (cc->video_frame != NULL) {
free(cc->video_frame);
}
if (cc->device_name != NULL) {
free(cc->device_name);
}
AFREE(cc);
}
/* Creates descriptor for a connecting emulated camera client.
* Param:
* csd - Camera service descriptor.
* param - Client parameters. Must be formatted as described in comments to
* get_token_value routine, and must contain at least 'name' parameter,
* identifiying the camera device to create the service for. Also parameters
* may contain a decimal 'inp_channel' parameter, selecting the input
* channel to use when communicating with the camera device.
* Return:
* Emulated camera client descriptor on success, or NULL on failure.
*/
static CameraClient*
_camera_client_create(CameraServiceDesc* csd, const char* param)
{
CameraClient* cc;
CameraInfo* ci;
int res;
ANEW0(cc);
/*
* Parse parameter string, containing camera client properties.
*/
/* Pull required device name. */
if (get_token_value_alloc(param, "name", &cc->device_name)) {
E("%s: Allocation failure, or required 'name' parameter is missing, or misformed in '%s'",
__FUNCTION__, param);
return NULL;
}
/* Pull optional input channel. */
res = get_token_value_int(param, "inp_channel", &cc->inp_channel);
if (res != 0) {
if (res == -1) {
/* 'inp_channel' parameter has been ommited. Use default input
* channel, which is zero. */
cc->inp_channel = 0;
} else {
E("%s: 'inp_channel' parameter is misformed in '%s'",
__FUNCTION__, param);
return NULL;
}
}
/* Get camera info for the emulated camera represented with this service.
* Array of camera information records has been created when the camera
* service was enumerating camera devices during the service initialization.
* By the camera service protocol, camera service clients must first obtain
* list of enumerated cameras via the 'list' query to the camera service, and
* then use device name reported in the list to connect to an emulated camera
* service. So, if camera information for the given device name is not found
* in the array, we fail this connection due to protocol violation. */
ci = _camera_service_get_camera_info_by_device_name(csd, cc->device_name);
if (ci == NULL) {
E("%s: Cannot find camera info for device '%s'",
__FUNCTION__, cc->device_name);
_camera_client_free(cc);
return NULL;
}
/* We can't allow multiple camera services for a single camera device, Lets
* make sure that there is no client created for this camera. */
if (ci->in_use) {
E("%s: Camera device '%s' is in use", __FUNCTION__, cc->device_name);
_camera_client_free(cc);
return NULL;
}
/* We're done. Set camera in use, and succeed the connection. */
ci->in_use = 1;
cc->camera_info = ci;
D("%s: Camera service is created for device '%s' using input channel %d",
__FUNCTION__, cc->device_name, cc->inp_channel);
return cc;
}
/********************************************************************************
* Camera client queries
*******************************************************************************/
/* Client has queried conection to the camera.
* Param:
* cc - Queried camera client descriptor.
* qc - Qemu client for the emulated camera.
* param - Query parameters. There are no parameters expected for this query.
*/
static void
_camera_client_query_connect(CameraClient* cc, QemudClient* qc, const char* param)
{
if (cc->camera != NULL) {
/* Already connected. */
W("%s: Camera '%s' is already connected", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, "Camera is already connected");
return;
}
/* Open camera device. */
cc->camera = camera_device_open(cc->device_name, cc->inp_channel);
if (cc->camera == NULL) {
E("%s: Unable to open camera device '%s'", __FUNCTION__, cc->device_name);
_qemu_client_reply_ko(qc, "Unable to open camera device.");
return;
}
D("%s: Camera device '%s' is now connected", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, NULL);
}
/* Client has queried disconection from the camera.
* Param:
* cc - Queried camera client descriptor.
* qc - Qemu client for the emulated camera.
* param - Query parameters. There are no parameters expected for this query.
*/
static void
_camera_client_query_disconnect(CameraClient* cc,
QemudClient* qc,
const char* param)
{
if (cc->camera == NULL) {
/* Already disconnected. */
W("%s: Camera '%s' is already disconnected", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, "Camera is not connected");
return;
}
/* Before we can go ahead and disconnect, we must make sure that camera is
* not capturing frames. */
if (cc->video_frame != NULL) {
E("%s: Cannot disconnect camera '%s' while it is not stopped",
__FUNCTION__, cc->device_name);
_qemu_client_reply_ko(qc, "Camera is not stopped");
return;
}
/* Close camera device. */
camera_device_close(cc->camera);
cc->camera = NULL;
D("Camera device '%s' is now disconnected", cc->device_name);
_qemu_client_reply_ok(qc, NULL);
}
/* Client has queried the client to start capturing video.
* Param:
* cc - Queried camera client descriptor.
* qc - Qemu client for the emulated camera.
* param - Query parameters. Parameters for this query must contain a 'dim', and
* a 'pix' parameters, where 'dim' must be "dim=<width>x<height>", and 'pix'
* must be "pix=<format>", where 'width' and 'height' must be numerical
* values for the capturing video frame width, and height, and 'format' must
* be a numerical value for the pixel format of the video frames expected by
* the client. 'format' must be one of the V4L2_PIX_FMT_XXX values.
*/
static void
_camera_client_query_start(CameraClient* cc, QemudClient* qc, const char* param)
{
char* w;
char dim[64];
int width, height, pix_format;
/* Sanity check. */
if (cc->camera == NULL) {
/* Not connected. */
E("%s: Camera '%s' is not connected", __FUNCTION__, cc->device_name);
_qemu_client_reply_ko(qc, "Camera is not connected");
return;
}
/*
* Parse parameters.
*/
if (param == NULL) {
E("%s: Missing parameters for the query", __FUNCTION__);
_qemu_client_reply_ko(qc, "Missing parameters for the query");
return;
}
/* Pull required 'dim' parameter. */
if (get_token_value(param, "dim", dim, sizeof(dim))) {
E("%s: Invalid or missing 'dim' parameter in '%s'", __FUNCTION__, param);
_qemu_client_reply_ko(qc, "Invalid or missing 'dim' parameter");
return;
}
/* Pull required 'pix' parameter. */
if (get_token_value_int(param, "pix", &pix_format)) {
E("%s: Invalid or missing 'pix' parameter in '%s'", __FUNCTION__, param);
_qemu_client_reply_ko(qc, "Invalid or missing 'pix' parameter");
return;
}
/* Parse 'dim' parameter, and get requested frame width and height. */
w = strchr(dim, 'x');
if (w == NULL || w[1] == '\0') {
E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
_qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
return;
}
*w = '\0'; w++;
errno = 0;
width = strtoi(dim, NULL, 10);
height = strtoi(w, NULL, 10);
if (errno) {
E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
_qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
return;
}
/* After collecting capture parameters lets see if camera has already
* started, and if so, lets see if parameters match. */
if (cc->video_frame != NULL) {
/* Already started. Match capture parameters. */
if (cc->pixel_format != pix_format ||cc->width != width ||
cc->height != height) {
/* Parameters match. Succeed the query. */
W("%s: Camera '%s' is already started", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, "Camera is already started");
} else {
/* Parameters don't match. Fail the query. */
E("%s: Camera '%s' is already started, and parameters don't match:\n"
"Current %.4s[%dx%d] != requested %.4s[%dx%d]",
__FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
cc->width, cc->height, (const char*)&pix_format, width, height);
_qemu_client_reply_ko(qc,
"Camera is already started with different capturing parameters");
}
return;
}
/*
* Start the camera.
*/
/* Save capturing parameters. */
cc->pixel_format = pix_format;
cc->width = width;
cc->height = height;
cc->pixel_num = cc->width * cc->height;
cc->frames_cached = 0;
/* Make sure that pixel format is known, and calculate video framebuffer size
* along the lines. */
switch (cc->pixel_format) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
cc->video_frame_size = (cc->pixel_num * 12) / 8;
break;
default:
E("%s: Unknown pixel format %.4s",
__FUNCTION__, (char*)&cc->pixel_format);
_qemu_client_reply_ko(qc, "Pixel format is unknown");
return;
}
/* Make sure that we have a converters between the original camera pixel
* format and the one that the client expects. Also a converter must exist
* for the preview window pixel format (RGB32) */
if (!has_converter(cc->camera_info->pixel_format, cc->pixel_format) ||
!has_converter(cc->camera_info->pixel_format, V4L2_PIX_FMT_RGB32)) {
E("%s: No conversion exist between %.4s and %.4s (or RGB32) pixel formats",
__FUNCTION__, (char*)&cc->camera_info->pixel_format, (char*)&cc->pixel_format);
_qemu_client_reply_ko(qc, "No conversion exist for the requested pixel format");
return;
}
/* TODO: At the moment camera framework in the emulator requires RGB32 pixel
* format for preview window. So, we need to keep two framebuffers here: one
* for the video, and another for the preview window. Watch out when this
* changes (if changes). */
cc->preview_frame_size = cc->pixel_num * 4;
/* Allocate buffer large enough to contain both, video and preview
* framebuffers. */
cc->video_frame =
(uint8_t*)malloc(cc->video_frame_size + cc->preview_frame_size);
if (cc->video_frame == NULL) {
E("%s: Not enough memory for framebuffers %d + %d",
__FUNCTION__, cc->video_frame_size, cc->preview_frame_size);
_qemu_client_reply_ko(qc, "Out of memory");
return;
}
/* Set framebuffer pointers. */
cc->preview_frame = (uint16_t*)(cc->video_frame + cc->video_frame_size);
/* Start the camera. */
if (camera_device_start_capturing(cc->camera, cc->camera_info->pixel_format,
cc->width, cc->height)) {
E("%s: Cannot start camera '%s' for %.4s[%dx%d]: %s",
__FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
cc->width, cc->height, strerror(errno));
free(cc->video_frame);
cc->video_frame = NULL;
_qemu_client_reply_ko(qc, "Cannot start the camera");
return;
}
D("%s: Camera '%s' is now started for %.4s[%dx%d]",
__FUNCTION__, cc->device_name, (char*)&cc->pixel_format, cc->width,
cc->height);
_qemu_client_reply_ok(qc, NULL);
}
/* Client has queried the client to stop capturing video.
* Param:
* cc - Queried camera client descriptor.
* qc - Qemu client for the emulated camera.
* param - Query parameters. There are no parameters expected for this query.
*/
static void
_camera_client_query_stop(CameraClient* cc, QemudClient* qc, const char* param)
{
if (cc->video_frame == NULL) {
/* Not started. */
W("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, "Camera is not started");
return;
}
/* Stop the camera. */
if (camera_device_stop_capturing(cc->camera)) {
E("%s: Cannot stop camera device '%s': %s",
__FUNCTION__, cc->device_name, strerror(errno));
_qemu_client_reply_ko(qc, "Cannot stop camera device");
return;
}
free(cc->video_frame);
cc->video_frame = NULL;
D("%s: Camera device '%s' is now stopped.", __FUNCTION__, cc->device_name);
_qemu_client_reply_ok(qc, NULL);
}
/* Client has queried next frame.
* Param:
* cc - Queried camera client descriptor.
* qc - Qemu client for the emulated camera.
* param - Query parameters. Parameters for this query are formatted as such:
* video=<size> preview=<size> whiteb=<red>,<green>,<blue> expcomp=<comp>
* where:
* - 'video', and 'preview' both must be decimal values, defining size of
* requested video, and preview frames respectively. Zero value for any
* of these parameters means that this particular frame is not requested.
* - whiteb contains float values required to calculate whilte balance.
* - expcomp contains a float value required to calculate exposure
* compensation.
*/
static void
_camera_client_query_frame(CameraClient* cc, QemudClient* qc, const char* param)
{
int video_size = 0;
int preview_size = 0;
int repeat;
ClientFrameBuffer fbs[2];
int fbs_num = 0;
size_t payload_size;
uint64_t tick;
float r_scale = 1.0f, g_scale = 1.0f, b_scale = 1.0f, exp_comp = 1.0f;
char tmp[256];
/* Sanity check. */
if (cc->video_frame == NULL) {
/* Not started. */
E("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
_qemu_client_reply_ko(qc, "Camera is not started");
return;
}
/* Pull required parameters. */
if (get_token_value_int(param, "video", &video_size) ||
get_token_value_int(param, "preview", &preview_size)) {
E("%s: Invalid or missing 'video', or 'preview' parameter in '%s'",
__FUNCTION__, param);
_qemu_client_reply_ko(qc,
"Invalid or missing 'video', or 'preview' parameter");
return;
}
/* Pull white balance values. */
if (!get_token_value(param, "whiteb", tmp, sizeof(tmp))) {
if (sscanf(tmp, "%g,%g,%g", &r_scale, &g_scale, &b_scale) != 3) {
D("Invalid value '%s' for parameter 'whiteb'", tmp);
r_scale = g_scale = b_scale = 1.0f;
}
}
/* Pull exposure compensation. */
if (!get_token_value(param, "expcomp", tmp, sizeof(tmp))) {
if (sscanf(tmp, "%g", &exp_comp) != 1) {
D("Invalid value '%s' for parameter 'whiteb'", tmp);
exp_comp = 1.0f;
}
}
/* Verify that framebuffer sizes match the ones that the started camera
* operates with. */
if ((video_size != 0 && cc->video_frame_size != video_size) ||
(preview_size != 0 && cc->preview_frame_size != preview_size)) {
E("%s: Frame sizes don't match for camera '%s':\n"
"Expected %d for video, and %d for preview. Requested %d, and %d",
__FUNCTION__, cc->device_name, cc->video_frame_size,
cc->preview_frame_size, video_size, preview_size);
_qemu_client_reply_ko(qc, "Frame size mismatch");
return;
}
/*
* Initialize framebuffer array for frame read.
*/
if (video_size) {
fbs[fbs_num].pixel_format = cc->pixel_format;
fbs[fbs_num].framebuffer = cc->video_frame;
fbs_num++;
}
if (preview_size) {
/* TODO: Watch out for preview format changes! */
fbs[fbs_num].pixel_format = V4L2_PIX_FMT_RGB32;
fbs[fbs_num].framebuffer = cc->preview_frame;
fbs_num++;
}
/* Capture new frame. */
tick = _get_timestamp();
repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
r_scale, g_scale, b_scale, exp_comp);
/* Note that there is no (known) way how to wait on next frame being
* available, so we could dequeue frame buffer from the device only when we
* know it's available. Instead we're shooting in the dark, and quite often
* device will response with EAGAIN, indicating that it doesn't have frame
* ready. In turn, it means that the last frame we have obtained from the
* device is still good, and we can reply with the cached frames. The only
* case when we need to keep trying to obtain a new frame is when frame cache
* is empty. To prevent ourselves from an indefinite loop in case device got
* stuck on something (observed with some Microsoft devices) we will limit
* the loop by 2 second time period (which is more than enough to obtain
* something from the device) */
while (repeat == 1 && !cc->frames_cached &&
(_get_timestamp() - tick) < 2000000LL) {
/* Sleep for 10 millisec before repeating the attempt. */
_camera_sleep(10);
repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
r_scale, g_scale, b_scale, exp_comp);
}
if (repeat == 1 && !cc->frames_cached) {
/* Waited too long for the first frame. */
E("%s: Unable to obtain first video frame from the camera '%s' in %d milliseconds: %s.",
__FUNCTION__, cc->device_name,
(uint32_t)(_get_timestamp() - tick) / 1000, strerror(errno));
_qemu_client_reply_ko(qc, "Unable to obtain video frame from the camera");
return;
} else if (repeat < 0) {
/* An I/O error. */
E("%s: Unable to obtain video frame from the camera '%s': %s.",
__FUNCTION__, cc->device_name, strerror(errno));
_qemu_client_reply_ko(qc, strerror(errno));
return;
}
/* We have cached something... */
cc->frames_cached = 1;
/*
* Build the reply.
*/
/* Payload includes "ok:" + requested video and preview frames. */
payload_size = 3 + video_size + preview_size;
/* Send payload size first. */
_qemu_client_reply_payload(qc, payload_size);
/* After that send the 'ok:'. Note that if there is no frames sent, we should
* use prefix "ok" instead of "ok:" */
if (video_size || preview_size) {
qemud_client_send(qc, (const uint8_t*)"ok:", 3);
} else {
/* Still 3 bytes: zero terminator is required in this case. */
qemud_client_send(qc, (const uint8_t*)"ok", 3);
}
/* After that send video frame (if requested). */
if (video_size) {
qemud_client_send(qc, cc->video_frame, video_size);
}
/* After that send preview frame (if requested). */
if (preview_size) {
qemud_client_send(qc, (const uint8_t*)cc->preview_frame, preview_size);
}
}
/* Handles a message received from the emulated camera client.
* Queries received here are represented as strings:
* - 'connect' - Connects to the camera device (opens it).
* - 'disconnect' - Disconnexts from the camera device (closes it).
* - 'start' - Starts capturing video from the connected camera device.
* - 'stop' - Stop capturing video from the connected camera device.
* - 'frame' - Queries video and preview frames captured from the camera.
* Param:
* opaque - Camera service descriptor.
* msg, msglen - Message received from the camera factory client.
* client - Camera factory client pipe.
*/
static void
_camera_client_recv(void* opaque,
uint8_t* msg,
int msglen,
QemudClient* client)
{
/*
* Emulated camera client queries.
*/
/* Connect to the camera. */
static const char _query_connect[] = "connect";
/* Disconnect from the camera. */
static const char _query_disconnect[] = "disconnect";
/* Start video capturing. */
static const char _query_start[] = "start";
/* Stop video capturing. */
static const char _query_stop[] = "stop";
/* Query frame(s). */
static const char _query_frame[] = "frame";
char query_name[64];
const char* query_param = NULL;
CameraClient* cc = (CameraClient*)opaque;
/*
* Emulated camera queries are formatted as such:
* "<query name> [<parameters>]"
*/
T("%s: Camera client query: '%s'", __FUNCTION__, (char*)msg);
if (_parse_query((const char*)msg, query_name, sizeof(query_name),
&query_param)) {
E("%s: Invalid query '%s'", __FUNCTION__, (char*)msg);
_qemu_client_reply_ko(client, "Invalid query");
return;
}
/* Dispatch the query to an appropriate handler. */
if (!strcmp(query_name, _query_frame)) {
/* A frame is queried. */
_camera_client_query_frame(cc, client, query_param);
} else if (!strcmp(query_name, _query_connect)) {
/* Camera connection is queried. */
_camera_client_query_connect(cc, client, query_param);
} else if (!strcmp(query_name, _query_disconnect)) {
/* Camera disnection is queried. */
_camera_client_query_disconnect(cc, client, query_param);
} else if (!strcmp(query_name, _query_start)) {
/* Start capturing is queried. */
_camera_client_query_start(cc, client, query_param);
} else if (!strcmp(query_name, _query_stop)) {
/* Stop capturing is queried. */
_camera_client_query_stop(cc, client, query_param);
} else {
E("%s: Unknown query '%s'", __FUNCTION__, (char*)msg);
_qemu_client_reply_ko(client, "Unknown query");
}
}
/* Emulated camera client has been disconnected from the service. */
static void
_camera_client_close(void* opaque)
{
CameraClient* cc = (CameraClient*)opaque;
D("%s: Camera client for device '%s' on input channel %d is now closed",
__FUNCTION__, cc->device_name, cc->inp_channel);
_camera_client_free(cc);
}
/********************************************************************************
* Camera service API
*******************************************************************************/
/* Connects a client to the camera service.
* There are two classes of the client that can connect to the service:
* - Camera factory that is insterested only in listing camera devices attached
* to the host.
* - Camera device emulators that attach to the actual camera devices.
* The distinction between these two classes is made by looking at extra
* parameters passed in client_param variable. If it's NULL, or empty, the client
* connects to a camera factory. Otherwise, parameters describe the camera device
* the client wants to connect to.
*/
static QemudClient*
_camera_service_connect(void* opaque,
QemudService* serv,
int channel,
const char* client_param)
{
QemudClient* client = NULL;
CameraServiceDesc* csd = (CameraServiceDesc*)opaque;
D("%s: Connecting camera client '%s'",
__FUNCTION__, client_param ? client_param : "Factory");
if (client_param == NULL || *client_param == '\0') {
/* This is an emulated camera factory client. */
client = qemud_client_new(serv, channel, client_param, csd,
_factory_client_recv, _factory_client_close,
NULL, NULL);
} else {
/* This is an emulated camera client. */
CameraClient* cc = _camera_client_create(csd, client_param);
if (cc != NULL) {
client = qemud_client_new(serv, channel, client_param, cc,
_camera_client_recv, _camera_client_close,
NULL, NULL);
}
}
return client;
}
void
android_camera_service_init(void)
{
static int _inited = 0;
if (!_inited) {
_camera_service_init(&_camera_service_desc);
QemudService* serv = qemud_service_register( SERVICE_NAME, 0,
&_camera_service_desc,
_camera_service_connect,
NULL, NULL);
if (serv == NULL) {
derror("%s: Could not register '%s' service",
__FUNCTION__, SERVICE_NAME);
return;
}
D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME);
}
}
void
android_list_web_cameras(void)
{
CameraInfo ci[MAX_CAMERA];
int connected_cnt;
int i;
/* Enumerate camera devices connected to the host. */
connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
if (connected_cnt <= 0) {
return;
}
printf("List of web cameras connected to the computer:\n");
for (i = 0; i < connected_cnt; i++) {
printf(" Camera '%s' is connected to device '%s' on channel %d using pixel format '%.4s'\n",
ci[i].display_name, ci[i].device_name, ci[i].inp_channel,
(const char*)&ci[i].pixel_format);
}
printf("\n");
}