video_converter 0.1.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/.Rakefile.swp +0 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +4 -0
- data/Rakefile +10 -0
- data/lib/video_converter/base.rb +70 -0
- data/lib/video_converter/command.rb +35 -0
- data/lib/video_converter/ffmpeg.rb +106 -0
- data/lib/video_converter/live_segmenter.rb +98 -0
- data/lib/video_converter/process.rb +32 -0
- data/lib/video_converter/profile.rb +37 -0
- data/lib/video_converter/version.rb +3 -0
- data/lib/video_converter.rb +31 -0
- data/test/ffmpeg_test.rb +37 -0
- data/test/fixtures/test.mp4 +0 -0
- data/test/fixtures/test.mp4.log-0.log +121 -0
- data/test/fixtures/test.mp4.log-0.log.mbtree +0 -0
- data/test/test_helper.rb +5 -0
- data/test/video_converter_test.rb +90 -0
- data/video_converter.gemspec +28 -0
- metadata +146 -0
data/.Rakefile.swp
ADDED
Binary file
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 novikov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class Base
|
5
|
+
attr_accessor :input, :profile, :one_pass, :type, :paral, :log, :id, :playlist_dir, :chunk_base, :no_convert
|
6
|
+
|
7
|
+
def initialize params
|
8
|
+
self.no_convert = params[:no_convert].nil? ? VideoConverter.no_convert : params[:no_convert]
|
9
|
+
unless no_convert
|
10
|
+
self.input = params[:input] or raise ArgumentError.new('Input is needed')
|
11
|
+
raise ArgumentError.new('Input does not exist') unless File.exists?(input)
|
12
|
+
end
|
13
|
+
self.profile = params[:profile] or raise ArgumentError.new('Profile is needed')
|
14
|
+
self.type = params[:type].nil? ? VideoConverter.type : params[:type].to_sym
|
15
|
+
raise ArgumentError.new("Incorrect type #{type}") unless [:hls, :mp4].include?(type)
|
16
|
+
if type == :hls
|
17
|
+
self.playlist_dir = params[:playlist_dir] or raise ArgumentError.new("Playlist dir is needed")
|
18
|
+
end
|
19
|
+
self.one_pass = params[:one_pass].nil? ? Ffmpeg.one_pass : params[:one_pass]
|
20
|
+
self.paral = params[:paral].nil? ? VideoConverter.paral : params[:paral]
|
21
|
+
if params[:log].nil?
|
22
|
+
self.log = '/dev/null'
|
23
|
+
else
|
24
|
+
self.log = params[:log]
|
25
|
+
FileUtils.mkdir_p File.dirname(log)
|
26
|
+
end
|
27
|
+
self.id = object_id
|
28
|
+
self.chunk_base = params[:chunk_base]
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
process = VideoConverter::Process.new(id)
|
33
|
+
process.pid = `cat /proc/self/stat`.split[3]
|
34
|
+
actions = []
|
35
|
+
actions << :convert unless no_convert
|
36
|
+
actions << :live_segment if type == :hls
|
37
|
+
actions.each do |action|
|
38
|
+
process.status = action.to_s
|
39
|
+
process.progress = 0
|
40
|
+
res = send action
|
41
|
+
if res
|
42
|
+
process.status = "#{action}_success"
|
43
|
+
else
|
44
|
+
process.status = "#{action}_error"
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def convert
|
54
|
+
params = {}
|
55
|
+
[:input, :profile, :one_pass, :paral, :log].each do |param|
|
56
|
+
params[param] = self.send(param)
|
57
|
+
end
|
58
|
+
Ffmpeg.new(params).run
|
59
|
+
end
|
60
|
+
|
61
|
+
def live_segment
|
62
|
+
params = {}
|
63
|
+
[:profile, :playlist_dir, :paral, :chunk_base, :log, :no_convert].each do |param|
|
64
|
+
params[param] = self.send(param)
|
65
|
+
end
|
66
|
+
params[:delete_input] = false if no_convert
|
67
|
+
LiveSegmenter.new(params).run
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class Command
|
5
|
+
class << self
|
6
|
+
attr_accessor :debug, :verbose
|
7
|
+
end
|
8
|
+
self.debug = false
|
9
|
+
self.verbose = false
|
10
|
+
|
11
|
+
attr_accessor :command
|
12
|
+
|
13
|
+
def initialize command, params = {}
|
14
|
+
res = command.clone
|
15
|
+
params.each do |param, value|
|
16
|
+
res.gsub! "%{#{param}}", value.to_s
|
17
|
+
end
|
18
|
+
self.command = res
|
19
|
+
raise ArgumentError.new("Command is not parsed '#{self.command}'") if self.command.match(/%{[\w\-.]+}/)
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute params = {}
|
23
|
+
puts command if params[:verbose] || self.class.verbose
|
24
|
+
if params[:debug] || self.class.debug
|
25
|
+
true
|
26
|
+
else
|
27
|
+
system command
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
command
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class Ffmpeg
|
5
|
+
class << self
|
6
|
+
attr_accessor :bin, :one_pass, :one_pass_command, :first_pass_command, :second_pass_command, :metadata_command
|
7
|
+
end
|
8
|
+
|
9
|
+
self.bin = '/usr/local/bin/ffmpeg'
|
10
|
+
|
11
|
+
self.one_pass = false
|
12
|
+
|
13
|
+
self.one_pass_command = "%{bin} -i %{input} -y -aspect %{aspect} -acodec copy -vcodec libx264 -g 100 -keyint_min 50 -b:v %{bitrate}k -bt %{bitrate}k -threads %{threads} -f mp4 %{file} 1>%{log} 2>&1 || exit 1"
|
14
|
+
|
15
|
+
self.first_pass_command = "%{bin} -i %{input} -y -aspect %{aspect} -an -vcodec libx264 -g 100 -keyint_min 50 -pass 1 -passlogfile %{input}.log -b:v 700k -bt 700k -threads %{threads} -f mp4 /dev/null 1>>%{log} 2>&1 || exit 1"
|
16
|
+
|
17
|
+
self.second_pass_command = "%{bin} -i %{input} -y -aspect %{aspect} -acodec copy -vcodec libx264 -g 100 -keyint_min 50 -pass 2 -passlogfile %{input}.log -b:v %{bitrate}k -bt %{bitrate}k -threads %{threads} -f mp4 %{file} 1>%{log} 2>&1 || exit 1"
|
18
|
+
|
19
|
+
self.metadata_command = "%{bin} -i %{input} 2>&1"
|
20
|
+
|
21
|
+
attr_accessor :input, :profile, :one_pass, :paral, :log
|
22
|
+
|
23
|
+
def initialize params
|
24
|
+
params.each do |param, value|
|
25
|
+
self.send("#{param}=", value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
res = true
|
31
|
+
threads = []
|
32
|
+
Profile.groups(profile).each do |qualities|
|
33
|
+
unless one_pass
|
34
|
+
group_command = Command.new self.class.first_pass_command, common_params.merge(qualities.first.to_hash)
|
35
|
+
res &&= group_command.execute
|
36
|
+
end
|
37
|
+
qualities.each do |quality|
|
38
|
+
if one_pass
|
39
|
+
quality_command = Command.new self.class.one_pass_command, common_params.merge(quality.to_hash)
|
40
|
+
else
|
41
|
+
quality_command = Command.new self.class.second_pass_command, common_params.merge(quality.to_hash)
|
42
|
+
end
|
43
|
+
if paral
|
44
|
+
threads << Thread.new { res &&= quality_command.execute }
|
45
|
+
else
|
46
|
+
res &&= quality_command.execute
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
threads.each { |t| t.join } if paral
|
51
|
+
res
|
52
|
+
end
|
53
|
+
|
54
|
+
def metadata
|
55
|
+
metadata = {}
|
56
|
+
s = `#{Command.new self.class.metadata_command, common_params}`
|
57
|
+
if (m = s.match(/Stream.*?Audio:\s*(\w+).*?(\d+)\s*Hz.*?(\d+)\s*kb\/s$/).to_a).any?
|
58
|
+
metadata[:audio_codec] = m[1]
|
59
|
+
metadata[:audio_sample_rate] = m[2].to_i
|
60
|
+
metadata[:audio_bitrate_in_kbps] = m[3].to_i
|
61
|
+
end
|
62
|
+
if (m = s.scan(/Stream #\d+:\d+/)).any?
|
63
|
+
metadata[:channels] = m.count
|
64
|
+
end
|
65
|
+
if (m = s.match(/Duration:\s+(\d+):(\d+):([\d.]+).*?bitrate:\s*(\d+)\s*kb\/s/).to_a).any?
|
66
|
+
metadata[:duration_in_ms] = ((m[1].to_i * 3600 + m[2].to_i * 60 + m[3].to_f) * 1000).to_i
|
67
|
+
metadata[:total_bitrate_in_kbps] = m[4].to_i
|
68
|
+
end
|
69
|
+
if (m = s.match(/Stream.*?Video:\s(\S+).*?,\s*((\d+)x(\d+)).*?(\d+)\s*kb\/s.*?([\d.]+)\s*fps/).to_a).any?
|
70
|
+
metadata[:video_codec] = m[1]
|
71
|
+
metadata[:width] = m[3].to_i
|
72
|
+
metadata[:height] = m[4].to_i
|
73
|
+
metadata[:video_bitrate_in_kbps] = m[5].to_i
|
74
|
+
metadata[:frame_rate] = m[6].to_f
|
75
|
+
end
|
76
|
+
if metadata.any?
|
77
|
+
if is_url?
|
78
|
+
url = URI.parse(input)
|
79
|
+
Net::HTTP.start(url.host) do |http|
|
80
|
+
response = http.request_head url.path
|
81
|
+
metadata[:file_size_in_bytes] = response['content-length'].to_i
|
82
|
+
puts response['content-type']
|
83
|
+
end
|
84
|
+
elsif is_local?
|
85
|
+
metadata[:file_size_in_bytes] = File.size(input)
|
86
|
+
end
|
87
|
+
metadata[:format] = File.extname(input).sub('.', '')
|
88
|
+
end
|
89
|
+
metadata
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def common_params
|
95
|
+
{ :bin => self.class.bin, :input => input, :log => log }
|
96
|
+
end
|
97
|
+
|
98
|
+
def is_url?
|
99
|
+
!!input.match(/^http:\/\//)
|
100
|
+
end
|
101
|
+
|
102
|
+
def is_local?
|
103
|
+
File.file?(input)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class LiveSegmenter
|
5
|
+
class << self
|
6
|
+
attr_accessor :bin, :ffprobe_bin, :chunks_command, :segment_length, :filename_prefix, :encoding_profile, :delete_input
|
7
|
+
end
|
8
|
+
|
9
|
+
self.bin = '/usr/local/bin/live_segmenter'
|
10
|
+
|
11
|
+
self.ffprobe_bin = '/usr/local/bin/ffprobe'
|
12
|
+
|
13
|
+
self.segment_length = 10
|
14
|
+
|
15
|
+
self.filename_prefix = 's'
|
16
|
+
|
17
|
+
self.encoding_profile = 's'
|
18
|
+
|
19
|
+
self.delete_input = true
|
20
|
+
|
21
|
+
self.chunks_command = '%{ffmpeg_bin} -i %{input} -vcodec libx264 -acodec copy -f mpegts pipe:1 2>>/dev/null | %{bin} %{segment_length} %{dir} %{filename_prefix} %{encoding_profile} 1>>%{log} 2>&1'
|
22
|
+
|
23
|
+
attr_accessor :profile, :playlist_dir, :paral, :segment_length, :filename_prefix, :encoding_profile, :delete_input, :chunk_base, :log
|
24
|
+
|
25
|
+
def initialize params
|
26
|
+
[:profile, :playlist_dir].each do |param|
|
27
|
+
self.send("#{param}=", params[param])
|
28
|
+
end
|
29
|
+
[:segment_length, :filename_prefix, :encoding_profile, :delete_input].each do |param|
|
30
|
+
self.send("#{param}=", params[param].nil? ? self.class.send(param) : params[param])
|
31
|
+
end
|
32
|
+
self.chunk_base = params[:chunk_base] ? params[:chunk_base] : '.'
|
33
|
+
self.chunk_base += '/' unless chunk_base.end_with?('/')
|
34
|
+
self.log = params[:log]
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
res = true
|
39
|
+
threads = []
|
40
|
+
p = Proc.new do |profile|
|
41
|
+
input = profile.to_hash[:file]
|
42
|
+
output = profile.to_hash[:dir]
|
43
|
+
make_chunks(input, output) && gen_quality_playlist(output, "#{File.basename(output)}.m3u8")
|
44
|
+
end
|
45
|
+
[profile].flatten.each do |profile|
|
46
|
+
if paral
|
47
|
+
threads << Thread.new { res &&= p.call(profile) }
|
48
|
+
else
|
49
|
+
res &&= p.call(profile)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
gen_group_playlists
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def make_chunks input, output
|
58
|
+
params = {}
|
59
|
+
[:segment_length, :filename_prefix, :encoding_profile].each { |param| params[param] = self.send(param) }
|
60
|
+
command = Command.new self.class.chunks_command, params.merge(:input => input, :dir => output).merge(common_params)
|
61
|
+
res = command.execute
|
62
|
+
FileUtils.rm input if delete_input
|
63
|
+
res
|
64
|
+
end
|
65
|
+
|
66
|
+
def gen_quality_playlist chunks_dir, playlist_name
|
67
|
+
res = ''
|
68
|
+
durations = []
|
69
|
+
Dir::glob(File.join(chunks_dir, 's-*[0-9].ts')).each do |chunk|
|
70
|
+
durations << (duration = chunk_duration chunk)
|
71
|
+
res += "#EXTINF:#%0.2f\n" % duration
|
72
|
+
res += "#{chunk_base}#{File.basename(chunks_dir)}/#{File.basename(chunk)}\n"
|
73
|
+
end
|
74
|
+
res = "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:#{durations.max}\n#EXT-X-MEDIA-SEQUENCE:0\n" + res + "#EXT-X-ENDLIST"
|
75
|
+
File.open(File.join(playlist_dir, playlist_name), 'w') { |f| f.write res }
|
76
|
+
end
|
77
|
+
|
78
|
+
def gen_group_playlists
|
79
|
+
Profile.groups(profile).each_with_index do |group, index|
|
80
|
+
res = "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-PLAYLIST-TYPE:VOD"
|
81
|
+
group.sort { |g1, g2| g1.to_hash[:bandwidth] <=> g2.to_hash[:bandwidth] }.each do |quality|
|
82
|
+
res += "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=#{quality.to_hash[:bandwidth] * 1000}\n"
|
83
|
+
res += File.join(chunk_base, playlist_dir, File.basename(quality.to_hash[:dir]) + '.m3u8')
|
84
|
+
end
|
85
|
+
res += "#EXT-X-ENDLIST"
|
86
|
+
File.open(File.join(playlist_dir, "playlist#{index + 1}.m3u8"), 'w') { |f| f.write res }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def common_params
|
91
|
+
{ :ffmpeg_bin => Ffmpeg.bin, :bin => self.class.bin, :log => log }
|
92
|
+
end
|
93
|
+
|
94
|
+
def chunk_duration chunk
|
95
|
+
s = `#{self.class.ffprobe_bin} #{chunk} 2>&1`.match(/Duration:.*(?:[0-9]{2}):(?:[0-9]{2}):([0-9]{2}(?:\.[0-9]{2})?)/).to_a[1].to_f
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class Process
|
5
|
+
attr_accessor :id, :status, :progress, :pid
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :path
|
9
|
+
end
|
10
|
+
self.path = 'tmp/processes'
|
11
|
+
|
12
|
+
def initialize id
|
13
|
+
self.id = id
|
14
|
+
Dir.mkdir(self.class.path) unless Dir.exists?(self.class.path)
|
15
|
+
end
|
16
|
+
|
17
|
+
[:status, :progress, :pid].each do |attr|
|
18
|
+
define_method(attr) { File.open(send("#{attr}_file".to_sym), 'r') { |f| f.read } rescue nil }
|
19
|
+
define_method("#{attr}=") { |value| File.open(send("#{attr}_file".to_sym), 'w') { |f| f.write value } }
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
`pkill -P #{pid}`
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
[:status, :progress, :pid].each do |attr|
|
29
|
+
define_method("#{attr}_file".to_sym) { File.join self.class.path, "#{id}_#{attr}" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoConverter
|
4
|
+
class Profile
|
5
|
+
class << self
|
6
|
+
attr_accessor :needed_params, :default_params
|
7
|
+
end
|
8
|
+
|
9
|
+
self.needed_params = [:bitrate]
|
10
|
+
self.default_params = {:aspect => '4:3', :threads => 1}
|
11
|
+
|
12
|
+
attr_accessor :params
|
13
|
+
|
14
|
+
def initialize params
|
15
|
+
self.class.needed_params.each do |needed_param|
|
16
|
+
raise ArgumentError.new("#{needed_param} is needed") unless params[needed_param]
|
17
|
+
end
|
18
|
+
self.params = self.class.default_params.merge params
|
19
|
+
raise ArgumentError.new("Output file or output dir is needed") unless params[:file] || params[:dir]
|
20
|
+
self.params[:dir] = params[:dir] || File.dirname(params[:file])
|
21
|
+
self.params[:file] = params[:file] || File.join(params[:dir], "#{object_id}.mp4")
|
22
|
+
FileUtils.mkdir_p self.params[:dir]
|
23
|
+
self.params[:bandwidth] = params[:bandwidth] || params[:bitrate]
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
params
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.groups profiles
|
31
|
+
groups = profiles.is_a?(Array) ? profiles : [profiles]
|
32
|
+
groups.map do |qualities|
|
33
|
+
qualities.is_a?(Array) ? qualities : [qualities]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "video_converter/version"
|
2
|
+
require "video_converter/profile"
|
3
|
+
require "video_converter/base"
|
4
|
+
require "video_converter/command"
|
5
|
+
require "video_converter/process"
|
6
|
+
require "video_converter/ffmpeg"
|
7
|
+
require "video_converter/live_segmenter"
|
8
|
+
require "fileutils"
|
9
|
+
require "net/http"
|
10
|
+
|
11
|
+
module VideoConverter
|
12
|
+
class << self
|
13
|
+
attr_accessor :type, :paral, :no_convert
|
14
|
+
end
|
15
|
+
|
16
|
+
self.type = :mp4
|
17
|
+
self.paral = true
|
18
|
+
self.no_convert = false
|
19
|
+
|
20
|
+
def self.new params
|
21
|
+
VideoConverter::Base.new params
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find id
|
25
|
+
VideoConverter::Process.new id
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.metadata input
|
29
|
+
VideoConverter::Ffmpeg.new(:input => input).metadata
|
30
|
+
end
|
31
|
+
end
|
data/test/ffmpeg_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FfmpegTest < Test::Unit::TestCase
|
4
|
+
context 'metadata' do
|
5
|
+
context 'when input does not exist' do
|
6
|
+
should 'be empty' do
|
7
|
+
assert VideoConverter::Ffmpeg.new(:input => 'tmp/fixtures/not_existed_file').metadata.empty?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when input is dir' do
|
12
|
+
should 'be empty' do
|
13
|
+
assert VideoConverter::Ffmpeg.new(:input => 'tmp/fixtures').metadata.empty?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when input is not video file' do
|
18
|
+
should 'be empty' do
|
19
|
+
assert VideoConverter::Ffmpeg.new(:input => __FILE__).metadata.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when input is video file' do
|
24
|
+
should 'not be empty' do
|
25
|
+
h = {:audio_bitrate_in_kbps=>97, :audio_codec=>"aac", :audio_sample_rate=>44100, :channels=>2, :duration_in_ms=>4019, :total_bitrate_in_kbps=>1123, :frame_rate=>29.97, :height=>360, :video_bitrate_in_kbps=>1020, :video_codec=>"h264", :width=>640, :file_size_in_bytes=>564356, :format=>"mp4"}
|
26
|
+
assert_equal h, VideoConverter::Ffmpeg.new(:input => 'test/fixtures/test.mp4').metadata
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when input is url' do
|
31
|
+
should 'not be empty' do
|
32
|
+
h = {:audio_codec=>"aac", :audio_sample_rate=>48000, :audio_bitrate_in_kbps=>83, :channels=>2, :duration_in_ms=>5570, :total_bitrate_in_kbps=>551, :video_codec=>"h264", :width=>560, :height=>320, :video_bitrate_in_kbps=>465, :frame_rate=>30.0, :file_size_in_bytes=>383631, :format=>"mp4"}
|
33
|
+
assert_equal h, VideoConverter::Ffmpeg.new(:input => 'http://techslides.com/demos/sample-videos/small.mp4').metadata
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
Binary file
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#options: 640x360 fps=30000/1001 timebase=1001/30000 bitdepth=8 cabac=1 ref=1 deblock=1:0:0 analyse=0x1:0 me=dia subme=2 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=1 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=100 keyint_min=50 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=abr mbtree=1 bitrate=700 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
|
2
|
+
in:0 out:0 type:I dur:2 cpbdur:2 q:33.14 aq:18.00 tex:205 mv:380 misc:103 imb:920 pmb:0 smb:0 d:- ref:;
|
3
|
+
in:1 out:1 type:i dur:2 cpbdur:2 q:29.14 aq:20.24 tex:4049 mv:2267 misc:100 imb:920 pmb:0 smb:0 d:- ref:;
|
4
|
+
in:2 out:2 type:P dur:2 cpbdur:2 q:32.05 aq:23.37 tex:3246 mv:1941 misc:797 imb:298 pmb:157 smb:465 d:- ref:0 w:0,2,-17 ;
|
5
|
+
in:3 out:3 type:P dur:2 cpbdur:2 q:28.05 aq:21.34 tex:8333 mv:4310 misc:333 imb:754 pmb:135 smb:31 d:- ref:0 w:1,3,-8 ;
|
6
|
+
in:4 out:4 type:P dur:2 cpbdur:2 q:24.05 aq:18.13 tex:16844 mv:5507 misc:289 imb:634 pmb:261 smb:25 d:- ref:0 w:5,43,-5,6,83,-38,78,-28 ;
|
7
|
+
in:5 out:5 type:P dur:2 cpbdur:2 q:23.54 aq:18.12 tex:20991 mv:6179 misc:350 imb:594 pmb:301 smb:25 d:- ref:0 w:5,39,-3,6,76,-24,83,-38 ;
|
8
|
+
in:6 out:6 type:P dur:2 cpbdur:2 q:23.72 aq:18.61 tex:24730 mv:6631 misc:327 imb:566 pmb:338 smb:16 d:- ref:0 w:6,77,-3,6,75,-22,77,-26 ;
|
9
|
+
in:7 out:7 type:P dur:2 cpbdur:2 q:24.08 aq:19.67 tex:23056 mv:6587 misc:285 imb:563 pmb:350 smb:7 d:- ref:0 w:6,73,-2,6,71,-14,73,-18 ;
|
10
|
+
in:8 out:8 type:P dur:2 cpbdur:2 q:24.34 aq:19.98 tex:24310 mv:6658 misc:296 imb:501 pmb:403 smb:16 d:- ref:0 w:6,73,-3,6,75,-22,71,-14 ;
|
11
|
+
in:9 out:9 type:P dur:2 cpbdur:2 q:24.64 aq:20.83 tex:23461 mv:6741 misc:414 imb:436 pmb:442 smb:42 d:- ref:0 w:6,71,-2,6,71,-14,64,0 ;
|
12
|
+
in:10 out:10 type:P dur:2 cpbdur:2 q:24.90 aq:21.09 tex:26155 mv:6973 misc:352 imb:466 pmb:428 smb:26 d:- ref:0 w:5,35,-1,6,69,-10,64,0 ;
|
13
|
+
in:11 out:11 type:P dur:2 cpbdur:2 q:25.25 aq:21.57 tex:25134 mv:6943 misc:339 imb:457 pmb:443 smb:20 d:- ref:0 w:6,69,-1,6,68,-8,70,-12 ;
|
14
|
+
in:12 out:12 type:P dur:2 cpbdur:2 q:25.54 aq:21.90 tex:26632 mv:6876 misc:412 imb:420 pmb:460 smb:40 d:- ref:0 w:6,69,-1,6,69,-10,67,-6 ;
|
15
|
+
in:13 out:13 type:P dur:2 cpbdur:2 q:25.88 aq:22.43 tex:24703 mv:6714 misc:519 imb:411 pmb:437 smb:72 d:- ref:0 w:4,17,-1,6,67,-6,68,-8 ;
|
16
|
+
in:14 out:14 type:P dur:2 cpbdur:2 q:26.14 aq:22.84 tex:25446 mv:7196 misc:390 imb:447 pmb:437 smb:36 d:- ref:0 w:6,67,0,6,68,-8,67,-6 ;
|
17
|
+
in:15 out:15 type:P dur:2 cpbdur:2 q:26.42 aq:23.21 tex:26798 mv:7010 misc:464 imb:452 pmb:411 smb:57 d:- ref:0 w:4,17,-1,6,68,-8,69,-10 ;
|
18
|
+
in:16 out:16 type:P dur:2 cpbdur:2 q:26.73 aq:23.70 tex:24895 mv:6860 misc:421 imb:367 pmb:484 smb:69 d:- ref:0 w:6,67,-1 ;
|
19
|
+
in:17 out:17 type:P dur:2 cpbdur:2 q:26.97 aq:24.00 tex:25065 mv:6580 misc:603 imb:350 pmb:464 smb:106 d:- ref:0 w:6,67,-1,6,67,-6,67,-6 ;
|
20
|
+
in:18 out:18 type:P dur:2 cpbdur:2 q:27.21 aq:24.39 tex:24375 mv:7175 misc:434 imb:409 pmb:459 smb:52 d:- ref:0 w:6,67,-2,6,66,-4,67,-6 ;
|
21
|
+
in:19 out:19 type:P dur:2 cpbdur:2 q:27.44 aq:24.61 tex:26022 mv:6799 misc:499 imb:372 pmb:467 smb:81 d:- ref:0 w:5,33,0 ;
|
22
|
+
in:20 out:20 type:P dur:2 cpbdur:2 q:27.70 aq:25.06 tex:25445 mv:6872 misc:475 imb:355 pmb:482 smb:83 d:- ref:0 w:5,33,0 ;
|
23
|
+
in:21 out:21 type:P dur:2 cpbdur:2 q:27.93 aq:25.37 tex:24717 mv:6969 misc:554 imb:378 pmb:455 smb:87 d:- ref:0 w:5,33,-1,6,66,-4,66,-4 ;
|
24
|
+
in:22 out:22 type:P dur:2 cpbdur:2 q:28.15 aq:25.55 tex:26902 mv:6863 misc:451 imb:444 pmb:401 smb:75 d:- ref:0 ;
|
25
|
+
in:23 out:23 type:P dur:2 cpbdur:2 q:28.41 aq:25.79 tex:26636 mv:6915 misc:529 imb:404 pmb:410 smb:106 d:- ref:0 w:6,65,-1 ;
|
26
|
+
in:24 out:24 type:P dur:2 cpbdur:2 q:28.66 aq:26.24 tex:24210 mv:6889 misc:557 imb:399 pmb:420 smb:101 d:- ref:0 w:6,65,-1,6,66,-4,66,-4 ;
|
27
|
+
in:25 out:25 type:P dur:2 cpbdur:2 q:28.86 aq:26.29 tex:23847 mv:6719 misc:594 imb:353 pmb:430 smb:137 d:- ref:0 w:6,65,-1 ;
|
28
|
+
in:26 out:26 type:P dur:2 cpbdur:2 q:29.04 aq:26.51 tex:25242 mv:6973 misc:441 imb:406 pmb:427 smb:87 d:- ref:0 ;
|
29
|
+
in:27 out:27 type:P dur:2 cpbdur:2 q:29.26 aq:26.70 tex:25484 mv:7034 misc:506 imb:393 pmb:432 smb:95 d:- ref:0 ;
|
30
|
+
in:28 out:28 type:P dur:2 cpbdur:2 q:29.47 aq:27.10 tex:25042 mv:6797 misc:521 imb:366 pmb:437 smb:117 d:- ref:0 ;
|
31
|
+
in:29 out:29 type:P dur:2 cpbdur:2 q:29.67 aq:27.32 tex:23727 mv:6518 misc:635 imb:338 pmb:421 smb:161 d:- ref:0 ;
|
32
|
+
in:30 out:30 type:P dur:2 cpbdur:2 q:29.84 aq:27.35 tex:26996 mv:6966 misc:502 imb:360 pmb:448 smb:112 d:- ref:0 ;
|
33
|
+
in:31 out:31 type:P dur:2 cpbdur:2 q:30.06 aq:27.58 tex:22884 mv:6358 misc:686 imb:263 pmb:450 smb:207 d:- ref:0 ;
|
34
|
+
in:32 out:32 type:P dur:2 cpbdur:2 q:30.19 aq:27.64 tex:23387 mv:6580 misc:601 imb:297 pmb:459 smb:164 d:- ref:0 ;
|
35
|
+
in:33 out:33 type:P dur:2 cpbdur:2 q:30.33 aq:27.83 tex:22916 mv:6440 misc:636 imb:295 pmb:457 smb:168 d:- ref:0 ;
|
36
|
+
in:34 out:34 type:P dur:2 cpbdur:2 q:30.46 aq:27.82 tex:21683 mv:6188 misc:689 imb:236 pmb:439 smb:245 d:- ref:0 ;
|
37
|
+
in:35 out:35 type:P dur:2 cpbdur:2 q:30.56 aq:28.06 tex:21790 mv:6219 misc:703 imb:263 pmb:434 smb:223 d:- ref:0 ;
|
38
|
+
in:36 out:36 type:P dur:2 cpbdur:2 q:30.66 aq:28.08 tex:19031 mv:5826 misc:735 imb:213 pmb:443 smb:264 d:- ref:0 ;
|
39
|
+
in:37 out:37 type:P dur:2 cpbdur:2 q:30.71 aq:28.22 tex:21104 mv:6127 misc:697 imb:282 pmb:430 smb:208 d:- ref:0 ;
|
40
|
+
in:38 out:38 type:P dur:2 cpbdur:2 q:30.80 aq:27.93 tex:18956 mv:5969 misc:779 imb:161 pmb:460 smb:299 d:- ref:0 ;
|
41
|
+
in:39 out:39 type:P dur:2 cpbdur:2 q:30.85 aq:27.81 tex:20882 mv:5861 misc:745 imb:221 pmb:444 smb:255 d:- ref:0 ;
|
42
|
+
in:41 out:40 type:P dur:2 cpbdur:2 q:30.93 aq:28.07 tex:28752 mv:7223 misc:561 imb:364 pmb:446 smb:110 d:- ref:0 ;
|
43
|
+
in:40 out:41 type:b dur:2 cpbdur:2 q:30.89 aq:30.01 tex:7803 mv:4482 misc:707 imb:27 pmb:310 smb:583 d:- ref:0 ;
|
44
|
+
in:43 out:42 type:P dur:2 cpbdur:2 q:31.00 aq:28.08 tex:26935 mv:7148 misc:581 imb:303 pmb:469 smb:148 d:- ref:0 ;
|
45
|
+
in:42 out:43 type:b dur:2 cpbdur:2 q:30.96 aq:30.11 tex:7909 mv:4292 misc:679 imb:29 pmb:300 smb:591 d:- ref:0 ;
|
46
|
+
in:45 out:44 type:P dur:2 cpbdur:2 q:31.03 aq:27.98 tex:26739 mv:6872 misc:677 imb:281 pmb:462 smb:177 d:- ref:0 ;
|
47
|
+
in:44 out:45 type:b dur:2 cpbdur:2 q:31.01 aq:29.63 tex:6849 mv:4516 misc:667 imb:22 pmb:315 smb:583 d:- ref:0 ;
|
48
|
+
in:47 out:46 type:P dur:2 cpbdur:2 q:31.05 aq:27.76 tex:25936 mv:6521 misc:655 imb:239 pmb:458 smb:223 d:- ref:0 ;
|
49
|
+
in:46 out:47 type:b dur:2 cpbdur:2 q:31.04 aq:30.40 tex:7107 mv:4175 misc:662 imb:19 pmb:300 smb:601 d:- ref:0 ;
|
50
|
+
in:49 out:48 type:P dur:2 cpbdur:2 q:31.05 aq:27.87 tex:27152 mv:6383 misc:665 imb:269 pmb:432 smb:219 d:- ref:0 ;
|
51
|
+
in:48 out:49 type:b dur:2 cpbdur:2 q:31.05 aq:31.23 tex:6677 mv:4091 misc:704 imb:22 pmb:301 smb:597 d:- ref:0 ;
|
52
|
+
in:51 out:50 type:P dur:2 cpbdur:2 q:31.06 aq:27.83 tex:23699 mv:6311 misc:654 imb:197 pmb:445 smb:278 d:- ref:0 ;
|
53
|
+
in:50 out:51 type:b dur:2 cpbdur:2 q:31.06 aq:31.44 tex:7277 mv:4334 misc:653 imb:26 pmb:316 smb:578 d:- ref:0 ;
|
54
|
+
in:53 out:52 type:P dur:2 cpbdur:2 q:31.04 aq:27.97 tex:25826 mv:6713 misc:573 imb:277 pmb:435 smb:208 d:- ref:0 ;
|
55
|
+
in:52 out:53 type:b dur:2 cpbdur:2 q:31.05 aq:30.39 tex:7357 mv:4487 misc:676 imb:26 pmb:322 smb:572 d:- ref:0 ;
|
56
|
+
in:54 out:54 type:P dur:2 cpbdur:2 q:31.04 aq:28.09 tex:22368 mv:5575 misc:705 imb:151 pmb:473 smb:296 d:- ref:0 ;
|
57
|
+
in:55 out:55 type:P dur:2 cpbdur:2 q:31.11 aq:28.43 tex:22624 mv:5356 misc:676 imb:130 pmb:475 smb:315 d:- ref:0 ;
|
58
|
+
in:56 out:56 type:P dur:2 cpbdur:2 q:31.18 aq:28.68 tex:22984 mv:5376 misc:752 imb:96 pmb:513 smb:311 d:- ref:0 ;
|
59
|
+
in:57 out:57 type:P dur:2 cpbdur:2 q:31.25 aq:28.62 tex:24476 mv:5388 misc:648 imb:112 pmb:515 smb:293 d:- ref:0 ;
|
60
|
+
in:58 out:58 type:P dur:2 cpbdur:2 q:31.34 aq:28.63 tex:23638 mv:5248 misc:658 imb:117 pmb:507 smb:296 d:- ref:0 ;
|
61
|
+
in:59 out:59 type:P dur:2 cpbdur:2 q:31.42 aq:28.96 tex:22401 mv:5055 misc:688 imb:112 pmb:483 smb:325 d:- ref:0 ;
|
62
|
+
in:60 out:60 type:P dur:2 cpbdur:2 q:31.48 aq:28.46 tex:42376 mv:7096 misc:336 imb:345 pmb:543 smb:32 d:- ref:0 w:6,65,-3 ;
|
63
|
+
in:61 out:61 type:P dur:2 cpbdur:2 q:31.76 aq:28.94 tex:17183 mv:4556 misc:717 imb:87 pmb:452 smb:381 d:- ref:0 ;
|
64
|
+
in:62 out:62 type:P dur:2 cpbdur:2 q:31.76 aq:29.07 tex:23387 mv:5228 misc:681 imb:97 pmb:518 smb:305 d:- ref:0 ;
|
65
|
+
in:63 out:63 type:P dur:2 cpbdur:2 q:31.84 aq:29.21 tex:26071 mv:5377 misc:648 imb:126 pmb:501 smb:293 d:- ref:0 ;
|
66
|
+
in:64 out:64 type:P dur:2 cpbdur:2 q:31.93 aq:29.29 tex:25836 mv:5059 misc:729 imb:112 pmb:493 smb:315 d:- ref:0 ;
|
67
|
+
in:65 out:65 type:P dur:2 cpbdur:2 q:32.03 aq:28.99 tex:26955 mv:5356 misc:689 imb:129 pmb:514 smb:277 d:- ref:0 ;
|
68
|
+
in:66 out:66 type:P dur:2 cpbdur:2 q:32.13 aq:29.10 tex:25827 mv:5203 misc:738 imb:126 pmb:512 smb:282 d:- ref:0 ;
|
69
|
+
in:67 out:67 type:P dur:2 cpbdur:2 q:32.23 aq:29.30 tex:24530 mv:5245 misc:673 imb:122 pmb:520 smb:278 d:- ref:0 ;
|
70
|
+
in:68 out:68 type:P dur:2 cpbdur:2 q:32.30 aq:29.41 tex:24731 mv:4905 misc:724 imb:111 pmb:506 smb:303 d:- ref:0 ;
|
71
|
+
in:69 out:69 type:P dur:2 cpbdur:2 q:32.38 aq:29.56 tex:25374 mv:4770 misc:720 imb:116 pmb:494 smb:310 d:- ref:0 ;
|
72
|
+
in:70 out:70 type:P dur:2 cpbdur:2 q:32.46 aq:29.69 tex:22297 mv:4759 misc:712 imb:128 pmb:491 smb:301 d:- ref:0 ;
|
73
|
+
in:71 out:71 type:P dur:2 cpbdur:2 q:32.51 aq:29.74 tex:16703 mv:4591 misc:754 imb:99 pmb:461 smb:360 d:- ref:0 ;
|
74
|
+
in:72 out:72 type:P dur:2 cpbdur:2 q:32.51 aq:29.20 tex:17551 mv:4877 misc:732 imb:134 pmb:449 smb:337 d:- ref:0 ;
|
75
|
+
in:73 out:73 type:P dur:2 cpbdur:2 q:32.52 aq:29.52 tex:16919 mv:4693 misc:748 imb:112 pmb:452 smb:356 d:- ref:0 ;
|
76
|
+
in:75 out:74 type:P dur:2 cpbdur:2 q:32.52 aq:29.28 tex:25128 mv:5752 misc:672 imb:169 pmb:487 smb:264 d:- ref:0 ;
|
77
|
+
in:74 out:75 type:b dur:2 cpbdur:2 q:32.52 aq:32.38 tex:5715 mv:3413 misc:608 imb:27 pmb:244 smb:649 d:- ref:0 ;
|
78
|
+
in:76 out:76 type:P dur:2 cpbdur:2 q:32.49 aq:29.78 tex:15885 mv:4424 misc:723 imb:104 pmb:451 smb:365 d:- ref:0 ;
|
79
|
+
in:77 out:77 type:P dur:2 cpbdur:2 q:32.48 aq:29.42 tex:16511 mv:4423 misc:810 imb:103 pmb:438 smb:379 d:- ref:0 ;
|
80
|
+
in:78 out:78 type:P dur:2 cpbdur:2 q:32.47 aq:29.58 tex:15842 mv:4754 misc:764 imb:113 pmb:457 smb:350 d:- ref:0 ;
|
81
|
+
in:79 out:79 type:P dur:2 cpbdur:2 q:32.47 aq:29.76 tex:17153 mv:4646 misc:753 imb:116 pmb:454 smb:350 d:- ref:0 ;
|
82
|
+
in:80 out:80 type:P dur:2 cpbdur:2 q:32.47 aq:29.62 tex:17509 mv:4765 misc:758 imb:122 pmb:457 smb:341 d:- ref:0 ;
|
83
|
+
in:81 out:81 type:P dur:2 cpbdur:2 q:32.47 aq:30.20 tex:16122 mv:4382 misc:808 imb:112 pmb:407 smb:401 d:- ref:0 ;
|
84
|
+
in:82 out:82 type:P dur:2 cpbdur:2 q:32.46 aq:30.02 tex:15440 mv:4399 misc:785 imb:102 pmb:409 smb:409 d:- ref:0 ;
|
85
|
+
in:83 out:83 type:P dur:2 cpbdur:2 q:32.45 aq:30.46 tex:15462 mv:4532 misc:790 imb:86 pmb:434 smb:400 d:- ref:0 ;
|
86
|
+
in:85 out:84 type:P dur:2 cpbdur:2 q:32.44 aq:30.16 tex:24122 mv:6105 misc:637 imb:319 pmb:456 smb:145 d:- ref:0 w:5,31,0,6,64,0,62,4 ;
|
87
|
+
in:84 out:85 type:b dur:2 cpbdur:2 q:32.44 aq:31.32 tex:6184 mv:4225 misc:711 imb:50 pmb:282 smb:588 d:- ref:0 ;
|
88
|
+
in:87 out:86 type:P dur:2 cpbdur:2 q:32.41 aq:30.25 tex:23201 mv:5815 misc:744 imb:246 pmb:468 smb:206 d:- ref:0 w:7,121,1,6,61,6,62,4 ;
|
89
|
+
in:86 out:87 type:b dur:2 cpbdur:2 q:32.42 aq:31.31 tex:5353 mv:3660 misc:747 imb:41 pmb:264 smb:615 d:- ref:0 ;
|
90
|
+
in:89 out:88 type:P dur:2 cpbdur:2 q:32.37 aq:29.96 tex:23360 mv:5700 misc:700 imb:264 pmb:455 smb:201 d:- ref:0 w:7,121,1,6,61,6,61,6 ;
|
91
|
+
in:88 out:89 type:b dur:2 cpbdur:2 q:32.39 aq:31.44 tex:5700 mv:3560 misc:772 imb:50 pmb:276 smb:594 d:- ref:0 ;
|
92
|
+
in:91 out:90 type:P dur:2 cpbdur:2 q:32.33 aq:29.77 tex:20051 mv:5795 misc:722 imb:264 pmb:445 smb:211 d:- ref:0 w:7,121,1,6,60,8,61,6 ;
|
93
|
+
in:90 out:91 type:b dur:2 cpbdur:2 q:32.35 aq:31.03 tex:5095 mv:3481 misc:824 imb:42 pmb:306 smb:572 d:- ref:0 ;
|
94
|
+
in:93 out:92 type:P dur:2 cpbdur:2 q:32.26 aq:29.64 tex:19332 mv:5664 misc:812 imb:251 pmb:430 smb:239 d:- ref:0 w:4,15,1,6,60,8,60,8 ;
|
95
|
+
in:92 out:93 type:b dur:2 cpbdur:2 q:32.29 aq:30.59 tex:4641 mv:3684 misc:731 imb:46 pmb:261 smb:613 d:- ref:0 ;
|
96
|
+
in:95 out:94 type:P dur:2 cpbdur:2 q:32.18 aq:29.45 tex:20130 mv:5813 misc:721 imb:310 pmb:436 smb:174 d:- ref:0 w:7,121,0,6,60,8,61,6 ;
|
97
|
+
in:94 out:95 type:b dur:2 cpbdur:2 q:32.22 aq:30.77 tex:4876 mv:3497 misc:707 imb:40 pmb:259 smb:621 d:- ref:0 ;
|
98
|
+
in:97 out:96 type:P dur:2 cpbdur:2 q:32.11 aq:29.43 tex:15411 mv:5139 misc:850 imb:243 pmb:389 smb:288 d:- ref:0 w:7,119,1,6,59,10,59,10 ;
|
99
|
+
in:96 out:97 type:b dur:2 cpbdur:2 q:32.15 aq:29.82 tex:4459 mv:3598 misc:711 imb:61 pmb:243 smb:616 d:- ref:0 ;
|
100
|
+
in:98 out:98 type:P dur:2 cpbdur:2 q:32.00 aq:28.84 tex:12074 mv:4437 misc:809 imb:216 pmb:361 smb:343 d:- ref:0 w:7,123,1 ;
|
101
|
+
in:99 out:99 type:P dur:2 cpbdur:2 q:31.96 aq:29.85 tex:7668 mv:3892 misc:888 imb:178 pmb:279 smb:463 d:- ref:0 w:7,123,1,6,61,6,64,0 ;
|
102
|
+
in:100 out:100 type:I dur:2 cpbdur:2 q:29.14 aq:26.06 tex:49418 mv:15983 misc:119 imb:920 pmb:0 smb:0 d:- ref:w:7,123,1,6,61,6,64,0 ;
|
103
|
+
in:102 out:101 type:P dur:2 cpbdur:2 q:32.12 aq:28.94 tex:11064 mv:4379 misc:837 imb:199 pmb:313 smb:408 d:- ref:0 w:6,59,1,6,59,10,60,8 ;
|
104
|
+
in:101 out:102 type:b dur:2 cpbdur:2 q:32.12 aq:28.93 tex:3230 mv:2835 misc:751 imb:48 pmb:216 smb:656 d:- ref:0 ;
|
105
|
+
in:103 out:103 type:P dur:2 cpbdur:2 q:31.96 aq:28.30 tex:8788 mv:3945 misc:907 imb:184 pmb:314 smb:422 d:- ref:0 w:6,61,1,6,60,8,60,8 ;
|
106
|
+
in:104 out:104 type:P dur:2 cpbdur:2 q:31.90 aq:27.99 tex:8506 mv:4178 misc:876 imb:200 pmb:324 smb:396 d:- ref:0 w:6,61,1,6,60,8,64,0 ;
|
107
|
+
in:105 out:105 type:P dur:2 cpbdur:2 q:31.83 aq:28.29 tex:9136 mv:4302 misc:898 imb:241 pmb:325 smb:354 d:- ref:0 w:7,119,2,6,64,0,59,10 ;
|
108
|
+
in:107 out:106 type:P dur:2 cpbdur:2 q:31.78 aq:27.99 tex:11777 mv:5042 misc:845 imb:265 pmb:381 smb:274 d:- ref:0 w:3,7,3,6,58,12,58,12 ;
|
109
|
+
in:106 out:107 type:b dur:2 cpbdur:2 q:31.81 aq:28.13 tex:3444 mv:2414 misc:798 imb:53 pmb:216 smb:651 d:- ref:0 ;
|
110
|
+
in:108 out:108 type:P dur:2 cpbdur:2 q:31.63 aq:27.64 tex:8423 mv:3728 misc:953 imb:180 pmb:344 smb:396 d:- ref:0 w:7,119,2,6,64,0,60,8 ;
|
111
|
+
in:109 out:109 type:P dur:2 cpbdur:2 q:31.57 aq:27.42 tex:9777 mv:4228 misc:939 imb:222 pmb:362 smb:336 d:- ref:0 w:7,117,2,6,60,8,64,0 ;
|
112
|
+
in:110 out:110 type:P dur:2 cpbdur:2 q:31.52 aq:27.28 tex:9009 mv:4443 misc:908 imb:255 pmb:348 smb:317 d:- ref:0 w:7,117,3,6,60,8,59,10 ;
|
113
|
+
in:111 out:111 type:P dur:2 cpbdur:2 q:31.46 aq:27.05 tex:7301 mv:3981 misc:918 imb:218 pmb:326 smb:376 d:- ref:0 w:6,59,2,6,60,8,64,0 ;
|
114
|
+
in:112 out:112 type:P dur:2 cpbdur:2 q:31.39 aq:26.82 tex:8446 mv:4488 misc:938 imb:279 pmb:323 smb:318 d:- ref:0 w:5,29,3,6,64,0,58,12 ;
|
115
|
+
in:113 out:113 type:P dur:2 cpbdur:2 q:31.33 aq:27.00 tex:8078 mv:4181 misc:917 imb:267 pmb:313 smb:340 d:- ref:0 w:7,115,2,6,58,12,58,12 ;
|
116
|
+
in:114 out:114 type:P dur:2 cpbdur:2 q:31.27 aq:26.55 tex:7409 mv:4054 misc:969 imb:294 pmb:262 smb:364 d:- ref:0 w:6,57,3,6,59,10,64,0 ;
|
117
|
+
in:115 out:115 type:P dur:2 cpbdur:2 q:31.20 aq:26.08 tex:7408 mv:4489 misc:879 imb:334 pmb:313 smb:273 d:- ref:0 w:7,113,2,6,64,0,58,12 ;
|
118
|
+
in:116 out:116 type:P dur:2 cpbdur:2 q:31.13 aq:24.99 tex:6417 mv:4080 misc:919 imb:378 pmb:208 smb:334 d:- ref:0 w:3,7,3,6,56,16,55,18 ;
|
119
|
+
in:117 out:117 type:P dur:2 cpbdur:2 q:31.06 aq:24.44 tex:5770 mv:3770 misc:988 imb:329 pmb:203 smb:388 d:- ref:0 w:3,7,2,6,57,14,57,14 ;
|
120
|
+
in:118 out:118 type:P dur:2 cpbdur:2 q:30.98 aq:24.21 tex:4146 mv:3085 misc:969 imb:305 pmb:169 smb:446 d:- ref:0 w:5,27,3,6,64,0,55,18 ;
|
121
|
+
in:119 out:119 type:P dur:2 cpbdur:2 q:30.89 aq:23.18 tex:4318 mv:3051 misc:943 imb:355 pmb:140 smb:425 d:- ref:0 w:6,55,2,6,50,28,50,28 ;
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class VideoConverterTest < Test::Unit::TestCase
|
4
|
+
context 'run' do
|
5
|
+
setup do
|
6
|
+
@input = 'test/fixtures/test.mp4'
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'with type mp4' do
|
10
|
+
setup do
|
11
|
+
@profiles = []
|
12
|
+
@profiles << (@p11 = VideoConverter::Profile.new(:bitrate => 300, :file => 'tmp/test11.mp4'))
|
13
|
+
@profiles << (@p12 = VideoConverter::Profile.new(:bitrate => 400, :file => 'tmp/test12.mp4'))
|
14
|
+
@profiles << (@p21 = VideoConverter::Profile.new(:bitrate => 700, :file => 'tmp/test21.mp4'))
|
15
|
+
@profiles << (@p22 = VideoConverter::Profile.new(:bitrate => 700, :file => 'tmp/test22.mp4'))
|
16
|
+
@c = VideoConverter.new(:input => @input, :profile => [[@p11, @p12], [@p21, @p22]], :verbose => false, :log => 'tmp/test.log', :type => :mp4)
|
17
|
+
@res = @c.run
|
18
|
+
end
|
19
|
+
should 'convert files' do
|
20
|
+
4.times do |n|
|
21
|
+
file = "tmp/test#{n / 2 + 1}#{n.even? ? 1 : 2}.mp4"
|
22
|
+
assert File.exists?(file)
|
23
|
+
assert File.size(file) > 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
should 'return success convert process' do
|
27
|
+
assert VideoConverter.find(@c.id)
|
28
|
+
assert @res
|
29
|
+
assert_equal 'convert_success', VideoConverter.find(@c.id).status
|
30
|
+
end
|
31
|
+
should 'write log file' do
|
32
|
+
assert File.exists?('tmp/test.log')
|
33
|
+
assert !File.read('tmp/test.log').empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with type hls' do
|
38
|
+
setup do
|
39
|
+
@profiles = []
|
40
|
+
@profiles << (@p11 = VideoConverter::Profile.new(:bitrate => 300, :dir => 'tmp/test11'))
|
41
|
+
@profiles << (@p12 = VideoConverter::Profile.new(:bitrate => 400, :dir => 'tmp/test12'))
|
42
|
+
@profiles << (@p21 = VideoConverter::Profile.new(:bitrate => 700, :dir => 'tmp/test21'))
|
43
|
+
@profiles << (@p22 = VideoConverter::Profile.new(:bitrate => 700, :dir => 'tmp/test22'))
|
44
|
+
end
|
45
|
+
context '' do
|
46
|
+
setup do
|
47
|
+
@c = VideoConverter.new(:input => @input, :profile => [[@p11, @p12], [@p21, @p22]], :verbose => false, :log => 'tmp/test.log', :type => :hls, :playlist_dir => 'tmp')
|
48
|
+
@res = @c.run
|
49
|
+
end
|
50
|
+
should 'create chunks' do
|
51
|
+
@profiles.each do |profile|
|
52
|
+
assert File.exists?(File.join(profile.to_hash[:dir], 's-00001.ts'))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
should 'create quality playlists' do
|
56
|
+
@profiles.each do |profile|
|
57
|
+
assert File.exists?(File.join(File.dirname(profile.to_hash[:dir]), File.basename(profile.to_hash[:dir]) + '.m3u8'))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
should 'create group playlists' do
|
61
|
+
playlist1 = File.join('tmp', 'playlist1.m3u8')
|
62
|
+
playlist2 = File.join('tmp', 'playlist2.m3u8')
|
63
|
+
assert File.exists? playlist1
|
64
|
+
assert File.exists? playlist2
|
65
|
+
assert File.read(playlist1).include?('test11')
|
66
|
+
assert File.read(playlist1).include?('test12')
|
67
|
+
assert !File.read(playlist1).include?('test21')
|
68
|
+
assert !File.read(playlist1).include?('test22')
|
69
|
+
assert File.read(playlist2).include?('test21')
|
70
|
+
assert File.read(playlist2).include?('test22')
|
71
|
+
assert !File.read(playlist2).include?('test11')
|
72
|
+
assert !File.read(playlist2).include?('test12')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with no_convert flag' do
|
77
|
+
setup do
|
78
|
+
@c = VideoConverter.new(:profile => VideoConverter::Profile.new(:file => 'test/fixtures/test.mp4', :dir => '/tmp/test', :bitrate => 300), :no_convert => true, :type => :hls, :playlist_dir => 'tmp')
|
79
|
+
@res = @c.run
|
80
|
+
end
|
81
|
+
should 'return true' do
|
82
|
+
assert @res
|
83
|
+
end
|
84
|
+
should 'create needed files' do
|
85
|
+
assert File.exists? '/tmp/test/s-00001.ts'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'video_converter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "video_converter"
|
8
|
+
spec.version = VideoConverter::VERSION
|
9
|
+
spec.authors = ["novikov"]
|
10
|
+
spec.email = ["novikov@inventos.ru"]
|
11
|
+
spec.description = %q{This gem allows you to convert video files using ffmpeg, mencoder and other user defined utilities to mp4, m3u8, and any other formats}
|
12
|
+
spec.summary = %q{Ffmpeg, mencoder based converter to mp4, m3u8}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "test-unit"
|
24
|
+
spec.add_development_dependency "shoulda-context"
|
25
|
+
|
26
|
+
spec.requirements << 'ffmpeg, version 1.2 or greated configured with libx264 and libfaac'
|
27
|
+
spec.requirements << 'live_segmenter to convert to hls'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: video_converter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- novikov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: test-unit
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: shoulda-context
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: This gem allows you to convert video files using ffmpeg, mencoder and
|
79
|
+
other user defined utilities to mp4, m3u8, and any other formats
|
80
|
+
email:
|
81
|
+
- novikov@inventos.ru
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .Rakefile.swp
|
87
|
+
- .gitignore
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE.txt
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- lib/video_converter.rb
|
93
|
+
- lib/video_converter/base.rb
|
94
|
+
- lib/video_converter/command.rb
|
95
|
+
- lib/video_converter/ffmpeg.rb
|
96
|
+
- lib/video_converter/live_segmenter.rb
|
97
|
+
- lib/video_converter/process.rb
|
98
|
+
- lib/video_converter/profile.rb
|
99
|
+
- lib/video_converter/version.rb
|
100
|
+
- test/ffmpeg_test.rb
|
101
|
+
- test/fixtures/test.mp4
|
102
|
+
- test/fixtures/test.mp4.log-0.log
|
103
|
+
- test/fixtures/test.mp4.log-0.log.mbtree
|
104
|
+
- test/test_helper.rb
|
105
|
+
- test/video_converter_test.rb
|
106
|
+
- video_converter.gemspec
|
107
|
+
homepage: ''
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
hash: -2129030905366019713
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
hash: -2129030905366019713
|
132
|
+
requirements:
|
133
|
+
- ffmpeg, version 1.2 or greated configured with libx264 and libfaac
|
134
|
+
- live_segmenter to convert to hls
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.8.25
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Ffmpeg, mencoder based converter to mp4, m3u8
|
140
|
+
test_files:
|
141
|
+
- test/ffmpeg_test.rb
|
142
|
+
- test/fixtures/test.mp4
|
143
|
+
- test/fixtures/test.mp4.log-0.log
|
144
|
+
- test/fixtures/test.mp4.log-0.log.mbtree
|
145
|
+
- test/test_helper.rb
|
146
|
+
- test/video_converter_test.rb
|