class ActiveStorage::Analyzer::VideoAnalyzer
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).metadata # => { 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.
Public Class Methods
accept?(blob)
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 23 def self.accept?(blob) blob.video? end
Public Instance Methods
metadata()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 27 def metadata { width: width, height: height, duration: duration, angle: angle, display_aspect_ratio: display_aspect_ratio, audio: audio?, video: video? }.compact end
Private Instance Methods
angle()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 53 def angle if tags["rotate"] Integer(tags["rotate"]) elsif side_data && side_data[0] && side_data[0]["rotation"] Integer(side_data[0]["rotation"]) end end
audio?()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 76 def audio? audio_stream.present? end
audio_stream()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 114 def audio_stream @audio_stream ||= streams.detect { |stream| stream["codec_type"] == "audio" } || {} end
computed_height()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 84 def computed_height if encoded_width && display_height_scale encoded_width * display_height_scale end end
container()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 122 def container probe["format"] || {} end
display_aspect_ratio()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 61 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()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 98 def display_height_scale @display_height_scale ||= Float(display_aspect_ratio.last) / display_aspect_ratio.first if display_aspect_ratio end
duration()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 48 def duration duration = video_stream["duration"] || container["duration"] Float(duration) if duration end
encoded_height()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 94 def encoded_height @encoded_height ||= Float(video_stream["height"]) if video_stream["height"] end
encoded_width()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 90 def encoded_width @encoded_width ||= Float(video_stream["width"]) if video_stream["width"] end
ffprobe_path()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 147 def ffprobe_path ActiveStorage.paths[:ffprobe] || "ffprobe" end
height()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 40 def height if rotated? encoded_width else computed_height || encoded_height end end
probe()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 126 def probe @probe ||= download_blob_to_tempfile { |file| probe_from(file) } end
probe_from(file)
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 130 def probe_from(file) instrument(File.basename(ffprobe_path)) do IO.popen([ ffprobe_path, "-print_format", "json", "-show_streams", "-show_format", "-v", "error", file.path ]) do |output| JSON.parse(output.read) end end rescue Errno::ENOENT logger.info "Skipping video analysis because ffprobe isn't installed" {} end
rotated?()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 72 def rotated? angle == 90 || angle == 270 || angle == -90 || angle == -270 end
side_data()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 106 def side_data @side_data ||= video_stream["side_data_list"] || {} end
streams()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 118 def streams probe["streams"] || [] end
video?()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 80 def video? video_stream.present? end
video_stream()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 110 def video_stream @video_stream ||= streams.detect { |stream| stream["codec_type"] == "video" } || {} end
width()
click to toggle source
# File lib/active_storage/analyzer/video_analyzer.rb, line 32 def width if rotated? computed_height || encoded_height else encoded_width end end