forked from asonix/mastodon
Video files with variable framerates are converted to constant framerate videos and the output framerate picked by ffmpeg is based on the original file's container framerate (which can be different from the average framerate). This means that an input video with variable framerate with about 30 frames per second on average, but a maximum of 120 fps will be converted to a constant 120 fps file, which won't be processed by other Mastodon servers. This commit changes it so that input files with VFR and a maximum framerate above the framerate threshold are converted to VFR files with the maximum frame rate enforced.
55 lines
1.8 KiB
Ruby
55 lines
1.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class VideoMetadataExtractor
|
|
attr_reader :duration, :bitrate, :video_codec, :audio_codec,
|
|
:colorspace, :width, :height, :frame_rate, :r_frame_rate
|
|
|
|
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
|
|
raise Paperclip::Errors::CommandNotFoundError, 'Could not run the `ffprobe` command. Please install ffmpeg.'
|
|
end
|
|
|
|
def valid?
|
|
!@invalid
|
|
end
|
|
|
|
private
|
|
|
|
def ffmpeg_command_output
|
|
command = Terrapin::CommandLine.new('ffprobe', '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel')
|
|
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]
|
|
@frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
|
|
@r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate])
|
|
end
|
|
|
|
if (audio_stream = audio_streams.first)
|
|
@audio_codec = audio_stream[:codec_name]
|
|
end
|
|
end
|
|
|
|
@invalid = true if @metadata.key?(:error)
|
|
end
|
|
end
|