video_converter 0.7.6 → 0.7.7
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.
|
@@ -16,14 +16,8 @@ module VideoConverter
|
|
|
16
16
|
|
|
17
17
|
def initialize command, *params
|
|
18
18
|
self.command = command.dup
|
|
19
|
-
if params.any?
|
|
20
|
-
|
|
21
|
-
self.command.gsub!(/%\{(\w+?)\}/) { |m| params[0][$1.to_sym] }
|
|
22
|
-
else
|
|
23
|
-
self.command.gsub!('?') { params.shift }
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
raise ArgumentError.new("Command is not parsed '#{self.command}'") if self.command.include?('?') || self.command.match(/%{[\w\-.]+}/)
|
|
19
|
+
self.command.gsub!(/%\{(\w+?)\}/) { |m| params[0][$1.to_sym] } if params.any?
|
|
20
|
+
raise ArgumentError.new("Command is not parsed '#{self.command}'") if self.command.match(/%{[\w\-.]+}/)
|
|
27
21
|
end
|
|
28
22
|
|
|
29
23
|
def execute params = {}
|
|
@@ -4,7 +4,7 @@ module VideoConverter
|
|
|
4
4
|
class Ffmpeg
|
|
5
5
|
class << self
|
|
6
6
|
attr_accessor :aliases, :bin, :ffprobe_bin
|
|
7
|
-
attr_accessor :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command, :split_command, :concat_command, :mux_command
|
|
7
|
+
attr_accessor :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command, :split_command, :concat_command, :mux_command, :volume_detect_command
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
self.aliases = {
|
|
@@ -16,21 +16,22 @@ module VideoConverter
|
|
|
16
16
|
:video_bitrate => 'b:v',
|
|
17
17
|
:audio_bitrate => 'b:a',
|
|
18
18
|
:size => 's',
|
|
19
|
-
:video_filter => 'vf',
|
|
20
19
|
:format => 'f',
|
|
21
20
|
:bitstream_format => 'bsf',
|
|
22
|
-
:pixel_format => 'pix_fmt'
|
|
21
|
+
:pixel_format => 'pix_fmt',
|
|
22
|
+
:audio_filter => 'af'
|
|
23
23
|
}
|
|
24
24
|
self.bin = '/usr/local/bin/ffmpeg'
|
|
25
25
|
self.ffprobe_bin = '/usr/local/bin/ffprobe'
|
|
26
26
|
|
|
27
|
-
self.one_pass_command = '%{bin} -i %{input} -y %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
|
28
|
-
self.first_pass_command = '%{bin} -i %{input} -y -pass 1 -an %{options} /dev/null 1>>%{log} 2>&1 || exit 1'
|
|
29
|
-
self.second_pass_command = '%{bin} -i %{input} -y -pass 2 %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
|
27
|
+
self.one_pass_command = '%{bin} -i %{input} %{watermark} -y %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
|
28
|
+
self.first_pass_command = '%{bin} -i %{input} %{watermark} -y -pass 1 -an %{options} /dev/null 1>>%{log} 2>&1 || exit 1'
|
|
29
|
+
self.second_pass_command = '%{bin} -i %{input} %{watermark} -y -pass 2 %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
|
30
30
|
self.keyframes_command = '%{ffprobe_bin} -show_frames -select_streams v:0 -print_format csv %{input} | grep frame,video,1 | cut -d\',\' -f5 | tr "\n" "," | sed \'s/,$//\''
|
|
31
31
|
self.split_command = '%{bin} -fflags +genpts -i %{input} %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
|
32
32
|
self.concat_command = "%{bin} -f concat -i %{input} %{options} %{output} 1>>%{log} 2>&1 || exit 1"
|
|
33
33
|
self.mux_command = "%{bin} %{inputs} %{maps} %{options} %{output} 1>>%{log} 2>&1 || exit 1"
|
|
34
|
+
self.volume_detect_command = "%{bin} -i %{input} -af volumedetect -f null - 2>&1"
|
|
34
35
|
|
|
35
36
|
def self.split(input, output)
|
|
36
37
|
output.options = { :format => 'segment', :map => 0, :codec => 'copy' }.merge(output.options)
|
|
@@ -64,13 +65,31 @@ module VideoConverter
|
|
|
64
65
|
end
|
|
65
66
|
# autodeinterlace
|
|
66
67
|
output.options[:deinterlace] = input.metadata[:interlaced] if output.options[:deinterlace].nil?
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
# volume
|
|
69
|
+
output.options[:audio_filter] = "volume=#{volume(output.volume)}" if output.volume
|
|
70
|
+
# filter_complex
|
|
71
|
+
filter_complex = [output.options[:filter_complex]].compact
|
|
72
|
+
filter_complex << "crop=#{output.crop}" if output.crop
|
|
73
|
+
if output.width || output.height
|
|
74
|
+
video_stream = input.metadata[:video_streams].first
|
|
75
|
+
output.width = (output.height * video_stream[:width].to_f / video_stream[:height].to_f / 2).to_i * 2 if output.height && !output.width
|
|
76
|
+
output.height = (output.width * video_stream[:height].to_f / video_stream[:width].to_f / 2).to_i * 2 if output.width && !output.height
|
|
77
|
+
filter_complex << "scale=#{scale(output.width, :w)}:#{scale(output.height, :h)}"
|
|
78
|
+
end
|
|
79
|
+
if output.watermarks && (output.watermarks[:width] || output.watermarks[:height])
|
|
80
|
+
filter_complex = ["[0:v] #{filter_complex.join(',')} [main]"]
|
|
81
|
+
filter_complex << "[1:v] scale=#{scale(output.watermarks[:width], :w, output.width)}:#{scale(output.watermarks[:height], :h, output.height)} [overlay]"
|
|
82
|
+
filter_complex << "[main] [overlay] overlay=#{overlay(output.watermarks[:x], :w)}:#{overlay(output.watermarks[:y], :h)}"
|
|
83
|
+
if output.rotate
|
|
84
|
+
filter_complex[filter_complex.count-1] += ' [overlayed]'
|
|
85
|
+
filter_complex << '[overlayed] ' + rotate(output.rotate)
|
|
86
|
+
end
|
|
87
|
+
output.options[:filter_complex] = "'#{filter_complex.join(';')}'"
|
|
88
|
+
else
|
|
89
|
+
filter_complex << "overlay=#{overlay(output.watermarks[:x], :w)}:#{overlay(output.watermarks[:y], :h)}" if output.watermarks
|
|
90
|
+
filter_complex << rotate(output.rotate) if output.rotate
|
|
91
|
+
output.options[:filter_complex] = filter_complex.join(',') if filter_complex.any?
|
|
92
|
+
end
|
|
74
93
|
|
|
75
94
|
output.options[:format] ||= File.extname(output.filename).delete('.')
|
|
76
95
|
output.options = {
|
|
@@ -159,6 +178,7 @@ module VideoConverter
|
|
|
159
178
|
{
|
|
160
179
|
:bin => bin,
|
|
161
180
|
:input => input.to_s,
|
|
181
|
+
:watermark => (output.watermarks ? '-i ' + output.watermarks[:url] : ''),
|
|
162
182
|
:options => output.options.map do |option, values|
|
|
163
183
|
unless output.respond_to?(option)
|
|
164
184
|
option = '-' + (aliases[option] || option).to_s
|
|
@@ -171,5 +191,34 @@ module VideoConverter
|
|
|
171
191
|
:log => output.log
|
|
172
192
|
}
|
|
173
193
|
end
|
|
194
|
+
|
|
195
|
+
def scale(size, wh, percent_of = nil)
|
|
196
|
+
if size.to_s.end_with?('%')
|
|
197
|
+
percent_of ? (percent_of * size.to_f / 100).to_i : "i#{wh}*#{size.to_f/100}"
|
|
198
|
+
else
|
|
199
|
+
size || "trunc\\(o#{{:h => :w, :w => :h}[wh]}/a/2\\)*2"
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def overlay(xy, wh)
|
|
204
|
+
if xy.to_s.end_with?('%')
|
|
205
|
+
xy = xy.to_f / 100
|
|
206
|
+
xy < 0 ? "main_#{wh}*#{1 + xy}-overlay_#{wh}" : "main_#{wh}*#{xy}"
|
|
207
|
+
else
|
|
208
|
+
xy.to_i < 0 ? "main_#{wh}-overlay_#{wh}#{xy}" : xy.to_i
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def rotate(angle)
|
|
213
|
+
{ 90 => 'transpose=2', 180 => 'transpose=2,transpose=2', 270 => 'transpose=1' }[angle]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def volume(level)
|
|
217
|
+
if level.to_s.end_with?('dB')
|
|
218
|
+
(level.to_f - input.mean_volume.to_f).round(4).to_s + 'dB'
|
|
219
|
+
else
|
|
220
|
+
level
|
|
221
|
+
end
|
|
222
|
+
end
|
|
174
223
|
end
|
|
175
224
|
end
|
|
@@ -66,6 +66,10 @@ module VideoConverter
|
|
|
66
66
|
@metadata
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
def mean_volume
|
|
70
|
+
@mean_volume ||= Command.new(Ffmpeg.volume_detect_command, :bin => Ffmpeg.bin, :input => input).capture.match(/mean_volume:\s([-\d.]+)\sdB/).to_a[1]
|
|
71
|
+
end
|
|
72
|
+
|
|
69
73
|
def select_outputs(outputs)
|
|
70
74
|
outputs.select { |output| !output.path || output.path == input }
|
|
71
75
|
end
|
|
@@ -9,17 +9,19 @@ module VideoConverter
|
|
|
9
9
|
self.log = 'converter.log'
|
|
10
10
|
self.keyframe_interval_in_seconds = 4
|
|
11
11
|
|
|
12
|
-
attr_accessor :chunks_dir, :crop, :drm, :faststart, :ffmpeg_output, :filename, :group, :height, :log, :no_fragments, :one_pass, :options, :path, :rotate, :streams, :thumbnails, :type, :uid, :width, :work_dir
|
|
12
|
+
attr_accessor :chunks_dir, :crop, :drm, :faststart, :ffmpeg_output, :filename, :group, :height, :log, :no_fragments, :one_pass, :options, :path, :rotate, :streams, :thumbnails, :type, :uid, :volume, :watermarks, :width, :work_dir
|
|
13
13
|
|
|
14
14
|
def initialize params = {}
|
|
15
15
|
self.work_dir = File.join(self.class.work_dir, params[:uid])
|
|
16
|
-
|
|
16
|
+
mkdir_mode = params.delete(:mkdir_mode)
|
|
17
|
+
mkdir_mode = mkdir_mode.to_i(8) if mkdir_mode.is_a?(String)
|
|
18
|
+
FileUtils.mkdir_p(work_dir, :mode => mkdir_mode)
|
|
17
19
|
self.filename = params[:filename] or raise ArgumentError.new('Filename required')
|
|
18
20
|
self.log = File.join(work_dir, self.class.log)
|
|
19
21
|
self.type = params[:type] || 'default'
|
|
20
22
|
if type == 'segmented'
|
|
21
23
|
self.chunks_dir = File.join(work_dir, File.basename(filename, '.*'))
|
|
22
|
-
FileUtils.mkdir_p(chunks_dir)
|
|
24
|
+
FileUtils.mkdir_p(chunks_dir, :mode => mkdir_mode)
|
|
23
25
|
if File.extname(filename) == '.m3u8'
|
|
24
26
|
self.ffmpeg_output = chunks_dir + '.ts'
|
|
25
27
|
params[:format] = 'mpegts'
|
|
@@ -33,7 +35,7 @@ module VideoConverter
|
|
|
33
35
|
self.ffmpeg_output = File.join(work_dir, filename)
|
|
34
36
|
end
|
|
35
37
|
raise ArgumentError.new('Invalid type') unless %w(default segmented playlist).include?(type)
|
|
36
|
-
[:path, :streams, :width, :height, :one_pass, :rotate, :faststart, :thumbnails, :group, :drm, :no_fragments, :crop].each { |attr| self.send("#{attr}=", params[attr]) }
|
|
38
|
+
[:path, :streams, :width, :height, :one_pass, :rotate, :faststart, :thumbnails, :group, :drm, :no_fragments, :crop, :watermarks, :volume].each { |attr| self.send("#{attr}=", params[attr]) }
|
|
37
39
|
[:video_bitrate, :audio_bitrate].each { |bitrate| params[bitrate] = "#{params[bitrate]}k" if params[bitrate].is_a?(Numeric) }
|
|
38
40
|
|
|
39
41
|
# options will be substituted to convertation commands
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: video_converter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.7
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-09-
|
|
12
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: video_screenshoter
|
|
@@ -141,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
141
141
|
version: '0'
|
|
142
142
|
segments:
|
|
143
143
|
- 0
|
|
144
|
-
hash:
|
|
144
|
+
hash: -3630338721637181246
|
|
145
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
146
|
none: false
|
|
147
147
|
requirements:
|
|
@@ -150,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
150
150
|
version: '0'
|
|
151
151
|
segments:
|
|
152
152
|
- 0
|
|
153
|
-
hash:
|
|
153
|
+
hash: -3630338721637181246
|
|
154
154
|
requirements:
|
|
155
155
|
- ffmpeg, version 1.2 or greated configured with libx264 and libfaac
|
|
156
156
|
- live_segmenter to convert to hls
|