# Lint as: python2, python3 # Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from __future__ import absolute_import from __future__ import division from __future__ import print_function import logging import os from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error from six.moves import range class ChameleonVideoCapturer(object): """ Wraps around chameleon APIs to provide an easy way to capture video frames. """ def __init__(self, chameleon_port, display_facade, timeout_get_all_frames_s=60): self.chameleon_port = chameleon_port self.display_facade = display_facade self.timeout_get_all_frames_s = timeout_get_all_frames_s self._checksums = [] self.was_plugged = None def __enter__(self): self.was_plugged = self.chameleon_port.plugged if not self.was_plugged: self.chameleon_port.plug() self.chameleon_port.wait_video_input_stable() return self def capture(self, player, max_frame_count, box=None): """ Captures frames upto max_frame_count, saves the image with filename same as the index of the frame in the frame buffer. @param player: object, VimeoPlayer or NativeHTML5Player @param max_frame_count: int, maximum total number of frames to capture. @param box: int tuple, left, upper, right, lower pixel coordinates. Defines the rectangular boundary within which to compare. @return: list of paths of captured images. """ self.capture_only(player, max_frame_count, box) # each checksum should be saved with a filename that is its index ind_paths = {i : str(i) for i in self.checksums} return self.write_images(ind_paths) def capture_only(self, player, max_frame_count, box=None): """ Asynchronously begins capturing video frames. Stops capturing when the number of frames captured is equal or more than max_frame_count. Does save the images, gets only the checksums. @param player: VimeoPlayer or NativeHTML5Player. @param max_frame_count: int, the maximum number of frames we want. @param box: int tuple, left, upper, right, lower pixel coordinates. Defines the rectangular boundary within which to compare. @return: list of checksums """ if not box: box = self.box self.chameleon_port.start_capturing_video(box) player.play() error_msg = "Expected current time to be > 1 seconds" utils.poll_for_condition(lambda : player.currentTime() > 1, timeout=5, sleep_interval=0.01, exception=error.TestError(error_msg)) error_msg = "Couldn't get the right number of frames" utils.poll_for_condition( lambda: self.chameleon_port.get_captured_frame_count() >= max_frame_count, error.TestError(error_msg), self.timeout_get_all_frames_s, sleep_interval=0.01) self.chameleon_port.stop_capturing_video() self.checksums = self.chameleon_port.get_captured_checksums() count = self.chameleon_port.get_captured_frame_count() # Due to the polling and asychronous calls we might get too many frames # cap at max del self.checksums[max_frame_count:] logging.debug("***# of frames received %s", count) logging.debug("Checksums before chopping repeated ones") for c in self.checksums: logging.debug(c) # Find the first frame that is different from previous ones. This # represents the start of 'interesting' frames first_index = 0 for i in range(1, count): if self.checksums[0] != self.checksums[i]: first_index = i break logging.debug("*** First interesting frame at index = %s", first_index) self.checksums = self.checksums[first_index:] return self.checksums def write_images(self, frame_indices, dest_dir, image_format): """ Saves frames of given indices to disk. The filename of the frame will be index in the list. @param frame_indices: list of frame indices to save. @param dest_dir: path to the desired destination dir. @param image_format: string, format to save the image as. e.g; PNG @return: list of file paths """ if type(frame_indices) is not list: frame_indices = [frame_indices] test_images = [] curr_checksum = None for i, frame_index in enumerate(frame_indices): path = os.path.join(dest_dir, str(i) + '.' + image_format) # previous is what was current in the previous iteration prev_checksum = curr_checksum curr_checksum = self.checksums[frame_index] if curr_checksum == prev_checksum: logging.debug("Image the same as previous image, copy it.") else: logging.debug("Read frame %d, store as %s.", i, path) curr_img = self.chameleon_port.read_captured_frame(frame_index) curr_img.save(path) test_images.append(path) return test_images def __exit__(self, exc_type, exc_val, exc_tb): if not self.was_plugged: self.chameleon_port.unplug()