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 ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in video_converter.gemspec
4
+ gemspec
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
@@ -0,0 +1,4 @@
1
+ video_converter
2
+ ===============
3
+
4
+ ffmpeg, mencoder video converter to mp4, m3u8
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/*_test.rb']
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
@@ -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,3 @@
1
+ module VideoConverter
2
+ VERSION = "0.1.0"
3
+ 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
@@ -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 ;
@@ -0,0 +1,5 @@
1
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require 'test/unit'
4
+ require 'shoulda-context'
5
+ require 'video_converter'
@@ -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