int enumerate_camera_devices(CameraInfo* cis, int max) { /* Array containing emulated webcam frame dimensions. * capXxx API provides device independent frame dimensions, by scaling frames * received from the device to whatever dimensions were requested by the user. * So, we can just use a small set of frame dimensions to emulate. */ static const CameraFrameDim _emulate_dims[] = { /* Emulates 640x480 frame. */ {640, 480}, /* Emulates 352x288 frame (required by camera framework). */ {352, 288}, /* Emulates 320x240 frame (required by camera framework). */ {320, 240}, /* Emulates 176x144 frame (required by camera framework). */ {176, 144} }; int inp_channel, found = 0; for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) { char name[256]; CameraDevice* cd; snprintf(name, sizeof(name), "%s%d", _default_window_name, found); cd = camera_device_open(name, inp_channel); if (cd != NULL) { WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque; /* Unfortunately, on Windows we have to start capturing in order to get the * actual frame properties. */ if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) { cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims)); if (cis[found].frame_sizes != NULL) { char disp_name[24]; sprintf(disp_name, "webcam%d", found); cis[found].display_name = ASTRDUP(disp_name); cis[found].device_name = ASTRDUP(name); cis[found].direction = ASTRDUP("front"); cis[found].inp_channel = inp_channel; cis[found].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims); memcpy(cis[found].frame_sizes, _emulate_dims, sizeof(_emulate_dims)); cis[found].pixel_format = wcd->pixel_format; cis[found].in_use = 0; found++; } else { E("%s: Unable to allocate dimensions", __FUNCTION__); } camera_device_stop_capturing(cd); } else { /* No more cameras. */ camera_device_close(cd); break; } camera_device_close(cd); } else { /* No more cameras. */ break; } } return found; }
/* 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); }