Class: RVideo::FrameCapturer

Inherits:
Object
  • Object
show all
Defined in:
lib/rvideo/frame_capturer.rb

Overview

FrameCapturer uses ffmpeg to capture frames from a movie in JPEG format.

You can capture one or many frames in a variety of ways:

- one frame at a given offset
- multiple frames every n seconds from a given offset

TODO

- n frames total, evenly distributed across the duration of the movie

For the offset options, three types of values are accepted:

- percentage e.g. '37%'
- seconds    e.g. '37s' or simply '37'
- frame      e.g. '37f'

If a time is outside of the duration of the file, it will choose a frame at the 99% mark.

Example:

RVideo::FrameCapturer.capture! :input => 'path/to/input.mp4', :offset => '10%'
# => ['/path/to/screenshot/input-10p.jpg']

In the case where you specify an :interval, e.g. :interval => 5 for a frame every 5 seconds, you will generally get a few more images that you might expect. Typically there will be at least two extra, one for the very start and one for the very end of the video. Then, depending on how close to a simple integer of seconds the duration of the video is, you may get one or two more.

# Assuming input.mp4 is 19.6 seconds long..
RVideo::FrameCapturer.capture! :input => 'path/to/input.mp4', :interval => 5
# => ['/path/to/input-1.jpg','/path/to/input-2.jpg','/path/to/input-3.jpg',
      '/path/to/input-4.jpg','/path/to/input-5.jpg','/path/to/input-6.jpg']

For more precision, you can try multiple capture commands, each getting a single frame but with increasing offsets.

Constant Summary collapse

VALID_TIMECODE_FORMAT =
/\A([0-9.,]*)(s|f|%)?\Z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ FrameCapturer

Returns a new instance of FrameCapturer.


46
47
48
49
50
51
52
53
54
55
# File 'lib/rvideo/frame_capturer.rb', line 46

def initialize(options)
  @ffmpeg_binary = options[:ffmpeg_binary] || "ffmpeg"

  @input = options[:input] || raise(ArgumentError, "need :input => /path/to/movie")

  @inspector = Inspector.new :file => @input

  @offset, @rate, @limit, @output = parse_options options
  @command = create_command(@input, @output, @offset)
end

Instance Attribute Details

#commandObject (readonly)

Returns the value of attribute command


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def command
  @command
end

#inputObject (readonly)

Returns the value of attribute input


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def input
  @input
end

#inspectorObject (readonly)

Returns the value of attribute inspector


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def inspector
  @inspector
end

#limitObject (readonly)

Returns the value of attribute limit


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def limit
  @limit
end

#offsetObject (readonly)

Returns the value of attribute offset


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def offset
  @offset
end

#outputObject (readonly)

Returns the value of attribute output


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def output
  @output
end

#rateObject (readonly)

Returns the value of attribute rate


40
41
42
# File 'lib/rvideo/frame_capturer.rb', line 40

def rate
  @rate
end

Class Method Details

.capture!(options) ⇒ Object


42
43
44
# File 'lib/rvideo/frame_capturer.rb', line 42

def self.capture!(options)
  new(options).capture!
end

Instance Method Details

#calculate_time(timecode) ⇒ Object

TODO This method should not be public, but I'm too lazy to update the specs right now..


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/rvideo/frame_capturer.rb', line 72

def calculate_time(timecode)
  m = VALID_TIMECODE_FORMAT.match(timecode.to_s)
  if m.nil? or m[1].nil? or m[1].empty?
    raise TranscoderError::ParameterError,
      "Invalid timecode for frame capture: #{timecode}. " <<
    "Must be a number, optionally followed by s, f, or %."
  end

  case m[2]
  when "s", nil
    t = m[1].to_f
  when "f"
    t = m[1].to_f / @inspector.fps.to_f
  when "%"
    # milliseconds / 1000 * percent / 100
    t = (@inspector.duration.to_i / 1000.0) * (m[1].to_f / 100.0)
  else
    raise TranscoderError::ParameterError,
      "Invalid timecode for frame capture: #{timecode}. " <<
    "Must be a number, optionally followed by s, f, or p."
  end

  if (t * 1000) > @inspector.duration
    calculate_time("99%")
  else
    t
  end
end

#capture!Object


57
58
59
60
61
62
63
# File 'lib/rvideo/frame_capturer.rb', line 57

def capture!
  RVideo.logger.info("\nCreating Screenshot: #{@command}\n")
  frame_result = do_execute("#{@command} 2>&1")
  RVideo.logger.info("\nScreenshot results: #{frame_result}")

  Dir[File.expand_path(@output).sub("%d", "*")].entries
end

#do_execute(command) ⇒ Object


65
66
67
# File 'lib/rvideo/frame_capturer.rb', line 65

def do_execute(command)
  `#{command}`
end