blob: 28efed21ecab083e1b9895f24dd597cc64cf328f [file] [log] [blame]
# Copyright (c) 2013 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
# 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']
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
# These are the variable names in WebRTC internals.
# Maximum decode time of the frames of the last 10 seconds.
GOOG_MAX_DECODE_MS = 'googMaxDecodeMs'
# The decode time of the last frame.
GOOG_DECODE_MS = 'googDecodeMs'
# Javascript function to get the decode time. addStats is a function called by
# Chrome to pass WebRTC statistics every second.
ADD_STATS_JAVASCRIPT = (
'var googMaxDecodeMs = new Array();'
'var googDecodeMs = new Array();'
'addStats = function(data) {'
' reports = data.reports;'
' for (var i=0; i < reports.length; i++) {'
' if (reports[i].type == "ssrc") {'
' values = reports[i].stats.values;'
' for (var j=0; j < values.length; j++) {'
' if (values[j] == "googMaxDecodeMs")'
' googMaxDecodeMs[googMaxDecodeMs.length] = values[j+1];'
' else if (values[j] == "googDecodeMs")'
' googDecodeMs[googDecodeMs.length] = values[j+1];'
' }'
' }'
' }'
'}')
# Measure the stats until getting 10 samples or exceeding 15 seconds. addStats
# is called once per second for now.
NUM_DECODE_TIME_SAMPLES = 10
TIMEOUT = 15
class video_WebRtcPerf(test.test):
"""The test outputs the decode time for WebRtc to performance
dashboard.
"""
version = 1
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):
"""
Checks if WebRTC decoding is hardware accelerated.
@param cr: Autotest Chrome instance.
@raises error.TestError if decoding is not hardware accelerated.
"""
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)
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 not working.')
def open_stats_page(self, cr):
"""
Opens WebRTC internal statistics page.
@param cr: Autotest Chrome instance.
@returns the tab of the stats page.
"""
tab = cr.browser.tabs.New()
tab.Navigate(WEBRTC_INTERNALS_URL)
tab.WaitForDocumentReadyStateToBeComplete()
# Inject stats callback function.
tab.EvaluateJavaScript(ADD_STATS_JAVASCRIPT)
return tab
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)
# Start chrome with test flags.
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)
# Open WebRTC internals page for statistics.
tab = self.open_stats_page(cr)
# Collect the decode time until there are enough samples.
start_time = time.time()
max_decode_time_list = []
decode_time_list = []
while (time.time() - start_time < TIMEOUT and
len(decode_time_list) < NUM_DECODE_TIME_SAMPLES):
time.sleep(1)
max_decode_time_list = []
decode_time_list = []
try:
max_decode_time_list = [int(x) for x in
tab.EvaluateJavaScript(GOOG_MAX_DECODE_MS)]
decode_time_list = [int(x) for x in
tab.EvaluateJavaScript(GOOG_DECODE_MS)]
except:
pass
# Output the values if they are valid.
if len(max_decode_time_list) < NUM_DECODE_TIME_SAMPLES:
raise error.TestError('Not enough ' + GOOG_MAX_DECODE_MS)
if len(decode_time_list) < NUM_DECODE_TIME_SAMPLES:
raise error.TestError('Not enough ' + GOOG_DECODE_MS)
max_decode_time = max(max_decode_time_list)
decode_time_median = self.get_median(decode_time_list)
logging.info("Max decode time list=%s", str(max_decode_time_list))
logging.info("Decode time list=%s", str(decode_time_list))
logging.info("Maximum decode time=%d, median=%d", max_decode_time,
decode_time_median)
self.output_perf_value(
description="decode_time.max", value=max_decode_time,
units="milliseconds", higher_is_better=False)
self.output_perf_value(
description="decode_time.percentile_0.50",
value=decode_time_median,
units="milliseconds", higher_is_better=False)
def get_median(self, seq):
"""
Calculates the median of a sequence of numbers.
@param seq: a list with numbers.
@returns the median of the numbers.
"""
seq.sort()
size = len(seq)
if size % 2 != 0:
return seq[size / 2]
return (seq[size / 2] + seq[size / 2 - 1]) / 2.0
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())