Skip to content
Closed
Show file tree
Hide file tree
Changes from 119 commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
7725ec2
Add files via upload
NeoPlato Nov 26, 2020
a5523e2
Add files via upload
NeoPlato Nov 26, 2020
ab07add
Add files via upload
NeoPlato Nov 26, 2020
e275d11
Add files via upload
NeoPlato Nov 26, 2020
b4f1fce
Livestreaming option
NeoPlato Nov 26, 2020
8341da8
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Nov 26, 2020
1ed3f68
Add files via upload
NeoPlato Nov 26, 2020
fd14d66
Update __init__.py
NeoPlato Nov 26, 2020
e8ba6f3
Update __init__.py
NeoPlato Nov 26, 2020
4a4be30
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Nov 26, 2020
49e8759
Update __init__.py
NeoPlato Nov 26, 2020
22bab46
Adding livestreaming support
NeoPlato Nov 27, 2020
2254be0
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Nov 27, 2020
5f77979
More livestreaming options
NeoPlato Nov 27, 2020
0d08bc7
More livestreaming
NeoPlato Nov 27, 2020
3c1fd9b
Update ffi.py
NeoPlato Nov 27, 2020
8d5507a
Update ffi_pixbuf.py
NeoPlato Nov 27, 2020
75eeb79
Delete stream_starter.py
NeoPlato Nov 27, 2020
a6d1fa3
Delete __init__.py
NeoPlato Nov 27, 2020
44ce2d1
Delete __main__.py
NeoPlato Nov 27, 2020
eb7a137
Delete default.cfg
NeoPlato Nov 27, 2020
4deae04
Delete main_utils.py
NeoPlato Nov 27, 2020
995db49
Delete utils.py
NeoPlato Nov 27, 2020
7490898
Delete stream_renderer.py
NeoPlato Nov 27, 2020
6f4ce3b
Delete cairo_renderer.py
NeoPlato Nov 27, 2020
3a67a2f
Delete scene.py
NeoPlato Nov 27, 2020
4c04a9f
Delete stream_file_writer.py
NeoPlato Nov 27, 2020
7b22388
Delete streaming_scene.py
NeoPlato Nov 27, 2020
4f821bd
Revert "Delete streaming_scene.py"
NeoPlato Nov 27, 2020
3437a19
Revert "Delete stream_file_writer.py"
NeoPlato Nov 27, 2020
9a381f6
Revert "Delete scene.py"
NeoPlato Nov 27, 2020
42533e9
Revert "Delete cairo_renderer.py"
NeoPlato Nov 27, 2020
7c9cff1
Revert "Delete stream_renderer.py"
NeoPlato Nov 27, 2020
5292be3
Revert "Delete utils.py"
NeoPlato Nov 27, 2020
33bc6f3
Revert "Delete main_utils.py"
NeoPlato Nov 27, 2020
2094a13
Revert "Delete default.cfg"
NeoPlato Nov 27, 2020
2df207e
Revert "Delete __main__.py"
NeoPlato Nov 27, 2020
7f27a50
Revert "Delete __init__.py"
NeoPlato Nov 27, 2020
173fc05
Revert "Delete stream_starter.py"
NeoPlato Nov 27, 2020
24e6c67
Delete zlib1.dll
NeoPlato Nov 27, 2020
502646e
Tiny correction
NeoPlato Nov 27, 2020
866933b
More stuff to correct. Again
NeoPlato Nov 27, 2020
e52b4b9
Update default.cfg
NeoPlato Nov 27, 2020
38edbc8
Livestreaming. Last time I hope
NeoPlato Nov 27, 2020
2cb6459
Update stream_file_writer.py
NeoPlato Nov 27, 2020
a501084
Update main_utils.py
NeoPlato Nov 27, 2020
0342be0
Update __init__.py
NeoPlato Nov 27, 2020
9671ad1
Update __init__.py
NeoPlato Nov 27, 2020
78fc7fb
Update manim/stream_starter.py
NeoPlato Nov 27, 2020
7e21430
Correction and extra syntax
NeoPlato Nov 29, 2020
a7384da
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Nov 29, 2020
6c9a140
Revert "Merge branch 'master' of https://github.com/NeoPlato/manim"
NeoPlato Nov 29, 2020
9dc7e16
Revert "Revert "Merge branch 'master' of https://github.com/NeoPlato/…
NeoPlato Nov 29, 2020
74cded9
I guess I'll just change the color back using colorama
NeoPlato Nov 30, 2020
fc37ec2
Update manim/stream_starter.py
NeoPlato Nov 30, 2020
6f6ea55
Update manim/stream_starter.py
NeoPlato Nov 30, 2020
be04f30
Update manim/_config/utils.py
NeoPlato Dec 3, 2020
cd612ff
Update scene.py
NeoPlato Dec 4, 2020
f311539
Update camera.py
NeoPlato Dec 4, 2020
ea5e2c7
Update cairo_renderer.py
NeoPlato Dec 4, 2020
034a4d1
Update streaming configuration
NeoPlato Dec 4, 2020
553e309
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Dec 4, 2020
4267ae8
Update unused arguments
NeoPlato Dec 4, 2020
3903bca
Update stream_file_writer.py
NeoPlato Dec 4, 2020
0e9ef90
Update scene_file_writer.py
NeoPlato Dec 4, 2020
424a671
Update scene_file_writer.py
NeoPlato Dec 4, 2020
71dfb58
Update stream_file_writer.py
NeoPlato Dec 4, 2020
7f78dd8
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Dec 4, 2020
5a93fb7
Updating to recent changes
NeoPlato Dec 6, 2020
9677699
Update __init__.py
NeoPlato Dec 6, 2020
b68b93a
Update __init__.py
NeoPlato Dec 6, 2020
289d11e
Merge branch 'master' into master
NeoPlato Dec 6, 2020
962a0f7
Wow so that's like 70 commits now
NeoPlato Dec 6, 2020
945621b
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Dec 6, 2020
350da82
Account for warning raised in Wait animation
NeoPlato Dec 6, 2020
8a90a9f
Yep the attributes thing didn't work
NeoPlato Dec 6, 2020
c8821a6
For the tests
NeoPlato Dec 6, 2020
c18216e
Plato can't test documents from his end
NeoPlato Dec 6, 2020
f22322f
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Dec 6, 2020
d0ead32
Update pyproject.toml
NeoPlato Dec 6, 2020
eec5f4b
Update poetry.lock
NeoPlato Dec 6, 2020
d05600e
Update poetry.lock
NeoPlato Dec 6, 2020
2c60617
Update manim/__main__.py
NeoPlato Dec 7, 2020
a48ca1e
Last of livestreaming configurations
NeoPlato Dec 8, 2020
066f195
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Dec 8, 2020
1c08a67
Shucks this again
NeoPlato Dec 8, 2020
ed2d86f
Revert "Shucks this again"
NeoPlato Dec 8, 2020
9c23094
Revert "Revert "Shucks this again""
NeoPlato Dec 8, 2020
5b6285e
I am the big dumb
NeoPlato Dec 8, 2020
ce8cbfd
Merge pull request #2 from ManimCommunity/master
NeoPlato Dec 8, 2020
6135afc
Update comments + new feature
NeoPlato Dec 10, 2020
6c6a875
Complete __init__ overriding
NeoPlato Dec 10, 2020
212e894
Finishing touches (Please don't hurt me)
NeoPlato Dec 10, 2020
4e33544
Popup ffplay window
NeoPlato Dec 10, 2020
a789aff
Formatting
NeoPlato Dec 10, 2020
e16c556
Merge branch 'master' into master
NeoPlato Dec 11, 2020
d76772e
Merge pull request #3 from ManimCommunity/master
NeoPlato Dec 16, 2020
aaca74a
Add IPython streaming (probably bound to crash and burn)
NeoPlato Dec 20, 2020
040281f
Merge branch 'master' into master
NeoPlato Dec 20, 2020
c2782db
Yas, this again
NeoPlato Dec 20, 2020
1b4e80b
IPython is necessary like air
NeoPlato Dec 20, 2020
000539c
Add name to ffplay window
NeoPlato Dec 21, 2020
08dc895
Have the renderer work
NeoPlato Dec 22, 2020
0a8b49a
The TypeError, right
NeoPlato Dec 22, 2020
9584944
Merge pull request #5 from ManimCommunity/master
NeoPlato Dec 29, 2020
973ae77
Please don't screw the dependencies
NeoPlato Dec 29, 2020
b011fc4
Rational IPython addition
NeoPlato Dec 29, 2020
9f352c8
add forgotten description keyword argument
NeoPlato Dec 30, 2020
97b41c8
The dependencies
NeoPlato Jan 2, 2021
2312d9d
Merge branch 'master' of https://github.com/ManimCommunity/manim into…
NeoPlato Jan 2, 2021
1baeea9
Merge pull request #7 from NeoPlato/ManimCommunity-master
NeoPlato Jan 2, 2021
b353209
Update poetry.lock
NeoPlato Jan 2, 2021
07bd4c8
Merge branch 'master' into master
NeoPlato Jan 20, 2021
85a0c8b
Sort out imports
NeoPlato Jan 20, 2021
a5e0dc7
How did this even happen
NeoPlato Jan 20, 2021
434155d
Fix variable error
NeoPlato Jan 21, 2021
ce6c76a
Find out when this got done in master
NeoPlato Jan 21, 2021
f6f5553
Last commit please
NeoPlato Jan 21, 2021
eb7cf71
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Jan 21, 2021
eb8430f
Describe decorator
NeoPlato Jan 23, 2021
4810b56
Punctuation, yay
NeoPlato Jan 23, 2021
638a03b
Update manim/stream_starter.py
NeoPlato Jan 23, 2021
d600098
Update manim/stream_starter.py
NeoPlato Jan 23, 2021
48d2009
Update manim/scene/streaming_scene.py
NeoPlato Jan 23, 2021
82a7960
Update manim/stream_starter.py
NeoPlato Jan 23, 2021
6b61eae
Update manim/scene/streaming_scene.py
NeoPlato Jan 23, 2021
ad36871
Update manim/scene/streaming_scene.py
NeoPlato Jan 23, 2021
7daf498
Update manim/stream_starter.py
NeoPlato Jan 23, 2021
449954b
Update manim/scene/streaming_scene.py
NeoPlato Jan 23, 2021
e347ed9
Documentation fixes
NeoPlato Jan 25, 2021
9ef7af2
Merge branch 'master' of https://github.com/NeoPlato/manim
NeoPlato Jan 25, 2021
069b771
Fix configuration from IPython
NeoPlato Feb 2, 2021
3e7d9d8
Fix conflicts
NeoPlato Feb 2, 2021
d7313db
Switch imports
NeoPlato Feb 8, 2021
e796f60
More fixes for the love of man
NeoPlato Mar 13, 2021
6e86313
Last bit
NeoPlato Mar 13, 2021
9b4bd67
Merge branch 'master' into master
NeoPlato Mar 13, 2021
cda56b8
A bracket!
NeoPlato Mar 13, 2021
d514586
Please
NeoPlato Mar 13, 2021
ffa1cd1
Update pyproject.toml
NeoPlato Mar 19, 2021
3bd9d1c
Merge branch 'master' into master
NeoPlato Mar 29, 2021
44ea5cf
Check difference
NeoPlato Mar 29, 2021
7719958
Update that might backfire horribly
NeoPlato Mar 29, 2021
f0e6df6
Ouch
NeoPlato Mar 29, 2021
c6e35a7
Update poetry.lock
NeoPlato Mar 29, 2021
eb76955
Missed a spot
NeoPlato Mar 29, 2021
515f836
Update stream_starter.py
NeoPlato Mar 29, 2021
641d68e
Last of commits
NeoPlato Mar 31, 2021
67f6f14
Pretty
NeoPlato Mar 31, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/source/tutorials/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ A list of all config options
'max_files_cached', 'media_dir', 'movie_file_extension', 'output_file',
'partial_movie_dir', 'pixel_height', 'pixel_width', 'png_mode', 'preview',
'progress_bar', 'quality', 'right_side', 'save_as_gif', 'save_last_frame',
'save_pngs', 'scene_names', 'show_in_file_browser', 'sound', 'tex_dir',
'tex_template', 'tex_template_file', 'text_dir', 'top', 'transparent',
'save_pngs', 'scene_names', 'show_in_file_browser', 'sound', 'streaming_dir',
'tex_dir', 'tex_template', 'tex_template_file', 'text_dir', 'top', 'transparent',
'upto_animation_number', 'use_js_renderer', 'verbosity', 'video_dir', 'write_all',
'write_to_movie']

Expand Down Expand Up @@ -435,6 +435,8 @@ A list of all CLI flags
--config_file CONFIG_FILE
Specify the configuration file
--custom_folders Use the folders defined in the [custom_folders] section of the config file to define the output folder structure
--livestream Run in streaming mode
--use-ipython Use IPython for streaming
Comment on lines +444 to +445
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this section exists after the click merge

-v {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --verbosity {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Verbosity level. Also changes the ffmpeg log level unless the latter is specified in the config
--progress_bar True/False
Expand Down
1 change: 1 addition & 0 deletions manim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@
from .utils import unit

from .plugins import *
from .stream_starter import *
6 changes: 6 additions & 0 deletions manim/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)
from manim.utils.file_ops import open_file as open_media_file
from manim._config.main_utils import parse_args
from manim.stream_starter import livestream

try:
from manim.grpc.impl import frame_server_impl
Expand Down Expand Up @@ -76,6 +77,11 @@ def main():

else:
config.digest_args(args)

if args.livestream:
livestream(use_ipython=args.use_ipython)
return

Comment on lines +48 to +52
Copy link
Member

@jsonvillanueva jsonvillanueva Apr 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need to move to cli/render/commands.py -- just keep the upstream changes for this file entirely

input_file = config.get_dir("input_file")
if config["use_js_renderer"]:
try:
Expand Down
12 changes: 12 additions & 0 deletions manim/_config/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ images_dir = {media_dir}/images/{module_name}
tex_dir = {media_dir}/Tex
text_dir = {media_dir}/texts
partial_movie_dir = {video_dir}/partial_movie_files/{scene_name}
streaming_dir = {media_dir}/streams

# --use_js_renderer
use_js_renderer = False
Expand Down Expand Up @@ -146,3 +147,14 @@ log_timestamps = True
# Uncomment the following line to manually set the loglevel for ffmpeg. See
# ffmpeg manpage for accepted values
loglevel = ERROR


# Streaming settings
# Protocol defaults to rtp
[streaming]
streaming_client = ffplay
streaming_protocol = rtp
streaming_ip = 127.0.0.1
streaming_port = 2000
streaming_url = {streaming_protocol}://{streaming_ip}:{streaming_port}
sdp_name = stream_{streaming_protocol}.sdp
16 changes: 16 additions & 0 deletions manim/_config/main_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ def _parse_args_no_subcmd(args: list) -> argparse.Namespace:

parser.add_argument(
"file",
nargs="?",
default="",
help="Path to file holding the python code for the scene",
)
parser.add_argument(
Expand Down Expand Up @@ -433,6 +435,20 @@ def _parse_args_no_subcmd(args: list) -> argparse.Namespace:
"section of the config file to define the output folder structure",
)

# Overrides default false streaming so streaming can happen
parser.add_argument(
"--livestream",
action="store_true",
help="Run in streaming mode",
)

# Optional use of IPython for streaming
parser.add_argument(
"--use-ipython",
action="store_true",
help="Use IPython for streaming",
)

Comment on lines +454 to +467
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is entire file doesn't even exist anymore. these options would need to be moved to one of the four files I mentioned for categories under the render command. Argparse has action="store_true", click does is_flag=True

# Specify the verbosity
parser.add_argument(
"-v",
Expand Down
28 changes: 27 additions & 1 deletion manim/_config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ class MyScene(Scene):
"save_pngs",
"scene_names",
"show_in_file_browser",
"streaming_dir",
"sound",
"tex_dir",
"tex_template_file",
Expand Down Expand Up @@ -539,6 +540,7 @@ def digest_parser(self, parser: configparser.ConfigParser) -> "ManimConfig":
"movie_file_extension",
"background_color",
"js_renderer_path",
"streaming_dir",
]:
setattr(self, key, parser["CLI"].get(key, fallback="", raw=True))

Expand All @@ -560,6 +562,23 @@ def digest_parser(self, parser: configparser.ConfigParser) -> "ManimConfig":
if val:
setattr(self, "ffmpeg_loglevel", val)

streaming_config = {
opt: parser["streaming"].get(opt, fallback="", raw=True)
for opt in [
"streaming_client",
"streaming_protocol",
"streaming_ip",
"streaming_port",
]
}
url = parser["streaming"].get("streaming_url", fallback="", raw=True)
sdp_name = parser["streaming"].get("sdp_name", fallback="", raw=True)
streaming_config["streaming_url"] = url.format(**streaming_config)
streaming_config["sdp_path"] = os.path.join(
self.get_dir("streaming_dir"), sdp_name.format(**streaming_config)
)
Comment on lines +586 to +600
Copy link
Member

@jsonvillanueva jsonvillanueva Apr 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but the file still needs to resolve conflicts -- keep both changes

self.streaming_config = streaming_config

return self

def digest_args(self, args: argparse.Namespace) -> "ManimConfig":
Expand Down Expand Up @@ -679,7 +698,7 @@ def digest_args(self, args: argparse.Namespace) -> "ManimConfig":
"partial_movie_dir",
]:
self[opt] = self._parser["custom_folders"].get(opt, raw=True)
# --media_dir overrides the deaful.cfg file
# --media_dir overrides the default.cfg file
if hasattr(args, "media_dir") and args.media_dir:
self.media_dir = args.media_dir

Expand Down Expand Up @@ -1161,6 +1180,7 @@ def get_dir(self, key: str, **kwargs: str) -> Path:
"input_file",
"output_file",
"partial_movie_dir",
"streaming_dir",
]
if key not in dirs:
raise KeyError(
Expand Down Expand Up @@ -1236,6 +1256,12 @@ def _set_dir(self, key: str, val: typing.Union[str, Path]):
doc="Directory to place partial movie files (no flag). See :meth:`ManimConfig.get_dir`.",
)

streaming_dir = property(
lambda self: self._d["streaming_dir"],
lambda self, val: self._set_dir("streaming_dir", val),
doc="Directory to have streamed files. See :meth:`ManimConfig.get_dir`.",
)

custom_folders = property(
lambda self: self._d["custom_folders"],
lambda self, val: self._set_boolean("custom_folders", val),
Expand Down
11 changes: 11 additions & 0 deletions manim/renderer/stream_renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .cairo_renderer import CairoRenderer
from .. import config
from ..scene.stream_file_writer import StreamFileWriter


class StreamCairoRenderer(CairoRenderer):
def init_scene(self, scene):
"""For compatibility with the __init__ from scene that's not being
directly overridden
"""
self.file_writer = StreamFileWriter(self)
103 changes: 103 additions & 0 deletions manim/scene/stream_file_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from abc import ABCMeta

import os
import subprocess

from .. import config, logger
from ..constants import FFMPEG_BIN
from .scene import Scene
from .scene_file_writer import SceneFileWriter
from ..utils.file_ops import guarantee_existence


class StreamFileWriter(SceneFileWriter):
def __init__(self, renderer):
super().__init__(renderer, "")
vars(self).update(config.streaming_config)
path = os.path.join(config.get_dir("streaming_dir"), "clips")
self.FOLDER_PATH = os.path.relpath(guarantee_existence(path))
# To prevent extensive overwriting
self.partial_movie_directory = self.FOLDER_PATH

def init_output_directories(self, scene_name):
"""The original :class:`SceneFileWriter` uses this method while initializing.
The method is overwritten carefully so as to correspond arguments in the
original and still avoid the folder creation effects of this method.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""The original :class:`SceneFileWriter` uses this method while initializing.
The method is overwritten carefully so as to correspond arguments in the
original and still avoid the folder creation effects of this method.
"""
"""Overridden to avoid creation of unnecessary output directories."""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should probably say more on this but sure

pass

@property
def file_path(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What file path is this for? Trying to access it yields

>>> manim.renderer.file_writer.file_path
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/behackl/code/manim/manim/scene/stream_file_writer.py", line 31, in file_path
    return self.partial_movie_files[-1]
IndexError: list index out of range

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Either please remove this method if it is not used, or add a one-line docstring if it is used.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self for this one too

return self.partial_movie_files[-1]

def end_animation(self, allow_write=False):
"""The point in the animation where the file exists."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring of the inherited method says

        Internally used by Manim to stop streaming to
        FFMPEG gracefully.

I don't understand how this fits together, please clarify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self

super().end_animation(allow_write=allow_write)
self.stream()

def combine_movie_files(self):
"""Also to reduce overriding code."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you are saying here. You override the method to reduce overriding code?

Suggested change
"""Also to reduce overriding code."""
"""Not required for live streaming: overridden."""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, needs more explanation, this does

pass

def stream(self):
logger.info(
"Houston, we are ready to launch. Sending over to %(url)s",
{"url": {self.streaming_url}},
)
command = [
FFMPEG_BIN,
"-re",
"-i",
self.file_path,
"-vcodec",
"copy",
"-an",
"-loglevel",
"quiet",
]

if self.streaming_protocol == "rtp":
command += ["-sdp_file", self.sdp_path]
command += [
"-f",
self.streaming_protocol, # Take a look here for other streaming protocols
self.streaming_url,
]
os.system(" ".join(command))

def open_movie_pipe(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The amount of code duplication between here and SceneFileWriter.open_movie_pipe is unfortunate, and could be reduced substantially by refactoring a little. I won't block the PR because of it, however.

Please add a one-line docstring summary, suggestion:

Suggested change
def open_movie_pipe(self):
def open_movie_pipe(self):
"""Used internally by Manim to initialise
FFMPEG and begin writing to FFMPEG's input
buffer.
"""

Copy link
Contributor Author

@NeoPlato NeoPlato Jan 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, that bit me a little that that code looks eerily similar to the one from the inherited class. That one I noticed as I was coding.

How would I refactor the command from the base class? That would be interesting to know

fps = config["frame_rate"]
height = config["pixel_height"]
width = config["pixel_width"]

command = [
FFMPEG_BIN,
"-y", # overwrite output file if it exists
"-f",
"rawvideo",
"-s",
"%dx%d" % (width, height), # size of one frame
"-pix_fmt",
"rgba",
"-r",
str(fps), # frames per second
"-i",
"-", # The imput comes from a pipe
"-an", # Tells FFMPEG not to expect any audio
"-loglevel",
"error",
]
if config["transparent"]:
command += ["-vcodec", "qtrle"]
else:
command += ["-vcodec", "libx264", "-pix_fmt", "yuv420p"]
command += [self.file_path]
self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE)

def close_movie_pipe(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More or less the same comment as above. Also, I am a bit confused: shouldn't the partial movie files also exist for livestreaming? If so, why is overriding this necessary at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn I see the flaw! Good sopt

self.writing_process.stdin.close()
self.writing_process.wait()
logger.info(
f"Animation {self.renderer.num_plays}: File at %(path)s",
{"path": {self.file_path}},
)
111 changes: 111 additions & 0 deletions manim/scene/streaming_scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from .. import config
from ..mobject.frame import FullScreenRectangle as Frame
from ..renderer.stream_renderer import StreamCairoRenderer
from ..utils.simple_functions import get_parameters
from .scene import Scene


class Stream:
"""This class is really intended for inheritance of the style:
>>> class Streamer(Stream, Scene): # doctest: +SKIP
... pass
...
>>>
This order is paramount. This :class:`Stream` class does the switch to
the specialized renderer, which uses the :class:`StreamFileWriter` to
handle specialized streaming services. That explains the calls to super,
which dig through the MRO of a class instead of using just a single
implementation contained in Scene. Okay, the references in super probably
point to things in the :class:`Scene` class only, but it's already happened so...
PS: You can probably already tell using this class on its own will bring you
errors.
"""

def __init__(self, **kwargs):
camera_class = self.mint_camera_class()
renderer = StreamCairoRenderer(camera_class=camera_class)
super().__init__(renderer=renderer, **kwargs)
# To identify the frame in a black background
self.add(Frame())
self.setup()

@classmethod
def mint_camera_class(cls):
"""Only __init__ arguments have the camera class now. In order for the
specialized renderer to be used, the scene's camera class must be found.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a very good one-line summary of this method, I don't really get an idea of what this does.
Also_ __init__ needs to be set as code.

Suggested change
"""Only __init__ arguments have the camera class now. In order for the
specialized renderer to be used, the scene's camera class must be found.
"""Only ``__init__`` arguments have the camera class now. In order for the
specialized renderer to be used, the scene's camera class must be found.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self, update this

Returns:
A camera class from the scene's inheritance hierachy
Raises:
AttributeError: If this lookup fails
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Returns:
A camera class from the scene's inheritance hierachy
Raises:
AttributeError: If this lookup fails
Returns
-------
:class:`~.Camera`
A camera class from the scene's inheritance hierachy.
Raises
------
AttributeError
When the lookup fails.

"""
for obj in cls.mro():
try:
parameter = get_parameters(obj.__init__)["camera_class"]
except KeyError:
continue
else:
return parameter.default
raise AttributeError("Object does not contain scene protocol")

def show_frame(self):
"""
Opens the current frame in the Default Image Viewer
of your system.
"""
self.renderer.update_frame(self, ignore_skipping=True)
self.renderer.camera.get_image().show()


def get_streamer(*scene):
"""Creates an instance of a class that has streaming services.
Optional arguments:
scene: The scene whose methods can be used in the resulting
instance, such as zooming in and arbitrary method constructions.
Defaults to just Scene
Returns:
StreamingScene: It's a Scene that Streams. Name deconstruction.
"""
bases = (Stream,) + (scene or (Scene,))
cls = type("StreamingScene", bases, {})
# This class doesn't really need a name, but we can go
# generic for this one
return cls()


def play_scene(scene, start=None, end=None):
"""Every scene has a render method that runs its setup and construct methods.
Using a streamer from classes with detailed implementation of this may call for
use of this.
>>> from example_scenes.basic import OpeningManimExample # doctest: +SKIP
>>> manim = get_streamer(OpeningManimExample) # doctest: +SKIP
>>> manim.render() # doctest: +SKIP
This should stream a complete rendering of the Scene to the URL specified.
Hence the function clears everything after it's finished for more use. Or
something like that.
Arguments:
scene: The scene to be played.
start: The animation to start with. Default original start point.
end: The animation to end with. Default original endpoint
Note: The animations use endpoint-inclusive indexing, meaning (0, 5) would
play 0 upto 5 inclusive of both.
"""
manim = get_streamer(scene)
original = (config.from_animation_number, config.upto_animation_number)
config.from_animation_number = start or config.from_animation_number
config.upto_animation_number = end or config.upto_animation_number
manim.render()
# Need to put it back because an end point less than the number of animations
# in a streamer makes any others ignored. That's a bug
config.from_animation_number, config.upto_animation_number = original
manim.clear()
Loading