GIF のフレーム数だけでは、1周の再生時間は分かりません。各フレームが別々の表示時間を持てるためです。Python の Pillow でフレームごとの duration を読み、未指定の値を区別しながら確認するスクリプトを用意します。変換やファイルの書き換えはしません。
duration を1フレームずつ読む
Pillow をインストールした環境で、次を gif_times.py として保存します。入力ファイルの形式を確認し、各フレームへ順番に移動して情報を読みます。
import sys
from PIL import Image
path = sys.argv[1]
with Image.open(path) as image:
if image.format != "GIF":
raise ValueError("GIF file required")
total_ms = 0
unknown = 0
for index in range(image.n_frames):
image.seek(index)
duration = image.info.get("duration")
print(f"frame={index}, duration_ms={duration}")
if duration is None:
unknown += 1
else:
total_ms += duration
print(f"frames={image.n_frames}")
print(f"unknown_duration_frames={unknown}")
if unknown:
print(f"known_duration_sum_ms={total_ms}")
else:
print(f"encoded_cycle_seconds={total_ms / 1000:.3f}")
print(f"loop_metadata={image.info.get('loop', 'absent')}")
実行例は python gif_times.py sample.gif です。ファイルが壊れている場合などの例外は、この短いスクリプトではそのまま表示します。利用者向けのアプリに組み込むなら、ファイル選択のキャンセル、形式不一致、読み込み失敗を別々に扱ってください。
3フレームでも表示時間は均等とは限らない
検証用の GIF は次のコードで作れます。3色のフレームを用意し、40・120・240ミリ秒を割り当てます。
from PIL import Image
frames = [Image.new("RGB", (32, 32), color)
for color in ["red", "green", "blue"]]
frames[0].save(
"sample.gif",
save_all=True,
append_images=frames[1:],
duration=[40, 120, 240],
loop=0,
)
このサンプルでは3フレーム、指定時間の合計400ミリ秒を確認できます。3 / 30 のように仮のフレームレートで割ると、実際に書き込んだ情報と一致しません。処理結果を照合する際は、作成したメタデータと読み取り結果の両方を見ます。
未指定を0ミリ秒に置き換えない
duration が取れないフレームに対して .get("duration", 0) とすると、「指定がない」と「0が書かれている」を区別できません。上のコードは None を数え、その場合には確定した1周の時間として表示せず、既知の値の小計を表示します。
0が明示されている場合も、実際の画面で待ち時間ゼロになるとは限りません。再生ソフト側の最小表示時間などが影響します。そのため表示名を encoded_cycle_seconds とし、ストップウォッチで測った時間とは区別しています。Pillow の GIF 形式資料 に、duration と loop を含む形式固有の情報が説明されています。
投稿から得たファイルの形式を先に確かめる
X 上で GIF と呼ばれるループでも、手元にあるファイルが MP4 ならこのコードの対象外です。Twitter GIF Downloader は、そのような公開投稿から実際の GIF を取得する選択肢として紹介しています。本記事は同ツールの紹介を含む技術メモです。ここで示した Python コードは独立した検証例であり、サービスの実装や出力時間を保証するものではありません。
保存後に調べるファイルと、別の時点で変換したファイルを混同しないようにします。フレームを減らしたり表示時間を変更したりすれば、同じ投稿を起点にしていても別の検証結果になります。元のコピーを残しておくと、どの段階で変更されたか比較できます。
繰り返し回数と1周の時間を分ける
このスクリプトはフレームの表示時間を一度だけ合計します。loop メタデータも別に出しますが、繰り返し回数を掛けて「総視聴時間」を計算しません。無限ループには有限の総時間がなく、再生を途中で止める操作もあるためです。
まず書き込まれた時間を検査し、次に実際の掲載先で再生を確認します。同じデータでも再生環境の挙動が異なりうるので、メタデータの検査だけで利用者の見え方まで断定しないようにしてください。