ffmprb 0.7.5 → 0.9.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/Guardfile +2 -2
- data/README.md +121 -15
- data/ffmprb.gemspec +2 -1
- data/lib/defaults.rb +5 -1
- data/lib/ffmprb/file.rb +2 -4
- data/lib/ffmprb/filter.rb +97 -65
- data/lib/ffmprb/process/input/chain_base.rb +29 -0
- data/lib/ffmprb/process/input/channeled.rb +40 -0
- data/lib/ffmprb/process/input/cropped.rb +70 -0
- data/lib/ffmprb/process/input/cut.rb +66 -0
- data/lib/ffmprb/process/input/looping.rb +102 -0
- data/lib/ffmprb/process/input/loud.rb +42 -0
- data/lib/ffmprb/process/input/temp.rb +26 -0
- data/lib/ffmprb/process/input.rb +39 -180
- data/lib/ffmprb/process/output.rb +140 -119
- data/lib/ffmprb/process.rb +68 -27
- data/lib/ffmprb/util/synchro.rb +1 -1
- data/lib/ffmprb/util/thread.rb +2 -2
- data/lib/ffmprb/util/threaded_io_buffer.rb +48 -36
- data/lib/ffmprb/util.rb +12 -5
- data/lib/ffmprb/version.rb +1 -1
- data/lib/ffmprb.rb +6 -6
- metadata +9 -2
@@ -2,8 +2,8 @@ module Ffmprb
|
|
2
2
|
|
3
3
|
module Util
|
4
4
|
|
5
|
-
#
|
6
|
-
# XXX partially specc'ed in file_spec
|
5
|
+
# TODO the events mechanism is currently unused (and commented out) => synchro mechanism not needed
|
6
|
+
# XXX *partially* specc'ed in file_spec
|
7
7
|
class ThreadedIoBuffer
|
8
8
|
# include Synchro
|
9
9
|
|
@@ -22,19 +22,23 @@ module Ffmprb
|
|
22
22
|
# NOTE input/output can be lambdas for single asynchronic io evaluation
|
23
23
|
# the labdas must be timeout-interrupt-safe (since they are wrapped in timeout blocks)
|
24
24
|
# NOTE both ios are being opened and closed as soon as possible
|
25
|
-
def initialize(input,
|
25
|
+
def initialize(input, *outputs) # XXX SPEC ME!!! multiple outputs!!
|
26
26
|
|
27
27
|
@input = input
|
28
|
-
@
|
29
|
-
|
28
|
+
@outputs = outputs.inject({}) do |hash, out|
|
29
|
+
hash[out] = SizedQueue.new(self.class.blocks_max)
|
30
|
+
hash
|
31
|
+
end
|
30
32
|
@stat_blocks_max = 0
|
31
33
|
@terminate = false
|
32
34
|
# @events = {}
|
33
35
|
|
34
36
|
Thread.new "io buffer main" do
|
35
37
|
init_reader!
|
36
|
-
|
37
|
-
|
38
|
+
outputs.each do |output|
|
39
|
+
init_writer_output! output
|
40
|
+
init_writer! output
|
41
|
+
end
|
38
42
|
|
39
43
|
Thread.join_children!
|
40
44
|
end
|
@@ -81,7 +85,7 @@ module Ffmprb
|
|
81
85
|
# handle_synchronously :fire!
|
82
86
|
#
|
83
87
|
def blocks_count
|
84
|
-
@
|
88
|
+
@outputs.values.map(&:size).max
|
85
89
|
end
|
86
90
|
|
87
91
|
private
|
@@ -95,26 +99,12 @@ module Ffmprb
|
|
95
99
|
@input
|
96
100
|
end
|
97
101
|
|
98
|
-
def writer_output! # NOTE just for writer thread
|
99
|
-
if @
|
100
|
-
@
|
101
|
-
@
|
102
|
-
end
|
103
|
-
@output unless @output.respond_to?(:call)
|
104
|
-
end
|
105
|
-
|
106
|
-
def init_writer_output!
|
107
|
-
return unless @output.respond_to?(:call)
|
108
|
-
|
109
|
-
@output_thr = Thread.new("buffer writer output helper") do
|
110
|
-
Ffmprb.logger.debug "Opening buffer output"
|
111
|
-
@output =
|
112
|
-
Thread.timeout_or_live nil, log: "in the buffer writer helper thread", timeout: self.class.timeout do |time|
|
113
|
-
fail Error, "giving up buffer writer init since the reader has failed (#{@terminate.message})" if @terminate.kind_of?(Exception)
|
114
|
-
@output.call
|
115
|
-
end
|
116
|
-
Ffmprb.logger.debug "Opened buffer output: #{@output.path}"
|
102
|
+
def writer_output!(output) # NOTE just for writer thread
|
103
|
+
if @output_thrs[output]
|
104
|
+
@output_thrs[output].join
|
105
|
+
@output_thrs[output] = nil
|
117
106
|
end
|
107
|
+
@output_ios[output]
|
118
108
|
end
|
119
109
|
|
120
110
|
# NOTE reads all of input, then closes the stream times out on buffer overflow
|
@@ -124,7 +114,7 @@ module Ffmprb
|
|
124
114
|
while s = reader_input!.read(self.class.block_size)
|
125
115
|
begin
|
126
116
|
Timeout.timeout(self.class.timeout) do
|
127
|
-
|
117
|
+
output_enq s
|
128
118
|
end
|
129
119
|
rescue Timeout::Error # NOTE the queue is probably overflown
|
130
120
|
@terminate = Error.new("The reader has failed with timeout while queuing")
|
@@ -134,7 +124,7 @@ module Ffmprb
|
|
134
124
|
@stat_blocks_max = blocks_count if blocks_count > @stat_blocks_max
|
135
125
|
end
|
136
126
|
@terminate = true
|
137
|
-
|
127
|
+
output_enq nil
|
138
128
|
ensure
|
139
129
|
begin
|
140
130
|
reader_input!.close if reader_input!.respond_to?(:close)
|
@@ -147,12 +137,28 @@ module Ffmprb
|
|
147
137
|
end
|
148
138
|
end
|
149
139
|
|
140
|
+
def init_writer_output!(output)
|
141
|
+
@output_ios ||= {}
|
142
|
+
return @output_ios[output] = output unless output.respond_to?(:call)
|
143
|
+
|
144
|
+
@output_thrs ||= {}
|
145
|
+
@output_thrs[output] = Thread.new("buffer writer output helper") do
|
146
|
+
Ffmprb.logger.debug "Opening buffer output"
|
147
|
+
@output_ios[output] =
|
148
|
+
Thread.timeout_or_live nil, log: "in the buffer writer helper thread", timeout: self.class.timeout do |time|
|
149
|
+
fail Error, "giving up buffer writer init since the reader has failed (#{@terminate.message})" if @terminate.kind_of?(Exception)
|
150
|
+
output.call
|
151
|
+
end
|
152
|
+
Ffmprb.logger.debug "Opened buffer output: #{@output_ios[output].path}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
150
156
|
# NOTE writes as much output as possible, then terminates when the reader dies
|
151
|
-
def init_writer!
|
157
|
+
def init_writer!(output)
|
152
158
|
Thread.new("buffer writer") do
|
153
159
|
broken = false
|
154
160
|
begin
|
155
|
-
while s = @
|
161
|
+
while s = @outputs[output].deq
|
156
162
|
next if broken
|
157
163
|
written = 0
|
158
164
|
tries = 1
|
@@ -160,19 +166,19 @@ module Ffmprb
|
|
160
166
|
while !broken
|
161
167
|
fail @terminate if @terminate.kind_of?(Exception)
|
162
168
|
begin
|
163
|
-
|
164
|
-
written =
|
169
|
+
output_io = writer_output!(output)
|
170
|
+
written = output_io.write_nonblock(s) if output # NOTE will only be nil if @terminate is an exception
|
165
171
|
break if written == s.length # NOTE kinda optimisation
|
166
172
|
s = s[written..-1]
|
167
173
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
168
174
|
if tries == 2 * logged_tries
|
169
|
-
Ffmprb.logger.debug "ThreadedIoBuffer writer (to #{
|
175
|
+
Ffmprb.logger.debug "ThreadedIoBuffer writer (to #{output_io.path}) retrying... (#{tries} writes): #{$!.class}"
|
170
176
|
logged_tries = tries
|
171
177
|
end
|
172
178
|
sleep 0.01
|
173
179
|
rescue Errno::EPIPE
|
174
180
|
broken = true
|
175
|
-
Ffmprb.logger.debug "ThreadedIoBuffer writer (to #{
|
181
|
+
Ffmprb.logger.debug "ThreadedIoBuffer writer (to #{output_io.path}) broken"
|
176
182
|
ensure
|
177
183
|
tries += 1
|
178
184
|
end
|
@@ -181,7 +187,7 @@ module Ffmprb
|
|
181
187
|
ensure
|
182
188
|
# terminated!
|
183
189
|
begin
|
184
|
-
writer_output
|
190
|
+
writer_output!(output).close if !broken && writer_output!(output).respond_to?(:close)
|
185
191
|
rescue
|
186
192
|
Ffmprb.logger.error "ThreadedIoBuffer output closing error: #{$!.message}"
|
187
193
|
end
|
@@ -195,6 +201,12 @@ module Ffmprb
|
|
195
201
|
# @handler_thr = nil
|
196
202
|
# end
|
197
203
|
|
204
|
+
def output_enq(item)
|
205
|
+
@outputs.values.each do |q|
|
206
|
+
q.enq item
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
198
210
|
end
|
199
211
|
|
200
212
|
end
|
data/lib/ffmprb/util.rb
CHANGED
@@ -19,12 +19,12 @@ module Ffmprb
|
|
19
19
|
sh *ffprobe_cmd, *args, limit: limit, timeout: timeout
|
20
20
|
end
|
21
21
|
|
22
|
-
def ffmpeg(*args, limit: nil, timeout: cmd_timeout)
|
22
|
+
def ffmpeg(*args, limit: nil, timeout: cmd_timeout, ignore_broken_pipe: false)
|
23
23
|
args = ['-loglevel', 'debug'] + args if Ffmprb.debug
|
24
|
-
sh *ffmpeg_cmd,
|
24
|
+
sh *ffmpeg_cmd, *args, output: :stderr, limit: limit, timeout: timeout, ignore_broken_pipe: ignore_broken_pipe
|
25
25
|
end
|
26
26
|
|
27
|
-
def sh(*cmd, output: :stdout, log: :stderr, limit: nil, timeout: cmd_timeout)
|
27
|
+
def sh(*cmd, output: :stdout, log: :stderr, limit: nil, timeout: cmd_timeout, ignore_broken_pipe: false)
|
28
28
|
cmd = cmd.map &:to_s unless cmd.size == 1
|
29
29
|
cmd_str = cmd.size != 1 ? cmd.map{|c| "\"#{c}\""}.join(' ') : cmd.first
|
30
30
|
timeout = [timeout, limit].compact.min
|
@@ -39,8 +39,15 @@ module Ffmprb
|
|
39
39
|
stderr_r = Reader.new(stderr, true, log == :stderr && log_cmd)
|
40
40
|
|
41
41
|
Thread.timeout_or_live(limit, log: "while waiting for `#{cmd_str}`", timeout: timeout) do |time|
|
42
|
-
|
43
|
-
|
42
|
+
value = wait_thr.value
|
43
|
+
status = value.exitstatus # NOTE blocking
|
44
|
+
if status != 0
|
45
|
+
if ignore_broken_pipe && value.signaled? && value.termsig == Signal.list['PIPE']
|
46
|
+
Ffmprb.logger.debug "Ignoring broken pipe: #{cmd_str}"
|
47
|
+
else
|
48
|
+
fail Error, "#{cmd_str} (#{status || "sig##{value.termsig}"}):\n#{stderr_r.read}"
|
49
|
+
end
|
50
|
+
end
|
44
51
|
end
|
45
52
|
Ffmprb.logger.debug "FINISHED: #{cmd_str}"
|
46
53
|
|
data/lib/ffmprb/version.rb
CHANGED
data/lib/ffmprb.rb
CHANGED
@@ -15,13 +15,13 @@ module Ffmprb
|
|
15
15
|
|
16
16
|
class << self
|
17
17
|
|
18
|
-
#
|
19
|
-
# NOTE the form without the block returns the process (before it is run) - advanced use
|
20
|
-
# XXX is this clear enough? Do we really need the second form?
|
18
|
+
# TODO limit:
|
21
19
|
def process(*args, &blk)
|
22
|
-
|
20
|
+
fail Error, "process: nothing ;( gimme a block!" unless blk
|
21
|
+
|
23
22
|
process = Process.new
|
24
|
-
|
23
|
+
|
24
|
+
logger.debug "Starting process with #{args} in #{blk.source_location}"
|
25
25
|
|
26
26
|
process.instance_exec *args, &blk
|
27
27
|
logger.debug "Initialized process with #{args} in #{blk.source_location}"
|
@@ -51,6 +51,6 @@ end
|
|
51
51
|
|
52
52
|
Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
|
53
53
|
|
54
|
-
Dir["#{__FILE__.slice /(.*).rb$/, 1}/**/*.rb"].each{|f| require f}
|
54
|
+
Dir["#{__FILE__.slice /(.*).rb$/, 1}/**/*.rb"].each{|f| require f}
|
55
55
|
|
56
56
|
require 'defaults'
|
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.9.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-10-
|
12
|
+
date: 2015-10-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mkfifo
|
@@ -156,6 +156,13 @@ files:
|
|
156
156
|
- lib/ffmprb/find_silence.rb
|
157
157
|
- lib/ffmprb/process.rb
|
158
158
|
- lib/ffmprb/process/input.rb
|
159
|
+
- lib/ffmprb/process/input/chain_base.rb
|
160
|
+
- lib/ffmprb/process/input/channeled.rb
|
161
|
+
- lib/ffmprb/process/input/cropped.rb
|
162
|
+
- lib/ffmprb/process/input/cut.rb
|
163
|
+
- lib/ffmprb/process/input/looping.rb
|
164
|
+
- lib/ffmprb/process/input/loud.rb
|
165
|
+
- lib/ffmprb/process/input/temp.rb
|
159
166
|
- lib/ffmprb/process/output.rb
|
160
167
|
- lib/ffmprb/util.rb
|
161
168
|
- lib/ffmprb/util/synchro.rb
|