ffmprb 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|