video_converter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|