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