ffmprb 0.11.4 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
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