mediakit 0.0.12 → 0.1.1
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/.codeclimate.yml +8 -0
- data/.gitignore +1 -1
- data/README.md +21 -2
- data/bin/console +2 -0
- data/lib/mediakit/drivers.rb +4 -5
- data/lib/mediakit/ffmpeg/options.rb +8 -0
- data/lib/mediakit/ffmpeg.rb +75 -11
- data/lib/mediakit/initializers/ffmpeg.rb +137 -84
- data/lib/mediakit/{utils/process_runner.rb → process/runner.rb} +15 -28
- data/lib/mediakit/{utils → process}/shell_escape.rb +1 -1
- data/lib/mediakit/railtie.rb +8 -0
- data/lib/mediakit/utils/constant_factory.rb +35 -0
- data/lib/mediakit/utils/null_logger.rb +13 -0
- data/lib/mediakit/version.rb +1 -1
- data/lib/mediakit.rb +2 -0
- data/mediakit.gemspec +1 -0
- data/sample/{raw_crop.rb → crop.rb} +0 -0
- data/sample/encode.rb +42 -0
- data/sample/instropections.rb +12 -0
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00fe5dc8bf1a3f9d14614c1ae42ccdccf3f7941b
|
4
|
+
data.tar.gz: 660ba4fe4cae816c0c30e05e340dbdd8a299dd4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19923e14f8f23410b9b6a202c53287f8ad6dbd70d93f695ddd74e5f612382d8353a15a0c8ec0bab49665367213ef502ab5ef32d59eeec7849357ea0d265ae4cd
|
7
|
+
data.tar.gz: 4779b3998cac878a698f9d3885df1e9ff00aef8de93c7fbd37f7ba7a40f37c26ece71059d22ca464b01409ee8ac6c7a177fe9367009d2767ffda1c770a967306
|
data/.codeclimate.yml
ADDED
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Mediakit
|
2
2
|
|
3
|
-
[](https://circleci.com/gh/ainame/mediakit/tree/master)
|
3
|
+
[](https://circleci.com/gh/ainame/mediakit/tree/master) [](https://codeclimate.com/github/ainame/mediakit)
|
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.
|
@@ -13,13 +13,14 @@ I've design this library for following purpose.
|
|
13
13
|
Currently you can use low-level inteface of mediakit!
|
14
14
|
|
15
15
|
* [x] low-level interface
|
16
|
-
* [x] execute command for ffmpeg
|
16
|
+
* [x] execute command for ffmpeg as a code
|
17
17
|
* [x] unit testing supports (fake driver)
|
18
18
|
* [x] nice command setting
|
19
19
|
* [x] read timeout setting
|
20
20
|
* [x] shell escape for security
|
21
21
|
* [x] ffmpeg instropection (retrive supported formats, codecs, encoders and decoders)
|
22
22
|
* [x] logger support
|
23
|
+
* [x] formats, codecs, encoders and decoders representation as a code
|
23
24
|
* [ ] low-level ffprobe interface
|
24
25
|
* [ ] high-level interface for ffmpeg
|
25
26
|
* [ ] low-level interface for sox
|
@@ -124,7 +125,25 @@ assert_equal(exit_status, true)
|
|
124
125
|
fake_driver.reset
|
125
126
|
```
|
126
127
|
|
128
|
+
### Formats/Codecs/Decoders/Encoders
|
127
129
|
|
130
|
+
FFmpeg has so many formats, codecs, decoders and encoders.
|
131
|
+
So, mediakit provide constant values for presenting those resources as a code.
|
132
|
+
|
133
|
+
```rb
|
134
|
+
ffmpeg = Mediakit::FFmpeg.create
|
135
|
+
ffmpeg.init
|
136
|
+
|
137
|
+
# can use after call Mediakit::FFmpeg#init
|
138
|
+
Mediakit::FFmpeg::AudioCodec::CODEC_MP3 #=> #<Mediakit::FFmpeg::AudioCodec:0x007fb514040ad0>
|
139
|
+
Mediakit::FFmpeg::AudioCodec::CODEC_MP3.name #=> "mp3"
|
140
|
+
Mediakit::FFmpeg::AudioCodec::CODEC_MP3.desc #=> "MP3 (MPEG audio layer 3)"
|
141
|
+
...
|
142
|
+
|
143
|
+
```
|
144
|
+
|
145
|
+
These values reflect your ffmpeg binary build conditions by automate.
|
146
|
+
`Mediakit::FFmpeg::AudioCodec::CODEC_MP3` is just a instance of Mediakit::FFmpeg::AudioCodec class.
|
128
147
|
### High Level Interface
|
129
148
|
|
130
149
|
TBD
|
data/bin/console
CHANGED
data/lib/mediakit/drivers.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'mediakit/utils/process_runner'
|
1
|
+
require 'mediakit/process/runner'
|
3
2
|
|
4
3
|
module Mediakit
|
5
4
|
module Drivers
|
@@ -34,12 +33,12 @@ module Mediakit
|
|
34
33
|
# @return [String] stdout output
|
35
34
|
def run(*args)
|
36
35
|
options, rest_args = parse_options(args.dup)
|
37
|
-
runner = Mediakit::
|
36
|
+
runner = Mediakit::Process::Runner.new(options)
|
38
37
|
begin
|
39
38
|
stdout, stderr, exit_status = runner.run(bin, *rest_args)
|
40
39
|
raise(FailError, stderr) unless exit_status
|
41
40
|
stdout
|
42
|
-
rescue Mediakit::
|
41
|
+
rescue Mediakit::Process::Runner::CommandNotFoundError => e
|
43
42
|
raise(ConfigurationError, "cant' find bin in #{bin}.")
|
44
43
|
end
|
45
44
|
end
|
@@ -53,7 +52,7 @@ module Mediakit
|
|
53
52
|
# @return [String] command
|
54
53
|
def command(*args)
|
55
54
|
options, rest_args = parse_options(args.dup)
|
56
|
-
runner = Mediakit::
|
55
|
+
runner = Mediakit::Process::Runner.new(options)
|
57
56
|
runner.build_command(bin, *rest_args)
|
58
57
|
end
|
59
58
|
|
@@ -24,10 +24,12 @@ module Mediakit
|
|
24
24
|
return if option.nil?
|
25
25
|
case option
|
26
26
|
when GlobalOption
|
27
|
+
raise(ArgumentError, 'you can give only a GlobalOption.') if @global
|
27
28
|
set_global(option)
|
28
29
|
when InputFileOption
|
29
30
|
add_input(option)
|
30
31
|
when OutputFileOption
|
32
|
+
raise(ArgumentError, 'you can give only a OutputFileOption.') if @output
|
31
33
|
set_output(option)
|
32
34
|
else
|
33
35
|
raise(ArgumentError)
|
@@ -96,6 +98,12 @@ module Mediakit
|
|
96
98
|
end
|
97
99
|
|
98
100
|
class GlobalOption < OrderedHash
|
101
|
+
def initialize(options = {})
|
102
|
+
if options.values.any? { |x| x.kind_of?(Hash) }
|
103
|
+
raise(ArgumentError, 'you can\'t give nested Hash in GlobalOption')
|
104
|
+
end
|
105
|
+
super
|
106
|
+
end
|
99
107
|
end
|
100
108
|
|
101
109
|
class OptionPathPair
|
data/lib/mediakit/ffmpeg.rb
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
require 'mediakit/drivers'
|
2
2
|
require 'mediakit/ffmpeg/options'
|
3
|
+
require 'mediakit/utils/constant_factory'
|
3
4
|
|
4
5
|
module Mediakit
|
5
6
|
class FFmpeg
|
6
|
-
class FFmpegError < StandardError;
|
7
|
+
class FFmpegError < StandardError;
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader(:codecs, :formats, :decoders, :encoders)
|
7
11
|
|
8
|
-
|
12
|
+
def self.create(driver = Mediakit::Drivers::FFmpeg.new)
|
13
|
+
@ffmpeg ||= new(driver)
|
14
|
+
end
|
9
15
|
|
10
16
|
def initialize(driver)
|
11
|
-
@driver
|
17
|
+
@driver = driver
|
12
18
|
@codecs, @formats, @decoders, @encoders = [], [], [], []
|
13
19
|
end
|
14
20
|
|
21
|
+
def init
|
22
|
+
Mediakit::Initializers::FFmpeg.setup(self)
|
23
|
+
end
|
24
|
+
|
15
25
|
# execute runners with options object
|
16
26
|
#
|
17
27
|
# @param [Mediakit::Runners::FFmpeg::Options] options options to create CLI argument
|
@@ -25,20 +35,74 @@ module Mediakit
|
|
25
35
|
@driver.command(args, driver_options)
|
26
36
|
end
|
27
37
|
|
28
|
-
|
29
|
-
def
|
38
|
+
module BaseTypeMatcher
|
39
|
+
def self.included(included_class)
|
40
|
+
define_method(:===) do |other|
|
41
|
+
return true if included_class::Base >= other
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
included_class.send(:module_function, :===)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Format
|
50
|
+
def self.using_attributes
|
51
|
+
[:name, :desc, :demuxing, :muxing]
|
52
|
+
end
|
53
|
+
|
54
|
+
include Utils::ConstantFactory
|
55
|
+
end
|
56
|
+
|
57
|
+
class Codec
|
58
|
+
def self.using_attributes
|
59
|
+
[:name, :desc, :type, :decode, :encode, :decoders, :encoders, :intra_frame, :lossy, :lossless]
|
60
|
+
end
|
61
|
+
|
62
|
+
include Utils::ConstantFactory
|
63
|
+
end
|
64
|
+
|
65
|
+
class AudioCodec < Codec
|
66
|
+
end
|
67
|
+
|
68
|
+
class VideoCodec < Codec
|
69
|
+
end
|
70
|
+
|
71
|
+
class SubtitleCodec < Codec
|
72
|
+
end
|
73
|
+
|
74
|
+
class Encoder
|
75
|
+
def self.using_attributes
|
76
|
+
[:name, :desc, :type, :frame_level, :slice_level, :experimental, :horizon_band, :direct_rendering_method]
|
77
|
+
end
|
78
|
+
|
79
|
+
include Utils::ConstantFactory
|
80
|
+
end
|
81
|
+
|
82
|
+
class AudioEncoder < Encoder
|
83
|
+
end
|
84
|
+
|
85
|
+
class VideoEncoder < Encoder
|
86
|
+
end
|
87
|
+
|
88
|
+
class SubtitleEncoder < Encoder
|
89
|
+
end
|
90
|
+
|
91
|
+
class Decoder
|
92
|
+
def self.using_attributes
|
93
|
+
[:name, :desc, :type, :frame_level, :slice_level, :experimental, :horizon_band, :direct_rendering_method]
|
94
|
+
end
|
95
|
+
|
96
|
+
include Utils::ConstantFactory
|
30
97
|
end
|
31
98
|
|
32
|
-
|
33
|
-
def to_s; name; end
|
99
|
+
class AudioDecoder < Decoder
|
34
100
|
end
|
35
101
|
|
36
|
-
|
37
|
-
def to_s; name; end
|
102
|
+
class VideoDecoder < Decoder
|
38
103
|
end
|
39
104
|
|
40
|
-
|
41
|
-
def to_s; name; end
|
105
|
+
class SubtitleDecoder < Decoder
|
42
106
|
end
|
43
107
|
end
|
44
108
|
end
|
@@ -3,6 +3,16 @@ require 'active_support/core_ext/string/inflections'
|
|
3
3
|
module Mediakit
|
4
4
|
module Initializers
|
5
5
|
class FFmpeg
|
6
|
+
def self.setup(ffmpeg)
|
7
|
+
Mediakit::Initializers::FFmpeg::FormatInitializer.new(ffmpeg).call
|
8
|
+
Mediakit::Initializers::FFmpeg::DecoderInitializer.new(ffmpeg).call
|
9
|
+
Mediakit::Initializers::FFmpeg::EncoderInitializer.new(ffmpeg).call
|
10
|
+
Mediakit::Initializers::FFmpeg::CodecInitializer.new(ffmpeg).call
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnknownTypeError < StandardError
|
14
|
+
end
|
15
|
+
|
6
16
|
class Base
|
7
17
|
attr_reader :items
|
8
18
|
|
@@ -12,6 +22,7 @@ module Mediakit
|
|
12
22
|
|
13
23
|
def call
|
14
24
|
@command.send(item_type).concat(parse_items)
|
25
|
+
@command.send(item_type).freeze
|
15
26
|
end
|
16
27
|
|
17
28
|
def item_type
|
@@ -37,49 +48,6 @@ module Mediakit
|
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
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
51
|
class FormatInitializer < Base
|
84
52
|
DELIMITER_FOR_FORMATS = "\n --\n".freeze
|
85
53
|
SUPPORT_PATTERN = /(?<support>(?<demuxing>[D.\s])(?<muxing>[E.\s]))/.freeze
|
@@ -91,7 +59,7 @@ module Mediakit
|
|
91
59
|
|
92
60
|
def raw_items
|
93
61
|
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('formats' => true))
|
94
|
-
output, _, _ = @command.run(options)
|
62
|
+
output, _, _ = @command.run(options, logger: nil)
|
95
63
|
return [] if output.nil? || output.empty?
|
96
64
|
output.split(DELIMITER_FOR_FORMATS)[1].each_line.to_a
|
97
65
|
end
|
@@ -99,10 +67,15 @@ module Mediakit
|
|
99
67
|
def create_item(line)
|
100
68
|
match = line.match(PATTERN)
|
101
69
|
if match
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
70
|
+
attributes = {
|
71
|
+
name: match[:name],
|
72
|
+
desc: match[:desc],
|
73
|
+
demuxing: match[:demuxing] == 'D',
|
74
|
+
muxing: match[:muxing] == 'E'
|
75
|
+
}
|
76
|
+
Mediakit::FFmpeg::Format.create_constant(
|
77
|
+
"FORMAT_#{attributes[:name].underscore.upcase}", attributes
|
78
|
+
)
|
106
79
|
end
|
107
80
|
end
|
108
81
|
end
|
@@ -128,7 +101,7 @@ module Mediakit
|
|
128
101
|
|
129
102
|
def raw_items
|
130
103
|
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('decoders' => true))
|
131
|
-
output, _, _ = @command.run(options)
|
104
|
+
output, _, _ = @command.run(options, logger: nil)
|
132
105
|
return [] if output.nil? || output.empty?
|
133
106
|
output.split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
134
107
|
end
|
@@ -136,23 +109,31 @@ module Mediakit
|
|
136
109
|
def create_item(line)
|
137
110
|
match = line.match(PATTERN)
|
138
111
|
if match
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
112
|
+
attributes = {
|
113
|
+
name: match[:name],
|
114
|
+
desc: match[:desc],
|
115
|
+
frame_level: match[:frame_level] != '.',
|
116
|
+
slice_level: match[:slice_level] != '.',
|
117
|
+
experimental: match[:experimental] != '.',
|
118
|
+
horizon_band: match[:horizon_band] != '.',
|
119
|
+
direct_rendering_method: match[:direct_rendering_method] != '.'
|
120
|
+
}
|
121
|
+
case match[:type]
|
122
|
+
when 'V'
|
123
|
+
Mediakit::FFmpeg::VideoDecoder.create_constant(
|
124
|
+
"DECODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :video)
|
125
|
+
)
|
126
|
+
when 'A'
|
127
|
+
Mediakit::FFmpeg::AudioDecoder.create_constant(
|
128
|
+
"DECODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :audio)
|
129
|
+
)
|
130
|
+
when 'S'
|
131
|
+
Mediakit::FFmpeg::SubtitleDecoder.create_constant(
|
132
|
+
"DECODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :subtitle)
|
133
|
+
)
|
134
|
+
else
|
135
|
+
raise(UnknownTypeError)
|
136
|
+
end
|
156
137
|
end
|
157
138
|
end
|
158
139
|
end
|
@@ -178,7 +159,7 @@ module Mediakit
|
|
178
159
|
|
179
160
|
def raw_items
|
180
161
|
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('encoders' => true))
|
181
|
-
output, _, _ = @command.run(options)
|
162
|
+
output, _, _ = @command.run(options, logger: nil)
|
182
163
|
return [] if output.nil? || output.empty?
|
183
164
|
output.split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
184
165
|
end
|
@@ -186,23 +167,95 @@ module Mediakit
|
|
186
167
|
def create_item(line)
|
187
168
|
match = line.match(PATTERN)
|
188
169
|
if match
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
170
|
+
attributes = {
|
171
|
+
name: match[:name],
|
172
|
+
desc: match[:desc],
|
173
|
+
frame_level: match[:frame_level] != '.',
|
174
|
+
slice_level: match[:slice_level] != '.',
|
175
|
+
experimental: match[:experimental] != '.',
|
176
|
+
horizon_band: match[:horizon_band] != '.',
|
177
|
+
direct_rendering_method: match[:direct_rendering_method] != '.'
|
178
|
+
}
|
179
|
+
|
180
|
+
case match[:type]
|
181
|
+
when 'V'
|
182
|
+
Mediakit::FFmpeg::VideoEncoder.create_constant(
|
183
|
+
"ENCODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :video)
|
184
|
+
)
|
185
|
+
when 'A'
|
186
|
+
Mediakit::FFmpeg::AudioEncoder.create_constant(
|
187
|
+
"ENCODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :audio)
|
188
|
+
)
|
189
|
+
when 'S'
|
190
|
+
Mediakit::FFmpeg::SubtitleEncoder.create_constant(
|
191
|
+
"ENCODER_#{attributes[:name].underscore.upcase}", attributes.merge(type: :subtitle)
|
192
|
+
)
|
193
|
+
else
|
194
|
+
raise(UnknownTypeError)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class CodecInitializer < Base
|
201
|
+
DELIMITER_FOR_CODECS = "\n -------\n".freeze
|
202
|
+
SUPPORT_PATTERN = /(?<support>(?<decode>[D.])(?<encode>[E.])(?<type>[VAS.])(?<intra_frame>[I.])(?<lossy>[L.])(?<lossless>[S.]))/.freeze
|
203
|
+
DESCRIPTION_PATTERN = /.*?( \(decoders: (?<decoders>.+?) \)| \(encoders: (?<encoders>.+?) \))*/.freeze
|
204
|
+
PATTERN = /\A\s*#{SUPPORT_PATTERN}\s+(?<name>\w+)\s+(?<desc>(#{DESCRIPTION_PATTERN}))\z/.freeze
|
205
|
+
|
206
|
+
def item_type
|
207
|
+
'codecs'
|
208
|
+
end
|
209
|
+
|
210
|
+
def raw_items
|
211
|
+
options = Mediakit::FFmpeg::Options.new(Mediakit::FFmpeg::Options::GlobalOption.new('codecs' => true))
|
212
|
+
output, _, _ = @command.run(options, logger: nil)
|
213
|
+
return [] if output.nil? || output.empty?
|
214
|
+
output.split(DELIMITER_FOR_CODECS)[1].each_line.to_a
|
215
|
+
end
|
216
|
+
|
217
|
+
def find_decoders(decoders)
|
218
|
+
return unless decoders
|
219
|
+
decoders.map { |name| @command.decoders.find { |decoder| decoder.name == name } }
|
220
|
+
end
|
221
|
+
|
222
|
+
def find_encoders(encoders)
|
223
|
+
return unless encoders
|
224
|
+
encoders.map { |name| @command.encoders.find { |encoder| encoder.name == name } }
|
225
|
+
end
|
226
|
+
|
227
|
+
def create_item(line)
|
228
|
+
match = line.match(PATTERN)
|
229
|
+
if match
|
230
|
+
decode = match[:decode] != '.'
|
231
|
+
encode = match[:encode] != '.'
|
232
|
+
attributes = {
|
233
|
+
name: match[:name],
|
234
|
+
desc: match[:desc],
|
235
|
+
decode: decode,
|
236
|
+
encode: encode,
|
237
|
+
decoders: find_decoders(match[:decoders] ? match[:decoders].split(' ') : (decode ? [match[:name]] : nil)),
|
238
|
+
encoders: find_encoders(match[:encoders] ? match[:encoders].split(' ') : (encode ? [match[:name]] : nil)),
|
239
|
+
intra_frame: match[:intra_frame] != '.',
|
240
|
+
lossy: match[:lossy] != '.',
|
241
|
+
lossless: match[:lossless] != '.'
|
242
|
+
}
|
243
|
+
case match[:type]
|
244
|
+
when 'V'
|
245
|
+
Mediakit::FFmpeg::VideoCodec.create_constant(
|
246
|
+
"CODEC_#{attributes[:name].underscore.upcase}", attributes.merge(type: :video)
|
247
|
+
)
|
248
|
+
when 'A'
|
249
|
+
Mediakit::FFmpeg::AudioCodec.create_constant(
|
250
|
+
"CODEC_#{attributes[:name].underscore.upcase}", attributes.merge(type: :audio)
|
251
|
+
)
|
252
|
+
when 'S'
|
253
|
+
Mediakit::FFmpeg::SubtitleCodec.create_constant(
|
254
|
+
"CODEC_#{attributes[:name].underscore.upcase}", attributes.merge(type: :subtitle)
|
255
|
+
)
|
256
|
+
else
|
257
|
+
raise(UnknownTypeError)
|
258
|
+
end
|
206
259
|
end
|
207
260
|
end
|
208
261
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'open3'
|
2
|
-
require 'thread'
|
3
2
|
require 'timeout'
|
4
3
|
require 'cool.io'
|
5
4
|
require 'logger'
|
6
|
-
require 'mediakit/
|
5
|
+
require 'mediakit/process/shell_escape'
|
6
|
+
require 'mediakit/utils/null_logger'
|
7
7
|
|
8
8
|
module Mediakit
|
9
|
-
module
|
10
|
-
class
|
9
|
+
module Process
|
10
|
+
class Runner
|
11
11
|
class CommandNotFoundError < StandardError;
|
12
12
|
end
|
13
13
|
class TimeoutError < StandardError;
|
@@ -17,10 +17,10 @@ module Mediakit
|
|
17
17
|
|
18
18
|
attr_reader(:logger)
|
19
19
|
|
20
|
-
def initialize(timeout: nil, nice: 0, logger:
|
20
|
+
def initialize(timeout: nil, nice: 0, logger: Logger.new(STDOUT))
|
21
21
|
@timeout = timeout
|
22
22
|
@nice = nice
|
23
|
-
@logger = logger ||
|
23
|
+
@logger = logger || Mediakit::Utils::NullLogger.new
|
24
24
|
end
|
25
25
|
|
26
26
|
# @overload run(command, *args)
|
@@ -37,11 +37,7 @@ module Mediakit
|
|
37
37
|
begin
|
38
38
|
stdin, stdout, stderr, wait_thread = Open3.popen3(command)
|
39
39
|
stdin.close
|
40
|
-
output, error_output, exit_status =
|
41
|
-
wait_with_timeout(stdout, stderr, wait_thread)
|
42
|
-
else
|
43
|
-
wait_without_timeout(stdout, stderr, wait_thread)
|
44
|
-
end
|
40
|
+
output, error_output, exit_status = wait(stdout, stderr, wait_thread)
|
45
41
|
rescue Errno::ENOENT => e
|
46
42
|
raise(CommandNotFoundError, "Can't find command - #{command}, #{e.meessage}")
|
47
43
|
end
|
@@ -49,13 +45,7 @@ module Mediakit
|
|
49
45
|
[output, error_output, exit_status]
|
50
46
|
end
|
51
47
|
|
52
|
-
def
|
53
|
-
wait_thread.join
|
54
|
-
exit_status = (wait_thread.value.exitstatus == 0)
|
55
|
-
[stdout.read, stderr.read, exit_status]
|
56
|
-
end
|
57
|
-
|
58
|
-
def wait_with_timeout(stdout, stderr, wait_thread)
|
48
|
+
def wait(stdout, stderr, wait_thread)
|
59
49
|
begin
|
60
50
|
setup_watchers(stdout, stderr)
|
61
51
|
loop_thread = Thread.new { run_loop }
|
@@ -87,13 +77,13 @@ module Mediakit
|
|
87
77
|
end
|
88
78
|
|
89
79
|
def setup_watchers(stdout, stderr)
|
90
|
-
@timer = TimeoutTimer.new(@timeout, Thread.current)
|
91
|
-
@out_watcher = IOWatcher.new(stdout) { |data| @timer.update; logger.info(data) }
|
92
|
-
@err_watcher = IOWatcher.new(stderr) { |data| @timer.update; logger.error(data) }
|
80
|
+
@timer = @timeout ? TimeoutTimer.new(@timeout, Thread.current) : nil
|
81
|
+
@out_watcher = IOWatcher.new(stdout) { |data| @timer.update if @timer; logger.info(data) }
|
82
|
+
@err_watcher = IOWatcher.new(stderr) { |data| @timer.update if @timer; logger.error(data) }
|
93
83
|
@loop = Coolio::Loop.new
|
94
84
|
@out_watcher.attach(@loop)
|
95
85
|
@err_watcher.attach(@loop)
|
96
|
-
@timer.attach(@loop)
|
86
|
+
@timer.attach(@loop) if @timer
|
97
87
|
end
|
98
88
|
|
99
89
|
def run_loop
|
@@ -116,7 +106,7 @@ module Mediakit
|
|
116
106
|
end
|
117
107
|
|
118
108
|
def force_kill_process(pid)
|
119
|
-
Process.kill('SIGKILL', pid)
|
109
|
+
::Process.kill('SIGKILL', pid)
|
120
110
|
rescue Errno::ESRCH => e
|
121
111
|
logger.warn("fail SIGKILL pid=#{pid} - #{e.message}, #{e.backtrace.join("\n")}")
|
122
112
|
end
|
@@ -131,8 +121,8 @@ module Mediakit
|
|
131
121
|
end
|
132
122
|
|
133
123
|
def on_read(data)
|
134
|
-
@data << data
|
135
124
|
@block.call(data)
|
125
|
+
@data << data
|
136
126
|
end
|
137
127
|
|
138
128
|
def on_close
|
@@ -144,7 +134,6 @@ module Mediakit
|
|
144
134
|
DEFAULT_CHECK_INTERVAL = 0.1
|
145
135
|
|
146
136
|
def initialize(duration, current_thread)
|
147
|
-
@mutex = Mutex.new
|
148
137
|
@duration = duration
|
149
138
|
@watched_at = Time.now
|
150
139
|
@current_thread = current_thread
|
@@ -158,9 +147,7 @@ module Mediakit
|
|
158
147
|
end
|
159
148
|
|
160
149
|
def update
|
161
|
-
@
|
162
|
-
@watched_at = Time.now
|
163
|
-
end
|
150
|
+
@watched_at = Time.now
|
164
151
|
end
|
165
152
|
|
166
153
|
private
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mediakit
|
2
|
+
module Utils
|
3
|
+
module ConstantFactory
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.class_eval do
|
7
|
+
attr_reader(*(base.using_attributes))
|
8
|
+
|
9
|
+
def initialize(attributes)
|
10
|
+
self.class.using_attributes.each do |key|
|
11
|
+
instance_variable_set("@#{key}", attributes[key])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def using_attributes
|
19
|
+
raise(NotImplementedError)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_constant(name, attributes)
|
23
|
+
raise(
|
24
|
+
ArgumentError,
|
25
|
+
<<EOS
|
26
|
+
you can give attribute keys which only defined by `using_attributes` and that is satisfied all keys.'
|
27
|
+
EOS
|
28
|
+
) unless Set.new(using_attributes) == Set.new(attributes.keys)
|
29
|
+
return self.const_get(name) if self.const_defined?(name)
|
30
|
+
self.const_set(name, new(attributes).freeze)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/mediakit/version.rb
CHANGED
data/lib/mediakit.rb
CHANGED
data/mediakit.gemspec
CHANGED
File without changes
|
data/sample/encode.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mediakit"
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
input_file = ARGV[0]
|
8
|
+
exit(1) unless input_file
|
9
|
+
|
10
|
+
output_file = ARGV[1] || 'out.mov'
|
11
|
+
|
12
|
+
def transcode_option(input, output)
|
13
|
+
options = Mediakit::FFmpeg::Options.new(
|
14
|
+
Mediakit::FFmpeg::Options::GlobalOption.new(
|
15
|
+
'y' => true,
|
16
|
+
'threads' => 4,
|
17
|
+
't' => 10
|
18
|
+
),
|
19
|
+
Mediakit::FFmpeg::Options::InputFileOption.new(
|
20
|
+
options: nil,
|
21
|
+
path: input,
|
22
|
+
),
|
23
|
+
Mediakit::FFmpeg::Options::OutputFileOption.new(
|
24
|
+
options: {
|
25
|
+
'acodec' => 'mp3',
|
26
|
+
'vcodec' => 'libx264',
|
27
|
+
#'vf' => 'crop=240:240:0:0',
|
28
|
+
'ar' => '44100',
|
29
|
+
'ab' => '128k',
|
30
|
+
},
|
31
|
+
path: output,
|
32
|
+
),
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
root = File.expand_path(File.join(File.dirname(__FILE__), '../'))
|
37
|
+
input_path = File.expand_path(input_file)
|
38
|
+
output_path = File.expand_path(File.join(root, 'out.mov'))
|
39
|
+
ffmpeg = Mediakit::FFmpeg.create
|
40
|
+
options = transcode_option(input_path, output_path)
|
41
|
+
puts "$ #{ffmpeg.command(options, nice: 10)}"
|
42
|
+
puts ffmpeg.run(options, nice: 10, timeout: 30)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mediakit"
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
ffmpeg = Mediakit::FFmpeg.new(Mediakit::Drivers::FFmpeg.new)
|
8
|
+
Mediakit::Initializers.setup(ffmpeg)
|
9
|
+
|
10
|
+
binding.pry
|
11
|
+
|
12
|
+
# ffmpeg.codecs.select {|c| c.name =~ /264/ } ...
|
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.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ainame
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0.31'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: minitest-power_assert
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.2'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.2'
|
111
125
|
description: |
|
112
126
|
mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
113
127
|
you can create complex manipulation for media as a ruby code.
|
@@ -117,6 +131,7 @@ executables: []
|
|
117
131
|
extensions: []
|
118
132
|
extra_rdoc_files: []
|
119
133
|
files:
|
134
|
+
- ".codeclimate.yml"
|
120
135
|
- ".gitignore"
|
121
136
|
- Gemfile
|
122
137
|
- README.md
|
@@ -131,12 +146,17 @@ files:
|
|
131
146
|
- lib/mediakit/ffprobe.rb
|
132
147
|
- lib/mediakit/initializers.rb
|
133
148
|
- lib/mediakit/initializers/ffmpeg.rb
|
134
|
-
- lib/mediakit/
|
135
|
-
- lib/mediakit/
|
149
|
+
- lib/mediakit/process/runner.rb
|
150
|
+
- lib/mediakit/process/shell_escape.rb
|
151
|
+
- lib/mediakit/railtie.rb
|
152
|
+
- lib/mediakit/utils/constant_factory.rb
|
153
|
+
- lib/mediakit/utils/null_logger.rb
|
136
154
|
- lib/mediakit/version.rb
|
137
155
|
- mediakit.gemspec
|
138
156
|
- sample/configure.rb
|
139
|
-
- sample/
|
157
|
+
- sample/crop.rb
|
158
|
+
- sample/encode.rb
|
159
|
+
- sample/instropections.rb
|
140
160
|
homepage: https://github.com/ainame/mediakit
|
141
161
|
licenses: []
|
142
162
|
metadata: {}
|