Home | History | Annotate | Download | only in video_VideoEncodeAccelerator
      1 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import errno
      6 import fnmatch
      7 import hashlib
      8 import logging
      9 import os
     10 import re
     11 import subprocess
     12 
     13 from autotest_lib.client.bin import utils
     14 from autotest_lib.client.common_lib import error
     15 from autotest_lib.client.common_lib import file_utils
     16 from autotest_lib.client.cros import chrome_binary_test
     17 from autotest_lib.client.cros.video import device_capability
     18 from autotest_lib.client.cros.video import helper_logger
     19 
     20 DOWNLOAD_BASE = 'http://commondatastorage.googleapis.com/chromiumos-test-assets-public/'
     21 BINARY = 'video_encode_accelerator_unittest'
     22 
     23 # These default values are from video_encode_accelerator_unittest.
     24 # The value of |requested_frame_rate| and |requested_subsequent_frame_rate|.
     25 DEFAULT_FRAME_RATE = 30
     26 # The ratio of |requested_subsequent_bit_rate| to |requested_bit_rate|.
     27 DEFAULT_SUBSEQUENT_BIT_RATE_RATIO = 2
     28 # The values of pixel formats //media/base/video_types.h.
     29 PIXEL_FORMAT_NV12 = 6
     30 
     31 def _remove_if_exists(filepath):
     32     try:
     33         os.remove(filepath)
     34     except OSError, e:
     35         if e.errno != errno.ENOENT: # no such file
     36             raise
     37 
     38 
     39 def _download_video(download_path, local_file):
     40     url = '%s%s' % (DOWNLOAD_BASE, download_path)
     41     logging.info('download "%s" to "%s"', url, local_file)
     42 
     43     file_utils.download_file(url, local_file)
     44 
     45     with open(local_file, 'r') as r:
     46         md5sum = hashlib.md5(r.read()).hexdigest()
     47         if md5sum not in download_path:
     48             raise error.TestError('unmatched md5 sum: %s' % md5sum)
     49 
     50 def _run_on_intel_cpu():
     51     try:
     52         lscpu_result = subprocess.check_output(['lscpu'])
     53     except subprocess.CalledProcessError:
     54         logging.warning('lscpu failed.')
     55         return False
     56     for cpu_info in lscpu_result.splitlines():
     57         key, _, value = cpu_info.partition(':')
     58         if key == 'Model name':
     59             return value.strip().startswith('Intel(R)')
     60     logging.warning("%s", lscpu_result)
     61     return False
     62 
     63 
     64 def _can_switch_bitrate():
     65     """Determine whether the board can switch the bitrate.
     66 
     67     The bitrate switch test case is mainly for ARC++. We don't have much control
     68     over some devices that do not run ARC++. Therefore we whitelist the boards
     69     that should pass the bitrate switch test. (crbug.com/890125)
     70     """
     71     # Most Intel chipsets are able to switch bitrate except these two old
     72     # chipsets, so we blacklist the devices.
     73     intel_blacklist = [
     74             # Rambi Bay Trial
     75             'cranky', 'banjo', 'candy', 'clapper', 'enguarde', 'expresso',
     76             'glimmer', 'gnawty', 'heli', 'hoofer', 'kip', 'kip14', 'ninja',
     77             'orco', 'quawks', 'squawks', 'sumo', 'swanky', 'winky',
     78 
     79             # Haswell
     80             'falco', 'leon', 'mccloud', 'monroe', 'panther', 'peppy', 'tricky',
     81             'wolf', 'zako',
     82     ]
     83     return (_run_on_intel_cpu() and
     84             utils.get_current_board() not in intel_blacklist)
     85 
     86 
     87 def _can_encode_nv12():
     88     """
     89     Determine whether the board can encode NV12.
     90 
     91     NV12 format is a mostly common input format driver supports in video
     92     encoding.
     93     There are devices that cannot encode NV12 input buffer because of chromium
     94     code base or driver issue.
     95     """
     96     # Although V4L2VEA supports NV12, some devices cannot encode NV12 probably
     97     # due to a driver issue.
     98     nv12_black_list = [
     99         r'^daisy.*',
    100         r'^nyan.*',
    101         r'^peach.*',
    102         r'^veyron.*',
    103     ]
    104 
    105     board = utils.get_current_board()
    106     for p in nv12_black_list:
    107         if re.match(p, board):
    108             return False
    109     return True
    110 
    111 
    112 class video_VideoEncodeAccelerator(chrome_binary_test.ChromeBinaryTest):
    113     """
    114     This test is a wrapper of the chrome unittest binary:
    115     video_encode_accelerator_unittest.
    116     """
    117 
    118     version = 1
    119 
    120     def get_filter_option(self, profile, size):
    121         """Get option of filtering test.
    122 
    123         @param profile: The profile to encode into.
    124         @param size: The size of test stream in pair format (width, height).
    125         """
    126 
    127         # Profiles used in blacklist to filter test for specific profiles.
    128         H264 = 1
    129         VP8 = 11
    130         VP9 = 12
    131 
    132         blacklist = {
    133                 # (board, profile, size): [tests to skip...]
    134 
    135                 # "board" supports Unix shell-type wildcards.
    136 
    137                 # Use None for "profile" or "size" to indicate no filter on it.
    138 
    139                 # It is possible to match multiple keys for board/profile/size
    140                 # in the blacklist, e.g. veyron_minnie could match both
    141                 # "veyron_*" and "veyron_minnie".
    142 
    143                 # rk3399 doesn't support HW encode for plane sizes not multiple
    144                 # of cache line.
    145                 ('kevin', None, None): ['CacheLineUnalignedInputTest/*'],
    146                 ('bob', None, None): ['CacheLineUnalignedInputTest/*'],
    147                 ('scarlet', None, None): ['CacheLineUnalignedInputTest/*'],
    148 
    149                 # Still high failure rate of VP8 EncoderPerf for veyrons,
    150                 # disable it for now. crbug/720386
    151                 ('veyron_*', VP8, None): ['EncoderPerf/*'],
    152 
    153                 # Disable mid_stream_bitrate_switch test cases for elm/hana.
    154                 # crbug/725087
    155                 ('elm', None, None): ['MidStreamParamSwitchBitrate/*',
    156                                       'MultipleEncoders/*'],
    157                 ('hana', None, None): ['MidStreamParamSwitchBitrate/*',
    158                                        'MultipleEncoders/*'],
    159 
    160                 # Around 40% failure on elm and hana 320x180 test stream.
    161                 # crbug/728906
    162                 ('elm', H264, (320, 180)): ['ForceBitrate/*'],
    163                 ('elm', VP8, (320, 180)): ['ForceBitrate/*'],
    164                 ('hana', H264, (320, 180)): ['ForceBitrate/*'],
    165                 ('hana', VP8, (320, 180)): ['ForceBitrate/*'],
    166                 }
    167 
    168         board = utils.get_current_board()
    169 
    170         # Disable 320x180 test case. Bitrate of vp8 encoder on the test case is
    171         # out of expected range. b/110059922
    172         # TODO(hiroh): Remove this once b/110059922 is fixed.
    173         if _run_on_intel_cpu():
    174             blacklist[(board, VP8, (320, 180))] = ['*']
    175 
    176         filter_list = []
    177         for (board_key, profile_key, size_key), value in blacklist.items():
    178             if (fnmatch.fnmatch(board, board_key) and
    179                 (profile_key is None or profile == profile_key) and
    180                 (size_key is None or size == size_key)):
    181                 filter_list += value
    182 
    183         if filter_list:
    184             return '-' + ':'.join(filter_list)
    185 
    186         return ''
    187 
    188     @helper_logger.video_log_wrapper
    189     @chrome_binary_test.nuke_chrome
    190     def run_once(self, in_cloud, streams, profile, capability):
    191         """Runs video_encode_accelerator_unittest on the streams.
    192 
    193         @param in_cloud: Input file needs to be downloaded first.
    194         @param streams: The test streams for video_encode_accelerator_unittest.
    195         @param profile: The profile to encode into.
    196 
    197         @raises error.TestFail for video_encode_accelerator_unittest failures.
    198         """
    199         device_capability.DeviceCapability().ensure_capability(capability)
    200 
    201         last_test_failure = None
    202         for (path, width, height, bit_rate, frame_rate, subsequent_bit_rate,
    203              subsequent_frame_rate, pixel_format) in streams:
    204             # Skip the bitrate test if the board cannot switch bitrate.
    205             if subsequent_bit_rate is not None and not _can_switch_bitrate():
    206                 logging.info('Skip the bitrate switch test: %s => %s',
    207                              bit_rate, subsequent_bit_rate)
    208                 continue
    209 
    210             if pixel_format == PIXEL_FORMAT_NV12 and not _can_encode_nv12():
    211                 logging.info('Skip the NV12 input buffer case.')
    212                 continue
    213 
    214             # Set the default value for None.
    215             frame_rate = frame_rate or DEFAULT_FRAME_RATE
    216             subsequent_bit_rate = (subsequent_bit_rate or
    217                                    bit_rate * DEFAULT_SUBSEQUENT_BIT_RATE_RATIO)
    218             subsequent_frame_rate = subsequent_frame_rate or DEFAULT_FRAME_RATE
    219 
    220             if in_cloud:
    221                 input_path = os.path.join(self.tmpdir, path.split('/')[-1])
    222                 _download_video(path, input_path)
    223             else:
    224                 input_path = os.path.join(self.cr_source_dir, path)
    225 
    226             output_path = os.path.join(self.tmpdir,
    227                     '%s.out' % input_path.split('/')[-1])
    228 
    229             test_stream_list = map(
    230                     str, [input_path, width, height, profile, output_path,
    231                           bit_rate, frame_rate, subsequent_bit_rate,
    232                           subsequent_frame_rate, pixel_format])
    233             cmd_line_list = [
    234                     '--test_stream_data="%s"' % ':'.join(test_stream_list),
    235                     '--ozone-platform=gbm',
    236                     helper_logger.chrome_vmodule_flag()]
    237 
    238             # Command line |gtest_filter| can override get_filter_option().
    239             predefined_filter = self.get_filter_option(profile, (width, height))
    240             if predefined_filter:
    241                 cmd_line_list.append('--gtest_filter="%s"' % predefined_filter)
    242 
    243             cmd_line = ' '.join(cmd_line_list)
    244             logging.debug('Executing with argument: %s', cmd_line)
    245             try:
    246                 self.run_chrome_test_binary(BINARY, cmd_line, as_chronos=False)
    247             except error.TestFail as test_failure:
    248                 # Continue to run the remaining test streams and raise
    249                 # the last failure after finishing all streams.
    250                 logging.exception('error while encoding %s', input_path)
    251                 last_test_failure = test_failure
    252             finally:
    253                 # Remove the downloaded video
    254                 if in_cloud:
    255                     _remove_if_exists(input_path)
    256                 _remove_if_exists(output_path)
    257 
    258         if last_test_failure:
    259             raise last_test_failure
    260