Add -webcam commandline option to control webcam emulation

-webcam name=<name>[,dir=<direction>] options controls web cameras to use for emulation.

Change-Id: I961fd399c7e041541adda040dd24f194cc383cb9
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index 6a904d6..fb53536 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -240,6 +240,110 @@
 abstract    = Fake camera control
 description = Must be 'back', if fake camera is facing back, 'front', if fake camera is facing front, or 'off' if fake camera is disabled.
 
+# Number of emulated web cameras
+#
+name        = hw.webcam.count
+type        = integer
+default     = 6
+abstract    = Number of emulated web cameras
+description = Defines number of web cameras to emulate. 0 disables webcam emulation.
+
+# Defines name of the emulated webcam with index 0
+#
+name        = hw.webcam.0.name
+type        = string
+default     = webcam0
+abstract    = Name of the 1-st emulated web camera
+description = Emulator-generated platform-independent name identifying a camera in the list of enumerated web cameras.
+
+# Defines name of the emulated webcam with index 1
+#
+name        = hw.webcam.1.name
+type        = string
+default     = webcam1
+abstract    = Name of the 2-nd emulated web camera
+description = Emulator-generated platform-independent camera name.
+
+# Defines name of the emulated webcam with index 2
+#
+name        = hw.webcam.2.name
+type        = string
+default     = webcam2
+abstract    = Name of the 3-rd emulated web camera
+description = Emulator-generated platform-independent camera name.
+
+# Defines name of the emulated webcam with index 3
+#
+name        = hw.webcam.3.name
+type        = string
+default     = webcam3
+abstract    = Name of the 4-th emulated web camera
+description = Emulator-generated platform-independent camera name.
+
+# Defines name of the emulated webcam with index 4
+#
+name        = hw.webcam.4.name
+type        = string
+default     = webcam4
+abstract    = Name of the 5-th emulated web camera
+description = Emulator-generated platform-independent camera name.
+
+# Defines name of the emulated webcam with index 5
+#
+name        = hw.webcam.5.name
+type        = string
+default     = webcam5
+abstract    = Name of the 6-th emulated web camera
+description = Emulator-generated platform-independent camera name.
+
+# Defines direction of the emulated webcam with index 0
+#
+name        = hw.webcam.0.direction
+type        = string
+default     = front
+abstract    = 1-st emulated web camera direction
+description = Direction of the 1-st emulated web camera
+
+# Defines direction of the emulated webcam with index 1
+#
+name        = hw.webcam.1.direction
+type        = string
+default     = front
+abstract    = 2-nd emulated web camera direction
+description = Direction of the 2-nd emulated web camera
+
+# Defines direction of the emulated webcam with index 2
+#
+name        = hw.webcam.2.direction
+type        = string
+default     = front
+abstract    = 3-rd emulated web camera direction
+description = Direction of the 3-rd emulated web camera
+
+# Defines direction of the emulated webcam with index 3
+#
+name        = hw.webcam.3.direction
+type        = string
+default     = front
+abstract    = 4-th emulated web camera direction
+description = Direction of the 4-th emulated web camera
+
+# Defines direction of the emulated webcam with index 4
+#
+name        = hw.webcam.4.direction
+type        = string
+default     = front
+abstract    = 5-th emulated web camera direction
+description = Direction of the 5-th emulated web camera
+
+# Defines direction of the emulated webcam with index 5
+#
+name        = hw.webcam.5.direction
+type        = string
+default     = front
+abstract    = 6-th emulated web camera direction
+description = Direction of the 6-th emulated web camera
+
 # Maximum VM heap size
 # Higher values are required for high-dpi devices
 # Default will depend on RAM size.
diff --git a/android/camera/camera-capture-linux.c b/android/camera/camera-capture-linux.c
index 072d06e..50324f0 100644
--- a/android/camera/camera-capture-linux.c
+++ b/android/camera/camera-capture-linux.c
@@ -1048,6 +1048,9 @@
         if (cd != NULL) {
             LinuxCameraDevice* lcd = (LinuxCameraDevice*)cd->opaque;
             if (!_camera_device_get_info(lcd, cis + found)) {
+                char user_name[24];
+                sprintf(user_name, "webcam%d", found);
+                cis[found].display_name = ASTRDUP(user_name);
                 cis[found].in_use = 0;
                 found++;
             }
diff --git a/android/camera/camera-capture-windows.c b/android/camera/camera-capture-windows.c
index e5120ab..0fb172e 100755
--- a/android/camera/camera-capture-windows.c
+++ b/android/camera/camera-capture-windows.c
@@ -504,7 +504,11 @@
                  * but the actual numbers may vary). */
                 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(CameraFrameDim));
                 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->width = wcd->frame_bitmap->bmiHeader.biWidth;
                     cis[found].frame_sizes->height = wcd->frame_bitmap->bmiHeader.biHeight;
diff --git a/android/camera/camera-common.h b/android/camera/camera-common.h
index 0cf8cea..a3fdd5f 100755
--- a/android/camera/camera-common.h
+++ b/android/camera/camera-common.h
@@ -88,12 +88,16 @@
  * representing that camera.
  */
 typedef struct CameraInfo {
+    /* User-friendly camera display name. */
+    char*               display_name;
     /* Device name for the camera. */
     char*               device_name;
     /* Input channel for the camera. */
     int                 inp_channel;
     /* Pixel format chosen for the camera. */
     uint32_t            pixel_format;
+    /* Direction the camera is facing: 'front', or 'back' */
+    char*               direction;
     /* Array of frame sizes supported for the pixel format chosen for the camera.
      * The size of the array is defined by the frame_sizes_num field of this
      * structure. */
@@ -120,8 +124,12 @@
 static __inline__ void _camera_info_free(CameraInfo* ci)
 {
     if (ci != NULL) {
+        if (ci->display_name != NULL)
+            free(ci->display_name);
         if (ci->device_name != NULL)
             free(ci->device_name);
+        if (ci->direction != NULL)
+            free(ci->direction);
         if (ci->frame_sizes != NULL)
             free(ci->frame_sizes);
         AFREE(ci);
diff --git a/android/camera/camera-service.c b/android/camera/camera-service.c
index a35f595..96d3c44 100644
--- a/android/camera/camera-service.c
+++ b/android/camera/camera-service.c
@@ -19,6 +19,7 @@
  */
 
 #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"
@@ -62,48 +63,6 @@
 static CameraServiceDesc    _camera_service_desc;
 
 /********************************************************************************
- * CameraServiceDesc API
- *******************************************************************************/
-
-/* Initializes camera service descriptor.
- */
-static void
-_camera_service_init(CameraServiceDesc* csd)
-{
-    /* Enumerate camera devices connected to the host. */
-    csd->camera_count = enumerate_camera_devices(csd->camera_info, MAX_CAMERA);
-    if (csd->camera_count >= 0) {
-        D("%s: Enumerated %d cameras connected to the host",
-          __FUNCTION__, csd->camera_count);
-    } else {
-        E("%s: Unable to enumerate camera devices", __FUNCTION__);
-        csd->camera_count = 0;
-        return;
-    }
-}
-
-/* Gets camera information for the given camera device name.
- * Param:
- *  cs - Initialized camera service descriptor.
- *  name - Camera 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. Note that camera information returned
- *  from this routine is constant.
- */
-static CameraInfo*
-_camera_service_get_camera_info(CameraServiceDesc* cs, const char* name)
-{
-    int n;
-    for (n = 0; n < cs->camera_count; n++) {
-        if (!strcmp(cs->camera_info[n].device_name, name)) {
-            return &cs->camera_info[n];
-        }
-    }
-    return NULL;
-}
-
-/********************************************************************************
  * Helper routines
  *******************************************************************************/
 
@@ -357,7 +316,7 @@
 }
 
 /* Represents camera information as a string formatted as follows:
- *  'name=<devname> channel=<num> pix=<format> framedims=<widh1xheight1,widh2xheight2,widhNxheightN>\n'
+ *  '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
@@ -394,6 +353,12 @@
     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);
@@ -414,6 +379,143 @@
     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].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
+ *******************************************************************************/
+
+/* Initializes camera service descriptor.
+ */
+static void
+_camera_service_init(CameraServiceDesc* csd)
+{
+    CameraInfo ci[MAX_CAMERA];
+    int connected_cnt;
+    int i;
+
+    /* 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;
+    connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
+    if (connected_cnt <= 0) {
+        /* Nothing is connected - nothing to emulate. */
+        return;
+    }
+
+    /* For each webcam declared in hw.ini find an actual camera information
+     * descriptor, and save it into the service descriptor for the emulation. */
+    for (i = 0; i < android_hw->hw_webcam_count; i++) {
+        const char* disp_name;
+        const char* dir;
+        CameraInfo* found;
+
+        switch (i) {
+            case 0:
+                disp_name = android_hw->hw_webcam_0_name;
+                dir = android_hw->hw_webcam_0_direction;
+                break;
+            case 1:
+                disp_name = android_hw->hw_webcam_1_name;
+                dir = android_hw->hw_webcam_1_direction;
+                break;
+            case 2:
+                disp_name = android_hw->hw_webcam_2_name;
+                dir = android_hw->hw_webcam_2_direction;
+                break;
+            case 3:
+                disp_name = android_hw->hw_webcam_3_name;
+                dir = android_hw->hw_webcam_3_direction;
+                break;
+            case 4:
+                disp_name = android_hw->hw_webcam_4_name;
+                dir = android_hw->hw_webcam_4_direction;
+                break;
+            case 5:
+            default:
+                disp_name = android_hw->hw_webcam_5_name;
+                dir = android_hw->hw_webcam_5_direction;
+                break;
+        }
+        found = _camera_info_get_by_display_name(disp_name, ci, connected_cnt);
+        if (found != NULL) {
+            /* Save to the camera info array that will be used by the service.
+             * Note that we just copy everything over, and NULL the source
+             * record. */
+            memcpy(csd->camera_info + csd->camera_count, found, sizeof(CameraInfo));
+            /* 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++;
+            memset(found, 0, sizeof(CameraInfo));
+        }
+    }
+}
+
+/* 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
  *******************************************************************************/
@@ -743,7 +845,7 @@
      * 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(csd, cc->device_name);
+    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);
@@ -1303,3 +1405,25 @@
         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");
+}
diff --git a/android/camera/camera-service.h b/android/camera/camera-service.h
index 6794187..e8df288 100644
--- a/android/camera/camera-service.h
+++ b/android/camera/camera-service.h
@@ -24,4 +24,7 @@
 /* Initializes camera emulation service over qemu pipe. */
 extern void android_camera_service_init(void);
 
+/* Lists available web cameras. */
+extern void android_list_web_cameras(void);
+
 #endif  /* ANDROID_CAMERA_CAMERA_SERVICE_H_ */
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 3bc2197..0cf358f 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -157,6 +157,7 @@
 OPT_PARAM( gpu, "<mode>", "set hardware OpenGLES emulation mode" )
 
 OPT_PARAM( fake_camera, "<mode>", "set fake camera emulation mode" )
+OPT_LIST( webcam, "name=<name>[,dir=<direction>]", "setup web camera emulation" )
 
 #undef CFG_FLAG
 #undef CFG_PARAM
diff --git a/android/help.c b/android/help.c
index 7f89376..4087911 100644
--- a/android/help.c
+++ b/android/help.c
@@ -1469,6 +1469,25 @@
     );
 }
 
+static void
+help_webcam(stralloc_t* out)
+{
+    PRINTF(
+    "  Use -webcam off to disable web camera emulation.\n"
+    "  Use -webcam list to list web cameras available for emulation.\n"
+    "  Use -webcam name=<name>[,dir=<direction>] to setup parameters for web camera emulation.\n"
+
+    "  <name> platform-independent name identifying emulated camera device.\n"
+    "  use '-webcam list' to obtain the list of emulated camera devices.\n"
+    "  <direction> defines direction the camera is facing. Valid values are:\n\n"
+
+    "     front -> emulate camera as facing front\n"
+    "     back  -> emulate camera as facing back\n\n"
+
+    "  Default direction value for emulated web camera is 'front'\n\n"
+    );
+}
+
 #define  help_no_skin   NULL
 #define  help_netspeed  help_shaper
 #define  help_netdelay  help_shaper
diff --git a/android/main.c b/android/main.c
index 314779f..c2f8663 100644
--- a/android/main.c
+++ b/android/main.c
@@ -155,6 +155,65 @@
     return convertMBToBytes(imageMB);
 }
 
+/* Parses a -webcam option, extracting 'name', and 'dir' values.
+ * Param:
+ *  param - -webcam option, that should be formatted as such:
+ *      name=<name>[,dir=<direction>]
+ * name, name_size - buffer (and its size) where to receive <name>
+ * dir, dir_size - buffer (and its size) where to receive <direction>
+ */
+static void
+_parseWebcamOption(const char* param,
+                   char* name, size_t name_size,
+                   char* dir, size_t dir_size)
+{
+    const char* dr;
+    const char* wc_opt = param;
+
+    /* Must start with 'name=' */
+    if (strlen(wc_opt) <= 5 || memcmp(wc_opt, "name=", 5)) {
+        derror("Invalid value for -webcam parameter: %s\n", param);
+        exit(1);
+    }
+
+    /* Move on to 'name' value. */
+    wc_opt += 5;
+    dr = strchr(wc_opt, ',');
+    if (dr == NULL) {
+        dr = wc_opt + strlen(wc_opt);
+    }
+
+    /* Make sure that <name> fits */
+    if ((dr - wc_opt) < name_size) {
+        memcpy(name, wc_opt, dr - wc_opt);
+        name[dr - wc_opt] = '\0';
+        if (*dr == '\0') {
+            /* Default direction value is 'front' */
+            strcpy(dir, "front");
+            return;
+        } else {
+            dr++;
+        }
+    } else {
+        derror("Invalid <name> value for -webcam parameter: %s\n", param);
+        exit(1);
+    }
+
+    /* Parse 'dir'. Must begin with 'dir=' */
+    if (strlen(dr) <= 4 || memcmp(dr, "dir=", 4)) {
+        derror("Invalid value for -webcam parameter: %s\n", param);
+        exit(1);
+    }
+    dr += 4;
+    /* Check the bounds, and the values */
+    if (strlen(dr) >= dir_size || (strcmp(dr, "front") && strcmp(dr, "back"))) {
+        derror("Invalid <direction> value for -webcam parameter: %s\n"
+               "Valid values are: 'front', or 'back'\n", param);
+        exit(1);
+    }
+    strcpy(dir, dr);
+}
+
 int main(int argc, char **argv)
 {
     char   tmp[MAX_PATH];
@@ -1098,6 +1157,67 @@
         }
     }
 
+    if (opts->webcam != NULL) {
+        ParamList*  pl = opts->webcam;
+        int webcam_num = 0;
+        for ( ; pl != NULL; pl = pl->next ) {
+            char webcam_name[64];
+            char webcam_dir[16];
+            if (!strcmp(pl->param, "off")) {
+                /* If 'off' is passed, there must be no other -webcam options. */
+                if (webcam_num || pl->next != NULL) {
+                    derror("'-webcam off' cannot be combined with other -webcam otions\n");
+                    exit(1);
+                }
+                break;
+            }
+            if (!strcmp(pl->param, "list")) {
+                /* If 'list' is passed, there must be no other -webcam options. */
+                if (webcam_num || pl->next != NULL) {
+                    derror("'-webcam list' cannot be combined with other -webcam otions\n");
+                    exit(1);
+                }
+                args[n++] = "-list-webcam";
+                break;
+            }
+            /* Extract name, and direction */
+            _parseWebcamOption(pl->param, webcam_name, sizeof(webcam_name),
+                               webcam_dir, sizeof(webcam_dir));
+            /* Save them to appropriate field in hw.ini */
+            switch (webcam_num) {
+                case 0:
+                    hw->hw_webcam_0_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_0_direction   = ASTRDUP(webcam_dir);
+                    break;
+                case 1:
+                    hw->hw_webcam_1_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_1_direction   = ASTRDUP(webcam_dir);
+                    break;
+                case 2:
+                    hw->hw_webcam_2_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_2_direction   = ASTRDUP(webcam_dir);
+                    break;
+                case 3:
+                    hw->hw_webcam_3_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_3_direction   = ASTRDUP(webcam_dir);
+                    break;
+                case 4:
+                    hw->hw_webcam_4_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_4_direction   = ASTRDUP(webcam_dir);
+                    break;
+                case 5:
+                    hw->hw_webcam_5_name        = ASTRDUP(webcam_name);
+                    hw->hw_webcam_5_direction   = ASTRDUP(webcam_dir);
+                    break;
+                default:
+                    derror("Too many -webcam options. Maximum number of -webcam options is 6\n");
+                    exit(1);
+            }
+            webcam_num++;
+        }
+        hw->hw_webcam_count = webcam_num;
+    }
+
     /* physical memory is now in hw->hw_ramSize */
 
     hw->avd_name = ASTRDUP(avdInfo_getName(avd));