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.

Current Version

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:

macOS (.dmg)
Windows (.exe)
Linux (.AppImage)

Install from Source

bash Terminal
# 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

  1. Launch FrameFuseVid
  2. Click "Scan Folder" and select the folder containing your Zoom cloud recording files
  3. The app auto-detects file types (screen share, speaker, gallery, audio, transcript)
  4. Choose a layout — PIP, Side-by-Side, Sequential, or Audio Merge
  5. Adjust settings (overlay position, size, quality preset)
  6. Click "Combine" and choose an output location
  7. Wait for processing — a progress bar shows percentage and FPS
  8. Done. Click "Show in Folder" to open the output.

System Requirements

ComponentMinimumRecommended
OSmacOS 10.13+ / Windows 10 / Ubuntu 18.04+Latest stable
RAM4 GB8 GB+
Storage500 MB + video spaceSSD
CPUIntel Core i5 / Apple M1Modern 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.

Tip

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

PresetEncoder SpeedCRFTrade-off
Fastultrafast28Larger file, faster encode
Mediummedium23Balanced quality and speed
Slowslow20Smaller 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.

Note

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:

  1. Click "Combine"
  2. Choose an output file name and location
  3. The progress bar shows: percentage complete, current timecode, and encoding FPS
  4. You can cancel at any time — the partial output file is cleaned up
  5. On completion, use "Show in Folder" to locate the output

Architecture

FrameFuseVid follows a standard Electron architecture with strict process isolation:

RENDERER PROCESS (sandboxed, no Node.js access) App.jsx VideoPreview DraggableOverlay LayoutPreview preload.js — IPC Bridge MAIN PROCESS (full Node.js access) Window Mgmt BrowserWindow FFmpeg Engine fluent-ffmpeg File Scanner Pattern matching Storage electron-store macOS / Windows / Linux · Bundled FFmpeg Binary · Local File System

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

text Directory Layout
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:

MethodReturnsDescription
openFiles()string[]Opens native file picker, returns selected paths
openFolder()stringOpens native folder picker
saveFile()stringOpens save dialog, returns output path
getFileInfo(path)objectFFprobe metadata (duration, resolution, fps, codec)
scanFolder(path)object[]Recursively scan folder, auto-detect file types
combine(options)voidStart FFmpeg combine operation
cancelProcess()voidKill running FFmpeg process
openPath(path)voidShow file in OS file manager
getStore(key)anyRead from persistent storage
setStore(key, val)voidWrite to persistent storage
getVersion()stringApp version from package.json

Event Listeners

javascript Subscribing to FFmpeg events
// 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

ffmpeg Generated filter complex for PIP layout
# 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

ffmpeg Generated filter complex for side-by-side
# 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

bash Build commands
# 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:

TypeFilename PatternsExample
Screen Shareshared_screen, screenshare, screen_sharezoom_shared_screen_recording.mp4
Speaker Viewspeaker, active_speaker, _as_, _avo_zoom_speaker_recording.mp4
Gallery Viewgallery, _gv_, _gvo_zoom_gallery_view.mp4
Audioaudio_only, .m4a, .mp3audio_only.m4a
Transcript.vtt, .srtclosed_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

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Make changes and test with npm run dev
  4. Commit with descriptive messages
  5. 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