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 +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/Guardfile +12 -0
- data/README.md +25 -22
- data/ffmprb.gemspec +13 -9
- data/lib/defaults.rb +23 -3
- data/lib/ffmprb.rb +13 -12
- data/lib/ffmprb/execution.rb +1 -1
- data/lib/ffmprb/file.rb +54 -48
- data/lib/ffmprb/file/sample.rb +4 -4
- data/lib/ffmprb/file/threaded_buffered.rb +48 -0
- data/lib/ffmprb/filter.rb +9 -7
- data/lib/ffmprb/find_silence.rb +7 -5
- data/lib/ffmprb/process.rb +67 -39
- data/lib/ffmprb/process/input.rb +37 -18
- data/lib/ffmprb/process/input/looping.rb +83 -46
- data/lib/ffmprb/process/input/temp.rb +5 -12
- data/lib/ffmprb/process/output.rb +68 -64
- data/lib/ffmprb/util.rb +32 -18
- data/lib/ffmprb/util/proc_vis.rb +163 -0
- data/lib/ffmprb/util/thread.rb +20 -12
- data/lib/ffmprb/util/threaded_io_buffer.rb +186 -74
- data/lib/ffmprb/version.rb +9 -1
- metadata +55 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afd76dfa3efb28bdc494678ca4e04ff6eb02abb6
|
4
|
+
data.tar.gz: ec23bc1bc45197f7850570317adbc040014120a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18639c467cfd887e03972453e6ce3ac3a6801101d4052a50bbdb8f739be15f0c9ebdc01e17ba0e6ff33288f2ce854a3ce32c45c87ef77599e8921498a8d536a6
|
7
|
+
data.tar.gz: dfb7c6405791d6766a31773a3d5bf759f27a3aaeafb0feccc42889d41d60cdbce64c6305330004e9741eae4ee8000b6b7e1bdf89446db4cc59fe2210c597b650
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.2.
|
1
|
+
ruby-2.2.4
|
data/.travis.yml
CHANGED
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
|
8
|
+
Allows for scripts like
|
9
9
|
```ruby
|
10
|
-
Ffmprb.process
|
11
|
-
|
12
|
-
in_main = input(
|
13
|
-
in_sound = input(
|
14
|
-
output
|
15
|
-
roll in_main.cut(from: 2, to: 5)
|
16
|
-
roll in_main.cut(from: 6, to: 16)
|
17
|
-
overlay in_sound.volume(0.8)
|
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
|
22
|
+
and saves you from the horror of...
|
23
23
|
```
|
24
|
-
ffmpeg -y -i flick.mp4 -i track.wav -filter_complex "[0:v]
|
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
|
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
|
131
|
+
Ffmprb.process do |av_input1, av_output1|
|
138
132
|
|
139
|
-
in_main = input(
|
140
|
-
output(
|
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
|
10
|
-
spec.email = ['costa@
|
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.
|
25
|
-
spec.add_development_dependency 'byebug', '>=
|
26
|
-
spec.add_development_dependency '
|
27
|
-
spec.add_development_dependency '
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency '
|
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.
|
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 =
|
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
|
-
|
24
|
-
|
25
|
-
|
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'
|
data/lib/ffmprb/execution.rb
CHANGED
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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:
|
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
|
-
|
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
|
-
|
48
|
+
file.unlink
|
57
49
|
rescue
|
58
|
-
Ffmprb.logger.warn "
|
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: #{
|
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
|
-
|
66
|
-
::File.mkfifo
|
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
|
-
|
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
|
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
|
165
|
-
|
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
|
-
(
|
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'
|