ffmpeg-filter_graph 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c05fb4a2ed62cd57c57bcd8d9bfbba450075712d
4
+ data.tar.gz: c9fdd4f24fcb014992c605e7a966c751965a92f0
5
+ SHA512:
6
+ metadata.gz: 61591d28787f1495c3626879772fad0e6e1a5afbc1107cf58c5de427b76f83c44d74e3e26c4f155008f5784f3d416ccc196e47c2048fde57a267c435ffbe7c78
7
+ data.tar.gz: 87ccab1f5766a0103fdf4e468a12988dba2345eaacbecdfc3ad4b8403bc9ddbe00e866111a7a3e75a80c0e29ee15a594c55b947d0effdbfb487028d4f57cdb1a
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+ - Extracted from `rifftrax` gem.
7
+ - `FFmpeg::FilterGraph::Helper` can be merged into client classes for easy use.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # Ffmpeg::FilterGraph
2
+
3
+ **Warning**: This gem is alpha as it gets. Use at your own peril.
4
+
5
+ The purpose of this gem is to help you create complex filter-graphs for FFmpeg.
6
+ In a sense, this gem is really a "string factory", as it's main output is a
7
+ single string you can pass as the argument to `ffmpeg`'s `-filter_complex`
8
+ command-line argument.
9
+
10
+
11
+ ## Wishlist
12
+ - Track inpads and outpats, to ensure that outpads are used, and inpads exist.
13
+ - Support abbreviated option names
14
+
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'ffmpeg-filter_graph'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install ffmpeg-filter_graph
31
+
32
+
33
+ ## Usage
34
+
35
+ ```ruby
36
+ # This class adds an additional audio stream to a given multimedia file. The
37
+ # new audio stream will contain a copy of the film's soundtrack, with the
38
+ # commentary track mixed into the rear speakers (for a nice Statler and Waldorf
39
+ # experience). To make the commentary more audible, with "duck" the soundtrack
40
+ # before mixing in the commentary.
41
+ class AddCommentary
42
+ include FFmpeg::Filters::Helper
43
+
44
+ # Surround-sound prefixes
45
+ Soundtrack = 'c'
46
+ DuckedSoundtrack = 'r'
47
+
48
+ # ffmpeg notation
49
+ SoundtrackVideo = '1:v'
50
+ SoundtrackAudio = '1:a'
51
+ CommentaryAudio = '2:a'
52
+
53
+ def initialize(media_container_path, commentary_audio_path, output_path)
54
+ @media_container_path = media_container_path
55
+ @commentary_audio_path = commentary_audio_path
56
+ @output_path = output_path
57
+ end
58
+
59
+ def call
60
+ filter_graph = create_filter_graph
61
+
62
+ spawn('ffmpeg', '-i', @media_container_path, '-i', @commentary_audio_path,
63
+ '-filter_complex', filter_graph.to_s,
64
+ '-map', SoundtrackVideo, '-map', SoundtrackAudio,
65
+ '-map', "[#{filter_graph.outputs.first}]",
66
+ @output_path)
67
+ end
68
+
69
+ private
70
+
71
+ def create_filter_graph
72
+ graph(
73
+ outputs: 'CommentaryTrack',
74
+
75
+ chains: [
76
+ # We need two copies for the mix, and one copy for the audo-ducking
77
+ chain(
78
+ inputs: [CommentaryAudio],
79
+ filters: a_split(number: 3),
80
+ outputs: %w(RLcom RRcom Ducker)
81
+ ),
82
+
83
+ # Auto-duck the soundtrack, so we can hear the commentary
84
+ chain(
85
+ inputs: [SoundtrackAudio, 'Ducker'],
86
+ filters: [
87
+ side_chain_compress(attack: 50, release: 200),
88
+ channel_split(channel_layout: '5.1')
89
+ ],
90
+ outputs: surround_channels(DuckedSoundtrack),
91
+ ),
92
+
93
+ # Merge commentary into rear channels
94
+ chain(
95
+ inputs: surround_channels(DuckedSoundtrack, :rl) << 'RLcom',
96
+ filters: mono_mix,
97
+ outputs: %w(RLmix)
98
+ ),
99
+ chain(
100
+ inputs: surround_channels(DuckedSoundtrack, :rr) << 'RRcom',
101
+ filters: mono_mix,
102
+ outputs: %w(RRmix)
103
+ ),
104
+ chain(
105
+ inputs: surround_channels(DuckedSoundtrack, :fl, :fr, :fc, :lfe) + %w(RLmix RRmix),
106
+ filters: [a_merge(inputs: 6)]
107
+ ),
108
+ ]
109
+ )
110
+ end
111
+
112
+ # You can easily encapsulate a group of filters with private methods, for
113
+ # better readability.
114
+ def mono_mix
115
+ [a_merge, pan(channel_layout: 'mono', outputs: ["c0 = c0 + c1"])]
116
+ end
117
+ end
118
+ ```
119
+
120
+
121
+ ## Development
122
+
123
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
124
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
125
+ prompt that will allow you to experiment.
126
+
127
+ To install this gem onto your local machine, run `bundle exec rake install`. To
128
+ release a new version, update the version number in `version.rb`, and then run
129
+ `bundle exec rake release`, which will create a git tag for the version, push
130
+ git commits and tags, and push the `.gem` file to
131
+ [rubygems.org](https://rubygems.org).
132
+
133
+
134
+ ## Contributing
135
+
136
+ Bug reports and pull requests are welcome on GitHub at
137
+ https://github.com/sangster/ffmpeg-filter_graph.
138
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'ffmpeg/filter_graph'
5
+ require 'pry'
6
+
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ffmpeg/filter_graph/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ffmpeg-filter_graph'
8
+ spec.version = Ffmpeg::FilterGraph::VERSION
9
+ spec.authors = ['Jon Sangster']
10
+ spec.email = ['jon@ertt.ca']
11
+
12
+ spec.summary = %q{FFmpeg filter-graph generator}
13
+ spec.description = %q{This gem generates complex filter-graphs for use with the ffmpeg application. ie: Values for the -filter_complex command-line option.}
14
+ spec.homepage = 'https://github.com/sangster/ffmpeg-filter_graph'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.12'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+
24
+ spec.add_development_dependency 'minitest', '~> 5.0'
25
+ spec.add_development_dependency 'minitest-reporters', '~> 1.1.8'
26
+
27
+ spec.add_development_dependency 'guard', '~> 2.13.0'
28
+ spec.add_development_dependency 'guard-minitest', '~> 2.4.4'
29
+
30
+ spec.add_development_dependency 'yard', '~> 0.7'
31
+ spec.add_development_dependency 'rdoc', '~> 3.12'
32
+ end
@@ -0,0 +1,22 @@
1
+ module FFmpeg
2
+ module FilterGraph
3
+ end
4
+ end
5
+
6
+ require 'ffmpeg/filter_graph/utils/strings'
7
+
8
+ require 'ffmpeg/filter_graph/helper'
9
+
10
+ require 'ffmpeg/filter_graph/filter'
11
+ require 'ffmpeg/filter_graph/filter_factory'
12
+ require 'ffmpeg/filter_graph/chain'
13
+ require 'ffmpeg/filter_graph/graph'
14
+
15
+ require 'ffmpeg/filter_graph/generate_filters_audio'
16
+ require 'ffmpeg/filter_graph/generate_filters_audio_sink'
17
+ require 'ffmpeg/filter_graph/generate_filters_audio_src'
18
+ require 'ffmpeg/filter_graph/generate_filters_multimedia'
19
+ require 'ffmpeg/filter_graph/generate_filters_video'
20
+ require 'ffmpeg/filter_graph/generate_filters_video_src'
21
+
22
+ require 'ffmpeg/filter_graph/version'
@@ -0,0 +1,21 @@
1
+ module FFmpeg::FilterGraph
2
+ class Chain
3
+ attr_accessor :inputs, :outputs, :filters
4
+
5
+ def initialize(inputs: [], outputs: [], filters: [])
6
+ self.inputs = inputs
7
+ self.outputs = outputs
8
+ self.filters = Array(filters).flatten.compact
9
+ end
10
+
11
+ def to_s
12
+ "#{join(inputs)} #{filters.map(&:to_s).join(', ')} #{join(outputs)}"
13
+ end
14
+
15
+ private
16
+
17
+ def join(arr)
18
+ arr.map { |i| "[#{i}]" }.join
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ module FFmpeg::FilterGraph
2
+ class Filter
3
+ # @param name [String] Sets the filter-name output by this filter
4
+ def self.name(name = nil)
5
+ if name.nil?
6
+ @name || fail('filter name not set')
7
+ else
8
+ @name = name
9
+ end
10
+ end
11
+
12
+ # @param opts [Array<String>] a list of valid options for this filter
13
+ def self.options(*opts)
14
+ @opts ||= []
15
+
16
+ if opts.any?
17
+ @opts.concat(opts)
18
+ attr_accessor *opts
19
+ end
20
+
21
+ @opts
22
+ end
23
+
24
+ def to_s
25
+ if options.any?
26
+ "#{self.class.name}=#{options_string}"
27
+ else
28
+ self.class.name
29
+ end
30
+ end
31
+
32
+ # @return [String] A string concatenating the set options.
33
+ # @note May be overridden by atypical filters
34
+ def options_string
35
+ join_options(options)
36
+ end
37
+
38
+ # Set all options to nil
39
+ def empty!
40
+ self.class.options.each { |opt| send("#{opt}=", nil) }
41
+ end
42
+
43
+ # @return [Hash<String,String>] a map of all options and their values. nil
44
+ # values will not be included.
45
+ def options
46
+ ret = {}
47
+ self.class.options.each do |opt|
48
+ if (val = self.send(opt))
49
+ ret[opt] = val
50
+ end
51
+ end
52
+ ret
53
+ end
54
+
55
+ protected
56
+
57
+ # @param keys [Array<Symbol>] the names of the options to join together
58
+ # @return [String, nil] a string concatenating all options, in the format of
59
+ # "k1=v1:k2=v2:k3=v3". Will return nil of there are no set values
60
+ def join_options(*keys)
61
+ opts = options
62
+ parts = keys.map { |k| "#{k}=#{opts[k]}" if opts[k] }.compact
63
+ parts.join(':') if parts.any?
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,80 @@
1
+ module FFmpeg::FilterGraph
2
+ # This factory creates a new [Filter] subclass. The filters are all very
3
+ # silimar, so it's easier to generate each class than to write them by hand.
4
+ class FilterFactory
5
+ include Utils::Strings
6
+
7
+ attr_accessor :class_name, :required, :optional, :editable, :options_string
8
+
9
+ def self.create(name, opts)
10
+ new(
11
+ name,
12
+ opts[:required],
13
+ opts[:optional],
14
+ opts[:editable],
15
+ &opts[:options_string]
16
+ )
17
+ end
18
+
19
+ # @param class_name [#to_s] the name of the class to create
20
+ # @param required [nil, Array<String>] an optional list of the filter
21
+ # options which must be passed to its constructor
22
+ # @param optional [nil, Array<String>] an optional list of the filter
23
+ # options which are not required by the filter
24
+ # @param editable [Bool] if this filter supports "Timeline editing"
25
+ # @param options_string [Block] An optional callback to override the
26
+ # default method of constructing the string of options in the filter's
27
+ # output.
28
+ # @see Filter#options_string
29
+ def initialize(class_name, required, optional, editable, &options_string)
30
+ self.class_name = class_name.to_s
31
+ self.required = required || []
32
+ self.optional = optional || []
33
+ self.options_string = options_string
34
+ end
35
+
36
+ # @param mod [Module] the module to create the new class in
37
+ # @param helper_module [Module] an optional module to create a helper-method
38
+ # in. ex: if the filter class is named MyFilter, a method will be created
39
+ # in the form of Helper.my_filter(*args); MyFilter.new(*args) end
40
+ def create_class_in(mod, helper_module: Helper)
41
+ # We need to make these local vars, to work in the Class.new block
42
+ cn = class_name.to_s
43
+
44
+ mod.const_set(cn, create_class(cn, required, optional, options_string))
45
+
46
+ if editable
47
+ end
48
+
49
+ if helper_module
50
+ helper_name = underscore(cn)
51
+
52
+ helper_module.module_exec do
53
+ klass = mod.const_get(cn)
54
+ define_method(helper_name) { |*args| klass.new(*args) }
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def create_class(cn, req, opt, opt_str)
62
+ Class.new(Filter) do
63
+ const_set('REQUIRED', req.freeze)
64
+ const_set('OPTIONAL', opt.freeze)
65
+
66
+ name(cn.downcase.to_sym)
67
+ options(*(const_get('REQUIRED') + const_get('OPTIONAL')))
68
+
69
+ def initialize(args = {})
70
+ self.class.const_get('REQUIRED').each do |o|
71
+ fail ArgumentError, "missing keyword: #{o}" unless args.key?(o)
72
+ end
73
+ args.each { |k, v| send("#{k}=", v) }
74
+ end
75
+
76
+ define_method(:options_string, &opt_str) if opt_str
77
+ end
78
+ end
79
+ end
80
+ end
File without changes
@@ -0,0 +1,177 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ ACompressor: {
5
+ optional: [:level_in, :threshold, :ratio, :attack, :release, :makeup,
6
+ :knee, :link, :detection, :mix]
7
+ },
8
+ ACrossFade: {
9
+ required: [:curve1, :curve2],
10
+ optional: [:nb_samples, :duration, :overlap]
11
+ },
12
+ ADelay: {
13
+ required: [:delays]
14
+ },
15
+ AEcho: {
16
+ required: [:in_gain, :out_gain, :delays, :decays]
17
+ },
18
+ AEmphasis: {
19
+ required: [:level_in, :level_out, :type],
20
+ optional: [:mode]
21
+ },
22
+ AEval: {
23
+ required: [:exprs],
24
+ optional: [:channel_layout],
25
+ options_string: -> { [exprs, join_options(:channel_layout)].compact.join(':') }
26
+ },
27
+ AFade: {
28
+ required: [:curve],
29
+ optional: [:type, :start_sample, :nb_samples, :start_time, :duration]
30
+ },
31
+ AffTFilt: {
32
+ optional: [:real, :imag, :win_size, :win_func, :overlap]
33
+ },
34
+ AFormat: {
35
+ optional: [:channel_layouts, :sample_fmts, :sample_rates]
36
+ },
37
+ AGate: {
38
+ optional: [:level_in, :range, :threshold, :ratio, :attack, :release,
39
+ :makeup, :knee, :detection, :line]
40
+ },
41
+ ALimiter: {
42
+ optional: [:level_in, :level_out, :limit, :attack, :release, :asc,
43
+ :asc_level, :level]
44
+ },
45
+ AllPass: {
46
+ required: [:frequency, :width_type, :width]
47
+ },
48
+ AMerge: {
49
+ optional: [:inputs]
50
+ },
51
+ AMix: {
52
+ optional: [:inputs, :duration, :dropout_transition]
53
+ },
54
+ APad: {
55
+ optional: [:packet_size, :pad_len, :whole_len]
56
+ },
57
+ APhaser: {
58
+ optional: [:in_gain, :out_gain, :delay, :decay, :speed, :type]
59
+ },
60
+ APulsator: {
61
+ required: [:amount],
62
+ optional: [:level_in, :level_out, :mode, :offset_l, :offset_r, :width,
63
+ :timing, :bpm, :ms, :hz]
64
+ },
65
+
66
+ # TODO AResample: { },
67
+
68
+ ASetNSamples: {
69
+ optional: [:nb_out_samples, :pad]
70
+ },
71
+ ASetRate: {
72
+ optional: [:sample_rate]
73
+ },
74
+ AShowInfo: {},
75
+ AStats: {
76
+ optional: [:length, :metadata, :reset]
77
+ },
78
+ ASyncTs: {
79
+ optional: [:compensate, :min_delta, :first_pts]
80
+ },
81
+ ATempo: {
82
+ optional: [:tempo],
83
+ options_string: -> { tempo }
84
+ },
85
+
86
+ # TODO ATrim: {},
87
+
88
+ BandPass: {
89
+ required: [:width_type, :width],
90
+ optional: [:frequency, :csg]
91
+ },
92
+ BandReject: {
93
+ required: [:width_type, :width],
94
+ optional: [:frequency]
95
+ },
96
+ Bass: {
97
+ required: [:gain, :width_type, :width],
98
+ optional: [:frequency]
99
+ },
100
+ BiQuad: {
101
+ required: [:b0, :b1, :b2, :a0, :a1, :a2]
102
+ },
103
+ Bs2b: {
104
+ required: [:profile, :fcut, :feed]
105
+ },
106
+ ChannelMap: {
107
+ required: [:map],
108
+ optional: [:channel_layout]
109
+ },
110
+ ChannelSplit: {
111
+ optional: [:channel_layout]
112
+ },
113
+ Chorus: {
114
+ required: [:delays, :decays, :speeds, :depths],
115
+ optional: [:in_gain, :out_gain]
116
+ },
117
+
118
+ # TODO Compand
119
+ # TODO CompensationDelay
120
+ # TODO DcShift
121
+ # TODO DynAudNorm
122
+
123
+ Earwax: {},
124
+
125
+ # TODO Equalizer
126
+ # TODO ExtraStereo
127
+ # TODO FirEqualizer
128
+ # TODO Flanger
129
+ # TODO HighPass
130
+ # TODO Join
131
+ # TODO Ladspa
132
+ # TODO LowPass
133
+
134
+ Pan: {
135
+ optional: [:channel_layout, :outputs],
136
+ options_string: -> { "#{channel_layout}| #{outputs.join(' | ')}" }
137
+ },
138
+ ReplayGain: {},
139
+ Resample: {},
140
+
141
+ # TODO RubberBand
142
+
143
+ SideChainCompress: {
144
+ optional: [:level_in, :threshold, :ratio, :attack, :release, :makeup,
145
+ :knee, :link, :detection, :level_sc, :mix]
146
+ },
147
+ SideChainGate: {
148
+ optional: [:level_in, :range, :threshold, :ratio, :attack, :release,
149
+ :makeup, :knee, :link, :detection, :level_sc]
150
+ },
151
+ SilenceDetect: {
152
+ optional: [:duration, :noise]
153
+ },
154
+
155
+ # TODO SilenceRemove
156
+ # TODO Sofalizer
157
+ # TODO StereoTools
158
+ # TODO StereoWiden
159
+ # TODO Scale_Npp
160
+ # TODO Select
161
+ # TODO Treble
162
+
163
+ Tremolo: {
164
+ optional: [:f, :d]
165
+ },
166
+ Vibrato: {
167
+ optional: [:f, :d]
168
+ },
169
+ Volume: {
170
+ editable: true,
171
+ optional: [:volume, :precision, :replaygain, :replaygain_preamp, :eval]
172
+ },
173
+ VolumeDetect: {},
174
+ }.each do |class_name, opts|
175
+ FilterFactory.create(class_name, opts).create_class_in(self)
176
+ end
177
+ end
@@ -0,0 +1,11 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ ABufferSink: {},
5
+ ANullSink: {},
6
+ }.each do |class_name, opts|
7
+ FilterFactory.create(class_name, opts).create_class_in(self)
8
+ end
9
+ end
10
+
11
+
@@ -0,0 +1,19 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ # TODO ABuffer
5
+ AEvalSrc: {
6
+ required: [:exprs],
7
+ optional: [:sample_rate, :duration, :channel_layout, :nb_samples],
8
+ options_string: -> { "#{exprs}:#{join_options(:duration, :sample_rate, :channel_layout, :nb_samples)}" }
9
+ },
10
+ ANullSrc: {
11
+ optional: [:channel_layout, :sample_rate, :nb_samples]
12
+ },
13
+ # TODO Flite
14
+ # TODO ANoiseSrc
15
+ # TODO Sine
16
+ }.each do |class_name, opts|
17
+ FilterFactory.create(class_name, opts).create_class_in(self)
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ Concat: {
5
+ optional: [:n, :v, :a]
6
+ },
7
+ ASplit: {
8
+ optional: [:number],
9
+ options_string: -> { number.to_s }
10
+ },
11
+ }.each do |class_name, opts|
12
+ FilterFactory.create(class_name, opts).create_class_in(self)
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ }.each do |class_name, opts|
5
+ FilterFactory.create(class_name, opts).create_class_in(self)
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # @see https://ffmpeg.org/ffmpeg-filters.html
2
+ module FFmpeg::FilterGraph
3
+ {
4
+ Color: {
5
+ optional: [:color, :size, :duration, :rate]
6
+ },
7
+ }.each do |class_name, opts|
8
+ FilterFactory.create(class_name, opts).create_class_in(self)
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module FFmpeg::FilterGraph
2
+ class FilterGraph
3
+ attr_accessor :chains, :outputs
4
+
5
+ def initialize(chains: [], outputs: [])
6
+ self.chains = chains
7
+ self.outputs = outputs
8
+ end
9
+
10
+ def add_outputs(*pads)
11
+ self.outputs.push(*pads.map(&:to_s))
12
+ end
13
+
14
+ def to_s
15
+ [
16
+ chains.map(&:to_s).join('; '),
17
+ outputs.map { |o| "[#{o}]" }.join(''),
18
+ ].join('')
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,32 @@
1
+ module FFmpeg::FilterGraph
2
+ module Helper
3
+ CHANNELS = %w(FL FR FC LFE RL RR)
4
+
5
+ def graph(*args); Graph.new(*args) end
6
+ def chain(*args); Chain.new(*args) end
7
+
8
+ # Filter-specific helpers will be added to this module by default, by the
9
+ # FilterFactory class.
10
+
11
+ def surround_channels(prefix = '', *channels)
12
+ channels = CHANNELS if channels.empty?
13
+ channels.map(&:to_s).map(&:upcase).map { |c| "#{prefix}#{c}" }
14
+ end
15
+
16
+ # @return the number of channels in the given channel layout
17
+ def count_channels(ch)
18
+ case ch
19
+ when Fixnum then ch
20
+ when '7.1' then 8
21
+ when '6.1' then 7
22
+ when '5.1' then 6
23
+ when '4.1' then 5
24
+ when '3.1' then 4
25
+ when '2.1' then 3
26
+ when 'stereo' then 2
27
+ when 'mono' then 1
28
+ else fail 'unknown layout'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module FFmpeg::FilterGraph::Utils
2
+ module Strings
3
+ # From github: activesupport/lib/active_support/inflector/methods.rb
4
+ def underscore(camel_cased_word)
5
+ return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
6
+ word = camel_cased_word.to_s.gsub(/::/, '/')
7
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
8
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
9
+ word.tr!("-", "_")
10
+ word.downcase!
11
+ word
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Ffmpeg
2
+ module FilterGraph
3
+ VERSION = '0.1.3'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffmpeg-filter_graph
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Jon Sangster
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.8
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.1.8
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.13.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.4.4
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.4.4
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rdoc
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.12'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.12'
125
+ description: 'This gem generates complex filter-graphs for use with the ffmpeg application.
126
+ ie: Values for the -filter_complex command-line option.'
127
+ email:
128
+ - jon@ertt.ca
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".travis.yml"
135
+ - CHANGELOG.md
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - ffmpeg-filter_graph.gemspec
142
+ - lib/ffmpeg/filter_graph.rb
143
+ - lib/ffmpeg/filter_graph/chain.rb
144
+ - lib/ffmpeg/filter_graph/filter.rb
145
+ - lib/ffmpeg/filter_graph/filter_factory.rb
146
+ - lib/ffmpeg/filter_graph/filters/audio.rb
147
+ - lib/ffmpeg/filter_graph/generate_filters_audio.rb
148
+ - lib/ffmpeg/filter_graph/generate_filters_audio_sink.rb
149
+ - lib/ffmpeg/filter_graph/generate_filters_audio_src.rb
150
+ - lib/ffmpeg/filter_graph/generate_filters_multimedia.rb
151
+ - lib/ffmpeg/filter_graph/generate_filters_video.rb
152
+ - lib/ffmpeg/filter_graph/generate_filters_video_src.rb
153
+ - lib/ffmpeg/filter_graph/graph.rb
154
+ - lib/ffmpeg/filter_graph/helper.rb
155
+ - lib/ffmpeg/filter_graph/utils/strings.rb
156
+ - lib/ffmpeg/filter_graph/version.rb
157
+ homepage: https://github.com/sangster/ffmpeg-filter_graph
158
+ licenses: []
159
+ metadata: {}
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 2.5.1
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: FFmpeg filter-graph generator
180
+ test_files: []
181
+ has_rdoc: