1# Lint as: python2, python3 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9 10import logging 11import os 12 13from autotest_lib.client.bin import utils 14from autotest_lib.client.common_lib import error 15from six.moves import range 16 17 18class ChameleonVideoCapturer(object): 19 """ 20 Wraps around chameleon APIs to provide an easy way to capture video frames. 21 22 """ 23 24 25 def __init__(self, chameleon_port, display_facade, 26 timeout_get_all_frames_s=60): 27 28 self.chameleon_port = chameleon_port 29 self.display_facade = display_facade 30 self.timeout_get_all_frames_s = timeout_get_all_frames_s 31 self._checksums = [] 32 33 self.was_plugged = None 34 35 36 def __enter__(self): 37 self.was_plugged = self.chameleon_port.plugged 38 39 if not self.was_plugged: 40 self.chameleon_port.plug() 41 self.chameleon_port.wait_video_input_stable() 42 43 return self 44 45 46 def capture(self, player, max_frame_count, box=None): 47 """ 48 Captures frames upto max_frame_count, saves the image with filename 49 same as the index of the frame in the frame buffer. 50 51 @param player: object, VimeoPlayer or BuiltinHtml5Player 52 @param max_frame_count: int, maximum total number of frames to capture. 53 @param box: int tuple, left, upper, right, lower pixel coordinates. 54 Defines the rectangular boundary within which to compare. 55 @return: list of paths of captured images. 56 57 """ 58 59 self.capture_only(player, max_frame_count, box) 60 # each checksum should be saved with a filename that is its index 61 ind_paths = {i : str(i) for i in self.checksums} 62 return self.write_images(ind_paths) 63 64 65 def capture_only(self, player, max_frame_count, box=None): 66 """ 67 Asynchronously begins capturing video frames. Stops capturing when the 68 number of frames captured is equal or more than max_frame_count. Does 69 save the images, gets only the checksums. 70 71 @param player: VimeoPlayer or BuiltinHtml5Player. 72 @param max_frame_count: int, the maximum number of frames we want. 73 @param box: int tuple, left, upper, right, lower pixel coordinates. 74 Defines the rectangular boundary within which to compare. 75 @return: list of checksums 76 77 """ 78 79 if not box: 80 box = self.box 81 82 self.chameleon_port.start_capturing_video(box) 83 84 player.play() 85 86 error_msg = "Expected current time to be > 1 seconds" 87 88 utils.poll_for_condition(lambda : player.currentTime() > 1, 89 timeout=5, 90 sleep_interval=0.01, 91 exception=error.TestError(error_msg)) 92 93 error_msg = "Couldn't get the right number of frames" 94 95 utils.poll_for_condition( 96 lambda: self.chameleon_port.get_captured_frame_count() >= 97 max_frame_count, 98 error.TestError(error_msg), 99 self.timeout_get_all_frames_s, 100 sleep_interval=0.01) 101 102 self.chameleon_port.stop_capturing_video() 103 104 self.checksums = self.chameleon_port.get_captured_checksums() 105 count = self.chameleon_port.get_captured_frame_count() 106 107 # Due to the polling and asychronous calls we might get too many frames 108 # cap at max 109 del self.checksums[max_frame_count:] 110 111 logging.debug("***# of frames received %s", count) 112 logging.debug("Checksums before chopping repeated ones") 113 for c in self.checksums: 114 logging.debug(c) 115 116 # Find the first frame that is different from previous ones. This 117 # represents the start of 'interesting' frames 118 first_index = 0 119 for i in range(1, count): 120 if self.checksums[0] != self.checksums[i]: 121 first_index = i 122 break 123 124 logging.debug("*** First interesting frame at index = %s", first_index) 125 self.checksums = self.checksums[first_index:] 126 return self.checksums 127 128 129 130 def write_images(self, frame_indices, dest_dir, image_format): 131 """ 132 Saves frames of given indices to disk. The filename of the frame will be 133 index in the list. 134 @param frame_indices: list of frame indices to save. 135 @param dest_dir: path to the desired destination dir. 136 @param image_format: string, format to save the image as. e.g; PNG 137 @return: list of file paths 138 139 """ 140 if type(frame_indices) is not list: 141 frame_indices = [frame_indices] 142 143 test_images = [] 144 curr_checksum = None 145 for i, frame_index in enumerate(frame_indices): 146 path = os.path.join(dest_dir, str(i) + '.' + image_format) 147 # previous is what was current in the previous iteration 148 prev_checksum = curr_checksum 149 curr_checksum = self.checksums[frame_index] 150 if curr_checksum == prev_checksum: 151 logging.debug("Image the same as previous image, copy it.") 152 else: 153 logging.debug("Read frame %d, store as %s.", i, path) 154 curr_img = self.chameleon_port.read_captured_frame(frame_index) 155 curr_img.save(path) 156 test_images.append(path) 157 return test_images 158 159 160 def __exit__(self, exc_type, exc_val, exc_tb): 161 if not self.was_plugged: 162 self.chameleon_port.unplug()