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.
@@ -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