ffmprb 0.7.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,8 @@ module Ffmprb
2
2
 
3
3
  module Util
4
4
 
5
- # XXX the events mechanism is currently unused (and commented out) => synchro mechanism not needed
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, output)
25
+ def initialize(input, *outputs) # XXX SPEC ME!!! multiple outputs!!
26
26
 
27
27
  @input = input
28
- @output = output
29
- @q = SizedQueue.new(self.class.blocks_max)
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
- init_writer_output!
37
- init_writer!
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
- @q.size
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 @output_thr
100
- @output_thr.join
101
- @output_thr = nil
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
- @q.enq s
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
- @q.enq nil
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 = @q.deq
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
- output = writer_output!
164
- written = output.write_nonblock(s) if output # NOTE will only be nil if @terminate is an exception
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 #{output.path}) retrying... (#{tries} writes): #{$!.class}"
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 #{output.path}) broken"
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!.close if !broken && writer_output!.respond_to?(:close)
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, '-y', *args, output: :stderr, limit: limit, timeout: timeout
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
- fail Error, "#{cmd_str}:\n#{stderr_r.read}" unless
43
- wait_thr.value.exitstatus == 0 # NOTE blocking
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
 
@@ -1,3 +1,3 @@
1
1
  module Ffmprb
2
- VERSION = '0.7.5'
2
+ VERSION = '0.9.0'
3
3
  end
data/lib/ffmprb.rb CHANGED
@@ -15,13 +15,13 @@ module Ffmprb
15
15
 
16
16
  class << self
17
17
 
18
- # NOTE the form with the block returns the result of #run
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
- logger.debug "Starting process with #{args} in #{blk.source_location}"
20
+ fail Error, "process: nothing ;( gimme a block!" unless blk
21
+
23
22
  process = Process.new
24
- return process unless blk
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} # XXX require_sub __FILE__ # or something
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.7.5
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-18 00:00:00.000000000 Z
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