Add video_WebRtcPower and video_PowerConsumption test.

The video_WebRtcPower measures the power consumption of WebRTC loopback.
The video_PowerConsumption test turns off the AC power and runs the
video_WebRtcPower test.

BUG=chromium:391396
TEST=Run the test on peach_pit
CQ-DEPEND=CL:206995

Change-Id: I8749b73a4097dcad354e9de1ab919414f5e20043
Reviewed-on: https://chromium-review.googlesource.com/206963
Reviewed-by: Wu-Cheng Li <wuchengli@chromium.org>
Commit-Queue: Wen-Ding Li <wending@google.com>
Tested-by: Wen-Ding Li <wending@google.com>
diff --git a/client/site_tests/video_WebRtcPower/control b/client/site_tests/video_WebRtcPower/control
new file mode 100644
index 0000000..0b46d30
--- /dev/null
+++ b/client/site_tests/video_WebRtcPower/control
@@ -0,0 +1,25 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = "Chrome OS Project, chromeos-video@google.com"
+NAME = "video_WebRtcPower"
+PURPOSE = "Test the power consumption of video encode/decode in a WebRTC call"
+CRITERIA = """
+The test outputs the video power consumption.
+"""
+TIME = "MEDIUM"
+TEST_CATEGORY = "Performance"
+TEST_CLASS = "video"
+TEST_TYPE = "client"
+DEPENDENCIES = "hw_video_acc_vp8"
+BUG_TEMPLATE = {
+    'labels': ['OS-Chrome', 'Cr-OS-Kernel-Video'],
+}
+
+DOC = """
+This test checks whether video encode and decode power consumption
+has regression.
+"""
+
+job.run_test('video_WebRtcPower')
diff --git a/client/site_tests/video_WebRtcPower/loopback.html b/client/site_tests/video_WebRtcPower/loopback.html
new file mode 100644
index 0000000..edf13c5
--- /dev/null
+++ b/client/site_tests/video_WebRtcPower/loopback.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head><title>Loopback test</title></head>
+<body>
+  <video id="remoteVideo" autoplay muted></video>
+<script>
+var localStream, localPeerConnection, remotePeerConnection;
+var remoteVideo = document.getElementById("remoteVideo");
+
+function start() {
+  navigator.getUserMedia = navigator.getUserMedia ||
+    navigator.webkitGetUserMedia;
+  navigator.getUserMedia(
+    {
+      audio:false,
+      video:{
+        "mandatory": {
+          "minWidth": "1280",
+          "minHeight": "720",
+        }
+      }
+    },
+    gotLocalStream,
+    gotError);
+}
+
+function gotLocalStream(stream) {
+  localStream = stream;
+  var servers = null;
+
+  localPeerConnection = new webkitRTCPeerConnection(servers);
+  localPeerConnection.onicecandidate = gotLocalIceCandidate;
+
+  remotePeerConnection = new webkitRTCPeerConnection(servers);
+  remotePeerConnection.onicecandidate = gotRemoteIceCandidate;
+  remotePeerConnection.onaddstream = gotRemoteStream;
+
+  localPeerConnection.addStream(localStream);
+  localPeerConnection.createOffer(gotLocalDescription);
+}
+
+function gotError(error) {
+  console.log("navigator.getUserMedia error: ", error);
+}
+
+function gotRemoteStream(event) {
+  remoteVideo.src = URL.createObjectURL(event.stream);
+}
+
+function gotLocalDescription(description) {
+  localPeerConnection.setLocalDescription(description);
+  remotePeerConnection.setRemoteDescription(description);
+  remotePeerConnection.createAnswer(gotRemoteDescription);
+}
+
+function gotRemoteDescription(description) {
+  remotePeerConnection.setLocalDescription(description);
+  localPeerConnection.setRemoteDescription(description);
+}
+
+function gotLocalIceCandidate(event) {
+  if (event.candidate)
+    remotePeerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
+}
+
+function gotRemoteIceCandidate(event) {
+  if (event.candidate)
+    localPeerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
+}
+
+window.onload=start;
+</script>
+</body>
+</html>
diff --git a/client/site_tests/video_WebRtcPower/video_WebRtcPower.py b/client/site_tests/video_WebRtcPower/video_WebRtcPower.py
new file mode 100755
index 0000000..d352aa4
--- /dev/null
+++ b/client/site_tests/video_WebRtcPower/video_WebRtcPower.py
@@ -0,0 +1,230 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from contextlib import closing
+import logging
+import os
+import time
+import urllib2
+
+from autotest_lib.client.bin import test, utils
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros import chrome
+from autotest_lib.client.cros import power_status, power_utils
+from autotest_lib.client.cros import service_stopper
+
+
+# Chrome flags to use fake camera and skip camera permission.
+EXTRA_BROWSER_ARGS = ['--use-fake-device-for-media-stream',
+                      '--use-fake-ui-for-media-stream']
+NO_WEBRTC_HARDWARE_ACCELERATION_BROWSER_ARGS = ['--disable-webrtc-hw-decoding',
+                                                '--disable-webrtc-hw-encoding']
+
+FAKE_FILE_ARG = '--use-file-for-fake-video-capture="%s"'
+DOWNLOAD_BASE = 'http://commondatastorage.googleapis.com/chromiumos-test-assets-public/crowd/'
+VIDEO_NAME = 'crowd720_25frames.y4m'
+
+WEBRTC_INTERNALS_URL = 'chrome://webrtc-internals'
+RTC_INIT_HISTOGRAM = 'Media.RTCVideoDecoderInitDecodeSuccess'
+HISTOGRAMS_URL = 'chrome://histograms/' + RTC_INIT_HISTOGRAM
+
+# Minimum battery charge percentage we prefer to run the test
+BATTERY_INITIAL_CHARGED_MIN = 30
+
+WEBRTC_WITH_HARDWARE_ACCELERATION = 'webrtc_with_hardware_acceleration'
+WEBRTC_WITHOUT_HARDWARE_ACCELERATION = 'webrtc_without_hardware_acceleration'
+
+# Measure duration in seconds
+MEASURE_DURATION = 120
+# Time to exclude from calculation after firing a task [seconds].
+STABILIZATION_DURATION = 5
+
+class video_WebRtcPower(test.test):
+    """The test outputs the power consumption for WebRTC to performance
+    dashboard.
+    """
+    version = 1
+
+    def initialize(self):
+        # Objects that need to be taken care of in cleanup() are initialized
+        # here to None. Otherwise we run the risk of AttributeError raised in
+        # cleanup() masking a real error that caused the test to fail during
+        # initialize() before those variables were assigned.
+        self._backlight = None
+
+        self._services = service_stopper.ServiceStopper(
+            service_stopper.ServiceStopper.POWER_DRAW_SERVICES)
+        self._services.stop_services()
+
+        self._power_status = power_status.get_status()
+        # Verify that we are running on battery and the battery is
+        # sufficiently charged.
+        self._power_status.assert_battery_state(BATTERY_INITIAL_CHARGED_MIN)
+
+
+    def start_loopback(self, cr):
+        """
+        Opens WebRTC loopback page.
+
+        @param cr: Autotest Chrome instance.
+        """
+        tab = cr.browser.tabs[0]
+        tab.Navigate(cr.browser.http_server.UrlOf(
+                os.path.join(self.bindir, 'loopback.html')))
+        tab.WaitForDocumentReadyStateToBeComplete()
+
+
+    def assert_hardware_accelerated(self, cr, is_hardware_accelerated):
+        """
+        Checks if WebRTC decoding is hardware accelerated.
+
+        @param cr: Autotest Chrome instance.
+        @param is_hardware_accelerated: if is_hardware_accelerated is True then
+        assert hardware accelerated otherwise assert hardware not accelerated.
+
+        @raises error.TestError if decoding is not hardware accelerated while
+        is_hardware_accelerated is True or if decoding is hardware accelerated
+        while is_hardware_accelerated is False.
+        """
+        tab = cr.browser.tabs.New()
+        def histograms_loaded():
+            """Returns true if histogram is loaded."""
+            tab.Navigate(HISTOGRAMS_URL)
+            tab.WaitForDocumentReadyStateToBeComplete()
+            return tab.EvaluateJavaScript(
+                    'document.documentElement.innerText.search("%s") != -1'
+                    % RTC_INIT_HISTOGRAM)
+
+        if is_hardware_accelerated:
+            utils.poll_for_condition(
+                    histograms_loaded,
+                    timeout=5,
+                    exception=error.TestError(
+                            'Cannot find rtc video decoder histogram.'),
+                    sleep_interval=1)
+
+            if tab.EvaluateJavaScript(
+                    'document.documentElement.innerText.search('
+                    '"1 = 100.0%") == -1'):
+                raise error.TestError(
+                        'Video decode acceleration is not working.')
+        else:
+            time.sleep(5)
+            if histograms_loaded():
+                raise error.TestError(
+                        'Video decode acceleration should not be working.')
+
+        tab.Close()
+
+
+    def run_once(self):
+        # Download test video.
+        url = DOWNLOAD_BASE + VIDEO_NAME
+        local_path = os.path.join(self.bindir, VIDEO_NAME)
+        self.download_file(url, local_path)
+
+        self._backlight = power_utils.Backlight()
+        self._backlight.set_default()
+
+        measurements = [power_status.SystemPower(
+                self._power_status.battery_path)]
+        self._power_logger = power_status.PowerLogger(measurements)
+        self._power_logger.start()
+
+        # Run the WebRTC tests.
+        self.test_webrtc(local_path)
+
+        keyvals = self._power_logger.calc()
+        measurement_type = '_' + measurements[0].domain + '_pwr'
+        energy_rate_with_hw = keyvals[WEBRTC_WITH_HARDWARE_ACCELERATION +
+                                      measurement_type]
+        self.output_perf_value(
+                description='webrtc_energy_rate.mean',
+                value=energy_rate_with_hw,
+                units='W', higher_is_better=False)
+        # Save the results to the autotest results directory
+        self._power_logger.save_results(self.resultsdir)
+
+        # Find the energy of full battery
+        batinfo = self._power_status.battery[0]
+        self.energy_full_design = batinfo.energy_full_design
+        logging.info("energy_full_design = %0.3f Wh",
+                     self.energy_full_design)
+
+        logging.info('Expected battery life using WebRtc with'
+                     'hardware acceleration : %0.3f hours',
+                     self.energy_full_design / energy_rate_with_hw)
+
+        energy_rate_without_hw = keyvals[WEBRTC_WITHOUT_HARDWARE_ACCELERATION +
+                                         measurement_type]
+        logging.info('Expected battery life using WebRtc without'
+                     'hardware acceleration : %0.3f hours',
+                     self.energy_full_design / energy_rate_without_hw)
+
+
+    def test_webrtc(self, local_path):
+        """
+        Runs the WebRTC test with and without hardware acceleration.
+
+        @param local_path: the path to the video file.
+        @param test_duration: test duration for a run (seconds).
+        """
+        EXTRA_BROWSER_ARGS.append(FAKE_FILE_ARG % local_path)
+        with chrome.Chrome(extra_browser_args=EXTRA_BROWSER_ARGS) as cr:
+            # Open WebRTC loopback page.
+            cr.browser.SetHTTPServerDirectories(self.bindir)
+            self.start_loopback(cr)
+
+            # Make sure decode is hardware accelerated.
+            self.assert_hardware_accelerated(cr, True)
+
+            # Record the start time and the end time of this power
+            # measurement test run.
+            time.sleep(STABILIZATION_DURATION)
+            start_time = time.time()
+            time.sleep(MEASURE_DURATION)
+            self._power_logger.checkpoint(WEBRTC_WITH_HARDWARE_ACCELERATION,
+                                          start_time)
+
+        # Start chrome with test flag
+        # and disabled WebRTC hardware encode and decode flag.
+        with chrome.Chrome(extra_browser_args=EXTRA_BROWSER_ARGS +
+                           NO_WEBRTC_HARDWARE_ACCELERATION_BROWSER_ARGS) as cr:
+
+            # Open WebRTC loopback page.
+            cr.browser.SetHTTPServerDirectories(self.bindir)
+            self.start_loopback(cr)
+
+            # Make sure decode is not hardware accelerated.
+            self.assert_hardware_accelerated(cr, False)
+
+            # Record the start time and the end time of this power
+            # measurement test run.
+            time.sleep(STABILIZATION_DURATION)
+            start_time = time.time()
+            time.sleep(MEASURE_DURATION)
+            self._power_logger.checkpoint(WEBRTC_WITHOUT_HARDWARE_ACCELERATION,
+                                          start_time)
+
+
+    def download_file(self, url, local_path):
+        """
+        Downloads a file from the specified URL.
+
+        @param url: URL of the file.
+        @param local_path: the path that the file will be saved to.
+        """
+        logging.info('Downloading "%s" to "%s"', url, local_path)
+        with closing(urllib2.urlopen(url)) as r, open(local_path, 'wb') as w:
+            w.write(r.read())
+
+
+    def cleanup(self):
+        # cleanup() is run by common_lib/test.py.
+        if self._backlight:
+            self._backlight.restore()
+        if self._services:
+            self._services.restore_services()
+
+        super(video_WebRtcPower, self).cleanup()
diff --git a/server/site_tests/video_PowerConsumption/control.webrtc b/server/site_tests/video_PowerConsumption/control.webrtc
new file mode 100644
index 0000000..94365a8
--- /dev/null
+++ b/server/site_tests/video_PowerConsumption/control.webrtc
@@ -0,0 +1,44 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = "Chrome OS Team"
+NAME = "video_PowerConsumption"
+TIME = "MEDIUM"
+TEST_CATEGORY = "Performance"
+TEST_CLASS = "video"
+SUITE = "video"
+TEST_TYPE = "server"
+DEPENDENCIES = "rpm, hw_video_acc_vp8"
+BUG_TEMPLATE = {
+    'labels': ['OS-Chrome', 'Cr-OS-Kernel-Video'],
+}
+
+DOC = """
+The test outputs the video power consumption for video
+encode and decode.
+"""
+
+import logging
+
+from autotest_lib.client.common_lib import error
+from autotest_lib.server import site_host_attributes
+
+
+def _run_client_test(machine):
+    """Runs client test with battery actively discharging."""
+    client = hosts.create_host(machine)
+    if not client.has_power():
+        msg = 'This test requires RPM support.'
+        logging.info('********************%s', msg)
+        raise error.TestError(msg)
+
+    client.power_off()
+    try:
+        client_at = autotest.Autotest(client)
+        client_at.run_test('video_WebRtcPower')
+    finally:
+        client.power_on()
+
+
+job.parallel_on_machines(_run_client_test, machines)
diff --git a/tko/perf_upload/perf_dashboard_config.json b/tko/perf_upload/perf_dashboard_config.json
index 0c9a29f..1019a94 100644
--- a/tko/perf_upload/perf_dashboard_config.json
+++ b/tko/perf_upload/perf_dashboard_config.json
@@ -378,5 +378,9 @@
   {
     "autotest_name": "video_WebRtcPerf",
     "master_name": "ChromeOSVideo"
+  },
+  {
+    "autotest_name": "video_WebRtcPower",
+    "master_name": "ChromeOSVideo"
   }
 ]