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
 
@@ -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
- chunks.each do |chunk|
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
- `#{ffmpeg_bin} -i #{concat_file} -vcodec copy -acodec copy -f mpegts pipe:1 2>>/dev/null | #{ffmpeg_bin} -y -i - -acodec copy -vcodec copy -absf aac_adtstoasc -f mp4 #{output} 1>>#{log} 2>&1`
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$/).to_a).any?
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
- Dir::glob(File.join(output.chunks_dir, "#{chunk_prefix}-*[0-9].ts")).sort { |c1, c2| File.basename(c1).match(/\d+/).to_s.to_i <=> File.basename(c2).match(/\d+/).to_s.to_i }.each do |chunk|
57
- durations << (duration = chunk_duration chunk)
58
- res += "#EXTINF:%0.2f,\n" % duration
59
- res += File.join(File.basename(output.chunks_dir), File.basename(chunk)) + "\n"
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
@@ -1,3 +1,3 @@
1
1
  module VideoConverter
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: video_converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
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: 2013-08-29 00:00:00.000000000 Z
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: -805925364969050678
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: -805925364969050678
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