예제 #1
0
    def _get_duration(self):
        """Get duration of the video.

        Returns
        -------
        duration : float
            Duration in seconds. ``None`` if duration is not available.
        duration_text : str
            Duration as a human readable string, e.g.,
            ``'00:02:53.33'``. ``None`` if duration is not available.

        """

        self.__dp("entered StoryBoard._get_duration")
        if 'duration' in self._ffprobe['format']:
            duration = float(self._ffprobe['format']['duration'])
            duration_text = util.humantime(duration)
            self.__dp("left StoryBoard._get_duration")
            return (duration, duration_text)
        else:
            self.__dp("left StoryBoard._get_duration")
            return (None, None)
예제 #2
0
 def test_video(self):
     vid = Video(self.videofile,
                 params={
                     'ffprobe_bin': self.ffprobe_bin,
                     'print_progress': False,
                 })
     self.assertEqual(vid.path, self.videofile)
     self.assertEqual(vid.filename, os.path.basename(self.videofile))
     self.assertIsNone(vid.title)
     self.assertIsInstance(vid.size, int)
     self.assertEqual(humansize(vid.size), vid.size_text)
     self.assertEqual(vid.format, 'Matroska')
     self.assertLess(abs(vid.duration - 10.0), 1.0)
     self.assertEqual(humantime(vid.duration), vid.duration_text)
     self.assertEqual(vid.dimension, (320, 180))
     self.assertEqual(vid.dimension_text, '320x180')
     self.assertAlmostEqual(vid.dar, 16 / 9)
     self.assertEqual(vid.dar_text, '16:9')
     self.assertEqual(vid.scan_type, 'Progressive scan')
     self.assertAlmostEqual(vid.frame_rate, 25.0)
     self.assertEqual(vid.frame_rate_text, '25 fps')
     self.assertIsInstance(vid.streams, list)
     self.assertEqual(len(vid.streams), 3)
     # sha1sum
     sha1sum = vid.compute_sha1sum()
     self.assertEqual(vid.sha1sum, sha1sum)
     self.assertEqual(len(sha1sum), 40)
     # video stream
     vstream = vid.streams[0]
     self.assertIsInstance(vstream, Stream)
     self.assertIsNotNone(vstream.codec)
     self.assertAlmostEqual(vstream.dar, 16 / 9)
     self.assertEqual(vstream.dar_text, '16:9')
     self.assertEqual(vstream.dimension, (320, 180))
     self.assertEqual(vstream.dimension_text, '320x180')
     self.assertAlmostEqual(vstream.frame_rate, 25.0)
     self.assertEqual(vstream.frame_rate_text, '25 fps')
     self.assertEqual(vstream.height, 180)
     self.assertEqual(vstream.index, 0)
     self.assertIsNotNone(vstream.info_string)
     self.assertIsNone(vstream.language_code)
     self.assertEqual(vstream.type, 'video')
     self.assertEqual(vstream.width, 320)
     # audio stream
     astream = vid.streams[1]
     self.assertIsNotNone(astream.codec)
     self.assertEqual(astream.index, 1)
     self.assertIsNone(astream.language_code)
     self.assertEqual(astream.type, 'audio')
     # subtitle stream
     sstream = vid.streams[2]
     self.assertIsNotNone(sstream.codec)
     self.assertEqual(sstream.index, 2)
     self.assertIsNone(sstream.language_code)
     self.assertEqual(sstream.type, 'subtitle')
     print('')
     print(vid.format_metadata(params={'include_sha1sum': True}))
     # specifically test the video_duration option
     vid = Video(self.videofile,
                 params={
                     'ffprobe_bin': self.ffprobe_bin,
                     'video_duration': 10.0,
                     'print_progress': False,
                 })
     self.assertAlmostEqual(vid.duration, 10.0)
     self.assertEqual(humantime(vid.duration), vid.duration_text)
예제 #3
0
    def __init__(self, video, params=None):
        """Initialize the Video class.

        See class docstring for parameters of the constructor.

        """

        if params is None:
            params = {}
        if 'debug' in params and params['debug']:
            self.__debug = True
        self.__dp("entered StoryBoard.__init__")
        if 'ffprobe_bin' in params:
            ffprobe_bin = params['ffprobe_bin']
        else:
            _, ffprobe_bin = fflocate.guess_bins()
        video_duration = _read_param(params, 'video_duration', None)
        print_progress = _read_param(params, 'print_progress', False)

        self.path = os.path.abspath(video)
        if not os.path.exists(self.path):
            raise OSError("'" + video + "' does not exist")
        self.filename = os.path.basename(self.path)
        if hasattr(self.filename, 'decode'):
            # python2 str, need to be decoded to unicode for proper
            # printing
            self.filename = self.filename.decode('utf-8')

        if print_progress:
            sys.stderr.write("Processing %s\n" % self.filename)
            sys.stderr.write("Crunching metadata...\n")

        self._call_ffprobe(ffprobe_bin)

        self.title = self._get_title()
        self.format = self._get_format()
        self.size, self.size_text = self._get_size()
        if video_duration is None:
            self.duration, self.duration_text = self._get_duration()
        else:
            self.duration = video_duration
            self.duration_text = util.humantime(video_duration)
        self.sha1sum = None  # SHA-1 digest is generated upon request

        # the remaining attributes will be dynamically set when parsing
        # streams
        self.dimension = None
        self.dimension_text = None
        self.frame_rate = None
        self.frame_rate_text = None
        self.dar = None
        self.dar_text = None

        self._process_streams()

        # detect if the file contains any video streams at all and try
        # to extract scan type only if it does
        for stream in self.streams:
            if stream.type == 'video':
                break
        else:
            # no video stream
            self.scan_type = None
            self.__dp("left StoryBoard.__init__")
            return
        self.scan_type = self._get_scan_type(ffprobe_bin, print_progress)
        self.__dp("left StoryBoard.__init__")
예제 #4
0
def create_thumbnail(frame, width, params=None):
    """Create thumbnail of a video frame.

    The timestamp of the frame can be overlayed to the thumbnail. See
    "Other Parameters" to how to enable this feature and relevant
    options.

    Parameters
    ----------
    frame : storyboard.frame.Frame
        The video frame to be thumbnailed.
    width : int
        Width of the thumbnail.
    params : dict, optional
        Optional parameters enclosed in a dict. Default is ``None``.
        See the "Other Parameters" section for understood key/value
        pairs.

    Returns
    -------
    thumbnail : PIL.Image.Image

    Other Parameters
    ----------------
    aspect_ratio : float, optional
        Aspect ratio of the thumbnail. If ``None``, use the aspect ratio
        (only considering the pixel dimensions) of the frame. Default is
        ``None``.
    draw_timestamp : bool, optional
        Whether to draw frame timestamp over the timestamp. Default is
        ``False``.
    timestamp_font : Font, optional
        Font for the timestamp, if `draw_timestamp` is ``True``.
        Default is the font constructed by ``Font()`` without arguments.
    timestamp_align : {'right', 'center', 'left'}, optional
        Horizontal alignment of the timestamp over the thumbnail, if
        `draw_timestamp` is ``True``. Default is ``'right'``. Note that
        the timestamp is always vertically aligned towards the bottom of
        the thumbnail.

    """

    if params is None:
        params = {}
    if 'aspect_ratio' in params:
        aspect_ratio = params['aspect_ratio']
    else:
        image_width, image_height = frame.image.size
        aspect_ratio = image_width / image_height
    height = int(round(width / aspect_ratio))
    size = (width, height)
    draw_timestamp = _read_param(params, 'draw_timestamp', False)
    if draw_timestamp:
        timestamp_font = _read_param(params, 'timestamp_font', Font())
        timestamp_align = _read_param(params, 'timestamp_align', 'right')

    thumbnail = frame.image.resize(size, Image.LANCZOS)

    if draw_timestamp:
        draw = ImageDraw.Draw(thumbnail)

        timestamp_text = util.humantime(frame.timestamp, ndigits=0)
        timestamp_width, timestamp_height = \
            draw.textsize(timestamp_text, timestamp_font.obj)

        # calculate upperleft corner of the timestamp overlay
        # we hard code a margin of 5 pixels
        timestamp_y = height - 5 - timestamp_height
        if timestamp_align == 'right':
            timestamp_x = width - 5 - timestamp_width
        elif timestamp_align == 'left':
            timestamp_x = 5
        elif timestamp_align == 'center':
            timestamp_x = int((width - timestamp_width) / 2)
        else:
            raise ValueError("timestamp alignment option '%s' not recognized" %
                             timestamp_align)

        # draw white timestamp with 1px thick black border
        for x_offset in range(-1, 2):
            for y_offset in range(-1, 2):
                draw.text((timestamp_x + x_offset, timestamp_y + y_offset),
                          timestamp_text,
                          fill='black',
                          font=timestamp_font.obj)
        draw.text((timestamp_x, timestamp_y),
                  timestamp_text,
                  fill='white',
                  font=timestamp_font.obj)

    return thumbnail