Plugins

Video Compressor

Compress videos in the browser using FFmpeg before upload.

Video Compressor

The PluginVideoCompressor uses FFmpeg.wasm to compress videos directly in the browser before upload.

Video compression requires additional dependencies and runs entirely in the browser. It can be CPU-intensive and may take significant time for large videos.

Installation

First, install the required FFmpeg packages:

Terminal
pnpm add @ffmpeg/ffmpeg @ffmpeg/util

Usage

import { PluginVideoCompressor } from "nuxt-upload-kit"

const uploader = useUploadKit({
  plugins: [
    PluginVideoCompressor({
      maxWidth: 1280,
      maxHeight: 720,
      videoBitrate: "1M",
      audioBitrate: "128k",
    }),
  ],
})

Options

OptionTypeDefaultDescription
maxWidthnumber1920Maximum video width
maxHeightnumber1080Maximum video height
videoBitratestring'2M'Video bitrate (e.g., '1M', '2M', '500k')
audioBitratestring'128k'Audio bitrate
fpsnumber30Target frame rate
codecstring'libx264'Video codec
presetstring'medium'Encoding preset (ultrafast to veryslow)
crfnumber23Constant Rate Factor (0-51, lower = better quality)

How It Works

  1. Load FFmpeg - Downloads FFmpeg.wasm (~30MB) on first use
  2. Write file - Copies video to FFmpeg's virtual filesystem
  3. Transcode - Runs FFmpeg with specified options
  4. Read output - Retrieves compressed video
  5. Compare - Only uses compressed version if smaller

Using the FFmpeg Composable

For more control, use the useFFMpeg composable directly:

const { load, convert, unload, status, progress, onConvertSuccess } = useFFMpeg({
  inputUrl: videoUrl,
  convertOptions: ["-preset", "medium", "-crf", "23"],
})

// Load FFmpeg (downloads WASM core on first call)
await load()

// Convert the video
const compressedFile = await convert([])

Encoding Presets

FFmpeg presets trade speed for compression:

PresetSpeedCompressionUse Case
ultrafastFastestWorstTesting
veryfastVery fastPoorQuick preview
fastFastBelow averageDevelopment
mediumBalancedGoodDefault
slowSlowBetterProduction
veryslowSlowestBestMaximum compression
PluginVideoCompressor({
  preset: "fast", // Faster encoding, larger files
})

CRF (Quality)

Constant Rate Factor controls quality vs size:

CRFQualityFile Size
18Visually losslessLargest
23DefaultBalanced
28GoodSmaller
35AcceptableMuch smaller
PluginVideoCompressor({
  crf: 28, // Smaller files, slightly lower quality
})

Resolution Scaling

Videos are scaled down if they exceed max dimensions:

PluginVideoCompressor({
  maxWidth: 1280, // Scale to 720p max
  maxHeight: 720,
})

Aspect ratio is always preserved.

Browser Compatibility

FFmpeg.wasm requires:

  • SharedArrayBuffer - Requires specific HTTP headers
  • WebAssembly - Supported in all modern browsers

Required HTTP Headers

Your server must return these headers:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

In Nuxt, add to your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    "/**": {
      headers: {
        "Cross-Origin-Embedder-Policy": "require-corp",
        "Cross-Origin-Opener-Policy": "same-origin",
      },
    },
  },
})

Progress Tracking

Video compression can take time. Track progress:

uploader.on("video-compressor:progress", ({ file, progress }) => {
  console.log(`Compressing ${file.name}: ${progress}%`)
})

uploader.on("video-compressor:complete", ({ file, originalSize, compressedSize }) => {
  const saved = (((originalSize - compressedSize) / originalSize) * 100).toFixed(1)
  console.log(`Compressed ${file.name}: saved ${saved}%`)
})

Example: Full Video Upload Setup

<script setup>
import { PluginVideoCompressor } from 'nuxt-upload-kit'

const compressionProgress = ref<Record<string, number>>({})

const uploader = useUploadKit({
  allowedFileTypes: ['video/*'],
  maxFileSize: 500 * 1024 * 1024, // 500MB
  plugins: [
    PluginVideoCompressor({
      maxWidth: 1920,
      maxHeight: 1080,
      videoBitrate: '2M',
      preset: 'medium',
      crf: 23
    })
  ]
})

uploader.on('video-compressor:progress', ({ file, progress }) => {
  compressionProgress.value[file.id] = progress
})
</script>

<template>
  <div v-for="file in uploader.files" :key="file.id">
    <span>{{ file.name }}</span>

    <!-- Show compression progress -->
    <div v-if="compressionProgress[file.id] !== undefined">Compressing: {{ compressionProgress[file.id] }}%</div>

    <!-- Show upload progress -->
    <div v-else-if="file.status === 'uploading'">Uploading: {{ file.progress.percentage }}%</div>
  </div>
</template>

Performance Tips

  1. Use appropriate presets - fast or veryfast for development
  2. Limit resolution - 720p is often sufficient for web
  3. Consider server-side - For large videos, server-side transcoding may be better
  4. Show progress - Long operations need user feedback
  5. Handle errors - FFmpeg can fail on unsupported codecs
Copyright © 2026