video_converter 0.5.0 → 0.6.0
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.
data/lib/video_converter/base.rb
CHANGED
@@ -16,14 +16,41 @@ module VideoConverter
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def run
|
19
|
+
success = convert && faststart && make_screenshots && segment
|
20
|
+
clear if clear_tmp && success
|
21
|
+
success
|
22
|
+
end
|
23
|
+
|
24
|
+
def convert
|
19
25
|
success = true
|
20
26
|
inputs.each do |input|
|
21
27
|
input.output_groups.each do |group|
|
22
28
|
success &&= Ffmpeg.new(input, group).run
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
end
|
30
|
+
end
|
31
|
+
success
|
32
|
+
end
|
33
|
+
|
34
|
+
def faststart
|
35
|
+
success = true
|
36
|
+
outputs.each do |output|
|
37
|
+
success &&= Faststart.new(output).run if output.faststart
|
38
|
+
end
|
39
|
+
success
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_screenshots
|
43
|
+
success = true
|
44
|
+
outputs.each do |output|
|
45
|
+
success &&= VideoScreenshoter.new(output.thumbnails.merge(:input => output.ffmpeg_output, :output_dir => File.join(output.work_dir, 'thumbnails'))).run if output.thumbnails
|
46
|
+
end
|
47
|
+
success
|
48
|
+
end
|
49
|
+
|
50
|
+
def segment
|
51
|
+
success = true
|
52
|
+
inputs.each do |input|
|
53
|
+
input.output_groups.each do |group|
|
27
54
|
if playlist = group.detect { |output| output.type == 'playlist' }
|
28
55
|
success &&= if playlist.format == 'm3u8'
|
29
56
|
LiveSegmenter.new(input, group).run
|
@@ -33,11 +60,20 @@ module VideoConverter
|
|
33
60
|
end
|
34
61
|
end
|
35
62
|
end
|
36
|
-
clear if clear_tmp && success
|
37
|
-
success
|
38
63
|
end
|
39
64
|
|
40
|
-
|
65
|
+
def split
|
66
|
+
Ffmpeg.new(inputs.first, outputs).split
|
67
|
+
end
|
68
|
+
|
69
|
+
def concat
|
70
|
+
list = File.join(outputs.first.work_dir, 'list.txt')
|
71
|
+
# NOTE ffmpeg concat list requires unescaped files
|
72
|
+
File.write(list, inputs.map { |input| "file '#{File.absolute_path(input.unescape)}'" }.join("\n"))
|
73
|
+
success = Ffmpeg.new(list, outputs).concat
|
74
|
+
FileUtils.rm list if success
|
75
|
+
success
|
76
|
+
end
|
41
77
|
|
42
78
|
def clear
|
43
79
|
`cat #{outputs.first.log} >> #{VideoConverter.log} && rm #{outputs.first.log}`
|
@@ -3,12 +3,13 @@
|
|
3
3
|
module VideoConverter
|
4
4
|
class Ffmpeg
|
5
5
|
class << self
|
6
|
-
attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command
|
6
|
+
attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command, :split_command, :concat_command
|
7
7
|
end
|
8
8
|
|
9
9
|
self.bin = '/usr/local/bin/ffmpeg'
|
10
10
|
self.ffprobe_bin = '/usr/local/bin/ffprobe'
|
11
11
|
self.options = {
|
12
|
+
:codec => '-c',
|
12
13
|
:video_codec => '-c:v',
|
13
14
|
:audio_codec => '-c:a',
|
14
15
|
:frame_rate => '-r',
|
@@ -24,12 +25,17 @@ module VideoConverter
|
|
24
25
|
:format => '-f',
|
25
26
|
:bitstream_format => '-bsf',
|
26
27
|
:pixel_format => '-pix_fmt',
|
27
|
-
:deinterlace => '-deinterlace'
|
28
|
+
:deinterlace => '-deinterlace',
|
29
|
+
:map => '-map',
|
30
|
+
:segment_time => '-segment_time',
|
31
|
+
:reset_timestamps => '-reset_timestamps'
|
28
32
|
}
|
29
33
|
self.one_pass_command = '%{bin} -i %{input} -y %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
30
34
|
self.first_pass_command = '%{bin} -i %{input} -y -pass 1 -an %{options} /dev/null 1>>%{log} 2>&1 || exit 1'
|
31
35
|
self.second_pass_command = '%{bin} -i %{input} -y -pass 2 %{options} %{output} 1>>%{log} 2>&1 || exit 1'
|
32
36
|
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/,$//\''
|
37
|
+
self.split_command = '%{bin} -fflags +genpts -i %{input} -segment_time %{segment_time} -reset_timestamps 1 -c copy -map 0 -f segment %{output} 1>>%{log} 2>&1 || exit 1'
|
38
|
+
self.concat_command = "%{bin} -f concat -i %{input} -c %{codec} %{output} 1>>%{log} 2>&1 || exit 1"
|
33
39
|
|
34
40
|
attr_accessor :input, :group
|
35
41
|
|
@@ -77,6 +83,16 @@ module VideoConverter
|
|
77
83
|
success
|
78
84
|
end
|
79
85
|
|
86
|
+
def split
|
87
|
+
Command.new(self.class.split_command, prepare_params(input, group.first)).execute
|
88
|
+
end
|
89
|
+
|
90
|
+
def concat
|
91
|
+
output = group.first
|
92
|
+
output.codec ||= 'copy'
|
93
|
+
Command.new(self.class.concat_command, prepare_params(input, output)).execute
|
94
|
+
end
|
95
|
+
|
80
96
|
private
|
81
97
|
|
82
98
|
def prepare_params input, output
|
@@ -91,6 +107,8 @@ module VideoConverter
|
|
91
107
|
:input => input.to_s,
|
92
108
|
:log => output.log,
|
93
109
|
:output => output.ffmpeg_output,
|
110
|
+
:codec => output.codec,
|
111
|
+
:segment_time => output.segment_time,
|
94
112
|
:options => self.class.options.map do |output_option, ffmpeg_option|
|
95
113
|
if output.send(output_option).present?
|
96
114
|
if output.send(output_option) == true
|
@@ -17,9 +17,9 @@ module VideoConverter
|
|
17
17
|
|
18
18
|
attr_accessor :uid, :work_dir, :log, :threads, :passlogfile
|
19
19
|
attr_accessor :type, :filename
|
20
|
-
attr_accessor :format, :ffmpeg_output, :video_codec, :audio_codec, :bitstream_format, :pixel_format
|
20
|
+
attr_accessor :format, :ffmpeg_output, :codec, :video_codec, :audio_codec, :bitstream_format, :pixel_format, :map
|
21
21
|
attr_accessor :one_pass, :video_bitrate, :audio_bitrate
|
22
|
-
attr_accessor :streams, :path, :chunks_dir
|
22
|
+
attr_accessor :streams, :path, :chunks_dir, :segment_time, :reset_timestamps
|
23
23
|
attr_accessor :frame_rate, :keyint_min, :keyframe_interval, :force_keyframes
|
24
24
|
attr_accessor :size, :width, :height, :video_filter
|
25
25
|
attr_accessor :thumbnails
|
@@ -45,14 +45,16 @@ module VideoConverter
|
|
45
45
|
self.format = 'mpegts'
|
46
46
|
self.ffmpeg_output = File.join(work_dir, File.basename(filename, '.*') + '.ts')
|
47
47
|
else
|
48
|
-
self.format = File.extname(filename).sub('.', '')
|
48
|
+
self.format = params[:format] || File.extname(filename).sub('.', '')
|
49
49
|
self.ffmpeg_output = File.join(work_dir, filename)
|
50
50
|
raise ArgumentError.new('Invalid playlist extension') if type == 'playlist' && !['f4m', 'm3u8'].include?(format)
|
51
51
|
end
|
52
|
-
self.
|
53
|
-
self.
|
52
|
+
self.codec = params[:codec]
|
53
|
+
self.video_codec = params[:video_codec] || self.class.video_codec unless codec
|
54
|
+
self.audio_codec = params[:audio_codec] || self.class.audio_codec unless codec
|
54
55
|
self.bitstream_format = params[:bitstream_format]
|
55
56
|
self.pixel_format = params[:pixel_format] || self.class.pixel_format
|
57
|
+
self.map = params[:map]
|
56
58
|
|
57
59
|
# Rate controle
|
58
60
|
self.one_pass = !!params[:one_pass]
|
@@ -66,6 +68,8 @@ module VideoConverter
|
|
66
68
|
self.chunks_dir = File.join(work_dir, File.basename(filename, '.*'))
|
67
69
|
FileUtils.mkdir_p(chunks_dir)
|
68
70
|
end
|
71
|
+
self.segment_time = params[:segment_time]
|
72
|
+
self.reset_timestamps = params[:reset_timestamps]
|
69
73
|
|
70
74
|
# Frame rate
|
71
75
|
self.frame_rate = params[:frame_rate]
|
@@ -123,4 +123,34 @@ class VideoConverterTest < Test::Unit::TestCase
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
126
|
+
|
127
|
+
context 'split' do
|
128
|
+
setup do
|
129
|
+
(@c = VideoConverter.new(
|
130
|
+
:input => "test/fixtures/test (1).mp4",
|
131
|
+
:output => { :segment_time => 2, :filename => '%01d.nut' }
|
132
|
+
)).split
|
133
|
+
end
|
134
|
+
should 'segment file' do
|
135
|
+
assert_equal %w(0.nut 1.nut 2.nut 3.nut converter.log), Dir.entries(@c.outputs.first.work_dir).delete_if { |e| %w(. ..).include?(e) }.sort
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'concat' do
|
140
|
+
setup do
|
141
|
+
FileUtils.cp("test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4")
|
142
|
+
(@c = VideoConverter.new(
|
143
|
+
:input => ["test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4"],
|
144
|
+
:output => { :filename => 'concat.mp4' }
|
145
|
+
)).concat
|
146
|
+
FileUtils.rm("test/fixtures/test (2).mp4")
|
147
|
+
end
|
148
|
+
should 'concat inputs' do
|
149
|
+
assert File.exists?(@c.outputs.first.ffmpeg_output)
|
150
|
+
assert_equal(
|
151
|
+
VideoConverter.new(:input => "test/fixtures/test (1).mp4").inputs.first.metadata[:duration_in_ms]/1000*2,
|
152
|
+
VideoConverter.new(:input => @c.outputs.first.ffmpeg_output).inputs.first.metadata[:duration_in_ms]/1000
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
126
156
|
end
|
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.
|
4
|
+
version: 0.6.0
|
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-05-
|
12
|
+
date: 2014-05-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: video_screenshoter
|
@@ -142,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
142
|
version: '0'
|
143
143
|
segments:
|
144
144
|
- 0
|
145
|
-
hash:
|
145
|
+
hash: 1551295134079710729
|
146
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
147
|
none: false
|
148
148
|
requirements:
|
@@ -151,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
151
|
version: '0'
|
152
152
|
segments:
|
153
153
|
- 0
|
154
|
-
hash:
|
154
|
+
hash: 1551295134079710729
|
155
155
|
requirements:
|
156
156
|
- ffmpeg, version 1.2 or greated configured with libx264 and libfaac
|
157
157
|
- live_segmenter to convert to hls
|