ez_video 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +41 -0
- data/LICENSE.txt +20 -0
- data/README.md +132 -0
- data/ez_video.gemspec +28 -0
- data/lib/ez_video.rb +15 -0
- data/lib/rvideo/errors.rb +27 -0
- data/lib/rvideo/float.rb +7 -0
- data/lib/rvideo/inspector.rb +573 -0
- data/lib/rvideo/tools/abstract_tool.rb +339 -0
- data/lib/rvideo/tools/ffmpeg.rb +208 -0
- data/lib/rvideo/tools/ffmpeg2theora.rb +42 -0
- data/lib/rvideo/tools/flvtool2.rb +50 -0
- data/lib/rvideo/tools/mencoder.rb +106 -0
- data/lib/rvideo/tools/mp4box.rb +21 -0
- data/lib/rvideo/tools/mp4creator.rb +35 -0
- data/lib/rvideo/tools/mplayer.rb +31 -0
- data/lib/rvideo/tools/yamdi.rb +44 -0
- data/lib/rvideo/transcoder.rb +138 -0
- data/lib/rvideo/version.rb +9 -0
- metadata +65 -0
@@ -0,0 +1,339 @@
|
|
1
|
+
module RVideo # :nodoc:
|
2
|
+
module Tools # :nodoc:
|
3
|
+
class AbstractTool
|
4
|
+
|
5
|
+
#
|
6
|
+
# AbstractTool is an interface to every transcoder tool class (e.g.
|
7
|
+
# ffmpeg, flvtool2). Called by the Transcoder class.
|
8
|
+
#
|
9
|
+
|
10
|
+
def self.assign(cmd, options = {})
|
11
|
+
tool_name = File.split(cmd.split(" ").first).last
|
12
|
+
begin
|
13
|
+
tool = "RVideo::Tools::#{tool_name.classify}".constantize.send(:new, cmd, options)
|
14
|
+
# rescue NameError, /uninitialized constant/
|
15
|
+
# raise TranscoderError::UnknownTool, "The recipe tried to use the '#{tool_name}' tool, which does not exist."
|
16
|
+
rescue => e
|
17
|
+
LOGGER.info $!
|
18
|
+
LOGGER.info e.backtrace.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
attr_reader :options, :command, :raw_result
|
25
|
+
attr_writer :original
|
26
|
+
|
27
|
+
def initialize(raw_command, options = {})
|
28
|
+
@raw_command = raw_command
|
29
|
+
@options = HashWithIndifferentAccess.new(options)
|
30
|
+
@command = interpolate_variables(raw_command)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Execute the command and parse the result.
|
35
|
+
#
|
36
|
+
# def execute
|
37
|
+
# @output_params = {}
|
38
|
+
# final_command = "#{@command} 2>&1"
|
39
|
+
# Transcoder.logger.info("\nExecuting Command: #{final_command}\n")
|
40
|
+
# @raw_result = `#{final_command}`
|
41
|
+
# Transcoder.logger.info("Result: \n#{@raw_result}")
|
42
|
+
# parse_result(@raw_result)
|
43
|
+
# end
|
44
|
+
|
45
|
+
def execute
|
46
|
+
@output_params = {}
|
47
|
+
|
48
|
+
# Dump the log output into a temp file
|
49
|
+
log_temp_file_name = "/tmp/transcode_output_#{Time.now.to_i}.txt"
|
50
|
+
|
51
|
+
final_command = "#{@command} 2>#{log_temp_file_name}"
|
52
|
+
Transcoder.logger.info("\nExecuting Command: #{final_command}\n")
|
53
|
+
`#{final_command}`
|
54
|
+
|
55
|
+
populate_raw_result(log_temp_file_name)
|
56
|
+
|
57
|
+
Transcoder.logger.info("Result: \n#{@raw_result}")
|
58
|
+
parse_result(@raw_result)
|
59
|
+
|
60
|
+
# Cleanup log file
|
61
|
+
begin
|
62
|
+
File.delete(log_temp_file_name)
|
63
|
+
rescue Exception => e
|
64
|
+
Transcoder.logger.error("Failed to delete output log file: #{log_temp_file_name}, e=#{e}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Magic parameters
|
70
|
+
#
|
71
|
+
def temp_dir
|
72
|
+
if @options['output_file']
|
73
|
+
"#{File.dirname(@options['output_file'])}/"
|
74
|
+
else
|
75
|
+
""
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def fps
|
81
|
+
format_fps(get_fps)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_fps
|
85
|
+
inspect_original if @original.nil?
|
86
|
+
fps = @options['fps'] || ""
|
87
|
+
case fps
|
88
|
+
when "copy"
|
89
|
+
get_original_fps
|
90
|
+
else
|
91
|
+
get_specific_fps
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def resolution
|
97
|
+
format_resolution(get_resolution)
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_resolution
|
101
|
+
inspect_original if @original.nil?
|
102
|
+
resolution_setting = @options['resolution'] || ""
|
103
|
+
case resolution_setting
|
104
|
+
when "copy"
|
105
|
+
get_original_resolution
|
106
|
+
when "width"
|
107
|
+
get_fit_to_width_resolution
|
108
|
+
when "height"
|
109
|
+
get_fit_to_height_resolution
|
110
|
+
when "letterbox"
|
111
|
+
get_letterbox_resolution
|
112
|
+
else
|
113
|
+
get_specific_resolution
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def audio_channels
|
119
|
+
format_audio_channels(get_audio_channels)
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_audio_channels
|
123
|
+
channels = @options['audio_channels'] || ""
|
124
|
+
case channels
|
125
|
+
when "stereo"
|
126
|
+
get_stereo_audio
|
127
|
+
when "mono"
|
128
|
+
get_mono_audio
|
129
|
+
else
|
130
|
+
{}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def audio_bit_rate
|
135
|
+
format_audio_bit_rate(get_audio_bit_rate)
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_audio_bit_rate
|
139
|
+
bit_rate = @options['audio_bit_rate'] || ""
|
140
|
+
case bit_rate
|
141
|
+
when ""
|
142
|
+
{}
|
143
|
+
else
|
144
|
+
get_specific_audio_bit_rate
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def audio_sample_rate
|
149
|
+
format_audio_sample_rate(get_audio_sample_rate)
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_audio_sample_rate
|
153
|
+
sample_rate = @options['audio_sample_rate'] || ""
|
154
|
+
case sample_rate
|
155
|
+
when ""
|
156
|
+
{}
|
157
|
+
else
|
158
|
+
get_specific_audio_sample_rate
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def video_quality
|
163
|
+
format_video_quality(get_video_quality)
|
164
|
+
end
|
165
|
+
|
166
|
+
def get_video_quality
|
167
|
+
inspect_original if @original.nil?
|
168
|
+
quality = @options['video_quality'] || 'medium'
|
169
|
+
video_bit_rate = @options['video_bit_rate'] || nil
|
170
|
+
h = {:video_quality => quality, :video_bit_rate => video_bit_rate}
|
171
|
+
h.merge!(get_fps).merge!(get_resolution)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
def get_fit_to_width_resolution
|
177
|
+
w = @options['width']
|
178
|
+
raise TranscoderError::ParameterError, "invalid width of '#{w}' for fit to width" unless valid_dimension?(w)
|
179
|
+
h = calculate_height(@original.width, @original.height, w)
|
180
|
+
{:scale => {:width => w, :height => h}}
|
181
|
+
end
|
182
|
+
|
183
|
+
def get_fit_to_height_resolution
|
184
|
+
h = @options['height']
|
185
|
+
raise TranscoderError::ParameterError, "invalid height of '#{h}' for fit to height" unless valid_dimension?(h)
|
186
|
+
w = calculate_width(@original.width, @original.height, h)
|
187
|
+
{:scale => {:width => w, :height => h}}
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_letterbox_resolution
|
191
|
+
lw = @options['width'].to_i
|
192
|
+
lh = @options['height'].to_i
|
193
|
+
raise TranscoderError::ParameterError, "invalid width of '#{lw}' for letterbox" unless valid_dimension?(lw)
|
194
|
+
raise TranscoderError::ParameterError, "invalid height of '#{lh}' for letterbox" unless valid_dimension?(lh)
|
195
|
+
w = calculate_width(@original.width, @original.height, lh)
|
196
|
+
h = calculate_height(@original.width, @original.height, lw)
|
197
|
+
if w > lw
|
198
|
+
w = lw
|
199
|
+
h = calculate_height(@original.width, @original.height, lw)
|
200
|
+
else
|
201
|
+
h = lh
|
202
|
+
w = calculate_width(@original.width, @original.height, lh)
|
203
|
+
end
|
204
|
+
{:scale => {:width => w, :height => h}, :letterbox => {:width => lw, :height => lh}}
|
205
|
+
end
|
206
|
+
|
207
|
+
def get_original_resolution
|
208
|
+
{:scale => {:width => @original.width, :height => @original.height}}
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_specific_resolution
|
212
|
+
w = @options['width']
|
213
|
+
h = @options['height']
|
214
|
+
raise TranscoderError::ParameterError, "invalid width of '#{w}' for specific resolution" unless valid_dimension?(w)
|
215
|
+
raise TranscoderError::ParameterError, "invalid height of '#{h}' for specific resolution" unless valid_dimension?(h)
|
216
|
+
{:scale => {:width => w, :height => h}}
|
217
|
+
end
|
218
|
+
|
219
|
+
def get_original_fps
|
220
|
+
return {} if @original.fps.nil?
|
221
|
+
{:fps => @original.fps}
|
222
|
+
end
|
223
|
+
|
224
|
+
def get_specific_fps
|
225
|
+
{:fps => @options['fps']}
|
226
|
+
end
|
227
|
+
|
228
|
+
# def get_video_quality
|
229
|
+
# fps = @options['fps'] || @original.fps
|
230
|
+
# raise TranscoderError::ParameterError, "could not find fps in order to determine video quality" if fps.nil?
|
231
|
+
# width = @original.width
|
232
|
+
# height = @
|
233
|
+
# format_video_quality({:quality => @options['video_quality'], :bit_rate => @options['video_bit_rate']})
|
234
|
+
# end
|
235
|
+
|
236
|
+
def get_stereo_audio
|
237
|
+
{:channels => "2"}
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_mono_audio
|
241
|
+
{:channels => "1"}
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_specific_audio_bit_rate
|
245
|
+
{:bit_rate => @options['audio_bit_rate']}
|
246
|
+
end
|
247
|
+
|
248
|
+
def get_specific_audio_sample_rate
|
249
|
+
{:sample_rate => @options['audio_sample_rate']}
|
250
|
+
end
|
251
|
+
|
252
|
+
def calculate_width(ow, oh, h)
|
253
|
+
w = ((ow.to_f / oh.to_f) * h.to_f).to_i
|
254
|
+
(w.to_f / 16).round * 16
|
255
|
+
end
|
256
|
+
|
257
|
+
def calculate_height(ow, oh, w)
|
258
|
+
h = (w.to_f / (ow.to_f / oh.to_f)).to_i
|
259
|
+
(h.to_f / 16).round * 16
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
def valid_dimension?(dim)
|
264
|
+
return false if dim.to_i <= 0
|
265
|
+
return true
|
266
|
+
end
|
267
|
+
|
268
|
+
def format_resolution(params={})
|
269
|
+
raise ParameterError, "The #{self.class} tool has not implemented the format_resolution method."
|
270
|
+
end
|
271
|
+
|
272
|
+
def format_fps(params={})
|
273
|
+
raise ParameterError, "The #{self.class} tool has not implemented the format_fps method."
|
274
|
+
end
|
275
|
+
|
276
|
+
def format_audio_channels(params={})
|
277
|
+
raise ParameterError, "The #{self.class} tool has not implemented the format_audio_channels method."
|
278
|
+
end
|
279
|
+
|
280
|
+
def format_audio_bit_rate(params={})
|
281
|
+
raise ParameterError, "The #{self.class} tool has not implemented the format_audio_bit_rate method."
|
282
|
+
end
|
283
|
+
|
284
|
+
def format_audio_sample_rate(params={})
|
285
|
+
raise ParameterError, "The #{self.class} tool has not implemented the format_audio_sample_rate method."
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
|
291
|
+
#
|
292
|
+
# Look for variables surrounded by $, and interpolate with either
|
293
|
+
# variables passed in the options hash, or special methods provided by
|
294
|
+
# the tool class (e.g. "$original_fps$" with ffmpeg).
|
295
|
+
#
|
296
|
+
# $foo$ should match
|
297
|
+
# \$foo or $foo\$ or \$foo\$ should not
|
298
|
+
|
299
|
+
def interpolate_variables(raw_command)
|
300
|
+
raw_command.scan(/[^\\]\$[-_a-zA-Z]+\$/).each do |match|
|
301
|
+
match = match[0..0] == "$" ? match : match[1..(match.size - 1)]
|
302
|
+
match.strip!
|
303
|
+
raw_command.gsub!(match, matched_variable(match))
|
304
|
+
end
|
305
|
+
raw_command.gsub("\\$", "$")
|
306
|
+
end
|
307
|
+
|
308
|
+
#
|
309
|
+
# Strip the $s. First, look for a supplied option that matches the
|
310
|
+
# variable name. If one is not found, look for a method that matches.
|
311
|
+
# If not found, raise ParameterError exception.
|
312
|
+
#
|
313
|
+
|
314
|
+
def matched_variable(match)
|
315
|
+
variable_name = match.gsub("$","")
|
316
|
+
if self.respond_to? variable_name
|
317
|
+
self.send(variable_name)
|
318
|
+
elsif @options.key?(variable_name)
|
319
|
+
@options[variable_name] || ""
|
320
|
+
else
|
321
|
+
raise TranscoderError::ParameterError, "command is looking for the #{variable_name} parameter, but it was not provided. (Command: #{@raw_command})"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def inspect_original
|
326
|
+
@original = Inspector.new(:file => options[:input_file])
|
327
|
+
end
|
328
|
+
|
329
|
+
# Pulls the interesting bits of the temp log file into memory. This is fairly tool-specific, so
|
330
|
+
# it's doubtful that this default version is going to work without being overridded.
|
331
|
+
def populate_raw_result(temp_file_name)
|
332
|
+
@raw_result = `tail -n 500 #{temp_file_name}`
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module RVideo
|
2
|
+
module Tools
|
3
|
+
class Ffmpeg
|
4
|
+
include AbstractTool::InstanceMethods
|
5
|
+
|
6
|
+
attr_reader :frame, :q, :size, :time, :output_bitrate, :video_size, :audio_size, :header_size, :overhead, :psnr, :output_fps
|
7
|
+
|
8
|
+
# Not sure if this is needed anymore...
|
9
|
+
def tool_command
|
10
|
+
'ffmpeg'
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_fps(params={})
|
14
|
+
" -r #{params[:fps]}"
|
15
|
+
end
|
16
|
+
def format_video_quality(params={})
|
17
|
+
bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
|
18
|
+
factor = (params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f)
|
19
|
+
case params[:video_quality]
|
20
|
+
when 'low'
|
21
|
+
bitrate ||= (factor / 12000).to_i
|
22
|
+
" -v #{bitrate}k -crf 30 -me zero -subq 1 -refs 1 -threads auto "
|
23
|
+
when 'medium'
|
24
|
+
bitrate ||= (factor / 9000).to_i
|
25
|
+
" -v #{bitrate}k -crf 22 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me hex -subq 3 -trellis 1 -refs 2 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250"
|
26
|
+
when 'high'
|
27
|
+
bitrate ||= (factor / 3600).to_i
|
28
|
+
" -v #{bitrate}k -crf 18 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me full -subq 6 -trellis 1 -refs 3 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71"
|
29
|
+
else
|
30
|
+
""
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def format_resolution(params={})
|
36
|
+
p = " -s #{params[:scale][:width]}x#{params[:scale][:height]} "
|
37
|
+
if params[:letterbox]
|
38
|
+
plr = ((params[:letterbox][:width] - params[:scale][:width]) / 2).to_i
|
39
|
+
ptb = ((params[:letterbox][:height] - params[:scale][:height]) / 2).to_i
|
40
|
+
p += " -padtop #{ptb} -padbottom #{ptb} -padleft #{plr} -padright #{plr} "
|
41
|
+
end
|
42
|
+
p
|
43
|
+
end
|
44
|
+
|
45
|
+
def format_audio_channels(params={})
|
46
|
+
" -ac #{params[:channels]}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def format_audio_bit_rate(params={})
|
50
|
+
" -ab #{params[:bit_rate]}k"
|
51
|
+
end
|
52
|
+
|
53
|
+
def format_audio_sample_rate(params={})
|
54
|
+
" -ar #{params[:sample_rate]}"
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Turns the temp log file into a useful string, from which we can parse the
|
61
|
+
# transcoding results.
|
62
|
+
# These log files can be enormous, so pulling the whole thing into memory is not an
|
63
|
+
# option.
|
64
|
+
def populate_raw_result(temp_file_name)
|
65
|
+
@raw_result = ""
|
66
|
+
|
67
|
+
# Is the log file exceptionally long? It's really not a big deal to pull in a thousand lines or so
|
68
|
+
# into memory. It's the gigantic files that cause problems. If the files isn't too large,
|
69
|
+
# just pull it in.
|
70
|
+
line_count = 0
|
71
|
+
if m = /^\s*(\d+)/.match(`wc -l #{temp_file_name}`)
|
72
|
+
line_count = m[1].to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
if line_count > 500
|
76
|
+
# Find the message indicating that the command is actually running.
|
77
|
+
running_string = "Press .* to stop encoding"
|
78
|
+
@raw_result << `grep "#{running_string}" #{temp_file_name}`
|
79
|
+
end
|
80
|
+
|
81
|
+
# Append the bottom of the log file, where the interesting bits live.
|
82
|
+
@raw_result << `tail -n 500 #{temp_file_name}`
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_result(result)
|
86
|
+
|
87
|
+
if m = /Unable for find a suitable output format for.*$/.match(result)
|
88
|
+
raise TranscoderError::InvalidCommand, m[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
if m = /Unknown codec \'(.*)\'/.match(result)
|
92
|
+
raise TranscoderError::InvalidFile, "Codec #{m[1]} not supported by this build of ffmpeg"
|
93
|
+
end
|
94
|
+
|
95
|
+
if m = /could not find codec parameters/.match(result)
|
96
|
+
raise TranscoderError::InvalidFile, "Codec not supported by this build of ffmpeg"
|
97
|
+
end
|
98
|
+
|
99
|
+
if m = /I\/O error occured\n(.*)$/.match(result)
|
100
|
+
raise TranscoderError::InvalidFile, "I/O error: #{m[1].strip}"
|
101
|
+
end
|
102
|
+
|
103
|
+
if m = /\n(.*)Unknown Format$/.match(result)
|
104
|
+
raise TranscoderError::InvalidFile, "unknown format (#{m[1]})"
|
105
|
+
end
|
106
|
+
|
107
|
+
if m = /\nERROR.*/m.match(result)
|
108
|
+
raise TranscoderError::InvalidFile, m[0]
|
109
|
+
end
|
110
|
+
|
111
|
+
if result =~ /usage: ffmpeg/
|
112
|
+
raise TranscoderError::InvalidCommand, "must pass a command to ffmpeg"
|
113
|
+
end
|
114
|
+
|
115
|
+
if result =~ /Output file does not contain.*stream/
|
116
|
+
raise TranscoderError, "Output file does not contain any video or audio streams."
|
117
|
+
end
|
118
|
+
|
119
|
+
if m = /Unsupported codec.*id=(.*)\).*for input stream\s*(.*)\s*/.match(result)
|
120
|
+
inspect_original if @original.nil?
|
121
|
+
case m[2]
|
122
|
+
when @original.audio_stream_id
|
123
|
+
codec_type = "audio"
|
124
|
+
codec = @original.audio_codec
|
125
|
+
when @original.video_stream_id
|
126
|
+
codec_type = "video"
|
127
|
+
codec = @original.video_codec
|
128
|
+
else
|
129
|
+
codec_type = "video or audio"
|
130
|
+
codec = "unknown"
|
131
|
+
end
|
132
|
+
|
133
|
+
raise TranscoderError::InvalidFile, "Unsupported #{codec_type} codec: #{codec} (id=#{m[1]}, stream=#{m[2]})"
|
134
|
+
#raise TranscoderError, "Codec #{m[1]} not supported (in stream #{m[2]})"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Could not open './spec/../config/../tmp/processed/1/kites-1.avi'
|
138
|
+
if result =~ /Could not open .#{@output_file}.\Z/
|
139
|
+
raise TranscoderError, "Could not write output file to #{@output_file}"
|
140
|
+
end
|
141
|
+
|
142
|
+
full_details = /Press .* to stop encoding\n(.*)/m.match(result)
|
143
|
+
raise TranscoderError, "Unexpected result details (#{result})" if full_details.nil?
|
144
|
+
details = full_details[1].strip.gsub(/\s*\n\s*/," - ")
|
145
|
+
|
146
|
+
if details =~ /Could not write header/
|
147
|
+
raise TranscoderError, details
|
148
|
+
end
|
149
|
+
|
150
|
+
#frame= 584 q=6.0 Lsize= 708kB time=19.5 bitrate= 297.8kbits/s
|
151
|
+
#video:49kB audio:153kB global headers:0kB muxing overhead 250.444444%
|
152
|
+
|
153
|
+
#frame= 4126 q=31.0 Lsize= 5917kB time=69.1 bitrate= 702.0kbits/s
|
154
|
+
#video:2417kB audio:540kB global headers:0kB muxing overhead 100.140277%
|
155
|
+
|
156
|
+
#frame= 273 fps= 31 q=10.0 Lsize= 398kB time=5.9 bitrate= 551.8kbits/s
|
157
|
+
#video:284kB audio:92kB global headers:0kB muxing overhead 5.723981%
|
158
|
+
|
159
|
+
#mdb:94, lastbuf:0 skipping granule 0
|
160
|
+
#size= 1080kB time=69.1 bitrate= 128.0kbits /s
|
161
|
+
#video:0kB audio:1080kB global headers:0kB muxing overhead 0.002893%
|
162
|
+
|
163
|
+
#size= 80kB time=5.1 bitrate= 128.0kbits/s ^Msize= 162kB time=10.3 bitrate= 128.0kbits/s ^Msize= 241kB time=15.4 bitrate= 128.0kbits/s ^Msize= 329kB time=21.1 bitrate= 128.0kbits/s ^Msize= 413kB time=26.4 bitrate= 128.0kbits/s ^Msize= 506kB time=32.4 bitrate= 128.0kbits/s ^Msize= 591kB time=37.8 bitrate= 128.0kbits/s ^Msize= 674kB time=43.2 bitrate= 128.0kbits/s ^Msize= 771kB time=49.4 bitrate= 128.0kbits/s ^Msize= 851kB time=54.5 bitrate= 128.0kbits/s ^Msize= 932kB time=59.6 bitrate= 128.0kbits/s ^Msize= 1015kB time=64.9 bitrate= 128.0kbits/s ^Msize= 1094kB time=70.0 bitrate= 128.0kbits/s ^Msize= 1175kB time=75.2 bitrate= 128.0kbits/s ^Msize= 1244kB time=79.6 bitrate= 128.0kbits/s ^Msize= 1335kB time=85.4 bitrate= 128.0kbits/s ^Msize= 1417kB time=90.7 bitrate= 128.0kbits/s ^Msize= 1508kB time=96.5 bitrate= 128.0kbits/s ^Msize= 1589kB time=101.7 bitrate= 128.0kbits/s ^Msize= 1671kB time=106.9 bitrate= 128.0kbits/s ^Msize= 1711kB time=109.5 bitrate= 128.0kbits/s - video:0kB audio:1711kB global headers:0kB muxing overhead 0.001826%
|
164
|
+
|
165
|
+
#mdb:14, lastbuf:0 skipping granule 0 - overread, skip -5 enddists: -2 -2 - overread, skip -5 enddists: -2 -2 - size= 90kB time=5.7 bitrate= 128.0kbits/s \nsize= 189kB time=12.1 bitrate= 128.0kbits/s
|
166
|
+
|
167
|
+
#size= 59kB time=20.2 bitrate= 24.0kbits/s \nsize= 139kB time=47.4 bitrate= 24.0kbits/s \nsize= 224kB time=76.5 bitrate= 24.0kbits/s \nsize= 304kB time=103.7 bitrate= 24.0kbits/s \nsi
|
168
|
+
|
169
|
+
#mdb:14, lastbuf:0 skipping granule 0 - overread, skip -5 enddists: -2 -2 - overread, skip -5 enddists: -2 -2 - size= 81kB time=10.3 bitrate= 64.0kbits/s \nsize= 153kB time=19.6 bitrate= 64.0kbits/s
|
170
|
+
|
171
|
+
#size= 65kB time=4.1 bitrate= 128.1kbits/s \nsize= 119kB time=7.6 bitrate= 128.0kbits/s \nsize= 188kB time=12.0 bitrate= 128.0kbits/s \nsize= 268kB time=17.1 bitrate= 128.0kbits/s \nsize=
|
172
|
+
|
173
|
+
#Error while decoding stream #0.1 [mpeg4aac @ 0xb7d089f0]faac: frame decoding failed: Gain control not yet implementedError while decoding stream #0.1frame= 2143 fps= 83 q=4.0 size= 4476kB time=71.3 bitrate= 514.5kbits/s ^M[mpeg4aac @ 0xb7d089f0]faac: frame decoding failed: Gain control not yet implementedError while decoding stream #0.1
|
174
|
+
|
175
|
+
# NOTE: had to remove "\s" from "\s.*L.*size=" from this regexp below.
|
176
|
+
# Not sure why. Unit tests were succeeding, but hand tests weren't.
|
177
|
+
if details =~ /video:/
|
178
|
+
#success = /^frame=\s*(\S*)\s*q=(\S*).*L.*size=\s*(\S*)\s*time=\s*(\S*)\s*bitrate=\s*(\S*)\s*/m.match(details)
|
179
|
+
@frame = sanitary_match(/frame=\s*(\S*)/, details)
|
180
|
+
@output_fps = sanitary_match(/fps=\s*(\S*)/, details)
|
181
|
+
@q = sanitary_match(/\s+q=\s*(\S*)/, details)
|
182
|
+
@size = sanitary_match(/size=\s*(\S*)/, details)
|
183
|
+
@time = sanitary_match(/time=\s*(\S*)/, details)
|
184
|
+
@output_bitrate = sanitary_match(/bitrate=\s*(\S*)/, details)
|
185
|
+
|
186
|
+
@video_size = /video:\s*(\S*)/.match(details)[1]
|
187
|
+
@audio_size = /audio:\s*(\S*)/.match(details)[1]
|
188
|
+
@header_size = /headers:\s*(\S*)/.match(details)[1]
|
189
|
+
@overhead = /overhead[:]*\s*(\S*)/.match(details)[1]
|
190
|
+
psnr_match = /PSNR=(.*)\s*size=/.match(details)
|
191
|
+
@psnr = psnr_match[1].strip if psnr_match
|
192
|
+
return true
|
193
|
+
end
|
194
|
+
|
195
|
+
#[mp3 @ 0x54340c]flv doesnt support that sample rate, choose from (44100, 22050, 11025)
|
196
|
+
#Could not write header for output file #0 (incorrect codec parameters ?)
|
197
|
+
|
198
|
+
raise TranscoderError::UnexpectedResult, details
|
199
|
+
end
|
200
|
+
|
201
|
+
def sanitary_match(regexp, string)
|
202
|
+
match = regexp.match(string)
|
203
|
+
return match[1] if match
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|