123456789_123456789_123456789_123456789_123456789_

Class: ActiveStorage::Analyzer::VideoAnalyzer

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Analyzer
Instance Chain:
self, Analyzer
Inherits: Analyzer
  • ::Object
Defined in: activestorage/lib/active_storage/analyzer/video_analyzer.rb

Overview

Active Storage Video Analyzer

Extracts the following from a video blob:

  • Width (pixels)
  • Height (pixels)
  • Duration (seconds)
  • Angle (degrees)
  • Display aspect ratio
  • Audio (true if file has an audio channel, false if not)
  • Video (true if file has an video channel, false if not)

Example:

ActiveStorage::Analyzer::VideoAnalyzer.new(blob).
# => { width: 640.0, height: 480.0, duration: 5.0, angle: 0, display_aspect_ratio: [4, 3], audio: true, video: true }

When a video's angle is 90, -90, 270 or -270 degrees, its width and height are automatically swapped for convenience.

This analyzer requires the FFmpeg system library, which is not provided by Rails.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Class Method Details

.accept?(blob) ⇒ Boolean

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 27

def self.accept?(blob)
  blob.video?
end

Instance Attribute Details

#audio?Boolean (readonly, private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 84

def audio?
  audio_stream.present?
end

#rotated?Boolean (readonly, private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 80

def rotated?
  angle == 90 || angle == 270 || angle == -90 || angle == -270
end

#video?Boolean (readonly, private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 88

def video?
  video_stream.present?
end

Instance Method Details

#angle (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 57

def angle
  if tags["rotate"]
    Integer(tags["rotate"])
  elsif display_matrix && display_matrix["rotation"]
    Integer(display_matrix["rotation"])
  end
end

#audio_stream (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 122

def audio_stream
  @audio_stream ||= streams.detect { |stream| stream["codec_type"] == "audio" } || {}
end

#computed_height (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 92

def computed_height
  if encoded_width && display_height_scale
    encoded_width * display_height_scale
  end
end

#container (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 130

def container
  probe["format"] || {}
end

#display_aspect_ratio (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 69

def display_aspect_ratio
  if descriptor = video_stream["display_aspect_ratio"]
    if terms = descriptor.split(":", 2)
      numerator   = Integer(terms[0])
      denominator = Integer(terms[1])

      [numerator, denominator] unless numerator == 0
    end
  end
end

#display_height_scale (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 106

def display_height_scale
  @display_height_scale ||= Float(display_aspect_ratio.last) / display_aspect_ratio.first if display_aspect_ratio
end

#display_matrix (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 65

def display_matrix
  side_data.detect { |data| data["side_data_type"] == "Display Matrix" }
end

#duration (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 52

def duration
  duration = video_stream["duration"] || container["duration"]
  Float(duration) if duration
end

#encoded_height (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 102

def encoded_height
  @encoded_height ||= Float(video_stream["height"]) if video_stream["height"]
end

#encoded_width (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 98

def encoded_width
  @encoded_width ||= Float(video_stream["width"]) if video_stream["width"]
end

#ffprobe_path (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 156

def ffprobe_path
  ActiveStorage.paths[:ffprobe] || "ffprobe"
end

#height (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 44

def height
  if rotated?
    encoded_width
  else
    computed_height || encoded_height
  end
end

#metadata

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 31

def 
  { width: width, height: height, duration: duration, angle: angle, display_aspect_ratio: display_aspect_ratio, audio: audio?, video: video? }.compact
end

#probe (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 134

def probe
  @probe ||= download_blob_to_tempfile { |file| probe_from(file) }
end

#probe_from(file) (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 138

def probe_from(file)
  instrument(File.basename(ffprobe_path)) do
    IO.popen([ ffprobe_path,
      "-print_format", "json",
      "-show_streams",
      "-show_format",
      "-v", "error",
      *Shellwords.split(ActiveStorage.ffprobe_arguments),
      file.path
    ]) do |output|
      JSON.parse(output.read)
    end
  end
rescue Errno::ENOENT
  logger.info "Skipping video analysis because ffprobe isn't installed"
  {}
end

#side_data (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 114

def side_data
  @side_data ||= video_stream["side_data_list"] || {}
end

#streams (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 126

def streams
  probe["streams"] || []
end

#tags (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 110

def tags
  @tags ||= video_stream["tags"] || {}
end

#video_stream (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 118

def video_stream
  @video_stream ||= streams.detect { |stream| stream["codec_type"] == "video" } || {}
end

#width (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/analyzer/video_analyzer.rb', line 36

def width
  if rotated?
    computed_height || encoded_height
  else
    encoded_width
  end
end