visualdynamics.gui.movie¶
movie
¶
Save an animation as a video that plays almost anywhere.
The file is an MP4 holding H.264, which opens with nothing installed in QuickTime, the Windows player, every browser, Keynote and PowerPoint, and on a phone. It is also small for an animation, which is a still background behind a smoothly moving mesh: a GIF of the same five seconds measured seven times larger, and it limits the color map to 256 levels.
The encoder is Qt Multimedia's. The FFmpeg inside the PySide6 wheels is
an LGPL build without libx264, and it passes H.264 to the platform's own
encoder: VideoToolbox on macOS, Media Foundation on Windows, and a GPU's
VAAPI or NVENC on Linux. A Linux machine without a working GPU encoder
has no H.264 at all, and Qt then records MPEG-4 Part 2 with no warning,
a file five times the size that browsers and Keynote refuse. So the
codec is checked before anything is written, and a machine that cannot
provide it is told why (unavailable_reason) instead of being given a
file that will not play.
Classes:
| Name | Description |
|---|---|
MovieUnavailable |
Raised when this machine cannot write an H.264 movie (it has no |
Functions:
| Name | Description |
|---|---|
unavailable_reason |
Why no movie can be saved on this machine, or None when one can. |
widget_frame |
|
over_plot |
Each of |
write_movie |
Encode |
mp4_reading |
The codec's four-character code and the frame count, read from an |
check |
Check that this installation can save an animation: render one |
Classes¶
MovieUnavailable
¶
Bases: RuntimeError
Raised when this machine cannot write an H.264 movie (it has no H.264 encoder, or Qt Multimedia does not load), so nothing is written.
Functions:¶
unavailable_reason
¶
Why no movie can be saved on this machine, or None when one can.
The reasons are that Qt Multimedia does not load, or that there is no H.264 encoder Qt can use.
Source code in src/visualdynamics/gui/movie.py
widget_frame
¶
widget drawn as an RGB array exactly width × height pixels:
scaled to fit, its shape kept, and the rest filled with the color
of its own corner, so a plot narrower than the frame sits centered
on its own background.
Source code in src/visualdynamics/gui/movie.py
over_plot
¶
Each of frames with widget drawn under it, as the window
shows a record's plot under its animation: the plot, with its cursor
where the frame is, says where in the record each frame is.
The plot is grabbed fresh for every frame, so whatever moved it
(the cursor, an envelope's caption) is in that frame. It is as wide
as the scene and as tall as its shape allows, up to share of the
scene's height, and the stacked frame is kept to an even height for
the H.264 encoder.
Source code in src/visualdynamics/gui/movie.py
write_movie
¶
Encode frames into an H.264 MP4 at path, overwriting it.
Frames are taken one at a time as the encoder asks for them, so a long animation is never held in memory. User input to the window is held off while it runs, because a click could rebuild the scene the frames come from. The file is read back afterward and its codec and frame count are checked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or Path
|
The file to write. |
required |
frames
|
iterable of ndarray
|
RGB or RGBA images, (height, width, 3 or 4) arrays read as uint8, all the same size. |
required |
fps
|
int
|
Frames per second, |
MOVIE_FPS
|
Returns:
| Type | Description |
|---|---|
int
|
How many frames were written. |
Raises:
| Type | Description |
|---|---|
MovieUnavailable
|
When the machine cannot encode H.264; nothing is written. |
ValueError
|
When |
RuntimeError
|
When the encoder reports an error or takes no frames for
|
Notes
The encoder is asked for an average of BITS_PER_PIXEL bits per pixel
per frame, 7.7 Mbit/s for a 1386 × 1552 view at 30 frames a second,
so a fine mesh's edges are kept. It takes the figure as a target
rather than a rule: a view it compresses well comes out under it
(about half a megabyte a second of video for a view that size of a
dense cone), and detail it cannot compress comes out over it.
Source code in src/visualdynamics/gui/movie.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
mp4_reading
¶
The codec's four-character code and the frame count, read from an MP4's own boxes.
The sample description says what the frames were encoded as, and
the sample-size table how many there are. b'avc1' is H.264, and
b'mp4v' is the MPEG-4 Part 2 file Qt writes when it has no H.264
encoder. Returns (None, 0) when the file has no sample table.
The file is walked box by box and never searched for bytes, because the encoded frames can hold any four bytes.
Source code in src/visualdynamics/gui/movie.py
check
¶
Check that this installation can save an animation: render one
second of a turning mesh off screen, encode it to path, and read
the file back.
visualdynamics --check-movie out.mp4 runs this and exits with the
returned code. The packaged builds are tested with it (the Windows
release smoke test and the macOS build), because the files a bundle
carries do not show whether the encoder inside them works.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or Path
|
The movie to write. |
required |
Returns:
| Type | Description |
|---|---|
tuple of (int, str)
|
0 and what was written when the file is H.264 with every frame;
2 and the reason when the machine has no H.264 encoder, or no
OpenGL to render with ( |