mediakit 0.0.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 +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +96 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/mediakit/drivers.rb +138 -0
- data/lib/mediakit/ffmpeg/options.rb +236 -0
- data/lib/mediakit/ffmpeg.rb +64 -0
- data/lib/mediakit/ffprobe.rb +32 -0
- data/lib/mediakit/utils/popen_helper.rb +68 -0
- data/lib/mediakit/version.rb +3 -0
- data/lib/mediakit.rb +7 -0
- data/mediakit.gemspec +31 -0
- data/sample/raw_crop.rb +39 -0
- data/templates/codec.rb.erb +33 -0
- data/templates/decoder.rb.erb +27 -0
- data/templates/encoder.rb.erb +27 -0
- data/templates/format.rb.erb +9 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ba4ab75bf6e7d06e4d47117147e26dd2dccdb650
|
4
|
+
data.tar.gz: d4e1e172178d66a94c581696e00235a896567cf0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 02b9db5afc15386e6398f267513cb5c7b040e540e7f6f677f5b9d9f70d410b49c16a28cea77a3006df674a95ce67d2e5ef8209629ce69e38c16bb52da3acdcb7
|
7
|
+
data.tar.gz: b42d2eb275b21769af3c90e3a8df77220cdd81abe5bc060f10912995ec6d2e0ffa230dba12f43eb34f036373aeacf7575e2e779b4d505f0c7878dd6405fd5b9b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Mediakit
|
2
|
+
|
3
|
+
mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
4
|
+
I've design this library for following purpose.
|
5
|
+
|
6
|
+
* have low and high level interfaces as you like use
|
7
|
+
* easy testing design by separation of concern
|
8
|
+
|
9
|
+
## Development Plan
|
10
|
+
|
11
|
+
* [x] low-level interface for ffmpeg
|
12
|
+
* [ ] high-level interface for ffmpeg
|
13
|
+
* [ ] low-level interface for sox
|
14
|
+
* [ ] high-level interface for sox
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'mediakit'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install mediakit
|
31
|
+
|
32
|
+
## Requirements
|
33
|
+
|
34
|
+
This library behave command wrapper in your script.
|
35
|
+
So it need each binary command file.
|
36
|
+
|
37
|
+
* latest ffmpeg which have ffprobe command
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
### Low Level Usage
|
42
|
+
|
43
|
+
The low level means it's near command line usage.
|
44
|
+
This is a little bore interface for constructing options,
|
45
|
+
but can pass certain it.
|
46
|
+
|
47
|
+
```rb
|
48
|
+
driver = Mediakit::Drivers::FFmpeg.new
|
49
|
+
ffmpeg = Mediakit::Runners::FFmpeg.new(driver)
|
50
|
+
|
51
|
+
options = Mediakit::Runners::FFmpeg::Options.new(
|
52
|
+
Mediakit::Runners::FFmpeg::Options::GlobalOption.new(
|
53
|
+
't' => 100,
|
54
|
+
'y' => true,
|
55
|
+
),
|
56
|
+
Mediakit::Runners::FFmpeg::Options::InputFileOption.new(
|
57
|
+
options: nil,
|
58
|
+
path: input,
|
59
|
+
),
|
60
|
+
Mediakit::Runners::FFmpeg::Options::OutputFileOption.new(
|
61
|
+
options: {
|
62
|
+
'vf' => 'crop=320:320:0:0',
|
63
|
+
'ar' => '44100',
|
64
|
+
'ab' => '128k',
|
65
|
+
},
|
66
|
+
path: output,
|
67
|
+
),
|
68
|
+
)
|
69
|
+
puts "$ ffmpeg #options"
|
70
|
+
puts ffmpeg.run(options)
|
71
|
+
```
|
72
|
+
|
73
|
+
### High Level Usage
|
74
|
+
|
75
|
+
TBD
|
76
|
+
|
77
|
+
## Development
|
78
|
+
|
79
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
80
|
+
|
81
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
1. Fork it ( https://github.com/[my-github-username]/mediakit/fork )
|
86
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
87
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
88
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
89
|
+
5. Create a new Pull Request
|
90
|
+
|
91
|
+
## References
|
92
|
+
|
93
|
+
* [streamio/streamio-ffmpeg](https://github.com/streamio/streamio-ffmpeg)
|
94
|
+
* [ruby-av/av](https://github.com/ruby-av/av)
|
95
|
+
* [Xuggler](http://www.xuggle.com/xuggler/)
|
96
|
+
* [PHP-FFMpeg/PHP-FFMpeg](https://github.com/PHP-FFMpeg/PHP-FFMpeg)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mediakit"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "pry"
|
14
|
+
Pry.start
|
data/bin/setup
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'mediakit/utils/popen_helper'
|
3
|
+
|
4
|
+
module Mediakit
|
5
|
+
module Drivers
|
6
|
+
class DriverError < StandardError; end
|
7
|
+
class ConfigurationError < DriverError; end
|
8
|
+
|
9
|
+
class Base
|
10
|
+
attr_reader(:bin)
|
11
|
+
def initialize(bin)
|
12
|
+
@bin = bin
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(*args)
|
16
|
+
raise(NotImplementedError)
|
17
|
+
end
|
18
|
+
|
19
|
+
def command(*args)
|
20
|
+
raise(NotImplementedError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class PopenDriver < Base
|
25
|
+
# execute command and return result
|
26
|
+
#
|
27
|
+
# @overload run(args)
|
28
|
+
# @param args [String] arguments for command
|
29
|
+
# @overload run(*args)
|
30
|
+
# @param args [Array] arguments for command
|
31
|
+
# @return result [Bool] runners result
|
32
|
+
def run(*args)
|
33
|
+
begin
|
34
|
+
escaped_args = Mediakit::Utils::PopenHelper.escape(*args)
|
35
|
+
Mediakit::Utils::PopenHelper.run(bin, escaped_args)
|
36
|
+
rescue Mediakit::Utils::PopenHelper::CommandNotFoundError => e
|
37
|
+
raise(ConfigurationError, "cant' find bin in #{bin}.")
|
38
|
+
rescue => e
|
39
|
+
raise(DriverError, "#{self.class} catch error with command(#{Mediakit::Utils::PopenHelper.command(bin,escaped_args)}) - #{e.message}, #{e.backtrace.join("\n")}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# return command to execute
|
44
|
+
#
|
45
|
+
# @overload run(args)
|
46
|
+
# @param args [String] arguments for command
|
47
|
+
# @overload run(*args)
|
48
|
+
# @param args [Array] arguments for command
|
49
|
+
# @return result [String] runners to execute
|
50
|
+
def command(*args)
|
51
|
+
escaped_args = Mediakit::Utils::PopenHelper.escape(*args)
|
52
|
+
"#{bin} #{escaped_args}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class CocaineDriver < Base
|
57
|
+
# execute command and return result
|
58
|
+
#
|
59
|
+
# @overload run(args)
|
60
|
+
# @param args [String] arguments for command
|
61
|
+
# @overload run(*args)
|
62
|
+
# @param args [Array] arguments for command
|
63
|
+
# @return result [Bool] runners result
|
64
|
+
def run(args = '')
|
65
|
+
begin
|
66
|
+
# Force escape args string on here,
|
67
|
+
# because this design can't use Cocaine::CommandLine's safety solution.
|
68
|
+
escaped_args = Mediakit::Utils::PopenHelper.escape(args.dup)
|
69
|
+
command_line = Cocaine::CommandLine.new(bin, escaped_args, swallow_stderr: true)
|
70
|
+
command_line.run
|
71
|
+
rescue => e
|
72
|
+
raise(DriverError, "#{self.class} catch error with runners [$ #{command_line.command}] - #{e.message}, #{e.backtrace.join("\n")}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# return command to execute
|
77
|
+
#
|
78
|
+
# @overload run(args)
|
79
|
+
# @param args [String] arguments for command
|
80
|
+
# @overload run(*args)
|
81
|
+
# @param args [Array] arguments for command
|
82
|
+
# @return result [String] runners to execute
|
83
|
+
def command(args = '')
|
84
|
+
Cocaine::CommandLine.new(bin, args).command
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class FakeDriver < Base
|
89
|
+
def run(args = '')
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
def command(args = '')
|
94
|
+
bin + args
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class AbstractFactory
|
99
|
+
class << self
|
100
|
+
attr_accessor(:bin_path)
|
101
|
+
|
102
|
+
def configure(&block)
|
103
|
+
yield(self)
|
104
|
+
end
|
105
|
+
|
106
|
+
def name
|
107
|
+
self.to_s.downcase.split('::').last
|
108
|
+
end
|
109
|
+
|
110
|
+
def bin
|
111
|
+
bin_path || name
|
112
|
+
end
|
113
|
+
|
114
|
+
def new(type = :popen)
|
115
|
+
case type.to_sym
|
116
|
+
when :popen
|
117
|
+
PopenDriver.new(bin)
|
118
|
+
when :cocaine
|
119
|
+
CocaineDriver.new(bin)
|
120
|
+
when :fake
|
121
|
+
FakeDriver.new(bin)
|
122
|
+
else
|
123
|
+
raise(ArgumentError)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class FFmpeg < AbstractFactory
|
130
|
+
end
|
131
|
+
|
132
|
+
class FFprobe < AbstractFactory
|
133
|
+
end
|
134
|
+
|
135
|
+
class Sox < AbstractFactory
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'active_support/ordered_hash'
|
2
|
+
|
3
|
+
module Mediakit
|
4
|
+
class FFmpeg
|
5
|
+
# presentation of ffmpeg runners option
|
6
|
+
#
|
7
|
+
# SYNOPSIS
|
8
|
+
# ffmpeg [global_options] {[input_file_options] -i input_file} ... {[output_file_options] output_file} ...
|
9
|
+
#
|
10
|
+
class Options
|
11
|
+
attr_reader(:global, :inputs, :output)
|
12
|
+
# constructor
|
13
|
+
#
|
14
|
+
# @option args [Mediakit::FFmpeg::Options::GlobalOption]
|
15
|
+
# @option args [Array<Mediakit::FFmpeg::Options::InputFileOption>] array object Mediakit::FFmpeg::Options::InputOption
|
16
|
+
# @option args [Mediakit::FFmpeg::Options::OutputFileOption]
|
17
|
+
def initialize(*args)
|
18
|
+
@global, @inputs, @output = nil, [], nil
|
19
|
+
args.each do |option|
|
20
|
+
add_option(option)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_option(option)
|
25
|
+
return if option.nil?
|
26
|
+
case option
|
27
|
+
when GlobalOption
|
28
|
+
set_global(option)
|
29
|
+
when InputFileOption
|
30
|
+
add_input(option)
|
31
|
+
when OutputFileOption
|
32
|
+
set_output(option)
|
33
|
+
else
|
34
|
+
raise(ArgumentError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def compose
|
39
|
+
composed_string = ''
|
40
|
+
composed_string << "#{global}" if global
|
41
|
+
composed_string << " #{inputs.map(&:compose).join(' ')}" if inputs && !inputs.empty?
|
42
|
+
composed_string << " #{output}" if output
|
43
|
+
composed_string
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :to_s, :compose
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def set_global(global)
|
51
|
+
@global = global
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_input(input)
|
55
|
+
@inputs.push(input)
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_output(output)
|
59
|
+
@output = output
|
60
|
+
end
|
61
|
+
|
62
|
+
# Base class for Options
|
63
|
+
class OrderedHash < ActiveSupport::OrderedHash
|
64
|
+
# initializer
|
65
|
+
#
|
66
|
+
# @param options [Hash] initial option values
|
67
|
+
def initialize(options = {})
|
68
|
+
options.each { |key, value| raise_if_invalid_arg_error(key, value) } if options
|
69
|
+
self.merge!(options) if options && options.kind_of?(Hash)
|
70
|
+
end
|
71
|
+
|
72
|
+
def []=(key, value)
|
73
|
+
raise_if_invalid_arg_error(key, value)
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def compose
|
78
|
+
self.map { |key, value| struct_option(key, value) }.compact.join(' ')
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
compose
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def raise_if_invalid_arg_error(key, value)
|
88
|
+
raise(ArgumentError, "#{self.class} can't accept nil value with key(#{key}).") if value.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
def struct_option(key, value)
|
92
|
+
case value
|
93
|
+
when TrueClass, FalseClass
|
94
|
+
value ? "-#{key}" : nil
|
95
|
+
else
|
96
|
+
"-#{key} #{value}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class GlobalOption < OrderedHash
|
102
|
+
end
|
103
|
+
|
104
|
+
class OptionPathPair
|
105
|
+
attr_reader(:options, :path)
|
106
|
+
|
107
|
+
def initialize(options:, path:)
|
108
|
+
@options = options
|
109
|
+
@path = path
|
110
|
+
end
|
111
|
+
|
112
|
+
def compose
|
113
|
+
raise(NotImplementedError)
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_s
|
117
|
+
compose
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class InputFileOption < OptionPathPair
|
122
|
+
# @param options [Hash] input options
|
123
|
+
# @param path [String] input file path
|
124
|
+
def initialize(options:, path:)
|
125
|
+
ordered_hash = OrderedHash.new(options)
|
126
|
+
super(options: ordered_hash, path: path)
|
127
|
+
end
|
128
|
+
|
129
|
+
def compose
|
130
|
+
"#{options} -i #{path}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class OutputFileOption < OptionPathPair
|
135
|
+
# @param options [Hash] output options
|
136
|
+
# @param path [String] output file path
|
137
|
+
def initialize(options:, path:)
|
138
|
+
ordered_hash = OrderedHash.new(options)
|
139
|
+
super(options: ordered_hash, path: path)
|
140
|
+
end
|
141
|
+
|
142
|
+
def compose
|
143
|
+
"#{options} #{path}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class InputFileOptionCollection
|
148
|
+
attr_reader(:input_file_options)
|
149
|
+
|
150
|
+
# @param *input_file_options [Mediakit::FFmpeg::InputFileOptions]
|
151
|
+
def initialize(*input_file_options)
|
152
|
+
@options = input_file_options
|
153
|
+
end
|
154
|
+
|
155
|
+
def empty?
|
156
|
+
@options.empty?
|
157
|
+
end
|
158
|
+
|
159
|
+
def compose
|
160
|
+
@options.map(&:compose).join(' ')
|
161
|
+
end
|
162
|
+
|
163
|
+
alias_method :to_s, :compose
|
164
|
+
end
|
165
|
+
|
166
|
+
# see https://www.ffmpeg.org/ffmpeg.html#Stream-specifiers-1
|
167
|
+
class StreamSpecifier
|
168
|
+
attr_reader(:stream_index, :stream_type, :program_id, :metadata_tag_key, :metadata_tag_value, :usable)
|
169
|
+
STREAM_TYPES = ['v', 'a', 's'].freeze
|
170
|
+
|
171
|
+
def initialize(stream_index: nil, stream_type: nil, program_id: nil, metadata_tag_key: nil, metadata_tag_value: nil, usable: nil)
|
172
|
+
raise(ArgumentError, "invalid args stream_type = #{stream_type}") if stream_type && !STREAM_TYPES.include?(stream_type)
|
173
|
+
@stream_index = stream_index
|
174
|
+
@stream_type = stream_type
|
175
|
+
@program_id = program_id
|
176
|
+
@metadata_tag_key = metadata_tag_key
|
177
|
+
@metadata_tag_value = metadata_tag_value
|
178
|
+
@usable = usable
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_s
|
182
|
+
case
|
183
|
+
when stream_index?, stream_type?
|
184
|
+
stream_specifier
|
185
|
+
when program?
|
186
|
+
program_specifier
|
187
|
+
when meta?
|
188
|
+
metadata_specifier
|
189
|
+
when usable?
|
190
|
+
usable_specifier
|
191
|
+
else
|
192
|
+
raise(RuntimeError)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def stream_specifier
|
199
|
+
[stream_type, stream_index].compact.join(':')
|
200
|
+
end
|
201
|
+
|
202
|
+
def program_specifier
|
203
|
+
["p:#{program_id}", stream_index].compact.join(':')
|
204
|
+
end
|
205
|
+
|
206
|
+
def metadata_specifier
|
207
|
+
[metadata_tag_key, metadata_tag_value].compact.join(':')
|
208
|
+
end
|
209
|
+
|
210
|
+
def usable_specifier
|
211
|
+
'u'
|
212
|
+
end
|
213
|
+
|
214
|
+
def stream_index?
|
215
|
+
!stream_index.nil?
|
216
|
+
end
|
217
|
+
|
218
|
+
def stream_type?
|
219
|
+
!stream_type.nil?
|
220
|
+
end
|
221
|
+
|
222
|
+
def meta?
|
223
|
+
!metadata_tag_key.nil?
|
224
|
+
end
|
225
|
+
|
226
|
+
def program?
|
227
|
+
!program_id.nil?
|
228
|
+
end
|
229
|
+
|
230
|
+
def usable?
|
231
|
+
!usable.nil?
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'mediakit/ffmpeg/options'
|
2
|
+
require 'mediakit/drivers'
|
3
|
+
|
4
|
+
module Mediakit
|
5
|
+
class FFmpeg
|
6
|
+
class FFmpegError < StandardError;
|
7
|
+
end
|
8
|
+
|
9
|
+
DELIMITER_FOR_CODECS = "\n -------\n".freeze
|
10
|
+
DELIMITER_FOR_FORMATS = "\n --\n".freeze
|
11
|
+
DELIMITER_FOR_CODER = "\n ------\n".freeze
|
12
|
+
|
13
|
+
def initialize(driver)
|
14
|
+
@driver = driver
|
15
|
+
end
|
16
|
+
|
17
|
+
# execute runners with options object
|
18
|
+
#
|
19
|
+
# @param options [Mediakit::Runners::FFmpeg::Options] options object to create CLI argument
|
20
|
+
def run(options)
|
21
|
+
args = options.compose
|
22
|
+
execute(args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def command(options)
|
26
|
+
args = options.compose
|
27
|
+
@driver.command(args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def codecs
|
31
|
+
@codecs ||= run(global_options('codecs')).split(DELIMITER_FOR_CODECS)[1].each_line.to_a
|
32
|
+
end
|
33
|
+
|
34
|
+
def formats
|
35
|
+
@formats ||= run(global_options('formats')).split(DELIMITER_FOR_FORMATS)[1].each_line.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
def decoders
|
39
|
+
@decoders ||= run(global_options('decoders')).split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
40
|
+
end
|
41
|
+
|
42
|
+
def encoders
|
43
|
+
@encoders ||= run(global_options('encoders')).split(DELIMITER_FOR_CODER)[1].each_line.to_a
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def execute(args = '')
|
49
|
+
begin
|
50
|
+
@driver.run(args)
|
51
|
+
rescue Drivers::DriverError => e
|
52
|
+
raise(FFmpegError, "#catch driver's error - #{e.message}, #{e.backtrace.join("\n")}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def global_options(flag)
|
57
|
+
Mediakit::FFmpeg::Options.new(
|
58
|
+
Mediakit::FFmpeg::Options::GlobalOptions.new(
|
59
|
+
flag => true,
|
60
|
+
),
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mediakit
|
2
|
+
class FFprobe
|
3
|
+
attr_reader(:driver)
|
4
|
+
|
5
|
+
class FFprobeError < StandardError;
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(driver)
|
9
|
+
@driver = driver
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args = '')
|
13
|
+
begin
|
14
|
+
driver.run(args)
|
15
|
+
rescue => e
|
16
|
+
raise(FFprobeError, "#{self.class} catch error - #{e.message}, #{e.backtrace.join("\n")}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_json(path)
|
21
|
+
args = "#{default_show_options} -print_format json #{path}"
|
22
|
+
execute(args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_show_options
|
26
|
+
[
|
27
|
+
'-show_streams',
|
28
|
+
'-show_format'
|
29
|
+
].join(' ')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Mediakit
|
5
|
+
module Utils
|
6
|
+
module PopenHelper
|
7
|
+
|
8
|
+
class CommandNotFoundError < StandardError;
|
9
|
+
end
|
10
|
+
|
11
|
+
# @overload run(command, *args)
|
12
|
+
# @param command [String]
|
13
|
+
# @param args [Array] args as array for safety shellescape
|
14
|
+
# @overload run(command, args)
|
15
|
+
# @param command [String] command name
|
16
|
+
# @param args [Array] args as string
|
17
|
+
# @return out [String] stdout of command
|
18
|
+
# @return err [String] stderr of command
|
19
|
+
# @return exit_status [Boolean] is succeeded
|
20
|
+
def run(bin, *args)
|
21
|
+
command = command(bin, *args)
|
22
|
+
|
23
|
+
out, err, exit_status = nil
|
24
|
+
begin
|
25
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
26
|
+
stdin.close
|
27
|
+
out = stdout.read
|
28
|
+
err = stderr.read
|
29
|
+
exit_status = (wait_thr.value.exitstatus == 0)
|
30
|
+
end
|
31
|
+
rescue Errno::ENOENT => e
|
32
|
+
raise(CommandNotFoundError, "Can't find command - #{command}, #{e.meessage}")
|
33
|
+
end
|
34
|
+
|
35
|
+
[out, err, exit_status]
|
36
|
+
end
|
37
|
+
|
38
|
+
module_function(:run)
|
39
|
+
|
40
|
+
def command(bin, *args)
|
41
|
+
escaped_args = escape(*args)
|
42
|
+
"#{bin} #{escaped_args}"
|
43
|
+
end
|
44
|
+
|
45
|
+
module_function(:command)
|
46
|
+
|
47
|
+
def escape(*args)
|
48
|
+
case args.size
|
49
|
+
when 1
|
50
|
+
_escape_with_split(args[0])
|
51
|
+
else
|
52
|
+
Shellwords.join(args.map { |x| Shellwords.escape(x) })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module_function(:escape)
|
57
|
+
|
58
|
+
def _escape_with_split(string)
|
59
|
+
splits = Shellwords.split(string)
|
60
|
+
splits = splits.map { |x| Shellwords.escape(x) }
|
61
|
+
splits.join(' ')
|
62
|
+
end
|
63
|
+
|
64
|
+
module_function(:_escape_with_split)
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/mediakit.rb
ADDED
data/mediakit.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mediakit/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mediakit"
|
8
|
+
spec.version = Mediakit::VERSION
|
9
|
+
spec.authors = ["ainame"]
|
10
|
+
spec.email = ["s.namai.09@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = 'mediakit is the libraries for ffmpeg and sox backed media manipulation something.'
|
13
|
+
spec.description = <<EOS
|
14
|
+
mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
15
|
+
you can create complex manipulation for media as a ruby code.
|
16
|
+
EOS
|
17
|
+
|
18
|
+
spec.homepage = "https://github.com/ainame/mediakit"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_runtime_dependency "cocaine", "~> 0.5.7"
|
26
|
+
spec.add_runtime_dependency "activesupport", "~> 4"
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "pry", "~> 0.10"
|
30
|
+
spec.add_development_dependency "ruby-debug-ide", "~> 0.4"
|
31
|
+
end
|
data/sample/raw_crop.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mediakit"
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
def transcode_option(input, output)
|
8
|
+
options = Mediakit::FFmpeg::Options.new(
|
9
|
+
Mediakit::FFmpeg::Options::GlobalOption.new(
|
10
|
+
't' => 100,
|
11
|
+
'y' => true,
|
12
|
+
),
|
13
|
+
Mediakit::FFmpeg::Options::InputFileOption.new(
|
14
|
+
options: nil,
|
15
|
+
path: input,
|
16
|
+
),
|
17
|
+
Mediakit::FFmpeg::Options::InputFileOption.new(
|
18
|
+
options: nil,
|
19
|
+
path: input,
|
20
|
+
),
|
21
|
+
Mediakit::FFmpeg::Options::OutputFileOption.new(
|
22
|
+
options: {
|
23
|
+
'vf' => 'crop=320:320:0:0',
|
24
|
+
'ar' => '44100',
|
25
|
+
'ab' => '128k',
|
26
|
+
},
|
27
|
+
path: output,
|
28
|
+
),
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
root = File.expand_path(File.join(File.dirname(__FILE__), '../'))
|
33
|
+
input_path = File.expand_path(File.join(root, 'test/fixtures/sample1.mp4'))
|
34
|
+
output_path = File.expand_path(File.join(root, 'out.mp4'))
|
35
|
+
driver = Mediakit::Drivers::FFmpeg.new
|
36
|
+
ffmpeg = Mediakit::FFmpeg.new(driver)
|
37
|
+
options = transcode_option(input_path, output_path)
|
38
|
+
puts "$ #{ffmpeg.command(options)}"
|
39
|
+
puts ffmpeg.run(options)
|
@@ -0,0 +1,33 @@
|
|
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
|
@@ -0,0 +1,27 @@
|
|
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
|
@@ -0,0 +1,27 @@
|
|
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
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mediakit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ainame
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cocaine
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.5.7
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.10'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: ruby-debug-ide
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.4'
|
97
|
+
description: |
|
98
|
+
mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
99
|
+
you can create complex manipulation for media as a ruby code.
|
100
|
+
email:
|
101
|
+
- s.namai.09@gmail.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".travis.yml"
|
108
|
+
- Gemfile
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- lib/mediakit.rb
|
114
|
+
- lib/mediakit/drivers.rb
|
115
|
+
- lib/mediakit/ffmpeg.rb
|
116
|
+
- lib/mediakit/ffmpeg/options.rb
|
117
|
+
- lib/mediakit/ffprobe.rb
|
118
|
+
- lib/mediakit/utils/popen_helper.rb
|
119
|
+
- lib/mediakit/version.rb
|
120
|
+
- mediakit.gemspec
|
121
|
+
- sample/raw_crop.rb
|
122
|
+
- templates/codec.rb.erb
|
123
|
+
- templates/decoder.rb.erb
|
124
|
+
- templates/encoder.rb.erb
|
125
|
+
- templates/format.rb.erb
|
126
|
+
homepage: https://github.com/ainame/mediakit
|
127
|
+
licenses: []
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.4.5
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: mediakit is the libraries for ffmpeg and sox backed media manipulation something.
|
149
|
+
test_files: []
|