Adding code for CrOS AV Analysis' Recording Server

Much thanks to @timkovich for authoring this code! These are the
scripts meant to setup and perform as the recording server which
will run on recording VMs set for ChromeOS AV team in LindaVista.
The function of the server is to listen for recording requests,
start recording using ffmpeg (setup in vm_prep.sh) and then upload
the files to our Google storage bucket. These files will be executed
by lab team setting up our machines.

BUG=967857
TEST=Tested Locally

Change-Id: Ia09c15cefd3e57438456b5f8f1f7ce03061ff04c
Reviewed-on: https://chromium-review.googlesource.com/1628135
Commit-Ready: Vinayak Suley <vsuley@chromium.org>
Tested-by: Vinayak Suley <vsuley@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: danny chan <dchan@chromium.org>
diff --git a/provingground/av_analysis/recording_server/README.txt b/provingground/av_analysis/recording_server/README.txt
new file mode 100644
index 0000000..9c5f4e2
--- /dev/null
+++ b/provingground/av_analysis/recording_server/README.txt
@@ -0,0 +1,11 @@
+These scripts setup the 'recording server' and its dependencies.
+
+Purpose:
+The purpose of the recording server is to always listen for recording requests
+ from DUTs, to record and then upload the results to Google storage.
+
+Usage:
+1. Create a directory 'rec_server' and copy files there.
+2. Switch to newly created directory.
+3. Run 'setup.sh' to setup the dependencies for the server.
+4. Then execute 'run_servlet.sh' to get the server up and running.
\ No newline at end of file
diff --git a/provingground/av_analysis/recording_server/requirements.txt b/provingground/av_analysis/recording_server/requirements.txt
new file mode 100644
index 0000000..3744fab
--- /dev/null
+++ b/provingground/av_analysis/recording_server/requirements.txt
@@ -0,0 +1,2 @@
+Flask
+google-cloud-storage
\ No newline at end of file
diff --git a/provingground/av_analysis/recording_server/run_servlet.sh b/provingground/av_analysis/recording_server/run_servlet.sh
new file mode 100644
index 0000000..0ec55f3
--- /dev/null
+++ b/provingground/av_analysis/recording_server/run_servlet.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Run the ffmpeg record and upload servlet.
+source env/bin/activate
+export GOOGLE_APPLICATION_CREDENTIALS=crosvideo-6c2cd30e9802.json
+export FLASK_APP=servlet.py
+
+flask run --host=0.0.0.0
diff --git a/provingground/av_analysis/recording_server/servlet.py b/provingground/av_analysis/recording_server/servlet.py
new file mode 100644
index 0000000..10f95dc
--- /dev/null
+++ b/provingground/av_analysis/recording_server/servlet.py
@@ -0,0 +1,97 @@
+import os
+import subprocess
+
+from flask import Flask, request, jsonify
+import google.cloud
+from google.cloud import storage
+
+app = Flask(__name__)
+
+
+def record(filename, duration):
+    """Record screen to |filename| and return error message if ffmpeg fails."""
+    # TODO(vsuley): This is hacky: fix the underlying problem.
+    # crbug.com/970936
+    r1 = subprocess.call(['modprobe', '-vr', 'uvcvideo'])
+    r2 = subprocess.call(['modprobe', '-v', 'uvcvideo'])
+    if r1 != 0 or r2 != 0:
+        return 'error reinstalling uvcvideo module'
+
+    ffmpeg_path = '/root/bin/ffmpeg'
+    ffmpeg = [ffmpeg_path, '-f', 'video4linux2', '-s', '1920x1080', '-r', '60',
+              '-i', '/dev/video0', '-f', 'alsa', '-i', 'hw:1', '-qscale', '2',
+              '-t', duration, '-strict', 'experimental', '-acodec', 'aac',
+              '-vcodec', 'libx264', '-pix_fmt', 'yuv420p', '-loglevel',
+              'error', '-y', filename]
+
+    # TODO(vsuley): Is 10s too much? Figure out how small you can make
+    # this number without breaking stuff. crbug.com/970938
+    wait_time = int(duration) + 10
+    try:
+        subprocess.check_output(ffmpeg, stderr=subprocess.STDOUT,
+                                timeout=wait_time)
+        return None
+    except subprocess.CalledProcessError as e:
+        return e.output.decode('utf-8')
+    except subprocess.TimeoutExpired as e:
+        return 'ffmpeg command timed out after %s seconds' % wait_time
+
+
+def upload(filename):
+    """Upload the recorded video file to Storage."""
+    BUCKET_NAME = 'cros-av-analysis'
+    try:
+        client = storage.Client()
+        bucket = client.get_bucket(BUCKET_NAME)
+        blob = bucket.blob(filename)
+
+        blob.upload_from_filename(filename)
+        blob.make_public()
+    except google.cloud.exceptions as e:
+        return 'Storage error: %s' % e
+
+    return None
+
+
+def outcome(status, filename):
+    """Clean up generated video and return status as JSON."""
+    try:
+        os.remove(filename)
+    except OSError as e:
+        app.logger.info(e)
+
+    if status['error'] is None:
+        del status['error']
+
+    return jsonify(status)
+
+
+@app.route('/record_and_upload', methods=['POST'])
+def record_and_upload():
+    """
+    Call ffmpeg to record screen then upload results to Storage.
+
+    @params filename (required): Filename to give recording.
+    @params duration (required): Length in seconds to record.
+
+    @returns: JSON containing error status.
+
+    """
+    filename = request.args.get('filename')
+    duration = request.args.get('duration')
+    status = {}
+
+    if not filename or not duration:
+        status['error'] = 'filename and duration are required'
+        return jsonify(status), 400
+
+    app.logger.info('Starting recording')
+    status['error'] = record(filename, duration)
+
+    # Short circuit if ffmpeg failed.
+    if status.get('error'):
+        return outcome(status, filename)
+
+    app.logger.info('Starting upload')
+    status['error'] = upload(filename)
+    return outcome(status, filename)
diff --git a/provingground/av_analysis/recording_server/setup.sh b/provingground/av_analysis/recording_server/setup.sh
new file mode 100755
index 0000000..24840c3
--- /dev/null
+++ b/provingground/av_analysis/recording_server/setup.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# Install the dependencies for the server.
+set -e
+
+# Setup virtualenv
+apt install python-pip
+pip install virtualenv
+python3 -m virtualenv venv
+source venv/bin/activate
+pip install -r requirements.txt