neu_glance.movie.rendering¶
Render a neu_glance.movie.animate.Timeline to a sequence of PNGs, through a real viewer.
Each frame is a round trip: Python pushes a state, the browser loads the visible chunks,
draws, and posts the image back. neu_glance.movie.animate decides what each frame is.
By default a headless chrome or chromium does the drawing, started with
subprocess.Popen (no selenium: the viewer’s own channel carries every state change and
screenshot). With GPU_FLAGS it is also fast: measured on one 1920x1080 mesh scene,
0.21 s/frame, against 2.17 s/frame without them and 0.57 s/frame through a browser on
another machine. record(..., browser="none") prints a URL and waits instead.
neuroglancer is imported only inside functions (a test checks), so import neu_glance
does not need it. Beyond neuroglancer.tool.screenshot, this:
requeues a wrong-sized reply instead of writing it;
writes frames atomically, and on resume checks each file’s PNG signature;
gives up after a bounded number of browser refreshes instead of hanging.
Attributes¶
Exceptions¶
A render could not start, or could not finish the frame it was on. |
Functions¶
|
The command that turns the frames into an mp4, quoted for a shell. Printed, never run. |
|
A chrome/chromium executable, or |
|
|
|
Run a browser on |
|
True only for a file that exists and begins with the PNG signature, so a frame |
|
A viewer for a browser to attach to. Returns it; its |
|
|
|
Print the encode command, stating plainly that nothing has been encoded yet. |
|
Render |
|
A state with the viewer's furniture switched off, ready to be a frame. |
|
Frame files numbered at or past |
|
Push |
Module Contents¶
- exception neu_glance.movie.rendering.RenderProblem¶
Bases:
RuntimeErrorA render could not start, or could not finish the frame it was on.
Initialize self. See help(type(self)) for accurate signature.
- neu_glance.movie.rendering.ffmpeg_command(out_dir: str, fps: float, *, output: str | None = None, crf: int = DEFAULT_CRF, pattern: str = 'frame_%05d.png') str¶
The command that turns the frames into an mp4, quoted for a shell. Printed, never run.
Not run, because ffmpeg is often not on
PATH(on a cluster it is usually a module), and because re-encoding should never mean re-rendering. Keep-pix_fmt yuv420p: without it the video encodes but does not play in QuickTime, PowerPoint or Slack.
- neu_glance.movie.rendering.find_browser(binary: str | None = None) str | None¶
A chrome/chromium executable, or
None.binaryoverrides the search.
- neu_glance.movie.rendering.frame_path(out_dir: str, index: int, pattern: str = 'frame_%05d.png') str¶
<out_dir>/frame_00042.png.patternmust be usable verbatim byffmpeg -i.
- neu_glance.movie.rendering.headless_browser(url: str, *, size: tuple[int, int] = (1920, 1080), binary: str | None = None, gpu: bool = True, extra_args: Sequence[str] = ())¶
Run a browser on
urlfor the duration of the block, with no window and no tab.No selenium is needed: the browser only has to load the URL, and the viewer’s channel does the rest. Yields a function returning
Nonewhile the browser runs, or why it stopped.The window is sized to the render, or every screenshot comes back the wrong size. The profile is a temporary directory, removed afterwards; sharing one would make a second render attach to the first browser.
- neu_glance.movie.rendering.is_written(path: str) bool¶
True only for a file that exists and begins with the PNG signature, so a frame killed mid-write is rendered again.
- neu_glance.movie.rendering.open_viewer(*, bind: str | None = None, port: int = 0)¶
A viewer for a browser to attach to. Returns it; its
.get_viewer_url()is the link.bindandporttake effect only before the first viewer in the process, since neuroglancer reads them at server start.bind="0.0.0.0"puts the host’s name in the URL, for a browser on another machine.
- neu_glance.movie.rendering.pending_frames(timeline: neu_glance.movie.animate.Timeline, out_dir: str, *, resume: bool = True, start_frame: int = 0, end_frame: int | None = None, pattern: str = 'frame_%05d.png') list[tuple[int, float, str]]¶
(index, seconds, path)for the frames still to render. States are built later.States are built one per frame later, rather than held for the whole timeline.
- neu_glance.movie.rendering.print_encode_hint(out_dir: str, fps: float, *, pattern: str = 'frame_%05d.png', file=None) None¶
Print the encode command, stating plainly that nothing has been encoded yet.
- neu_glance.movie.rendering.record(timeline: neu_glance.movie.animate.Timeline, out_dir: str, *, size: tuple[int, int] = (1920, 1080), viewer=None, bind: str | None = None, port: int = 0, prefetch: int = 1, refresh_timeout: float = 120.0, give_up_after: int = 5, connect_timeout: float = 600.0, size_attempts: int = 5, resume: bool = True, start_frame: int = 0, end_frame: int | None = None, pattern: str = 'frame_%05d.png', shape: bool = True, verbose: bool = False, progress_interval: float = 10.0, browser: str = 'auto', browser_binary: str | None = None, gpu: bool = True) list[str]¶
Render
timelineintoout_dir. Returns the frame paths written this run.browser="auto"runs a headless chrome or chromium if one is on the PATH, else prints a URL to open;"headless"requires one;"none"always waits for you to open the URL.verbose=Trueprints the capture loop’s own lines; otherwise a progress bar is shown, and the last lines are printed only if the render fails.Writes
timeline.jsonbeside the frames. Interrupting is safe and re-running resumes, unless the timeline changed, in which case every frame is rendered again. Frames past the end of the timeline, left by a longer earlier run, are deleted.start_frame/end_framerender a slice with the same numbering.
- neu_glance.movie.rendering.render_state(state: Mapping[str, Any], *, show_axis_lines: bool = False, show_default_annotations: bool = False, show_scale_bar: bool | None = None, gpu_memory_limit: int | None = None, system_memory_limit: int | None = None, concurrent_downloads: int | None = None) dict¶
A state with the viewer’s furniture switched off, ready to be a frame.
Apply it once to the base state, before interpolation. Axis lines and default annotations (bounding boxes) default to off, unlike in the viewer. The memory limits and concurrent downloads are left at the viewer’s defaults unless given; they matter for scenes with many mesh layers.
- neu_glance.movie.rendering.stale_frames(out_dir: str, frame_count: int, pattern: str = 'frame_%05d.png') list[str]¶
Frame files numbered at or past
frame_count— leftovers from a longer earlier run.ffmpeg -i frame_%05d.pngreads the whole numbered sequence, so after shortening an animation these would be appended to the video.
- neu_glance.movie.rendering.wait_for_browser(viewer, state: Mapping[str, Any], *, timeout: float = 600.0, notify_every: float = 15.0, stall_timeout: float = 180.0, launched: bool = False, watch=None) None¶
Push
state, then block until a browser has it open and loaded.launched=Truewhen the browser was started here, which changes the messages (no “open this link”) and the diagnosis if it never attaches.Two waits:
timeoutfor a browser to attach, then for the scene to load, failing if no progress is made forstall_timeout. A viewer with no layers and no dimensions never reports ready, so it cannot answer a screenshot; attachment is therefore tested withstate’s dimensions and no layers, which is ready almost at once. The viewer config (such as its size) is not touched, since a smallviewerSizeis shown magnified in the user’s window.
- neu_glance.movie.rendering.BROWSERS = ('google-chrome', 'google-chrome-stable', 'chromium-browser', 'chromium')¶
- neu_glance.movie.rendering.CHROME_OFF¶
- neu_glance.movie.rendering.GPU_FLAGS = ('--use-angle=vulkan', '--enable-features=Vulkan', '--enable-gpu', '--ignore-gpu-blocklist')¶
- neu_glance.movie.rendering.HEADLESS_FLAGS = ('--headless=new', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions', '--mute-audio')¶
- neu_glance.movie.rendering.PNG_MAGIC = b'\x89PNG\r\n\x1a\n'¶