video_converter 0.5.0 → 0.6.0

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.
@@ -16,14 +16,41 @@ module VideoConverter
16
16
  end
17
17
 
18
18
  def run
19
+ success = convert && faststart && make_screenshots && segment
20
+ clear if clear_tmp && success
21
+ success
22
+ end
23
+
24
+ def convert
19
25
  success = true
20
26
  inputs.each do |input|
21
27
  input.output_groups.each do |group|
22
28
  success &&= Ffmpeg.new(input, group).run
23
- group.each do |output|
24
- success &&= Faststart.new(output).run if output.faststart
25
- success &&= VideoScreenshoter.new(output.thumbnails.merge(:input => output.ffmpeg_output, :output_dir => File.join(output.work_dir, 'thumbnails'))).run if output.thumbnails
26
- end
29
+ end
30
+ end
31
+ success
32
+ end
33
+
34
+ def faststart
35
+ success = true
36
+ outputs.each do |output|
37
+ success &&= Faststart.new(output).run if output.faststart
38
+ end
39
+ success
40
+ end
41
+
42
+ def make_screenshots
43
+ success = true
44
+ outputs.each do |output|
45
+ success &&= VideoScreenshoter.new(output.thumbnails.merge(:input => output.ffmpeg_output, :output_dir => File.join(output.work_dir, 'thumbnails'))).run if output.thumbnails
46
+ end
47
+ success
48
+ end
49
+
50
+ def segment
51
+ success = true
52
+ inputs.each do |input|
53
+ input.output_groups.each do |group|
27
54
  if playlist = group.detect { |output| output.type == 'playlist' }
28
55
  success &&= if playlist.format == 'm3u8'
29
56
  LiveSegmenter.new(input, group).run
@@ -33,11 +60,20 @@ module VideoConverter
33
60
  end
34
61
  end
35
62
  end
36
- clear if clear_tmp && success
37
- success
38
63
  end
39
64
 
40
- private
65
+ def split
66
+ Ffmpeg.new(inputs.first, outputs).split
67
+ end
68
+
69
+ def concat
70
+ list = File.join(outputs.first.work_dir, 'list.txt')
71
+ # NOTE ffmpeg concat list requires unescaped files
72
+ File.write(list, inputs.map { |input| "file '#{File.absolute_path(input.unescape)}'" }.join("\n"))
73
+ success = Ffmpeg.new(list, outputs).concat
74
+ FileUtils.rm list if success
75
+ success
76
+ end
41
77
 
42
78
  def clear
43
79
  `cat #{outputs.first.log} >> #{VideoConverter.log} && rm #{outputs.first.log}`
@@ -3,12 +3,13 @@
3
3
  module VideoConverter
4
4
  class Ffmpeg
5
5
  class << self
6
- attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command
6
+ attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command, :split_command, :concat_command
7
7
  end
8
8
 
9
9
  self.bin = '/usr/local/bin/ffmpeg'
10
10
  self.ffprobe_bin = '/usr/local/bin/ffprobe'
11
11
  self.options = {
12
+ :codec => '-c',
12
13
  :video_codec => '-c:v',
13
14
  :audio_codec => '-c:a',
14
15
  :frame_rate => '-r',
@@ -24,12 +25,17 @@ module VideoConverter
24
25
  :format => '-f',
25
26
  :bitstream_format => '-bsf',
26
27
  :pixel_format => '-pix_fmt',
27
- :deinterlace => '-deinterlace'
28
+ :deinterlace => '-deinterlace',
29
+ :map => '-map',
30
+ :segment_time => '-segment_time',
31
+ :reset_timestamps => '-reset_timestamps'
28
32
  }
29
33
  self.one_pass_command = '%{bin} -i %{input} -y %{options} %{output} 1>>%{log} 2>&1 || exit 1'
30
34
  self.first_pass_command = '%{bin} -i %{input} -y -pass 1 -an %{options} /dev/null 1>>%{log} 2>&1 || exit 1'
31
35
  self.second_pass_command = '%{bin} -i %{input} -y -pass 2 %{options} %{output} 1>>%{log} 2>&1 || exit 1'
32
36
  self.keyframes_command = '%{ffprobe_bin} -show_frames -select_streams v:0 -print_format csv %{input} | grep frame,video,1 | cut -d\',\' -f5 | tr "\n" "," | sed \'s/,$//\''
37
+ self.split_command = '%{bin} -fflags +genpts -i %{input} -segment_time %{segment_time} -reset_timestamps 1 -c copy -map 0 -f segment %{output} 1>>%{log} 2>&1 || exit 1'
38
+ self.concat_command = "%{bin} -f concat -i %{input} -c %{codec} %{output} 1>>%{log} 2>&1 || exit 1"
33
39
 
34
40
  attr_accessor :input, :group
35
41
 
@@ -77,6 +83,16 @@ module VideoConverter
77
83
  success
78
84
  end
79
85
 
86
+ def split
87
+ Command.new(self.class.split_command, prepare_params(input, group.first)).execute
88
+ end
89
+
90
+ def concat
91
+ output = group.first
92
+ output.codec ||= 'copy'
93
+ Command.new(self.class.concat_command, prepare_params(input, output)).execute
94
+ end
95
+
80
96
  private
81
97
 
82
98
  def prepare_params input, output
@@ -91,6 +107,8 @@ module VideoConverter
91
107
  :input => input.to_s,
92
108
  :log => output.log,
93
109
  :output => output.ffmpeg_output,
110
+ :codec => output.codec,
111
+ :segment_time => output.segment_time,
94
112
  :options => self.class.options.map do |output_option, ffmpeg_option|
95
113
  if output.send(output_option).present?
96
114
  if output.send(output_option) == true
@@ -60,6 +60,10 @@ module VideoConverter
60
60
  input
61
61
  end
62
62
 
63
+ def unescape
64
+ input.gsub(/\\+([^n])/, '\1')
65
+ end
66
+
63
67
  private
64
68
 
65
69
  def exists?
@@ -17,9 +17,9 @@ module VideoConverter
17
17
 
18
18
  attr_accessor :uid, :work_dir, :log, :threads, :passlogfile
19
19
  attr_accessor :type, :filename
20
- attr_accessor :format, :ffmpeg_output, :video_codec, :audio_codec, :bitstream_format, :pixel_format
20
+ attr_accessor :format, :ffmpeg_output, :codec, :video_codec, :audio_codec, :bitstream_format, :pixel_format, :map
21
21
  attr_accessor :one_pass, :video_bitrate, :audio_bitrate
22
- attr_accessor :streams, :path, :chunks_dir
22
+ attr_accessor :streams, :path, :chunks_dir, :segment_time, :reset_timestamps
23
23
  attr_accessor :frame_rate, :keyint_min, :keyframe_interval, :force_keyframes
24
24
  attr_accessor :size, :width, :height, :video_filter
25
25
  attr_accessor :thumbnails
@@ -45,14 +45,16 @@ module VideoConverter
45
45
  self.format = 'mpegts'
46
46
  self.ffmpeg_output = File.join(work_dir, File.basename(filename, '.*') + '.ts')
47
47
  else
48
- self.format = File.extname(filename).sub('.', '')
48
+ self.format = params[:format] || File.extname(filename).sub('.', '')
49
49
  self.ffmpeg_output = File.join(work_dir, filename)
50
50
  raise ArgumentError.new('Invalid playlist extension') if type == 'playlist' && !['f4m', 'm3u8'].include?(format)
51
51
  end
52
- self.video_codec = params[:video_codec] || self.class.video_codec
53
- self.audio_codec = params[:audio_codec] || self.class.audio_codec
52
+ self.codec = params[:codec]
53
+ self.video_codec = params[:video_codec] || self.class.video_codec unless codec
54
+ self.audio_codec = params[:audio_codec] || self.class.audio_codec unless codec
54
55
  self.bitstream_format = params[:bitstream_format]
55
56
  self.pixel_format = params[:pixel_format] || self.class.pixel_format
57
+ self.map = params[:map]
56
58
 
57
59
  # Rate controle
58
60
  self.one_pass = !!params[:one_pass]
@@ -66,6 +68,8 @@ module VideoConverter
66
68
  self.chunks_dir = File.join(work_dir, File.basename(filename, '.*'))
67
69
  FileUtils.mkdir_p(chunks_dir)
68
70
  end
71
+ self.segment_time = params[:segment_time]
72
+ self.reset_timestamps = params[:reset_timestamps]
69
73
 
70
74
  # Frame rate
71
75
  self.frame_rate = params[:frame_rate]
@@ -1,3 +1,3 @@
1
1
  module VideoConverter
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -123,4 +123,34 @@ class VideoConverterTest < Test::Unit::TestCase
123
123
  end
124
124
  end
125
125
  end
126
+
127
+ context 'split' do
128
+ setup do
129
+ (@c = VideoConverter.new(
130
+ :input => "test/fixtures/test (1).mp4",
131
+ :output => { :segment_time => 2, :filename => '%01d.nut' }
132
+ )).split
133
+ end
134
+ should 'segment file' do
135
+ assert_equal %w(0.nut 1.nut 2.nut 3.nut converter.log), Dir.entries(@c.outputs.first.work_dir).delete_if { |e| %w(. ..).include?(e) }.sort
136
+ end
137
+ end
138
+
139
+ context 'concat' do
140
+ setup do
141
+ FileUtils.cp("test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4")
142
+ (@c = VideoConverter.new(
143
+ :input => ["test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4"],
144
+ :output => { :filename => 'concat.mp4' }
145
+ )).concat
146
+ FileUtils.rm("test/fixtures/test (2).mp4")
147
+ end
148
+ should 'concat inputs' do
149
+ assert File.exists?(@c.outputs.first.ffmpeg_output)
150
+ assert_equal(
151
+ VideoConverter.new(:input => "test/fixtures/test (1).mp4").inputs.first.metadata[:duration_in_ms]/1000*2,
152
+ VideoConverter.new(:input => @c.outputs.first.ffmpeg_output).inputs.first.metadata[:duration_in_ms]/1000
153
+ )
154
+ end
155
+ end
126
156
  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.5.0
4
+ version: 0.6.0
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: 2014-05-16 00:00:00.000000000 Z
12
+ date: 2014-05-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: video_screenshoter
@@ -142,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
142
  version: '0'
143
143
  segments:
144
144
  - 0
145
- hash: 948361468017971705
145
+ hash: 1551295134079710729
146
146
  required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  none: false
148
148
  requirements:
@@ -151,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
151
  version: '0'
152
152
  segments:
153
153
  - 0
154
- hash: 948361468017971705
154
+ hash: 1551295134079710729
155
155
  requirements:
156
156
  - ffmpeg, version 1.2 or greated configured with libx264 and libfaac
157
157
  - live_segmenter to convert to hls