ffmprb 0.6.9 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/circle.yml +3 -0
- data/exe/ffmprb +11 -0
- data/lib/ffmprb/execution.rb +23 -0
- data/lib/ffmprb/file.rb +61 -20
- data/lib/ffmprb/filter.rb +5 -5
- data/lib/ffmprb/find_silence.rb +29 -0
- data/lib/ffmprb/process/input.rb +20 -11
- data/lib/ffmprb/process/output.rb +21 -9
- data/lib/ffmprb/util/io_buffer.rb +17 -6
- data/lib/ffmprb/util.rb +2 -2
- data/lib/ffmprb/version.rb +1 -1
- data/lib/ffmprb.rb +14 -36
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f8aefe1bb66a17ba85e93faf30b2ac540badee5
|
4
|
+
data.tar.gz: e3260e027699ad813154fc24c64049f42b39c114
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 430d988b9a2c68dbdbbe7d75f1e20d3e9c2a92c2f0f905b8c461005d5667a522e9830b1fba2f88a339b131b285765401fdc6bfdc15cd1088295a017f6859f62c
|
7
|
+
data.tar.gz: 0675dcaeabd664f44e497f72f96860437171661cae6250b223a9610105206e139c38f2b5b04a49fffd09ff0b886039d4754a47f27a31d32322418ce3e754ae4f
|
data/circle.yml
ADDED
data/exe/ffmprb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
class Execution
|
4
|
+
|
5
|
+
def initialize(*params, script:)
|
6
|
+
@params = params
|
7
|
+
@script = eval("lambda{#{script}}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
Ffmprb.process *@params, &@script
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.execute
|
17
|
+
return STDERR.puts "Usage: (not quite usual) $ ffmprb streams... < script.ffmprb" unless
|
18
|
+
ARGV.length > 1 && ARGV.grep(/^-/).empty?
|
19
|
+
|
20
|
+
Execution.new(*ARGV, script: STDIN.read).run
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/ffmprb/file.rb
CHANGED
@@ -13,16 +13,9 @@ module Ffmprb
|
|
13
13
|
output_fifo_file = temp_fifo(extname)
|
14
14
|
|
15
15
|
Ffmprb.logger.debug "Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"
|
16
|
-
buff = Util::IoBuffer.new(
|
17
|
-
|
18
|
-
|
19
|
-
::File.open(input_fifo_file.path, 'r')
|
20
|
-
},
|
21
|
-
->{
|
22
|
-
Ffmprb.logger.debug "Trying to open #{output_fifo_file.path} for buffering+writing"
|
23
|
-
::File.open(output_fifo_file.path, 'w')
|
24
|
-
}
|
25
|
-
)
|
16
|
+
buff = Util::IoBuffer.new(async_opener(input_fifo_file, 'r'), async_opener(output_fifo_file, 'w'))
|
17
|
+
|
18
|
+
# NOTE the blocking cleanup thread to join for synchronisation
|
26
19
|
thr = Util::Thread.new do
|
27
20
|
buff.flush!
|
28
21
|
Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"
|
@@ -31,7 +24,7 @@ module Ffmprb
|
|
31
24
|
end
|
32
25
|
Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"
|
33
26
|
|
34
|
-
yield buff if block_given?
|
27
|
+
# XXX see io_buffer's XXX yield buff if block_given?
|
35
28
|
|
36
29
|
OpenStruct.new in: input_fifo_file, out: output_fifo_file, thr: thr
|
37
30
|
end
|
@@ -83,6 +76,15 @@ module Ffmprb
|
|
83
76
|
::File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('', 'p' + extname)
|
84
77
|
end
|
85
78
|
|
79
|
+
protected
|
80
|
+
|
81
|
+
def async_opener(file, mode)
|
82
|
+
->{
|
83
|
+
Ffmprb.logger.debug "Trying to open #{file.path} for #{mode}-buffering"
|
84
|
+
::File.open(file.path, mode)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
86
88
|
end
|
87
89
|
|
88
90
|
|
@@ -100,15 +102,35 @@ module Ffmprb
|
|
100
102
|
|
101
103
|
# Info
|
102
104
|
|
105
|
+
def exist?
|
106
|
+
::File.exist? path
|
107
|
+
end
|
108
|
+
|
103
109
|
def extname
|
104
110
|
::File.extname path
|
105
111
|
end
|
106
112
|
|
113
|
+
def channel?(medium)
|
114
|
+
case medium
|
115
|
+
when :video
|
116
|
+
image_extname? || movie_extname?
|
117
|
+
when :audio
|
118
|
+
sound_extname? || movie_extname?
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
107
122
|
def length
|
108
|
-
@duration
|
109
|
-
|
123
|
+
return @duration if @duration
|
124
|
+
|
125
|
+
# NOTE first attempt
|
126
|
+
@duration = probe['format']['duration']
|
127
|
+
@duration &&= @duration.to_f
|
128
|
+
return @duration if @duration
|
110
129
|
|
111
|
-
|
130
|
+
# NOTE a harder try
|
131
|
+
@duration = probe(true)['frames'].reduce(0) do |sum, frame|
|
132
|
+
sum + frame['pkt_duration_time'].to_f
|
133
|
+
end
|
112
134
|
end
|
113
135
|
|
114
136
|
def resolution
|
@@ -127,9 +149,9 @@ module Ffmprb
|
|
127
149
|
|
128
150
|
Ffmprb.logger.debug "Snap shooting files, video path: #{video ? video.path : 'NONE'}, audio path: #{audio ? audio.path : 'NONE'}"
|
129
151
|
|
130
|
-
raise Error, "Incorrect output extname (must be .jpg)" unless !video || video.
|
131
|
-
raise Error, "Incorrect audio extname (must be .mp3)" unless !audio || audio.
|
132
|
-
raise Error, "Can sample either video OR audio UNLESS a block is given" unless block_given? ||
|
152
|
+
raise Error, "Incorrect output extname (must be .jpg)" unless !video || video.channel?(:video) && !video.channel?(:audio)
|
153
|
+
raise Error, "Incorrect audio extname (must be .mp3)" unless !audio || audio.channel?(:audio) && !audio.channel?(:video)
|
154
|
+
raise Error, "Can sample either video OR audio UNLESS a block is given" unless block_given? || !!audio != !!video
|
133
155
|
|
134
156
|
cmd = ['-i', path]
|
135
157
|
cmd += ['-deinterlace', '-an', '-ss', at, '-r', 1, '-vcodec', 'mjpeg', '-f', 'mjpeg', video.path] if video
|
@@ -153,6 +175,13 @@ module Ffmprb
|
|
153
175
|
|
154
176
|
# Manipulation
|
155
177
|
|
178
|
+
def read
|
179
|
+
::File.read path
|
180
|
+
end
|
181
|
+
def write(s)
|
182
|
+
::File.write path, s
|
183
|
+
end
|
184
|
+
|
156
185
|
def remove
|
157
186
|
FileUtils.remove_entry path
|
158
187
|
Ffmprb.logger.debug "Removed file with path: #{path}"
|
@@ -170,15 +199,27 @@ module Ffmprb
|
|
170
199
|
end
|
171
200
|
end
|
172
201
|
|
173
|
-
def probe(
|
174
|
-
return @probe unless !@probe ||
|
202
|
+
def probe(harder=false)
|
203
|
+
return @probe unless !@probe || harder
|
175
204
|
cmd = ['-v', 'quiet', '-i', path, '-print_format', 'json', '-show_format', '-show_streams']
|
176
|
-
cmd << '-show_frames' if
|
205
|
+
cmd << '-show_frames' if harder
|
177
206
|
@probe = JSON.parse(Util::ffprobe *cmd).tap do |probe|
|
178
207
|
raise Error, "This doesn't look like a ffprobable file" unless probe['streams']
|
179
208
|
end
|
180
209
|
end
|
181
210
|
|
211
|
+
def image_extname?
|
212
|
+
extname =~ /^\.(jpe?g|y4m)$/i
|
213
|
+
end
|
214
|
+
|
215
|
+
def sound_extname?
|
216
|
+
extname =~ /^\.(mp3|wav)$/i
|
217
|
+
end
|
218
|
+
|
219
|
+
def movie_extname?
|
220
|
+
extname =~ /^\.(mp4|flv)$/i
|
221
|
+
end
|
222
|
+
|
182
223
|
end
|
183
224
|
|
184
225
|
end
|
data/lib/ffmprb/filter.rb
CHANGED
@@ -18,8 +18,7 @@ module Ffmprb
|
|
18
18
|
inout "afade=out:d=#{duration}", input, output
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
def amix(inputs, output=nil)
|
21
|
+
def amix_to_first(inputs, output=nil)
|
23
22
|
inout "amix=#{[*inputs].length}:duration=first", inputs, output
|
24
23
|
end
|
25
24
|
|
@@ -169,7 +168,7 @@ module Ffmprb
|
|
169
168
|
filters.concat [
|
170
169
|
*afade_out(blend_duration, "#{inputs.first}:a", "#{aux_lbl}:a"),
|
171
170
|
*afade_in(blend_duration, "#{inputs.last}:a", "#{auxx_lbl}:a"),
|
172
|
-
*
|
171
|
+
*amix_to_first(["#{auxx_lbl}:a", "#{aux_lbl}:a"], "#{output}:a")
|
173
172
|
] if audio
|
174
173
|
end
|
175
174
|
end
|
@@ -182,8 +181,9 @@ module Ffmprb
|
|
182
181
|
inout "volume='#{volume_exp volume}':eval=frame", input, output
|
183
182
|
end
|
184
183
|
|
185
|
-
def volume_exp(volume)
|
186
|
-
return volume unless volume.
|
184
|
+
def volume_exp(volume)
|
185
|
+
return volume unless volume.is_a?(Hash)
|
186
|
+
|
187
187
|
raise Error, "volume cannot be empty" if volume.empty?
|
188
188
|
|
189
189
|
prev_at = 0.0
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
def self.find_silence(input_file, output_file)
|
4
|
+
logger.debug "Finding silence (#{input_file.path}->#{output_file.path})"
|
5
|
+
filters = Filter.silencedetect
|
6
|
+
options = ['-i', input_file.path, *Filter.complex_options(filters), output_file.path]
|
7
|
+
silence = []
|
8
|
+
Util.ffmpeg(*options).split("\n").each do |line|
|
9
|
+
next unless line =~ /^\[silencedetect\s.*\]\s*silence_(\w+):\s*(\d+\.?d*)/
|
10
|
+
case $1
|
11
|
+
when 'start'
|
12
|
+
silence << OpenStruct.new(start_at: $2.to_f)
|
13
|
+
when 'end'
|
14
|
+
if silence.empty?
|
15
|
+
silence << OpenStruct.new(start_at: 0.0, end_at: $2.to_f)
|
16
|
+
else
|
17
|
+
raise Error, "ffmpeg is being stupid: silence_end with no silence_start" if silence.last.end_at
|
18
|
+
silence.last.end_at = $2.to_f
|
19
|
+
end
|
20
|
+
else
|
21
|
+
Ffmprb.warn "Unknown silence mark: #{$1}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
logger.debug "Found silence (#{input_file.path}->#{output_file.path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
|
26
|
+
silence
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/ffmprb/process/input.rb
CHANGED
@@ -105,12 +105,11 @@ module Ffmprb
|
|
105
105
|
|
106
106
|
|
107
107
|
def initialize(io, only: nil)
|
108
|
-
@io = io
|
108
|
+
@io = resolve(io)
|
109
109
|
@channels = [*only]
|
110
110
|
@channels = nil if @channels.empty?
|
111
111
|
raise Error, "Inadequate A/V channels" if
|
112
|
-
|
113
|
-
[:video, :audio].any?{|medium| !@io.channel?(medium) && channel?(medium, true)}
|
112
|
+
[:video, :audio].any?{|medium| !@io.channel?(medium) && channel?(medium, true)}
|
114
113
|
end
|
115
114
|
|
116
115
|
def options
|
@@ -121,7 +120,7 @@ module Ffmprb
|
|
121
120
|
|
122
121
|
# Channelling
|
123
122
|
|
124
|
-
if @io.respond_to?(:filters_for)
|
123
|
+
if @io.respond_to?(:filters_for)
|
125
124
|
lbl_aux = "au#{lbl}"
|
126
125
|
@io.filters_for(lbl_aux, process: process, video: video, audio: audio) +
|
127
126
|
[
|
@@ -136,8 +135,8 @@ module Ffmprb
|
|
136
135
|
in_lbl = process[self]
|
137
136
|
raise Error, "Data corruption" unless in_lbl
|
138
137
|
[
|
139
|
-
*(video && channel?(:video)? Filter.copy("#{in_lbl}:v", "#{lbl}:v"): nil),
|
140
|
-
*(audio && channel?(:audio)? Filter.anull("#{in_lbl}:a", "#{lbl}:a"): nil)
|
138
|
+
*(video && @io.channel?(:video) && channel?(:video)? Filter.copy("#{in_lbl}:v", "#{lbl}:v"): nil),
|
139
|
+
*(audio && @io.channel?(:audio) && channel?(:audio)? Filter.anull("#{in_lbl}:a", "#{lbl}:a"): nil)
|
141
140
|
]
|
142
141
|
end
|
143
142
|
end
|
@@ -162,13 +161,23 @@ module Ffmprb
|
|
162
161
|
Loud.new self, volume: vol
|
163
162
|
end
|
164
163
|
|
165
|
-
# XXX? protected
|
166
|
-
|
167
164
|
def channel?(medium, force=false)
|
168
|
-
return
|
165
|
+
return !!@channels && @channels.include?(medium) && @io.channel?(medium) if force
|
166
|
+
|
167
|
+
(!@channels || @channels.include?(medium)) && @io.channel?(medium)
|
168
|
+
end
|
169
|
+
|
170
|
+
protected
|
169
171
|
|
170
|
-
|
171
|
-
|
172
|
+
def resolve(io)
|
173
|
+
return io unless io.is_a? String
|
174
|
+
|
175
|
+
case io
|
176
|
+
when /^\/\w/
|
177
|
+
File.open io
|
178
|
+
else
|
179
|
+
raise Error, "Cannot resolve input: #{io}"
|
180
|
+
end
|
172
181
|
end
|
173
182
|
|
174
183
|
end
|
@@ -5,13 +5,15 @@ module Ffmprb
|
|
5
5
|
class Output
|
6
6
|
|
7
7
|
def initialize(io, only: nil, resolution: Ffmprb::QVGA, fps: 30)
|
8
|
-
@io = io
|
8
|
+
@io = resolve(io)
|
9
9
|
@channels = [*only]
|
10
10
|
@channels = nil if @channels.empty?
|
11
11
|
@resolution = resolution
|
12
12
|
@fps = 30
|
13
13
|
end
|
14
14
|
|
15
|
+
# XXX This method is exceptionally long at the moment. This is not too grand.
|
16
|
+
# However, structuring the code should be undertaken with care, as not to harm the composition clarity.
|
15
17
|
def options(process)
|
16
18
|
# XXX TODO manage stream labels through process
|
17
19
|
raise Error, "Nothing to roll..." if @reels.select(&:reel).empty?
|
@@ -21,7 +23,6 @@ module Ffmprb
|
|
21
23
|
|
22
24
|
# Concatting
|
23
25
|
segments = []
|
24
|
-
Ffmprb.logger.debug "Concatting segments: start"
|
25
26
|
|
26
27
|
@reels.each_with_index do |curr_reel, i|
|
27
28
|
|
@@ -55,7 +56,6 @@ module Ffmprb
|
|
55
56
|
# NOTE make sure previous reel rolls _long_ enough AND then _just_ enough
|
56
57
|
|
57
58
|
prev_lbl = segments.pop
|
58
|
-
Ffmprb.logger.debug "Concatting segments: #{prev_lbl} popped"
|
59
59
|
|
60
60
|
lbl_pad = "bl#{prev_lbl}#{i}"
|
61
61
|
# NOTE generously padding the previous segment to support for all the cases
|
@@ -141,7 +141,6 @@ module Ffmprb
|
|
141
141
|
end
|
142
142
|
|
143
143
|
segments << lbl # NOTE can be nil
|
144
|
-
Ffmprb.logger.debug "Concatting segments: #{lbl} pushed"
|
145
144
|
end
|
146
145
|
|
147
146
|
segments.compact!
|
@@ -174,7 +173,7 @@ module Ffmprb
|
|
174
173
|
filters +=
|
175
174
|
Filter.copy("#{lbl_out}:v", "#{lbl_nxt}:v") if channel?(:video)
|
176
175
|
filters +=
|
177
|
-
Filter.
|
176
|
+
Filter.amix_to_first(["#{lbl_out}:a", "#{lbl_over}:a"], "#{lbl_nxt}:a") if channel?(:audio)
|
178
177
|
|
179
178
|
lbl_out = lbl_nxt
|
180
179
|
end
|
@@ -281,15 +280,28 @@ module Ffmprb
|
|
281
280
|
add_reel reel, after, transition, (onto == :full_screen)
|
282
281
|
end
|
283
282
|
|
284
|
-
# XXX? protected
|
285
|
-
|
286
283
|
def channel?(medium, force=false)
|
287
|
-
return
|
284
|
+
return !!@channels && @channels.include?(medium) && @io.channel?(medium) if force
|
288
285
|
|
289
|
-
(!@channels || @channels.include?(medium)) &&
|
286
|
+
(!@channels || @channels.include?(medium)) && @io.channel?(medium) &&
|
290
287
|
reels_channel?(medium)
|
291
288
|
end
|
292
289
|
|
290
|
+
protected
|
291
|
+
|
292
|
+
def resolve(io)
|
293
|
+
return io unless io.is_a? String
|
294
|
+
|
295
|
+
case io
|
296
|
+
when /^\/\w/
|
297
|
+
File.create(io).tap do |file|
|
298
|
+
Ffmprb.logger.warn "Output file exists (#{file.path}), will overwrite" if file.exist?
|
299
|
+
end
|
300
|
+
else
|
301
|
+
raise Error, "Cannot resolve output: #{io}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
293
305
|
private
|
294
306
|
|
295
307
|
def reels_channel?(medium)
|
@@ -70,6 +70,8 @@ module Ffmprb
|
|
70
70
|
while s = @q.deq
|
71
71
|
next if broken
|
72
72
|
written = 0
|
73
|
+
tries = 1
|
74
|
+
logged_tries = 1/2
|
73
75
|
while !broken
|
74
76
|
raise @terminate if @terminate.kind_of?(Exception)
|
75
77
|
begin
|
@@ -77,15 +79,17 @@ module Ffmprb
|
|
77
79
|
written = output.write_nonblock(s) if output # NOTE will only be nil if @terminate is an exception
|
78
80
|
break if written == s.length # NOTE kinda optimisation
|
79
81
|
s = s[written..-1]
|
80
|
-
rescue Errno::EAGAIN
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
83
|
+
if tries == 2 * logged_tries
|
84
|
+
Ffmprb.logger.debug "IoBuffer writer (to #{output.path}) retrying... (#{tries} writes): #{$!.class}"
|
85
|
+
logged_tries = tries
|
86
|
+
end
|
85
87
|
sleep 0.01
|
86
88
|
rescue Errno::EPIPE
|
87
89
|
broken = true
|
88
90
|
Ffmprb.logger.debug "IoBuffer writer (to #{output.path}) broken"
|
91
|
+
ensure
|
92
|
+
tries += 1
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
@@ -187,14 +191,21 @@ module Ffmprb
|
|
187
191
|
|
188
192
|
@output_thr = Util::Thread.new("buffer writer output helper") do
|
189
193
|
Ffmprb.logger.debug "Opening buffer output"
|
194
|
+
tries = 1
|
195
|
+
logged_tries = 1/2
|
190
196
|
begin
|
191
197
|
Timeout::timeout(self.class.timeout/2) do
|
192
198
|
@output = @output.call
|
193
199
|
Ffmprb.logger.debug "Opened buffer output: #{@output.path}"
|
194
200
|
end
|
195
201
|
rescue Timeout::Error
|
196
|
-
|
202
|
+
if tries == 2 * logged_tries
|
203
|
+
Ffmprb.logger.info "A little bit of timeout in the buffer writer helper thread (##{tries})"
|
204
|
+
logged_tries = tries
|
205
|
+
end
|
197
206
|
retry unless @terminate.kind_of?(Exception)
|
207
|
+
ensure
|
208
|
+
tries += 1
|
198
209
|
end
|
199
210
|
end
|
200
211
|
end
|
data/lib/ffmprb/util.rb
CHANGED
@@ -13,12 +13,12 @@ module Ffmprb
|
|
13
13
|
attr_accessor :ffmpeg_cmd, :ffprobe_cmd
|
14
14
|
|
15
15
|
def ffprobe(*args)
|
16
|
-
sh ffprobe_cmd, *args
|
16
|
+
sh *ffprobe_cmd, *args
|
17
17
|
end
|
18
18
|
|
19
19
|
def ffmpeg(*args)
|
20
20
|
args = ['-loglevel', 'debug'] + args if Ffmprb.debug
|
21
|
-
sh ffmpeg_cmd, '-y', *args, output: :stderr
|
21
|
+
sh *ffmpeg_cmd, '-y', *args, output: :stderr
|
22
22
|
end
|
23
23
|
|
24
24
|
def sh(*cmd, output: :stdout, log: :stderr)
|
data/lib/ffmprb/version.rb
CHANGED
data/lib/ffmprb.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
require 'ffmprb/execution'
|
1
2
|
require 'ffmprb/file'
|
2
3
|
require 'ffmprb/filter'
|
4
|
+
require 'ffmprb/find_silence'
|
3
5
|
require 'ffmprb/process'
|
4
6
|
require 'ffmprb/util'
|
5
7
|
require 'ffmprb/version'
|
6
8
|
|
7
9
|
require 'logger'
|
8
|
-
require 'time'
|
9
|
-
require 'timeout'
|
10
10
|
|
11
11
|
module Ffmprb
|
12
12
|
|
@@ -19,8 +19,8 @@ module Ffmprb
|
|
19
19
|
class Error < StandardError
|
20
20
|
end
|
21
21
|
|
22
|
-
Util.ffmpeg_cmd = 'ffmpeg'
|
23
|
-
Util.ffprobe_cmd = 'ffprobe'
|
22
|
+
Util.ffmpeg_cmd = ['ffmpeg']
|
23
|
+
Util.ffprobe_cmd = ['ffprobe']
|
24
24
|
|
25
25
|
Process.duck_audio_hi = 0.9
|
26
26
|
Process.duck_audio_lo = 0.1
|
@@ -34,14 +34,18 @@ module Ffmprb
|
|
34
34
|
|
35
35
|
class << self
|
36
36
|
|
37
|
+
# NOTE the form with the block returns the result of #run
|
38
|
+
# NOTE the form without the block returns the process (before it is run)
|
37
39
|
def process(*args, &blk)
|
38
40
|
logger.debug "Starting process with #{args} in #{blk.source_location}"
|
39
|
-
Process.new
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
process = Process.new
|
42
|
+
return process unless blk
|
43
|
+
|
44
|
+
process.instance_exec *args, &blk
|
45
|
+
logger.debug "Initialized process with #{args} in #{blk.source_location}"
|
46
|
+
|
47
|
+
process.run.tap do
|
48
|
+
logger.debug "Finished process with #{args} in #{blk.source_location}"
|
45
49
|
end
|
46
50
|
end
|
47
51
|
alias :action! :process # ;)
|
@@ -59,32 +63,6 @@ module Ffmprb
|
|
59
63
|
@logger = logger
|
60
64
|
end
|
61
65
|
|
62
|
-
def find_silence(input_file, output_file)
|
63
|
-
logger.debug "Finding silence (#{input_file.path}->#{output_file.path})"
|
64
|
-
filters = Filter.silencedetect
|
65
|
-
options = ['-i', input_file.path, *Filter.complex_options(filters), output_file.path]
|
66
|
-
silence = []
|
67
|
-
Util.ffmpeg(*options).split("\n").each do |line|
|
68
|
-
next unless line =~ /^\[silencedetect\s.*\]\s*silence_(\w+):\s*(\d+\.?d*)/
|
69
|
-
case $1
|
70
|
-
when 'start'
|
71
|
-
silence << OpenStruct.new(start_at: $2.to_f)
|
72
|
-
when 'end'
|
73
|
-
if silence.empty?
|
74
|
-
silence << OpenStruct.new(start_at: 0.0, end_at: $2.to_f)
|
75
|
-
else
|
76
|
-
raise Error, "ffmpeg is being stupid: silence_end with no silence_start" if silence.last.end_at
|
77
|
-
silence.last.end_at = $2.to_f
|
78
|
-
end
|
79
|
-
else
|
80
|
-
Ffmprb.warn "Unknown silence mark: #{$1}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
logger.debug "Found silence (#{input_file.path}->#{output_file.path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
|
85
|
-
silence
|
86
|
-
end
|
87
|
-
|
88
66
|
end
|
89
67
|
|
90
68
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffmprb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- showbox.com
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mkfifo
|
@@ -126,7 +126,8 @@ dependencies:
|
|
126
126
|
description: A DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends
|
127
127
|
email:
|
128
128
|
- costa@showbox.com
|
129
|
-
executables:
|
129
|
+
executables:
|
130
|
+
- ffmprb
|
130
131
|
extensions: []
|
131
132
|
extra_rdoc_files: []
|
132
133
|
files:
|
@@ -143,10 +144,14 @@ files:
|
|
143
144
|
- bin/rake
|
144
145
|
- bin/rspec
|
145
146
|
- bin/setup
|
147
|
+
- circle.yml
|
148
|
+
- exe/ffmprb
|
146
149
|
- ffmprb.gemspec
|
147
150
|
- lib/ffmprb.rb
|
151
|
+
- lib/ffmprb/execution.rb
|
148
152
|
- lib/ffmprb/file.rb
|
149
153
|
- lib/ffmprb/filter.rb
|
154
|
+
- lib/ffmprb/find_silence.rb
|
150
155
|
- lib/ffmprb/process.rb
|
151
156
|
- lib/ffmprb/process/input.rb
|
152
157
|
- lib/ffmprb/process/output.rb
|