video2gif 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18744ed8233c2766731e647303b9b4493545d860
4
- data.tar.gz: 94cafe3965b7cf4f5800b6ebd1d5c24f457f66f1
3
+ metadata.gz: 1266f3c1c715f41fa3c9d15bd11b788c74b4095d
4
+ data.tar.gz: 33c432d73a9daa08f89b1a758875a504a1c33f8f
5
5
  SHA512:
6
- metadata.gz: 8224b9efbff707dd5e79587fb51531ce158a9bc447a0d173b903987883ee3b0c32598a4d9de309b6f16f7b551f72d21691aef4898bfde74574e657946706e015
7
- data.tar.gz: 48e256f90d5a3dcf546684708997a342bd4f12081f93b57049ac851c708a6c787dc92792da2828932c94eff27ea88247493498e4bc2e2383e55284c18a2c8f67
6
+ metadata.gz: 820669a53355384b56f7060249a064bc57b1ccd000b575490e055b58ea18cd87438271bf5c0afb99fe139f0feff735a3f4dfd68d72b53dc148018345471b473b
7
+ data.tar.gz: ae0e081d4cd1ae6fa56a00259c8ffb7132f02ce3eea545cb90f36e788e857034fd3234057beaa03a2c33f30e69ce97ed9b667dace0ae097863eeb1f8625d0885
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- video2gif (0.0.13)
4
+ video2gif (0.0.14)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -3,15 +3,24 @@
3
3
 
4
4
  module Video2gif
5
5
  module FFMpeg
6
- def self.filter_complex(options)
7
- filter_complex = []
6
+ def self.filtergraph(options)
7
+ filtergraph = []
8
8
 
9
- filter_complex << "fps=#{ options[:fps] || 10 }"
9
+ # Set 'fps' filter first, drop unneeded frames instead of
10
+ # processing those.
11
+ filtergraph << "fps=#{ options[:fps] || 10 }"
10
12
 
13
+ # Crop if needed, using either settings discovered during the
14
+ # autocrop run or manually set parameters, so we don't process
15
+ # additional parts of the image (and exclude it from palette
16
+ # generation).
11
17
  if options[:autocrop]
12
- filter_complex << options[:autocrop]
13
- else
14
- filter_complex << 'crop=' + [
18
+ filtergraph << options[:autocrop]
19
+ elsif options[:wregion] ||
20
+ options[:hregion] ||
21
+ options[:xoffset] ||
22
+ options[:yoffset]
23
+ filtergraph << 'crop=' + [
15
24
  "w=#{ options[:wregion] || 'in_w' }",
16
25
  "h=#{ options[:hregion] || 'in_h' }",
17
26
  "x=#{ options[:xoffset] || 0 }",
@@ -19,29 +28,39 @@ module Video2gif
19
28
  ].join(':')
20
29
  end
21
30
 
31
+ # If we're not attempting to convert HDR to SDR, the standard
32
+ # 'scale' filter is preferred (if we're resizing at all). Scale
33
+ # here before other filters to avoid unnecessary processing.
22
34
  if options[:width] && !options[:tonemap]
23
- filter_complex << "scale=flags=lanczos:sws_dither=none:width=#{options[:width]}:height=-1"
35
+ filtergraph << "scale=flags=lanczos:sws_dither=none:width=#{options[:width]}:height=-1"
24
36
  end
25
37
 
38
+ # If we're attempting to convert HDR to SDR, use a set of 'zscale'
39
+ # filters, 'format' filters, and the 'tonemap' filter. The
40
+ # 'zscale' will do the resize for us as well.
26
41
  if options[:tonemap]
27
- filter_complex << "zscale=dither=none:filter=lanczos:width=#{options[:width]}:height=-1" if options[:width]
28
- filter_complex << 'zscale=transfer=linear:npl=100'
29
- filter_complex << 'zscale=npl=100'
30
- filter_complex << 'format=gbrpf32le'
31
- filter_complex << 'zscale=primaries=bt709'
32
- filter_complex << "tonemap=tonemap=#{options[:tonemap]}:desat=0"
33
- filter_complex << 'zscale=transfer=bt709:matrix=bt709:range=tv'
34
- filter_complex << 'format=yuv420p'
42
+ filtergraph << "zscale=dither=none:filter=lanczos:width=#{options[:width]}:height=-1" if options[:width]
43
+ filtergraph << 'zscale=transfer=linear:npl=100'
44
+ filtergraph << 'zscale=npl=100'
45
+ filtergraph << 'format=gbrpf32le'
46
+ filtergraph << 'zscale=primaries=bt709'
47
+ filtergraph << "tonemap=tonemap=#{options[:tonemap]}:desat=0"
48
+ filtergraph << 'zscale=transfer=bt709:matrix=bt709:range=tv'
49
+ filtergraph << 'format=yuv420p'
35
50
  end
36
51
 
37
- filter_complex << "eq=contrast=#{options[:contrast]}" if options[:contrast]
38
- filter_complex << "eq=brightness=#{options[:brightness]}" if options[:brightness]
39
- filter_complex << "eq=saturation=#{options[:saturation]}" if options[:saturation]
40
- filter_complex << "eq=gamma=#{options[:gamma]}" if options[:gamma]
41
- filter_complex << "eq=gamma_r=#{options[:gamma_r]}" if options[:gamma_r]
42
- filter_complex << "eq=gamma_g=#{options[:gamma_g]}" if options[:gamma_g]
43
- filter_complex << "eq=gamma_b=#{options[:gamma_b]}" if options[:gamma_b]
44
-
52
+ # Perform any desired equalization before we overlay text so that
53
+ # it won't be affected.
54
+ filtergraph << "eq=contrast=#{options[:contrast]}" if options[:contrast]
55
+ filtergraph << "eq=brightness=#{options[:brightness]}" if options[:brightness]
56
+ filtergraph << "eq=saturation=#{options[:saturation]}" if options[:saturation]
57
+ filtergraph << "eq=gamma=#{options[:gamma]}" if options[:gamma]
58
+ filtergraph << "eq=gamma_r=#{options[:gamma_r]}" if options[:gamma_r]
59
+ filtergraph << "eq=gamma_g=#{options[:gamma_g]}" if options[:gamma_g]
60
+ filtergraph << "eq=gamma_b=#{options[:gamma_b]}" if options[:gamma_b]
61
+
62
+ # If there is text to superimpose, do it here before palette
63
+ # generation to ensure the color looks appropriate.
45
64
  if options[:text]
46
65
  count_of_lines = options[:text].scan(/\\n/).count + 1
47
66
  text = options[:text]
@@ -52,7 +71,7 @@ module Video2gif
52
71
  .gsub(/\B"\b([^"\u201C\u201D\u201E\u201F\u2033\u2036\r\n]+)\b?"\B/, "\u201C\\1\u201D")
53
72
  .gsub(/\B'\b([^'\u2018\u2019\u201A\u201B\u2032\u2035\r\n]+)\b?'\B/, "\u2018\\1\u2019")
54
73
 
55
- filter_complex << 'drawtext=' + [
74
+ filtergraph << 'drawtext=' + [
56
75
  "x='#{ options[:xpos] || '(main_w/2-text_w/2)' }'",
57
76
  "y='#{ options[:ypos] || "(main_h-line_h*1.5*#{count_of_lines})" }'",
58
77
  "fontsize='#{ options[:textsize] || 32 }'",
@@ -63,21 +82,38 @@ module Video2gif
63
82
  ].join(':')
64
83
  end
65
84
 
66
- filter_complex << 'split [palettegen] [paletteuse]'
67
- filter_complex << "[palettegen] palettegen=#{options[:palette] || 256}:stats_mode=diff [palette]"
68
- filter_complex << '[paletteuse] [palette] paletteuse=dither=floyd_steinberg:diff_mode=rectangle'
85
+ # Split the stream into two copies, labeled with output pads for
86
+ # the palettegen/paletteuse filters to use.
87
+ filtergraph << 'split[palettegen][paletteuse]'
88
+
89
+ # Using a copy of the stream created above labeled "palettegen",
90
+ # generate a palette from the stream using the specified number of
91
+ # colors and optimizing for moving objects in the stream. Label
92
+ # this stream's output as "palette."
93
+ filtergraph << "[palettegen]palettegen=#{options[:palette] || 256}:stats_mode=diff[palette]"
94
+
95
+ # Using a copy of the stream from the 'split' filter and the
96
+ # generated palette as inputs, apply the final palette to the GIF.
97
+ # For non-moving parts of the GIF, attempt to reuse the same
98
+ # palette from frame to frame.
99
+ filtergraph << "[paletteuse][palette]paletteuse=dither=#{options[:dither] || 'floyd_steinberg'}:diff_mode=rectangle"
69
100
 
70
- filter_complex.join(',')
101
+ filtergraph.join(',')
71
102
  end
72
103
 
73
- def self.cropdetect_command(options, logger)
74
- command = ['ffmpeg']
104
+ def self.ffmpeg_command(options, executable: 'ffmpeg')
105
+ command = [executable]
106
+ command << '-y' # always overwrite
75
107
  command << '-analyzeduration' << '2147483647' << '-probesize' << '2147483647'
76
108
  command << '-loglevel' << 'verbose'
77
109
  command << '-ss' << options[:seek] if options[:seek]
78
110
  command << '-t' << options[:time] if options[:time]
79
111
  command << '-i' << options[:input_filename]
80
- command << '-filter_complex' << "cropdetect=limit=#{options[:autocrop]}"
112
+ end
113
+
114
+ def self.cropdetect_command(options, logger, executable: 'ffmpeg')
115
+ command = ffmpeg_command(options, executable: executable)
116
+ command << '-filtergraph' << "cropdetect=limit=#{options[:autocrop]}"
81
117
  command << '-f' << 'null'
82
118
  command << '-'
83
119
 
@@ -86,15 +122,9 @@ module Video2gif
86
122
  command
87
123
  end
88
124
 
89
- def self.gif_command(options, logger)
90
- command = ['ffmpeg']
91
- command << '-y' # always overwrite
92
- command << '-analyzeduration' << '2147483647' << '-probesize' << '2147483647'
93
- command << '-loglevel' << 'verbose'
94
- command << '-ss' << options[:seek] if options[:seek]
95
- command << '-t' << options[:time] if options[:time]
96
- command << '-i' << options[:input_filename]
97
- command << '-filter_complex' << filter_complex(options)
125
+ def self.gif_command(options, logger, executable: 'ffmpeg')
126
+ command = ffmpeg_command(options, executable: executable)
127
+ command << '-filter_complex' << filtergraph(options)
98
128
  command << '-f' << 'gif'
99
129
  command << options[:output_filename]
100
130
 
@@ -57,6 +57,17 @@ module Video2gif
57
57
  options[:palette] = p
58
58
  end
59
59
 
60
+ parser.on('-d [ALGORITHM]',
61
+ '--[no-]dither [ALGORITHM]',
62
+ 'Set the dithering algorithm for the palette generation",
63
+ "(default enabled with "floyd_steinberg")') do |d|
64
+ if d.nil?
65
+ options[:dither] = 'floyd_steinberg'
66
+ else
67
+ options[:dither] = d || 'none'
68
+ end
69
+ end
70
+
60
71
  parser.on('-c SIZE',
61
72
  '--crop-size-w SIZE',
62
73
  'Pixel size of width to select from source video, before scaling') do |s|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Video2gif
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.14'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: video2gif
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emily St.