ffmpeg-filter_graph 0.1.3

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 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: