ffmprb 0.9.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'