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
data/exp/zip2mp4k60 ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ gem 'ffmprb'
4
+ require 'ffmprb'
5
+
6
+ if ARGV.length != 1
7
+ warn "Usage: zip2mp4k60 <zip-file>"
8
+ exit 1
9
+ end
10
+ zip_path = File.expand_path(ARGV[0])
11
+ out_path = File.join(File.dirname(zip_path), "#{File.basename zip_path, '.*'}.mp4")
12
+ video_opts = {resolution: '3840x2160', fps: 60}
13
+
14
+
15
+ Dir.mktmpdir do |tmp_dir|
16
+ Dir.chdir tmp_dir do
17
+ # XXX? this doesn't work as ffmprb input because of the way process do... works
18
+ system "unzip '#{zip_path}'"
19
+ Ffmprb.process do
20
+ output out_path, video: video_opts do
21
+ Dir['*'].sort.each do |in_path|
22
+ roll input in_path
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
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 = ["showbox.com", "Costa Shapiro"]
9
+ spec.authors = ["Costa Shapiro"]
10
10
  spec.email = ['costa@mouldwarp.com']
11
11
 
12
- spec.summary = "ffmprb is your audio/video montage friend, based on https://ffmpeg.org"
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
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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.add_development_dependency 'bundler', '>= 1.11.2'
27
- spec.add_development_dependency 'byebug', '>= 8.2.4'
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
 
@@ -40,16 +40,16 @@ module Ffmprb
40
40
 
41
41
  # NOTE http://12factor.net etc
42
42
 
43
- Ffmprb.log_level = Logger::INFO
44
- Ffmprb.ffmpeg_debug = ENV.fetch('FFMPRB_FFMPEG_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
45
- Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
43
+ self.log_level = Logger::INFO
44
+ self.ffmpeg_debug = ENV.fetch('FFMPRB_FFMPEG_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
45
+ self.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
46
46
 
47
47
  proc_vis_firebase = ENV['FFMPRB_PROC_VIS_FIREBASE']
48
48
  if Ffmprb::FIREBASE_AVAILABLE
49
49
  fail Error, "Please provide just the name of the firebase in FFMPRB_PROC_VIS_FIREBASE (e.g. my-proc-vis-io for https://my-proc-vis-io.firebaseio.com/proc/)" if proc_vis_firebase =~ /\//
50
- Ffmprb.proc_vis_firebase = proc_vis_firebase
50
+ self.proc_vis_firebase = proc_vis_firebase
51
51
  elsif proc_vis_firebase
52
- Ffmprb.logger.warn "Firebase unavailable (have firebase gem installed or unset FFMPRB_PROC_VIS_FIREBASE to get rid of this warning)"
52
+ logger.warn "Firebase unavailable (have firebase gem installed or unset FFMPRB_PROC_VIS_FIREBASE to get rid of this warning)"
53
53
  end
54
54
 
55
55
  end
@@ -25,7 +25,7 @@ module Ffmprb
25
25
  else
26
26
  Logger::WARN
27
27
  end
28
- Ffmprb.process *ios, ignore_broken_pipes: false, &script
28
+ Ffmprb.process *ios, &script
29
29
  end
30
30
 
31
31
  # NOTE a hack from http://stackoverflow.com/a/23955971/714287
@@ -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 "Snap shooting files, video path: #{video ? video.path : 'NONE'}, audio path: #{audio ? audio.path : 'NONE'}"
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 "Removed sample files"
33
+ Ffmprb.logger.debug{"Removed sample files"}
34
34
  rescue
35
35
  Ffmprb.logger.warn "#{$!.class.name} removing sample files: #{$!.message}"
36
36
  end
@@ -1,13 +1,13 @@
1
1
  module Ffmprb
2
2
 
3
- class File # NOTE I would rather rename it to Stream at the moment
3
+ class File
4
4
 
5
5
  class << self
6
6
 
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 "Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"
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 "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"
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 "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"
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/file.rb CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
4
4
 
5
5
  module Ffmprb
6
6
 
7
- class File # NOTE I would rather rename it to Stream at the moment
7
+ class File < ::File
8
8
  include Util::ProcVis::Node
9
9
 
10
10
  class << self
@@ -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 "Trying to open #{path} (for #{mode}-buffering or something)"
21
- ::File.open(path, mode)
20
+ Ffmprb.logger.debug{"Trying to open #{path} (for #{mode}-buffering or something)"}
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 "Created file with path: #{file.path}"
27
+ Ffmprb.logger.debug{"Created file with path: #{file.path}"}
28
28
  end
29
29
  end
30
30
 
31
- def open(path)
31
+ def access(path)
32
32
  new(path: path, mode: :read).tap do |file|
33
- Ffmprb.logger.debug "Opened file with path: #{file.path}"
33
+ Ffmprb.logger.debug{"Accessed 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 "Created temp file with path: #{path}"
40
+ Ffmprb.logger.debug{"Created temp file with path: #{path}"}
41
41
 
42
42
  return file unless block_given?
43
43
 
@@ -49,13 +49,13 @@ 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 "Removed temp file with path: #{path}"
52
+ Ffmprb.logger.debug{"Removed temp file with path: #{path}"}
53
53
  end
54
54
  end
55
55
 
56
56
  def temp_fifo(extname='.tmp', &blk)
57
57
  path = temp_fifo_path(extname)
58
- ::File.mkfifo path
58
+ mkfifo path
59
59
  fifo_file = create(path)
60
60
 
61
61
  return fifo_file unless block_given?
@@ -69,24 +69,24 @@ 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 "Removed temp file with path: #{path}"
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, Dir::Tmpname.make_tmpname('', 'p' + extname)
77
+ join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
78
78
  end
79
79
 
80
80
  def image?(extname)
81
- !!(extname =~ File.image_extname_regex)
81
+ !!(extname =~ image_extname_regex)
82
82
  end
83
83
 
84
84
  def sound?(extname)
85
- !!(extname =~ File.sound_extname_regex)
85
+ !!(extname =~ sound_extname_regex)
86
86
  end
87
87
 
88
88
  def movie?(extname)
89
- !!(extname =~ File.movie_extname_regex)
89
+ !!(extname =~ movie_extname_regex)
90
90
  end
91
91
 
92
92
  end
@@ -112,15 +112,15 @@ module Ffmprb
112
112
  # Info
113
113
 
114
114
  def exist?
115
- ::File.exist? path
115
+ File.exist? path
116
116
  end
117
117
 
118
118
  def basename
119
- @basename ||= ::File.basename(path)
119
+ @basename ||= File.basename(path)
120
120
  end
121
121
 
122
122
  def extname
123
- @extname ||= ::File.extname(path)
123
+ @extname ||= File.extname(path)
124
124
  end
125
125
 
126
126
  def channel?(medium)
@@ -156,10 +156,10 @@ module Ffmprb
156
156
  # Manipulation
157
157
 
158
158
  def read
159
- ::File.read path
159
+ File.read path
160
160
  end
161
161
  def write(s)
162
- ::File.write path, s
162
+ File.write path, s
163
163
  end
164
164
 
165
165
  def unlink
@@ -168,7 +168,7 @@ module Ffmprb
168
168
  else
169
169
  FileUtils.remove_entry path
170
170
  end
171
- Ffmprb.logger.debug "Removed file with path: #{path}"
171
+ Ffmprb.logger.debug{"Removed file with path: #{path}"}
172
172
  @path = nil
173
173
  end
174
174
 
data/lib/ffmprb/filter.rb CHANGED
@@ -9,15 +9,15 @@ module Ffmprb
9
9
  attr_accessor :silence_noise_max_db
10
10
 
11
11
  def alphamerge(inputs, output=nil)
12
- inout "alphamerge", inputs, output
12
+ inout 'alphamerge', inputs, output
13
13
  end
14
14
 
15
15
  def afade_in(duration, input=nil, output=nil)
16
- inout "afade=in:d=%{duration}:curve=hsin", input, output, duration: duration
16
+ inout 'afade=in:d=%{duration}:curve=hsin', input, output, duration: duration
17
17
  end
18
18
 
19
19
  def afade_out(duration, input=nil, output=nil)
20
- inout "afade=out:d=%{duration}:curve=hsin", input, output, duration: duration
20
+ inout 'afade=out:d=%{duration}:curve=hsin', input, output, duration: duration
21
21
  end
22
22
 
23
23
  def amix_to_first_same_volume(inputs, output=nil)
@@ -33,24 +33,45 @@ module Ffmprb
33
33
  end
34
34
  end
35
35
  filters +
36
- inout("amix=%{inputs_count}:duration=shortest:dropout_transition=0, volume=%{inputs_count}",
36
+ inout('amix=%{inputs_count}:duration=shortest:dropout_transition=0, volume=%{inputs_count}',
37
37
  new_inputs, output, inputs_count: (inputs.empty?? nil : inputs.size))
38
38
  end
39
39
 
40
40
  def anull(input=nil, output=nil)
41
- inout "anull", input, output
41
+ inout 'anull', input, output
42
42
  end
43
43
 
44
44
  def anullsink(input=nil)
45
- inout "anullsink", input, nil
45
+ inout 'anullsink', input, nil
46
46
  end
47
47
 
48
48
  def asplit(inputs=nil, outputs=nil)
49
- inout "asplit", inputs, outputs
49
+ inout 'asplit', inputs, outputs
50
+ end
51
+
52
+ def areverse(input=nil, output=nil)
53
+ inout 'areverse', input, output
54
+ end
55
+
56
+ def atempo(tempo, input=nil, output=nil)
57
+ fail Error, "Push the tempo!" unless
58
+ tempo > 0
59
+
60
+ fil = ''
61
+ tmp = tempo
62
+ while tmp > 2.0
63
+ fil += 'atempo=2.0, '
64
+ tmp /= 2.0
65
+ end
66
+ while tmp < 0.5
67
+ fil += 'atempo=0.5, '
68
+ tmp /= 0.5
69
+ end
70
+ inout "#{fil}atempo=#{tmp.to_f}", input, output
50
71
  end
51
72
 
52
73
  def atrim(st, en=nil, input=nil, output=nil)
53
- inout "atrim=%{start_end}, asetpts=PTS-STARTPTS", input, output,
74
+ inout 'atrim=%{start_end}, asetpts=PTS-STARTPTS', input, output,
54
75
  start_end: [st, en].compact.join(':')
55
76
  end
56
77
 
@@ -58,45 +79,64 @@ module Ffmprb
58
79
  color_source '0x000000@0', duration, resolution, fps, output
59
80
  end
60
81
 
61
- def color_source(color, duration, resolution, fps, output=nil)
62
- inout "color=%{color}:d=%{duration}:s=%{resolution}:r=%{fps}", nil, output,
63
- color: color, duration: duration, resolution: resolution, fps: fps
82
+ def blend_a(duration, inputs, output=nil)
83
+ fail Error, "must be given 2 inputs" unless inputs.size == 2
84
+
85
+ aux_lbl = "blnd#{inputs[0]}"
86
+ auxx_lbl = "x#{aux_lbl}"
87
+ [
88
+ *afade_out(duration, inputs[0], aux_lbl),
89
+ *afade_in(duration, inputs[1], auxx_lbl),
90
+ *amix_to_first_same_volume([auxx_lbl, aux_lbl], output)
91
+ ]
64
92
  end
65
93
 
66
- def fade_out_alpha(duration, input=nil, output=nil)
67
- inout "fade=out:d=%{duration}:alpha=1", input, output, duration: duration
94
+ def blend_v(duration, resolution, fps, inputs, output=nil)
95
+ fail Error, "must be given 2 inputs" unless inputs.size == 2
96
+
97
+ aux_lbl = "blnd#{inputs[0]}"
98
+ auxx_lbl = "x#{aux_lbl}"
99
+ [
100
+ *white_source(duration, resolution, fps, aux_lbl),
101
+ *inout([
102
+ *alphamerge([inputs[0], aux_lbl]),
103
+ *fade_out_alpha(duration)
104
+ ].join(', '), nil, auxx_lbl),
105
+ *overlay(0, 0, [inputs[1], auxx_lbl], output),
106
+ ]
68
107
  end
69
108
 
70
- def fps(fps, input=nil, output=nil)
71
- inout "fps=fps=%{fps}", input, output, fps: fps
109
+ def color_source(color, duration, resolution, fps, output=nil)
110
+ inout 'color=%{color}:d=%{duration}:s=%{resolution}:r=%{fps}', nil, output,
111
+ color: color, duration: duration, resolution: resolution, fps: fps
72
112
  end
73
113
 
74
114
  def concat_v(inputs, output=nil)
75
115
  return copy(inputs, output) if inputs.size == 1
76
- inout "concat=#{inputs.size}:v=1:a=0", inputs, output
116
+ inout 'concat=%{count}:v=1:a=0', inputs, output, count: inputs.size
77
117
  end
78
118
 
79
119
  def concat_a(inputs, output=nil)
80
120
  return anull(inputs, output) if inputs.size == 1
81
- inout "concat=#{inputs.size}:v=0:a=1", inputs, output
121
+ inout 'concat=%{count}:v=0:a=1', inputs, output, count: inputs.size
82
122
  end
83
123
 
84
124
  def concat_av(inputs, output=nil)
85
125
  fail Error, "must be given an even number of inputs" unless inputs.size.even?
86
- inout "concat=#{inputs.size/2}:v=1:a=1", inputs, output
126
+ inout 'concat=%{count}:v=1:a=1', inputs, output, count: inputs.size/2
87
127
  end
88
128
 
89
129
  def copy(input=nil, output=nil)
90
- inout "copy", input, output
130
+ inout 'copy', input, output
91
131
  end
92
132
 
93
133
  # TODO unused at the moment
94
134
  def crop(crop, input=nil, output=nil)
95
- inout "crop=x=%{left}:y=%{top}:w=%{width}:h=%{height}", input, output, crop
135
+ inout 'crop=x=%{left}:y=%{top}:w=%{width}:h=%{height}', input, output, crop
96
136
  end
97
137
 
98
138
  def crop_prop(crop, input=nil, output=nil)
99
- inout "crop=%{crop_exp}", input, output,
139
+ inout 'crop=%{crop_exp}', input, output,
100
140
  crop_exp: crop_prop_exps(crop).join(':')
101
141
  end
102
142
 
@@ -136,33 +176,58 @@ module Ffmprb
136
176
  exps
137
177
  end
138
178
 
179
+ def fade_out_alpha(duration, input=nil, output=nil)
180
+ inout 'fade=out:d=%{duration}:alpha=1', input, output, duration: duration
181
+ end
182
+
183
+ def fps(fps, input=nil, output=nil)
184
+ inout 'fps=fps=%{fps}', input, output, fps: fps
185
+ end
186
+
187
+ def interpolate_v(fps, input=nil, output=nil)
188
+ inout 'framerate=fps=%{fps}', input, output, fps: fps
189
+ end
190
+ # TODO other effects like... minterpolate=fps=%{fps}:mi_mode=mci:mc_mode=aobmc:vsbmc=1
191
+
139
192
  # NOTE might be very useful with UGC: def cropdetect
140
193
 
141
194
  def nullsink(input=nil)
142
- inout "nullsink", input, nil
195
+ inout 'nullsink', input, nil
143
196
  end
144
197
 
145
198
  def overlay(x=0, y=0, inputs=nil, output=nil)
146
- inout "overlay=x=%{x}:y=%{y}:eof_action=pass", inputs, output, x: x, y: y
199
+ inout 'overlay=x=%{x}:y=%{y}:eof_action=pass', inputs, output, x: x, y: y
147
200
  end
148
201
 
149
202
  def pad(resolution, input=nil, output=nil)
150
203
  width, height = resolution.to_s.split('x')
151
204
  inout [
152
- inout("pad=%{width}:%{height}:(%{width}-iw*min(%{width}/iw\\,%{height}/ih))/2:(%{height}-ih*min(%{width}/iw\\,%{height}/ih))/2",
205
+ inout('pad=%{width}:%{height}:(%{width}-iw*min(%{width}/iw\\,%{height}/ih))/2:(%{height}-ih*min(%{width}/iw\\,%{height}/ih))/2',
153
206
  width: width, height: height),
154
207
  *setsar(1) # NOTE the scale & pad formulae damage SAR a little, unfortunately
155
208
  ].join(', '), input, output
156
209
  end
157
210
 
211
+ def pp(input=nil, output=nil)
212
+ inout 'pp=hb/vb/dr/al', input, output
213
+ end
214
+
215
+ def reverse(input=nil, output=nil)
216
+ inout 'reverse', input, output
217
+ end
218
+
158
219
  def setsar(ratio, input=nil, output=nil)
159
- inout "setsar=%{ratio}", input, output, ratio: ratio
220
+ inout 'setsar=%{ratio}', input, output, ratio: ratio
221
+ end
222
+
223
+ def setpts(ratio, input=nil, output=nil)
224
+ inout 'setpts=%{r_fps}*PTS', input, output, r_fps: 1.0/ratio
160
225
  end
161
226
 
162
227
  def scale(resolution, input=nil, output=nil)
163
228
  width, height = resolution.to_s.split('x')
164
229
  inout [
165
- inout("scale=iw*min(%{width}/iw\\,%{height}/ih):ih*min(%{width}/iw\\,%{height}/ih)", width: width, height: height),
230
+ inout('scale=iw*min(%{width}/iw\\,%{height}/ih):ih*min(%{width}/iw\\,%{height}/ih)', width: width, height: height),
166
231
  *setsar(1) # NOTE the scale & pad formulae damage SAR a little, unfortunately
167
232
  ].join(', '), input, output
168
233
  end
@@ -182,49 +247,22 @@ module Ffmprb
182
247
  end
183
248
 
184
249
  def silencedetect(input=nil, output=nil)
185
- inout "silencedetect=d=1:n=%{silence_noise_max_db}dB", input, output,
250
+ inout 'silencedetect=d=1:n=%{silence_noise_max_db}dB', input, output,
186
251
  silence_noise_max_db: silence_noise_max_db
187
252
  end
188
253
 
189
254
  def silent_source(duration, output=nil)
190
- inout "aevalsrc=0:d=%{duration}", nil, output, duration: duration
255
+ inout 'aevalsrc=0:d=%{duration}', nil, output, duration: duration
191
256
  end
192
257
 
193
258
  # NOTE might be very useful with transitions: def smartblur
194
259
 
195
260
  def split(inputs=nil, outputs=nil)
196
- inout "split", inputs, outputs
197
- end
198
-
199
- def blend_v(duration, resolution, fps, inputs, output=nil)
200
- fail Error, "must be given 2 inputs" unless inputs.size == 2
201
-
202
- aux_lbl = "blnd#{inputs[0]}"
203
- auxx_lbl = "x#{aux_lbl}"
204
- [
205
- *white_source(duration, resolution, fps, aux_lbl),
206
- *inout([
207
- *alphamerge([inputs[0], aux_lbl]),
208
- *fade_out_alpha(duration)
209
- ].join(', '), nil, auxx_lbl),
210
- *overlay(0, 0, [inputs[1], auxx_lbl], output),
211
- ]
212
- end
213
-
214
- def blend_a(duration, inputs, output=nil)
215
- fail Error, "must be given 2 inputs" unless inputs.size == 2
216
-
217
- aux_lbl = "blnd#{inputs[0]}"
218
- auxx_lbl = "x#{aux_lbl}"
219
- [
220
- *afade_out(duration, inputs[0], aux_lbl),
221
- *afade_in(duration, inputs[1], auxx_lbl),
222
- *amix_to_first_same_volume([auxx_lbl, aux_lbl], output)
223
- ]
261
+ inout 'split', inputs, outputs
224
262
  end
225
263
 
226
264
  def trim(st, en=nil, input=nil, output=nil)
227
- inout "trim=%{start_end}, setpts=PTS-STARTPTS", input, output,
265
+ inout 'trim=%{start_end}, setpts=PTS-STARTPTS', input, output,
228
266
  start_end: [st, en].compact.join(':')
229
267
  end
230
268
 
@@ -261,8 +299,12 @@ module Ffmprb
261
299
  color_source '0xFFFFFF@1', duration, resolution, fps, output
262
300
  end
263
301
 
302
+
264
303
  def complex_args(*filters)
265
- ['-filter_complex', filters.join('; ')] unless filters.empty?
304
+ [].tap do |args|
305
+ args << '-filter_complex' << filters.join('; ') unless
306
+ filters.empty?
307
+ end
266
308
  end
267
309
 
268
310
  private
@@ -276,9 +318,6 @@ module Ffmprb
276
318
  filter = filter + " #{[*outputs].map{|s| "[#{s}]"}.join ' '}" if outputs
277
319
  [filter]
278
320
  end
279
-
280
321
  end
281
-
282
322
  end
283
-
284
323
  end
@@ -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 "Finding silence (#{path})"
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 "Found silence (#{path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
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
 
@@ -14,16 +14,18 @@ module Ffmprb
14
14
  def unfiltered=(input); @io = input; end
15
15
 
16
16
 
17
- def chain_copy(src_input) # XXX SPEC ME
17
+ def chain_copy(src_input) # TODO SPEC ME
18
18
  dup.tap do |top|
19
19
  top.unfiltered = unfiltered.chain_copy(src_input)
20
20
  end
21
21
  end
22
22
 
23
- end
23
+ def filters_for(lbl, video:, audio:)
24
24
 
25
+ # Doing nothing
26
+ unfiltered.filters_for lbl, video: video, audio: audio
27
+ end
28
+ end
25
29
  end
26
-
27
30
  end
28
-
29
31
  end
@@ -25,7 +25,7 @@ module Ffmprb
25
25
 
26
26
  def filters_for(lbl, video:, audio:)
27
27
 
28
- # Doing nothing
28
+ # Doing basically nothing
29
29
 
30
30
  unfiltered.filters_for lbl,
31
31
  video: channel?(:video) && video, audio: channel?(:audio) && audio