Would you like to create an animation with pyglet and Python and then export it to mp4 video? Learn how to do that in this article.

Render pyglet animation to an mp4 video with Python and ffmpeg.

Look at this animation. It is created with pyglet, a cross-platform windowing and multimedia library for Python. The animation has 300 frames.

The animation is played at no particular framerate. Each frame that is generated is written to an mp4 video file. Here you see the 5 seconds result video in a video player:

How dows that work?

A pyglet.window.Window is created. But instead of running an infinite event loop, it has a run method that renders 300 frames in a for-loop. The resulting video length (5 seconds) is determined by the ffmpeg framerate settings (60 fps).

Each iteration, the label position on the screen is updated and frame data is extracted from the buffer as a list of bytes. The bytes are passed to the VideoWriter that has a reference to an open video stream. The stream was opened in a ffmpeg subprocess and can be written to.

Test it yourself

import math
import pyglet
import subprocess

class VideoWriter:
    def __init__(self):
        fps = "60"
        frame_size = "1920x1080"
        command = [
        "ffmpeg", # ffmpeg executable
        "-y", # overwrite output files
        "-f", "rawvideo", # force format
        "-s", frame_size, # frame size
        "-pix_fmt", "rgba", # pixel format
        "-r", fps, # frame rate
        "-i", "-", # input is pipe
        "-an", # disable audio
        "-metadata", "comment=Rendered by Loek",
        "-vf", "vflip", # video filters, vertical flip
        "-vcodec", "libx264",
        "-pix_fmt", "yuv420p",
        "test.mp4" # output file
        ]
        self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE)

    def write_frame(self, bytes):
        self.writing_process.stdin.write(bytes)

    def close(self):
        self.writing_process.stdin.close()
        self.writing_process.wait()
        self.writing_process.terminate()


class MainWindow(pyglet.window.Window):
    def __init__(self, video_writer):
        super().__init__()
        self.video_writer = video_writer
        self.width = 960
        self.height = 540
        self.label = pyglet.text.Label(":-)", x=100, y=300, font_size=48)
        self.render()

    def render(self):
        self.clear()
        self.label.draw();
        self.flip()

    def run(self):
        for x in range(60 * 5): # 5 seconds
            self.label.x += 2
            self.label.y = math.sin(x / 10) * 10 + 400
            self.render()
            event = self.dispatch_events()

            data = pyglet.image.get_buffer_manager().get_color_buffer().get_image_data().get_data()
            self.video_writer.write_frame(data)

        self.video_writer.close()

window = MainWindow(VideoWriter())
window.run()

Run the code and test.mp4 should look like this:

Written by Loek van den Ouweland on 2023-01-05.
Questions regarding this artice? You can send them to the address below.