forked from mastodon/mastodon
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix media processing getting stuck on too much stdin/stderr (mastodon…
…#16136) * Fix media processing getting stuck on too much stdin/stderr See thoughtbot/terrapin#5 * Remove dependency on paperclip-av-transcoder gem * Remove dependency on streamio-ffmpeg gem * Disable stdin on ffmpeg process
- Loading branch information
1 parent
c858d8d
commit ab73e7e
Showing
12 changed files
with
234 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# frozen_string_literal: true | ||
|
||
class VideoMetadataExtractor | ||
attr_reader :duration, :bitrate, :video_codec, :audio_codec, | ||
:colorspace, :width, :height, :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]) | ||
end | ||
|
||
if (audio_stream = audio_streams.first) | ||
@audio_codec = audio_stream[:codec_name] | ||
end | ||
end | ||
|
||
@invalid = true if @metadata.key?(:error) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# frozen_string_literal: true | ||
|
||
module Paperclip | ||
# This transcoder is only to be used for the MediaAttachment model | ||
# to check when uploaded videos are actually gifv's | ||
class Transcoder < Paperclip::Processor | ||
def initialize(file, options = {}, attachment = nil) | ||
super | ||
|
||
@current_format = File.extname(@file.path) | ||
@basename = File.basename(@file.path, @current_format) | ||
@format = options[:format] | ||
@time = options[:time] || 3 | ||
@passthrough_options = options[:passthrough_options] | ||
@convert_options = options[:convert_options].dup | ||
end | ||
|
||
def make | ||
metadata = VideoMetadataExtractor.new(@file.path) | ||
|
||
unless metadata.valid? | ||
log("Unsupported file #{@file.path}") | ||
return File.open(@file.path) | ||
end | ||
|
||
update_attachment_type(metadata) | ||
update_options_from_metadata(metadata) | ||
|
||
destination = Tempfile.new([@basename, @format ? ".#{@format}" : '']) | ||
destination.binmode | ||
|
||
@output_options = @convert_options[:output]&.dup || {} | ||
@input_options = @convert_options[:input]&.dup || {} | ||
|
||
case @format.to_s | ||
when /jpg$/, /jpeg$/, /png$/, /gif$/ | ||
@input_options['ss'] = @time | ||
|
||
@output_options['f'] = 'image2' | ||
@output_options['vframes'] = 1 | ||
when 'mp4' | ||
@output_options['acodec'] = 'aac' | ||
@output_options['strict'] = 'experimental' | ||
end | ||
|
||
command_arguments, interpolations = prepare_command(destination) | ||
|
||
begin | ||
command = Terrapin::CommandLine.new('ffmpeg', command_arguments.join(' '), logger: Paperclip.logger) | ||
command.run(interpolations) | ||
rescue Terrapin::ExitStatusError => e | ||
raise Paperclip::Error, "Error while transcoding #{@basename}: #{e}" | ||
rescue Terrapin::CommandNotFoundError | ||
raise Paperclip::Errors::CommandNotFoundError, 'Could not run the `ffmpeg` command. Please install ffmpeg.' | ||
end | ||
|
||
destination | ||
end | ||
|
||
private | ||
|
||
def prepare_command(destination) | ||
command_arguments = ['-nostdin'] | ||
interpolations = {} | ||
interpolation_keys = 0 | ||
|
||
@input_options.each_pair do |key, value| | ||
interpolation_key = interpolation_keys | ||
command_arguments << "-#{key} :#{interpolation_key}" | ||
interpolations[interpolation_key] = value | ||
interpolation_keys += 1 | ||
end | ||
|
||
command_arguments << '-i :source' | ||
interpolations[:source] = @file.path | ||
|
||
@output_options.each_pair do |key, value| | ||
interpolation_key = interpolation_keys | ||
command_arguments << "-#{key} :#{interpolation_key}" | ||
interpolations[interpolation_key] = value | ||
interpolation_keys += 1 | ||
end | ||
|
||
command_arguments << '-y :destination' | ||
interpolations[:destination] = destination.path | ||
|
||
[command_arguments, interpolations] | ||
end | ||
|
||
def update_options_from_metadata(metadata) | ||
return unless @passthrough_options && @passthrough_options[:video_codecs].include?(metadata.video_codec) && @passthrough_options[:audio_codecs].include?(metadata.audio_codec) && @passthrough_options[:colorspaces].include?(metadata.colorspace) | ||
|
||
@format = @passthrough_options[:options][:format] || @format | ||
@time = @passthrough_options[:options][:time] || @time | ||
@convert_options = @passthrough_options[:options][:convert_options].dup | ||
end | ||
|
||
def update_attachment_type(metadata) | ||
@attachment.instance.type = MediaAttachment.types[:gifv] unless metadata.audio_codec | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.