uploads: add support for uploading .avif files.

Features of AVIF include:

* Lossless and lossy compression.
* High dynamic range (HDR) images
* Wide color gamut images (i.e. 10- and 12-bit color depths)
* Transparency (through alpha planes).
* Animations (with an optional cover image).
* Auxiliary image sequences, where the file contains a single primary
  image and a short secondary video, like Apple's Live Photos.
* Metadata rotation, mirroring, and cropping.

The AVIF format is still relatively new and some of these features aren't well
supported by browsers or other software:

* Animated AVIFs aren't supported by Firefox or by libvips.
* HDR images aren't supported by Firefox.
* Rotated, mirrored, and cropped AVIFs aren't supported by Firefox or Chrome.
* Image grids, where the file contains multiple images that are tiled
  together into one big image, aren't supported by Firefox.
* AVIF as a whole has only been supported for a year or two by Chrome
  and Firefox, and less than a year by Safari.

For these reasons, only basic AVIFs that don't use animation, rotation,
cropping, or image grids can be uploaded.
This commit is contained in:
evazion
2022-10-24 19:10:57 -05:00
parent 420ff2f2f5
commit c96d60a840
26 changed files with 197 additions and 15 deletions

View File

@@ -32,13 +32,17 @@ class FFmpeg
end
# Get file metadata using ffprobe.
#
# @see https://ffmpeg.org/ffprobe.html
# @see https://gist.github.com/nrk/2286511
# @return [Hash] a hash of the file's metadata
#
# @return [Hash] A hash of the file's metadata. Will be empty if reading the file failed for any reason.
def metadata
output = shell!("ffprobe -v quiet -print_format json -show_format -show_streams #{file.path.shellescape}")
json = JSON.parse(output)
json.with_indifferent_access
rescue Error
{}
end
def width
@@ -64,7 +68,7 @@ class FFmpeg
# @return [Integer, nil] The number of frames in the video or animation, or nil if unknown.
def frame_count
if video_streams.first.has_key?(:nb_frames)
if video_streams.first&.has_key?(:nb_frames)
video_streams.first[:nb_frames].to_i
elsif playback_info.has_key?(:frame)
playback_info[:frame].to_i
@@ -80,11 +84,11 @@ class FFmpeg
end
def video_streams
metadata[:streams].select { |stream| stream[:codec_type] == "video" }
metadata[:streams].to_a.select { |stream| stream[:codec_type] == "video" }
end
def audio_streams
metadata[:streams].select { |stream| stream[:codec_type] == "audio" }
metadata[:streams].to_a.select { |stream| stream[:codec_type] == "audio" }
end
def has_audio?