ffmprb 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,7 +18,7 @@ module Ffmprb
18
18
  fail Error, "Can sample either video OR audio UNLESS a block is given" unless block_given? || !!audio != !!video
19
19
 
20
20
  cmd = %W[-i #{path}]
21
- cmd.concat %W[-deinterlace -an -ss #{at} -r 1 -vcodec mjpeg -f mjpeg #{video.path}] if video
21
+ cmd.concat %W[-deinterlace -an -ss #{at} -vframes 1 #{video.path}] if video
22
22
  cmd.concat %W[-vn -ss #{at} -t 1 #{audio.path}] if audio
23
23
  Util.ffmpeg *cmd
24
24
 
@@ -28,11 +28,11 @@ module Ffmprb
28
28
  yield *[video || nil, audio || nil].compact
29
29
  ensure
30
30
  begin
31
- video.remove if video
32
- audio.remove if audio
31
+ video.unlink if video
32
+ audio.unlink if audio
33
33
  Ffmprb.logger.debug "Removed sample files"
34
34
  rescue
35
- Ffmprb.logger.warn "Error removing sample files: #{$!.message}"
35
+ Ffmprb.logger.warn "#{$!.class.name} removing sample files: #{$!.message}"
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,48 @@
1
+ module Ffmprb
2
+
3
+ class File # NOTE I would rather rename it to Stream at the moment
4
+
5
+ class << self
6
+
7
+ def threaded_buffered_fifo(extname='.tmp', reader_open_on_writer_idle_limit: nil, proc_vis: nil)
8
+ input_fifo_file = temp_fifo(extname)
9
+ output_fifo_file = temp_fifo(extname)
10
+ Ffmprb.logger.debug "Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"
11
+ Util::Thread.new do
12
+ begin
13
+ io_buff = Util::ThreadedIoBuffer.new(opener(input_fifo_file, 'r'), opener(output_fifo_file, 'w'), keep_outputs_open_on_input_idle_limit: reader_open_on_writer_idle_limit)
14
+ if proc_vis
15
+ proc_vis.proc_vis_edge input_fifo_file, io_buff
16
+ proc_vis.proc_vis_edge io_buff, output_fifo_file
17
+ end
18
+ begin
19
+ # yield input_fifo_file, output_fifo_file, io_buff if block_given?
20
+ ensure
21
+ Util::Thread.join_children!
22
+ end
23
+ Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"
24
+ ensure
25
+ input_fifo_file.unlink if input_fifo_file
26
+ output_fifo_file.unlink if output_fifo_file
27
+ end
28
+ end
29
+ Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"
30
+
31
+ [input_fifo_file, output_fifo_file]
32
+ end
33
+
34
+ end
35
+
36
+ def threaded_buffered_copy_to(*dsts)
37
+ Util::ThreadedIoBuffer.new(
38
+ self.class.opener(self, 'r'),
39
+ *dsts.map{|io| self.class.opener io, 'w'}
40
+ ).tap do |io_buff|
41
+ proc_vis_edge self, io_buff
42
+ dsts.each{ |dst| proc_vis_edge io_buff, dst }
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
data/lib/ffmprb/filter.rb CHANGED
@@ -72,18 +72,18 @@ module Ffmprb
72
72
  end
73
73
 
74
74
  def concat_v(inputs, output=nil)
75
- inout "concat=%{inputs_count}:v=1:a=0", inputs, output,
76
- inputs_count: (inputs.empty?? nil : inputs.size)
75
+ return copy(inputs, output) if inputs.size == 1
76
+ inout "concat=#{inputs.size}:v=1:a=0", inputs, output
77
77
  end
78
78
 
79
79
  def concat_a(inputs, output=nil)
80
- inout "concat=%{inputs_count}:v=0:a=1", inputs, output,
81
- inputs_count: (inputs.empty?? nil : inputs.size)
80
+ return anull(inputs, output) if inputs.size == 1
81
+ inout "concat=#{inputs.size}:v=0:a=1", inputs, output
82
82
  end
83
83
 
84
84
  def concat_av(inputs, output=nil)
85
- inout "concat=%{inputs_count}:v=1:a=1", inputs, output,
86
- inputs_count: (inputs.empty? || inputs.size % 2 != 0 ? nil : inputs.size/2) # XXX meh
85
+ fail Error, "must be given an even number of inputs" unless inputs.size.even?
86
+ inout "concat=#{inputs.size/2}:v=1:a=1", inputs, output
87
87
  end
88
88
 
89
89
  def copy(input=nil, output=nil)
@@ -233,6 +233,7 @@ module Ffmprb
233
233
  volume_exp: volume_exp(volume)
234
234
  end
235
235
 
236
+ # NOTE supposedly volume list is sorted
236
237
  def volume_exp(volume)
237
238
  return volume unless volume.is_a?(Hash)
238
239
 
@@ -242,6 +243,7 @@ module Ffmprb
242
243
  prev_vol = volume[prev_at] || 1.0
243
244
  exp = "#{volume[volume.keys.last]}"
244
245
  volume.each do |at, vol|
246
+ next if at == 0.0
245
247
  vol_exp =
246
248
  if (vol - prev_vol).abs < 0.001
247
249
  vol
@@ -259,7 +261,7 @@ module Ffmprb
259
261
  color_source '0xFFFFFF@1', duration, resolution, fps, output
260
262
  end
261
263
 
262
- def complex_options(*filters)
264
+ def complex_args(*filters)
263
265
  ['-filter_complex', filters.join('; ')] unless filters.empty?
264
266
  end
265
267
 
@@ -2,10 +2,12 @@ module Ffmprb
2
2
 
3
3
  class << self
4
4
 
5
+ # NOTE not for streaming just yet
5
6
  def find_silence(input_file, output_file)
6
- logger.debug "Finding silence (#{input_file.path}->#{output_file.path})"
7
+ path = "#{input_file.path}->#{output_file.path}"
8
+ logger.debug "Finding silence (#{path})"
7
9
  silence = []
8
- Util.ffmpeg('-i', input_file.path, *find_silence_detect_options, output_file.path).
10
+ Util.ffmpeg('-i', input_file.path, *find_silence_detect_args, output_file.path).
9
11
  scan(SILENCE_DETECT_REGEX).each do |mark, time|
10
12
  time = time.to_f
11
13
 
@@ -23,7 +25,7 @@ module Ffmprb
23
25
  Ffmprb.warn "Unknown silence mark: #{mark}"
24
26
  end
25
27
  end
26
- logger.debug "Found silence (#{input_file.path}->#{output_file.path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
28
+ logger.debug "Found silence (#{path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
27
29
  silence
28
30
  end
29
31
 
@@ -31,8 +33,8 @@ module Ffmprb
31
33
 
32
34
  SILENCE_DETECT_REGEX = /\[silencedetect\s.*\]\s*silence_(\w+):\s*(\d+\.?\d*)/
33
35
 
34
- def find_silence_detect_options
35
- Filter.complex_options Filter.silencedetect
36
+ def find_silence_detect_args
37
+ Filter.complex_args Filter.silencedetect
36
38
  end
37
39
 
38
40
  end
@@ -1,6 +1,7 @@
1
1
  module Ffmprb
2
2
 
3
3
  class Process
4
+ include Util::ProcVis::Node
4
5
 
5
6
  class << self
6
7
 
@@ -9,7 +10,8 @@ module Ffmprb
9
10
  attr_accessor :duck_audio_transition_length,
10
11
  :duck_audio_transition_in_start, :duck_audio_transition_out_start
11
12
 
12
- attr_accessor :input_options
13
+ attr_accessor :input_video_auto_rotate
14
+ attr_accessor :input_video_fps
13
15
 
14
16
  attr_accessor :output_video_resolution
15
17
  attr_accessor :output_video_fps
@@ -17,22 +19,36 @@ module Ffmprb
17
19
 
18
20
  attr_accessor :timeout
19
21
 
20
- def intermediate_channel_extname(*media)
21
- if media == [:video]
22
- '.y4m'
23
- elsif media == [:audio]
24
- '.wav'
25
- elsif media.sort == [:audio, :video]
26
- '.flv'
22
+ def intermediate_channel_extname(video:, audio:)
23
+ if video
24
+ if audio
25
+ '.flv'
26
+ else
27
+ '.y4m'
28
+ end
27
29
  else
28
- fail Error, "I don't know how to channel [#{media.join ', '}]"
30
+ if audio
31
+ '.wav'
32
+ else
33
+ fail Error, "I don't know how to channel [#{media.join ', '}]"
34
+ end
29
35
  end
30
36
  end
31
37
 
38
+ def input_video_options
39
+ {
40
+ auto_rotate: input_video_auto_rotate,
41
+ fps: input_video_fps
42
+ }
43
+ end
44
+ def input_audio_options
45
+ {
46
+ }
47
+ end
32
48
  def output_video_options
33
49
  {
34
- resolution: output_video_resolution,
35
- fps: output_video_fps
50
+ fps: output_video_fps,
51
+ resolution: output_video_resolution
36
52
  }
37
53
  end
38
54
  def output_audio_options
@@ -88,25 +104,28 @@ module Ffmprb
88
104
  end
89
105
 
90
106
  attr_accessor :timeout
91
- attr_accessor :ignore_broken_pipe
107
+ attr_accessor :name
108
+ attr_reader :parent
109
+ attr_accessor :ignore_broken_pipes
92
110
 
93
111
  def initialize(*args, **opts)
112
+ self.timeout = opts.delete(:timeout) || Process.timeout
113
+ @name = opts.delete(:name)
114
+ @parent = opts.delete(:parent)
115
+ parent.proc_vis_node self if parent
116
+ self.ignore_broken_pipes = opts.delete(:ignore_broken_pipes)
117
+ fail Error, "Unknown options: #{opts}" unless opts.empty? # XXX refactor into a separate error
94
118
  @inputs, @outputs = [], []
95
- self.timeout = opts.delete(:timeout) || self.class.timeout
96
-
97
- self.ignore_broken_pipe = opts.delete(:ignore_broken_pipe)
98
- fail Error, "Unknown options: #{opts}" unless opts.empty?
99
119
  end
100
120
 
101
- def input(io, **opts)
102
- Input.new(io, self, **self.class.input_options.merge(opts)).tap do |inp|
121
+ def input(io, video: true, audio: true)
122
+ Input.new(io, self,
123
+ video: channel_params(video, Process.input_video_options),
124
+ audio: channel_params(audio, Process.input_audio_options)
125
+ ).tap do |inp|
126
+ fail Error, "Too many inputs to the process, try breaking it down somehow" if @inputs.size > Util.ffmpeg_inputs_max
103
127
  @inputs << inp
104
- end
105
- end
106
-
107
- def temp_input(extname) # XXX SPEC ME
108
- input(nil).tap do |inp|
109
- inp.temporise! extname
128
+ proc_vis_edge inp.io, self
110
129
  end
111
130
  end
112
131
 
@@ -116,11 +135,12 @@ module Ffmprb
116
135
 
117
136
  def output(io, video: true, audio: true, &blk)
118
137
  Output.new(io, self,
119
- video: channel_params(video, self.class.output_video_options),
120
- audio: channel_params(audio, self.class.output_audio_options)
121
- ).tap do |out|
122
- @outputs << out
123
- out.instance_exec &blk if blk
138
+ video: channel_params(video, Process.output_video_options),
139
+ audio: channel_params(audio, Process.output_audio_options)
140
+ ).tap do |outp|
141
+ @outputs << outp
142
+ proc_vis_edge self, outp.io
143
+ outp.instance_exec &blk if blk
124
144
  end
125
145
  end
126
146
 
@@ -132,13 +152,17 @@ module Ffmprb
132
152
  def run(limit: nil) # TODO (async: false)
133
153
  # NOTE this is both for the future async: option and according to
134
154
  # the threading policy (a parent death will be noticed and handled by children)
135
- thr = Util::Thread.new do
155
+ thr = Util::Thread.new main: !parent do
156
+ proc_vis_node Thread.current
136
157
  # NOTE yes, an exception can occur anytime, and we'll just die, it's ok, see above
137
158
  # XXX just to return something -- no apparent practical use
138
159
  cmd = command
139
- Util.ffmpeg(*cmd, limit: limit, timeout: timeout, ignore_broken_pipe: @ignore_broken_pipe).tap do |res|
160
+ opts = {limit: limit, timeout: timeout}
161
+ opts[:ignore_broken_pipes] = ignore_broken_pipes unless ignore_broken_pipes.nil?
162
+ Util.ffmpeg(*cmd, **opts).tap do |res|
140
163
  Util::Thread.join_children! limit, timeout: timeout
141
164
  end
165
+ proc_vis_node Thread.current, :remove
142
166
  end
143
167
  thr.value if thr.join limit # NOTE should not block for more than limit
144
168
  end
@@ -146,19 +170,24 @@ module Ffmprb
146
170
  private
147
171
 
148
172
  def command
149
- input_options + filter_options + output_options
173
+ input_args + filter_args + output_args
150
174
  end
151
175
 
152
- def input_options
153
- @inputs.map(&:options).flatten(1)
176
+ def input_args
177
+ filter_args # NOTE must run first
178
+ @input_args ||= @inputs.map(&:args).reduce(:+)
154
179
  end
155
180
 
156
- def filter_options
157
- Filter.complex_options @outputs.map(&:filters).reduce(:+)
181
+ # NOTE must run first
182
+ def filter_args
183
+ @filter_args ||= Filter.complex_args(
184
+ @outputs.map(&:filters).reduce(:+)
185
+ )
158
186
  end
159
187
 
160
- def output_options
161
- @outputs.map(&:options).flatten(1)
188
+ def output_args
189
+ filter_args # NOTE must run first
190
+ @output_args ||= @outputs.map(&:args).reduce(:+)
162
191
  end
163
192
 
164
193
  def channel_params(value, default)
@@ -168,7 +197,6 @@ module Ffmprb
168
197
  {}
169
198
  end
170
199
  end
171
-
172
200
  end
173
201
 
174
202
  end
@@ -7,15 +7,29 @@ module Ffmprb
7
7
  class << self
8
8
 
9
9
  def resolve(io)
10
- return io unless io.is_a? String
11
-
12
- case io
13
- when /^\/\w/
14
- File.open(io).tap do |file|
15
- Ffmprb.logger.warn "Input file does no exist (#{file.path}), will probably fail" unless file.exist?
16
- end
17
- else
18
- fail Error, "Cannot resolve input: #{io}"
10
+ return io unless io.is_a? String # XXX XXX
11
+
12
+ File.open(io).tap do |file|
13
+ Ffmprb.logger.warn "Input file does no exist (#{file.path}), will probably fail" unless file.exist?
14
+ end
15
+ end
16
+
17
+ # XXX check for unknown options
18
+
19
+ def video_args(video=nil)
20
+ video = Process.input_video_options.merge(video.to_h)
21
+ [].tap do |args|
22
+ fps = nil # NOTE ah, ruby
23
+ args.concat %W[-noautorotate] unless video.delete(:auto_rotate)
24
+ args.concat %W[-r #{fps}] if (fps = video.delete(:fps))
25
+ fail "Unknown input video options: #{video}" unless video.empty?
26
+ end
27
+ end
28
+
29
+ def audio_args(audio=nil)
30
+ audio = Process.input_audio_options.merge(audio.to_h)
31
+ [].tap do |args|
32
+ fail "Unknown input audio options: #{audio}" unless audio.empty?
19
33
  end
20
34
  end
21
35
 
@@ -24,10 +38,13 @@ module Ffmprb
24
38
  attr_accessor :io
25
39
  attr_reader :process
26
40
 
27
- def initialize(io, process, **opts)
41
+ def initialize(io, process, video:, audio:)
28
42
  @io = self.class.resolve(io)
29
43
  @process = process
30
- @opts = opts
44
+ @channels = {
45
+ video: video && @io.channel?(:video) && OpenStruct.new(video),
46
+ audio: audio && @io.channel?(:audio) && OpenStruct.new(audio)
47
+ }
31
48
  end
32
49
 
33
50
 
@@ -36,14 +53,12 @@ module Ffmprb
36
53
  end
37
54
 
38
55
 
39
- def options
40
- opts = []
41
- @opts.map do |name, value|
42
- next unless value
43
- opts << "-#{name}"
44
- opts << value unless value == true
56
+ def args
57
+ [].tap do |args|
58
+ args.concat self.class.video_args(channel :video) if channel? :video
59
+ args.concat self.class.audio_args(channel :audio) if channel? :audio
60
+ args.concat ['-i', io.path]
45
61
  end
46
- opts << '-i' << io.path
47
62
  end
48
63
 
49
64
  def filters_for(lbl, video:, audio:)
@@ -68,6 +83,10 @@ module Ffmprb
68
83
  io.channel? medium
69
84
  end
70
85
 
86
+ def channel(medium)
87
+ @channels[medium]
88
+ end
89
+
71
90
 
72
91
  def chain_copy(src_input)
73
92
  src_input
@@ -4,7 +4,8 @@ module Ffmprb
4
4
 
5
5
  class Input
6
6
 
7
- def loop(times=31)
7
+ def loop(times=Util.ffmpeg_inputs_max)
8
+ Ffmprb.logger.warn "Looping more than #{Util.ffmpeg_inputs_max} times is 'unstable': either use double looping or ask for this feature" if times > Util.ffmpeg_inputs_max
8
9
  Looping.new self, times
9
10
  end
10
11
 
@@ -14,83 +15,119 @@ module Ffmprb
14
15
 
15
16
  def initialize(unfiltered, times)
16
17
  super unfiltered
18
+
17
19
  @times = times
18
20
 
19
- @raw = unfiltered
21
+ @raw = @_unfiltered = unfiltered
22
+ # NOTE find the actual input io (not a filter)
20
23
  @raw = @raw.unfiltered while @raw.respond_to? :unfiltered
21
- @src_io = @raw.io
22
- @raw.temporise!
23
- @aux_input = @raw.process.temp_input(@src_io.extname)
24
24
  end
25
25
 
26
26
  def filters_for(lbl, video:, audio:)
27
27
 
28
+ # The plan:
29
+ # 1) Create and route an aux input which would hold the filtered, looped and parameterised stream off the raw input (keep the raw input)
30
+ # 2) Tee+buffer the original raw input io: one stream goes back into the process throw the raw input io replacement fifo; the other is fed into the filtering process
31
+ # 3) Which uses the same underlying filters to produce a filtered and parameterised stream, which is fed into the looping process through a N-Tee+buffer
32
+ # 4) Invoke the looping process which just concatenates its N inputs and produces the new raw input (the aux input)
33
+ # XXX
34
+ # -) If the consumer is broken of the:
35
+ # a. raw input - the Tee+buffer is resilient - unless the f-p-l breaks too;
36
+ # b. the f-p-l stream - the looping process fails, the N-Tee+buffer breaks, the filtering process fails, and the Tee+buffer may fail
37
+
28
38
  # Looping
39
+ # NOTE all the processing is done before looping
29
40
 
30
- loop_unfiltered(video: video, audio: audio).filters_for lbl,
31
- video: OpenStruct.new, audio: OpenStruct.new # NOTE the processing is done before looping
41
+ aux_input(video: video, audio: audio).filters_for lbl,
42
+ video: OpenStruct.new, audio: OpenStruct.new
32
43
  end
33
44
 
34
45
  protected
35
46
 
36
- def loop_unfiltered(video:, audio:)
37
- fail Error, "Double looping is not supported... yet" unless @src_io # TODO video & audio params check
38
- src_io = @src_io
39
- @src_io = nil
47
+ def aux_input(video:, audio:)
40
48
 
41
- Ffmprb.logger.debug "Validating limitations..."
49
+ # NOTE (2)
50
+ # NOTE replace the raw input io with a copy io, getting original fifo/file
51
+ intermediate_extname = Process.intermediate_channel_extname(video: @raw.io.channel?(:video), audio: @raw.io.channel?(:audio))
52
+ src_io = @raw.temporise_io!(intermediate_extname)
53
+ if src_io.extname != intermediate_extname
54
+ meh_src_io, src_io = src_io, File.temp_fifo(intermediate_extname)
55
+ Util::Thread.new "source converter" do
56
+ Ffmprb.process do
42
57
 
43
- raw = unfiltered
44
- raw = raw.unfiltered while raw.respond_to? :unfiltered
45
- fail Error, "Something is wrong (double looping?)" unless raw == @raw
58
+ inp = input(meh_src_io)
59
+ output(src_io) do
60
+ lay inp
61
+ end
46
62
 
47
- dst_io = File.temp_fifo(src_io.extname)
63
+ end
64
+ end
65
+ end
66
+ cpy_io = File.temp_fifo(src_io.extname)
67
+ Ffmprb.logger.debug "(L2) Temporising the raw input (#{src_io.path}) and creating copy (#{cpy_io.path})"
48
68
 
49
- buff_raw_io = File.temp_fifo(src_io.extname)
50
- Util::ThreadedIoBuffer.new(
51
- File.async_opener(buff_raw_io, 'r'),
52
- File.async_opener(raw.io, 'w')
53
- )
69
+ src_io.threaded_buffered_copy_to @raw.io, cpy_io
54
70
 
55
- Ffmprb.logger.debug "Preprocessed looping input will be #{dst_io.path} and raw input copy will go through #{buff_raw_io.path} to #{raw.io.path}..."
71
+ # NOTE (3)
72
+ # NOTE preprocessed and filtered fifo
73
+ dst_io = File.temp_fifo(intermediate_extname)
74
+ @raw.process.proc_vis_node dst_io
56
75
 
57
76
  Util::Thread.new "looping input processor" do
58
- Ffmprb.logger.debug "Processing before looping"
59
-
60
- process = Process.new
61
- in1 = process.input(src_io)
62
- process.output(dst_io, video: video, audio: audio).
63
- lay in1.copy(unfiltered)
64
- process.output(buff_raw_io,
65
- video: OpenStruct.new, audio: OpenStruct.new # NOTE raw input copy
66
- ).
67
- lay in1
68
- process.run # TODO limit:
77
+ # Ffmprb.logger.debug "Processing before looping"
69
78
 
79
+ Ffmprb.logger.debug "(L3) Pre-processing into (#{dst_io.path})"
80
+ Ffmprb.process @_unfiltered, parent: @raw.process do |unfiltered| # TODO limit:
81
+
82
+ inp = input(cpy_io)
83
+ output(dst_io, video: video, audio: audio) do
84
+ lay inp.copy(unfiltered)
85
+ end
86
+
87
+ end
70
88
  end
71
89
 
72
- buff_ios = (0..times).map{File.temp_fifo src_io.extname}
90
+ # Ffmprb.logger.debug "Preprocessed (from #{src_io.path}) looping input: #{dst_io.path}, output: #{io.io.path}, and raw input copy will go through #{buff_raw_io.path} to #{@raw.io.path}..."
91
+
92
+ buff_ios = (1..times).map{File.temp_fifo intermediate_extname}
73
93
  Ffmprb.logger.debug "Preprocessed #{dst_io.path} will be teed to #{buff_ios.map(&:path).join '; '}"
74
- Util::ThreadedIoBuffer.new(
75
- File.async_opener(dst_io, 'r'),
76
- *buff_ios.map{|io| File.async_opener io, 'w'}
77
- )
94
+ looping = true
95
+ Util::Thread.new "cloning buffer watcher" do
96
+ dst_io.threaded_buffered_copy_to *buff_ios
97
+ Util::Thread.join_children!
78
98
 
79
- Ffmprb.logger.debug "Concatenation of #{buff_ios.map(&:path).join '; '} will go to #{@aux_input.io.path} to be fed to this process"
99
+ Ffmprb.logger.warn "Looping ~from #{src_io.path} finished before its consumer: if you just wanted to loop input #{Util.ffmpeg_inputs_max} times, that's fine, but if you expected it to loop indefinitely... #{Util.ffmpeg_inputs_max} is the maximum #loop can do at the moment, and it may just not be enough in this case (workaround by concatting or file a complaint at https://github.com/showbox-oss/ffmprb/issues please)." if looping && times == Util.ffmpeg_inputs_max
100
+ end
101
+
102
+ # Ffmprb.logger.debug "Concatenation of #{buff_ios.map(&:path).join '; '} will go to #{@io.io.path} to be fed to this process"
103
+
104
+ # NOTE additional (filtered, processed and looped) input io
105
+ aux_io = File.temp_fifo(intermediate_extname)
106
+
107
+ # NOTE (4)
80
108
 
81
109
  Util::Thread.new "looper" do
82
110
  Ffmprb.logger.debug "Looping #{buff_ios.size} times"
83
111
 
84
- process = Process.new(ignore_broken_pipe: true) # NOTE may not write its entire output, it's ok
85
- ins = buff_ios.map{|i| process.input i}
86
- process.output(@aux_input.io, video: nil, audio: nil) do
87
- ins.each{|i| lay i}
88
- end
89
- process.run # TODO limit:
112
+ Ffmprb.logger.debug "(L4) Looping (#{buff_ios.map &:path}) into (#{aux_io.path})"
113
+ begin
114
+ Ffmprb.process parent: @raw.process do # NOTE may not write its entire output, it's ok
115
+
116
+ ins = buff_ios.map{ |i| input i }
117
+ output(aux_io, video: nil, audio: nil) do
118
+ ins.each{ |i| lay i }
119
+ end
90
120
 
121
+ end
122
+ ensure
123
+ looping = false # NOTE see the above warning
124
+ end
91
125
  end
92
126
 
93
- self.unfiltered = @aux_input
127
+ # NOTE (1)
128
+
129
+ Ffmprb.logger.debug "(L1) Creating a new input (#{aux_io.path}) to the process"
130
+ @raw.process.input(aux_io)
94
131
  end
95
132
 
96
133
  end