Skip to content

Commit

Permalink
Merge 1f12c14 into a686844
Browse files Browse the repository at this point in the history
  • Loading branch information
matthutchinson committed Aug 14, 2017
2 parents a686844 + 1f12c14 commit f63deed
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Metrics/ClassLength:

# Offense count: 6
Metrics/CyclomaticComplexity:
Max: 8
Max: 9

# Offense count: 152
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ forked process. See the section below for more capture configuration options.
lolcommits has some capture options for additional lulz. You can enable these
via environment variables like so;

* `LOLCOMMITS_DEVICE` set a webcam device - **mac and linux only**
* `LOLCOMMITS_DEVICE` set a webcam device - **except windows (non-animated) captures**
* `LOLCOMMITS_ANIMATE` (in seconds) set time for capturing an animated gif -
**requires ffmpeg**
* `LOLCOMMITS_DELAY` (in seconds) set delay time before capturing (for slow
Expand Down Expand Up @@ -169,11 +169,11 @@ you capture and the capabilities of your machine).

* Linux - [follow this guide](https://www.ffmpeg.org/download.html#build-linux)
* macOS - `brew install ffmpeg`
* Windows - [follow this guide](https://ffmpeg.org/download.html#build-windows)

To enable, just set the `LOLCOMMITS_ANIMATE` environment variable with the
number of seconds to capture. If you find animated capturing takes too long, try
setting `LOLCOMMITS_FORK=true`. Animated gif captures are currently NOT
supported on Windows.
setting `LOLCOMMITS_FORK=true`.

![Example animated lolcommit
gif](http://cdn2.usa.bugleblogs.com/blogs/000/000/003/de0eb9aa695.gif "Example
Expand Down
10 changes: 3 additions & 7 deletions bin/lolcommits
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,12 @@ class App
# Duration for animated capturing
#
# If animation is enabled, returns an integer > 0 representing seconds.
# Seconds will be 0 if no option set, or if the platform doesn't support
# animated captures (in which case animated capturing will be disabled).
# Seconds will be 0 if no option set, meaning no animated capture will take
# place.
#
# @return [Integer]
def self.capture_animate
if Platform.can_animate?
(options[:animate] || ENV['LOLCOMMITS_ANIMATE']).to_i
else
0
end
(options[:animate] || ENV['LOLCOMMITS_ANIMATE']).to_i
end

def self.capture_animated?
Expand Down
1 change: 1 addition & 0 deletions lib/lolcommits/capturer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ def initialize(attributes = {})
require 'lolcommits/capturer/capture_linux'
require 'lolcommits/capturer/capture_linux_animated'
require 'lolcommits/capturer/capture_windows'
require 'lolcommits/capturer/capture_windows_animated'
require 'lolcommits/capturer/capture_cygwin'
require 'lolcommits/capturer/capture_fake'
2 changes: 1 addition & 1 deletion lib/lolcommits/capturer/capture_linux_animated.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def capture
fps = video_fps(video_location)
skip = frame_skip(fps)
delay = frame_delay(fps, skip)
debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay} (video fps: #{fps})"

# create the looping animated gif from frames (picks nth frame with seq,
# quotes output and concats to a single line with tr)
Expand Down
2 changes: 1 addition & 1 deletion lib/lolcommits/capturer/capture_mac_animated.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def capture
# use fps to set delay and number of frames to skip (for lower filesized gifs)
skip = frame_skip(fps)
delay = frame_delay(fps, skip)
debug "Capturer: animated gif choosing every #{skip} frames with a frame delay of #{delay}"
debug "Capturer: animated gif choosing every #{skip} frames with a frame delay of #{delay} (video fps: #{fps})"

# create the looping animated gif from frames (picks nth frame with seq,
# quotes output and concats to a single line with tr)
Expand Down
102 changes: 102 additions & 0 deletions lib/lolcommits/capturer/capture_windows_animated.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
module Lolcommits
class CaptureWindowsAnimated < Capturer
def capture
# make a fresh frames directory
FileUtils.rm_rf(frames_location)
FileUtils.mkdir_p(frames_location)

# abort capture if we don't have a device name
return unless capture_device_string

# capture raw video with ffmpeg dshow
system_call "ffmpeg -v quiet -y -f dshow -i video=\"#{capture_device_string}\" -video_size 320x240 -t #{capture_duration} \"#{video_location}\" > NUL"

return unless File.exist?(video_location)
# convert raw video to png frames with ffmpeg
system_call "ffmpeg #{capture_delay_string} -v quiet -i \"#{video_location}\" -t #{animated_duration} \"#{frames_location}/%09d.png\" > NUL"

# use fps to set delay and number of frames to skip (for lower filesized gifs)
fps = video_fps(video_location)
skip = frame_skip(fps)
delay = frame_delay(fps, skip)
debug "Capturer: animated gif choosing every #{skip} frames with a frame delay of #{delay} (video fps: #{fps})"

# create the looping animated gif from frames (delete frame files except every #{skip} frame)
Dir["#{frames_location}/*.png"].each do |frame_filename|
basename = File.basename(frame_filename)
frame_number = basename.split('.').first.to_i
File.delete(frame_filename) if frame_number % skip != 0
end

# convert to animated gif with delay and gif optimisation
system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 \"#{frames_location}/*.png\" -coalesce \"#{snapshot_location}\""
end

private

def ffpmeg_list_devices_cmd
'ffmpeg -list_devices true -f dshow -i dummy 2>&1'
end

def device_names
@_device_names ||= begin
cmd_output = system_call(ffpmeg_list_devices_cmd, true).split('DirectShow video devices')[1]
names = []

cmd_output.lines.each do |line|
break if line =~ /audio devices/
names << Regexp.last_match(1) if line =~ /"(.+)"\n/
end

if names.empty?
debug 'Capturer: unable to find any capture devices'
else
debug "Capturer: found #{names.length} devices: #{names.join(', ')}"
end

names
end
end

def system_call(call_str, capture_output = false)
debug "Capturer: making system call for \n #{call_str}"
capture_output ? `#{call_str}` : system(call_str)
end

def frame_delay(fps, skip)
# calculate frame delay
delay = ((100.0 * skip) / fps.to_f).to_i
delay < 6 ? 6 : delay # hard limit for IE browsers
end

def video_fps(file)
# inspect fps of the captured video file (default to 29.97)
fps = system_call("ffmpeg -i \"#{file}\" 2>&1 | sed -n \"s/.*, \\(.*\\) fp.*/\\1/p\"", true)
fps.to_i < 1 ? 29.97 : fps.to_f
end

def frame_skip(fps)
# of frames to skip depends on movie fps
case fps
when 0..15
2
when 16..28
3
else
4
end
end

def capture_device_string
capture_device || device_names.first
end

def capture_delay_string
" -ss #{capture_delay}" if capture_delay.to_i > 0
end

def capture_duration
animated_duration.to_i + capture_delay.to_i
end
end
end
6 changes: 3 additions & 3 deletions lib/lolcommits/platform.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def self.capturer_class(animate = false)
elsif platform_linux?
animate ? CaptureLinuxAnimated : CaptureLinux
elsif platform_windows?
CaptureWindows
animate ? CaptureWindowsAnimated : CaptureWindows
elsif platform_cygwin?
CaptureCygwin
else
Expand Down Expand Up @@ -54,7 +54,7 @@ def self.host_os
# Is the platform capable of capturing animated GIFs from webcam?
# @return Boolean
def self.can_animate?
platform_linux? || platform_mac?
platform_linux? || platform_mac? || platform_windows?
end

# Is a valid install of imagemagick present on the system?
Expand Down Expand Up @@ -116,7 +116,7 @@ def self.git_config_color_always?
# @note Currently only functions on Mac.
# @return String
def self.device_list
# TODO: handle other platforms here (linux/windows)
# TODO: handle other platforms here (linux/windows) e.g with ffmpeg -list_devices
return unless Platform.platform_mac?
capturer = Lolcommits::CaptureMacAnimated.new
`#{capturer.executable_path} -l`
Expand Down

0 comments on commit f63deed

Please sign in to comment.