ffmprb 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3808779bc8553727f43a738d9401a25f367bb4c5
4
- data.tar.gz: 8e99c02d956e9a0ffb452dbe8d1b714642e8fd78
3
+ metadata.gz: afd76dfa3efb28bdc494678ca4e04ff6eb02abb6
4
+ data.tar.gz: ec23bc1bc45197f7850570317adbc040014120a5
5
5
  SHA512:
6
- metadata.gz: 09d7b19987ead902b04d9e2d0209d069c20a551cb2c0f2725c8cc385208df930bb95b6df5aed1247f182f3582956d2f5bb0f0c51f847a47b419942e4716f603d
7
- data.tar.gz: 29cc7f3e457084b62daedcda67619d653968a21f0f7b4015310ce5c1012e84d23a75c59a68c107a3097cf3fdf1ac9db6acbb4825acae24129acc011917886eaa
6
+ metadata.gz: 18639c467cfd887e03972453e6ce3ac3a6801101d4052a50bbdb8f739be15f0c9ebdc01e17ba0e6ff33288f2ce854a3ce32c45c87ef77599e8921498a8d536a6
7
+ data.tar.gz: dfb7c6405791d6766a31773a3d5bf759f27a3aaeafb0feccc42889d41d60cdbce64c6305330004e9741eae4ee8000b6b7e1bdf89446db4cc59fe2210c597b650
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /.*_history
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.2.1
1
+ ruby-2.2.4
data/.travis.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.1
3
+ - 2.2.4
data/Guardfile CHANGED
@@ -10,3 +10,15 @@ guard :rspec,
10
10
  watch('spec/spec_helper.rb') { 'spec' }
11
11
  watch('.rspec') { 'spec' }
12
12
  end
13
+
14
+ guard :bundler do
15
+ require 'guard/bundler'
16
+ require 'guard/bundler/verify'
17
+ helper = Guard::Bundler::Verify.new
18
+
19
+ files = ['Gemfile']
20
+ files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
21
+
22
+ # Assume files are symlinked from somewhere
23
+ files.each { |file| watch(helper.real_path(file)) }
24
+ end
data/README.md CHANGED
@@ -5,33 +5,27 @@
5
5
 
6
6
  A DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends.
7
7
 
8
- Allows for code like
8
+ Allows for scripts like
9
9
  ```ruby
10
- Ffmprb.process('flick.mp4', 'track.wav', 'cine.flv') do |av_input1, a_input1, av_output1|
11
-
12
- in_main = input(av_input1)
13
- in_sound = input(a_input1)
14
- output(av_output1, video: {resolution: Ffmprb::HD_720p}) do
15
- roll in_main.cut(from: 2, to: 5).crop(0.25), transition: {blend: 1}
16
- roll in_main.cut(from: 6, to: 16).volume(2), after: 2, transition: {blend: 1}
17
- overlay in_sound.volume(0.8), duck: :audio
10
+ Ffmprb.process do
11
+
12
+ in_main = input('flick.mp4')
13
+ in_sound = input('track.mp3')
14
+ output 'cine.flv', video: {resolution: '1280x720'} do
15
+ roll in_main.crop(0.25).cut(from: 2, to: 5), transition: {blend: 1}
16
+ roll in_main.volume(2).cut(from: 6, to: 16), after: 2, transition: {blend: 1}
17
+ overlay in_sound.volume(0.8)
18
18
  end
19
19
 
20
20
  end
21
21
  ```
22
- and saves you from the horrors of (the native ffmpeg equivalent)
22
+ and saves you from the horror of...
23
23
  ```
24
- ffmpeg -y -i flick.mp4 -i track.wav -filter_complex "[0:v] copy [tmcpsp0:v]; [0:a] anull [tmcpsp0:a]; [tmcpsp0:v] trim=2:5, setpts=PTS-STARTPTS [cpsp0:v]; [tmcpsp0:a] atrim=2:5, asetpts=PTS-STARTPTS [cpsp0:a]; [cpsp0:v] crop=x=in_w*0.25:y=in_h*0.25:w=in_w*0.5:h=in_h*0.5 [sp0:v]; [cpsp0:a] anull [sp0:a]; [sp0:v] scale=iw*min(1280/iw\,720/ih):ih*min(1280/iw\,720/ih), pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2, fps=fps=30 [rl0:v]; [sp0:a] anull [rl0:a]; color=black:d=1.0:s=1280x720:r=30 [bl0:v]; aevalsrc=0:d=1.0 [bl0:a]; [bl0:v] trim=0:1.0, setpts=PTS-STARTPTS [tm0b:v]; [bl0:a] atrim=0:1.0, asetpts=PTS-STARTPTS [tm0b:a]; color=white:d=1.0:s=1280x720:r=30 [rn70152323222540:v]; [tm0b:v] [rn70152323222540:v] alphamerge, fade=out:d=1.0:alpha=1 [xrn70152323222540:v]; [rl0:v] [xrn70152323222540:v] overlay=x=0:y=0:eof_action=pass [tn0:v]; [tm0b:a] afade=out:d=1.0 [rn70152323222540:a]; [rl0:a] afade=in:d=1.0 [xrn70152323222540:a]; [xrn70152323222540:a] [rn70152323222540:a] amix=2:duration=first [tn0:a]; [0:v] copy [tmldsp1:v]; [0:a] anull [tmldsp1:a]; [tmldsp1:v] trim=6, setpts=PTS-STARTPTS [ldsp1:v]; [tmldsp1:a] atrim=6, asetpts=PTS-STARTPTS [ldsp1:a]; [ldsp1:v] copy [sp1:v]; [ldsp1:a] volume='2':eval=frame [sp1:a]; [sp1:v] scale=iw*min(1280/iw\,720/ih):ih*min(1280/iw\,720/ih), pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2, fps=fps=30 [rl1:v]; [sp1:a] anull [rl1:a]; color=black:d=3.0:s=1280x720:r=30 [bltn01:v]; aevalsrc=0:d=3.0 [bltn01:a]; [tn0:v] [bltn01:v] concat=2:v=1:a=0 [pdtn01:v]; [tn0:a] [bltn01:a] concat=2:v=0:a=1 [pdtn01:a]; [pdtn01:v] split [pdtn01a:v] [pdtn01b:v]; [pdtn01:a] asplit [pdtn01a:a] [pdtn01b:a]; [pdtn01a:v] trim=0:2, setpts=PTS-STARTPTS [tmtn01a:v]; [pdtn01a:a] atrim=0:2, asetpts=PTS-STARTPTS [tmtn01a:a]; [pdtn01b:v] trim=2:3.0, setpts=PTS-STARTPTS [tm1b:v]; [pdtn01b:a] atrim=2:3.0, asetpts=PTS-STARTPTS [tm1b:a]; color=white:d=1.0:s=1280x720:r=30 [rn70152323237760:v]; [tm1b:v] [rn70152323237760:v] alphamerge, fade=out:d=1.0:alpha=1 [xrn70152323237760:v]; [rl1:v] [xrn70152323237760:v] overlay=x=0:y=0:eof_action=pass [tn1:v]; [tm1b:a] afade=out:d=1.0 [rn70152323237760:a]; [rl1:a] afade=in:d=1.0 [xrn70152323237760:a]; [xrn70152323237760:a] [rn70152323237760:a] amix=2:duration=first [tn1:a]; color=black:d=11.0:s=1280x720:r=30 [bltn12:v]; aevalsrc=0:d=11.0 [bltn12:a]; [tn1:v] [bltn12:v] concat=2:v=1:a=0 [pdtn12:v]; [tn1:a] [bltn12:a] concat=2:v=0:a=1 [pdtn12:a]; [pdtn12:v] split [pdtn12a:v] [pdtn12b:v]; [pdtn12:a] asplit [pdtn12a:a] [pdtn12b:a]; [pdtn12a:v] trim=0:10, setpts=PTS-STARTPTS [tmtn12a:v]; [pdtn12a:a] atrim=0:10, asetpts=PTS-STARTPTS [tmtn12a:a]; color=black:d=1.0:s=1280x720:r=30 [bk2:v]; aevalsrc=0:d=1.0 [bk2:a]; [pdtn12b:v] trim=10:11.0, setpts=PTS-STARTPTS [tm2b:v]; [pdtn12b:a] atrim=10:11.0, asetpts=PTS-STARTPTS [tm2b:a]; color=white:d=1.0:s=1280x720:r=30 [rn70152323255640:v]; [tm2b:v] [rn70152323255640:v] alphamerge, fade=out:d=1.0:alpha=1 [xrn70152323255640:v]; [bk2:v] [xrn70152323255640:v] overlay=x=0:y=0:eof_action=pass [tn2:v]; [tm2b:a] afade=out:d=1.0 [rn70152323255640:a]; [bk2:a] afade=in:d=1.0 [xrn70152323255640:a]; [xrn70152323255640:a] [rn70152323255640:a] amix=2:duration=first [tn2:a]; [tmtn01a:v] [tmtn12a:v] [tn2:v] concat=3:v=1:a=0 [oo:v]; [tmtn01a:a] [tmtn12a:a] [tn2:a] concat=3:v=0:a=1 [oo:a]; [1:a] anull [ldol0:a]; [ldol0:a] volume='0.8':eval=frame [ol0:a]" -map "[oo:v]" -map "[oo:a]" /tmp/inter1a.flv -map "[ol0:a]" /tmp/inter1b.wav
24
+ ffmpeg -y -noautorotate -i flick.mp4 -i track.wav -filter_complex "[0:v] fps=fps=16 [cptmo0rl0:v]; [0:a] anull [cptmo0rl0:a]; [cptmo0rl0:v] crop=x=in_w*0.25:y=in_h*0.25:w=in_w*0.5:h=in_h*0.5 [tmptmo0rl0:v]; [tmptmo0rl0:v] scale=iw*min(1280/iw\,720/ih):ih*min(1280/iw\,720/ih), setsar=1, pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2, setsar=1 [tmo0rl0:v]; [cptmo0rl0:a] anull [tmo0rl0:a]; color=0x000000@0:d=3:s=1280x720:r=16 [blo0rl0:v]; [tmo0rl0:v] [blo0rl0:v] concat=2:v=1:a=0 [pdo0rl0:v]; [pdo0rl0:v] trim=2:5, setpts=PTS-STARTPTS [o0rl0:v]; aevalsrc=0:d=3 [blo0rl0:a]; [tmo0rl0:a] [blo0rl0:a] concat=2:v=0:a=1 [pdo0rl0:a]; [pdo0rl0:a] atrim=2:5, asetpts=PTS-STARTPTS [o0rl0:a]; color=0x000000@0:d=1.0:s=1280x720:r=16 [bl0:v]; aevalsrc=0:d=1.0 [bl0:a]; [bl0:v] trim=0:1.0, setpts=PTS-STARTPTS [o0tm0b:v]; [bl0:a] atrim=0:1.0, asetpts=PTS-STARTPTS [o0tm0b:a]; color=0xFFFFFF@1:d=1.0:s=1280x720:r=16 [blndo0tm0b:v]; [o0tm0b:v] [blndo0tm0b:v] alphamerge, fade=out:d=1.0:alpha=1 [xblndo0tm0b:v]; [o0rl0:v] [xblndo0tm0b:v] overlay=x=0:y=0:eof_action=pass [o0tn0:v]; [o0tm0b:a] afade=out:d=1.0:curve=hsin [blndo0tm0b:a]; [o0rl0:a] afade=in:d=1.0:curve=hsin [xblndo0tm0b:a]; [blndo0tm0b:a] apad [apdblndo0tm0b:a]; [xblndo0tm0b:a] [apdblndo0tm0b:a] amix=2:duration=shortest:dropout_transition=0, volume=2 [o0tn0:a]; [0:v] scale=iw*min(1280/iw\,720/ih):ih*min(1280/iw\,720/ih), setsar=1, pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2, setsar=1, fps=fps=16 [ldtmo0rl1:v]; [0:a] anull [ldtmo0rl1:a]; [ldtmo0rl1:v] copy [tmo0rl1:v]; [ldtmo0rl1:a] volume='2':eval=frame [tmo0rl1:a]; color=0x000000@0:d=10:s=1280x720:r=16 [blo0rl1:v]; [tmo0rl1:v] [blo0rl1:v] concat=2:v=1:a=0 [pdo0rl1:v]; [pdo0rl1:v] trim=6:16, setpts=PTS-STARTPTS [o0rl1:v]; aevalsrc=0:d=10 [blo0rl1:a]; [tmo0rl1:a] [blo0rl1:a] concat=2:v=0:a=1 [pdo0rl1:a]; [pdo0rl1:a] atrim=6:16, asetpts=PTS-STARTPTS [o0rl1:a]; color=0x000000@0:d=3.0:s=1280x720:r=16 [blo0tn01:v]; aevalsrc=0:d=3.0 [blo0tn01:a]; [o0tn0:v] [blo0tn01:v] concat=2:v=1:a=0 [pdo0tn01:v]; [o0tn0:a] [blo0tn01:a] concat=2:v=0:a=1 [pdo0tn01:a]; [pdo0tn01:v] split [pdo0tn01a:v] [pdo0tn01b:v]; [pdo0tn01:a] asplit [pdo0tn01a:a] [pdo0tn01b:a]; [pdo0tn01a:v] trim=0:2, setpts=PTS-STARTPTS [tmo0tn01a:v]; [pdo0tn01a:a] atrim=0:2, asetpts=PTS-STARTPTS [tmo0tn01a:a]; [pdo0tn01b:v] trim=2:3.0, setpts=PTS-STARTPTS [o0tm1b:v]; [pdo0tn01b:a] atrim=2:3.0, asetpts=PTS-STARTPTS [o0tm1b:a]; color=0xFFFFFF@1:d=1.0:s=1280x720:r=16 [blndo0tm1b:v]; [o0tm1b:v] [blndo0tm1b:v] alphamerge, fade=out:d=1.0:alpha=1 [xblndo0tm1b:v]; [o0rl1:v] [xblndo0tm1b:v] overlay=x=0:y=0:eof_action=pass [o0tn1:v]; [o0tm1b:a] afade=out:d=1.0:curve=hsin [blndo0tm1b:a]; [o0rl1:a] afade=in:d=1.0:curve=hsin [xblndo0tm1b:a]; [blndo0tm1b:a] apad [apdblndo0tm1b:a]; [xblndo0tm1b:a] [apdblndo0tm1b:a] amix=2:duration=shortest:dropout_transition=0, volume=2 [o0tn1:a]; [tmo0tn01a:v] [o0tn1:v] concat=2:v=1:a=0 [o0o:v]; [tmo0tn01a:a] [o0tn1:a] concat=2:v=0:a=1 [o0o:a]; [1:a] anull [ldo0l0:a]; [ldo0l0:a] volume='0.8':eval=frame [o0l0:a]; [o0o:v] copy [o0o0:v]; [o0l0:a] apad [apdo0l0:a]; [o0o:a] [apdo0l0:a] amix=2:duration=shortest:dropout_transition=0, volume=2 [o0o0:a]" -map "[o0o0:v]" -map "[o0o0:a]" -c:a libmp3lame cine.flv
25
25
  ```
26
- ```
27
- ffmpeg -y -i /tmp/inter1a.flv -filter_complex "silencedetect=d=2:n=-30dB" /tmp/inter2.flv
28
- ```
29
- ```
30
- ffmpeg -y -i /tmp/inter2.flv -i /tmp/inter1b.wav -filter_complex "[0:v] copy [sp0:v]; [0:a] anull [sp0:a]; [sp0:v] scale=iw*min(1280/iw\,720/ih):ih*min(1280/iw\,720/ih), pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2, fps=fps=30 [rl0:v]; [sp0:a] anull [rl0:a]; [rl0:v] concat=1:v=1:a=0 [oo:v]; [rl0:a] concat=1:v=0:a=1 [oo:a]; [1:a] anull [ldol0:a]; [ldol0:a] volume='if(between(t, 9.5, 10.5), (-0.8*t + 8.500000000000002)/1.0, if(between(t, 0.5, 9.5), 0.9, if(between(t, -0.5, 0.5), (0.8*t + 0.5)/1.0, if(between(t, 0.0, -0.5), 0.1, if(between(t, 0.0, 0.0), 0.1, 0.1)))))':eval=frame [ol0:a]; [oo:v] copy [oo0:v]; [oo:a] [ol0:a] amix=2:duration=first [oo0:a]" -map "[oo0:v]" -map "[oo0:a]" cine.flv
31
- ```
32
- Umm... That's the idea.
26
+ ...that's the idea, but there's much more to it.
33
27
  The docs, as well as any other part of this gem, are a work in progress.
34
- So you're very welcome to look around the [specs](https://github.com/showbox-oss/ffmprb/tree/master/spec) for the current functionality coverage.
28
+ So you're very welcome to look around the [specs](https://github.com/showbox-oss/ffmprb/tree/master/spec) for the actual functionality coverage.
35
29
 
36
30
  ## Installation
37
31
 
@@ -134,16 +128,20 @@ you must not share its objects between different threads.
134
128
  Inside a `process` block, there are input definitions and output definitions;
135
129
  naturally, the latter use the former:
136
130
  ```ruby
137
- Ffmprb.process('flick.mp4', 'film.flv') do |av_input1, av_output1|
131
+ Ffmprb.process do |av_input1, av_output1|
138
132
 
139
- in_main = input(av_input1)
140
- output(av_output1, video: {resolution: Ffmprb::HD_720p, fps: 25}) do
133
+ in_main = input('flick.mp4')
134
+ output('film.flv', video: {resolution: Ffmprb::HD_720p, fps: 25}) do
141
135
  roll in_main.crop(0.05), transition: {blend: 1}
142
136
  end
143
137
 
144
138
  end
145
139
  ```
146
140
 
141
+ ### ProcVis support (experimental)
142
+
143
+ To enable [ProcVis](https://procvis.io) support (source), define `FFMPRB_PROC_VIS_FIREBASE_URL=my-proc-vis-io` (replace with your Firebase instance) in your running environment and watch the log for `You may view your process visualised at: https://proc-vis-io.firebaseapp.com/?pid=70311657638000 (a sample ProcVis snapshot of a full specs run).
144
+
147
145
  ## Development
148
146
 
149
147
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -154,6 +152,11 @@ To release a new version, update the version number in `version.rb`, and then ru
154
152
  `bundle exec rake release` to create a git tag for the version, push git commits
155
153
  and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
156
154
 
155
+ ### Debug
156
+
157
+ To enable debug logging, define `FFMPRB_DEBUG=1` in the running environment.
158
+ To enable ffmpeg debug logging, `FFMPRB_FFMPEG_DEBUG=1`
159
+
157
160
  ### Threading policy
158
161
 
159
162
  Generally, avoid using threads, but not at any cost.
data/ffmprb.gemspec CHANGED
@@ -6,8 +6,8 @@ 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 @ Showbox"]
10
- spec.email = ['costa@showbox.com']
9
+ spec.authors = ["showbox.com", "Costa Shapiro"]
10
+ spec.email = ['costa@mouldwarp.com']
11
11
 
12
12
  spec.summary = "ffmprb is your audio/video montage friend, based on https://ffmpeg.org"
13
13
  spec.description = "A DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends"
@@ -19,13 +19,17 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  # NOTE I'm not happy with this dependency, and there's nothing crossplatform (= for windoze too) at the moment
22
- spec.add_dependency 'mkfifo'
22
+ spec.add_dependency 'mkfifo', '~> 0.1.1'
23
23
 
24
- spec.add_development_dependency 'bundler', '>= 1.9.9'
25
- spec.add_development_dependency 'byebug', '>= 4.0.5'
26
- spec.add_development_dependency 'guard-rspec', '>= 2.12.8'
27
- spec.add_development_dependency 'rake', '>= 10.4.2'
28
- spec.add_development_dependency 'rmagick', '>= 2.15'
29
- spec.add_development_dependency 'rspec', '>= 3.2.0'
24
+ spec.add_development_dependency 'bundler', '>= 1.11.2'
25
+ spec.add_development_dependency 'byebug', '>= 8.2.2'
26
+ spec.add_development_dependency 'simplecov', '>= 0.10.0'
27
+ spec.add_development_dependency 'guard-rspec', '>= 4.6.5'
28
+ spec.add_development_dependency 'guard-bundler', '>= 2.1.0'
29
+ spec.add_development_dependency 'rake', '>= 11.1.2'
30
+ spec.add_development_dependency 'rmagick', '>= 2.15.4'
30
31
  spec.add_development_dependency 'ruby-sox', '>= 0.0.3'
32
+ spec.add_development_dependency 'firebase', '>= 0.2.6'
33
+
34
+ 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
31
35
  end
data/lib/defaults.rb CHANGED
@@ -6,6 +6,7 @@ module Ffmprb
6
6
 
7
7
  Filter.silence_noise_max_db = -40
8
8
 
9
+ # NOTE ducking is currently not for streams
9
10
  Process.duck_audio_silent_min = 3
10
11
  Process.duck_audio_transition_length = 1
11
12
  Process.duck_audio_transition_in_start = -0.4
@@ -14,20 +15,39 @@ module Ffmprb
14
15
  Process.duck_audio_volume_lo = 0.1
15
16
  Process.timeout = 30
16
17
 
17
- Process.input_options = {noautorotate: true}
18
+ Process.input_video_auto_rotate = false
19
+ Process.input_video_fps = nil # NOTE the documented ffmpeg default is 25
18
20
 
19
21
  Process.output_video_resolution = CGA
20
22
  Process.output_video_fps = 16
21
23
  Process.output_audio_encoder = 'libmp3lame'
22
24
 
25
+ Util.cmd_timeout = 30
23
26
  Util.ffmpeg_cmd = %w[ffmpeg -y]
27
+ Util.ffmpeg_inputs_max = 31
24
28
  Util.ffprobe_cmd = ['ffprobe']
25
- Util.cmd_timeout = 30
26
29
 
27
30
  Util::ThreadedIoBuffer.blocks_max = 1024
28
31
  Util::ThreadedIoBuffer.block_size = 64*1024
29
- Util::ThreadedIoBuffer.timeout = 9
32
+ Util::ThreadedIoBuffer.timeout = 1
33
+ Util::ThreadedIoBuffer.timeout_limit = 15
34
+ # NOTE all this effectively sets the minimum throughput: blocks_max * blocks_size / timeout * timeout_limit
35
+ Util::ThreadedIoBuffer.io_wait_timeout = 1
30
36
 
31
37
  Util::Thread.timeout = 15
32
38
 
39
+
40
+ # NOTE http://12factor.net etc
41
+
42
+ Ffmprb.ffmpeg_debug = ENV.fetch('FFMPRB_FFMPEG_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
43
+ Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
44
+
45
+ proc_vis_firebase = ENV['FFMPRB_PROC_VIS_FIREBASE']
46
+ if Ffmprb::FIREBASE_AVAILABLE
47
+ 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 =~ /\//
48
+ Ffmprb.proc_vis_firebase = proc_vis_firebase
49
+ elsif proc_vis_firebase
50
+ Ffmprb.logger.warn "Firebase unavailable (have firebase gem installed or unset FFMPRB_PROC_VIS_FIREBASE to get rid of this warning)"
51
+ end
52
+
33
53
  end
data/lib/ffmprb.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'logger'
2
2
  require 'ostruct'
3
+ require 'timeout'
3
4
 
4
5
  # IMPORTANT NOTE ffmprb uses threads internally, however, it is not "thread-safe"
5
6
 
7
+ require_relative 'ffmprb/util' # NOTE utils are like (micro-)gem candidates, errors are also there
8
+
6
9
  module Ffmprb
7
10
 
8
11
  ENV_VAR_FALSE_REGEX = /^(0|no?|false)?$/i
@@ -12,32 +15,31 @@ module Ffmprb
12
15
  HD_720p = '1280x720'
13
16
  HD_1080p = '1920x1080'
14
17
 
15
- class Error < StandardError; end
16
-
17
18
  class << self
18
19
 
19
20
  # TODO limit:
20
- def process(*args, **opts, &blk)
21
+ def process(*args, name: nil, **opts, &blk)
21
22
  fail Error, "process: nothing ;( gimme a block!" unless blk
22
23
 
23
- process = Process.new(**opts)
24
-
25
- logger.debug "Starting process with #{args} in #{blk.source_location}"
24
+ name ||= blk.source_location.map(&:to_s).map{ |s| ::File.basename s.to_s, ::File.extname(s) }.join(':')
25
+ process = Process.new(name: name, **opts)
26
+ proc_vis_node process if respond_to? :proc_vis_node # XXX simply include the ProcVis if it makes into a gem
27
+ logger.debug "Starting process with #{args} #{opts} in #{blk.source_location}"
26
28
 
27
29
  process.instance_exec *args, &blk
28
- logger.debug "Initialized process with #{args} in #{blk.source_location}"
30
+ logger.debug "Initialized process with #{args} #{opts} in #{blk.source_location}"
29
31
 
30
32
  process.run.tap do
31
- logger.debug "Finished process with #{args} in #{blk.source_location}"
33
+ logger.debug "Finished process with #{args} #{opts} in #{blk.source_location}"
32
34
  end
33
35
  end
34
36
  alias :action! :process # ;)
35
37
 
36
- attr_accessor :debug
38
+ attr_accessor :debug, :ffmpeg_debug
37
39
 
38
40
  def logger
39
41
  @logger ||= Logger.new(STDERR).tap do |logger|
40
- logger.level = debug ? Logger::DEBUG : Logger::INFO
42
+ logger.level = debug ? Logger::DEBUG : Logger::INFO # XXX thorify for -v -vv and -q
41
43
  end
42
44
  end
43
45
 
@@ -48,15 +50,14 @@ module Ffmprb
48
50
 
49
51
  end
50
52
 
53
+ include Util::ProcVis if FIREBASE_AVAILABLE
51
54
  end
52
55
 
53
- Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
54
56
 
55
57
  require_relative 'ffmprb/execution'
56
58
  require_relative 'ffmprb/file'
57
59
  require_relative 'ffmprb/filter'
58
60
  require_relative 'ffmprb/find_silence'
59
61
  require_relative 'ffmprb/process'
60
- require_relative 'ffmprb/util'
61
62
 
62
63
  require 'defaults'
@@ -8,7 +8,7 @@ module Ffmprb
8
8
  end
9
9
 
10
10
  def run
11
- Ffmprb.process *@params, &@script
11
+ Ffmprb.process *@params, ignore_broken_pipes: false, &@script
12
12
  end
13
13
 
14
14
  end
data/lib/ffmprb/file.rb CHANGED
@@ -4,31 +4,22 @@ require 'tempfile'
4
4
 
5
5
  module Ffmprb
6
6
 
7
- class File
7
+ class File # NOTE I would rather rename it to Stream at the moment
8
+ include Util::ProcVis::Node
8
9
 
9
10
  class << self
10
11
 
12
+ # NOTE careful when subclassing, it doesn't inherit the attr values
11
13
  attr_accessor :image_extname_regex, :sound_extname_regex, :movie_extname_regex
12
14
 
13
- def threaded_buffered_fifo(extname='.tmp')
14
- input_fifo_file = temp_fifo(extname)
15
- output_fifo_file = temp_fifo(extname)
16
- Ffmprb.logger.debug "Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"
17
- Util::Thread.new do
18
- begin
19
- Util::ThreadedIoBuffer.new async_opener(input_fifo_file, 'r'), async_opener(output_fifo_file, 'w')
20
- Util::Thread.join_children!
21
- Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"
22
- ensure
23
- input_fifo_file.remove if input_fifo_file
24
- output_fifo_file.remove if output_fifo_file
25
- end
26
- end
27
- Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"
28
-
29
- # TODO see threaded_io_buffer's XXXs: yield buff if block_given?
30
-
31
- [input_fifo_file, output_fifo_file]
15
+ # NOTE must be timeout-safe
16
+ def opener(file, mode=nil)
17
+ ->{
18
+ path = file.respond_to?(:path)? file.path : file
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)
22
+ }
32
23
  end
33
24
 
34
25
  def create(path)
@@ -38,14 +29,15 @@ module Ffmprb
38
29
  end
39
30
 
40
31
  def open(path)
41
- new(path: (path.respond_to?(:path)? path.path : path), mode: :read).tap do |file|
32
+ new(path: path, mode: :read).tap do |file|
42
33
  Ffmprb.logger.debug "Opened file with path: #{file.path}"
43
34
  end
44
35
  end
45
36
 
46
37
  def temp(extname)
47
38
  file = create(Tempfile.new(['', extname]))
48
- Ffmprb.logger.debug "Created temp file with path: #{file.path}"
39
+ path = file.path
40
+ Ffmprb.logger.debug "Created temp file with path: #{path}"
49
41
 
50
42
  return file unless block_given?
51
43
 
@@ -53,24 +45,31 @@ module Ffmprb
53
45
  yield file
54
46
  ensure
55
47
  begin
56
- FileUtils.remove_entry file.path
48
+ file.unlink
57
49
  rescue
58
- Ffmprb.logger.warn "Error removing temp file with path #{file.path}: #{$!.message}"
50
+ Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.message}"
59
51
  end
60
- Ffmprb.logger.debug "Removed temp file with path: #{file.path}"
52
+ Ffmprb.logger.debug "Removed temp file with path: #{path}"
61
53
  end
62
54
  end
63
55
 
64
56
  def temp_fifo(extname='.tmp', &blk)
65
- fifo_file = create(temp_fifo_path extname)
66
- ::File.mkfifo fifo_file.path
57
+ path = temp_fifo_path(extname)
58
+ ::File.mkfifo path
59
+ fifo_file = create(path)
67
60
 
68
61
  return fifo_file unless block_given?
69
62
 
63
+ path = fifo_file.path
70
64
  begin
71
65
  yield fifo_file
72
66
  ensure
73
- fifo_file.remove
67
+ begin
68
+ fifo_file.unlink
69
+ rescue
70
+ Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.message}"
71
+ end
72
+ Ffmprb.logger.debug "Removed temp file with path: #{path}"
74
73
  end
75
74
  end
76
75
 
@@ -78,35 +77,32 @@ module Ffmprb
78
77
  ::File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('', 'p' + extname)
79
78
  end
80
79
 
81
- # NOTE must be timeout-safe
82
- def async_opener(file, mode)
83
- ->{
84
- Ffmprb.logger.debug "Trying to open #{file.path} for #{mode}-buffering"
85
- ::File.open(file.path, mode)
86
- }
87
- end
88
-
89
80
  def image?(extname)
90
- extname =~ image_extname_regex
81
+ !!(extname =~ File.image_extname_regex)
91
82
  end
92
83
 
93
84
  def sound?(extname)
94
- extname =~ sound_extname_regex
85
+ !!(extname =~ File.sound_extname_regex)
95
86
  end
96
87
 
97
88
  def movie?(extname)
98
- extname =~ movie_extname_regex
89
+ !!(extname =~ File.movie_extname_regex)
99
90
  end
100
91
 
101
92
  end
102
93
 
94
+ attr_reader :mode
103
95
 
104
96
  def initialize(path:, mode:)
105
- @path = path
106
- @path.close if @path && @path.respond_to?(:close) # NOTE specially for temp files
107
- path! # NOTE early (exception) raiser
108
97
  @mode = mode.to_sym
109
98
  fail Error, "Open for read, create for write, ??? for #{@mode}" unless %i[read write].include?(@mode)
99
+ @path = path
100
+ @path.close if @path && @path.respond_to?(:close) # NOTE we operate on closed files
101
+ path! # NOTE early (exception) raiser
102
+ end
103
+
104
+ def label
105
+ basename
110
106
  end
111
107
 
112
108
  def path
@@ -119,8 +115,12 @@ module Ffmprb
119
115
  ::File.exist? path
120
116
  end
121
117
 
118
+ def basename
119
+ @basename ||= ::File.basename(path)
120
+ end
121
+
122
122
  def extname
123
- ::File.extname path
123
+ @extname ||= ::File.extname(path)
124
124
  end
125
125
 
126
126
  def channel?(medium)
@@ -132,11 +132,12 @@ module Ffmprb
132
132
  end
133
133
  end
134
134
 
135
- def length
135
+ def length(force=false)
136
+ @duration = nil if force
136
137
  return @duration if @duration
137
138
 
138
139
  # NOTE first attempt
139
- @duration = probe['format']['duration']
140
+ @duration = probe(force)['format']['duration']
140
141
  @duration &&= @duration.to_f
141
142
  return @duration if @duration
142
143
 
@@ -161,8 +162,12 @@ module Ffmprb
161
162
  ::File.write path, s
162
163
  end
163
164
 
164
- def remove
165
- FileUtils.remove_entry path
165
+ def unlink
166
+ if path.respond_to? :unlink
167
+ path.unlink
168
+ else
169
+ FileUtils.remove_entry path
170
+ end
166
171
  Ffmprb.logger.debug "Removed file with path: #{path}"
167
172
  @path = nil
168
173
  end
@@ -170,7 +175,7 @@ module Ffmprb
170
175
  private
171
176
 
172
177
  def path!
173
- ( # NOTE specially for temp files
178
+ (
174
179
  @path.respond_to?(:path)? @path.path : @path
175
180
  ).tap do |path|
176
181
  # TODO ensure readabilty/writability/readiness
@@ -192,3 +197,4 @@ module Ffmprb
192
197
  end
193
198
 
194
199
  require_relative 'file/sample'
200
+ require_relative 'file/threaded_buffered'