ffmprb 0.11.3 → 0.11.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +12 -0
- data/Gemfile +8 -1
- data/Gemfile.lock +121 -0
- data/README.md +10 -8
- data/TODO.md +1 -0
- data/bin/test +7 -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/index.html +24412 -0
- data/ffmprb.gemspec +34 -14
- data/lib/defaults.rb +1 -1
- data/lib/ffmprb.rb +3 -3
- data/lib/ffmprb/file.rb +8 -8
- data/lib/ffmprb/file/sample.rb +2 -2
- data/lib/ffmprb/file/threaded_buffered.rb +3 -3
- data/lib/ffmprb/filter.rb +4 -1
- data/lib/ffmprb/find_silence.rb +5 -2
- data/lib/ffmprb/process.rb +5 -3
- data/lib/ffmprb/process/input.rb +1 -1
- data/lib/ffmprb/process/input/looping.rb +6 -11
- data/lib/ffmprb/process/output.rb +9 -6
- data/lib/ffmprb/util.rb +5 -3
- data/lib/ffmprb/util/proc_vis.rb +1 -1
- data/lib/ffmprb/util/thread.rb +6 -5
- data/lib/ffmprb/util/threaded_io_buffer.rb +20 -19
- data/lib/ffmprb/version.rb +2 -2
- data/tmp/output.rb +383 -0
- metadata +90 -138
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.ruby-version +0 -1
- data/.travis.yml +0 -3
- data/circle.yml +0 -7
data/ffmprb.gemspec
CHANGED
@@ -6,14 +6,43 @@ require 'ffmprb/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'ffmprb'
|
8
8
|
spec.version = Ffmprb::VERSION
|
9
|
-
spec.authors = ["
|
9
|
+
spec.authors = ["Costa Shapiro"]
|
10
10
|
spec.email = ['costa@mouldwarp.com']
|
11
11
|
|
12
|
-
spec.summary = "ffmprb is your audio/video
|
12
|
+
spec.summary = "ffmprb is your audio/video manipulation pal, based on https://ffmpeg.org"
|
13
13
|
spec.description = "A video and audio composing DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends"
|
14
14
|
spec.homepage = Ffmprb::GEM_GITHUB_URL
|
15
15
|
|
16
|
-
|
16
|
+
def self.ignore_match(pattern, path)
|
17
|
+
path = '/' + path unless path =~ %r'^/'
|
18
|
+
if File.directory?(path)
|
19
|
+
path += '/' unless path =~ %r'/$'
|
20
|
+
end
|
21
|
+
if pattern.is_a?(Array)
|
22
|
+
pattern
|
23
|
+
else
|
24
|
+
[pattern]
|
25
|
+
end.any? do |pattern|
|
26
|
+
pattern = '**/' + pattern unless pattern =~ %r'^/'
|
27
|
+
pattern += '**' if pattern =~ %r'/$'
|
28
|
+
File.fnmatch(pattern, path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
pattern =
|
33
|
+
if (ignore_pattern_lines = File.readlines('.gemignore') rescue nil)
|
34
|
+
ignore_pattern_lines.map do |line|
|
35
|
+
stripped = line.split(/(?<!\\)#/)[0].strip
|
36
|
+
stripped unless stripped.empty?
|
37
|
+
end.compact
|
38
|
+
else
|
39
|
+
%w[test/ spec/ features/]
|
40
|
+
end
|
41
|
+
|
42
|
+
# NOTE dotfiles are ignored by .glob
|
43
|
+
spec.files = Dir['**/*'].reject{|path| ignore_match pattern, path}
|
44
|
+
# TODO:rm spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
45
|
+
|
17
46
|
spec.bindir = 'exe'
|
18
47
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
48
|
spec.require_paths = ['lib']
|
@@ -23,15 +52,6 @@ Gem::Specification.new do |spec|
|
|
23
52
|
# NOTE make it into an optional dependency? Nah for now
|
24
53
|
spec.add_dependency 'thor', '~> 0.19.1'
|
25
54
|
|
26
|
-
spec.
|
27
|
-
|
28
|
-
spec.add_development_dependency 'simplecov', '>= 0.11.2'
|
29
|
-
spec.add_development_dependency 'guard-rspec', '>= 4.6.5'
|
30
|
-
spec.add_development_dependency 'guard-bundler', '>= 2.1.0'
|
31
|
-
spec.add_development_dependency 'rake', '>= 11.1.2'
|
32
|
-
spec.add_development_dependency 'rmagick', '>= 2.15.4'
|
33
|
-
spec.add_development_dependency 'ruby-sox', '>= 0.0.3'
|
34
|
-
spec.add_development_dependency 'firebase', '>= 0.2.6'
|
35
|
-
|
36
|
-
spec.post_install_message = "Have fun with your montage! To enable proc visualisation, install firebase gem and set FFMPRB_PROC_VIS_FIREBASE env." unless Ffmprb::FIREBASE_AVAILABLE
|
55
|
+
spec.post_install_message = "Have fun with your a/v! To enable proc visualisation, install firebase gem and set FFMPRB_PROC_VIS_FIREBASE env." unless
|
56
|
+
Ffmprb::FIREBASE_AVAILABLE
|
37
57
|
end
|
data/lib/defaults.rb
CHANGED
@@ -19,7 +19,7 @@ module Ffmprb
|
|
19
19
|
Process.input_video_fps = nil # NOTE the documented ffmpeg default is 25
|
20
20
|
|
21
21
|
Process.output_video_resolution = CGA
|
22
|
-
Process.output_video_fps = 16
|
22
|
+
Process.output_video_fps = 16 # NOTE the documented ffmpeg default is 25
|
23
23
|
Process.output_audio_encoder = 'libmp3lame'
|
24
24
|
Process.output_audio_sampling_freq = nil # NOTE Use ffmpeg default by default, specify otherwise e.g. 44100
|
25
25
|
|
data/lib/ffmprb.rb
CHANGED
@@ -25,13 +25,13 @@ module Ffmprb
|
|
25
25
|
name ||= blk.source_location.map(&:to_s).map{ |s| ::File.basename s.to_s, ::File.extname(s) }.join(':')
|
26
26
|
process = Process.new(name: name, **opts)
|
27
27
|
proc_vis_node process if respond_to? :proc_vis_node # XXX simply include the ProcVis if it makes into a gem
|
28
|
-
logger.debug
|
28
|
+
logger.debug{"Starting process with #{args} #{opts} in #{blk.source_location}"}
|
29
29
|
|
30
30
|
process.instance_exec *args, &blk
|
31
|
-
logger.debug
|
31
|
+
logger.debug{"Initialized process with #{args} #{opts} in #{blk.source_location}"}
|
32
32
|
|
33
33
|
process.run.tap do
|
34
|
-
logger.debug
|
34
|
+
logger.debug{"Finished process with #{args} #{opts} in #{blk.source_location}"}
|
35
35
|
end
|
36
36
|
end
|
37
37
|
alias :action! :process # ;)
|
data/lib/ffmprb/file.rb
CHANGED
@@ -17,27 +17,27 @@ module Ffmprb
|
|
17
17
|
->{
|
18
18
|
path = file.respond_to?(:path)? file.path : file
|
19
19
|
mode ||= file.respond_to?(mode)? file.mode.to_s[0] : 'r'
|
20
|
-
Ffmprb.logger.debug
|
20
|
+
Ffmprb.logger.debug{"Trying to open #{path} (for #{mode}-buffering or something)"}
|
21
21
|
::File.open(path, mode)
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
25
25
|
def create(path)
|
26
26
|
new(path: path, mode: :write).tap do |file|
|
27
|
-
Ffmprb.logger.debug
|
27
|
+
Ffmprb.logger.debug{"Created file with path: #{file.path}"}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def open(path)
|
32
32
|
new(path: path, mode: :read).tap do |file|
|
33
|
-
Ffmprb.logger.debug
|
33
|
+
Ffmprb.logger.debug{"Opened file with path: #{file.path}"}
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
def temp(extname)
|
38
38
|
file = create(Tempfile.new(['', extname]))
|
39
39
|
path = file.path
|
40
|
-
Ffmprb.logger.debug
|
40
|
+
Ffmprb.logger.debug{"Created temp file with path: #{path}"}
|
41
41
|
|
42
42
|
return file unless block_given?
|
43
43
|
|
@@ -49,7 +49,7 @@ module Ffmprb
|
|
49
49
|
rescue
|
50
50
|
Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.message}"
|
51
51
|
end
|
52
|
-
Ffmprb.logger.debug
|
52
|
+
Ffmprb.logger.debug{"Removed temp file with path: #{path}"}
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -69,12 +69,12 @@ module Ffmprb
|
|
69
69
|
rescue
|
70
70
|
Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.message}"
|
71
71
|
end
|
72
|
-
Ffmprb.logger.debug
|
72
|
+
Ffmprb.logger.debug{"Removed temp file with path: #{path}"}
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
def temp_fifo_path(extname)
|
77
|
-
::File.join Dir.tmpdir,
|
77
|
+
::File.join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
|
78
78
|
end
|
79
79
|
|
80
80
|
def image?(extname)
|
@@ -168,7 +168,7 @@ module Ffmprb
|
|
168
168
|
else
|
169
169
|
FileUtils.remove_entry path
|
170
170
|
end
|
171
|
-
Ffmprb.logger.debug
|
171
|
+
Ffmprb.logger.debug{"Removed file with path: #{path}"}
|
172
172
|
@path = nil
|
173
173
|
end
|
174
174
|
|
data/lib/ffmprb/file/sample.rb
CHANGED
@@ -11,7 +11,7 @@ module Ffmprb
|
|
11
11
|
audio = File.temp('.wav') if audio == true
|
12
12
|
video = File.temp('.png') if video == true
|
13
13
|
|
14
|
-
Ffmprb.logger.debug
|
14
|
+
Ffmprb.logger.debug{"Snap shooting files, video path: #{video ? video.path : 'NONE'}, audio path: #{audio ? audio.path : 'NONE'}"}
|
15
15
|
|
16
16
|
fail Error, "Incorrect output extname (must be image)" unless !video || video.channel?(:video) && !video.channel?(:audio)
|
17
17
|
fail Error, "Incorrect audio extname (must be sound)" unless !audio || audio.channel?(:audio) && !audio.channel?(:video)
|
@@ -30,7 +30,7 @@ module Ffmprb
|
|
30
30
|
begin
|
31
31
|
video.unlink if video
|
32
32
|
audio.unlink if audio
|
33
|
-
Ffmprb.logger.debug
|
33
|
+
Ffmprb.logger.debug{"Removed sample files"}
|
34
34
|
rescue
|
35
35
|
Ffmprb.logger.warn "#{$!.class.name} removing sample files: #{$!.message}"
|
36
36
|
end
|
@@ -7,7 +7,7 @@ module Ffmprb
|
|
7
7
|
def threaded_buffered_fifo(extname='.tmp', reader_open_on_writer_idle_limit: nil, proc_vis: nil)
|
8
8
|
input_fifo_file = temp_fifo(extname)
|
9
9
|
output_fifo_file = temp_fifo(extname)
|
10
|
-
Ffmprb.logger.debug
|
10
|
+
Ffmprb.logger.debug{"Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"}
|
11
11
|
Util::Thread.new do
|
12
12
|
begin
|
13
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)
|
@@ -20,13 +20,13 @@ module Ffmprb
|
|
20
20
|
ensure
|
21
21
|
Util::Thread.join_children!
|
22
22
|
end
|
23
|
-
Ffmprb.logger.debug
|
23
|
+
Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"}
|
24
24
|
ensure
|
25
25
|
input_fifo_file.unlink if input_fifo_file
|
26
26
|
output_fifo_file.unlink if output_fifo_file
|
27
27
|
end
|
28
28
|
end
|
29
|
-
Ffmprb.logger.debug
|
29
|
+
Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"}
|
30
30
|
|
31
31
|
[input_fifo_file, output_fifo_file]
|
32
32
|
end
|
data/lib/ffmprb/filter.rb
CHANGED
@@ -262,7 +262,10 @@ module Ffmprb
|
|
262
262
|
end
|
263
263
|
|
264
264
|
def complex_args(*filters)
|
265
|
-
[
|
265
|
+
[].tap do |args|
|
266
|
+
args << '-filter_complex' << filters.join('; ') unless
|
267
|
+
filters.empty?
|
268
|
+
end
|
266
269
|
end
|
267
270
|
|
268
271
|
private
|
data/lib/ffmprb/find_silence.rb
CHANGED
@@ -5,7 +5,7 @@ module Ffmprb
|
|
5
5
|
# NOTE not for streaming just yet
|
6
6
|
def find_silence(input_file, output_file)
|
7
7
|
path = "#{input_file.path}->#{output_file.path}"
|
8
|
-
logger.debug
|
8
|
+
logger.debug{"Finding silence (#{path})"}
|
9
9
|
silence = []
|
10
10
|
Util.ffmpeg('-i', input_file.path, *find_silence_detect_args, output_file.path).
|
11
11
|
scan(SILENCE_DETECT_REGEX).each do |mark, time|
|
@@ -25,7 +25,10 @@ module Ffmprb
|
|
25
25
|
Ffmprb.warn "Unknown silence mark: #{mark}"
|
26
26
|
end
|
27
27
|
end
|
28
|
-
logger.debug
|
28
|
+
logger.debug{
|
29
|
+
silence_map = silence.map{|t,v| "#{t}: #{v}"}
|
30
|
+
"Found silence (#{path}): [#{silence_map}]"
|
31
|
+
}
|
29
32
|
silence
|
30
33
|
end
|
31
34
|
|
data/lib/ffmprb/process.rb
CHANGED
@@ -39,7 +39,7 @@ module Ffmprb
|
|
39
39
|
def input_video_options
|
40
40
|
{
|
41
41
|
auto_rotate: input_video_auto_rotate,
|
42
|
-
fps: input_video_fps
|
42
|
+
fps: input_video_fps # TODO seen failing on apng (w/ffmpeg v4.x)
|
43
43
|
}
|
44
44
|
end
|
45
45
|
def input_audio_options
|
@@ -97,7 +97,10 @@ module Ffmprb
|
|
97
97
|
end
|
98
98
|
overlay in_over.volume ducked_overlay_volume
|
99
99
|
|
100
|
-
Ffmprb.logger.debug
|
100
|
+
Ffmprb.logger.debug{
|
101
|
+
ducked_overlay_volume_map = ducked_overlay_volume.map{|t,v| "#{t}: #{v}"}
|
102
|
+
"Ducking audio with volumes: {#{ducked_overlay_volume_map.join ', '}}"
|
103
|
+
}
|
101
104
|
end
|
102
105
|
|
103
106
|
end
|
@@ -157,7 +160,6 @@ module Ffmprb
|
|
157
160
|
thr = Util::Thread.new main: !parent do
|
158
161
|
proc_vis_node Thread.current
|
159
162
|
# NOTE yes, an exception can occur anytime, and we'll just die, it's ok, see above
|
160
|
-
# XXX just to return something -- no apparent practical use
|
161
163
|
cmd = command
|
162
164
|
opts = {limit: limit, timeout: timeout}
|
163
165
|
opts[:ignore_broken_pipes] = ignore_broken_pipes unless ignore_broken_pipes.nil?
|
data/lib/ffmprb/process/input.rb
CHANGED
@@ -60,7 +60,7 @@ module Ffmprb
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
cpy_io = File.temp_fifo(src_io.extname)
|
63
|
-
Ffmprb.logger.debug
|
63
|
+
Ffmprb.logger.debug{"(L2) Temporising the raw input (#{src_io.path}) and creating copy (#{cpy_io.path})"}
|
64
64
|
|
65
65
|
src_io.threaded_buffered_copy_to @raw.io, cpy_io
|
66
66
|
|
@@ -70,9 +70,8 @@ module Ffmprb
|
|
70
70
|
@raw.process.proc_vis_node dst_io
|
71
71
|
|
72
72
|
Util::Thread.new "looping input processor" do
|
73
|
-
|
73
|
+
Ffmprb.logger.debug{"(L3) Pre-processing into (#{dst_io.path})"}
|
74
74
|
|
75
|
-
Ffmprb.logger.debug "(L3) Pre-processing into (#{dst_io.path})"
|
76
75
|
Ffmprb.process @_unfiltered, parent: @raw.process do |unfiltered| # TODO limit:
|
77
76
|
|
78
77
|
inp = input(cpy_io)
|
@@ -83,10 +82,8 @@ module Ffmprb
|
|
83
82
|
end
|
84
83
|
end
|
85
84
|
|
86
|
-
# 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}..."
|
87
|
-
|
88
85
|
buff_ios = (1..times).map{File.temp_fifo intermediate_extname}
|
89
|
-
Ffmprb.logger.debug
|
86
|
+
Ffmprb.logger.debug{"Preprocessed #{dst_io.path} will be teed to #{buff_ios.map(&:path).join '; '}"}
|
90
87
|
Util::Thread.new "cloning buffer watcher" do
|
91
88
|
dst_io.threaded_buffered_copy_to(*buff_ios).tap do |io_buff|
|
92
89
|
Util::Thread.join_children!
|
@@ -94,17 +91,15 @@ module Ffmprb
|
|
94
91
|
end
|
95
92
|
end
|
96
93
|
|
97
|
-
# Ffmprb.logger.debug "Concatenation of #{buff_ios.map(&:path).join '; '} will go to #{@io.io.path} to be fed to this process"
|
98
|
-
|
99
94
|
# NOTE additional (filtered, processed and looped) input io
|
100
95
|
aux_io = File.temp_fifo(intermediate_extname)
|
101
96
|
|
102
97
|
# NOTE (4)
|
103
98
|
|
104
99
|
Util::Thread.new "looper" do
|
105
|
-
Ffmprb.logger.debug
|
100
|
+
Ffmprb.logger.debug{"Looping #{buff_ios.size} times"}
|
106
101
|
|
107
|
-
Ffmprb.logger.debug
|
102
|
+
Ffmprb.logger.debug{"(L4) Looping (#{buff_ios.map &:path}) into (#{aux_io.path})"}
|
108
103
|
begin # NOTE may not write its entire output, it's ok
|
109
104
|
Ffmprb.process parent: @raw.process, ignore_broken_pipes: false do
|
110
105
|
|
@@ -121,7 +116,7 @@ module Ffmprb
|
|
121
116
|
|
122
117
|
# NOTE (1)
|
123
118
|
|
124
|
-
Ffmprb.logger.debug
|
119
|
+
Ffmprb.logger.debug{"(L1) Creating a new input (#{aux_io.path}) to the process"}
|
125
120
|
@raw.process.input(aux_io)
|
126
121
|
end
|
127
122
|
|
@@ -84,7 +84,7 @@ module Ffmprb
|
|
84
84
|
# NOTE Image-Padding to match the target resolution
|
85
85
|
# TODO full screen only at the moment (see exception above)
|
86
86
|
|
87
|
-
Ffmprb.logger.debug
|
87
|
+
Ffmprb.logger.debug{"#{self} asking for filters of #{curr_reel.reel.io.inspect} video: #{channel(:video)}, audio: #{channel(:audio)}"}
|
88
88
|
@filters.concat(
|
89
89
|
curr_reel.reel.filters_for lbl, video: channel(:video), audio: channel(:audio)
|
90
90
|
)
|
@@ -151,7 +151,7 @@ module Ffmprb
|
|
151
151
|
) if channel?(:audio)
|
152
152
|
|
153
153
|
segments << new_prev_lbl
|
154
|
-
Ffmprb.logger.debug
|
154
|
+
Ffmprb.logger.debug{"Concatting segments: #{new_prev_lbl} pushed"}
|
155
155
|
end
|
156
156
|
|
157
157
|
if curr_reel.transition
|
@@ -264,7 +264,7 @@ module Ffmprb
|
|
264
264
|
end
|
265
265
|
process.proc_vis_edge process, main_av_o, :remove
|
266
266
|
process.proc_vis_edge process, main_av_inter_i
|
267
|
-
Ffmprb.logger.debug
|
267
|
+
Ffmprb.logger.debug{"Re-routed the main audio output (#{main_av_inter_i.path}->...->#{main_av_o.path}) through the process of audio ducking"}
|
268
268
|
|
269
269
|
over_a_i, over_a_o = File.threaded_buffered_fifo(Process.intermediate_channel_extname(audio: true, video: false), proc_vis: process)
|
270
270
|
lbl_over = "o#{idx}l#{i}"
|
@@ -273,10 +273,10 @@ module Ffmprb
|
|
273
273
|
)
|
274
274
|
@channel_lbl_ios["#{lbl_over}:a"] = over_a_i
|
275
275
|
process.proc_vis_edge process, over_a_i
|
276
|
-
Ffmprb.logger.debug
|
276
|
+
Ffmprb.logger.debug{"Routed and buffering auxiliary output fifos (#{over_a_i.path}>#{over_a_o.path}) for overlay"}
|
277
277
|
|
278
278
|
inter_i, inter_o = File.threaded_buffered_fifo(intermediate_extname, proc_vis: process)
|
279
|
-
Ffmprb.logger.debug
|
279
|
+
Ffmprb.logger.debug{"Allocated fifos to buffer media (#{inter_i.path}>#{inter_o.path}) while finding silence"}
|
280
280
|
|
281
281
|
ignore_broken_pipes_was = process.ignore_broken_pipes # XXX maybe throw an exception instead?
|
282
282
|
process.ignore_broken_pipes = true # NOTE audio ducking process may break the overlay pipe
|
@@ -285,7 +285,10 @@ module Ffmprb
|
|
285
285
|
process.proc_vis_edge main_av_inter_o, inter_i # XXX mark it better
|
286
286
|
silence = Ffmprb.find_silence(main_av_inter_o, inter_i)
|
287
287
|
|
288
|
-
Ffmprb.logger.debug
|
288
|
+
Ffmprb.logger.debug{
|
289
|
+
silence_map = silence.map{|s| "#{s.start_at}-#{s.end_at}"}
|
290
|
+
"Audio ducking with silence: [#{silence_map.join ', '}]"
|
291
|
+
}
|
289
292
|
|
290
293
|
Process.duck_audio inter_o, over_a_o, silence, main_av_o,
|
291
294
|
process_options: {parent: process, ignore_broken_pipes: ignore_broken_pipes_was, timeout: process.timeout},
|
data/lib/ffmprb/util.rb
CHANGED
@@ -18,8 +18,10 @@ module Ffmprb
|
|
18
18
|
sh *ffprobe_cmd, *args, limit: limit, timeout: timeout
|
19
19
|
end
|
20
20
|
|
21
|
+
# TODO warn on broken pipes incompatibility with 4.x or something
|
21
22
|
def ffmpeg(*args, limit: nil, timeout: cmd_timeout, ignore_broken_pipes: true)
|
22
|
-
args = ['-loglevel', 'debug'] + args if
|
23
|
+
args = ['-loglevel', 'debug'] + args if
|
24
|
+
Ffmprb.ffmpeg_debug
|
23
25
|
sh *ffmpeg_cmd, *args, output: :stderr, limit: limit, timeout: timeout, ignore_broken_pipes: ignore_broken_pipes
|
24
26
|
end
|
25
27
|
|
@@ -42,7 +44,7 @@ module Ffmprb
|
|
42
44
|
value = wait_thr.value
|
43
45
|
status = value.exitstatus # NOTE blocking
|
44
46
|
if status != 0
|
45
|
-
if value.signaled? && value.termsig == Signal.list['PIPE']
|
47
|
+
if value.signaled? && value.termsig == Signal.list['PIPE'] # TODO! this doesn't seem to work for ffmpeg 4.x (it ignores SIGPIPEs)
|
46
48
|
if ignore_broken_pipes
|
47
49
|
Ffmprb.logger.info "Ignoring broken pipe: #{cmd_str}"
|
48
50
|
else
|
@@ -54,7 +56,7 @@ module Ffmprb
|
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
57
|
-
Ffmprb.logger.debug
|
59
|
+
Ffmprb.logger.debug{"FINISHED: #{cmd_str}"}
|
58
60
|
|
59
61
|
Thread.join_children! limit, timeout: timeout
|
60
62
|
|
data/lib/ffmprb/util/proc_vis.rb
CHANGED
@@ -137,7 +137,7 @@ module Ffmprb
|
|
137
137
|
@proc_vis_firebase_client =
|
138
138
|
if proc_vis_firebase
|
139
139
|
url = "https://#{proc_vis_firebase}.firebaseio.com/proc/"
|
140
|
-
Ffmprb.logger.debug
|
140
|
+
Ffmprb.logger.debug{"Connecting to #{url}"}
|
141
141
|
begin
|
142
142
|
Firebase::Client.new(url).tap do
|
143
143
|
Ffmprb.logger.info "Connected to #{url}"
|
data/lib/ffmprb/util/thread.rb
CHANGED
@@ -42,12 +42,13 @@ module Ffmprb
|
|
42
42
|
attr_reader :name
|
43
43
|
|
44
44
|
def initialize(name="some", main: false, &blk)
|
45
|
+
orig_caller = caller
|
45
46
|
@name = name
|
46
47
|
@parent = Thread.current
|
47
48
|
@live_children = []
|
48
49
|
@children_mon = Monitor.new
|
49
50
|
@dead_children_q = Queue.new
|
50
|
-
Ffmprb.logger.debug
|
51
|
+
Ffmprb.logger.debug{"about to launch #{name}"}
|
51
52
|
sync_q = Queue.new
|
52
53
|
super() do
|
53
54
|
@parent.proc_vis_node self if @parent.respond_to? :proc_vis_node
|
@@ -57,10 +58,10 @@ module Ffmprb
|
|
57
58
|
Ffmprb.logger.warn "Not the main: true thread run by a not #{self.class.name} thread" unless main
|
58
59
|
end
|
59
60
|
sync_q.enq :ok
|
60
|
-
Ffmprb.logger.debug
|
61
|
+
Ffmprb.logger.debug{"#{name} thread launched"}
|
61
62
|
begin
|
62
63
|
blk.call.tap do
|
63
|
-
Ffmprb.logger.debug
|
64
|
+
Ffmprb.logger.debug{"#{name} thread done"}
|
64
65
|
end
|
65
66
|
rescue Exception
|
66
67
|
Ffmprb.logger.warn "#{$!.class.name} raised in #{name} thread: #{$!.message}\nBacktrace:\n\t#{$!.backtrace.join("\n\t")}"
|
@@ -84,7 +85,7 @@ module Ffmprb
|
|
84
85
|
|
85
86
|
def child_lives(thr)
|
86
87
|
@children_mon.synchronize do
|
87
|
-
Ffmprb.logger.debug
|
88
|
+
Ffmprb.logger.debug{"picking up #{thr.name} thread"}
|
88
89
|
@live_children << thr
|
89
90
|
end
|
90
91
|
proc_vis_edge self, thr
|
@@ -92,7 +93,7 @@ module Ffmprb
|
|
92
93
|
|
93
94
|
def child_dies(thr)
|
94
95
|
@children_mon.synchronize do
|
95
|
-
Ffmprb.logger.debug
|
96
|
+
Ffmprb.logger.debug{"releasing #{thr.name} thread"}
|
96
97
|
@dead_children_q.enq thr
|
97
98
|
fail "System Error" unless @live_children.delete thr
|
98
99
|
end
|