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
@@ -23,7 +23,7 @@ module Ffmprb
|
|
23
23
|
|
24
24
|
lbl_aux = "cp#{lbl}"
|
25
25
|
lbl_tmp = "tmp#{lbl}"
|
26
|
-
|
26
|
+
super(lbl_aux, video: unsize(video), audio: audio) +
|
27
27
|
[
|
28
28
|
*((video && channel?(:video))? [
|
29
29
|
Filter.crop_prop(ratios, "#{lbl_aux}:v", "#{lbl_tmp}:v"),
|
@@ -58,6 +58,9 @@ module Ffmprb
|
|
58
58
|
fail Error, "Crop #{key} must be between 0 and 1 (not '#{value}')" unless
|
59
59
|
(0...1).include? value
|
60
60
|
end
|
61
|
+
fail Error, "Unreasonable crop args (#{ratios})" unless
|
62
|
+
(!ratios.include?(:left) || !ratios.include?(:right) || ratios[:left] + ratios[:right] < 1) &&
|
63
|
+
(!ratios.include?(:top) || !ratios.include?(:bottom) || ratios[:top] + ratios[:bottom] < 1)
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
@@ -23,12 +23,12 @@ module Ffmprb
|
|
23
23
|
|
24
24
|
def filters_for(lbl, video:, audio:)
|
25
25
|
fail Error, "cut needs resolution and fps (reorder your filters?)" unless
|
26
|
-
!video || video.resolution && video.fps
|
26
|
+
!video || (video.resolution && video.fps)
|
27
27
|
|
28
28
|
# Trimming
|
29
29
|
|
30
30
|
lbl_aux = "tm#{lbl}"
|
31
|
-
|
31
|
+
super(lbl_aux, video: video, audio: audio) +
|
32
32
|
if to
|
33
33
|
lbl_blk = "bl#{lbl}"
|
34
34
|
lbl_pad = "pd#{lbl}"
|
@@ -41,6 +41,7 @@ module Ffmprb
|
|
41
41
|
protected
|
42
42
|
|
43
43
|
def aux_input(video:, audio:)
|
44
|
+
Ffmprb.logger.debug{"Creating aux inp with #{audio} / #{video}"}
|
44
45
|
|
45
46
|
# NOTE (2)
|
46
47
|
# NOTE replace the raw input io with a copy io, getting original fifo/file
|
@@ -50,17 +51,16 @@ module Ffmprb
|
|
50
51
|
meh_src_io, src_io = src_io, File.temp_fifo(intermediate_extname)
|
51
52
|
Util::Thread.new "source converter" do
|
52
53
|
Ffmprb.process do
|
53
|
-
|
54
54
|
inp = input(meh_src_io)
|
55
|
-
|
55
|
+
# TODO this is not properly tested, unfortunately
|
56
|
+
output src_io, video: video, audio: audio do
|
56
57
|
lay inp
|
57
58
|
end
|
58
|
-
|
59
59
|
end
|
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,33 +82,29 @@ 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!
|
93
|
-
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
|
90
|
+
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 something)." if times == Util.ffmpeg_inputs_max && io_buff.stats.blocks_buff == 0
|
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
|
|
111
106
|
ins = buff_ios.map{ |i| input i }
|
112
|
-
output(aux_io, video:
|
107
|
+
output(aux_io, video: video, audio: audio) do
|
113
108
|
ins.each{ |i| lay i }
|
114
109
|
end
|
115
110
|
|
@@ -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
|
|
@@ -26,7 +26,7 @@ module Ffmprb
|
|
26
26
|
# Modulating volume
|
27
27
|
|
28
28
|
lbl_aux = "ld#{lbl}"
|
29
|
-
|
29
|
+
super(lbl_aux, video: video, audio: audio) +
|
30
30
|
[
|
31
31
|
*((video && channel?(:video))? Filter.copy("#{lbl_aux}:v", "#{lbl}:v"): nil),
|
32
32
|
*((audio && channel?(:audio))? Filter.volume(@volume, "#{lbl_aux}:a", "#{lbl}:a"): nil)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
class Process
|
4
|
+
|
5
|
+
class Input
|
6
|
+
|
7
|
+
# TODO? speed-up/slow-down
|
8
|
+
def pace(ratio)
|
9
|
+
Paced.new self, pace: ratio
|
10
|
+
end
|
11
|
+
|
12
|
+
class Paced < ChainBase
|
13
|
+
|
14
|
+
attr_reader :ratio
|
15
|
+
|
16
|
+
def initialize(unfiltered, pace:)
|
17
|
+
super unfiltered
|
18
|
+
@ratio = pace
|
19
|
+
end
|
20
|
+
|
21
|
+
def filters_for(lbl, video:, audio:)
|
22
|
+
|
23
|
+
# Pacing
|
24
|
+
|
25
|
+
lbl_aux = "pc#{lbl}"
|
26
|
+
super(lbl_aux, video: video, audio: audio) +
|
27
|
+
[
|
28
|
+
*((video && channel?(:video))? Filter.setpts(@ratio, "#{lbl_aux}:v", "#{lbl}:v"): nil),
|
29
|
+
*((audio && channel?(:audio))? Filter.atempo(@ratio, "#{lbl_aux}:a", "#{lbl}:a"): nil)
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
class Process
|
4
|
+
|
5
|
+
class Input
|
6
|
+
|
7
|
+
def pp
|
8
|
+
Postprocessed.new self
|
9
|
+
end
|
10
|
+
|
11
|
+
# TODO test this somehow
|
12
|
+
|
13
|
+
class Postprocessed < ChainBase
|
14
|
+
|
15
|
+
def filters_for(lbl, video:, audio:)
|
16
|
+
|
17
|
+
# Postprocessing
|
18
|
+
|
19
|
+
lbl_aux = "pp#{lbl}"
|
20
|
+
super(lbl_aux, video: video, audio: audio) +
|
21
|
+
[
|
22
|
+
*((video && channel?(:video))? Filter.pp("#{lbl_aux}:v", "#{lbl}:v"): nil),
|
23
|
+
*((audio && channel?(:audio))? Filter.anull("#{lbl_aux}:a", "#{lbl}:a"): nil)
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
class Process
|
4
|
+
|
5
|
+
class Input
|
6
|
+
|
7
|
+
def reverse
|
8
|
+
Reversed.new self
|
9
|
+
end
|
10
|
+
|
11
|
+
class Reversed < ChainBase
|
12
|
+
|
13
|
+
# TODO check this is reasonable and not a (live) stream...
|
14
|
+
|
15
|
+
def filters_for(lbl, video:, audio:)
|
16
|
+
|
17
|
+
# Reversing
|
18
|
+
|
19
|
+
lbl_aux = "rv#{lbl}"
|
20
|
+
super(lbl_aux, video: video, audio: audio) +
|
21
|
+
[
|
22
|
+
*((video && channel?(:video))? Filter.reverse("#{lbl_aux}:v", "#{lbl}:v"): nil),
|
23
|
+
*((audio && channel?(:audio))? Filter.areverse("#{lbl_aux}:a", "#{lbl}:a"): nil)
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ffmprb/process/input.rb
CHANGED
@@ -7,14 +7,15 @@ module Ffmprb
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def resolve(io)
|
10
|
-
return io unless
|
10
|
+
return io unless
|
11
|
+
io.is_a? String
|
11
12
|
|
12
|
-
File.
|
13
|
+
File.access(io).tap do |file|
|
13
14
|
Ffmprb.logger.warn "Input file does no exist (#{file.path}), will probably fail" unless file.exist?
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
#
|
18
|
+
# TODO! check for unknown options
|
18
19
|
|
19
20
|
def video_args(video=nil)
|
20
21
|
video = Process.input_video_options.merge(video.to_h)
|
@@ -110,4 +111,7 @@ require_relative 'input/cropped'
|
|
110
111
|
require_relative 'input/cut'
|
111
112
|
require_relative 'input/looping'
|
112
113
|
require_relative 'input/loud'
|
114
|
+
require_relative 'input/paced'
|
115
|
+
require_relative 'input/postprocessed'
|
116
|
+
require_relative 'input/reversed'
|
113
117
|
require_relative 'input/temp'
|
@@ -6,14 +6,15 @@ module Ffmprb
|
|
6
6
|
|
7
7
|
class << self
|
8
8
|
|
9
|
-
# XXX check for unknown options
|
10
|
-
|
11
9
|
def video_args(video=nil)
|
12
10
|
video = Process.output_video_options.merge(video.to_h)
|
13
11
|
[].tap do |args|
|
14
|
-
encoder =
|
15
|
-
|
16
|
-
|
12
|
+
if (encoder = video.delete(:encoder)) # NOTE extra encoder options possible
|
13
|
+
args.concat "-c:v #{encoder}".split(' ')
|
14
|
+
end
|
15
|
+
if (pixel_format = video.delete(:pixel_format))
|
16
|
+
args.concat %W[-pix_fmt #{pixel_format}]
|
17
|
+
end
|
17
18
|
video.delete :resolution # NOTE is handled otherwise
|
18
19
|
video.delete :fps # NOTE is handled otherwise
|
19
20
|
Util.assert_options_empty! video
|
@@ -23,15 +24,19 @@ module Ffmprb
|
|
23
24
|
def audio_args(audio=nil)
|
24
25
|
audio = Process.output_audio_options.merge(audio.to_h)
|
25
26
|
[].tap do |args|
|
26
|
-
encoder =
|
27
|
-
|
28
|
-
|
27
|
+
if (encoder = audio.delete(:encoder)) # NOTE extra encoder options possible
|
28
|
+
args.concat "-c:a #{encoder}".split(' ')
|
29
|
+
end
|
30
|
+
if (sampling_freq = audio.delete(:sampling_freq))
|
31
|
+
args.concat %W[-ar #{sampling_freq}]
|
32
|
+
end
|
29
33
|
Util.assert_options_empty! audio
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
37
|
def resolve(io)
|
34
|
-
return io unless
|
38
|
+
return io unless
|
39
|
+
io.is_a? String
|
35
40
|
|
36
41
|
File.create(io).tap do |file|
|
37
42
|
Ffmprb.logger.warn "Output file exists (#{file.path}), will probably overwrite" if file.exist?
|
@@ -57,11 +62,15 @@ module Ffmprb
|
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
60
|
-
#
|
65
|
+
# TODO This method is exceptionally long at the moment. This is not too grand.
|
61
66
|
# However, structuring the code should be undertaken with care, as not to harm the composition clarity.
|
62
67
|
def filters
|
63
|
-
fail Error, "Nothing to roll..." unless
|
64
|
-
|
68
|
+
fail Error, "Nothing to roll..." unless
|
69
|
+
@reels
|
70
|
+
fail Error, "Supporting just full_screen for now, sorry." unless
|
71
|
+
@reels.all? &:full_screen?
|
72
|
+
fail Error, "Supporting just a known output FPS" unless
|
73
|
+
!channel(:video) || (video_fps = channel(:video).fps)
|
65
74
|
return @filters if @filters
|
66
75
|
|
67
76
|
idx = process.output_index(self)
|
@@ -80,13 +89,18 @@ module Ffmprb
|
|
80
89
|
# NOTE mapping input to this lbl
|
81
90
|
|
82
91
|
lbl = "o#{idx}rl#{i}"
|
92
|
+
lbl_aux = "t#{lbl}"
|
83
93
|
|
84
94
|
# NOTE Image-Padding to match the target resolution
|
85
95
|
# TODO full screen only at the moment (see exception above)
|
86
96
|
|
87
|
-
Ffmprb.logger.debug
|
97
|
+
Ffmprb.logger.debug{"#{self} asking for filters of #{curr_reel.reel.io.inspect} video: #{channel(:video)}, audio: #{channel(:audio)}"}
|
88
98
|
@filters.concat(
|
89
|
-
|
99
|
+
[
|
100
|
+
*curr_reel.reel.filters_for(lbl_aux, video: channel(:video), audio: channel(:audio)),
|
101
|
+
*(channel?(:video)? Filter.interpolate_v(video_fps, "#{lbl_aux}:v", "#{lbl}:v"): nil),
|
102
|
+
*(channel?(:audio)? Filter.anull("#{lbl_aux}:a", "#{lbl}:a"): nil)
|
103
|
+
]
|
90
104
|
)
|
91
105
|
end
|
92
106
|
|
@@ -103,7 +117,7 @@ module Ffmprb
|
|
103
117
|
# NOTE generously padding the previous segment to support for all the cases
|
104
118
|
@filters.concat(
|
105
119
|
Filter.blank_source trim_prev_at + transition_length,
|
106
|
-
channel(:video).resolution,
|
120
|
+
channel(:video).resolution, video_fps, "#{lbl_pad}:v"
|
107
121
|
) if channel?(:video)
|
108
122
|
@filters.concat(
|
109
123
|
Filter.silent_source trim_prev_at + transition_length, "#{lbl_pad}:a"
|
@@ -151,7 +165,7 @@ module Ffmprb
|
|
151
165
|
) if channel?(:audio)
|
152
166
|
|
153
167
|
segments << new_prev_lbl
|
154
|
-
Ffmprb.logger.debug
|
168
|
+
Ffmprb.logger.debug{"Concatting segments: #{new_prev_lbl} pushed"}
|
155
169
|
end
|
156
170
|
|
157
171
|
if curr_reel.transition
|
@@ -164,7 +178,7 @@ module Ffmprb
|
|
164
178
|
if !lbl # no reel
|
165
179
|
lbl_aux = "o#{idx}bk#{i}"
|
166
180
|
@filters.concat(
|
167
|
-
Filter.blank_source transition_length, channel(:video).resolution,
|
181
|
+
Filter.blank_source transition_length, channel(:video).resolution, video_fps, "#{lbl_aux}:v"
|
168
182
|
) if channel?(:video)
|
169
183
|
@filters.concat(
|
170
184
|
Filter.silent_source transition_length, "#{lbl_aux}:a"
|
@@ -180,7 +194,7 @@ module Ffmprb
|
|
180
194
|
|
181
195
|
# TODO the only supported transition, see #*lay
|
182
196
|
@filters.concat(
|
183
|
-
Filter.blend_v transition_length, channel(:video).resolution,
|
197
|
+
Filter.blend_v transition_length, channel(:video).resolution, video_fps, ["#{lbl_end1}:v", "#{lbl || lbl_aux}:v"], "#{lbl_reel}:v"
|
184
198
|
) if channel?(:video)
|
185
199
|
@filters.concat(
|
186
200
|
Filter.blend_a transition_length, ["#{lbl_end1}:a", "#{lbl || lbl_aux}:a"], "#{lbl_reel}:a"
|
@@ -238,7 +252,7 @@ module Ffmprb
|
|
238
252
|
|
239
253
|
# NOTE multi-process overlays last
|
240
254
|
|
241
|
-
@channel_lbl_ios = {} #
|
255
|
+
@channel_lbl_ios = {} # TODO this is a spaghetti machine
|
242
256
|
@channel_lbl_ios["#{lbl_out}:v"] = io if channel?(:video)
|
243
257
|
@channel_lbl_ios["#{lbl_out}:a"] = io if channel?(:audio)
|
244
258
|
|
@@ -252,7 +266,7 @@ module Ffmprb
|
|
252
266
|
Ffmprb.logger.info "ATTENTION: ducking audio (due to the absence of a simple ffmpeg filter) does not support streaming main input. yet."
|
253
267
|
|
254
268
|
# So ducking just audio here, ye?
|
255
|
-
#
|
269
|
+
# TODO! check if we're on audio channel
|
256
270
|
|
257
271
|
main_av_o = @channel_lbl_ios["#{lbl_out}:a"]
|
258
272
|
fail Error, "Main output does not contain audio to duck" unless main_av_o
|
@@ -260,11 +274,11 @@ module Ffmprb
|
|
260
274
|
intermediate_extname = Process.intermediate_channel_extname video: main_av_o.channel?(:video), audio: main_av_o.channel?(:audio)
|
261
275
|
main_av_inter_i, main_av_inter_o = File.threaded_buffered_fifo(intermediate_extname, reader_open_on_writer_idle_limit: Util::ThreadedIoBuffer.timeout * 2, proc_vis: process)
|
262
276
|
@channel_lbl_ios.each do |channel_lbl, io|
|
263
|
-
@channel_lbl_ios[channel_lbl] = main_av_inter_i if io == main_av_o #
|
277
|
+
@channel_lbl_ios[channel_lbl] = main_av_inter_i if io == main_av_o # TODO ~~~spaghetti
|
264
278
|
end
|
265
279
|
process.proc_vis_edge process, main_av_o, :remove
|
266
280
|
process.proc_vis_edge process, main_av_inter_i
|
267
|
-
Ffmprb.logger.debug
|
281
|
+
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
282
|
|
269
283
|
over_a_i, over_a_o = File.threaded_buffered_fifo(Process.intermediate_channel_extname(audio: true, video: false), proc_vis: process)
|
270
284
|
lbl_over = "o#{idx}l#{i}"
|
@@ -273,26 +287,28 @@ module Ffmprb
|
|
273
287
|
)
|
274
288
|
@channel_lbl_ios["#{lbl_over}:a"] = over_a_i
|
275
289
|
process.proc_vis_edge process, over_a_i
|
276
|
-
Ffmprb.logger.debug
|
290
|
+
Ffmprb.logger.debug{"Routed and buffering auxiliary output fifos (#{over_a_i.path}>#{over_a_o.path}) for overlay"}
|
277
291
|
|
278
292
|
inter_i, inter_o = File.threaded_buffered_fifo(intermediate_extname, proc_vis: process)
|
279
|
-
Ffmprb.logger.debug
|
293
|
+
Ffmprb.logger.debug{"Allocated fifos to buffer media (#{inter_i.path}>#{inter_o.path}) while finding silence"}
|
280
294
|
|
281
|
-
ignore_broken_pipes_was = process.ignore_broken_pipes #
|
295
|
+
ignore_broken_pipes_was = process.ignore_broken_pipes # TODO? maybe throw an exception instead?
|
282
296
|
process.ignore_broken_pipes = true # NOTE audio ducking process may break the overlay pipe
|
283
297
|
|
284
298
|
Util::Thread.new "audio ducking" do
|
285
|
-
process.proc_vis_edge main_av_inter_o, inter_i #
|
299
|
+
process.proc_vis_edge main_av_inter_o, inter_i # TODO mark it better
|
286
300
|
silence = Ffmprb.find_silence(main_av_inter_o, inter_i)
|
287
301
|
|
288
|
-
Ffmprb.logger.debug
|
302
|
+
Ffmprb.logger.debug{
|
303
|
+
silence_map = silence.map{|s| "#{s.start_at}-#{s.end_at}"}
|
304
|
+
"Audio ducking with silence: [#{silence_map.join ', '}]"
|
305
|
+
}
|
289
306
|
|
290
307
|
Process.duck_audio inter_o, over_a_o, silence, main_av_o,
|
291
308
|
process_options: {parent: process, ignore_broken_pipes: ignore_broken_pipes_was, timeout: process.timeout},
|
292
309
|
video: channel(:video), audio: channel(:audio)
|
293
310
|
end
|
294
311
|
end
|
295
|
-
|
296
312
|
end
|
297
313
|
|
298
314
|
@filters
|
@@ -302,7 +318,7 @@ module Ffmprb
|
|
302
318
|
fail Error, "Must generate filters first." unless @channel_lbl_ios
|
303
319
|
|
304
320
|
[].tap do |args|
|
305
|
-
io_channel_lbls = {} #
|
321
|
+
io_channel_lbls = {} # TODO ~~~spaghetti
|
306
322
|
@channel_lbl_ios.each do |channel_lbl, io|
|
307
323
|
(io_channel_lbls[io] ||= []) << channel_lbl
|
308
324
|
end
|
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?
|
@@ -183,7 +185,7 @@ module Ffmprb
|
|
183
185
|
# NOTE must run first
|
184
186
|
def filter_args
|
185
187
|
@filter_args ||= Filter.complex_args(
|
186
|
-
@outputs.map(&:filters).reduce
|
188
|
+
@outputs.map(&:filters).reduce :+
|
187
189
|
)
|
188
190
|
end
|
189
191
|
|
@@ -193,11 +195,8 @@ module Ffmprb
|
|
193
195
|
end
|
194
196
|
|
195
197
|
def channel_params(value, default)
|
196
|
-
|
197
|
-
|
198
|
-
elsif value != false
|
199
|
-
{}
|
200
|
-
end
|
198
|
+
default.merge(value.respond_to?(:to_h)? value.to_h : {}) unless
|
199
|
+
value == false
|
201
200
|
end
|
202
201
|
end
|
203
202
|
|
data/lib/ffmprb/util/proc_vis.rb
CHANGED
@@ -51,7 +51,8 @@ module Ffmprb
|
|
51
51
|
|
52
52
|
def proc_vis_node(obj, op=:upsert)
|
53
53
|
return unless proc_vis_init?
|
54
|
-
fail Error, "Must be a #{Node.name}" unless
|
54
|
+
fail Error, "Must be a #{Node.name}" unless
|
55
|
+
obj.kind_of? Node
|
55
56
|
|
56
57
|
obj._proc_vis = self
|
57
58
|
obj.proc_vis_name.tap do |lbl|
|
@@ -63,7 +64,7 @@ module Ffmprb
|
|
63
64
|
@_proc_vis_nodes[obj] = lbl
|
64
65
|
end
|
65
66
|
end
|
66
|
-
proc_vis_update #
|
67
|
+
proc_vis_update # TODO optimise
|
67
68
|
end
|
68
69
|
end
|
69
70
|
|
@@ -115,7 +116,7 @@ module Ffmprb
|
|
115
116
|
prev_t = Time.now
|
116
117
|
while @_proc_vis_upq.deq # NOTE currently, runs forever (nil terminator needed)
|
117
118
|
proc_vis_do_update
|
118
|
-
Thread.current.live! #
|
119
|
+
Thread.current.live! # TODO? not the best we can do here
|
119
120
|
while Time.now - prev_t < UPDATE_PERIOD_SEC
|
120
121
|
@_proc_vis_upq.deq # NOTE drains the queue
|
121
122
|
end
|
@@ -137,7 +138,7 @@ module Ffmprb
|
|
137
138
|
@proc_vis_firebase_client =
|
138
139
|
if proc_vis_firebase
|
139
140
|
url = "https://#{proc_vis_firebase}.firebaseio.com/proc/"
|
140
|
-
Ffmprb.logger.debug
|
141
|
+
Ffmprb.logger.debug{"Connecting to #{url}"}
|
141
142
|
begin
|
142
143
|
Firebase::Client.new(url).tap do
|
143
144
|
Ffmprb.logger.info "Connected to #{url}"
|
data/lib/ffmprb/util/thread.rb
CHANGED
@@ -25,7 +25,7 @@ module Ffmprb
|
|
25
25
|
end
|
26
26
|
rescue Timeout::Error
|
27
27
|
if timeouts > 2 * logged_timeouts
|
28
|
-
Ffmprb.logger.info "A little bit of timeout #{log.respond_to?(:call)? log.call : log} (##{timeouts})"
|
28
|
+
Ffmprb.logger.info "A little bit of timeout #{log.respond_to?(:call)? log.call : log} (##{timeouts}x#{timeout})"
|
29
29
|
logged_timeouts = timeouts
|
30
30
|
end
|
31
31
|
current.live!
|
@@ -40,34 +40,39 @@ module Ffmprb
|
|
40
40
|
end
|
41
41
|
|
42
42
|
attr_reader :name
|
43
|
+
attr_reader :backtrace
|
43
44
|
|
44
45
|
def initialize(name="some", main: false, &blk)
|
46
|
+
orig_caller = caller
|
45
47
|
@name = name
|
46
48
|
@parent = Thread.current
|
47
49
|
@live_children = []
|
48
50
|
@children_mon = Monitor.new
|
49
51
|
@dead_children_q = Queue.new
|
50
|
-
|
52
|
+
@backtrace = (@parent.respond_to?(:backtrace)? @parent.backtrace : []) + caller
|
53
|
+
Ffmprb.logger.debug{"about to launch #{'main ' if main}#{name}"}
|
51
54
|
sync_q = Queue.new
|
52
55
|
super() do
|
56
|
+
self.report_on_exception = false
|
53
57
|
@parent.proc_vis_node self if @parent.respond_to? :proc_vis_node
|
54
58
|
if @parent.respond_to? :child_lives
|
55
59
|
@parent.child_lives self
|
60
|
+
Ffmprb.logger.warn "Not the main: false thread run by a #{self.class.name} thread" if main
|
56
61
|
else
|
57
62
|
Ffmprb.logger.warn "Not the main: true thread run by a not #{self.class.name} thread" unless main
|
58
63
|
end
|
59
64
|
sync_q.enq :ok
|
60
|
-
Ffmprb.logger.debug
|
65
|
+
Ffmprb.logger.debug{"#{name} thread launched"}
|
61
66
|
begin
|
62
67
|
blk.call.tap do
|
63
|
-
Ffmprb.logger.debug
|
68
|
+
Ffmprb.logger.debug{"#{name} thread done"}
|
64
69
|
end
|
65
70
|
rescue Exception
|
66
|
-
Ffmprb.logger.warn "#{$!.class.name} raised in #{name} thread: #{$!.message}\nBacktrace:\n\t#{$!.backtrace.join("\n\t")}"
|
71
|
+
Ffmprb.logger.warn "#{$!.class.name} raised in #{name} thread: #{$!.message}\nBacktrace:\n\t#{($!.backtrace + backtrace ).join("\n\t")}"
|
67
72
|
cause = $!
|
68
73
|
Ffmprb.logger.warn "...caused by #{cause.class.name}: #{cause.message}\nBacktrace:\n\t#{cause.backtrace.join("\n\t")}" while
|
69
74
|
cause = cause.cause
|
70
|
-
|
75
|
+
raise $! # TODO? I have no idea why I need to give it `$!` -- the docs say I need not
|
71
76
|
ensure
|
72
77
|
@parent.child_dies self if @parent.respond_to? :child_dies
|
73
78
|
@parent.proc_vis_node self, :remove if @parent.respond_to? :proc_vis_node
|
@@ -84,7 +89,7 @@ module Ffmprb
|
|
84
89
|
|
85
90
|
def child_lives(thr)
|
86
91
|
@children_mon.synchronize do
|
87
|
-
Ffmprb.logger.debug
|
92
|
+
Ffmprb.logger.debug{"picking up #{thr.name} thread"}
|
88
93
|
@live_children << thr
|
89
94
|
end
|
90
95
|
proc_vis_edge self, thr
|
@@ -92,7 +97,7 @@ module Ffmprb
|
|
92
97
|
|
93
98
|
def child_dies(thr)
|
94
99
|
@children_mon.synchronize do
|
95
|
-
Ffmprb.logger.debug
|
100
|
+
Ffmprb.logger.debug{"releasing #{thr.name} thread"}
|
96
101
|
@dead_children_q.enq thr
|
97
102
|
fail "System Error" unless @live_children.delete thr
|
98
103
|
end
|
@@ -110,9 +115,6 @@ module Ffmprb
|
|
110
115
|
fail "System Error" unless thr.join(timeout) # NOTE should not block
|
111
116
|
end
|
112
117
|
end
|
113
|
-
|
114
118
|
end
|
115
|
-
|
116
119
|
end
|
117
|
-
|
118
120
|
end
|