|
1 | 1 | # Copyright (C) 2022-2026 ETH Zurich, Manuel Kaufmann, Velko Vechev, Dario Mylonopoulos |
2 | 2 | import copy |
3 | 3 | import os |
| 4 | +import subprocess |
4 | 5 | import sys |
5 | 6 | from array import array |
6 | 7 | from collections import namedtuple |
|
31 | 32 | from aitviewer.utils.perf_timer import PerfTimer |
32 | 33 | from aitviewer.utils.utils import get_video_paths, video_to_gif |
33 | 34 |
|
| 35 | + |
| 36 | +class FFmpegWriter: |
| 37 | + """Thin ffmpeg pipe writer, replaces scikit-video which broke on NumPy 2.0.""" |
| 38 | + |
| 39 | + def __init__(self, path, inputdict, outputdict): |
| 40 | + self._path = path |
| 41 | + self._inputdict = inputdict |
| 42 | + self._outputdict = outputdict |
| 43 | + self._proc = None |
| 44 | + |
| 45 | + def _start(self, frame): |
| 46 | + h, w = frame.shape[:2] |
| 47 | + pix_fmt = "rgba" if frame.ndim == 3 and frame.shape[2] == 4 else "rgb24" |
| 48 | + input_args = [a for k, v in self._inputdict.items() for a in (k, str(v))] |
| 49 | + output_args = [a for k, v in self._outputdict.items() for a in (k, str(v))] |
| 50 | + cmd = ( |
| 51 | + ["ffmpeg", "-y"] |
| 52 | + + input_args |
| 53 | + + ["-f", "rawvideo", "-pix_fmt", pix_fmt, "-s", f"{w}x{h}", "-i", "pipe:0"] |
| 54 | + + output_args |
| 55 | + + [self._path] |
| 56 | + ) |
| 57 | + self._proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) |
| 58 | + |
| 59 | + def writeFrame(self, frame): |
| 60 | + if self._proc is None: |
| 61 | + self._start(frame) |
| 62 | + self._proc.stdin.write(frame.tobytes()) |
| 63 | + |
| 64 | + def close(self): |
| 65 | + if self._proc is not None: |
| 66 | + self._proc.stdin.close() |
| 67 | + self._proc.wait() |
| 68 | + |
| 69 | + |
34 | 70 | MeshMouseIntersection = namedtuple( |
35 | 71 | "MeshMouseIntersection", |
36 | 72 | "node instance_id tri_id vert_id point_world point_local bc_coords", |
@@ -1876,9 +1912,6 @@ def export_video( |
1876 | 1912 | quality="medium", |
1877 | 1913 | ensure_no_overwrite=True, |
1878 | 1914 | ): |
1879 | | - # Load this module to reduce load time. |
1880 | | - import skvideo.io |
1881 | | - |
1882 | 1915 | if rotate_camera and not isinstance(self.scene.camera, ViewerCamera): |
1883 | 1916 | print("Cannot export a video with camera rotation while using a camera that is not a ViewerCamera") |
1884 | 1917 | return |
@@ -1974,7 +2007,7 @@ def export_video( |
1974 | 2007 | } |
1975 | 2008 | ) |
1976 | 2009 |
|
1977 | | - writer = skvideo.io.FFmpegWriter( |
| 2010 | + writer = FFmpegWriter( |
1978 | 2011 | path_video, |
1979 | 2012 | inputdict={ |
1980 | 2013 | "-framerate": str(output_fps), |
|
0 commit comments