FrameFuseVid Documentation
FrameFuseVid is a cross-platform desktop application that combines multiple Zoom cloud recording files into a single professional video. It processes everything locally — no files are uploaded to any server.
You are reading documentation for FrameFuseVid v0.1.0. For the latest release, check the GitHub releases page.
Installation
Download the latest release for your platform from GitHub:
Install from Source
# Clone the repository
git clone https://github.com/mkhalid-s/framefusevid.git
cd framefusevid
# Install dependencies
npm install
# Start in development mode
npm run dev
Quick Start
- Launch FrameFuseVid
- Click "Scan Folder" and select the folder containing your Zoom cloud recording files
- The app auto-detects file types (screen share, speaker, gallery, audio, transcript)
- Choose a layout — PIP, Side-by-Side, Sequential, or Audio Merge
- Adjust settings (overlay position, size, quality preset)
- Click "Combine" and choose an output location
- Wait for processing — a progress bar shows percentage and FPS
- Done. Click "Show in Folder" to open the output.
System Requirements
| Component | Minimum | Recommended |
|---|---|---|
| OS | macOS 10.13+ / Windows 10 / Ubuntu 18.04+ | Latest stable |
| RAM | 4 GB | 8 GB+ |
| Storage | 500 MB + video space | SSD |
| CPU | Intel Core i5 / Apple M1 | Modern multi-core |
Selecting Files
FrameFuseVid offers two ways to import recordings:
Folder Scanning
Click "Scan Folder" to pick a directory. The app recursively scans for supported video, audio, and subtitle files and auto-assigns types based on Zoom's naming conventions (see File Detection Patterns).
Manual Selection
Click "Add Files" to pick individual files. You can manually override the detected file type using the dropdown next to each file.
Each file shows metadata extracted via FFprobe:
- Resolution (e.g., 1920×1080)
- Duration
- File size
- Frame rate (FPS)
- Codec information
Layout Options
Picture-in-Picture (PIP)
Overlays a secondary video on top of the main video. The overlay is resizable (10%–50%) and can be dragged to any position or snapped to corners.
Use PIP when your main content is a screen share and you want the speaker visible in a small overlay.
Side-by-Side
Splits the frame into a 50/50 horizontal layout with both videos playing simultaneously. Both inputs are scaled and padded to fill their half.
Sequential
Concatenates videos one after another. Optionally burns VTT/SRT subtitles into the output. Useful for multi-segment recordings.
Audio Merge
Replaces the audio track of a video with a separate audio file (e.g., the Zoom audio-only M4A). Keeps the original video stream untouched.
PIP Customization
When the PIP layout is selected, additional controls appear:
- Overlay Size — slider from 10% to 50% of the main video dimensions
- Position Presets — Top-Left, Top-Right, Bottom-Left, Bottom-Right
- Custom Position — drag the overlay in the live preview to place it anywhere
- Source Selection — choose which file is the main video and which is the overlay
Positions are stored as percentages, so they scale correctly regardless of output resolution.
Quality Presets
| Preset | Encoder Speed | CRF | Trade-off |
|---|---|---|---|
| Fast | ultrafast | 28 | Larger file, faster encode |
| Medium | medium | 23 | Balanced quality and speed |
| Slow | slow | 20 | Smaller file, best quality |
All presets use H.264 video codec and AAC audio at 192 kbps.
Subtitle Burning
When a VTT or SRT file is present in the file list, enable "Burn Subtitles" to hard-code captions into the video pixels. This ensures captions are visible on any player without separate subtitle file support.
Burned subtitles cannot be toggled off during playback. If you need toggleable captions, keep the subtitle file separate alongside the video.
Processing
After configuring your layout and settings:
- Click "Combine"
- Choose an output file name and location
- The progress bar shows: percentage complete, current timecode, and encoding FPS
- You can cancel at any time — the partial output file is cleaned up
- On completion, use "Show in Folder" to locate the output
Architecture
FrameFuseVid follows a standard Electron architecture with strict process isolation:
Security Model
- Context Isolation — the renderer has no direct access to Node.js APIs
- Preload Bridge — only whitelisted IPC channels are exposed via
contextBridge - No Remote Code — all processing is local; no external URLs loaded
- No Telemetry — zero data collection or network calls
Project Structure
framefusevid/
├── src/
│ ├── main/
│ │ ├── main.js # Electron main process, IPC handlers, FFmpeg
│ │ └── preload.js # Context bridge, exposes electronAPI
│ ├── App.jsx # React UI (file select, preview, process, done)
│ ├── index.jsx # React entry point
│ └── index.css # Tailwind CSS directives
├── docs/ # GitHub Pages documentation site
├── .github/
│ ├── workflows/
│ │ ├── build.yml # CI: build + release on all platforms
│ │ └── deploy-docs.yml # CI: deploy docs to GitHub Pages
│ └── ISSUE_TEMPLATE/ # Bug report & feature request templates
├── vite.config.js # Vite bundler config
├── tailwind.config.js # Tailwind theme
├── package.json # Scripts, dependencies, electron-builder config
└── README.md # User-facing readme
IPC API Reference
The preload script exposes window.electronAPI with the following methods:
| Method | Returns | Description |
|---|---|---|
openFiles() | string[] | Opens native file picker, returns selected paths |
openFolder() | string | Opens native folder picker |
saveFile() | string | Opens save dialog, returns output path |
getFileInfo(path) | object | FFprobe metadata (duration, resolution, fps, codec) |
scanFolder(path) | object[] | Recursively scan folder, auto-detect file types |
combine(options) | void | Start FFmpeg combine operation |
cancelProcess() | void | Kill running FFmpeg process |
openPath(path) | void | Show file in OS file manager |
getStore(key) | any | Read from persistent storage |
setStore(key, val) | void | Write to persistent storage |
getVersion() | string | App version from package.json |
Event Listeners
// Listen for FFmpeg process start
window.electronAPI.onFFmpegStarted(() => {
console.log('Encoding started');
});
// Listen for progress updates
window.electronAPI.onFFmpegProgress((progress) => {
console.log(progress.percent, progress.timemark);
});
FFmpeg Pipeline
FrameFuseVid constructs FFmpeg filter graphs dynamically based on the selected layout:
PIP Filter Chain
# Scale overlay to percentage of main video
[1:v]scale=iw*0.25:-1[pip];
# Position overlay at user-defined X/Y coordinates
[0:v][pip]overlay=x:y[out]
Side-by-Side Filter Chain
# Scale both inputs to half width, same height, with padding
[0:v]scale=960:1080:force_original_aspect_ratio=decrease,
pad=960:1080:(ow-iw)/2:(oh-ih)/2[left];
[1:v]scale=960:1080:force_original_aspect_ratio=decrease,
pad=960:1080:(ow-iw)/2:(oh-ih)/2[right];
# Stack horizontally
[left][right]hstack[out]
Building & Packaging
# Development (Vite dev server + Electron with hot-reload)
npm run dev
# Build for current platform
npm run build
# Platform-specific builds
npm run build:mac # macOS .dmg
npm run build:win # Windows .exe (NSIS installer)
npm run build:linux # Linux .AppImage
Builds are output to the dist/ directory. The GitHub Actions workflow in .github/workflows/build.yml runs matrix builds on Ubuntu, Windows, and macOS and publishes release artifacts automatically when a version tag is pushed.
File Detection Patterns
FrameFuseVid identifies Zoom recording types by matching filename patterns:
| Type | Filename Patterns | Example |
|---|---|---|
| Screen Share | shared_screen, screenshare, screen_share | zoom_shared_screen_recording.mp4 |
| Speaker View | speaker, active_speaker, _as_, _avo_ | zoom_speaker_recording.mp4 |
| Gallery View | gallery, _gv_, _gvo_ | zoom_gallery_view.mp4 |
| Audio | audio_only, .m4a, .mp3 | audio_only.m4a |
| Transcript | .vtt, .srt | closed_caption.vtt |
Supported Formats
Input
- Video: MP4, MOV, M4V, AVI, MKV, WebM
- Audio: M4A, MP3, AAC, WAV
- Subtitles: VTT, SRT
Output
- MP4 (H.264 + AAC) — default
- MKV, MOV
FAQ
Does FrameFuseVid upload my files anywhere?
No. All processing happens locally on your machine using the bundled FFmpeg binary. There are no network calls, no telemetry, and no cloud dependencies.
Can I use this with non-Zoom recordings?
Yes. While auto-detection is optimized for Zoom naming conventions, you can manually add any supported video/audio file and assign its type.
Why is the macOS build unsigned?
The current builds are not code-signed with an Apple Developer certificate. On first launch, macOS may block the app. Right-click the .app and select "Open" to bypass Gatekeeper, or allow it in System Settings > Privacy & Security.
How do I get the best quality output?
Use the Slow quality preset (CRF 20, slow encoder). This produces the smallest file at the highest quality, but encoding takes longer.
Can I process multiple recordings at once?
Batch processing is on the roadmap but not yet available in v0.1.0. Currently you process one set of recordings at a time.
Contributing
We welcome contributions. See the full Contributing Guide for details.
Development Workflow
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Make changes and test with
npm run dev - Commit with descriptive messages
- Push and open a Pull Request against
main
Code Style
- ES6+ features, functional React components with hooks
- Meaningful variable names; comments for complex logic only
- Keep components under ~300 lines when possible