Class: RVideo::Inspector

Inherits:
Object
  • Object
show all
Defined in:
lib/rvideo/inspector.rb

Overview

To inspect a video or audio file, initialize an Inspector object.

file = RVideo::Inspector.new(options_hash)

Inspector accepts three options: file, raw_response, and ffmpeg_binary. Either raw_response or file is required; ffmpeg binary is optional.

:file is a path to a file to be inspected.

:raw_response is the full output of “ffmpeg -i [file]”. If the :raw_response option is used, RVideo will not actually inspect a file; it will simply parse the provided response. This is useful if your application has already collected the ffmpeg -i response, and you don't want to call it again.

:ffmpeg_binary is an optional argument that specifies the path to the ffmpeg binary to be used. If a path is not explicitly declared, RVideo will assume that ffmpeg exists in the Unix path. Type “which ffmpeg” to check if ffmpeg is installed and exists in your operating system's path.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Inspector

Returns a new instance of Inspector.


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rvideo/inspector.rb', line 26

def initialize(options = {})
  if not (options[:raw_response] or options[:file])
    raise ArgumentError, "Must supply either an input file or a pregenerated response"
  end
  
  if options[:raw_response]
    initialize_with_raw_response(options[:raw_response])
  elsif options[:file]
    initialize_with_file(options[:file], options[:ffmpeg_binary])
  end
  
   = /(Input \#.*)\n(Must|At\sleast)/m.match(@raw_response)
  # metadata = /(Input \#.*)\n.+\n\Z/m.match(@raw_response)
  
  if /Unknown format/i.match(@raw_response) || .nil?
    @unknown_format = true
  elsif /Duration: N\/A/im.match(@raw_response)
    # in this case, we can at least still get the container type
    @unreadable_file = true
    @raw_metadata = [1]
  else
    @raw_metadata = [1]
  end
end

Instance Attribute Details

#ffmpeg_binaryObject

Returns the value of attribute ffmpeg_binary


24
25
26
# File 'lib/rvideo/inspector.rb', line 24

def ffmpeg_binary
  @ffmpeg_binary
end

#filenameObject (readonly)

Returns the value of attribute filename


22
23
24
# File 'lib/rvideo/inspector.rb', line 22

def filename
  @filename
end

#full_filenameObject (readonly)

Returns the value of attribute full_filename


22
23
24
# File 'lib/rvideo/inspector.rb', line 22

def full_filename
  @full_filename
end

#pathObject (readonly)

Returns the value of attribute path


22
23
24
# File 'lib/rvideo/inspector.rb', line 22

def path
  @path
end

#raw_metadataObject (readonly)

Returns the value of attribute raw_metadata


22
23
24
# File 'lib/rvideo/inspector.rb', line 22

def 
  @raw_metadata
end

#raw_responseObject (readonly)

Returns the value of attribute raw_response


22
23
24
# File 'lib/rvideo/inspector.rb', line 22

def raw_response
  @raw_response
end

Instance Method Details

#aspect_rotated?Boolean

Returns:

  • (Boolean)

416
417
418
# File 'lib/rvideo/inspector.rb', line 416

def aspect_rotated?
  video_orientation % 180 == 90
end

#audio?Boolean

Does the file have an audio stream?

Returns:

  • (Boolean)

101
102
103
# File 'lib/rvideo/inspector.rb', line 101

def audio?
  not audio_match.nil?
end

#audio_bit_rateObject


230
231
232
233
# File 'lib/rvideo/inspector.rb', line 230

def audio_bit_rate
  return nil unless audio?
  audio_match[7].to_i
end

#audio_bit_rate_unitsObject


235
236
237
238
# File 'lib/rvideo/inspector.rb', line 235

def audio_bit_rate_units
  return nil unless audio?
  audio_match[8]
end

#audio_bit_rate_with_unitsObject


240
241
242
# File 'lib/rvideo/inspector.rb', line 240

def audio_bit_rate_with_units
  "#{audio_bit_rate} #{audio_bit_rate_units}"
end

#audio_channelsObject


301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/rvideo/inspector.rb', line 301

def audio_channels
  return nil unless audio?
  
  case audio_match[5]
  when "mono"   then 1
  when "stereo" then 2
  when /(\d+) channels/ then $1.to_i
  when /^(\d).(\d)$/ then $1.to_i+$2.to_i
  else
    raise RuntimeError, "Unknown number of channels"
  end
end

#audio_channels_stringObject

The channels used in the audio stream.

Examples:

"stereo"
"mono"
"5:1"

296
297
298
299
# File 'lib/rvideo/inspector.rb', line 296

def audio_channels_string
  return nil unless audio?
  audio_match[5]
end

#audio_codecObject

The audio codec used.

Example:

"aac"

257
258
259
260
# File 'lib/rvideo/inspector.rb', line 257

def audio_codec
  return nil unless audio?
  audio_match[2]
end

#audio_sample_bit_depthObject

This should almost always return 16, as the vast majority of audio is 16 bit.


316
317
318
319
# File 'lib/rvideo/inspector.rb', line 316

def audio_sample_bit_depth
  return nil unless audio?
  audio_match[6].to_i
end

#audio_sample_rateObject

The sampling rate of the audio stream.

Example:

44100

268
269
270
271
# File 'lib/rvideo/inspector.rb', line 268

def audio_sample_rate
  return nil unless audio?
  audio_match[3].to_i
end

#audio_sample_rate_unitsObject Also known as: audio_sample_units

The units used for the sampling rate. May always be Hz.

Example:

"Hz"

279
280
281
282
# File 'lib/rvideo/inspector.rb', line 279

def audio_sample_rate_units
  return nil unless audio?
  audio_match[4]
end

#audio_sample_rate_with_unitsObject


285
286
287
# File 'lib/rvideo/inspector.rb', line 285

def audio_sample_rate_with_units
  "#{audio_sample_rate} #{audio_sample_rate_units}"
end

#audio_streamObject


244
245
246
247
248
249
# File 'lib/rvideo/inspector.rb', line 244

def audio_stream
  return nil unless valid?
  
  match = /\n\s*Stream.*Audio:.*\n/.match(@raw_response)
  match[0].strip if match
end

#audio_stream_idObject

The ID of the audio stream (useful for troubleshooting).

Example:

#0.1

326
327
328
329
# File 'lib/rvideo/inspector.rb', line 326

def audio_stream_id
  return nil unless audio?
  audio_match[1]
end

#bitrateObject

The bitrate of the movie.

Example:

3132

210
211
212
213
# File 'lib/rvideo/inspector.rb', line 210

def bitrate
  return nil unless valid?
  bitrate_match[1].to_i
end

#bitrate_unitsObject

The bitrate units used. In practice, this may always be kb/s.

Example:

"kb/s"

221
222
223
224
# File 'lib/rvideo/inspector.rb', line 221

def bitrate_units
  return nil unless valid?
  bitrate_match[2]
end

#bitrate_with_unitsObject


226
227
228
# File 'lib/rvideo/inspector.rb', line 226

def bitrate_with_units
  "#{bitrate} #{bitrate_units}"
end

#codec_time_baseObject


458
459
460
461
# File 'lib/rvideo/inspector.rb', line 458

def codec_time_base
  return nil unless video?
  video_match[15]
end

#containerObject

Returns the container format for the file. Instead of returning a single format, this may return a string of related formats.

Examples:

"avi"

"mov,mp4,m4a,3gp,3g2,mj2"

166
167
168
169
# File 'lib/rvideo/inspector.rb', line 166

def container
  return nil if @unknown_format
  /Input \#\d+\,\s*(\S+),\s*from/.match(@raw_metadata)[1]
end

#display_aspect_ratioObject


425
426
427
428
# File 'lib/rvideo/inspector.rb', line 425

def display_aspect_ratio
  return nil unless video?
  video_match[8]
end

#durationObject

The duration of the movie in milliseconds, as an integer.

Example:

24400         # 24.4 seconds

Note that the precision of the duration is in tenths of a second, not thousandths, but milliseconds are a more standard unit of time than deciseconds.


192
193
194
195
196
197
# File 'lib/rvideo/inspector.rb', line 192

def duration
  return nil unless valid?
  
  units = raw_duration.split(":")
  (units[0].to_i * 60 * 60 * 1000) + (units[1].to_i * 60 * 1000) + (units[2].to_f * 1000).to_i
end

#ffmpeg_buildObject

Returns the build description for ffmpeg.

Example:

built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build
  5250)

153
154
155
# File 'lib/rvideo/inspector.rb', line 153

def ffmpeg_build
  /(\n\s*)(built on.*)(\n)/.match(@raw_response)[2]
end

#ffmpeg_configurationObject

Returns the configuration options used to build ffmpeg.

Example:

--enable-mp3lame --enable-gpl --disable-ffplay --disable-ffserver
  --enable-a52 --enable-xvid

129
130
131
# File 'lib/rvideo/inspector.rb', line 129

def ffmpeg_configuration 
  /(\s*configuration:)(.*)\n/.match(@raw_response)[2].strip
end

#ffmpeg_libavObject

Returns the versions of libavutil, libavcodec, and libavformat used by ffmpeg.

Example:

libavutil version: 49.0.0
libavcodec version: 51.9.0
libavformat version: 50.4.0

142
143
144
# File 'lib/rvideo/inspector.rb', line 142

def ffmpeg_libav
  /^(\s*lib.*\n)+/.match(@raw_response)[0].split("\n").each {|l| l.strip! }
end

#ffmpeg_versionObject

Returns the version of ffmpeg used, In practice, this may or may not be useful.

Examples:

SVN-r6399
CVS

118
119
120
# File 'lib/rvideo/inspector.rb', line 118

def ffmpeg_version
  @ffmpeg_version = @raw_response.split("\n").first.split("version").last.split(",").first.strip
end

#fpsObject Also known as: framerate

The frame rate of the video in frames per second

Example:

"29.97"

447
448
449
450
# File 'lib/rvideo/inspector.rb', line 447

def fps
  return nil unless video?
  video_match[2] || video_match[13] || video_match[14]
end

#heightObject

The height of the video in pixels.


381
382
383
384
385
386
387
388
# File 'lib/rvideo/inspector.rb', line 381

def height
  return nil unless video?
  if aspect_rotated?
    video_match[5].to_i
  else
    video_match[6].to_i
  end
end

#initialize_with_file(file, ffmpeg_binary = nil) ⇒ Object


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rvideo/inspector.rb', line 55

def initialize_with_file(file, ffmpeg_binary = nil)
  if ffmpeg_binary
    @ffmpeg_binary = ffmpeg_binary
    if not FileTest.exist?(@ffmpeg_binary)
      raise "ffmpeg could not be found (trying #{@ffmpeg_binary})" 
    end
  else
    # assume it is in the unix path
    if not FileTest.exist?(`which ffmpeg`.chomp)
      raise "ffmpeg could not be found (expected ffmpeg to be found in the Unix path)"
    end
    @ffmpeg_binary = "ffmpeg"
  end
  
  if not FileTest.exist?(file.gsub('"',''))
    raise TranscoderError::InputFileNotFound, "File not found (#{file})"
  end
  
  @full_filename = file
  @filename      = File.basename(@full_filename)
  @path          = File.dirname(@full_filename)
  
  @raw_response = `#{@ffmpeg_binary} -i #{@full_filename.shell_quoted} 2>&1`
end

#initialize_with_raw_response(raw_response) ⇒ Object


51
52
53
# File 'lib/rvideo/inspector.rb', line 51

def initialize_with_raw_response(raw_response)
  @raw_response = raw_response
end

#invalid?Boolean

Returns false if the file can be read successfully. Returns false otherwise.

Returns:

  • (Boolean)

86
87
88
# File 'lib/rvideo/inspector.rb', line 86

def invalid?
  not valid?
end

#pixel_aspect_ratioObject


420
421
422
423
# File 'lib/rvideo/inspector.rb', line 420

def pixel_aspect_ratio
  return nil unless video?
  video_match[7]
end

#ratioObject


199
200
201
202
# File 'lib/rvideo/inspector.rb', line 199

def ratio
  return nil unless valid?
  width.to_f / height.to_f
end

#raw_durationObject

The duration of the movie, as a string.

Example:

"00:00:24.4"  # 24.4 seconds

177
178
179
180
# File 'lib/rvideo/inspector.rb', line 177

def raw_duration
  return nil unless valid?
  /Duration:\s*([0-9\:\.]+),/.match(@raw_metadata)[1]
end

#resolutionObject

width x height, as a string.

Examples:

320x240
1280x720

396
397
398
399
# File 'lib/rvideo/inspector.rb', line 396

def resolution
  return nil unless video?
  "#{width}x#{height}"
end

#rotated?Boolean

Returns:

  • (Boolean)

412
413
414
# File 'lib/rvideo/inspector.rb', line 412

def rotated?
  video_orientation != 0
end

#time_baseObject


453
454
455
456
# File 'lib/rvideo/inspector.rb', line 453

def time_base
  return nil unless video?
  video_match[14]
end

#unknown_format?Boolean

True if the format is not understood (“Unknown Format”)

Returns:

  • (Boolean)

91
92
93
# File 'lib/rvideo/inspector.rb', line 91

def unknown_format?
  @unknown_format ? true : false
end

#unreadable_file?Boolean

True if the file is not readable (“Duration: N/A, bitrate: N/A”)

Returns:

  • (Boolean)

96
97
98
# File 'lib/rvideo/inspector.rb', line 96

def unreadable_file?
  @unreadable_file ? true : false
end

#valid?Boolean

Returns true if the file can be read successfully. Returns false otherwise.

Returns:

  • (Boolean)

81
82
83
# File 'lib/rvideo/inspector.rb', line 81

def valid?
  not (@unknown_format or @unreadable_file)
end

#video?Boolean

Does the file have a video stream?

Returns:

  • (Boolean)

106
107
108
# File 'lib/rvideo/inspector.rb', line 106

def video?
  not video_match.nil?
end

#video_bit_rateObject

The portion of the overall bitrate the video is responsible for.


431
432
433
434
# File 'lib/rvideo/inspector.rb', line 431

def video_bit_rate
  return nil unless video?
  video_match[9]
end

#video_bit_rate_unitsObject


436
437
438
439
# File 'lib/rvideo/inspector.rb', line 436

def video_bit_rate_units
  return nil unless video?
  video_match[10]
end

#video_codecObject

The video codec used.

Example:

"mpeg4"

354
355
356
357
# File 'lib/rvideo/inspector.rb', line 354

def video_codec
  return nil unless video?
  video_match[3]
end

#video_colorspaceObject

The colorspace of the video stream.

Example:

"yuv420p"

365
366
367
368
# File 'lib/rvideo/inspector.rb', line 365

def video_colorspace
  return nil unless video?
  video_match[4]
end

#video_orientationObject


401
402
403
404
405
406
407
408
409
410
# File 'lib/rvideo/inspector.rb', line 401

def video_orientation
  stdout=''
  stderr=''
  open4.spawn "qtrotate #{full_filename}", :stdout=> stdout, :timeout => 10, :stderr => stderr
  @orientation ||= stdout.chomp.to_i
rescue Timeout::Error
  0
rescue
  0
end

#video_streamObject


331
332
333
334
335
336
# File 'lib/rvideo/inspector.rb', line 331

def video_stream
  return nil unless valid?
  
  match = /\n\s*Stream.*Video:.*\n/.match(@raw_response)
  match[0].strip unless match.nil?
end

#video_stream_idObject

The ID of the video stream (useful for troubleshooting).

Example:

#0.0

343
344
345
346
# File 'lib/rvideo/inspector.rb', line 343

def video_stream_id
  return nil unless video?
  video_match[1]
end

#widthObject

The width of the video in pixels.


371
372
373
374
375
376
377
378
# File 'lib/rvideo/inspector.rb', line 371

def width
  return nil unless video?
  if aspect_rotated?
    video_match[6].to_i
  else
    video_match[5].to_i
  end
end