ffmprb 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +4 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Guardfile +12 -0
- data/README.md +72 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/guard +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/setup +7 -0
- data/ffmprb.gemspec +30 -0
- data/lib/ffmprb/file.rb +184 -0
- data/lib/ffmprb/filter.rb +234 -0
- data/lib/ffmprb/process/input.rb +178 -0
- data/lib/ffmprb/process/output.rb +332 -0
- data/lib/ffmprb/process.rb +98 -0
- data/lib/ffmprb/util/io_buffer.rb +211 -0
- data/lib/ffmprb/util/synchro.rb +47 -0
- data/lib/ffmprb/util/thread.rb +28 -0
- data/lib/ffmprb/util.rb +89 -0
- data/lib/ffmprb/version.rb +3 -0
- data/lib/ffmprb.rb +83 -0
- metadata +181 -0
@@ -0,0 +1,211 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
module Util
|
4
|
+
|
5
|
+
# XXX the events mechanism is currently unused (and commented out) => synchro mechanism not needed
|
6
|
+
# XXX partially specc'ed in file_spec
|
7
|
+
class IoBuffer
|
8
|
+
# include Synchro
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
attr_accessor :blocks_max
|
13
|
+
attr_accessor :block_size
|
14
|
+
attr_accessor :timeout
|
15
|
+
|
16
|
+
def default_size
|
17
|
+
blocks_max * block_size
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# NOTE input/output can be lambdas for single asynchronic io evaluation
|
23
|
+
# NOTE both ios are closed as soon as possible
|
24
|
+
def initialize(input, output,
|
25
|
+
blocks_max: self.class.blocks_max, block_size: self.class.block_size)
|
26
|
+
|
27
|
+
@input = input
|
28
|
+
@output = output
|
29
|
+
@q = SizedQueue.new(blocks_max)
|
30
|
+
@stat_blocks_max = 0
|
31
|
+
@terminate = false
|
32
|
+
# @events = {}
|
33
|
+
|
34
|
+
# NOTE reads all of input, then closes the stream times out on buffer overflow
|
35
|
+
|
36
|
+
@reader = Util::Thread.new("buffer reader") do
|
37
|
+
begin
|
38
|
+
while s = reader_input!.read(block_size)
|
39
|
+
begin
|
40
|
+
Timeout::timeout(self.class.timeout) do
|
41
|
+
@q.enq s
|
42
|
+
end
|
43
|
+
rescue Timeout::Error # NOTE the queue is probably overflown
|
44
|
+
@terminate = Error.new("The reader has failed with timeout while queuing")
|
45
|
+
# timeout!
|
46
|
+
raise Error, "Looks like we're stuck (#{self.class.timeout}s idle) with #{blocks_max}x#{block_size}B blocks (buffering #{reader_input!.path}->...)..."
|
47
|
+
end
|
48
|
+
@stat_blocks_max = blocks_count if blocks_count > @stat_blocks_max
|
49
|
+
end
|
50
|
+
@terminate = true
|
51
|
+
@q.enq nil
|
52
|
+
ensure
|
53
|
+
begin
|
54
|
+
reader_input!.close if reader_input!.respond_to?(:close)
|
55
|
+
rescue
|
56
|
+
Ffmprb.logger.error "IoBuffer input closing error: #{$!.message}"
|
57
|
+
end
|
58
|
+
# reader_done!
|
59
|
+
Ffmprb.logger.debug "IoBuffer reader terminated (blocks max: #{@stat_blocks_max})"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
init_writer_output!
|
64
|
+
|
65
|
+
# NOTE writes as much output as possible, then terminates when the reader dies
|
66
|
+
|
67
|
+
@writer = Util::Thread.new("buffer writer") do
|
68
|
+
broken = false
|
69
|
+
begin
|
70
|
+
while s = @q.deq
|
71
|
+
next if broken
|
72
|
+
written = 0
|
73
|
+
while !broken
|
74
|
+
raise @terminate if @terminate.kind_of?(Exception)
|
75
|
+
begin
|
76
|
+
output = writer_output!
|
77
|
+
written = output.write_nonblock(s) if output # NOTE will only be nil if @terminate is an exception
|
78
|
+
break if written == s.length # NOTE kinda optimisation
|
79
|
+
s = s[written..-1]
|
80
|
+
rescue Errno::EAGAIN
|
81
|
+
Ffmprb.logger.debug "IoBuffer writer (to #{output.path}) retrying"
|
82
|
+
sleep 0.01
|
83
|
+
rescue Errno::EWOULDBLOCK
|
84
|
+
Ffmprb.logger.debug "IoBuffer writer (to #{output.path}) taking a break"
|
85
|
+
sleep 0.01
|
86
|
+
rescue Errno::EPIPE
|
87
|
+
broken = true
|
88
|
+
Ffmprb.logger.debug "IoBuffer writer (to #{output.path}) broken"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
ensure
|
93
|
+
# terminated!
|
94
|
+
begin
|
95
|
+
writer_output!.close if !broken && writer_output!.respond_to?(:close)
|
96
|
+
rescue
|
97
|
+
Ffmprb.logger.error "IoBuffer output closing error: #{$!.message}"
|
98
|
+
end
|
99
|
+
Ffmprb.logger.debug "IoBuffer writer terminated (blocks max: #{@stat_blocks_max})"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def flush! # NOTE blocking, closes ios
|
105
|
+
e = nil
|
106
|
+
# [@reader, @writer, @handler_thr].each do |thr|
|
107
|
+
[@reader, @writer, @output_thr].compact.each do |thr|
|
108
|
+
begin
|
109
|
+
thr.join
|
110
|
+
rescue
|
111
|
+
if e
|
112
|
+
Ffmprb.logger.debug "Additionally got (hidden): #{$!.message}"
|
113
|
+
else
|
114
|
+
e = $!
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
raise e if e
|
119
|
+
end
|
120
|
+
# handle_synchronously :flush!
|
121
|
+
#
|
122
|
+
# def once(event, &blk)
|
123
|
+
# event = event.to_sym
|
124
|
+
# wait_for_handler!
|
125
|
+
# if @events[event].respond_to? :call
|
126
|
+
# raise Error, "Once upon a time (one #once(event) at a time) please"
|
127
|
+
# elsif @events[event]
|
128
|
+
# Ffmprb.logger.debug "IoBuffer (post-)reacting to #{event}"
|
129
|
+
# @handler_thr = Util::Thread.new "#{event} handler", &blk
|
130
|
+
# else
|
131
|
+
# Ffmprb.logger.debug "IoBuffer subscribing to #{event}"
|
132
|
+
# @events[event] = blk
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
# handle_synchronously :once
|
136
|
+
#
|
137
|
+
# def reader_done!
|
138
|
+
# Ffmprb.logger.debug "IoBuffer reader terminated (blocks max: #{@stat_blocks_max})"
|
139
|
+
# fire! :reader_done
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# def terminated!
|
143
|
+
# fire! :terminated
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# def timeout!
|
147
|
+
# fire! :timeout
|
148
|
+
# end
|
149
|
+
|
150
|
+
protected
|
151
|
+
#
|
152
|
+
# def fire!(event)
|
153
|
+
# wait_for_handler!
|
154
|
+
# Ffmprb.logger.debug "IoBuffer firing #{event}"
|
155
|
+
# if blk = @events.to_h[event.to_sym]
|
156
|
+
# @handler_thr = Util::Thread.new "#{event} handler", &blk
|
157
|
+
# end
|
158
|
+
# @events[event.to_sym] = true
|
159
|
+
# end
|
160
|
+
# handle_synchronously :fire!
|
161
|
+
#
|
162
|
+
def blocks_count
|
163
|
+
@q.size
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def reader_input! # NOTE just for reader thread
|
169
|
+
if @input.respond_to?(:call)
|
170
|
+
Ffmprb.logger.debug "Opening buffer input"
|
171
|
+
@input = @input.call
|
172
|
+
Ffmprb.logger.debug "Opened buffer input: #{@input.path}"
|
173
|
+
end
|
174
|
+
@input
|
175
|
+
end
|
176
|
+
|
177
|
+
def writer_output! # NOTE just for writer thread
|
178
|
+
if @output_thr
|
179
|
+
@output_thr.join
|
180
|
+
@output_thr = nil
|
181
|
+
end
|
182
|
+
@output unless @output.respond_to?(:call)
|
183
|
+
end
|
184
|
+
|
185
|
+
def init_writer_output!
|
186
|
+
return unless @output.respond_to?(:call)
|
187
|
+
|
188
|
+
@output_thr = Util::Thread.new("buffer writer output helper") do
|
189
|
+
Ffmprb.logger.debug "Opening buffer output"
|
190
|
+
begin
|
191
|
+
Timeout::timeout(self.class.timeout/2) do
|
192
|
+
@output = @output.call
|
193
|
+
Ffmprb.logger.debug "Opened buffer output: #{@output.path}"
|
194
|
+
end
|
195
|
+
rescue Timeout::Error
|
196
|
+
Ffmprb.logger.info "A little bit of timeout in the buffer writer helper thread"
|
197
|
+
retry unless @terminate.kind_of?(Exception)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
#
|
202
|
+
# def wait_for_handler!
|
203
|
+
# @handler_thr.join if @handler_thr
|
204
|
+
# @handler_thr = nil
|
205
|
+
# end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# XXX unused and commented out
|
2
|
+
# module Ffmprb
|
3
|
+
#
|
4
|
+
# module Util
|
5
|
+
#
|
6
|
+
# # NOTE doesn't have specs (and not too proud about it)
|
7
|
+
# module Synchro
|
8
|
+
#
|
9
|
+
# module ClassMethods
|
10
|
+
#
|
11
|
+
# def handle_synchronously(*methods)
|
12
|
+
# prepend Module.new do
|
13
|
+
#
|
14
|
+
# methods.each do |method|
|
15
|
+
#
|
16
|
+
# define_method method do
|
17
|
+
# @_synchro.synchronize do
|
18
|
+
# super
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# module InstanceMethods
|
30
|
+
#
|
31
|
+
# def initialize(*args)
|
32
|
+
# @_synchro = Monitor.new
|
33
|
+
# super
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# def self.included(mod)
|
39
|
+
# mod.prepend InstanceMethods
|
40
|
+
# mod.extend ClassMethods
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ffmprb
|
2
|
+
|
3
|
+
module Util
|
4
|
+
|
5
|
+
# NOTE doesn't have specs (and not too proud about it)
|
6
|
+
class Thread < ::Thread
|
7
|
+
|
8
|
+
def initialize(name="some", &blk)
|
9
|
+
super() do
|
10
|
+
begin
|
11
|
+
Ffmprb.logger.debug "#{name} thread launched"
|
12
|
+
blk.call
|
13
|
+
Ffmprb.logger.debug "#{name} thread done"
|
14
|
+
rescue Exception
|
15
|
+
Ffmprb.logger.warn "#{$!.class} caught in a #{name} thread (hidden): #{$!.message}\nBacktrace:\n\t#{$!.backtrace.join("\n\t")}"
|
16
|
+
cause = $!
|
17
|
+
Ffmprb.logger.warn "...caused by #{cause.class}: #{cause.message}\nBacktrace:\n\t#{cause.backtrace.join("\n\t")}" while
|
18
|
+
cause = cause.cause
|
19
|
+
raise
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/ffmprb/util.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# require 'ffmprb/util/synchro'
|
2
|
+
require 'ffmprb/util/thread'
|
3
|
+
require 'ffmprb/util/io_buffer'
|
4
|
+
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
module Ffmprb
|
8
|
+
|
9
|
+
module Util
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def ffprobe(args)
|
14
|
+
sh "ffprobe#{args}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def ffmpeg(args)
|
18
|
+
args = " -loglevel debug#{args}" if Ffmprb.debug
|
19
|
+
sh "ffmpeg -y#{args}", output: :stderr
|
20
|
+
end
|
21
|
+
|
22
|
+
def sh(cmd, output: :stdout, log: :stderr)
|
23
|
+
Ffmprb.logger.info cmd
|
24
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
25
|
+
stdin.close
|
26
|
+
|
27
|
+
# XXX process timeouting/cleanup here will be appreciated
|
28
|
+
|
29
|
+
begin
|
30
|
+
log_cmd = "#{cmd.split(' ').first.upcase}: " if log
|
31
|
+
stdout_r = Reader.new(stdout, output == :stdout, log == :stdout && log_cmd)
|
32
|
+
stderr_r = Reader.new(stderr, true, log == :stderr && log_cmd)
|
33
|
+
|
34
|
+
raise Error, "#{cmd}:\n#{stderr_r.read}" unless
|
35
|
+
wait_thr.value.exitstatus == 0 # NOTE blocks
|
36
|
+
|
37
|
+
# NOTE only one of them will return non-nil, see above
|
38
|
+
stdout_r.read || stderr_r.read
|
39
|
+
ensure
|
40
|
+
begin
|
41
|
+
stdout_r.join if stdout_r
|
42
|
+
stdout_r = nil
|
43
|
+
stderr_r.join if stderr_r
|
44
|
+
rescue
|
45
|
+
Ffmprb.logger.error "Thread joining error: #{$!.message}"
|
46
|
+
stderr_r.join if stdout_r
|
47
|
+
end
|
48
|
+
Ffmprb.logger.debug "FINISHED: #{cmd}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class Reader < Thread
|
57
|
+
|
58
|
+
def initialize(input, store=false, log=nil)
|
59
|
+
@output = ''
|
60
|
+
@queue = Queue.new
|
61
|
+
super "reader" do
|
62
|
+
begin
|
63
|
+
while s = input.gets
|
64
|
+
Ffmprb.logger.debug log + s.chomp if log
|
65
|
+
@output << s if store
|
66
|
+
end
|
67
|
+
@queue.enq @output
|
68
|
+
rescue Exception
|
69
|
+
@queue.enq Error.new("Exception in a reader thread")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def read
|
75
|
+
case res = @queue.deq
|
76
|
+
when Exception
|
77
|
+
raise res
|
78
|
+
when ''
|
79
|
+
nil
|
80
|
+
else
|
81
|
+
res
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/lib/ffmprb.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'ffmprb/file'
|
2
|
+
require 'ffmprb/filter'
|
3
|
+
require 'ffmprb/process'
|
4
|
+
require 'ffmprb/util'
|
5
|
+
require 'ffmprb/version'
|
6
|
+
|
7
|
+
require 'logger'
|
8
|
+
require 'time'
|
9
|
+
require 'timeout'
|
10
|
+
|
11
|
+
module Ffmprb
|
12
|
+
|
13
|
+
ENV_VAR_FALSE_REGEX = /^(0|no?|false)?$/i
|
14
|
+
|
15
|
+
QVGA = '320x240'
|
16
|
+
HD_720p = '1280x720'
|
17
|
+
HD_1080p = '1920x1080'
|
18
|
+
|
19
|
+
class Error < StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
Util::IoBuffer.blocks_max = 1024
|
23
|
+
Util::IoBuffer.block_size = 64*1024
|
24
|
+
Util::IoBuffer.timeout = 9
|
25
|
+
|
26
|
+
class << self
|
27
|
+
|
28
|
+
def process(*args, &blk)
|
29
|
+
logger.debug "Starting process with #{args} in #{blk.source_location}"
|
30
|
+
Process.new.tap do |process|
|
31
|
+
if blk
|
32
|
+
process.instance_exec *args, &blk
|
33
|
+
process.run
|
34
|
+
logger.debug "Finished process with #{args} in #{blk.source_location}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias :action! :process # ;)
|
39
|
+
|
40
|
+
attr_accessor :debug
|
41
|
+
|
42
|
+
def logger
|
43
|
+
@logger ||= Logger.new(STDERR).tap do |logger|
|
44
|
+
logger.level = debug ? Logger::DEBUG : Logger::INFO
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger=(logger)
|
49
|
+
@logger.close if @logger
|
50
|
+
@logger = logger
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_silence(input_file, output_file)
|
54
|
+
logger.debug "Finding silence (#{input_file.path}->#{output_file.path})"
|
55
|
+
filters = Filter.silencedetect
|
56
|
+
options = " -i #{input_file.path}#{Filter.complex_options filters} #{output_file.path}"
|
57
|
+
silence = []
|
58
|
+
Util.ffmpeg(options).split("\n").each do |line|
|
59
|
+
next unless line =~ /^\[silencedetect\s.*\]\s*silence_(\w+):\s*(\d+\.?d*)/
|
60
|
+
case $1
|
61
|
+
when 'start'
|
62
|
+
silence << OpenStruct.new(start_at: $2.to_f)
|
63
|
+
when 'end'
|
64
|
+
if silence.empty?
|
65
|
+
silence << OpenStruct.new(start_at: 0.0, end_at: $2.to_f)
|
66
|
+
else
|
67
|
+
raise Error, "ffmpeg is being stupid: silence_end with no silence_start" if silence.last.end_at
|
68
|
+
silence.last.end_at = $2.to_f
|
69
|
+
end
|
70
|
+
else
|
71
|
+
Ffmprb.warn "Unknown silence mark: #{$1}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
logger.debug "Found silence (#{input_file.path}->#{output_file.path}): [#{silence.map{|t,v| "#{t}: #{v}"}}]"
|
76
|
+
silence
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
Ffmprb.debug = ENV.fetch('FFMPRB_DEBUG', '') !~ Ffmprb::ENV_VAR_FALSE_REGEX
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ffmprb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- showbox.com
|
8
|
+
- Costa Shapiro @ Showbox
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-07-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mkfifo
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.9.9
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.9.9
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: byebug
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 4.0.5
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 4.0.5
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: guard-rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.12.8
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.12.8
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rake
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 10.4.2
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 10.4.2
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rmagick
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '2.15'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.15'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rspec
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 3.2.0
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 3.2.0
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: ruby-sox
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 0.0.3
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.0.3
|
126
|
+
description: A DSL (Damn-Simple Language) and a micro-engine for ffmpeg and ffriends
|
127
|
+
email:
|
128
|
+
- costa@showbox.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".gitignore"
|
134
|
+
- ".rspec"
|
135
|
+
- ".ruby-version"
|
136
|
+
- ".travis.yml"
|
137
|
+
- Gemfile
|
138
|
+
- Guardfile
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- bin/console
|
142
|
+
- bin/guard
|
143
|
+
- bin/rake
|
144
|
+
- bin/rspec
|
145
|
+
- bin/setup
|
146
|
+
- ffmprb.gemspec
|
147
|
+
- lib/ffmprb.rb
|
148
|
+
- lib/ffmprb/file.rb
|
149
|
+
- lib/ffmprb/filter.rb
|
150
|
+
- lib/ffmprb/process.rb
|
151
|
+
- lib/ffmprb/process/input.rb
|
152
|
+
- lib/ffmprb/process/output.rb
|
153
|
+
- lib/ffmprb/util.rb
|
154
|
+
- lib/ffmprb/util/io_buffer.rb
|
155
|
+
- lib/ffmprb/util/synchro.rb
|
156
|
+
- lib/ffmprb/util/thread.rb
|
157
|
+
- lib/ffmprb/version.rb
|
158
|
+
homepage: https://github.com/showbox-oss/ffmprb
|
159
|
+
licenses: []
|
160
|
+
metadata: {}
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 2.4.6
|
178
|
+
signing_key:
|
179
|
+
specification_version: 4
|
180
|
+
summary: ffmprb is your audio/video montage friend, based on https://ffmpeg.org
|
181
|
+
test_files: []
|