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
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 = ["
|
9
|
+
spec.authors = ["Costa Shapiro"]
|
10
10
|
spec.email = ['costa@mouldwarp.com']
|
11
11
|
|
12
|
-
spec.summary = "ffmprb is your audio/video
|
12
|
+
spec.summary = "ffmprb is your audio/video manipulation pal, based on https://ffmpeg.org"
|
13
13
|
spec.description = "A video and audio composing DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends"
|
14
14
|
spec.homepage = Ffmprb::GEM_GITHUB_URL
|
15
15
|
|
16
|
-
|
16
|
+
def self.ignore_match(pattern, path)
|
17
|
+
path = '/' + path unless path =~ %r'^/'
|
18
|
+
if File.directory?(path)
|
19
|
+
path += '/' unless path =~ %r'/$'
|
20
|
+
end
|
21
|
+
if pattern.is_a?(Array)
|
22
|
+
pattern
|
23
|
+
else
|
24
|
+
[pattern]
|
25
|
+
end.any? do |pattern|
|
26
|
+
pattern = '**/' + pattern unless pattern =~ %r'^/'
|
27
|
+
pattern += '**' if pattern =~ %r'/$'
|
28
|
+
File.fnmatch(pattern, path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
pattern =
|
33
|
+
if (ignore_pattern_lines = File.readlines('.gemignore') rescue nil)
|
34
|
+
ignore_pattern_lines.map do |line|
|
35
|
+
stripped = line.split(/(?<!\\)#/)[0].strip
|
36
|
+
stripped unless stripped.empty?
|
37
|
+
end.compact
|
38
|
+
else
|
39
|
+
%w[test/ spec/ features/]
|
40
|
+
end
|
41
|
+
|
42
|
+
# NOTE dotfiles are ignored by .glob
|
43
|
+
spec.files = Dir['**/*'].reject{|path| ignore_match pattern, path}
|
44
|
+
# TODO:rm spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
45
|
+
|
17
46
|
spec.bindir = 'exe'
|
18
47
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
48
|
spec.require_paths = ['lib']
|
@@ -23,15 +52,6 @@ Gem::Specification.new do |spec|
|
|
23
52
|
# NOTE make it into an optional dependency? Nah for now
|
24
53
|
spec.add_dependency 'thor', '~> 0.19.1'
|
25
54
|
|
26
|
-
spec.
|
27
|
-
|
28
|
-
spec.add_development_dependency 'simplecov', '>= 0.11.2'
|
29
|
-
spec.add_development_dependency 'guard-rspec', '>= 4.6.5'
|
30
|
-
spec.add_development_dependency 'guard-bundler', '>= 2.1.0'
|
31
|
-
spec.add_development_dependency 'rake', '>= 11.1.2'
|
32
|
-
spec.add_development_dependency 'rmagick', '>= 2.15.4'
|
33
|
-
spec.add_development_dependency 'ruby-sox', '>= 0.0.3'
|
34
|
-
spec.add_development_dependency 'firebase', '>= 0.2.6'
|
35
|
-
|
36
|
-
spec.post_install_message = "Have fun with your montage! To enable proc visualisation, install firebase gem and set FFMPRB_PROC_VIS_FIREBASE env." unless Ffmprb::FIREBASE_AVAILABLE
|
55
|
+
spec.post_install_message = "Have fun with your a/v! To enable proc visualisation, install firebase gem and set FFMPRB_PROC_VIS_FIREBASE env." unless
|
56
|
+
Ffmprb::FIREBASE_AVAILABLE
|
37
57
|
end
|
data/lib/defaults.rb
CHANGED
@@ -19,7 +19,7 @@ module Ffmprb
|
|
19
19
|
Process.input_video_fps = nil # NOTE the documented ffmpeg default is 25
|
20
20
|
|
21
21
|
Process.output_video_resolution = CGA
|
22
|
-
Process.output_video_fps = 16
|
22
|
+
Process.output_video_fps = 16 # NOTE the documented ffmpeg default is 25
|
23
23
|
Process.output_audio_encoder = 'libmp3lame'
|
24
24
|
Process.output_audio_sampling_freq = nil # NOTE Use ffmpeg default by default, specify otherwise e.g. 44100
|
25
25
|
|
@@ -40,16 +40,16 @@ module Ffmprb
|
|
40
40
|
|
41
41
|
# NOTE http://12factor.net etc
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
50
|
+
self.proc_vis_firebase = proc_vis_firebase
|
51
51
|
elsif proc_vis_firebase
|
52
|
-
|
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
|
data/lib/ffmprb/execution.rb
CHANGED
data/lib/ffmprb/file/sample.rb
CHANGED
@@ -11,7 +11,7 @@ module Ffmprb
|
|
11
11
|
audio = File.temp('.wav') if audio == true
|
12
12
|
video = File.temp('.png') if video == true
|
13
13
|
|
14
|
-
Ffmprb.logger.debug
|
14
|
+
Ffmprb.logger.debug{"Snap shooting files, video path: #{video ? video.path : 'NONE'}, audio path: #{audio ? audio.path : 'NONE'}"}
|
15
15
|
|
16
16
|
fail Error, "Incorrect output extname (must be image)" unless !video || video.channel?(:video) && !video.channel?(:audio)
|
17
17
|
fail Error, "Incorrect audio extname (must be sound)" unless !audio || audio.channel?(:audio) && !audio.channel?(:video)
|
@@ -30,7 +30,7 @@ module Ffmprb
|
|
30
30
|
begin
|
31
31
|
video.unlink if video
|
32
32
|
audio.unlink if audio
|
33
|
-
Ffmprb.logger.debug
|
33
|
+
Ffmprb.logger.debug{"Removed sample files"}
|
34
34
|
rescue
|
35
35
|
Ffmprb.logger.warn "#{$!.class.name} removing sample files: #{$!.message}"
|
36
36
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Ffmprb
|
2
2
|
|
3
|
-
class File
|
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
|
10
|
+
Ffmprb.logger.debug{"Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"}
|
11
11
|
Util::Thread.new do
|
12
12
|
begin
|
13
13
|
io_buff = Util::ThreadedIoBuffer.new(opener(input_fifo_file, 'r'), opener(output_fifo_file, 'w'), keep_outputs_open_on_input_idle_limit: reader_open_on_writer_idle_limit)
|
@@ -20,13 +20,13 @@ module Ffmprb
|
|
20
20
|
ensure
|
21
21
|
Util::Thread.join_children!
|
22
22
|
end
|
23
|
-
Ffmprb.logger.debug
|
23
|
+
Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"}
|
24
24
|
ensure
|
25
25
|
input_fifo_file.unlink if input_fifo_file
|
26
26
|
output_fifo_file.unlink if output_fifo_file
|
27
27
|
end
|
28
28
|
end
|
29
|
-
Ffmprb.logger.debug
|
29
|
+
Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"}
|
30
30
|
|
31
31
|
[input_fifo_file, output_fifo_file]
|
32
32
|
end
|
data/lib/ffmprb/file.rb
CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
|
|
4
4
|
|
5
5
|
module Ffmprb
|
6
6
|
|
7
|
-
class File
|
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
|
21
|
-
::File.open
|
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
|
27
|
+
Ffmprb.logger.debug{"Created file with path: #{file.path}"}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def access(path)
|
32
32
|
new(path: path, mode: :read).tap do |file|
|
33
|
-
Ffmprb.logger.debug
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
77
|
+
join Dir.tmpdir, "#{rand(2**222)}p#{extname}"
|
78
78
|
end
|
79
79
|
|
80
80
|
def image?(extname)
|
81
|
-
!!(extname =~
|
81
|
+
!!(extname =~ image_extname_regex)
|
82
82
|
end
|
83
83
|
|
84
84
|
def sound?(extname)
|
85
|
-
!!(extname =~
|
85
|
+
!!(extname =~ sound_extname_regex)
|
86
86
|
end
|
87
87
|
|
88
88
|
def movie?(extname)
|
89
|
-
!!(extname =~
|
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
|
-
|
115
|
+
File.exist? path
|
116
116
|
end
|
117
117
|
|
118
118
|
def basename
|
119
|
-
@basename ||=
|
119
|
+
@basename ||= File.basename(path)
|
120
120
|
end
|
121
121
|
|
122
122
|
def extname
|
123
|
-
@extname ||=
|
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
|
-
|
159
|
+
File.read path
|
160
160
|
end
|
161
161
|
def write(s)
|
162
|
-
|
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
|
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
|
12
|
+
inout 'alphamerge', inputs, output
|
13
13
|
end
|
14
14
|
|
15
15
|
def afade_in(duration, input=nil, output=nil)
|
16
|
-
inout
|
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
|
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(
|
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
|
41
|
+
inout 'anull', input, output
|
42
42
|
end
|
43
43
|
|
44
44
|
def anullsink(input=nil)
|
45
|
-
inout
|
45
|
+
inout 'anullsink', input, nil
|
46
46
|
end
|
47
47
|
|
48
48
|
def asplit(inputs=nil, outputs=nil)
|
49
|
-
inout
|
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
|
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
|
62
|
-
|
63
|
-
|
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
|
67
|
-
|
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
|
71
|
-
inout
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
195
|
+
inout 'nullsink', input, nil
|
143
196
|
end
|
144
197
|
|
145
198
|
def overlay(x=0, y=0, inputs=nil, output=nil)
|
146
|
-
inout
|
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(
|
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
|
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(
|
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
|
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
|
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
|
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
|
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
|
-
[
|
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
|
data/lib/ffmprb/find_silence.rb
CHANGED
@@ -5,7 +5,7 @@ module Ffmprb
|
|
5
5
|
# NOTE not for streaming just yet
|
6
6
|
def find_silence(input_file, output_file)
|
7
7
|
path = "#{input_file.path}->#{output_file.path}"
|
8
|
-
logger.debug
|
8
|
+
logger.debug{"Finding silence (#{path})"}
|
9
9
|
silence = []
|
10
10
|
Util.ffmpeg('-i', input_file.path, *find_silence_detect_args, output_file.path).
|
11
11
|
scan(SILENCE_DETECT_REGEX).each do |mark, time|
|
@@ -25,7 +25,10 @@ module Ffmprb
|
|
25
25
|
Ffmprb.warn "Unknown silence mark: #{mark}"
|
26
26
|
end
|
27
27
|
end
|
28
|
-
logger.debug
|
28
|
+
logger.debug{
|
29
|
+
silence_map = silence.map{|t,v| "#{t}: #{v}"}
|
30
|
+
"Found silence (#{path}): [#{silence_map}]"
|
31
|
+
}
|
29
32
|
silence
|
30
33
|
end
|
31
34
|
|
@@ -14,16 +14,18 @@ module Ffmprb
|
|
14
14
|
def unfiltered=(input); @io = input; end
|
15
15
|
|
16
16
|
|
17
|
-
def chain_copy(src_input) #
|
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
|
-
|
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
|