video_converter 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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