# Copyright 2014 The Chromium 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 telemetry.internal.image_processing import frame_generator
from telemetry.internal.util import external_modules

cv2 = external_modules.ImportRequiredModule('cv2')


class VideoFileFrameGenerator(frame_generator.FrameGenerator):
  """Provides a Frame Generator for a video file.

  Sample Usage:
    generator = VideoFileFrameGenerator(sys.argv[1]).GetGenerator()
    for frame in generator:
      # Do something

  Attributes:
    _capture: The openCV video capture.
    _frame_count: The number of frames in the video capture.
    _frame_index: The frame number of the current frame.
    _timestamp: The timestamp of the current frame.
    _dimensions: The dimensions of the video capture."""
  def __init__(self, video_filename, start_frame_index=0):
    """Initializes the VideoFileFrameGenerator object.

    Args:
      video_filename: str, The path to the video file.
      start_frame_index: int, The number of frames to skip at the start of the
          file.

    Raises:
      FrameReadError: A read error occurred during initialization."""
    self._capture = cv2.VideoCapture(video_filename)
    self._frame_count = int(self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))
    self._frame_index = -1
    self._timestamp = 0
    width = self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
    height = self._capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)
    self._dimensions = (int(width), int(height))
    if self._frame_count <= start_frame_index:
      raise frame_generator.FrameReadError('Not enough frames in capture.')
    while self._frame_index < start_frame_index - 1:
      self._ReadFrame(True)

    super(self.__class__, self).__init__()

  def _ReadFrame(self, skip_decode=False):
    """Reads the next frame, updates attributes.

    Args:
      skip_decode: Whether or not to skip decoding. Useful for seeking.

    Returns:
      The frame if not EOF, 'None' if EOF.

    Raises:
      FrameReadError: Unexpectedly failed to read a frame from the capture."""
    if self._frame_index >= self._frame_count - 1:
      return None
    self._timestamp = self._capture.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
    if skip_decode:
      ret = self._capture.grab()
      frame = None
    else:
      ret, frame = self._capture.read()
    if not ret:
      raise frame_generator.FrameReadError(
          'Failed to read frame from capture.')
    self._frame_index += 1
    return frame

  # OVERRIDE
  def _CreateGenerator(self):
    while True:
      frame = self._ReadFrame()
      if frame is None:
        break
      yield frame

  # OVERRIDE
  @property
  def CurrentTimestamp(self):
    return self._timestamp

  # OVERRIDE
  @property
  def CurrentFrameNumber(self):
    return self._frame_index

  # OVERRIDE
  @property
  def Dimensions(self):
    return self._dimensions
