File: ffmpeg.py

package info (click to toggle)
python-moderngl-window 2.4.6-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 69,220 kB
  • sloc: python: 11,387; makefile: 21
file content (105 lines) | stat: -rw-r--r-- 3,228 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from .base import BaseVideoCapture
import subprocess
import moderngl


class FFmpegCapture(BaseVideoCapture):
    """
        ``FFmpegCapture`` it's an utility class to capture runtime render
        and save it as video.

        Args:


        Example:

        .. code:: python

            import moderngl_window
            from moderngl_window.capture.ffmpeg import FFmpegCapture

            class CaptureTest(modenrgl_window.WindowConfig):

                def __init__(self, **kwargs):
                    super().__init__(**kwargs)
                    # do other initialization

                    # define VideoCapture class
                    self.cap = FFmpegCapture(source=self.wnd.fbo)

                    # start recording
                    self.cap.start_capture(
                        filename="video.mp4",
                        framerate=30
                    )

                def render(self, time, frametime):
                    # do other render stuff

                    # call record function after
                    self.cap.save()

                def close(self):
                    # if realease func is not called during
                    # runtime. make sure to do it on the closing event
                    self.cap.release()

    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._ffmpeg = None

    def _start_func(self) -> bool:
        """
            choose the right pixel format based on the number of components
            and start a ffmper pipe with a subprocess.
        """
        pix_fmt = 'rgb24'   # 3 component, 1 byte per color -> 24 bit

        # for the framebuffer is easier because i can read 3 component even if
        # the color attachment has less components
        if isinstance(self._source, moderngl.Texture) and self._components == 4:
            pix_fmt = 'rgba'  # 4 component , 1 byte per color -> 32 bit

        command = [
            'ffmpeg',
            '-hide_banner',
            '-loglevel', 'error', '-stats',  # less verbose, only stats of recording
            '-y',  # (optional) overwrite output file if it exists
            '-f', 'rawvideo',
            '-vcodec', 'rawvideo',
            '-s', f'{self._width}x{self._height}',  # size of one frame
            '-pix_fmt', pix_fmt,
            '-r', f'{self._framerate}',  # frames per second
            '-i', '-',  # The imput comes from a pipe
            '-vf', 'vflip',
            '-an',  # Tells FFMPEG not to expect any audio
            self._filename,
        ]

        # ffmpeg binary need to be on the PATH.
        try:
            self._ffmpeg = subprocess.Popen(
                command,
                stdin=subprocess.PIPE,
                bufsize=0
            )
        except FileNotFoundError:
            print("ffmpeg command not found. Be sure to add it to PATH")
            return

        return True

    def _release_func(self):
        """
        Safely release the capture
        """
        self._ffmpeg.stdin.close()
        _ = self._ffmpeg.wait()

    def _dump_frame(self, frame):
        """
        write the frame data in to the ffmpeg pipe
        """
        self._ffmpeg.stdin.write(frame)