Embedding Videos¶
Including video clips in Fluvie compositions
Embed existing video clips into your compositions with trimming, positioning, and audio control.
Table of Contents¶
- Overview
- EmbeddedVideo Widget
- Trimming and Timing
- Audio from Videos
- Positioning Videos
- Multiple Video Clips
- Best Practices
Overview¶
Fluvie can embed video files into your compositions. During rendering:
- Frame Extraction: Video frames are extracted at your composition's FPS
- Caching: Extracted frames are cached for efficiency
- Compositing: Video frames are composited with other widgets
- Audio Mixing: Video audio is mixed with other audio tracks
Supported Formats¶
Any format supported by FFmpeg: - MP4 (H.264, H.265) - MOV - WebM - AVI - MKV - And many more
EmbeddedVideo Widget¶
Basic Usage¶
Scene(
durationInFrames: 150,
children: [
EmbeddedVideo(
assetPath: 'assets/video.mp4',
startFrame: 0,
durationInFrames: 150,
),
],
)
Properties¶
| Property | Type | Default | Description |
|---|---|---|---|
assetPath |
String |
required | Path to video file |
startFrame |
int |
0 |
Frame to start showing video |
durationInFrames |
int? |
null |
How long to show (null = full length) |
trimStart |
Duration? |
null |
Skip beginning of video |
trimEnd |
Duration? |
null |
Stop at this point in video |
volume |
double |
1.0 |
Audio volume (0.0 - 1.0) |
fit |
BoxFit |
cover |
How video fits container |
alignment |
Alignment |
center |
Position within container |
playbackSpeed |
double |
1.0 |
Speed multiplier |
loop |
bool |
false |
Loop when reaching end |
Trimming and Timing¶
Use Part of a Video¶
EmbeddedVideo(
assetPath: 'assets/footage.mp4',
// Skip first 10 seconds of source video
trimStart: Duration(seconds: 10),
// Stop at 25 seconds of source video
trimEnd: Duration(seconds: 25),
// Display for 5 seconds in composition (at 30fps)
startFrame: 30,
durationInFrames: 150,
)
Timeline Visualization¶
Source Video: [0s----10s====15s====20s====25s----30s]
↓ trimStart trimEnd ↓
Trimmed Section: [=====15 seconds=====]
↓
Composition: [Frame 30------------------Frame 180]
↑ startFrame durationInFrames=150
Speed Up Video¶
EmbeddedVideo(
assetPath: 'assets/timelapse.mp4',
startFrame: 0,
durationInFrames: 90,
playbackSpeed: 2.0, // 2x speed
)
Slow Motion¶
EmbeddedVideo(
assetPath: 'assets/action.mp4',
startFrame: 0,
durationInFrames: 180,
playbackSpeed: 0.5, // Half speed
)
Audio from Videos¶
Include Video Audio¶
EmbeddedVideo(
assetPath: 'assets/clip.mp4',
startFrame: 0,
durationInFrames: 150,
volume: 1.0, // Full volume
)
Mute Video¶
EmbeddedVideo(
assetPath: 'assets/clip.mp4',
startFrame: 0,
durationInFrames: 150,
volume: 0.0, // Muted
)
Lower Video Audio for Background Music¶
Video(
scenes: [
Scene(
durationInFrames: 300,
children: [
EmbeddedVideo(
assetPath: 'assets/interview.mp4',
volume: 0.3, // Lower to hear music
),
],
),
],
audioTracks: [
AudioTrack(
source: AudioSource.asset('assets/background_music.mp3'),
volume: 0.7,
),
],
)
Positioning Videos¶
Full Screen¶
Fixed Size¶
VCenter(
child: SizedBox(
width: 640,
height: 360,
child: EmbeddedVideo(
assetPath: 'assets/clip.mp4',
fit: BoxFit.contain,
),
),
)
Corner Position¶
VPositioned(
right: 20,
bottom: 20,
child: SizedBox(
width: 320,
height: 180,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: EmbeddedVideo(
assetPath: 'assets/pip.mp4',
fit: BoxFit.cover,
),
),
),
)
Picture-in-Picture Layout¶
Scene(
durationInFrames: 300,
children: [
// Main video (full screen)
VPositioned.fill(
child: EmbeddedVideo(
assetPath: 'assets/main.mp4',
fit: BoxFit.cover,
),
),
// PiP video (corner)
VPositioned(
right: 40,
top: 40,
child: Container(
width: 300,
height: 170,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white, width: 3),
boxShadow: [
BoxShadow(
color: Colors.black54,
blurRadius: 10,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(9),
child: EmbeddedVideo(
assetPath: 'assets/pip.mp4',
fit: BoxFit.cover,
volume: 0.0, // Mute PiP
),
),
),
),
],
)
Multiple Video Clips¶
Sequential Clips¶
Video(
fps: 30,
width: 1920,
height: 1080,
scenes: [
Scene(
durationInFrames: 150,
children: [
VPositioned.fill(
child: EmbeddedVideo(assetPath: 'assets/clip1.mp4'),
),
],
),
Scene(
durationInFrames: 150,
transitionIn: SceneTransition.crossFade(durationInFrames: 20),
children: [
VPositioned.fill(
child: EmbeddedVideo(assetPath: 'assets/clip2.mp4'),
),
],
),
Scene(
durationInFrames: 150,
transitionIn: SceneTransition.crossFade(durationInFrames: 20),
children: [
VPositioned.fill(
child: EmbeddedVideo(assetPath: 'assets/clip3.mp4'),
),
],
),
],
)
Side-by-Side¶
Scene(
durationInFrames: 180,
children: [
VRow(
children: [
Expanded(
child: EmbeddedVideo(
assetPath: 'assets/left.mp4',
fit: BoxFit.cover,
volume: 0.5,
),
),
SizedBox(width: 10),
Expanded(
child: EmbeddedVideo(
assetPath: 'assets/right.mp4',
fit: BoxFit.cover,
volume: 0.5,
),
),
],
),
],
)
Overlapping Videos¶
Scene(
durationInFrames: 180,
children: [
// Background video
VPositioned.fill(
child: EmbeddedVideo(
assetPath: 'assets/background.mp4',
fit: BoxFit.cover,
volume: 0.0,
),
),
// Overlay video (with transparency)
VPositioned.fill(
child: Opacity(
opacity: 0.5,
child: EmbeddedVideo(
assetPath: 'assets/overlay.mp4',
fit: BoxFit.cover,
volume: 0.0,
),
),
),
],
)
Looping Videos¶
Auto-Loop¶
Scene(
durationInFrames: 300, // 10 seconds
children: [
VPositioned.fill(
child: EmbeddedVideo(
assetPath: 'assets/short_loop.mp4', // 2-second clip
loop: true, // Will loop ~5 times
volume: 0.0,
),
),
],
)
Best Practices¶
1. Pre-Process Videos¶
For best performance, pre-process your source videos:
# Transcode to consistent format
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 output.mp4
# Trim externally for large files
ffmpeg -i input.mp4 -ss 00:00:10 -t 00:00:15 -c copy trimmed.mp4
2. Match Frame Rates¶
Use source videos with the same FPS as your composition when possible:
Video(
fps: 30, // Composition FPS
scenes: [
Scene(
children: [
// Best: source is also 30fps
EmbeddedVideo(assetPath: 'assets/30fps_clip.mp4'),
],
),
],
)
3. Consider File Size¶
- Large video files take longer to extract frames
- Consider pre-trimming long videos
- Use appropriate resolution for output
4. Handle Missing Videos¶
Widget buildVideo(String? path) {
if (path == null) {
return Container(
color: Colors.black,
child: Center(
child: Icon(Icons.videocam_off, color: Colors.grey),
),
);
}
return EmbeddedVideo(
assetPath: path,
fit: BoxFit.cover,
);
}
5. Disk Space Awareness¶
Frame extraction creates temporary files:
Related¶
- Video Sequence - Multiple video clips in sequence
- Audio - Audio tracks and mixing
- Frame Extraction - How extraction works
- Performance Tips