1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! fframes renders videos from Rust code. A video is a type that implements [`Video`]. It declares
//! the size, frame rate and duration once and returns an SVG tree ([`Svgr`]) for every frame from
//! the [`svgr!`] macro. The renderer rasterises the frames on all cores, mixes the audio and
//! encodes the result with FFmpeg.
//!
//! # Pipeline
//!
//! 1. [`Video::duration`], [`Video::define_scenes`] and [`Video::audio`] are resolved into a frame
//! timeline once per render. Scenes ([`Scene`], [`Scenes`], [`Overlap`]) are placed back to back,
//! audio tracks ([`AudioMap`], [`AudioTrack`]) are placed at their sample position.
//! 2. For every frame the renderer builds a [`Frame`] (index, time, scene offset) and a
//! [`FFramesContext`] (media lookups, scene info, video size) and calls
//! [`Video::render_frame`]. Frames are rendered concurrently, so the method must be pure: read
//! precomputed data from `self`, no I/O, no panics.
//! 3. The returned [`Svgr`] becomes a `usvgr::Tree`. With the `compile-time-svgtree` feature the
//! macro emits the tree at compile time and every subtree without `{}` interpolation carries a
//! static hash, which lets the backends cache its rasterisation across frames. Without the
//! feature the markup is a string parsed per frame.
//! 4. A rendering backend ([`FFramesRenderBackend`]) rasterises the tree. The built-in
//! [`cpu::CpuRenderingBackend`] renders one video segment per thread with tiny-skia. The Skia
//! backend in the `fframes_skia_renderer` crate walks the tree on the GPU and also executes
//! [`Shader`] layers.
//! 5. Segments are encoded through FFmpeg ([`EncoderOptions`]), concatenated, and muxed with the
//! audio mix ([`AudioMixOptions`]: summing, ducking, fades, master limiter).
//!
//! # Minimal video
//!
//! ```rust
//! use fframes::{AudioMap, Duration, FFramesContext, Frame, RenderOptions, Svgr, Video, animation::Easing};
//!
//! // Every file in the folder becomes a field; fonts are registered by family name.
//! fframes::include_media_dir!(pub struct Media, "media");
//!
//! struct Hello<'a> {
//! media: &'a Media,
//! title: &'a str,
//! }
//!
//! impl Video for Hello<'_> {
//! const FPS: usize = 30;
//! const WIDTH: usize = 1920;
//! const HEIGHT: usize = 1080;
//!
//! fn duration(&self) -> Duration<'_> {
//! Duration::Seconds(3.0)
//! }
//!
//! fn audio(&self) -> AudioMap<'_> {
//! AudioMap::none()
//! }
//!
//! fn render_frame<'a>(&'a self, frame: Frame, _ctx: &FFramesContext<'a, '_>) -> Svgr<'a> {
//! let opacity = frame.animate(&fframes::timeline!(
//! at 0.0 => 0.5, animate 0.0_f32 => 1.0, Easing::EaseOut
//! ));
//! fframes::svgr!(
//! <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080"
//! width={Self::WIDTH} height={Self::HEIGHT}>
//! <text x="120" y="560" font-family="DM Sans" font-size="150" fill="#fff"
//! opacity={opacity}>
//! "Hello " {self.title}
//! </text>
//! </svg>
//! )
//! }
//! }
//!
//! fn main() -> std::process::ExitCode {
//! let media = Media::prepare().expect("embedded media");
//! let video = Hello { media: &media, title: "world" };
//! // `render`, `frame`, `strip`, `inspect`, `snapshot`, `audio` and more (feature `cli`).
//! fframes::cli::new(&video, RenderOptions { media: Some(&media), ..Default::default() }).run()
//! }
//! ```
//!
//! Without the `cli` feature call [`render`] with the output path, a backend and [`RenderOptions`],
//! or render single frames through [`Previewer`].
//!
//! # Feature flags
//!
//! - `cpu_renderer` (default): the tiny-skia based [`cpu::CpuRenderingBackend`] and the encoding
//! pipeline. Disable it for a `wasm32` build.
//! - `cli`: the [`cli`] module and its `clap` dependency.
//! - `compile-time-svgtree`: [`svgr!`] builds the SVG tree at compile time and hashes static
//! subtrees. Required by the Skia backend and by [`Shader`].
//! - `exif`: EXIF orientation of loaded images.
//! - Codecs `h264`, `h265`, `aac`, `mp3lame`, `opus`, `vpx`: compile the library into the static
//! FFmpeg build. Some of them need `libav-agree-gpl`, `libav-agree-nonfree` or
//! `libav-agree-version3`, which state that you accept the corresponding FFmpeg license terms.
//! - Hardware acceleration `videotoolbox`, `audiotoolbox`, `vaapi`, `nvidia`, `qsv`, `vulkan`,
//! `mediacodec`: enable the platform encoders and decoders in FFmpeg.
//!
//! # FFmpeg
//!
//! Decoding, encoding and muxing use the FFmpeg libraries through `ffmpeg-sys-next`, re-exported
//! as [`ffmpeg_sys_fframes`]. On Linux and macOS FFmpeg is compiled from source during
//! `cargo build` and linked statically, so the toolchain listed in the repository README (nasm,
//! yasm, clang, the codec dev packages) must be installed. On Windows a prebuilt FFmpeg 9 is
//! linked through `FFMPEG_DIR` or vcpkg and the codec features are not available.
//!
pub use *;
// Methods that we are not pub use ::* should be declared here:
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
// reexported deps
pub use crateroxmltree;
pub use fframes_media as media;
pub use *;
pub use lazy_static;
pub use lru;
pub use bytemuck;
pub use ffmpeg_sys_fframes;
pub use serde;
pub use ttf_parser;
pub use usvgr;
pub use exif;