Home | History | Annotate | Download | only in video_GlitchDetection
      1 # Copyright (c) 2014 The Chromium OS 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 datetime, logging, os, time
      6 
      7 from autotest_lib.client.bin import test, utils
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.client.common_lib import file_utils
     10 from autotest_lib.client.common_lib import sequence_utils
     11 from autotest_lib.client.common_lib.cros import chrome
     12 from autotest_lib.client.cros import constants as cros_constants
     13 from autotest_lib.client.cros.chameleon import chameleon
     14 from autotest_lib.client.cros.chameleon import chameleon_port_finder
     15 from autotest_lib.client.cros.chameleon import chameleon_video_capturer
     16 from autotest_lib.client.cros.image_comparison import publisher
     17 from autotest_lib.client.cros.video import constants
     18 from autotest_lib.client.cros.video import frame_checksum_utils
     19 from autotest_lib.client.cros.video import native_html5_player
     20 from autotest_lib.client.cros.multimedia import local_facade_factory
     21 
     22 
     23 class video_GlitchDetection(test.test):
     24     """
     25     Video playback test using image comparison.
     26 
     27     Captures frames using chameleon and compares them to known golden frames.
     28 
     29     If frames don't match, upload to GS for viewing later.
     30 
     31     """
     32     version = 2
     33 
     34 
     35     def run_once(self, source_path, codec, resolution, host, args,
     36                  collect_only = False):
     37 
     38         board = utils.get_current_board()
     39 
     40         file_utils.make_leaf_dir(constants.TEST_DIR)
     41 
     42         with chrome.Chrome(extension_paths = [
     43             cros_constants.MULTIMEDIA_TEST_EXTENSION], autotest_ext = True) as cr:
     44 
     45             cr.browser.platform.SetHTTPServerDirectories(self.bindir)
     46             html_fullpath = os.path.join(self.bindir, 'video.html')
     47             player = native_html5_player.NativeHtml5Player(
     48                 tab = cr.browser.tabs[0],
     49                 full_url = cr.browser.platform.http_server.UrlOf(html_fullpath),
     50                 video_id = 'video',
     51                 video_src_path = source_path,
     52                 event_timeout = 120)
     53 
     54             chameleon_board = chameleon.create_chameleon_board(host.hostname,
     55                                                                args)
     56             display_facade = local_facade_factory.LocalFacadeFactory(
     57                 cr).create_display_facade()
     58 
     59             finder = chameleon_port_finder.ChameleonVideoInputFinder(
     60                 chameleon_board, display_facade)
     61 
     62             capturer = chameleon_video_capturer.ChameleonVideoCapturer(
     63                 finder.find_port(interface = 'hdmi'), display_facade)
     64 
     65             with capturer:
     66                 player.load_video()
     67 
     68                 player.verify_video_can_play()
     69 
     70                 display_facade.move_to_display(
     71                     display_facade.get_first_external_display_index())
     72                 display_facade.set_fullscreen(True)
     73                 # HACK: Unset and reset fullscreen. There is a bug in Chrome
     74                 # that fails to move the window to a correct position.
     75                 # Resetting fullscren helps, check http://crbug.com/574284
     76                 display_facade.set_fullscreen(False)
     77                 display_facade.set_fullscreen(True)
     78                 time.sleep(5)
     79 
     80                 box = (0, 0, constants.DESIRED_WIDTH, constants.DESIRED_HEIGHT)
     81 
     82                 #TODO: mussa, Revisit once crbug/580736 is fixed
     83                 for n in xrange(constants.NUM_CAPTURE_TRIES):
     84                     logging.debug('Trying to capture frames. TRY #%d', n + 1)
     85                     raw_test_checksums = capturer.capture_only(
     86                         player, max_frame_count = constants.FCOUNT,
     87                         box = box)
     88 
     89                     raw_test_checksums = [tuple(checksum) for checksum in
     90                                           raw_test_checksums]
     91 
     92                     overreach_counts = self.overreach_frame_counts(
     93                             raw_test_checksums,
     94                             constants.MAX_FRAME_REPEAT_COUNT)
     95 
     96                     if not overreach_counts: # no checksums exceeded threshold
     97                         break
     98 
     99                     player.pause()
    100                     player.seek_to(datetime.timedelta(seconds=0))
    101 
    102                 else:
    103                     msg = ('Framecount overreach detected even after %d '
    104                            'tries. Checksums: %s' % (constants.NUM_CAPTURE_TRIES,
    105                                                      overreach_counts))
    106                     raise error.TestFail(msg)
    107 
    108 
    109                 # produces unique checksums mapped to their occur. indices
    110                 test_checksum_indices = frame_checksum_utils.checksum_indices(
    111                         raw_test_checksums)
    112 
    113                 test_checksums = test_checksum_indices.keys()
    114 
    115                 test_indices = test_checksum_indices.values()
    116 
    117                 golden_checksums_filepath = os.path.join(
    118                     constants.TEST_DIR,
    119                     constants.GOLDEN_CHECKSUMS_FILENAME)
    120 
    121                 if collect_only:
    122                     capturer.write_images(test_indices, constants.TEST_DIR,
    123                                           constants.IMAGE_FORMAT)
    124 
    125                     logging.debug("Write golden checksum file to %s",
    126                                   golden_checksums_filepath)
    127 
    128                     with open(golden_checksums_filepath, "w+") as f:
    129                         for checksum in test_checksums:
    130                             f.write(' '.join([str(i) for i in checksum]) + '\n')
    131                     return
    132 
    133                 golden_checksums_remote_filepath = os.path.join(
    134                     constants.GOLDEN_CHECKSUM_REMOTE_BASE_DIR,
    135                     board,
    136                     codec + '_' + resolution,
    137                     constants.GOLDEN_CHECKSUMS_FILENAME)
    138 
    139                 file_utils.download_file(golden_checksums_remote_filepath,
    140                                          golden_checksums_filepath)
    141 
    142                 golden_checksums = self.read_checksum_file(
    143                     golden_checksums_filepath)
    144 
    145                 golden_checksum_count = len(golden_checksums)
    146                 test_checksum_count = len(test_checksums)
    147 
    148                 eps = constants.MAX_DIFF_TOTAL_FCOUNT
    149                 if golden_checksum_count - test_checksum_count > eps:
    150                     msg = ('Expecting about %d checksums, received %d. '
    151                            'Allowed delta is %d') % (
    152                             golden_checksum_count,
    153                             test_checksum_count,
    154                             eps)
    155                     raise error.TestFail(msg)
    156 
    157                 # Some frames might be missing during either golden frame
    158                 # collection or during a test run. Using LCS ensures we
    159                 # ignore a few missing frames while comparing test vs golden
    160 
    161                 lcs_len = sequence_utils.lcs_length(golden_checksums,
    162                                                     test_checksums)
    163 
    164                 missing_frames_count = len(golden_checksums) - lcs_len
    165                 unknown_frames_count = len(test_checksums) - lcs_len
    166 
    167                 msg = ('# of matching frames : %d. # of missing frames : %d. '
    168                        '# of unknown test frames : %d. Max allowed # of '
    169                        'missing frames : %d. # of golden frames : %d. # of '
    170                        'test_checksums : %d' %(lcs_len, missing_frames_count,
    171                                                unknown_frames_count,
    172                                                constants.MAX_NONMATCHING_FCOUNT,
    173                                                len(golden_checksums),
    174                                                len(test_checksums)))
    175                 logging.debug(msg)
    176 
    177                 if (missing_frames_count + unknown_frames_count >
    178                         constants.MAX_NONMATCHING_FCOUNT):
    179                     unknown_frames = set(test_checksums) - set(golden_checksums)
    180 
    181                     store_indices = [test_checksum_indices[c] for c in
    182                                      unknown_frames]
    183 
    184                     paths = capturer.write_images(store_indices,
    185                                                   constants.TEST_DIR,
    186                                                   constants.IMAGE_FORMAT)
    187 
    188                     path_publish = publisher.ImageDiffPublisher(self.resultsdir)
    189                     path_publish.publish_paths(paths, self.tagged_testname)
    190 
    191                     raise error.TestFail("Too many non-matching frames")
    192 
    193 
    194     def overreach_frame_counts(self, checksums, max_frame_repeat_count):
    195         """
    196         Checks that captured frames have not exceed the max repeat count.
    197 
    198         @param checksums: list of frame checksums received from chameleon.
    199         @param max_frame_repeat_count: int. max allowed count.
    200         @return : dictionary, checksums and their counts
    201 
    202         """
    203 
    204         logging.debug("Verify no frame is repeated more than %d",
    205                       max_frame_repeat_count)
    206 
    207         counts = frame_checksum_utils.checksum_counts(checksums)
    208         overreach_counts = {}
    209 
    210         for k, v in counts.iteritems():
    211             logging.debug("%s : %s", k, v)
    212             if v > max_frame_repeat_count:
    213                 overreach_counts[k] = v
    214 
    215         return overreach_counts
    216 
    217 
    218 
    219     def read_checksum_file(self, path):
    220         """
    221         Reads the golden checksum file. Each line in file has the format
    222         w x y z where w x y z is a chameleon frame checksum
    223         @param path: complete path to the golden checksum file.
    224         @returns a list of 4-tuples (w x y z).
    225 
    226         """
    227         checksums = []
    228         with open(path) as f:
    229             for line in f:
    230                 checksum = tuple(int(val) for val in line.split())
    231                 checksums.append(checksum)
    232         return  checksums
    233