mediakit 0.0.6 → 0.0.9
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/README.md +54 -8
- data/circle.yml +38 -0
- data/lib/mediakit/drivers.rb +28 -11
- data/lib/mediakit/ffmpeg.rb +19 -9
- data/lib/mediakit/initializers/ffmpeg.rb +211 -0
- data/lib/mediakit/initializers.rb +5 -0
- data/lib/mediakit/utils/process_runner.rb +33 -17
- data/lib/mediakit/version.rb +1 -1
- data/lib/mediakit.rb +1 -0
- metadata +5 -8
- data/.travis.yml +0 -8
- data/lib/mediakit/ffmpeg/introspection.rb +0 -29
- data/templates/codec.rb.erb +0 -33
- data/templates/decoder.rb.erb +0 -27
- data/templates/encoder.rb.erb +0 -27
- data/templates/format.rb.erb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 575608c79c6aefb71acac345ee3216f2ce8291d6
|
4
|
+
data.tar.gz: 0fdf5b24e11a8b2f9218a1e63b3b9473ca2a0884
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f79fbba7a247c1fc51b31c906e96b98cc28331e7a6cd68e6eb76a93dba763384966c0f2ec1fab2afde66da7e5d71398d5dd6a4527cedc135fcb190391e44e60e
|
7
|
+
data.tar.gz: 24e596222c852222c8a1fd5366b2225c1e0360524975fb4f7539ef2c3ed79bd520f4f83103bdc9abf15bf55aff7670e490aaa250a8b2186b0a2573d9f2ac2634
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Mediakit
|
2
2
|
|
3
|
-
[](https://circleci.com/gh/ainame/mediakit/tree/master)
|
4
4
|
|
5
5
|
mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
6
6
|
I've design this library for following purpose.
|
@@ -12,11 +12,14 @@ I've design this library for following purpose.
|
|
12
12
|
|
13
13
|
Currently you can use low-level inteface of mediakit!
|
14
14
|
|
15
|
-
* [
|
16
|
-
* [
|
17
|
-
* [
|
18
|
-
* [
|
19
|
-
* [
|
15
|
+
* [ ] low-level interface
|
16
|
+
* [x] execute command for ffmpeg
|
17
|
+
* [x] unit testing supports (fake driver)
|
18
|
+
* [x] nice command setting
|
19
|
+
* [x] read timeout setting
|
20
|
+
* [x] shell escape for security
|
21
|
+
* [ ] logger support
|
22
|
+
* [ ] ffmpeg instropection (support formats, codecs, encoders, decoders..)
|
20
23
|
* [ ] high-level interface for ffmpeg
|
21
24
|
* [ ] low-level interface for sox
|
22
25
|
* [ ] high-level interface for sox
|
@@ -53,7 +56,7 @@ This is a little bore interface for constructing options,
|
|
53
56
|
but can pass certain it.
|
54
57
|
|
55
58
|
```rb
|
56
|
-
driver = Mediakit::Drivers::FFmpeg.new(
|
59
|
+
driver = Mediakit::Drivers::FFmpeg.new()
|
57
60
|
ffmpeg = Mediakit::FFmpeg.new(driver)
|
58
61
|
|
59
62
|
options = Mediakit::FFmpeg::Options.new(
|
@@ -75,9 +78,52 @@ options = Mediakit::FFmpeg::Options.new(
|
|
75
78
|
),
|
76
79
|
)
|
77
80
|
puts "$ #{ffmpeg.command(options)}"
|
78
|
-
puts ffmpeg.run(options)
|
81
|
+
puts ffmpeg.run(options, timeout: 30, nice: 0)
|
79
82
|
```
|
80
83
|
|
84
|
+
#### Drivers
|
85
|
+
|
86
|
+
mediakit has two drivers for execute command. First, Popen Driver is implemented by Open3#popen3 that is main driver. Second, FakeDriver is the fake object for unit-testing.
|
87
|
+
|
88
|
+
##### Driver Usage
|
89
|
+
|
90
|
+
instatiate
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
default_driver = Mediakit::Drivers::FFmpeg.new # default is popen driver
|
94
|
+
driver = Mediakit::Drivers::FFmpeg.new(:popen) # explicitly use popen driver
|
95
|
+
fake_driver = Mediakit::Drivers::FFmpeg.new(:fake) # fake driver
|
96
|
+
```
|
97
|
+
|
98
|
+
testing
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
# setup
|
102
|
+
fake_driver = Mediakit::Drivers::FFmpeg.new(:fake)
|
103
|
+
fake_driver.output = 'output'
|
104
|
+
fake_driver.error_output = 'error_output'
|
105
|
+
fake_driver.exit_status = true
|
106
|
+
ffmpeg = Mediakit::FFmpeg.new(fake_driver)
|
107
|
+
|
108
|
+
# excursie
|
109
|
+
options = Mediakit::FFmpeg::Options.new(
|
110
|
+
Mediakit::FFmpeg::Options::GlobalOption.new(
|
111
|
+
'version' => true,
|
112
|
+
),
|
113
|
+
}
|
114
|
+
out, err, exit_status = ffmpeg.run(options)
|
115
|
+
|
116
|
+
# verify
|
117
|
+
assert_equal(fake_driver.last_command, 'ffmpeg -version')
|
118
|
+
assert_equal(out, 'output')
|
119
|
+
assert_equal(err, 'error_output')
|
120
|
+
assert_equal(exit_status, true)
|
121
|
+
|
122
|
+
# teardown
|
123
|
+
fake_driver.reset
|
124
|
+
```
|
125
|
+
|
126
|
+
|
81
127
|
### High Level Interface
|
82
128
|
|
83
129
|
TBD
|
data/circle.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
machine:
|
2
|
+
ruby:
|
3
|
+
version: 2.2.2
|
4
|
+
dependencies:
|
5
|
+
cache_directories:
|
6
|
+
- "~/ffmpeg_build"
|
7
|
+
pre:
|
8
|
+
- >
|
9
|
+
FFMPEG_DIR="$HOME/ffmpeg_build";
|
10
|
+
FFMPEG_VERSION="2.6.3";
|
11
|
+
if [ ! -d "$FFMPEG_DIR" ]; then
|
12
|
+
mkdir "$FFMPEG_DIR" &&
|
13
|
+
cd "$FFMPEG_DIR" &&
|
14
|
+
mkdir ffmpeg &&
|
15
|
+
mkdir fdkaac &&
|
16
|
+
mkdir src &&
|
17
|
+
cd src &&
|
18
|
+
wget https://downloads.sourceforge.net/project/opencore-amr/fdk-aac/fdk-aac-0.1.4.tar.gz &&
|
19
|
+
tar xfz fdk-aac-0.1.4.tar.gz &&
|
20
|
+
cd fdk-aac-0.1.4 &&
|
21
|
+
autoreconf -fvi &&
|
22
|
+
./configure --prefix="$FFMPEG_DIR/fdkaac" --disable-shared &&
|
23
|
+
sudo make install &&
|
24
|
+
cd "$FFMPEG_DIR" &&
|
25
|
+
wget "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" &&
|
26
|
+
tar -xjpf "ffmpeg-$FFMPEG_VERSION.tar.bz2" &&
|
27
|
+
cd "ffmpeg-$FFMPEG_VERSION" &&
|
28
|
+
./configure
|
29
|
+
--extra-cflags="-I$FFMPEG_DIR/fdkaac/include"
|
30
|
+
--extra-ldflags="-L$FFMPEG_DIR/fdkaac/lib"
|
31
|
+
--enable-gpl
|
32
|
+
--enable-nonfree
|
33
|
+
--enable-libfaac
|
34
|
+
--enable-libmp3lame
|
35
|
+
--enable-libfdk-aac &&
|
36
|
+
sudo make install;
|
37
|
+
fi;
|
38
|
+
cd "$FFMPEG_DIR/ffmpeg-$FFMPEG_VERSION" && sudo make install
|
data/lib/mediakit/drivers.rb
CHANGED
@@ -30,12 +30,12 @@ module Mediakit
|
|
30
30
|
# @overload run(*args, options)
|
31
31
|
# @param [Array] args arguments for command
|
32
32
|
# @option [Hash] options run options
|
33
|
-
# @return [
|
33
|
+
# @return [String] stdout output
|
34
34
|
def run(*args)
|
35
|
-
options = (args.
|
35
|
+
options, rest_args = parse_options(args.dup)
|
36
|
+
runner = Mediakit::Utils::ProcessRunner.new(options)
|
36
37
|
begin
|
37
|
-
|
38
|
-
stdout, stderr, exit_status = runner.run(bin, *args)
|
38
|
+
stdout, stderr, exit_status = runner.run(bin, *rest_args)
|
39
39
|
raise(FailError, stderr) unless exit_status
|
40
40
|
stdout
|
41
41
|
rescue Mediakit::Utils::ProcessRunner::CommandNotFoundError => e
|
@@ -51,18 +51,35 @@ module Mediakit
|
|
51
51
|
# @param args [Array] arguments for command
|
52
52
|
# @return [String] command
|
53
53
|
def command(*args)
|
54
|
-
|
55
|
-
|
54
|
+
options, rest_args = parse_options(args.dup)
|
55
|
+
runner = Mediakit::Utils::ProcessRunner.new(options)
|
56
|
+
runner.build_command(bin, *rest_args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_options(args)
|
60
|
+
options = (args.last && args.last.kind_of?(Hash)) ? args.pop : {}
|
61
|
+
[options, args]
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
59
|
-
class FakeDriver <
|
60
|
-
|
61
|
-
|
65
|
+
class FakeDriver < PopenDriver
|
66
|
+
attr_accessor(:last_command, :output, :error_output, :exit_status)
|
67
|
+
|
68
|
+
# fake driver for testing
|
69
|
+
#
|
70
|
+
# @overload run(args)
|
71
|
+
# @param [String] args string argument for command
|
72
|
+
# @overload run(*args, options)
|
73
|
+
# @param [Array] args arguments for command
|
74
|
+
# @option [Hash] options run options
|
75
|
+
# @return [String] stdout output
|
76
|
+
def run(*args)
|
77
|
+
@last_command = command(*args)
|
78
|
+
[(output || ''), (error_output || ''), (exit_status || true)]
|
62
79
|
end
|
63
80
|
|
64
|
-
def
|
65
|
-
|
81
|
+
def reset
|
82
|
+
@last_command, @output, @error_output, @exit_status = nil
|
66
83
|
end
|
67
84
|
end
|
68
85
|
|
data/lib/mediakit/ffmpeg.rb
CHANGED
@@ -1,34 +1,44 @@
|
|
1
1
|
require 'mediakit/drivers'
|
2
2
|
require 'mediakit/ffmpeg/options'
|
3
|
-
require 'mediakit/ffmpeg/introspection'
|
4
3
|
|
5
4
|
module Mediakit
|
6
5
|
class FFmpeg
|
7
|
-
include Introspection
|
8
6
|
class FFmpegError < StandardError; end
|
9
7
|
|
8
|
+
attr_accessor(:codecs, :formats, :decoders, :encoders)
|
10
9
|
|
11
10
|
def initialize(driver)
|
12
11
|
@driver = driver
|
12
|
+
@codecs, @formats, @decoders, @encoders = [], [], [], []
|
13
13
|
end
|
14
14
|
|
15
15
|
# execute runners with options object
|
16
16
|
#
|
17
17
|
# @param [Mediakit::Runners::FFmpeg::Options] options options to create CLI argument
|
18
|
-
def run(options)
|
18
|
+
def run(options, driver_options = {})
|
19
19
|
args = options.compose
|
20
|
-
|
20
|
+
@driver.run(args, driver_options)
|
21
21
|
end
|
22
22
|
|
23
|
-
def command(options)
|
23
|
+
def command(options, driver_options = {})
|
24
24
|
args = options.compose
|
25
|
-
@driver.command(args)
|
25
|
+
@driver.command(args, driver_options)
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
Codec = Struct.new(:name, :desc, :type, :decode, :encode, :decoders, :encoders, :intra_frame, :lossy, :lossless) do |klass|
|
29
|
+
def to_s; name; end
|
30
|
+
end
|
31
|
+
|
32
|
+
Format = Struct.new(:name, :desc, :demuxing, :muxing) do |klass|
|
33
|
+
def to_s; name; end
|
34
|
+
end
|
35
|
+
|
36
|
+
Encoder = Struct.new(:name, :desc, :type, :frame_level, :slice_level, :experimental, :horizon_band, :direct_rendering_method) do |klass|
|
37
|
+
def to_s; name; end
|
38
|
+
end
|
29
39
|
|
30
|
-
|
31
|
-
|
40
|
+
Decoder = Struct.new(:name, :desc, :type, :frame_level, :slice_level, :experimental, :horizon_band, :direct_rendering_method) do |klass|
|
41
|
+
def to_s; name; end
|
32
42
|
end
|
33
43
|
end
|
34
44
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
3
|
+
module Mediakit
|
4
|
+
module Initializers
|
5
|
+
class FFmpeg
|
6
|
+
class Base
|
7
|
+
attr_reader :items
|
8
|
+
|
9
|
+
def initialize(command)
|
10
|
+
@command = command
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
@command.send(item_type).concat(parse_items)
|
15
|
+
end
|
16
|
+
|
17
|
+
def item_type
|
18
|
+
raise(NotImplementedError)
|
19
|
+
end
|
20
|
+
|
21
|
+
def raw_items
|
22
|
+
raise(NotImplementedError)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_item(line)
|
26
|
+
raise(NotImplementedError)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_items
|
30
|
+
items = []
|
31
|
+
raw_items.each do |line|
|
32
|
+
chomped_text = line.chomp
|
33
|
+
item = create_item(chomped_text)
|
34
|
+
items << item if item
|
35
|
+
end
|
36
|
+
items
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class CodecInitializer < Base
|
41
|
+
DELIMITER_FOR_CODECS = "\n -------\n".freeze
|
42
|
+
SUPPORT_PATTERN = /(?<support>(?<decode>[D.])(?<encode>[E.])(?<type>[VAS.])(?<intra_frame>[I.])(?<lossy>[L.])(?<lossless>[S.]))/.freeze
|
43
|
+
DESCRIPTION_PATTERN = /.*?( \(decoders: (?<decoders>.+?) \)| \(encoders: (?<encoders>.+?) \))*/.freeze
|
44
|
+
PATTERN = /\A\s*#{SUPPORT_PATTERN}\s+(?<name>\w+)\s+(?<desc>(#{DESCRIPTION_PATTERN}))\z/.freeze
|
45
|
+
|
46
|
+
def item_type
|
47
|
+
'codecs'
|
48
|
+
end
|
49
|
+
|
50
|
+
def raw_items
|
51
|
+
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('codecs' => true))
|
52
|
+
output, _, _ = @command.run(options)
|
53
|
+
return [] if output.nil? || output.empty?
|
54
|
+
output.split(DELIMITER_FOR_CODECS)[1].each_line.to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_item(line)
|
58
|
+
match = line.match(PATTERN)
|
59
|
+
if match
|
60
|
+
type = case match[:type]
|
61
|
+
when "A"
|
62
|
+
:audio
|
63
|
+
when "V"
|
64
|
+
:video
|
65
|
+
when "S"
|
66
|
+
:subtitle
|
67
|
+
else
|
68
|
+
:unknown
|
69
|
+
end
|
70
|
+
decode = match[:decode] != '.'
|
71
|
+
encode = match[:encode] != '.'
|
72
|
+
decoders = match[:decoders] ? match[:decoders].split(' ') : (decode ? [match[:name]] : nil)
|
73
|
+
encoders = match[:encoders] ? match[:encoders].split(' ') : (encode ? [match[:name]] : nil)
|
74
|
+
intra_frame = match[:intra_frame] != '.'
|
75
|
+
lossy = match[:lossy] != '.'
|
76
|
+
lossless = match[:lossless] != '.'
|
77
|
+
|
78
|
+
Mediakit::FFmpeg::Codec.new(match[:name], match[:desc], type, decode, encode, decoders, encoders, intra_frame, lossy, lossless)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class FormatInitializer < Base
|
84
|
+
DELIMITER_FOR_FORMATS = "\n --\n".freeze
|
85
|
+
SUPPORT_PATTERN = /(?<support>(?<demuxing>[D.\s])(?<muxing>[E.\s]))/.freeze
|
86
|
+
PATTERN = /\A\s*#{SUPPORT_PATTERN}\s+(?<name>\w+)\s+(?<desc>.+)\z/.freeze
|
87
|
+
|
88
|
+
def item_type
|
89
|
+
'formats'
|
90
|
+
end
|
91
|
+
|
92
|
+
def raw_items
|
93
|
+
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('formats' => true))
|
94
|
+
output, _, _ = @command.run(options)
|
95
|
+
return [] if output.nil? || output.empty?
|
96
|
+
output.split(DELIMITER_FOR_FORMATS)[1].each_line.to_a
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_item(line)
|
100
|
+
match = line.match(PATTERN)
|
101
|
+
if match
|
102
|
+
demuxing = match[:demuxing] == 'D'
|
103
|
+
muxing = match[:muxing] == 'E'
|
104
|
+
|
105
|
+
Mediakit::FFmpeg::Format.new(match[:name], match[:desc], demuxing, muxing)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class DecoderInitializer < Base
|
111
|
+
DELIMITER_FOR_CODER = "\n ------\n".freeze
|
112
|
+
# Encoders:
|
113
|
+
# V..... = Video
|
114
|
+
# A..... = Audio
|
115
|
+
# S..... = Subtitle
|
116
|
+
# .F.... = Frame-level multithreading
|
117
|
+
# ..S... = Slice-level multithreading
|
118
|
+
# ...X.. = Codec is experimental
|
119
|
+
# ....B. = Supports draw_horiz_band
|
120
|
+
# .....D = Supports direct rendering method 1
|
121
|
+
# ------
|
122
|
+
SUPPORT_PATTERN = /(?<support>(?<type>[VAS.])(?<frame_level>[F.])(?<slice_level>[S.])(?<experimental>[X.])(?<horizon_band>[B.])(?<direct_rendering_method>[D.]))/.freeze
|
123
|
+
PATTERN = /\A\s*#{SUPPORT_PATTERN}\s+(?<name>\w+)\s+(?<desc>.+)\z/.freeze
|
124
|
+
|
125
|
+
def item_type
|
126
|
+
'decoders'
|
127
|
+
end
|
128
|
+
|
129
|
+
def raw_items
|
130
|
+
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('decoders' => true))
|
131
|
+
output, _, _ = @command.run(options)
|
132
|
+
return [] if output.nil? || output.empty?
|
133
|
+
output.split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_item(line)
|
137
|
+
match = line.match(PATTERN)
|
138
|
+
if match
|
139
|
+
type = case match[:type]
|
140
|
+
when 'V'
|
141
|
+
:video
|
142
|
+
when 'A'
|
143
|
+
:audio
|
144
|
+
when 'S'
|
145
|
+
:subtitle
|
146
|
+
else
|
147
|
+
:unknown
|
148
|
+
end
|
149
|
+
frame_leval = match[:frame_level] != '.'
|
150
|
+
slice_leval = match[:slice_level] != '.'
|
151
|
+
experimental = match[:experimental] != '.'
|
152
|
+
horizon_band = match[:horizon_band] != '.'
|
153
|
+
direct_rendering_method = match[:direct_rendering_method] != '.'
|
154
|
+
|
155
|
+
Mediakit::FFmpeg::Decoder.new(match[:name], match[:desc], type, frame_leval, slice_leval, experimental, horizon_band, direct_rendering_method)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class EncoderInitializer < Base
|
161
|
+
DELIMITER_FOR_CODER = "\n ------\n".freeze
|
162
|
+
# Encoders:
|
163
|
+
# V..... = Video
|
164
|
+
# A..... = Audio
|
165
|
+
# S..... = Subtitle
|
166
|
+
# .F.... = Frame-level multithreading
|
167
|
+
# ..S... = Slice-level multithreading
|
168
|
+
# ...X.. = Codec is experimental
|
169
|
+
# ....B. = Supports draw_horiz_band
|
170
|
+
# .....D = Supports direct rendering method 1
|
171
|
+
# ------
|
172
|
+
SUPPORT_PATTERN = /(?<support>(?<type>[VAS.])(?<frame_level>[F.])(?<slice_level>[S.])(?<experimental>[X.])(?<horizon_band>[B.])(?<direct_rendering_method>[D.]))/.freeze
|
173
|
+
PATTERN = /\A\s*#{SUPPORT_PATTERN}\s+(?<name>\w+)\s+(?<desc>.+)\z/.freeze
|
174
|
+
|
175
|
+
def item_type
|
176
|
+
'encoders'
|
177
|
+
end
|
178
|
+
|
179
|
+
def raw_items
|
180
|
+
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('encoders' => true))
|
181
|
+
output, _, _ = @command.run(options)
|
182
|
+
return [] if output.nil? || output.empty?
|
183
|
+
output.split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_item(line)
|
187
|
+
match = line.match(PATTERN)
|
188
|
+
if match
|
189
|
+
type = case match[:type]
|
190
|
+
when 'V'
|
191
|
+
:video
|
192
|
+
when 'A'
|
193
|
+
:audio
|
194
|
+
when 'S'
|
195
|
+
:subtitle
|
196
|
+
else
|
197
|
+
:unknown
|
198
|
+
end
|
199
|
+
frame_level = match[:frame_level] != '.'
|
200
|
+
slice_level = match[:slice_level] != '.'
|
201
|
+
experimental = match[:experimental] != '.'
|
202
|
+
horizon_band = match[:horizon_band] != '.'
|
203
|
+
direct_rendering_method = match[:direct_rendering_method] != '.'
|
204
|
+
|
205
|
+
Mediakit::FFmpeg::Encoder.new(match[:name], match[:desc], type, frame_level, slice_level, experimental, horizon_band, direct_rendering_method)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -14,7 +14,7 @@ module Mediakit
|
|
14
14
|
|
15
15
|
DEFAULT_READ_TIMEOUT_INTERVAL = 30
|
16
16
|
|
17
|
-
def initialize(timeout:
|
17
|
+
def initialize(timeout: nil, nice: 0)
|
18
18
|
@timeout = timeout
|
19
19
|
@nice = nice
|
20
20
|
end
|
@@ -30,7 +30,6 @@ module Mediakit
|
|
30
30
|
# @return exit_status [Boolean] is succeeded
|
31
31
|
def run(bin, *args)
|
32
32
|
command = build_command(bin, *args)
|
33
|
-
exit_status = nil
|
34
33
|
begin
|
35
34
|
stdin, stdout, stderr, wait_thread = Open3.popen3(command)
|
36
35
|
stdin.close
|
@@ -54,22 +53,19 @@ module Mediakit
|
|
54
53
|
|
55
54
|
def wait_with_timeout(stdout, stderr, wait_thread)
|
56
55
|
begin
|
57
|
-
|
58
|
-
|
59
|
-
loop_thread = Thread.new { loop.run }
|
56
|
+
setup_watchers(stdout, stderr)
|
57
|
+
loop_thread = Thread.new { run_loop }
|
60
58
|
wait_thread.join
|
61
59
|
exit_status = (wait_thread.value.exitstatus == 0)
|
62
60
|
rescue Timeout::Error => error
|
63
61
|
force_kill_process(wait_thread.pid)
|
64
62
|
raise(error)
|
65
63
|
ensure
|
66
|
-
|
67
|
-
err_watcher.close if err_watcher && !err_watcher.closed?
|
68
|
-
timer.detach if timer && timer.attached?
|
64
|
+
teardown_watchers
|
69
65
|
loop_thread.join if loop_thread
|
70
66
|
end
|
71
67
|
|
72
|
-
[out_watcher.data, err_watcher.data,exit_status]
|
68
|
+
[@out_watcher.data, @err_watcher.data, exit_status]
|
73
69
|
end
|
74
70
|
|
75
71
|
def build_command(bin, *args)
|
@@ -86,15 +82,31 @@ module Mediakit
|
|
86
82
|
"#{bin} #{escaped_args}"
|
87
83
|
end
|
88
84
|
|
89
|
-
def setup_watchers(
|
90
|
-
timer = TimeoutTimer.new(@timeout, Thread.current)
|
91
|
-
|
92
|
-
|
93
|
-
out_watcher.attach(loop)
|
94
|
-
err_watcher = IOWatcher.new(stderr) { timer.update }
|
95
|
-
err_watcher.attach(loop)
|
85
|
+
def setup_watchers(stdout, stderr)
|
86
|
+
@timer = TimeoutTimer.new(@timeout, Thread.current)
|
87
|
+
@out_watcher = IOWatcher.new(stdout) { @timer.update }
|
88
|
+
@err_watcher = IOWatcher.new(stderr) { @timer.update }
|
96
89
|
|
97
|
-
|
90
|
+
@loop = Coolio::Loop.new
|
91
|
+
@out_watcher.attach(@loop)
|
92
|
+
@err_watcher.attach(@loop)
|
93
|
+
@timer.attach(@loop)
|
94
|
+
end
|
95
|
+
|
96
|
+
def run_loop
|
97
|
+
@loop.run
|
98
|
+
rescue => e
|
99
|
+
# workaround for ambiguous RuntimeError
|
100
|
+
# TODO: replace logger method
|
101
|
+
warn(e.message)
|
102
|
+
warn(e.backtrace)
|
103
|
+
end
|
104
|
+
|
105
|
+
def teardown_watchers
|
106
|
+
@loop.watchers.each { |w| w.detach if w.attached? }
|
107
|
+
@out_watcher.close if @out_watcher && !@out_watcher.closed?
|
108
|
+
@err_watcher.close if @err_watcher && !@err_watcher.closed?
|
109
|
+
@loop.stop if @loop.has_active_watchers?
|
98
110
|
end
|
99
111
|
|
100
112
|
def force_kill_process(pid)
|
@@ -116,6 +128,10 @@ module Mediakit
|
|
116
128
|
@data << data
|
117
129
|
@block.call(self)
|
118
130
|
end
|
131
|
+
|
132
|
+
def on_close
|
133
|
+
@block = nil
|
134
|
+
end
|
119
135
|
end
|
120
136
|
|
121
137
|
class TimeoutTimer < Coolio::TimerWatcher
|
data/lib/mediakit/version.rb
CHANGED
data/lib/mediakit.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mediakit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ainame
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -104,28 +104,25 @@ extensions: []
|
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
106
|
- ".gitignore"
|
107
|
-
- ".travis.yml"
|
108
107
|
- Gemfile
|
109
108
|
- README.md
|
110
109
|
- Rakefile
|
111
110
|
- bin/console
|
112
111
|
- bin/setup
|
112
|
+
- circle.yml
|
113
113
|
- lib/mediakit.rb
|
114
114
|
- lib/mediakit/drivers.rb
|
115
115
|
- lib/mediakit/ffmpeg.rb
|
116
|
-
- lib/mediakit/ffmpeg/introspection.rb
|
117
116
|
- lib/mediakit/ffmpeg/options.rb
|
118
117
|
- lib/mediakit/ffprobe.rb
|
118
|
+
- lib/mediakit/initializers.rb
|
119
|
+
- lib/mediakit/initializers/ffmpeg.rb
|
119
120
|
- lib/mediakit/utils/process_runner.rb
|
120
121
|
- lib/mediakit/utils/shell_escape.rb
|
121
122
|
- lib/mediakit/version.rb
|
122
123
|
- mediakit.gemspec
|
123
124
|
- sample/configure.rb
|
124
125
|
- sample/raw_crop.rb
|
125
|
-
- templates/codec.rb.erb
|
126
|
-
- templates/decoder.rb.erb
|
127
|
-
- templates/encoder.rb.erb
|
128
|
-
- templates/format.rb.erb
|
129
126
|
homepage: https://github.com/ainame/mediakit
|
130
127
|
licenses: []
|
131
128
|
metadata: {}
|
data/.travis.yml
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module Mediakit
|
2
|
-
class FFmpeg
|
3
|
-
module Introspection
|
4
|
-
DELIMITER_FOR_CODECS = "\n -------\n".freeze
|
5
|
-
DELIMITER_FOR_FORMATS = "\n --\n".freeze
|
6
|
-
DELIMITER_FOR_CODER = "\n ------\n".freeze
|
7
|
-
|
8
|
-
def codecs
|
9
|
-
@codecs ||= run(global_options('codecs')).split(DELIMITER_FOR_CODECS)[1].each_line.to_a
|
10
|
-
end
|
11
|
-
|
12
|
-
def formats
|
13
|
-
@formats ||= run(global_options('formats')).split(DELIMITER_FOR_FORMATS)[1].each_line.to_a
|
14
|
-
end
|
15
|
-
|
16
|
-
def decoders
|
17
|
-
@decoders ||= run(global_options('decoders')).split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
18
|
-
end
|
19
|
-
|
20
|
-
def encoders
|
21
|
-
@encoders ||= run(global_options('encoders')).split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
22
|
-
end
|
23
|
-
|
24
|
-
def global_options(flag)
|
25
|
-
Options.new(Options::GlobalOption.new(flag => true))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/templates/codec.rb.erb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Mediakit
|
2
|
-
module Codecs
|
3
|
-
module Video
|
4
|
-
<% @items.select{|x| x.type == :video }.each do |item| %>
|
5
|
-
class Codec<%= item.name.classify %>
|
6
|
-
def name; '<%= item.name %>' end
|
7
|
-
def encoders; <%= item.encoders.inspect %>; end
|
8
|
-
def decoders; <%= item.decoders.inspect %>; end
|
9
|
-
end
|
10
|
-
<% end %>
|
11
|
-
end
|
12
|
-
|
13
|
-
module Audio
|
14
|
-
<% @items.select{|x| x.type == :audio }.each do |item| %>
|
15
|
-
class Codec<%= item.name.classify %>
|
16
|
-
def name; '<%= item.name %>' end
|
17
|
-
def encoders; <%= item.encoders.inspect %>; end
|
18
|
-
def decoders; <%= item.decoders.inspect %>; end
|
19
|
-
end
|
20
|
-
<% end %>
|
21
|
-
end
|
22
|
-
|
23
|
-
module Subtitle
|
24
|
-
<% @items.select{|x| x.type == :subtitle }.each do |item| %>
|
25
|
-
class Codec<%= item.name.classify %>
|
26
|
-
def name; '<%= item.name %>' end
|
27
|
-
def encoders; <%= item.encoders.inspect %>; end
|
28
|
-
def decoders; <%= item.decoders.inspect %>; end
|
29
|
-
end
|
30
|
-
<% end %>
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/templates/decoder.rb.erb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Mediakit
|
2
|
-
module Decoders
|
3
|
-
module Video
|
4
|
-
<% @items.select{|x| x.type == :video }.each do |item| %>
|
5
|
-
class Decoder<%= item.name.classify %>
|
6
|
-
def name; '<%= item.name %>' end
|
7
|
-
end
|
8
|
-
<% end %>
|
9
|
-
end
|
10
|
-
|
11
|
-
module Audio
|
12
|
-
<% @items.select{|x| x.type == :audio }.each do |item| %>
|
13
|
-
class Decoder<%= item.name.classify %>
|
14
|
-
def name; '<%= item.name %>' end
|
15
|
-
end
|
16
|
-
<% end %>
|
17
|
-
end
|
18
|
-
|
19
|
-
module Subtitle
|
20
|
-
<% @items.select{|x| x.type == :subtitle }.each do |item| %>
|
21
|
-
class Decoder<%= item.name.classify %>
|
22
|
-
def name; '<%= item.name %>' end
|
23
|
-
end
|
24
|
-
<% end %>
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/templates/encoder.rb.erb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Mediakit
|
2
|
-
module Encoders
|
3
|
-
module Video
|
4
|
-
<% @items.select{|x| x.type == :video }.each do |item| %>
|
5
|
-
class Encoder<%= item.name.classify %>
|
6
|
-
def name; '<%= item.name %>' end
|
7
|
-
end
|
8
|
-
<% end %>
|
9
|
-
end
|
10
|
-
|
11
|
-
module Audio
|
12
|
-
<% @items.select{|x| x.type == :audio }.each do |item| %>
|
13
|
-
class Encoder<%= item.name.classify %>
|
14
|
-
def name; '<%= item.name %>' end
|
15
|
-
end
|
16
|
-
<% end %>
|
17
|
-
end
|
18
|
-
|
19
|
-
module Subtitle
|
20
|
-
<% @items.select{|x| x.type == :subtitle }.each do |item| %>
|
21
|
-
class Encoder<%= item.name.classify %>
|
22
|
-
def name; '<%= item.name %>' end
|
23
|
-
end
|
24
|
-
<% end %>
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|