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