ffmprb 0.11.4 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Dockerfile +11 -5
- data/Gemfile +5 -5
- data/Gemfile.lock +54 -54
- data/README.md +57 -15
- data/TODO.md +0 -1
- data/bin/dev +12 -0
- data/bin/test +9 -3
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.3/application.css +1 -0
- data/coverage/assets/0.12.3/application.js +7 -0
- data/coverage/assets/0.12.3/colorbox/border.png +0 -0
- data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.3/favicon_green.png +0 -0
- data/coverage/assets/0.12.3/favicon_red.png +0 -0
- data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.3/loading.gif +0 -0
- data/coverage/assets/0.12.3/magnify.png +0 -0
- data/coverage/index.html +47166 -24254
- data/exp/EXP +7 -0
- data/exp/av-cut-mp4you60.ffmprb +10 -0
- data/exp/docker-compose.yml +9 -0
- data/exp/gop-cut-cat-you60 +141 -0
- data/exp/present/Dockerfile +13 -0
- data/exp/present/Gemfile +3 -0
- data/exp/present/Gemfile.lock +22 -0
- data/exp/present/bin/up-deps +8 -0
- data/exp/present/docker-compose.yml +21 -0
- data/exp/present/exp/present +10 -0
- data/exp/present/exp/present.rb +37 -0
- data/exp/run +9 -0
- data/exp/stitch2.ffmprb +5 -0
- data/exp/unzip-mp4you60 +58 -0
- data/exp/youtubby/Dockerfile +13 -0
- data/exp/youtubby/Gemfile +7 -0
- data/exp/youtubby/Gemfile.lock +73 -0
- data/exp/youtubby/README.md +21 -0
- data/exp/youtubby/bin/up-deps +8 -0
- data/exp/youtubby/docker-compose.yml +20 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60 +13 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60.rb +230 -0
- data/exp/youtubby/exp/media-upload +13 -0
- data/exp/youtubby/exp/tmp/CURRENT +2 -0
- data/exp/youtubby/google_youtube.rb +39 -0
- data/exp/youtubby/old-ul.py +181 -0
- data/exp/youtubby/old-ul.rb +87 -0
- data/exp/youtubby/py-Dockerfile +11 -0
- data/exp/youtubby/py-docker-compose.yml +13 -0
- data/exp/youtubby/requirements.txt +19 -0
- data/exp/youtubby/upload.rb +38 -0
- data/exp/zip-cut-mp4you60 +42 -0
- data/exp/zip2mp4k60 +27 -0
- data/lib/defaults.rb +5 -5
- data/lib/ffmprb/execution.rb +1 -1
- data/lib/ffmprb/file/threaded_buffered.rb +1 -1
- data/lib/ffmprb/file.rb +14 -14
- data/lib/ffmprb/filter.rb +96 -60
- data/lib/ffmprb/process/input/chain_base.rb +6 -4
- data/lib/ffmprb/process/input/channeled.rb +1 -1
- data/lib/ffmprb/process/input/cropped.rb +4 -1
- data/lib/ffmprb/process/input/cut.rb +2 -2
- data/lib/ffmprb/process/input/looping.rb +5 -5
- data/lib/ffmprb/process/input/loud.rb +1 -1
- data/lib/ffmprb/process/input/paced.rb +35 -0
- data/lib/ffmprb/process/input/postprocessed.rb +29 -0
- data/lib/ffmprb/process/input/reversed.rb +29 -0
- data/lib/ffmprb/process/input.rb +6 -2
- data/lib/ffmprb/process/output.rb +36 -23
- data/lib/ffmprb/process.rb +3 -6
- data/lib/ffmprb/util/proc_vis.rb +4 -3
- data/lib/ffmprb/util/thread.rb +8 -7
- data/lib/ffmprb/util/threaded_io_buffer.rb +5 -3
- data/lib/ffmprb/util.rb +37 -14
- data/lib/ffmprb/version.rb +1 -1
- data/lib/ffmprb.rb +5 -2
- data/tmp/exp/docker-compose.yml +9 -0
- data/tmp/exp/src/SAM_3132.MP4 +0 -0
- data/tmp/ffmprb-0.11.4.gem +0 -0
- 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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
50
|
+
self.proc_vis_firebase = proc_vis_firebase
|
51
51
|
elsif proc_vis_firebase
|
52
|
-
|
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
|
data/lib/ffmprb/execution.rb
CHANGED
data/lib/ffmprb/file.rb
CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
|
|
4
4
|
|
5
5
|
module Ffmprb
|
6
6
|
|
7
|
-
class File
|
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
|
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
|
31
|
+
def access(path)
|
32
32
|
new(path: path, mode: :read).tap do |file|
|
33
|
-
Ffmprb.logger.debug{"
|
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
|
-
|
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
|
-
|
77
|
+
join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
|
78
78
|
end
|
79
79
|
|
80
80
|
def image?(extname)
|
81
|
-
!!(extname =~
|
81
|
+
!!(extname =~ image_extname_regex)
|
82
82
|
end
|
83
83
|
|
84
84
|
def sound?(extname)
|
85
|
-
!!(extname =~
|
85
|
+
!!(extname =~ sound_extname_regex)
|
86
86
|
end
|
87
87
|
|
88
88
|
def movie?(extname)
|
89
|
-
!!(extname =~
|
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
|
-
|
115
|
+
File.exist? path
|
116
116
|
end
|
117
117
|
|
118
118
|
def basename
|
119
|
-
@basename ||=
|
119
|
+
@basename ||= File.basename(path)
|
120
120
|
end
|
121
121
|
|
122
122
|
def extname
|
123
|
-
@extname ||=
|
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
|
-
|
159
|
+
File.read path
|
160
160
|
end
|
161
161
|
def write(s)
|
162
|
-
|
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
|
12
|
+
inout 'alphamerge', inputs, output
|
13
13
|
end
|
14
14
|
|
15
15
|
def afade_in(duration, input=nil, output=nil)
|
16
|
-
inout
|
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
|
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(
|
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
|
41
|
+
inout 'anull', input, output
|
42
42
|
end
|
43
43
|
|
44
44
|
def anullsink(input=nil)
|
45
|
-
inout
|
45
|
+
inout 'anullsink', input, nil
|
46
46
|
end
|
47
47
|
|
48
48
|
def asplit(inputs=nil, outputs=nil)
|
49
|
-
inout
|
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
|
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
|
62
|
-
|
63
|
-
|
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
|
67
|
-
|
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
|
71
|
-
inout
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
195
|
+
inout 'nullsink', input, nil
|
143
196
|
end
|
144
197
|
|
145
198
|
def overlay(x=0, y=0, inputs=nil, output=nil)
|
146
|
-
inout
|
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(
|
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
|
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(
|
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
|
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
|
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
|
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
|
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) #
|
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
|
-
|
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
|
@@ -23,7 +23,7 @@ module Ffmprb
|
|
23
23
|
|
24
24
|
lbl_aux = "cp#{lbl}"
|
25
25
|
lbl_tmp = "tmp#{lbl}"
|
26
|
-
|
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
|
-
|
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
|
-
|
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
|
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:
|
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
|
-
|
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
|