luma/rico: added EmuCapture script and readme for it

The script is complete at this point. Multiple instances of it can be run
with different AVDs at the same time.

Change-Id: I7fb10330445d5515112128a08314afb66b39629d
diff --git a/rico/emucapture/README.md b/rico/emucapture/README.md
new file mode 100644
index 0000000..127e224
--- /dev/null
+++ b/rico/emucapture/README.md
@@ -0,0 +1,70 @@
+**EmuCapture** provides a socket interface for streaming screen capture data from Android emulators. It is meant to be used in place of the [Minicap library](https://github.com/openstf/minicap) when working with emulators instead of physical devices. 
+
+EmuCapture has been tested on Ubuntu 14.04 and Python 2.7.8. Compatibility with other OSes and Python versions is unknown.
+
+## Requirements
+* Xvfb
+* Imagemagick
+* Android SDK
+	- <sdk-path>/tools and <sdk_path>/platform-tools> need to be added to your PATH variable.
+	- AVDs need to be created before using EmuCapture. If you want to run N concurrent emulators, there should be N separate AVDs.
+
+## Features
+* Uses Xvfb to start a display (with supplied display number) with a virtual framebuffer that is memory mapped to a file.
+* Launches Android emulator on that display.
+* Finds out dimension of window and uses Imagemagick to capture corresponding region from framebuffer and convert it to JPEG.
+* Starts a TCP server that listens on port (6100 + display_number) for clients and once connected sends out a global header followed by a continous stream of JPEG frames (format in Usage section).
+
+## Usage
+
+### Running
+
+```bash
+usage: emucapture.py [-h] display_num avd_name
+
+positional arguments:
+  display_num  The display number to use with Xvfb
+  avd_name     The name of the AVD instance to use
+
+optional arguments:
+  -h, --help   show this help message and exit
+ ```
+It takes upwards of five minutes for EmuCapture to launch the Emulator and be ready for clients. Once it is ready, you can connect to it using
+```bash
+nc localhost <portnumber>
+```
+The port number is computed from the display number. For example, if the display number is 3, the port number is 6103. If it is 4, port number is 6104 and so on. The default display number is 0 and other applications will have windows on it (you should not use it with EmuCapture).
+
+We closely follow the [Minicap protocol](https://github.com/openstf/minicap)). When you first connect to the socket, you get a global header followed by the first frame. The global header will not appear again. More frames keep getting sent until you stop EmuCapture.
+
+### Global header binary format
+
+| Bytes | Length | Type | Explanation |
+|-------|--------|------|-------------|
+| 0     | 1 | unsigned char | Version (currently 1) |
+| 1     | 1 | unsigned char | Size of the header (from byte 0) |
+| 2-5   | 4 | uint32 (low endian) | Number to identify specific emulator instance when querying devices through ADB (eg. 5554) |
+| 6-9   | 4 | uint32 (low endian) | Display width in pixels |
+| 10-13 | 4 | uint32 (low endian) | Display height in pixels |
+| 14-17 | 4 | uint32 (low endian) | Display width in pixels |
+| 18-21 | 4 | uint32 (low endian) | Display height in pixels |
+| 22    | 1 | unsigned char | Display orientation (fixed to 0) |
+| 23    | 1 | unsigned char | Quirk bitflags (fixed to 1) |
+
+#### Quirk bitflags
+
+Minicap provided three Quirk bitflags to let the client know if Minicap was operating under certain conditions.
+Currently, we always report QUIRK_DUMB as that condition is always true for EmuCapture:
+
+| Value | Name | Explanation |
+|-------|------|-------------|
+| 1     | QUIRK_DUMB | Frames will get sent even if there are no changes from the previous frame. Informative, doesn't require any actions on your part. You can limit the capture rate by reading frame data slower in your own code if you wish. |
+
+### Frame binary format
+
+Appears a potentially unlimited number of times.
+
+| Bytes | Length | Type | Explanation |
+|-------|--------|------|-------------|
+| 0-3   | 4 | uint32 (low endian) | Frame size in bytes (=n) |
+| 4-(n+4) | n | unsigned char[] | Frame in JPG format |
\ No newline at end of file
diff --git a/rico/emucapture/emucapture.py b/rico/emucapture/emucapture.py
new file mode 100644
index 0000000..3452407
--- /dev/null
+++ b/rico/emucapture/emucapture.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python2.7
+# Copyright 2016 The Vanadium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+"""Continuously capture screenshots from an Android emulator instance.
+
+Launches Xvfb with supplied display number, launches android emulator with
+supplied avd name on that display, and captures the Xvfb framebuffer in a loop
+as JPEG images and outputs in through a TCP socket in a format similar to
+Minicap (https://github.com/openstf/minicap).
+"""
+import argparse
+import atexit
+import os
+import re
+import socket
+import struct
+from subprocess import PIPE
+from subprocess import Popen
+import time
+import traceback
+
+# The depth needed for the emulator is minimum of 24
+# The size should be changed if you change the display associated with
+# the AVD that you're using with the emulator.
+SCREEN_SIZE = "1000x900x24"
+# The number of seconds we wait for XVFB to start.
+XVFB_WAIT_TIME = 5
+# The number of seconds we wait for the emulator to start.
+# If we don't wait long enough, we might grab wrong dimension
+# for the emulator window (initially it is smaller when it is loading).
+EMULATOR_WAIT_TIME = 300
+# Each display is assigned a port number that starts from this port
+# Display :0 will be port 6100, display :1 will be 6101 etc.
+BASE_PORT_NUM = 6100
+# To be compatible with Minicap, we use a similar header format as specified at
+# https://github.com/openstf/minicap (under "Usage")
+HEADER_SIZE = 24
+HEADER_VERSION = 1
+
+procs = []
+
+
+@atexit.register
+def kill_subprocesses():
+  """Auto kill subprocesses (Xvfb and emulator) when this script is killed.
+
+  Details at: http://sharats.me/the-ever-useful-and-neat-subprocess-module.html
+  """
+  for process in procs:
+    process.kill()
+
+
+def run_shell_cmd(command):
+  """Runs the command in a shell and returns a tuple with output and error."""
+  try:
+    process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
+  except OSError:
+    print "could not run command: " + command
+    traceback.print_exc()
+  return process.communicate()
+
+
+def get_window_names(phrase, display_number):
+  """Names of all windows on given display whose names contain the phrase."""
+
+  out = run_shell_cmd("xwininfo -display localhost:" + str(display_number) +
+                      " -tree -root | grep " + phrase)[0]
+  window_names = []
+  for line in out.split("\n"):
+    try:
+      window_names.append(line.split("\"")[1])
+    except IndexError:
+      print "Window name without a \" was found."
+  return window_names
+
+
+def get_window_details(win_name, display_number):
+  """Return the x/y coordinates as well as width/height of a window."""
+
+  out = run_shell_cmd("xwininfo -display localhost:" + str(display_number) +
+                      " -name " + win_name)[0]
+  lines = out.split("\n")
+  x = y = w = h = 0
+  xt = yt = wt = ht = False
+  for line in lines:
+    if "Absolute upper-left X:" in line:
+      x = int(re.sub("[^0-9]", "", line))
+      xt = True
+    elif "Absolute upper-left Y:" in line:
+      y = int(re.sub("[^0-9]", "", line))
+      yt = True
+    elif "Width:" in line:
+      w = int(re.sub("[^0-9]", "", line))
+      wt = True
+    elif "Height:" in line:
+      h = int(re.sub("[^0-9]", "", line))
+      ht = True
+  if xt and yt and wt and ht:
+    return (x, y, w, h)
+  else:
+    raise RuntimeError("Could not find position or size of specified window.")
+
+
+if __name__ == "__main__":
+  parser = argparse.ArgumentParser()
+  parser.add_argument("display_num", help="The display number to use with Xvfb",
+                      type=int)
+  parser.add_argument("avd_name", help="The name of the AVD instance to use")
+  args = parser.parse_args()
+  display_num = args.display_num
+  avd_name = args.avd_name
+
+  # Folder where xvfb will store its framebuffer as a memory mapped file.
+  # We create a separate subdirectory under /var/tmp for each display.
+  fbdir = os.path.join("/var/tmp", str(display_num))
+  if not os.path.exists(fbdir):
+    os.mkdir(fbdir)
+
+  # Start xvfb and give it a few seconds to start.
+  cmd = ["Xvfb", ":"+str(display_num), "-ac", "-fbdir", fbdir, "-screen", "0",
+         SCREEN_SIZE]
+  proc_xvfb = Popen(cmd, stdout=PIPE, stderr=PIPE)
+  procs.append(proc_xvfb)
+  time.sleep(XVFB_WAIT_TIME)
+  print "display: " + str(display_num)
+
+  # Run the emulator on the display created by Xvfb.
+  cmd = ("DISPLAY=:" + str(display_num) + " emulator64-x86 -avd " + avd_name +
+         " -noaudio -nojni -netfast -no-boot-anim -qemu -enable-kvm -snapshot")
+  proc_emulator = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+  procs.append(proc_emulator)
+  time.sleep(EMULATOR_WAIT_TIME)
+
+  # We assume that each display will only have one window with an emulator.
+  # We locate it by using the fact that the window name contains the avd_name
+  # that we specify when running the emulator.
+  window_name = get_window_names(avd_name, display_num)[0]
+  window_x, window_y, window_width, window_height = get_window_details(
+      window_name, display_num)
+
+  # This is where we start the server that dumps the screenshots as JPEG images
+  # The port number assigned is dependent on the diplay number
+  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  port = BASE_PORT_NUM + display_num
+  server_address = ("localhost", port)
+  sock.bind(server_address)
+  sock.listen(1)
+
+  # This command grabs the pixels corresponding to the emulator window from the
+  # Xvfb framebuffer (that is mapped to a file) and converts it to a JPEG image
+  # The - argument at the end means that the JPEG image is output to stdout
+  cmd = ("convert /var/tmp/" + str(display_num) +
+         "/Xvfb_screen0 -crop {}x{}+{}+{} -").format(window_width,
+                                                     window_height, window_x,
+                                                     window_y)
+
+  # We use a similar header format as Minicap -- specified here
+  # https://github.com/openstf/minicap (under "Usage").
+  # The only difference is that instead of the real PID we have a number that
+  # helps identify the emulator this server is associated with.
+  # We grab that number (say "5554") from the window name (which in this case
+  # will be 5554:avd_name). We also have the same virtual display width/height
+  # as the real display width/height.
+  pid = int(window_name.split(":")[0])
+  print "emulator: " + str(pid)
+  print "port: " + str(port)
+  # The struct python library is used to pack different data as bytes.
+  # More info here: https://docs.python.org/3/library/struct.html
+  global_header = (struct.pack("B", HEADER_VERSION) +
+                   struct.pack("B", HEADER_SIZE) +
+                   struct.pack("<I", pid) +
+                   struct.pack("<I", window_width) +
+                   struct.pack("<I", window_height) +
+                   struct.pack("<I", window_width) +
+                   struct.pack("<I", window_height) +
+                   struct.pack("B", 0) +
+                   struct.pack("B", 1))
+
+  while True:
+    connection, client_address = sock.accept()
+    # Every time a new client connects, we send the global header first.
+    connection.sendall(global_header)
+
+    try:
+      while True:
+        proc = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+        jpeg, err = proc.communicate()
+        # Each frame consist of the frame size as a 4 byte uint32 followed by
+        # the actual JPEG bytes.
+        frame_size = struct.pack("<I", len(jpeg) + 4)
+        frame = frame_size + jpeg
+        connection.sendall(frame)
+    except socket.error, e:
+      # This handles the case when the client disconnects unexpectedly.
+      # We just go back to accepting another connection.
+      print e
+    finally:
+      connection.close()
+
+