class ActiveStorage::Analyzer::VideoAnalyzer
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).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
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 25 def self.accept?(blob) blob.video? end
Public Instance Methods
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 29 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
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 55 def angle if tags["rotate"] Integer(tags["rotate"]) elsif display_matrix && display_matrix["rotation"] Integer(display_matrix["rotation"]) end end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 82 def audio? audio_stream.present? end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 120 def audio_stream @audio_stream ||= streams.detect { |stream| stream["codec_type"] == "audio" } || {} end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 90 def computed_height if encoded_width && display_height_scale encoded_width * display_height_scale end end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 128 def container probe["format"] || {} end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 67 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
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 104 def display_height_scale @display_height_scale ||= Float(display_aspect_ratio.last) / display_aspect_ratio.first if display_aspect_ratio end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 63 def display_matrix side_data.detect { |data| data["side_data_type"] == "Display Matrix" } end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 50 def duration duration = video_stream["duration"] || container["duration"] Float(duration) if duration end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 100 def encoded_height @encoded_height ||= Float(video_stream["height"]) if video_stream["height"] end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 96 def encoded_width @encoded_width ||= Float(video_stream["width"]) if video_stream["width"] end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 153 def ffprobe_path ActiveStorage.paths[:ffprobe] || "ffprobe" end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 42 def height if rotated? encoded_width else computed_height || encoded_height end end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 132 def probe @probe ||= download_blob_to_tempfile { |file| probe_from(file) } end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 136 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
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 78 def rotated? angle == 90 || angle == 270 || angle == -90 || angle == -270 end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 112 def side_data @side_data ||= video_stream["side_data_list"] || {} end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 124 def streams probe["streams"] || [] end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 86 def video? video_stream.present? end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 116 def video_stream @video_stream ||= streams.detect { |stream| stream["codec_type"] == "video" } || {} end
Source
# File lib/active_storage/analyzer/video_analyzer.rb, line 34 def width if rotated? computed_height || encoded_height else encoded_width end end