diff --git a/requirements.txt b/requirements.txt index 4c7ebd2..cf3b360 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs==23.1.0 +av==9.0.2 Pillow==9.3.0 -moviepy==1.0.3 sortedcontainers==2.4.0 pytest~=6.2.5 setuptools>=67.0.0 \ No newline at end of file diff --git a/scrivid/_file_objects/adjustments.py b/scrivid/_file_objects/adjustments.py index 4b12b40..47f06a0 100644 --- a/scrivid/_file_objects/adjustments.py +++ b/scrivid/_file_objects/adjustments.py @@ -5,7 +5,7 @@ from .properties import EXCLUDED, Properties from abc import ABC, abstractmethod -from typing import NamedTuple, TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Hashable, Union diff --git a/scrivid/compile_video.py b/scrivid/compile_video.py index 234a6e5..4cf178e 100644 --- a/scrivid/compile_video.py +++ b/scrivid/compile_video.py @@ -10,13 +10,13 @@ from copy import deepcopy import os +import logging import shutil from typing import NamedTuple, TYPE_CHECKING from PIL import Image -from moviepy.editor import ImageClip -from moviepy.video.compositing.concatenate import concatenate_videoclips +import av if TYPE_CHECKING: from ._file_objects.adjustments import RootAdjustment @@ -146,13 +146,23 @@ def _generate_frames(motion_tree: MotionTree): return index, frames -def _stitch_video(temporary_directory, video_length, metadata): - clips = [(ImageClip(f"{temporary_directory}\\{m}.png") - .set_duration(1 / metadata.frame_rate)) - for m in range(video_length)] +def _stitch_video(temporary_directory, video_length, metadata: Metadata): + frame_rate = metadata.frame_rate + output_file = str(metadata.save_location / f"{metadata.video_name}.mp4") - concat_clip = concatenate_videoclips(clips, method="compose") - concat_clip.write_videofile(f"{metadata.save_location}\\{metadata.video_name}.mp4", fps=metadata.frame_rate) + with av.open(output_file, "w", format="mp4", options={"crf": "23", "pix_fmt": "rgb24"}) as container: + stream = container.add_stream("libx264", rate=frame_rate) + stream.height = metadata.window_height + stream.width = metadata.window_width + + for index in range(video_length): + image_path = os.path.join(temporary_directory, f"{index}.png") + + with av.open(image_path) as image: + frame = next(image.decode(video=0)) + frame.pts = index + frame.pict_type = (index % frame_rate == 0) + container.mux(stream.encode(frame)) def compile_video(instructions: Sequence[INSTRUCTIONS], metadata: Metadata): @@ -164,6 +174,7 @@ class of the Adjustment hierarchy. :param metadata: An instance of Metadata that stores the attributes of the video. """ + logging.basicConfig(level=logging.CRITICAL) separated_instructions = separate_instructions(instructions) motion_tree = parse(separated_instructions) diff --git a/setup.cfg b/setup.cfg index 8ca01d1..efab9ce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,7 +18,7 @@ classifiers = packages = scrivid install_requires = Pillow>=9.3 - moviepy>=1 + av>=9 sortedcontainers>2 python_requires = >=3.8 zip_safe = no