Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize videos larger than max allowed resolution #34

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ GEM
reline (>= 0.3.8)
jmespath (1.6.2)
json (2.6.3)
json-canonicalization (0.3.2)
json-canonicalization (0.4.0)
json-jwt (1.15.3)
activesupport (>= 4.2)
aes_key_wrap
Expand Down
4 changes: 3 additions & 1 deletion app/models/concerns/attachmentable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ module Attachmentable
extend ActiveSupport::Concern

MAX_MATRIX_LIMIT = 33_177_600 # 7680x4320px or approx. 847MB in RAM
GIF_MATRIX_LIMIT = 921_600 # 1280x720px
MAX_GIF_WIDTH = 3840
MAX_GIF_HEIGHT = 2160
GIF_MATRIX_LIMIT = MAX_GIF_WIDTH * MAX_GIF_HEIGHT

# For some file extensions, there exist different content
# type variants, and browsers often send the wrong one,
Expand Down
11 changes: 8 additions & 3 deletions app/models/media_attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ class MediaAttachment < ApplicationRecord
IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 16.megabytes).to_i
VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 99.megabytes).to_i

MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px
MAX_VIDEO_WIDTH = 3840
MAX_VIDEO_HEIGHT = 2160
MAX_VIDEO_MATRIX_LIMIT = MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT # 3840x2160px
MAX_VIDEO_FRAME_RATE = 120
MAX_VIDEO_FRAMES = 36_000 # Approx. 5 minutes at 120 fps

Expand Down Expand Up @@ -102,7 +104,9 @@ class MediaAttachment < ApplicationRecord
'preset' => 'veryfast',
'movflags' => 'faststart', # Move metadata to start of file so playback can begin before download finishes
'pix_fmt' => 'yuv420p', # Ensure color space for cross-browser compatibility
'vf' => 'crop=floor(iw/2)*2:floor(ih/2)*2', # h264 requires width and height to be even. Crop instead of scale to avoid blurring
# scale videos if larger than maximum width or height, keeping same size otherwise. then,
# h264 requires width and height to be even. Crop instead of scale to avoid blurring
'vf' => "scale=iw*min(1\\,min(#{MAX_VIDEO_WIDTH}/iw\\,#{MAX_VIDEO_HEIGHT}/ih)):-1,crop=floor(iw/2)*2:floor(ih/2)*2",
'c:v' => 'h264',
'c:a' => 'aac',
'b:a' => '192k',
Expand Down Expand Up @@ -348,7 +352,8 @@ def check_video_dimensions
return unless movie.valid?

raise Mastodon::StreamValidationError, 'Video has no video stream' if movie.width.nil? || movie.frame_rate.nil?
raise Mastodon::DimensionsValidationError, "#{movie.width}x#{movie.height} videos are not supported" if movie.width * movie.height > MAX_VIDEO_MATRIX_LIMIT
# Only reject if more than 1.5x max allowed size to reject resource overuse attacks, otherwise rescale
raise Mastodon::DimensionsValidationError, "#{movie.width}x#{movie.height} videos are not supported" if movie.width * movie.height > MAX_VIDEO_MATRIX_LIMIT * 1.5
raise Mastodon::DimensionsValidationError, "#{movie.frame_rate.floor}fps videos are not supported" if movie.frame_rate.floor > MAX_VIDEO_FRAME_RATE
end

Expand Down