ffmprb 0.11.3 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +5 -5
  2. data/Dockerfile +18 -0
  3. data/Gemfile +8 -1
  4. data/Gemfile.lock +121 -0
  5. data/README.md +65 -21
  6. data/TODO.md +0 -0
  7. data/bin/dev +12 -0
  8. data/bin/test +13 -0
  9. data/coverage/assets/0.10.0/application.css +799 -0
  10. data/coverage/assets/0.10.0/application.js +1707 -0
  11. data/coverage/assets/0.10.0/colorbox/border.png +0 -0
  12. data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  13. data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  14. data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  15. data/coverage/assets/0.10.0/favicon_green.png +0 -0
  16. data/coverage/assets/0.10.0/favicon_red.png +0 -0
  17. data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  18. data/coverage/assets/0.10.0/loading.gif +0 -0
  19. data/coverage/assets/0.10.0/magnify.png +0 -0
  20. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  21. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  22. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  23. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  24. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  25. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  26. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  27. data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  28. data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  29. data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  30. data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  31. data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  32. data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  33. data/coverage/assets/0.10.2/application.css +799 -0
  34. data/coverage/assets/0.10.2/application.js +1707 -0
  35. data/coverage/assets/0.10.2/colorbox/border.png +0 -0
  36. data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
  37. data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
  38. data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
  39. data/coverage/assets/0.10.2/favicon_green.png +0 -0
  40. data/coverage/assets/0.10.2/favicon_red.png +0 -0
  41. data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
  42. data/coverage/assets/0.10.2/loading.gif +0 -0
  43. data/coverage/assets/0.10.2/magnify.png +0 -0
  44. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  45. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  46. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  47. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  48. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  49. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  50. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  51. data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  52. data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
  53. data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  54. data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
  55. data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
  56. data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  57. data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png +0 -0
  58. data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  59. data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png +0 -0
  60. data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png +0 -0
  61. data/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  62. data/coverage/assets/0.12.2/application.css +1 -0
  63. data/coverage/assets/0.12.2/application.js +7 -0
  64. data/coverage/assets/0.12.2/colorbox/border.png +0 -0
  65. data/coverage/assets/0.12.2/colorbox/controls.png +0 -0
  66. data/coverage/assets/0.12.2/colorbox/loading.gif +0 -0
  67. data/coverage/assets/0.12.2/colorbox/loading_background.png +0 -0
  68. data/coverage/assets/0.12.2/favicon_green.png +0 -0
  69. data/coverage/assets/0.12.2/favicon_red.png +0 -0
  70. data/coverage/assets/0.12.2/favicon_yellow.png +0 -0
  71. data/coverage/assets/0.12.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  72. data/coverage/assets/0.12.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  73. data/coverage/assets/0.12.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  74. data/coverage/assets/0.12.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  75. data/coverage/assets/0.12.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  76. data/coverage/assets/0.12.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  77. data/coverage/assets/0.12.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  78. data/coverage/assets/0.12.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  79. data/coverage/assets/0.12.2/images/ui-icons_222222_256x240.png +0 -0
  80. data/coverage/assets/0.12.2/images/ui-icons_2e83ff_256x240.png +0 -0
  81. data/coverage/assets/0.12.2/images/ui-icons_454545_256x240.png +0 -0
  82. data/coverage/assets/0.12.2/images/ui-icons_888888_256x240.png +0 -0
  83. data/coverage/assets/0.12.2/images/ui-icons_cd0a0a_256x240.png +0 -0
  84. data/coverage/assets/0.12.2/loading.gif +0 -0
  85. data/coverage/assets/0.12.2/magnify.png +0 -0
  86. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
  87. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  88. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
  89. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
  90. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  91. data/coverage/assets/0.12.3/application.css +1 -0
  92. data/coverage/assets/0.12.3/application.js +7 -0
  93. data/coverage/assets/0.12.3/colorbox/border.png +0 -0
  94. data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
  95. data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
  96. data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
  97. data/coverage/assets/0.12.3/favicon_green.png +0 -0
  98. data/coverage/assets/0.12.3/favicon_red.png +0 -0
  99. data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
  100. data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  101. data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  102. data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  103. data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  104. data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  105. data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  106. data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  107. data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  108. data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
  109. data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
  110. data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
  111. data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
  112. data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
  113. data/coverage/assets/0.12.3/loading.gif +0 -0
  114. data/coverage/assets/0.12.3/magnify.png +0 -0
  115. data/coverage/index.html +47324 -0
  116. data/exp/EXP +7 -0
  117. data/exp/av-cut-mp4you60.ffmprb +10 -0
  118. data/exp/docker-compose.yml +9 -0
  119. data/exp/gop-cut-cat-you60 +141 -0
  120. data/exp/present/Dockerfile +13 -0
  121. data/exp/present/Gemfile +3 -0
  122. data/exp/present/Gemfile.lock +22 -0
  123. data/exp/present/bin/up-deps +8 -0
  124. data/exp/present/docker-compose.yml +21 -0
  125. data/exp/present/exp/present +10 -0
  126. data/exp/present/exp/present.rb +37 -0
  127. data/exp/run +9 -0
  128. data/exp/stitch2.ffmprb +5 -0
  129. data/exp/unzip-mp4you60 +58 -0
  130. data/exp/youtubby/Dockerfile +13 -0
  131. data/exp/youtubby/Gemfile +7 -0
  132. data/exp/youtubby/Gemfile.lock +73 -0
  133. data/exp/youtubby/README.md +21 -0
  134. data/exp/youtubby/bin/up-deps +8 -0
  135. data/exp/youtubby/docker-compose.yml +20 -0
  136. data/exp/youtubby/exp/gop-raw-cut-you-HD60 +13 -0
  137. data/exp/youtubby/exp/gop-raw-cut-you-HD60.rb +230 -0
  138. data/exp/youtubby/exp/media-upload +13 -0
  139. data/exp/youtubby/exp/tmp/CURRENT +2 -0
  140. data/exp/youtubby/google_youtube.rb +39 -0
  141. data/exp/youtubby/old-ul.py +181 -0
  142. data/exp/youtubby/old-ul.rb +87 -0
  143. data/exp/youtubby/py-Dockerfile +11 -0
  144. data/exp/youtubby/py-docker-compose.yml +13 -0
  145. data/exp/youtubby/requirements.txt +19 -0
  146. data/exp/youtubby/upload.rb +38 -0
  147. data/exp/zip-cut-mp4you60 +42 -0
  148. data/exp/zip2mp4k60 +27 -0
  149. data/ffmprb.gemspec +34 -14
  150. data/lib/defaults.rb +6 -6
  151. data/lib/ffmprb/execution.rb +1 -1
  152. data/lib/ffmprb/file/sample.rb +2 -2
  153. data/lib/ffmprb/file/threaded_buffered.rb +4 -4
  154. data/lib/ffmprb/file.rb +20 -20
  155. data/lib/ffmprb/filter.rb +100 -61
  156. data/lib/ffmprb/find_silence.rb +5 -2
  157. data/lib/ffmprb/process/input/chain_base.rb +6 -4
  158. data/lib/ffmprb/process/input/channeled.rb +1 -1
  159. data/lib/ffmprb/process/input/cropped.rb +4 -1
  160. data/lib/ffmprb/process/input/cut.rb +2 -2
  161. data/lib/ffmprb/process/input/looping.rb +11 -16
  162. data/lib/ffmprb/process/input/loud.rb +1 -1
  163. data/lib/ffmprb/process/input/paced.rb +35 -0
  164. data/lib/ffmprb/process/input/postprocessed.rb +29 -0
  165. data/lib/ffmprb/process/input/reversed.rb +29 -0
  166. data/lib/ffmprb/process/input.rb +7 -3
  167. data/lib/ffmprb/process/output.rb +45 -29
  168. data/lib/ffmprb/process.rb +8 -9
  169. data/lib/ffmprb/util/proc_vis.rb +5 -4
  170. data/lib/ffmprb/util/thread.rb +13 -11
  171. data/lib/ffmprb/util/threaded_io_buffer.rb +25 -22
  172. data/lib/ffmprb/util.rb +38 -13
  173. data/lib/ffmprb/version.rb +2 -2
  174. data/lib/ffmprb.rb +8 -5
  175. data/tmp/exp/docker-compose.yml +9 -0
  176. data/tmp/exp/src/SAM_3132.MP4 +0 -0
  177. data/tmp/ffmprb-0.11.4.gem +0 -0
  178. data/tmp/output.rb +383 -0
  179. metadata +159 -139
  180. data/.gitignore +0 -10
  181. data/.rspec +0 -4
  182. data/.ruby-version +0 -1
  183. data/.travis.yml +0 -3
  184. 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
- unfiltered.filters_for(lbl_aux, video: unsize(video), audio: audio) +
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
- unfiltered.filters_for(lbl_aux, video: video, audio: audio) +
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
- output(src_io, video: nil, audio: nil) do # XXX this is not properly tested, unfortunately
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 "(L2) Temporising the raw input (#{src_io.path}) and creating copy (#{cpy_io.path})"
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
- # Ffmprb.logger.debug "Processing before looping"
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 "Preprocessed #{dst_io.path} will be teed to #{buff_ios.map(&:path).join '; '}"
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 file a complaint at #{Ffmprb::GEM_GITHUB_URL}/issues please)." if times == Util.ffmpeg_inputs_max && io_buff.stats.blocks_buff == 0
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 "Looping #{buff_ios.size} times"
100
+ Ffmprb.logger.debug{"Looping #{buff_ios.size} times"}
106
101
 
107
- Ffmprb.logger.debug "(L4) Looping (#{buff_ios.map &:path}) into (#{aux_io.path})"
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: nil, audio: nil) do
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 "(L1) Creating a new input (#{aux_io.path}) to the process"
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
- unfiltered.filters_for(lbl_aux, video: video, audio: audio) +
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
@@ -7,14 +7,15 @@ module Ffmprb
7
7
  class << self
8
8
 
9
9
  def resolve(io)
10
- return io unless io.is_a? String # XXX XXX
10
+ return io unless
11
+ io.is_a? String
11
12
 
12
- File.open(io).tap do |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
- # XXX check for unknown options
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 = pixel_format = nil # NOTE ah, ruby
15
- args.concat %W[-c:v #{encoder}] if (encoder = video.delete(:encoder))
16
- args.concat %W[-pix_fmt #{pixel_format}] if (pixel_format = video.delete(:pixel_format))
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 = nil
27
- args.concat %W[-c:a #{encoder}] if (encoder = audio.delete(:encoder))
28
- args.concat %W[-ar #{sampling_freq}] if (sampling_freq = audio.delete(:sampling_freq))
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 io.is_a? String # XXX XXX
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
- # XXX This method is exceptionally long at the moment. This is not too grand.
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 @reels
64
- fail Error, "Supporting just full_screen for now, sorry." unless @reels.all?(&:full_screen?)
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 "#{self} asking for filters of #{curr_reel.reel.io.inspect} video: #{channel(:video)}, audio: #{channel(:audio)}"
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
- curr_reel.reel.filters_for lbl, video: channel(:video), audio: channel(:audio)
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, channel(:video).fps, "#{lbl_pad}:v"
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 "Concatting segments: #{new_prev_lbl} pushed"
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, channel(:video).fps, "#{lbl_aux}:v"
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, channel(:video).fps, ["#{lbl_end1}:v", "#{lbl || lbl_aux}:v"], "#{lbl_reel}:v"
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 = {} # XXX this is a spaghetti machine
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
- # XXX check if we're on audio channel
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 # XXX ~~~spaghetti
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 "Re-routed the main audio output (#{main_av_inter_i.path}->...->#{main_av_o.path}) through the process of audio ducking"
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 "Routed and buffering auxiliary output fifos (#{over_a_i.path}>#{over_a_o.path}) for overlay"
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 "Allocated fifos to buffer media (#{inter_i.path}>#{inter_o.path}) while finding silence"
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 # XXX maybe throw an exception instead?
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 # XXX mark it better
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 "Audio ducking with silence: [#{silence.map{|s| "#{s.start_at}-#{s.end_at}"}.join ', '}]"
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 = {} # XXX ~~~spaghetti
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
@@ -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 "Ducking audio with volumes: {#{ducked_overlay_volume.map{|t,v| "#{t}: #{v}"}.join ', '}}"
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
- if value
197
- default.merge(value == true ? {} : value.to_h)
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
 
@@ -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 obj.kind_of? Node # XXX duck typing FTW
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 # XXX optimise
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! # XXX not the best we can do here
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 "Connecting to #{url}"
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}"
@@ -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
- Ffmprb.logger.debug "about to launch #{name}"
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 "#{name} thread launched"
65
+ Ffmprb.logger.debug{"#{name} thread launched"}
61
66
  begin
62
67
  blk.call.tap do
63
- Ffmprb.logger.debug "#{name} thread done"
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
- fail $! # XXX I have no idea why I need to give it `$!` -- the docs say I need not
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 "picking up #{thr.name} thread"
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 "releasing #{thr.name} thread"
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