ffmprb 0.6.6
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 +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: []
|