From b17a0d30c02795a87d0ab5f691416ddddf5fb0bd Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 15 Sep 2017 17:43:16 +0100 Subject: [PATCH] exoplayer: Add exoplayer workload This is based heavily on a combination of WA2's video workload and LISA's exoplayer workload. --- wa/workloads/exoplayer/__init__.py | 187 +++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 wa/workloads/exoplayer/__init__.py diff --git a/wa/workloads/exoplayer/__init__.py b/wa/workloads/exoplayer/__init__.py new file mode 100644 index 00000000..d475ceff --- /dev/null +++ b/wa/workloads/exoplayer/__init__.py @@ -0,0 +1,187 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2017, Arm Limited and contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import defaultdict +import re +import os +import time +import urllib + +from wa import ApkWorkload, Parameter, ConfigError, WorkloadError +from wa.framework.configuration.core import settings +from wa.utils.types import boolean +from wa.utils.misc import ensure_directory_exists +from devlib.utils.android import grant_app_permissions + +# Regexps for benchmark synchronization +REGEXPS = { + 'start' : '.*Displayed com.google.android.exoplayer2.demo/.PlayerActivity', + 'duration' : '.*period \[(?P[0-9]+.*)\]', + 'end' : '.*state \[.+, .+, E\]' +} + + +DOWNLOAD_URLS = { + 'mp4_1080p': 'http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4', + 'mov_720p': 'http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_h264.mov', + 'mov_480p': 'http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_h264.mov', +} + + +class ExoPlayer(ApkWorkload): + """ + Android ExoPlayer + + ExoPlayer is the basic video player library that is used by the YouTube + android app. The aim of this workload is to test a proxy for YouTube + performance on targets where running the real YouTube app is not possible + due its dependencies. + + ExoPlayer sources: https://github.com/google/ExoPlayer + + The 'demo' application is used by this workload. It can easily be built by + loading the ExoPlayer sources into Android Studio. + + Version r2.4.0 built from commit d979469 is known to work + """ + + name = 'exoplayer' + + video_directory = os.path.join(settings.dependencies_directory, name) + + package_names = ['com.google.android.exoplayer2.demo'] + versions = ['2.4.0'] + action = 'com.google.android.exoplayer.demo.action.VIEW' + default_format = 'mov_720p' + + parameters = [ + Parameter('version', allowed_values=versions, default=versions[-1], override=True), + Parameter('duration', kind=int, default=20, + description=""" + Playback duration of the video file. This becomes the duration of the workload. + If provided must be shorter than the length of the media. + """), + Parameter('format', allowed_values=DOWNLOAD_URLS.keys(), + description=""" + Specifies which format video file to play. Default is {} + """.format(default_format)), + Parameter('filename', + description=""" + The name of the video file to play. This can be either a path + to the file anywhere on your file system, or it could be just a + name, in which case, the workload will look for it in + ``{}`` + *Note*: either format or filename should be specified, but not both! + """.format(video_directory)), + Parameter('force_dependency_push', kind=boolean, default=False, + description=""" + If true, video will always be pushed to device, regardless + of whether the file is already on the device. Default is ``False``. + """), + ] + + def validate(self): + if self.format and self.filename: + raise ConfigError('Either format *or* filename must be specified; but not both.') + + if not self.format and not self.filename: + self.format = self.default_format + + def _find_host_video_file(self): + """Pick the video file we're going to use, download it if necessary""" + if self.filename: + if self.filename[0] in './' or len(self.filename) > 1 and self.filename[1] == ':': + filepath = os.path.abspath(self.filename) + else: + filepath = os.path.join(self.video_directory, self.filename) + if not os.path.isfile(filepath): + raise WorkloadError('{} does not exist.'.format(filepath)) + return filepath + else: + # Search for files we've already downloaded + files = [] + for filename in os.listdir(self.video_directory): + format_ext, format_resolution = self.format.split('_') + _, file_ext = os.path.splitext(filename) + if file_ext == '.' + format_ext and format_resolution in filename: + files.append(os.path.join(self.video_directory, filename)) + + if not files: + # Download a file with the requested format + url = DOWNLOAD_URLS[self.format] + filepath = os.path.join(self.video_directory, os.path.basename(url)) + self.logger.info('Downloading {} to {}...'.format(url, filepath)) + urllib.urlretrieve(url, filepath) + return filepath + else: + if len(files) > 1: + self.logger.warn('Multiple files found for {} format. Using {}.' + .format(self.format, files[0])) + self.logger.warn('Use "filename"parameter instead of ' + '"format" to specify a different file.') + return files[0] + + def init_resources(self, context): + ensure_directory_exists(self.video_directory) + self.host_video_file = self._find_host_video_file() + + def setup(self, context): + super(ExoPlayer, self).setup(context) + + grant_app_permissions(self.target, self.package) + + self.device_video_file = self.target.path.join(self.target.working_directory, + os.path.basename(self.host_video_file)) + if self.force_dependency_push or not self.target.file_exists(self.device_video_file): + self.logger.info('Copying {} to device.'.format(self.host_video_file)) + self.target.push(self.host_video_file, self.device_video_file, timeout=120) + + self.play_cmd = 'am start -a {} -d "file://{}"'.format(self.action, + self.device_video_file) + + self.monitor = self.target.get_logcat_monitor(REGEXPS.values()) + self.monitor.start() + + def run(self, context): + self.target.execute(self.play_cmd) + + self.monitor.wait_for(REGEXPS['start']) + self.logger.info('Playing media file') + + line = self.monitor.wait_for(REGEXPS['duration'])[0] + media_duration_s = int(round(float(re.search(REGEXPS['duration'], line) + .group('duration')))) + + self.logger.info('Media duration is {} seconds'.format(media_duration_s)) + + if self.duration > media_duration_s: + raise ConfigError( + "'duration' param ({}) longer than media duration ({})".format( + self.duration, media_duration_s)) + + if self.duration: + self.logger.info('Waiting {} seconds before ending playback' + .format(self.duration)) + time.sleep(self.duration) + else: + self.logger.info('Waiting for playback completion ({} seconds)' + .format(media_duration_s)) + self.monitor.wait_for(REGEXPS['end'], timeout = media_duration_s + 30) + + def teardown(self, context): + super(ExoPlayer, self).teardown(context) + self.monitor.stop()