class FFMPEG::Movie

Attributes

audio_bitrate[R]
audio_channels[R]
audio_codec[R]
audio_sample_rate[R]
audio_stream[R]
bitrate[R]
colorspace[R]
container[R]
creation_time[R]
dar[R]
duration[R]
frame_rate[R]
height[R]
path[R]
rotation[R]
sar[R]
time[R]
video_bitrate[R]
video_codec[R]
video_stream[R]
width[R]

Public Class Methods

new(path) click to toggle source
# File lib/ffmpeg/movie.rb, line 11
def initialize(path)
  raise Errno::ENOENT, "the file '#{path}' does not exist" unless File.exist?(path)

  @path = path

  # ffmpeg will output to stderr
  command = "#{FFMPEG.ffprobe_binary} -i #{Shellwords.escape(path)} -print_format json -show_format -show_streams -show_error"
  std_output = ''
  std_error = ''

  Open3.popen3(command) do |stdin, stdout, stderr|
    std_output = stdout.read unless stdout.nil?
    std_error = stderr.read unless stderr.nil?
  end

  fix_encoding(std_output)

  metadata = MultiJson.load(std_output, symbolize_keys: true)

  if metadata.key?(:error)

    @duration = 0

  else

    video_streams = metadata[:streams].select { |stream| stream.key?(:codec_type) and stream[:codec_type] === 'video' }
    audio_streams = metadata[:streams].select { |stream| stream.key?(:codec_type) and stream[:codec_type] === 'audio' }

    @container = metadata[:format][:format_name]

    @duration = metadata[:format][:duration].to_f

    @time = metadata[:format][:start_time].to_f

    @creation_time = if metadata[:format].key?(:tags) and metadata[:format][:tags].key?(:creation_time)
                       Time.parse(metadata[:format][:tags][:creation_time])
                     else
                       nil
                     end

    @bitrate = metadata[:format][:bit_rate].to_i

    unless video_streams.empty?
      # TODO: Handle multiple video codecs (is that possible?)
      video_stream = video_streams.first
      @video_codec = video_stream[:codec_name]
      @colorspace = video_stream[:pix_fmt]
      @width = video_stream[:width]
      @height = video_stream[:height]
      @video_bitrate = video_stream[:bit_rate].to_i
      @sar = video_stream[:sample_aspect_ratio]
      @dar = video_stream[:display_aspect_ratio]

      @frame_rate = unless video_stream[:avg_frame_rate] == '0/0'
                      Rational(video_stream[:avg_frame_rate])
                    else
                      nil
                    end

      @video_stream = "#{video_stream[:codec_name]} (#{video_stream[:profile]}) (#{video_stream[:codec_tag_string]} / #{video_stream[:codec_tag]}), #{colorspace}, #{resolution} [SAR #{sar} DAR #{dar}]"

      @rotation = if video_stream.key?(:tags) and video_stream[:tags].key?(:rotate)
                    video_stream[:tags][:rotate].to_i
                  else
                    nil
                  end
    end

    unless audio_streams.empty?
      # TODO: Handle multiple audio codecs
      audio_stream = audio_streams.first
      @audio_channels = audio_stream[:channels].to_i
      @audio_codec = audio_stream[:codec_name]
      @audio_sample_rate = audio_stream[:sample_rate].to_i
      @audio_bitrate = audio_stream[:bit_rate].to_i
      @audio_channel_layout = audio_stream[:channel_layout]
      @audio_stream = "#{audio_codec} (#{audio_stream[:codec_tag_string]} / #{audio_stream[:codec_tag]}), #{audio_sample_rate} Hz, #{audio_channel_layout}, #{audio_stream[:sample_fmt]}, #{audio_bitrate} bit/s"
    end

  end

  @invalid = true if metadata.key?(:error)
  @invalid = true if std_error.include?("Unsupported codec")
  @invalid = true if std_error.include?("is not supported")
  @invalid = true if std_error.include?("could not find codec parameters")
end

Public Instance Methods

audio_channel_layout() click to toggle source
# File lib/ffmpeg/movie.rb, line 128
def audio_channel_layout
  # TODO Whenever support for ffmpeg/ffprobe 1.2.1 is dropped this is no longer needed
  @audio_channel_layout || case(audio_channels)
                             when 1
                               'stereo'
                             when 2
                               'stereo'
                             when 6
                               '5.1'
                             else
                               'unknown'
                           end
end
calculated_aspect_ratio() click to toggle source
# File lib/ffmpeg/movie.rb, line 116
def calculated_aspect_ratio
  aspect_from_dar || aspect_from_dimensions
end
calculated_pixel_aspect_ratio() click to toggle source
# File lib/ffmpeg/movie.rb, line 120
def calculated_pixel_aspect_ratio
  aspect_from_sar || 1
end
resolution() click to toggle source
# File lib/ffmpeg/movie.rb, line 110
def resolution
  unless width.nil? or height.nil?
    "#{width}x#{height}"
  end
end
screenshot(output_file, options = EncodingOptions.new, transcoder_options = {}, &block) click to toggle source
# File lib/ffmpeg/movie.rb, line 146
def screenshot(output_file, options = EncodingOptions.new, transcoder_options = {}, &block)
  Transcoder.new(self, output_file, options.merge(screenshot: true), transcoder_options).run &block
end
size() click to toggle source
# File lib/ffmpeg/movie.rb, line 124
def size
  File.size(@path)
end
transcode(output_file, options = EncodingOptions.new, transcoder_options = {}, &block) click to toggle source
# File lib/ffmpeg/movie.rb, line 142
def transcode(output_file, options = EncodingOptions.new, transcoder_options = {}, &block)
  Transcoder.new(self, output_file, options, transcoder_options).run &block
end
valid?() click to toggle source
# File lib/ffmpeg/movie.rb, line 98
def valid?
  not @invalid
end

Protected Instance Methods

aspect_from_dar() click to toggle source
# File lib/ffmpeg/movie.rb, line 151
def aspect_from_dar
  return nil unless dar
  w, h = dar.split(":")
  aspect = w.to_f / h.to_f
  aspect.zero? ? nil : aspect
end
aspect_from_dimensions() click to toggle source
# File lib/ffmpeg/movie.rb, line 165
def aspect_from_dimensions
  aspect = width.to_f / height.to_f
  aspect.nan? ? nil : aspect
end
aspect_from_sar() click to toggle source
# File lib/ffmpeg/movie.rb, line 158
def aspect_from_sar
  return nil unless sar
  w, h = sar.split(":")
  aspect = w.to_f / h.to_f
  aspect.zero? ? nil : aspect
end
fix_encoding(output) click to toggle source
# File lib/ffmpeg/movie.rb, line 170
def fix_encoding(output)
  output[/test/] # Running a regexp on the string throws error if it's not UTF-8
rescue ArgumentError
  output.force_encoding("ISO-8859-1")
end