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 theFFmpeg 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 30

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 87

def audio?
  audio_stream.present?
end

#rotated? ⇒ Boolean (readonly, private)

[ GitHub ]

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

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 91

def video?
  video_stream.present?
end

Instance Method Details

#angle (private)

[ GitHub ]

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

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 125

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 95

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 133

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

#display_aspect_ratio (private)

[ GitHub ]

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

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 109

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 68

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 55

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 105

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 101

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 159

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

#height (private)

[ GitHub ]

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

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 34

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 137

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 141

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, allow_duplicate_key: true)
    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 117

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

#streams (private)

[ GitHub ]

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

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

#tags (private)

[ GitHub ]

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

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

#video_stream (private)

[ GitHub ]

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

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 39

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