debprado-rvideo 0.9.6
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.
- data/License.txt +20 -0
- data/README.txt +91 -0
- data/RULES +11 -0
- data/debprado-rvideo.gemspec +10 -0
- data/lib/rvideo.rb +22 -0
- data/lib/rvideo/errors.rb +24 -0
- data/lib/rvideo/float.rb +7 -0
- data/lib/rvideo/inspector.rb +545 -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 +74 -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 = cmd.split(" ").first
|
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
|