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 31

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 88

def audio?
  audio_stream.present?
end

#rotated? ⇒ Boolean (readonly, private)

[ GitHub ]

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

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 92

def video?
  video_stream.present?
end

Instance Method Details

#angle (private)

[ GitHub ]

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

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 126

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 96

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 134

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

#display_aspect_ratio (private)

[ GitHub ]

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

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 110

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 69

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 56

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 106

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 102

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 160

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

#height (private)

[ GitHub ]

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

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 35

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 138

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 142

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 118

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

#streams (private)

[ GitHub ]

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

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

#tags (private)

[ GitHub ]

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

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

#video_stream (private)

[ GitHub ]

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

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 40

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