ffmprb 0.11.4 → 0.12.1

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.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/Dockerfile +11 -5
  3. data/Gemfile +5 -5
  4. data/Gemfile.lock +54 -54
  5. data/README.md +57 -15
  6. data/TODO.md +0 -1
  7. data/bin/dev +12 -0
  8. data/bin/test +9 -3
  9. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
  10. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  11. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
  12. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
  13. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  14. data/coverage/assets/0.12.3/application.css +1 -0
  15. data/coverage/assets/0.12.3/application.js +7 -0
  16. data/coverage/assets/0.12.3/colorbox/border.png +0 -0
  17. data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
  18. data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
  19. data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
  20. data/coverage/assets/0.12.3/favicon_green.png +0 -0
  21. data/coverage/assets/0.12.3/favicon_red.png +0 -0
  22. data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
  23. data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  24. data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  25. data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  26. data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  27. data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  28. data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  29. data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  30. data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  31. data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
  32. data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
  33. data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
  34. data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
  35. data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
  36. data/coverage/assets/0.12.3/loading.gif +0 -0
  37. data/coverage/assets/0.12.3/magnify.png +0 -0
  38. data/coverage/index.html +47166 -24254
  39. data/exp/EXP +7 -0
  40. data/exp/av-cut-mp4you60.ffmprb +10 -0
  41. data/exp/docker-compose.yml +9 -0
  42. data/exp/gop-cut-cat-you60 +141 -0
  43. data/exp/present/Dockerfile +13 -0
  44. data/exp/present/Gemfile +3 -0
  45. data/exp/present/Gemfile.lock +22 -0
  46. data/exp/present/bin/up-deps +8 -0
  47. data/exp/present/docker-compose.yml +21 -0
  48. data/exp/present/exp/present +10 -0
  49. data/exp/present/exp/present.rb +37 -0
  50. data/exp/run +9 -0
  51. data/exp/stitch2.ffmprb +5 -0
  52. data/exp/unzip-mp4you60 +58 -0
  53. data/exp/youtubby/Dockerfile +13 -0
  54. data/exp/youtubby/Gemfile +7 -0
  55. data/exp/youtubby/Gemfile.lock +73 -0
  56. data/exp/youtubby/README.md +21 -0
  57. data/exp/youtubby/bin/up-deps +8 -0
  58. data/exp/youtubby/docker-compose.yml +20 -0
  59. data/exp/youtubby/exp/gop-raw-cut-you-HD60 +13 -0
  60. data/exp/youtubby/exp/gop-raw-cut-you-HD60.rb +230 -0
  61. data/exp/youtubby/exp/media-upload +13 -0
  62. data/exp/youtubby/exp/tmp/CURRENT +2 -0
  63. data/exp/youtubby/google_youtube.rb +39 -0
  64. data/exp/youtubby/old-ul.py +181 -0
  65. data/exp/youtubby/old-ul.rb +87 -0
  66. data/exp/youtubby/py-Dockerfile +11 -0
  67. data/exp/youtubby/py-docker-compose.yml +13 -0
  68. data/exp/youtubby/requirements.txt +19 -0
  69. data/exp/youtubby/upload.rb +38 -0
  70. data/exp/zip-cut-mp4you60 +42 -0
  71. data/exp/zip2mp4k60 +27 -0
  72. data/lib/defaults.rb +5 -5
  73. data/lib/ffmprb/execution.rb +1 -1
  74. data/lib/ffmprb/file/threaded_buffered.rb +1 -1
  75. data/lib/ffmprb/file.rb +14 -14
  76. data/lib/ffmprb/filter.rb +96 -60
  77. data/lib/ffmprb/process/input/chain_base.rb +6 -4
  78. data/lib/ffmprb/process/input/channeled.rb +1 -1
  79. data/lib/ffmprb/process/input/cropped.rb +4 -1
  80. data/lib/ffmprb/process/input/cut.rb +2 -2
  81. data/lib/ffmprb/process/input/looping.rb +5 -5
  82. data/lib/ffmprb/process/input/loud.rb +1 -1
  83. data/lib/ffmprb/process/input/paced.rb +35 -0
  84. data/lib/ffmprb/process/input/postprocessed.rb +29 -0
  85. data/lib/ffmprb/process/input/reversed.rb +29 -0
  86. data/lib/ffmprb/process/input.rb +6 -2
  87. data/lib/ffmprb/process/output.rb +36 -23
  88. data/lib/ffmprb/process.rb +3 -6
  89. data/lib/ffmprb/util/proc_vis.rb +4 -3
  90. data/lib/ffmprb/util/thread.rb +8 -7
  91. data/lib/ffmprb/util/threaded_io_buffer.rb +5 -3
  92. data/lib/ffmprb/util.rb +37 -14
  93. data/lib/ffmprb/version.rb +1 -1
  94. data/lib/ffmprb.rb +5 -2
  95. data/tmp/exp/docker-compose.yml +9 -0
  96. data/tmp/exp/src/SAM_3132.MP4 +0 -0
  97. data/tmp/ffmprb-0.11.4.gem +0 -0
  98. metadata +72 -4
data/exp/zip2mp4k60 ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ gem 'ffmprb'
4
+ require 'ffmprb'
5
+
6
+ if ARGV.length != 1
7
+ warn "Usage: zip2mp4k60 <zip-file>"
8
+ exit 1
9
+ end
10
+ zip_path = File.expand_path(ARGV[0])
11
+ out_path = File.join(File.dirname(zip_path), "#{File.basename zip_path, '.*'}.mp4")
12
+ video_opts = {resolution: '3840x2160', fps: 60}
13
+
14
+
15
+ Dir.mktmpdir do |tmp_dir|
16
+ Dir.chdir tmp_dir do
17
+ # XXX? this doesn't work as ffmprb input because of the way process do... works
18
+ system "unzip '#{zip_path}'"
19
+ Ffmprb.process do
20
+ output out_path, video: video_opts do
21
+ Dir['*'].sort.each do |in_path|
22
+ roll input in_path
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/defaults.rb CHANGED
@@ -40,16 +40,16 @@ module Ffmprb
40
40
 
41
41
  # NOTE http://12factor.net etc
42
42
 
43
- Ffmprb.log_level = Logger::INFO
44
- Ffmprb.ffmpeg_debug = ENV.fetch('FFMPRB_FFMPEG_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
45
- Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
43
+ self.log_level = Logger::INFO
44
+ self.ffmpeg_debug = ENV.fetch('FFMPRB_FFMPEG_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
45
+ self.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
46
46
 
47
47
  proc_vis_firebase = ENV['FFMPRB_PROC_VIS_FIREBASE']
48
48
  if Ffmprb::FIREBASE_AVAILABLE
49
49
  fail Error, "Please provide just the name of the firebase in FFMPRB_PROC_VIS_FIREBASE (e.g. my-proc-vis-io for https://my-proc-vis-io.firebaseio.com/proc/)" if proc_vis_firebase =~ /\//
50
- Ffmprb.proc_vis_firebase = proc_vis_firebase
50
+ self.proc_vis_firebase = proc_vis_firebase
51
51
  elsif proc_vis_firebase
52
- Ffmprb.logger.warn "Firebase unavailable (have firebase gem installed or unset FFMPRB_PROC_VIS_FIREBASE to get rid of this warning)"
52
+ logger.warn "Firebase unavailable (have firebase gem installed or unset FFMPRB_PROC_VIS_FIREBASE to get rid of this warning)"
53
53
  end
54
54
 
55
55
  end
@@ -25,7 +25,7 @@ module Ffmprb
25
25
  else
26
26
  Logger::WARN
27
27
  end
28
- Ffmprb.process *ios, ignore_broken_pipes: false, &script
28
+ Ffmprb.process *ios, &script
29
29
  end
30
30
 
31
31
  # NOTE a hack from http://stackoverflow.com/a/23955971/714287
@@ -1,6 +1,6 @@
1
1
  module Ffmprb
2
2
 
3
- class File # NOTE I would rather rename it to Stream at the moment
3
+ class File
4
4
 
5
5
  class << self
6
6
 
data/lib/ffmprb/file.rb CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
4
4
 
5
5
  module Ffmprb
6
6
 
7
- class File # NOTE I would rather rename it to Stream at the moment
7
+ class File < ::File
8
8
  include Util::ProcVis::Node
9
9
 
10
10
  class << self
@@ -18,7 +18,7 @@ module Ffmprb
18
18
  path = file.respond_to?(:path)? file.path : file
19
19
  mode ||= file.respond_to?(mode)? file.mode.to_s[0] : 'r'
20
20
  Ffmprb.logger.debug{"Trying to open #{path} (for #{mode}-buffering or something)"}
21
- ::File.open(path, mode)
21
+ ::File.open path, mode
22
22
  }
23
23
  end
24
24
 
@@ -28,9 +28,9 @@ module Ffmprb
28
28
  end
29
29
  end
30
30
 
31
- def open(path)
31
+ def access(path)
32
32
  new(path: path, mode: :read).tap do |file|
33
- Ffmprb.logger.debug{"Opened file with path: #{file.path}"}
33
+ Ffmprb.logger.debug{"Accessed file with path: #{file.path}"}
34
34
  end
35
35
  end
36
36
 
@@ -55,7 +55,7 @@ module Ffmprb
55
55
 
56
56
  def temp_fifo(extname='.tmp', &blk)
57
57
  path = temp_fifo_path(extname)
58
- ::File.mkfifo path
58
+ mkfifo path
59
59
  fifo_file = create(path)
60
60
 
61
61
  return fifo_file unless block_given?
@@ -74,19 +74,19 @@ module Ffmprb
74
74
  end
75
75
 
76
76
  def temp_fifo_path(extname)
77
- ::File.join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
77
+ join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
78
78
  end
79
79
 
80
80
  def image?(extname)
81
- !!(extname =~ File.image_extname_regex)
81
+ !!(extname =~ image_extname_regex)
82
82
  end
83
83
 
84
84
  def sound?(extname)
85
- !!(extname =~ File.sound_extname_regex)
85
+ !!(extname =~ sound_extname_regex)
86
86
  end
87
87
 
88
88
  def movie?(extname)
89
- !!(extname =~ File.movie_extname_regex)
89
+ !!(extname =~ movie_extname_regex)
90
90
  end
91
91
 
92
92
  end
@@ -112,15 +112,15 @@ module Ffmprb
112
112
  # Info
113
113
 
114
114
  def exist?
115
- ::File.exist? path
115
+ File.exist? path
116
116
  end
117
117
 
118
118
  def basename
119
- @basename ||= ::File.basename(path)
119
+ @basename ||= File.basename(path)
120
120
  end
121
121
 
122
122
  def extname
123
- @extname ||= ::File.extname(path)
123
+ @extname ||= File.extname(path)
124
124
  end
125
125
 
126
126
  def channel?(medium)
@@ -156,10 +156,10 @@ module Ffmprb
156
156
  # Manipulation
157
157
 
158
158
  def read
159
- ::File.read path
159
+ File.read path
160
160
  end
161
161
  def write(s)
162
- ::File.write path, s
162
+ File.write path, s
163
163
  end
164
164
 
165
165
  def unlink
data/lib/ffmprb/filter.rb CHANGED
@@ -9,15 +9,15 @@ module Ffmprb
9
9
  attr_accessor :silence_noise_max_db
10
10
 
11
11
  def alphamerge(inputs, output=nil)
12
- inout "alphamerge", inputs, output
12
+ inout 'alphamerge', inputs, output
13
13
  end
14
14
 
15
15
  def afade_in(duration, input=nil, output=nil)
16
- inout "afade=in:d=%{duration}:curve=hsin", input, output, duration: duration
16
+ inout 'afade=in:d=%{duration}:curve=hsin', input, output, duration: duration
17
17
  end
18
18
 
19
19
  def afade_out(duration, input=nil, output=nil)
20
- inout "afade=out:d=%{duration}:curve=hsin", input, output, duration: duration
20
+ inout 'afade=out:d=%{duration}:curve=hsin', input, output, duration: duration
21
21
  end
22
22
 
23
23
  def amix_to_first_same_volume(inputs, output=nil)
@@ -33,24 +33,45 @@ module Ffmprb
33
33
  end
34
34
  end
35
35
  filters +
36
- inout("amix=%{inputs_count}:duration=shortest:dropout_transition=0, volume=%{inputs_count}",
36
+ inout('amix=%{inputs_count}:duration=shortest:dropout_transition=0, volume=%{inputs_count}',
37
37
  new_inputs, output, inputs_count: (inputs.empty?? nil : inputs.size))
38
38
  end
39
39
 
40
40
  def anull(input=nil, output=nil)
41
- inout "anull", input, output
41
+ inout 'anull', input, output
42
42
  end
43
43
 
44
44
  def anullsink(input=nil)
45
- inout "anullsink", input, nil
45
+ inout 'anullsink', input, nil
46
46
  end
47
47
 
48
48
  def asplit(inputs=nil, outputs=nil)
49
- inout "asplit", inputs, outputs
49
+ inout 'asplit', inputs, outputs
50
+ end
51
+
52
+ def areverse(input=nil, output=nil)
53
+ inout 'areverse', input, output
54
+ end
55
+
56
+ def atempo(tempo, input=nil, output=nil)
57
+ fail Error, "Push the tempo!" unless
58
+ tempo > 0
59
+
60
+ fil = ''
61
+ tmp = tempo
62
+ while tmp > 2.0
63
+ fil += 'atempo=2.0, '
64
+ tmp /= 2.0
65
+ end
66
+ while tmp < 0.5
67
+ fil += 'atempo=0.5, '
68
+ tmp /= 0.5
69
+ end
70
+ inout "#{fil}atempo=#{tmp.to_f}", input, output
50
71
  end
51
72
 
52
73
  def atrim(st, en=nil, input=nil, output=nil)
53
- inout "atrim=%{start_end}, asetpts=PTS-STARTPTS", input, output,
74
+ inout 'atrim=%{start_end}, asetpts=PTS-STARTPTS', input, output,
54
75
  start_end: [st, en].compact.join(':')
55
76
  end
56
77
 
@@ -58,45 +79,64 @@ module Ffmprb
58
79
  color_source '0x000000@0', duration, resolution, fps, output
59
80
  end
60
81
 
61
- def color_source(color, duration, resolution, fps, output=nil)
62
- inout "color=%{color}:d=%{duration}:s=%{resolution}:r=%{fps}", nil, output,
63
- color: color, duration: duration, resolution: resolution, fps: fps
82
+ def blend_a(duration, inputs, output=nil)
83
+ fail Error, "must be given 2 inputs" unless inputs.size == 2
84
+
85
+ aux_lbl = "blnd#{inputs[0]}"
86
+ auxx_lbl = "x#{aux_lbl}"
87
+ [
88
+ *afade_out(duration, inputs[0], aux_lbl),
89
+ *afade_in(duration, inputs[1], auxx_lbl),
90
+ *amix_to_first_same_volume([auxx_lbl, aux_lbl], output)
91
+ ]
64
92
  end
65
93
 
66
- def fade_out_alpha(duration, input=nil, output=nil)
67
- inout "fade=out:d=%{duration}:alpha=1", input, output, duration: duration
94
+ def blend_v(duration, resolution, fps, inputs, output=nil)
95
+ fail Error, "must be given 2 inputs" unless inputs.size == 2
96
+
97
+ aux_lbl = "blnd#{inputs[0]}"
98
+ auxx_lbl = "x#{aux_lbl}"
99
+ [
100
+ *white_source(duration, resolution, fps, aux_lbl),
101
+ *inout([
102
+ *alphamerge([inputs[0], aux_lbl]),
103
+ *fade_out_alpha(duration)
104
+ ].join(', '), nil, auxx_lbl),
105
+ *overlay(0, 0, [inputs[1], auxx_lbl], output),
106
+ ]
68
107
  end
69
108
 
70
- def fps(fps, input=nil, output=nil)
71
- inout "fps=fps=%{fps}", input, output, fps: fps
109
+ def color_source(color, duration, resolution, fps, output=nil)
110
+ inout 'color=%{color}:d=%{duration}:s=%{resolution}:r=%{fps}', nil, output,
111
+ color: color, duration: duration, resolution: resolution, fps: fps
72
112
  end
73
113
 
74
114
  def concat_v(inputs, output=nil)
75
115
  return copy(inputs, output) if inputs.size == 1
76
- inout "concat=#{inputs.size}:v=1:a=0", inputs, output
116
+ inout 'concat=%{count}:v=1:a=0', inputs, output, count: inputs.size
77
117
  end
78
118
 
79
119
  def concat_a(inputs, output=nil)
80
120
  return anull(inputs, output) if inputs.size == 1
81
- inout "concat=#{inputs.size}:v=0:a=1", inputs, output
121
+ inout 'concat=%{count}:v=0:a=1', inputs, output, count: inputs.size
82
122
  end
83
123
 
84
124
  def concat_av(inputs, output=nil)
85
125
  fail Error, "must be given an even number of inputs" unless inputs.size.even?
86
- inout "concat=#{inputs.size/2}:v=1:a=1", inputs, output
126
+ inout 'concat=%{count}:v=1:a=1', inputs, output, count: inputs.size/2
87
127
  end
88
128
 
89
129
  def copy(input=nil, output=nil)
90
- inout "copy", input, output
130
+ inout 'copy', input, output
91
131
  end
92
132
 
93
133
  # TODO unused at the moment
94
134
  def crop(crop, input=nil, output=nil)
95
- inout "crop=x=%{left}:y=%{top}:w=%{width}:h=%{height}", input, output, crop
135
+ inout 'crop=x=%{left}:y=%{top}:w=%{width}:h=%{height}', input, output, crop
96
136
  end
97
137
 
98
138
  def crop_prop(crop, input=nil, output=nil)
99
- inout "crop=%{crop_exp}", input, output,
139
+ inout 'crop=%{crop_exp}', input, output,
100
140
  crop_exp: crop_prop_exps(crop).join(':')
101
141
  end
102
142
 
@@ -136,33 +176,58 @@ module Ffmprb
136
176
  exps
137
177
  end
138
178
 
179
+ def fade_out_alpha(duration, input=nil, output=nil)
180
+ inout 'fade=out:d=%{duration}:alpha=1', input, output, duration: duration
181
+ end
182
+
183
+ def fps(fps, input=nil, output=nil)
184
+ inout 'fps=fps=%{fps}', input, output, fps: fps
185
+ end
186
+
187
+ def interpolate_v(fps, input=nil, output=nil)
188
+ inout 'framerate=fps=%{fps}', input, output, fps: fps
189
+ end
190
+ # TODO other effects like... minterpolate=fps=%{fps}:mi_mode=mci:mc_mode=aobmc:vsbmc=1
191
+
139
192
  # NOTE might be very useful with UGC: def cropdetect
140
193
 
141
194
  def nullsink(input=nil)
142
- inout "nullsink", input, nil
195
+ inout 'nullsink', input, nil
143
196
  end
144
197
 
145
198
  def overlay(x=0, y=0, inputs=nil, output=nil)
146
- inout "overlay=x=%{x}:y=%{y}:eof_action=pass", inputs, output, x: x, y: y
199
+ inout 'overlay=x=%{x}:y=%{y}:eof_action=pass', inputs, output, x: x, y: y
147
200
  end
148
201
 
149
202
  def pad(resolution, input=nil, output=nil)
150
203
  width, height = resolution.to_s.split('x')
151
204
  inout [
152
- inout("pad=%{width}:%{height}:(%{width}-iw*min(%{width}/iw\\,%{height}/ih))/2:(%{height}-ih*min(%{width}/iw\\,%{height}/ih))/2",
205
+ inout('pad=%{width}:%{height}:(%{width}-iw*min(%{width}/iw\\,%{height}/ih))/2:(%{height}-ih*min(%{width}/iw\\,%{height}/ih))/2',
153
206
  width: width, height: height),
154
207
  *setsar(1) # NOTE the scale & pad formulae damage SAR a little, unfortunately
155
208
  ].join(', '), input, output
156
209
  end
157
210
 
211
+ def pp(input=nil, output=nil)
212
+ inout 'pp=hb/vb/dr/al', input, output
213
+ end
214
+
215
+ def reverse(input=nil, output=nil)
216
+ inout 'reverse', input, output
217
+ end
218
+
158
219
  def setsar(ratio, input=nil, output=nil)
159
- inout "setsar=%{ratio}", input, output, ratio: ratio
220
+ inout 'setsar=%{ratio}', input, output, ratio: ratio
221
+ end
222
+
223
+ def setpts(ratio, input=nil, output=nil)
224
+ inout 'setpts=%{r_fps}*PTS', input, output, r_fps: 1.0/ratio
160
225
  end
161
226
 
162
227
  def scale(resolution, input=nil, output=nil)
163
228
  width, height = resolution.to_s.split('x')
164
229
  inout [
165
- inout("scale=iw*min(%{width}/iw\\,%{height}/ih):ih*min(%{width}/iw\\,%{height}/ih)", width: width, height: height),
230
+ inout('scale=iw*min(%{width}/iw\\,%{height}/ih):ih*min(%{width}/iw\\,%{height}/ih)', width: width, height: height),
166
231
  *setsar(1) # NOTE the scale & pad formulae damage SAR a little, unfortunately
167
232
  ].join(', '), input, output
168
233
  end
@@ -182,49 +247,22 @@ module Ffmprb
182
247
  end
183
248
 
184
249
  def silencedetect(input=nil, output=nil)
185
- inout "silencedetect=d=1:n=%{silence_noise_max_db}dB", input, output,
250
+ inout 'silencedetect=d=1:n=%{silence_noise_max_db}dB', input, output,
186
251
  silence_noise_max_db: silence_noise_max_db
187
252
  end
188
253
 
189
254
  def silent_source(duration, output=nil)
190
- inout "aevalsrc=0:d=%{duration}", nil, output, duration: duration
255
+ inout 'aevalsrc=0:d=%{duration}', nil, output, duration: duration
191
256
  end
192
257
 
193
258
  # NOTE might be very useful with transitions: def smartblur
194
259
 
195
260
  def split(inputs=nil, outputs=nil)
196
- inout "split", inputs, outputs
197
- end
198
-
199
- def blend_v(duration, resolution, fps, inputs, output=nil)
200
- fail Error, "must be given 2 inputs" unless inputs.size == 2
201
-
202
- aux_lbl = "blnd#{inputs[0]}"
203
- auxx_lbl = "x#{aux_lbl}"
204
- [
205
- *white_source(duration, resolution, fps, aux_lbl),
206
- *inout([
207
- *alphamerge([inputs[0], aux_lbl]),
208
- *fade_out_alpha(duration)
209
- ].join(', '), nil, auxx_lbl),
210
- *overlay(0, 0, [inputs[1], auxx_lbl], output),
211
- ]
212
- end
213
-
214
- def blend_a(duration, inputs, output=nil)
215
- fail Error, "must be given 2 inputs" unless inputs.size == 2
216
-
217
- aux_lbl = "blnd#{inputs[0]}"
218
- auxx_lbl = "x#{aux_lbl}"
219
- [
220
- *afade_out(duration, inputs[0], aux_lbl),
221
- *afade_in(duration, inputs[1], auxx_lbl),
222
- *amix_to_first_same_volume([auxx_lbl, aux_lbl], output)
223
- ]
261
+ inout 'split', inputs, outputs
224
262
  end
225
263
 
226
264
  def trim(st, en=nil, input=nil, output=nil)
227
- inout "trim=%{start_end}, setpts=PTS-STARTPTS", input, output,
265
+ inout 'trim=%{start_end}, setpts=PTS-STARTPTS', input, output,
228
266
  start_end: [st, en].compact.join(':')
229
267
  end
230
268
 
@@ -261,6 +299,7 @@ module Ffmprb
261
299
  color_source '0xFFFFFF@1', duration, resolution, fps, output
262
300
  end
263
301
 
302
+
264
303
  def complex_args(*filters)
265
304
  [].tap do |args|
266
305
  args << '-filter_complex' << filters.join('; ') unless
@@ -279,9 +318,6 @@ module Ffmprb
279
318
  filter = filter + " #{[*outputs].map{|s| "[#{s}]"}.join ' '}" if outputs
280
319
  [filter]
281
320
  end
282
-
283
321
  end
284
-
285
322
  end
286
-
287
323
  end
@@ -14,16 +14,18 @@ module Ffmprb
14
14
  def unfiltered=(input); @io = input; end
15
15
 
16
16
 
17
- def chain_copy(src_input) # XXX SPEC ME
17
+ def chain_copy(src_input) # TODO SPEC ME
18
18
  dup.tap do |top|
19
19
  top.unfiltered = unfiltered.chain_copy(src_input)
20
20
  end
21
21
  end
22
22
 
23
- end
23
+ def filters_for(lbl, video:, audio:)
24
24
 
25
+ # Doing nothing
26
+ unfiltered.filters_for lbl, video: video, audio: audio
27
+ end
28
+ end
25
29
  end
26
-
27
30
  end
28
-
29
31
  end
@@ -25,7 +25,7 @@ module Ffmprb
25
25
 
26
26
  def filters_for(lbl, video:, audio:)
27
27
 
28
- # Doing nothing
28
+ # Doing basically nothing
29
29
 
30
30
  unfiltered.filters_for lbl,
31
31
  video: channel?(:video) && video, audio: channel?(:audio) && audio
@@ -23,7 +23,7 @@ module Ffmprb
23
23
 
24
24
  lbl_aux = "cp#{lbl}"
25
25
  lbl_tmp = "tmp#{lbl}"
26
- unfiltered.filters_for(lbl_aux, video: unsize(video), audio: audio) +
26
+ super(lbl_aux, video: unsize(video), audio: audio) +
27
27
  [
28
28
  *((video && channel?(:video))? [
29
29
  Filter.crop_prop(ratios, "#{lbl_aux}:v", "#{lbl_tmp}:v"),
@@ -58,6 +58,9 @@ module Ffmprb
58
58
  fail Error, "Crop #{key} must be between 0 and 1 (not '#{value}')" unless
59
59
  (0...1).include? value
60
60
  end
61
+ fail Error, "Unreasonable crop args (#{ratios})" unless
62
+ (!ratios.include?(:left) || !ratios.include?(:right) || ratios[:left] + ratios[:right] < 1) &&
63
+ (!ratios.include?(:top) || !ratios.include?(:bottom) || ratios[:top] + ratios[:bottom] < 1)
61
64
  end
62
65
  end
63
66
 
@@ -23,12 +23,12 @@ module Ffmprb
23
23
 
24
24
  def filters_for(lbl, video:, audio:)
25
25
  fail Error, "cut needs resolution and fps (reorder your filters?)" unless
26
- !video || video.resolution && video.fps
26
+ !video || (video.resolution && video.fps)
27
27
 
28
28
  # Trimming
29
29
 
30
30
  lbl_aux = "tm#{lbl}"
31
- unfiltered.filters_for(lbl_aux, video: video, audio: audio) +
31
+ super(lbl_aux, video: video, audio: audio) +
32
32
  if to
33
33
  lbl_blk = "bl#{lbl}"
34
34
  lbl_pad = "pd#{lbl}"
@@ -41,6 +41,7 @@ module Ffmprb
41
41
  protected
42
42
 
43
43
  def aux_input(video:, audio:)
44
+ Ffmprb.logger.debug{"Creating aux inp with #{audio} / #{video}"}
44
45
 
45
46
  # NOTE (2)
46
47
  # NOTE replace the raw input io with a copy io, getting original fifo/file
@@ -50,12 +51,11 @@ module Ffmprb
50
51
  meh_src_io, src_io = src_io, File.temp_fifo(intermediate_extname)
51
52
  Util::Thread.new "source converter" do
52
53
  Ffmprb.process do
53
-
54
54
  inp = input(meh_src_io)
55
- output(src_io, video: nil, audio: nil) do # XXX this is not properly tested, unfortunately
55
+ # TODO this is not properly tested, unfortunately
56
+ output src_io, video: video, audio: audio do
56
57
  lay inp
57
58
  end
58
-
59
59
  end
60
60
  end
61
61
  end
@@ -87,7 +87,7 @@ module Ffmprb
87
87
  Util::Thread.new "cloning buffer watcher" do
88
88
  dst_io.threaded_buffered_copy_to(*buff_ios).tap do |io_buff|
89
89
  Util::Thread.join_children!
90
- Ffmprb.logger.warn "Looping ~from #{src_io.path} finished before its consumer: if you just wanted to loop input #{Util.ffmpeg_inputs_max} times, that's fine, but if you expected it to loop indefinitely... #{Util.ffmpeg_inputs_max} is the maximum #loop can do at the moment, and it may just not be enough in this case (workaround by concatting or file a complaint at #{Ffmprb::GEM_GITHUB_URL}/issues please)." if times == Util.ffmpeg_inputs_max && io_buff.stats.blocks_buff == 0
90
+ Ffmprb.logger.warn "Looping ~from #{src_io.path} finished before its consumer: if you just wanted to loop input #{Util.ffmpeg_inputs_max} times, that's fine, but if you expected it to loop indefinitely... #{Util.ffmpeg_inputs_max} is the maximum #loop can do at the moment, and it may just not be enough in this case (workaround by concatting or something)." if times == Util.ffmpeg_inputs_max && io_buff.stats.blocks_buff == 0
91
91
  end
92
92
  end
93
93
 
@@ -104,7 +104,7 @@ module Ffmprb
104
104
  Ffmprb.process parent: @raw.process, ignore_broken_pipes: false do
105
105
 
106
106
  ins = buff_ios.map{ |i| input i }
107
- output(aux_io, video: nil, audio: nil) do
107
+ output(aux_io, video: video, audio: audio) do
108
108
  ins.each{ |i| lay i }
109
109
  end
110
110
 
@@ -26,7 +26,7 @@ module Ffmprb
26
26
  # Modulating volume
27
27
 
28
28
  lbl_aux = "ld#{lbl}"
29
- unfiltered.filters_for(lbl_aux, video: video, audio: audio) +
29
+ super(lbl_aux, video: video, audio: audio) +
30
30
  [
31
31
  *((video && channel?(:video))? Filter.copy("#{lbl_aux}:v", "#{lbl}:v"): nil),
32
32
  *((audio && channel?(:audio))? Filter.volume(@volume, "#{lbl_aux}:a", "#{lbl}:a"): nil)
@@ -0,0 +1,35 @@
1
+ module Ffmprb
2
+
3
+ class Process
4
+
5
+ class Input
6
+
7
+ # TODO? speed-up/slow-down
8
+ def pace(ratio)
9
+ Paced.new self, pace: ratio
10
+ end
11
+
12
+ class Paced < ChainBase
13
+
14
+ attr_reader :ratio
15
+
16
+ def initialize(unfiltered, pace:)
17
+ super unfiltered
18
+ @ratio = pace
19
+ end
20
+
21
+ def filters_for(lbl, video:, audio:)
22
+
23
+ # Pacing
24
+
25
+ lbl_aux = "pc#{lbl}"
26
+ super(lbl_aux, video: video, audio: audio) +
27
+ [
28
+ *((video && channel?(:video))? Filter.setpts(@ratio, "#{lbl_aux}:v", "#{lbl}:v"): nil),
29
+ *((audio && channel?(:audio))? Filter.atempo(@ratio, "#{lbl_aux}:a", "#{lbl}:a"): nil)
30
+ ]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Ffmprb
2
+
3
+ class Process
4
+
5
+ class Input
6
+
7
+ def pp
8
+ Postprocessed.new self
9
+ end
10
+
11
+ # TODO test this somehow
12
+
13
+ class Postprocessed < ChainBase
14
+
15
+ def filters_for(lbl, video:, audio:)
16
+
17
+ # Postprocessing
18
+
19
+ lbl_aux = "pp#{lbl}"
20
+ super(lbl_aux, video: video, audio: audio) +
21
+ [
22
+ *((video && channel?(:video))? Filter.pp("#{lbl_aux}:v", "#{lbl}:v"): nil),
23
+ *((audio && channel?(:audio))? Filter.anull("#{lbl_aux}:a", "#{lbl}:a"): nil)
24
+ ]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Ffmprb
2
+
3
+ class Process
4
+
5
+ class Input
6
+
7
+ def reverse
8
+ Reversed.new self
9
+ end
10
+
11
+ class Reversed < ChainBase
12
+
13
+ # TODO check this is reasonable and not a (live) stream...
14
+
15
+ def filters_for(lbl, video:, audio:)
16
+
17
+ # Reversing
18
+
19
+ lbl_aux = "rv#{lbl}"
20
+ super(lbl_aux, video: video, audio: audio) +
21
+ [
22
+ *((video && channel?(:video))? Filter.reverse("#{lbl_aux}:v", "#{lbl}:v"): nil),
23
+ *((audio && channel?(:audio))? Filter.areverse("#{lbl_aux}:a", "#{lbl}:a"): nil)
24
+ ]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end