• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import datetime, logging, os, time
6
7from autotest_lib.client.bin import test, utils
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib import file_utils
10from autotest_lib.client.common_lib import sequence_utils
11from autotest_lib.client.common_lib.cros import chrome
12from autotest_lib.client.cros import constants as cros_constants
13from autotest_lib.client.cros.chameleon import chameleon
14from autotest_lib.client.cros.chameleon import chameleon_port_finder
15from autotest_lib.client.cros.chameleon import chameleon_video_capturer
16from autotest_lib.client.cros.image_comparison import publisher
17from autotest_lib.client.cros.video import constants
18from autotest_lib.client.cros.video import frame_checksum_utils
19from autotest_lib.client.cros.video import native_html5_player
20from autotest_lib.client.cros.multimedia import local_facade_factory
21
22
23class 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