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 +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'
|