posts: fix calculation of animated PNG duration.

Fix certain animated PNGs returning NaN as the duration because the
frame rate was being reported as "0/0" by FFMpeg. This happens when the
animation has zero delay between frames. This is supposed to mean a PNG
with an infinitely fast frame rate, but in practice browsers limit it to
around 10FPS. The exact frame rate browsers will use is unknown and
implementation defined.
This commit is contained in:
evazion
2021-10-06 21:00:09 -05:00
parent f6a6289c8d
commit 2595f18b2f
4 changed files with 24 additions and 3 deletions

View File

@@ -59,10 +59,18 @@ class FFmpeg
end
end
# @return [Float, nil] The frame rate of the video or animation, or nil if
# unknown. The frame rate can be unknown for animated PNGs that have zero
# delay between frames.
def frame_rate
rate = video_streams.first[:avg_frame_rate] # "100/57"
numerator, denominator = rate.split("/")
(numerator.to_f / denominator.to_f)
if numerator.to_f == 0 || denominator.to_f == 0
nil
else
(numerator.to_f / denominator.to_f)
end
end
def video_streams

View File

@@ -61,8 +61,9 @@ class MediaFile::Image < MediaFile
if is_animated_gif?
frame_count / duration
elsif is_animated_png?
# XXX we have to resort to ffprobe to get the frame rate because libvips and exiftool can't get it.
video.frame_rate
# XXX As with GIFs, animated PNGs can have an unspecified frame rate.
# Assume 10FPS if the frame rate is unspecified.
video.frame_rate.presence || 10.0
else
nil
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@@ -263,6 +263,18 @@ class MediaFileTest < ActiveSupport::TestCase
end
end
context "that is animated but with an unspecified frame rate" do
should "have an assumed frame rate of 10FPS" do
file = MediaFile.open("test/files/test-animated-inf-fps.png")
assert_equal(false, file.is_corrupt?)
assert_equal(true, file.is_animated?)
assert_equal(0.2, file.duration)
assert_equal(2, file.frame_count)
assert_equal(10, file.frame_rate)
end
end
context "that is animated but malformed" do
should "be handled correctly" do
file = MediaFile.open("test/files/apng/iend_missing.png")