2021-05-05 19:44:01 +02:00
# frozen_string_literal: true
class VideoMetadataExtractor
attr_reader :duration , :bitrate , :video_codec , :audio_codec ,
2022-02-22 17:11:22 +01:00
:colorspace , :width , :height , :frame_rate , :r_frame_rate
2021-05-05 19:44:01 +02:00
def initialize ( path )
@path = path
@metadata = Oj . load ( ffmpeg_command_output , mode : :strict , symbol_keys : true )
parse_metadata
rescue Terrapin :: ExitStatusError , Oj :: ParseError
@invalid = true
rescue Terrapin :: CommandNotFoundError
2025-03-04 14:28:33 +01:00
raise Paperclip :: Errors :: CommandNotFoundError , 'Could not run the `ffprobe` command. Please install ffmpeg.' # rubocop:disable I18n/RailsI18n/DecorateString -- This error is not user-facing
2021-05-05 19:44:01 +02:00
end
def valid?
! @invalid
end
private
def ffmpeg_command_output
2024-04-22 04:00:24 -05:00
command = Terrapin :: CommandLine . new ( Rails . configuration . x . ffprobe_binary , '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel' )
2021-05-05 19:44:01 +02:00
command . run ( path : @path , format : 'json' , loglevel : 'fatal' )
end
def parse_metadata
if @metadata . key? ( :format )
@duration = @metadata [ :format ] [ :duration ] . to_f
@bitrate = @metadata [ :format ] [ :bit_rate ] . to_i
end
if @metadata . key? ( :streams )
video_streams = @metadata [ :streams ] . select { | stream | stream [ :codec_type ] == 'video' }
audio_streams = @metadata [ :streams ] . select { | stream | stream [ :codec_type ] == 'audio' }
if ( video_stream = video_streams . first )
@video_codec = video_stream [ :codec_name ]
@colorspace = video_stream [ :pix_fmt ]
@width = video_stream [ :width ]
@height = video_stream [ :height ]
2024-06-07 19:42:43 +02:00
@frame_rate = parse_framerate ( video_stream [ :avg_frame_rate ] )
@r_frame_rate = parse_framerate ( video_stream [ :r_frame_rate ] )
2023-08-18 08:32:47 +02:00
# For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we
# should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue.
@frame_rate || = @r_frame_rate
2024-11-21 11:58:04 +01:00
# If the video has not been re-encoded by ffmpeg, it may contain rotation information,
# and we need to simulate applying it to the dimensions
2024-12-10 23:47:42 -05:00
@width , @height = @height , @width if video_stream [ :side_data_list ] & . any? { | x | x [ :rotation ] & . abs == 90 }
2021-05-05 19:44:01 +02:00
end
if ( audio_stream = audio_streams . first )
@audio_codec = audio_stream [ :codec_name ]
end
end
@invalid = true if @metadata . key? ( :error )
end
2024-06-07 19:42:43 +02:00
def parse_framerate ( raw )
Rational ( raw )
rescue ZeroDivisionError
nil
end
2021-05-05 19:44:01 +02:00
end