video_converter 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
@@ -13,9 +13,9 @@ module VideoConverter
|
|
13
13
|
|
14
14
|
self.one_pass_command = "%{bin} -i %{input} -y -acodec copy -vcodec %{video_codec} -g 100 -keyint_min 50 -b:v %{video_bitrate}k -bt %{video_bitrate}k %{vf} %{frame_rate} -progress %{progressfile} -f mp4 %{local_path} 1>%{log} 2>&1 || exit 1"
|
15
15
|
|
16
|
-
self.first_pass_command = "%{bin} -i %{input} -y -an -vcodec %{video_codec} -g %{keyframe_interval} -keyint_min 25 -pass 1 -passlogfile %{passlogfile} -progress %{progressfile} -b:v 3000k %{vf} %{frame_rate} -threads %{threads} -f mp4 /dev/null 1>>%{log} 2>&1 || exit 1"
|
16
|
+
self.first_pass_command = "%{bin} -i %{input} -y -an -vcodec %{video_codec} -g %{keyframe_interval} -keyint_min 25 -pass 1 -passlogfile %{passlogfile} -progress %{progressfile} -b:v 3000k %{vf} %{frame_rate} -threads %{threads} -pix_fmt yuv420p -f mp4 /dev/null 1>>%{log} 2>&1 || exit 1"
|
17
17
|
|
18
|
-
self.second_pass_command = "%{bin} -i %{input} -y -pass 2 -passlogfile %{passlogfile} -progress %{progressfile} -c:a %{audio_codec} -b:a %{audio_bitrate}k -c:v %{video_codec} -g %{keyframe_interval} -keyint_min 25 %{frame_rate} -b:v %{video_bitrate}k %{vf} -threads %{threads} -f mp4 %{local_path} 1>%{log} 2>&1 || exit 1"
|
18
|
+
self.second_pass_command = "%{bin} -i %{input} -y -pass 2 -passlogfile %{passlogfile} -progress %{progressfile} -c:a %{audio_codec} -b:a %{audio_bitrate}k -ac 2 -c:v %{video_codec} -g %{keyframe_interval} -keyint_min 25 %{frame_rate} -b:v %{video_bitrate}k %{vf} -threads %{threads} -pix_fmt yuv420p -f mp4 %{local_path} 1>%{log} 2>&1 || exit 1"
|
19
19
|
|
20
20
|
attr_accessor :input_array, :output_array, :one_pass, :paral, :log, :process
|
21
21
|
|
data/lib/video_converter/hls.rb
CHANGED
@@ -24,7 +24,8 @@ module VideoConverter
|
|
24
24
|
`mkdir -p #{output_dir}` unless Dir.exists?(output_dir)
|
25
25
|
concat_file = File.join(output_dir, 'concat.ts')
|
26
26
|
`rm #{concat_file}` if File.exists? concat_file
|
27
|
-
|
27
|
+
need_reconvert = false
|
28
|
+
chunks.each_with_index do |chunk, chunk_index|
|
28
29
|
local_chunk = if chunks_dir
|
29
30
|
File.join(chunks_dir, chunk)
|
30
31
|
elsif replace_in_chunk
|
@@ -36,6 +37,9 @@ module VideoConverter
|
|
36
37
|
yield message if block_given?
|
37
38
|
`cat #{local_chunk} >> #{concat_file}`
|
38
39
|
else
|
40
|
+
# NOTE because of troubles with timestamps
|
41
|
+
need_reconvert = true unless [0,chunks.count-1].include?(chunk_index)
|
42
|
+
|
39
43
|
chunk = File.join(File.dirname(input), chunk) unless chunk.match(/(^https?:\/\/)|(^\/)/)
|
40
44
|
message = "Download #{chunk} to #{concat_file}"
|
41
45
|
puts message if verbose
|
@@ -45,7 +49,10 @@ module VideoConverter
|
|
45
49
|
end
|
46
50
|
raise "Cannot download chunks from #{input}" unless File.exists?(concat_file) && File.size(concat_file) > 0
|
47
51
|
puts "Convert #{concat_file}" if verbose
|
48
|
-
|
52
|
+
cmd = "#{ffmpeg_bin} -i #{concat_file} -vcodec copy -acodec copy -f mpegts pipe:1 2>>/dev/null | #{ffmpeg_bin} -y -i - -acodec copy -vcodec #{need_reconvert ? 'libx264' : 'copy'} -absf aac_adtstoasc -f mp4 #{output} 1>>#{log} 2>&1"
|
53
|
+
puts cmd if verbose
|
54
|
+
yield cmd if block_given?
|
55
|
+
`#{cmd}`
|
49
56
|
`rm #{concat_file}`
|
50
57
|
output
|
51
58
|
end
|
@@ -38,7 +38,7 @@ module VideoConverter
|
|
38
38
|
def metadata
|
39
39
|
metadata = {}
|
40
40
|
s = `#{Command.new self.class.metadata_command, common_params}`.encode!('UTF-8', 'UTF-8', :invalid => :replace)
|
41
|
-
if (m = s.match(/Stream.*?Audio:\s*(\w+).*?(\d+)\s*Hz.*?(\d+)\s*kb\/s
|
41
|
+
if (m = s.match(/Stream.*?Audio:\s*(\w+).*?(\d+)\s*Hz.*?(\d+)\s*kb\/s.*?$/).to_a).any?
|
42
42
|
metadata[:audio_codec] = m[1]
|
43
43
|
metadata[:audio_sample_rate] = m[2].to_i
|
44
44
|
metadata[:audio_bitrate_in_kbps] = m[3].to_i
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module VideoConverter
|
4
4
|
class LiveSegmenter
|
5
5
|
class << self
|
6
|
-
attr_accessor :bin, :ffprobe_bin, :chunks_command, :chunk_prefix, :encoding_profile, :log, :paral
|
6
|
+
attr_accessor :bin, :ffprobe_bin, :chunks_command, :chunk_prefix, :encoding_profile, :log, :paral, :select_streams
|
7
7
|
end
|
8
8
|
|
9
9
|
self.bin = '/usr/local/bin/live_segmenter'
|
@@ -12,6 +12,7 @@ module VideoConverter
|
|
12
12
|
self.encoding_profile = 's'
|
13
13
|
self.log = '/dev/null'
|
14
14
|
self.paral = true
|
15
|
+
self.select_streams = 'v'
|
15
16
|
|
16
17
|
self.chunks_command = '%{ffmpeg_bin} -f mp4 -i %{local_path} -vcodec copy -acodec copy -f mpegts -bsf h264_mp4toannexb pipe:1 2>>/dev/null | %{bin} %{segment_seconds} %{chunks_dir} %{chunk_prefix} %{encoding_profile} 1>>%{log} 2>&1'
|
17
18
|
|
@@ -53,10 +54,20 @@ module VideoConverter
|
|
53
54
|
def gen_quality_playlist output
|
54
55
|
res = ''
|
55
56
|
durations = []
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
# order desc
|
58
|
+
chunks = Dir::glob(File.join(output.chunks_dir, "#{chunk_prefix}-*[0-9].ts")).sort do |c1, c2|
|
59
|
+
File.basename(c2).match(/\d+/).to_s.to_i <=> File.basename(c1).match(/\d+/).to_s.to_i
|
60
|
+
end
|
61
|
+
# chunk duration = (pts of first frame of the next chunk - pts of first frame of current chunk) / time_base
|
62
|
+
# for the last chunks the last two pts are used
|
63
|
+
prl_pts, l_pts = `#{self.class.ffprobe_bin} -show_frames -select_streams #{self.class.select_streams} -print_format csv -loglevel fatal #{chunks.first} | tail -n2 2>&1`.split("\n").map { |l| l.split(',')[3].to_i }
|
64
|
+
next_chunk_pts = 2 * l_pts - prl_pts
|
65
|
+
chunks.each do |chunk|
|
66
|
+
durations << (duration = (next_chunk_pts - (next_chunk_pts =
|
67
|
+
`#{self.class.ffprobe_bin} -show_frames -select_streams #{self.class.select_streams} -print_format csv -loglevel fatal #{chunk} | head -n1 2>&1`.split(',')[3].to_i
|
68
|
+
)) / `#{self.class.ffprobe_bin} -show_streams -select_streams #{self.class.select_streams} -loglevel fatal #{chunk} 2>&1`.match(/\ntime_base=1\/(\d+)/)[1].to_f)
|
69
|
+
res = File.join(File.basename(output.chunks_dir), File.basename(chunk)) + "\n" + res
|
70
|
+
res = "#EXTINF:%0.2f,\n" % duration + res
|
60
71
|
end
|
61
72
|
res = "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:#{durations.max}\n#EXT-X-MEDIA-SEQUENCE:0\n" + res + "#EXT-X-ENDLIST"
|
62
73
|
File.open(File.join(output.work_dir, output.filename), 'w') { |f| f.write res }
|
@@ -76,9 +87,5 @@ module VideoConverter
|
|
76
87
|
def common_params
|
77
88
|
{ :ffmpeg_bin => Ffmpeg.bin, :bin => self.class.bin, :log => log, :chunk_prefix => chunk_prefix, :encoding_profile => encoding_profile }
|
78
89
|
end
|
79
|
-
|
80
|
-
def chunk_duration chunk
|
81
|
-
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
|
82
|
-
end
|
83
90
|
end
|
84
91
|
end
|
@@ -11,7 +11,7 @@ module VideoConverter
|
|
11
11
|
self.collect_progress_interval = 10
|
12
12
|
|
13
13
|
def self.find uid
|
14
|
-
if Dir.exists?(File.join(process_dir, uid))
|
14
|
+
if Dir.exists?(File.join(process_dir, uid.to_s))
|
15
15
|
new uid
|
16
16
|
else
|
17
17
|
nil
|
@@ -19,7 +19,7 @@ module VideoConverter
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize uid, output_array = nil
|
22
|
-
self.uid = uid
|
22
|
+
self.uid = uid.to_s
|
23
23
|
self.process_dir = File.join(self.class.process_dir, uid)
|
24
24
|
|
25
25
|
unless Dir.exists? process_dir
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: video_converter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-03-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: video_screenshoter
|
@@ -137,7 +137,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
137
|
version: '0'
|
138
138
|
segments:
|
139
139
|
- 0
|
140
|
-
hash:
|
140
|
+
hash: 3814024992440192523
|
141
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
142
|
none: false
|
143
143
|
requirements:
|
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
146
|
version: '0'
|
147
147
|
segments:
|
148
148
|
- 0
|
149
|
-
hash:
|
149
|
+
hash: 3814024992440192523
|
150
150
|
requirements:
|
151
151
|
- ffmpeg, version 1.2 or greated configured with libx264 and libfaac
|
152
152
|
- live_segmenter to convert to hls
|