fdintino / pillow-avif-plugin

A pillow plugin that adds avif support via libavif
BSD 2-Clause "Simplified" License
77 stars 12 forks source link

Sequences do not retain proper FPS #14

Closed cdgriffith closed 1 year ago

cdgriffith commented 1 year ago

Very quick example:

Source incoming

import pillow_avif
from PIL import Image

image = Image.open("incoming.gif")
image.seek(0)
image.save("out.avif", save_all=True)
image.save("out.gif", save_all=True)

The GIF renders at the proper speed: out

The AVIF is warp speed (converted to mp4 to show in line, original attached as zip example_avif.zip):

https://user-images.githubusercontent.com/3275435/183226578-418915fd-4ec2-4791-b44b-157a35007a0c.mp4

With a GIF as the source it's not the end of the world, because the EXIF has a 'duration' set. However AVIF files do not, so going from one animated sequence to another, there is no way I see to calculate the speed properly to provide duration to the .save function manually.

fdintino commented 1 year ago

I wonder if this is a flaw in whatever you're using to view / decode the AVIF images. Because when I load the file you attached in the zip file, it plays at the expected FPS.

cdgriffith commented 1 year ago

I have been using mpv to view them on windows. Looking at the stream properties with ffprobe I can see the issue is that the fps is being set to a thousand frames per second!

Stream #0:0[0x1](und): Video: av1 (Main) (av01 / 0x31307661), yuv420p(pc, smpte170m/bt709/iec61966-2-1), 256x192 [SAR 1:1 DAR 4:3], 45490 kb/s, 1k fps, 1k tbr, 1k tbn (default)
    Metadata:
      creation_time   : 2022-08-06T00:35:18.000000Z
      handler_name    : libavif
      encoder         : AOM Coding

Compare to a AVIF generated with ffmpeg (library libsvtav1):

  Stream #0:0[0x1](und): Video: av1 (Main) (av01 / 0x31307661), yuv420p(tv, progressive), 256x192 [SAR 1:1 DAR 4:3], 91 kb/s, 20 fps, 20 tbr, 10240 tbn (default)
    Metadata:
      handler_name    : PictureHandler
      encoder         : Lavc59.41.101 libsvtav1
fdintino commented 1 year ago

The 1000 fps figure is misleading. AVIF image sequences, like most other animated image formats, support frames with varying durations. An animated gif need not all have a constant frame-rate. But AVIF is also capable of encoding a 30 fps video. Underlying this flexibility is the representation of frame times as units of duration in timescales. For convenience, pillow-avif-plugin sets the timescale at 1ms so that it can losslessly encode the duration of any source image (your source gif, for instance, has 50ms frames; the AVIF image it creates has each frame set to a duration of 50 timescales).

If you view the image in google chrome, which uses dav1d for its AVIF decoding, you'll see that it plays with the exact same timing as the gif (I've embedded the file you included in the issue description as an html img element here). So I would contend that this is a bug with mpv and how it is using ffmpeg to transcode the AVIF file. Here is a link to the same image encoded with timescales of 50ms. I'm curious whether mpv does a better job when the "framerate" is closer to those used most commonly in video formats.

cdgriffith commented 1 year ago

That does slow it down some (still very fast). Yeah I'm sure it's mpv / ffplay both trying to play it purely as video. I just did a manual encode with avifenc to test and it also is at warp speed.

So if the reference encoder has the same results as you do, that's on the player not handling it IMO.

Thanks for the insights! I didn't realize AVIF had both video and image sequence modes, I just assumed it defaulted to "video mode" for any sequence type.