ffmprb 0.11.3 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Dockerfile +18 -0
- data/Gemfile +8 -1
- data/Gemfile.lock +121 -0
- data/README.md +65 -21
- data/TODO.md +0 -0
- data/bin/dev +12 -0
- data/bin/test +13 -0
- data/coverage/assets/0.10.0/application.css +799 -0
- data/coverage/assets/0.10.0/application.js +1707 -0
- data/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.0/loading.gif +0 -0
- data/coverage/assets/0.10.0/magnify.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.10.2/application.css +799 -0
- data/coverage/assets/0.10.2/application.js +1707 -0
- data/coverage/assets/0.10.2/colorbox/border.png +0 -0
- data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.2/favicon_green.png +0 -0
- data/coverage/assets/0.10.2/favicon_red.png +0 -0
- data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.2/loading.gif +0 -0
- data/coverage/assets/0.10.2/magnify.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.2/application.css +1 -0
- data/coverage/assets/0.12.2/application.js +7 -0
- data/coverage/assets/0.12.2/colorbox/border.png +0 -0
- data/coverage/assets/0.12.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.2/favicon_green.png +0 -0
- data/coverage/assets/0.12.2/favicon_red.png +0 -0
- data/coverage/assets/0.12.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.2/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.2/loading.gif +0 -0
- data/coverage/assets/0.12.2/magnify.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.3/application.css +1 -0
- data/coverage/assets/0.12.3/application.js +7 -0
- data/coverage/assets/0.12.3/colorbox/border.png +0 -0
- data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.3/favicon_green.png +0 -0
- data/coverage/assets/0.12.3/favicon_red.png +0 -0
- data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.3/loading.gif +0 -0
- data/coverage/assets/0.12.3/magnify.png +0 -0
- data/coverage/index.html +47324 -0
- data/exp/EXP +7 -0
- data/exp/av-cut-mp4you60.ffmprb +10 -0
- data/exp/docker-compose.yml +9 -0
- data/exp/gop-cut-cat-you60 +141 -0
- data/exp/present/Dockerfile +13 -0
- data/exp/present/Gemfile +3 -0
- data/exp/present/Gemfile.lock +22 -0
- data/exp/present/bin/up-deps +8 -0
- data/exp/present/docker-compose.yml +21 -0
- data/exp/present/exp/present +10 -0
- data/exp/present/exp/present.rb +37 -0
- data/exp/run +9 -0
- data/exp/stitch2.ffmprb +5 -0
- data/exp/unzip-mp4you60 +58 -0
- data/exp/youtubby/Dockerfile +13 -0
- data/exp/youtubby/Gemfile +7 -0
- data/exp/youtubby/Gemfile.lock +73 -0
- data/exp/youtubby/README.md +21 -0
- data/exp/youtubby/bin/up-deps +8 -0
- data/exp/youtubby/docker-compose.yml +20 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60 +13 -0
- data/exp/youtubby/exp/gop-raw-cut-you-HD60.rb +230 -0
- data/exp/youtubby/exp/media-upload +13 -0
- data/exp/youtubby/exp/tmp/CURRENT +2 -0
- data/exp/youtubby/google_youtube.rb +39 -0
- data/exp/youtubby/old-ul.py +181 -0
- data/exp/youtubby/old-ul.rb +87 -0
- data/exp/youtubby/py-Dockerfile +11 -0
- data/exp/youtubby/py-docker-compose.yml +13 -0
- data/exp/youtubby/requirements.txt +19 -0
- data/exp/youtubby/upload.rb +38 -0
- data/exp/zip-cut-mp4you60 +42 -0
- data/exp/zip2mp4k60 +27 -0
- data/ffmprb.gemspec +34 -14
- data/lib/defaults.rb +6 -6
- data/lib/ffmprb/execution.rb +1 -1
- data/lib/ffmprb/file/sample.rb +2 -2
- data/lib/ffmprb/file/threaded_buffered.rb +4 -4
- data/lib/ffmprb/file.rb +20 -20
- data/lib/ffmprb/filter.rb +100 -61
- data/lib/ffmprb/find_silence.rb +5 -2
- data/lib/ffmprb/process/input/chain_base.rb +6 -4
- data/lib/ffmprb/process/input/channeled.rb +1 -1
- data/lib/ffmprb/process/input/cropped.rb +4 -1
- data/lib/ffmprb/process/input/cut.rb +2 -2
- data/lib/ffmprb/process/input/looping.rb +11 -16
- data/lib/ffmprb/process/input/loud.rb +1 -1
- data/lib/ffmprb/process/input/paced.rb +35 -0
- data/lib/ffmprb/process/input/postprocessed.rb +29 -0
- data/lib/ffmprb/process/input/reversed.rb +29 -0
- data/lib/ffmprb/process/input.rb +7 -3
- data/lib/ffmprb/process/output.rb +45 -29
- data/lib/ffmprb/process.rb +8 -9
- data/lib/ffmprb/util/proc_vis.rb +5 -4
- data/lib/ffmprb/util/thread.rb +13 -11
- data/lib/ffmprb/util/threaded_io_buffer.rb +25 -22
- data/lib/ffmprb/util.rb +38 -13
- data/lib/ffmprb/version.rb +2 -2
- data/lib/ffmprb.rb +8 -5
- data/tmp/exp/docker-compose.yml +9 -0
- data/tmp/exp/src/SAM_3132.MP4 +0 -0
- data/tmp/ffmprb-0.11.4.gem +0 -0
- data/tmp/output.rb +383 -0
- metadata +159 -139
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.ruby-version +0 -1
- data/.travis.yml +0 -3
- data/circle.yml +0 -7
@@ -6,7 +6,7 @@ module Ffmprb
|
|
6
6
|
|
7
7
|
# TODO the events mechanism is currently unused (and commented out) => synchro mechanism not needed
|
8
8
|
class ThreadedIoBuffer
|
9
|
-
#
|
9
|
+
# TODO? include Synchro
|
10
10
|
include ProcVis::Node
|
11
11
|
|
12
12
|
class << self
|
@@ -29,7 +29,7 @@ module Ffmprb
|
|
29
29
|
def initialize(input, *outputs, keep_outputs_open_on_input_idle_limit: nil)
|
30
30
|
super() # NOTE for the monitor, apparently
|
31
31
|
|
32
|
-
Ffmprb.logger.debug
|
32
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer initializing with (#{ThreadedIoBuffer.blocks_max}x#{ThreadedIoBuffer.block_size})"}
|
33
33
|
|
34
34
|
@input = input
|
35
35
|
@outputs = outputs.map do |outp|
|
@@ -47,10 +47,11 @@ module Ffmprb
|
|
47
47
|
end
|
48
48
|
|
49
49
|
Thread.join_children!.tap do
|
50
|
-
Ffmprb.logger.debug
|
50
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer (#{@input.path}->#{@outputs.map(&:io).map(&:path)}) terminated successfully (#{stats})"}
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
|
+
# TODO?
|
54
55
|
#
|
55
56
|
# def once(event, &blk)
|
56
57
|
# event = event.to_sym
|
@@ -58,17 +59,17 @@ module Ffmprb
|
|
58
59
|
# if @events[event].respond_to? :call
|
59
60
|
# fail Error, "Once upon a time (one #once(event) at a time) please"
|
60
61
|
# elsif @events[event]
|
61
|
-
# Ffmprb.logger.debug
|
62
|
+
# Ffmprb.logger.debug{"ThreadedIoBuffer (post-)reacting to #{event}"}
|
62
63
|
# @handler_thr = Util::Thread.new "#{event} handler", &blk
|
63
64
|
# else
|
64
|
-
# Ffmprb.logger.debug
|
65
|
+
# Ffmprb.logger.debug{"ThreadedIoBuffer subscribing to #{event}"}
|
65
66
|
# @events[event] = blk
|
66
67
|
# end
|
67
68
|
# end
|
68
69
|
# handle_synchronously :once
|
69
70
|
#
|
70
71
|
# def reader_done!
|
71
|
-
# Ffmprb.logger.debug
|
72
|
+
# Ffmprb.logger.debug{"ThreadedIoBuffer reader terminated (#{stats})"}
|
72
73
|
# fire! :reader_done
|
73
74
|
# end
|
74
75
|
#
|
@@ -84,7 +85,7 @@ module Ffmprb
|
|
84
85
|
#
|
85
86
|
# def fire!(event)
|
86
87
|
# wait_for_handler!
|
87
|
-
# Ffmprb.logger.debug
|
88
|
+
# Ffmprb.logger.debug{"ThreadedIoBuffer firing #{event}"}
|
88
89
|
# if blk = @events.to_h[event.to_sym]
|
89
90
|
# @handler_thr = Util::Thread.new "#{event} handler", &blk
|
90
91
|
# end
|
@@ -104,9 +105,9 @@ module Ffmprb
|
|
104
105
|
|
105
106
|
def reader_input! # NOTE just for reader thread
|
106
107
|
if @input.respond_to?(:call)
|
107
|
-
Ffmprb.logger.debug
|
108
|
+
Ffmprb.logger.debug{"Opening buffer input"}
|
108
109
|
@input = @input.call
|
109
|
-
Ffmprb.logger.debug
|
110
|
+
Ffmprb.logger.debug{"Opened buffer input: #{@input.path}"}
|
110
111
|
end
|
111
112
|
@input
|
112
113
|
end
|
@@ -137,17 +138,17 @@ module Ffmprb
|
|
137
138
|
rescue IO::WaitReadable
|
138
139
|
if @keep_outputs_open_on_input_idle_limit && stats.bytes_in > 0 && stats.blocks_buff == 0 && timeouts * ThreadedIoBuffer.io_wait_timeout > @keep_outputs_open_on_input_idle_limit
|
139
140
|
if s.length > 0 # NOTE let's see if it helps outputting an incomplete block
|
140
|
-
Ffmprb.logger.debug
|
141
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer reader (from #{input_io.path}) giving a chance to write #{s.length}/#{ThreadedIoBuffer.block_size}b after waiting >#{@keep_outputs_open_on_input_idle_limit}s, after reading #{stats.bytes_in}b"}
|
141
142
|
break
|
142
143
|
else
|
143
|
-
Ffmprb.logger.debug
|
144
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer reader (from #{input_io.path}) giving up after waiting >#{@keep_outputs_open_on_input_idle_limit}s, after reading #{stats.bytes_in}b, closing outputs"}
|
144
145
|
raise EOFError
|
145
146
|
end
|
146
147
|
else
|
147
148
|
Thread.current.live!
|
148
149
|
timeouts += 1
|
149
150
|
if timeouts > 2 * logged_timeouts
|
150
|
-
Ffmprb.logger.debug
|
151
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer reader (from #{input_io.path}) retrying... (#{timeouts} reads): #{$!.class}"}
|
151
152
|
logged_timeouts = timeouts
|
152
153
|
end
|
153
154
|
IO.select [input_io], nil, nil, ThreadedIoBuffer.io_wait_timeout
|
@@ -165,7 +166,7 @@ module Ffmprb
|
|
165
166
|
output_enq! s
|
166
167
|
end
|
167
168
|
rescue EOFError
|
168
|
-
Ffmprb.logger.debug
|
169
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer reader (from #{input_io.path}) breaking off"}
|
169
170
|
rescue AllOutputsBrokenError
|
170
171
|
Ffmprb.logger.info "All outputs broken"
|
171
172
|
rescue Exception
|
@@ -182,7 +183,7 @@ module Ffmprb
|
|
182
183
|
Ffmprb.logger.error "#{$!.class.name} closing ThreadedIoBuffer input: #{$!.message}"
|
183
184
|
end
|
184
185
|
# reader_done!
|
185
|
-
Ffmprb.logger.debug
|
186
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer reader terminated (#{stats})"}
|
186
187
|
end
|
187
188
|
end
|
188
189
|
end
|
@@ -191,13 +192,13 @@ module Ffmprb
|
|
191
192
|
return output.io = output._io unless output._io.respond_to?(:call)
|
192
193
|
|
193
194
|
output.thr = Thread.new("buffer writer output helper") do
|
194
|
-
Ffmprb.logger.debug
|
195
|
+
Ffmprb.logger.debug{"Opening buffer output"}
|
195
196
|
output.io =
|
196
197
|
Thread.timeout_or_live nil, log: "in the buffer writer helper thread", timeout: ThreadedIoBuffer.timeout do |time|
|
197
198
|
fail Error, "giving up buffer writer init since the reader has failed (#{@reader_failed.message})" if @reader_failed
|
198
199
|
output._io.call
|
199
200
|
end
|
200
|
-
Ffmprb.logger.debug
|
201
|
+
Ffmprb.logger.debug{"Opened buffer output: #{output.io.path}"}
|
201
202
|
end
|
202
203
|
end
|
203
204
|
|
@@ -223,7 +224,7 @@ module Ffmprb
|
|
223
224
|
Thread.current.live!
|
224
225
|
timeouts += 1
|
225
226
|
if timeouts > 2 * logged_timeouts
|
226
|
-
Ffmprb.logger.debug
|
227
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer writer (to #{output_io.path}) retrying... (#{timeouts} writes): #{$!.class}"}
|
227
228
|
logged_timeouts = timeouts
|
228
229
|
end
|
229
230
|
IO.select nil, [output_io], nil, ThreadedIoBuffer.io_wait_timeout
|
@@ -234,9 +235,9 @@ module Ffmprb
|
|
234
235
|
retry
|
235
236
|
end
|
236
237
|
end
|
237
|
-
Ffmprb.logger.debug
|
238
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer writer (to #{output_io.path}) breaking off"}
|
238
239
|
rescue Errno::EPIPE
|
239
|
-
Ffmprb.logger.debug
|
240
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer writer (to #{output_io.path}) broken"}
|
240
241
|
output.broken = true
|
241
242
|
ensure
|
242
243
|
# terminated!
|
@@ -246,7 +247,7 @@ module Ffmprb
|
|
246
247
|
Ffmprb.logger.error "#{$!.class.name} closing ThreadedIoBuffer output: #{$!.message}"
|
247
248
|
end
|
248
249
|
output.broken = true
|
249
|
-
Ffmprb.logger.debug
|
250
|
+
Ffmprb.logger.debug{"ThreadedIoBuffer writer (to #{output_io && output_io.path}) terminated (#{stats})"}
|
250
251
|
end
|
251
252
|
end
|
252
253
|
end
|
@@ -279,9 +280,11 @@ module Ffmprb
|
|
279
280
|
logged_timeouts = timeouts
|
280
281
|
end
|
281
282
|
|
282
|
-
retry unless
|
283
|
+
retry unless # NOTE the queue has probably overflown
|
284
|
+
timeouts >= ThreadedIoBuffer.timeout_limit
|
283
285
|
|
284
|
-
@reader_failed ||=
|
286
|
+
@reader_failed ||= # NOTE screw the race condition
|
287
|
+
Error.new("the writer has failed with timeout limit while queuing")
|
285
288
|
# timeout!
|
286
289
|
fail Error, "Looks like we're stuck (>#{ThreadedIoBuffer.timeout_limit*ThreadedIoBuffer.timeout}s idle) with #{ThreadedIoBuffer.blocks_max}x#{ThreadedIoBuffer.block_size}b blocks (buffering #{reader_input!.path}->...)..."
|
287
290
|
end
|
data/lib/ffmprb/util.rb
CHANGED
@@ -9,6 +9,8 @@ module Ffmprb
|
|
9
9
|
class BrokenPipeError < Error; end
|
10
10
|
class TimeLimitError < Error; end
|
11
11
|
|
12
|
+
FFMPEG_BROKEN_PIPE_ERROR_RE = /^.*\berror\b.*:.*\bbroken pipe\b.*$/i
|
13
|
+
|
12
14
|
class << self
|
13
15
|
|
14
16
|
attr_accessor :ffmpeg_cmd, :ffmpeg_inputs_max, :ffprobe_cmd
|
@@ -18,17 +20,25 @@ module Ffmprb
|
|
18
20
|
sh *ffprobe_cmd, *args, limit: limit, timeout: timeout
|
19
21
|
end
|
20
22
|
|
23
|
+
# TODO un-colorise ffmpeg output for logging, also, convert ^M into something
|
21
24
|
def ffmpeg(*args, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: true)
|
22
|
-
args = [
|
23
|
-
|
25
|
+
args = %w[-loglevel debug] + args if
|
26
|
+
Ffmprb.ffmpeg_debug
|
27
|
+
sh *ffmpeg_cmd, *args,
|
28
|
+
output: :stderr,
|
29
|
+
limit: limit,
|
30
|
+
timeout: timeout,
|
31
|
+
ignore_broken_pipes: ignore_broken_pipes,
|
32
|
+
broken_pipe_error_re: FFMPEG_BROKEN_PIPE_ERROR_RE
|
24
33
|
end
|
25
34
|
|
26
|
-
def sh(*cmd, input: nil, output: :stdout, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: false)
|
35
|
+
def sh(*cmd, input: nil, output: :stdout, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: false, broken_pipe_error_re: nil)
|
27
36
|
cmd = cmd.map &:to_s unless cmd.size == 1
|
28
37
|
cmd_str = cmd.size != 1 ? cmd.map{|c| sh_escape c}.join(' ') : cmd.first
|
38
|
+
cmd_log_line = "#{log_hash cmd_str}: `#{cmd_str}`"
|
29
39
|
timeout = [timeout, limit].compact.min
|
30
|
-
thr = Thread.new
|
31
|
-
Ffmprb.logger.info "Popening
|
40
|
+
thr = Thread.new cmd_log_line do
|
41
|
+
Ffmprb.logger.info "Popening #{cmd_log_line}..."
|
32
42
|
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
|
33
43
|
begin
|
34
44
|
stdin.write input if input
|
@@ -37,29 +47,33 @@ module Ffmprb
|
|
37
47
|
log_cmd = cmd.first.upcase
|
38
48
|
stdout_r = Reader.new(stdout, store: output == :stdout, log_with: log_cmd)
|
39
49
|
stderr_r = Reader.new(stderr, store: true, log_with: log_cmd, log_as: output == :stderr && Logger::DEBUG || Logger::INFO)
|
50
|
+
stderr_s = nil
|
40
51
|
|
41
|
-
Thread.timeout_or_live(limit, log: "while waiting for
|
52
|
+
Thread.timeout_or_live(limit, log: "while waiting for #{cmd_log_line}", timeout: timeout) do |time|
|
42
53
|
value = wait_thr.value
|
43
54
|
status = value.exitstatus # NOTE blocking
|
44
55
|
if status != 0
|
45
|
-
|
56
|
+
stderr_s = stderr_r.read
|
57
|
+
if (value.signaled? && value.termsig == Signal.list['PIPE']) ||
|
58
|
+
# NOTE this doesn't seem to work for ffmpeg 4.x (it ignores SIGPIPEs)
|
59
|
+
(broken_pipe_error_re && status == 1 && stderr_s =~ broken_pipe_error_re)
|
46
60
|
if ignore_broken_pipes
|
47
|
-
Ffmprb.logger.info "Ignoring broken pipe: #{
|
61
|
+
Ffmprb.logger.info "Ignoring broken pipe: #{cmd_log_line}"
|
48
62
|
else
|
49
|
-
fail BrokenPipeError,
|
63
|
+
fail BrokenPipeError, cmd_log_line
|
50
64
|
end
|
51
65
|
else
|
52
66
|
status ||= "sig##{value.termsig}"
|
53
|
-
fail Error, "#{
|
67
|
+
fail Error, "#{cmd_log_line} (#{status}):\n#{stderr_s}"
|
54
68
|
end
|
55
69
|
end
|
56
70
|
end
|
57
|
-
Ffmprb.logger.debug
|
71
|
+
Ffmprb.logger.debug{"FINISHED: #{cmd_log_line}"}
|
58
72
|
|
59
73
|
Thread.join_children! limit, timeout: timeout
|
60
74
|
|
61
75
|
# NOTE only one of them will return non-nil, see above
|
62
|
-
stdout_r.read || stderr_r.read
|
76
|
+
stdout_r.read || stderr_s || stderr_r.read
|
63
77
|
ensure
|
64
78
|
process_dead! wait_thr, cmd_str, limit
|
65
79
|
end
|
@@ -71,7 +85,18 @@ module Ffmprb
|
|
71
85
|
def assert_options_empty!(opts)
|
72
86
|
fail ArgumentError, "Unknown options: #{opts}" unless opts.empty?
|
73
87
|
end
|
74
|
-
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def log_hash(s)
|
92
|
+
n = s.hash
|
93
|
+
%w[bcdfghk aeiuy mnprqstvwxz].reduce '' do |hash, chars|
|
94
|
+
hash + chars[n % chars.length]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def broken_pipe_error_printed?(s)
|
99
|
+
end
|
75
100
|
|
76
101
|
# NOTE a best guess kinda method
|
77
102
|
def sh_escape(str)
|
data/lib/ffmprb/version.rb
CHANGED
data/lib/ffmprb.rb
CHANGED
@@ -13,8 +13,10 @@ module Ffmprb
|
|
13
13
|
|
14
14
|
CGA = '320x200'
|
15
15
|
QVGA = '320x240'
|
16
|
+
HD_480p = '854x480'
|
16
17
|
HD_720p = '1280x720'
|
17
18
|
HD_1080p = '1920x1080'
|
19
|
+
HD_4K = '3840x2160'
|
18
20
|
|
19
21
|
class << self
|
20
22
|
|
@@ -22,16 +24,17 @@ module Ffmprb
|
|
22
24
|
def process(*args, name: nil, **opts, &blk)
|
23
25
|
fail Error, "process: nothing ;( gimme a block!" unless blk
|
24
26
|
|
25
|
-
name ||= blk.source_location.map(&:to_s).map{ |s|
|
27
|
+
name ||= blk.source_location.map(&:to_s).map{ |s| File.basename s.to_s, File.extname(s) }.join(':')
|
26
28
|
process = Process.new(name: name, **opts)
|
27
|
-
|
28
|
-
|
29
|
+
# TODO simply include the ProcVis if it makes into a gem
|
30
|
+
proc_vis_node process if respond_to? :proc_vis_node
|
31
|
+
logger.debug{"Starting process with #{args} #{opts} in #{blk.source_location}"}
|
29
32
|
|
30
33
|
process.instance_exec *args, &blk
|
31
|
-
logger.debug
|
34
|
+
logger.debug{"Initialized process with #{args} #{opts} in #{blk.source_location}"}
|
32
35
|
|
33
36
|
process.run.tap do
|
34
|
-
logger.debug
|
37
|
+
logger.debug{"Finished process with #{args} #{opts} in #{blk.source_location}"}
|
35
38
|
end
|
36
39
|
end
|
37
40
|
alias :action! :process # ;)
|
Binary file
|
Binary file
|