DEV Community

Abhirup Pal
Abhirup Pal

Posted on

Compressing videos to webm in the browser

πŸš€ Supercharge Your Web Videos: MP4 to WebM Compression with React

Ever bored with nothing interesting at work? Well, that’s when I decided to scratch my itch to tinker around with the current state of browser APIs. Could we compress videos directly through web APIs? In this blog, I’ll show you how to use modern browser features to compress MP4 videos to WebM formatβ€”all within a React app.

πŸ› οΈ What You'll Need

Before we dive in, make sure you've got:

  • React with Typescript
  • Ant Design to build some nice UI.

Quick setup:

npm install antd
Enter fullscreen mode Exit fullscreen mode

Setting up the component

Let's set up our React component with all the React imports:

import { useState, useRef, useEffect, ChangeEvent } from "react";
import { Button, Progress, message, Flex } from "antd";

const VideoCompression = () => {
  const [sourceVideo, setSourceVideo] = useState<File | null>(null);
  const [compressedVideo, setCompressedVideo] = useState<Blob | null>(null);
  const [isCompressing, setIsCompressing] = useState(false);
  const [progress, setProgress] = useState(0);
  const [width, setWidth] = useState<string>("");
  const [height, setHeight] = useState<string>("");
  const videoRef = useRef<HTMLVideoElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
Enter fullscreen mode Exit fullscreen mode

Accepting the File Upload

We need a way to choose our MP4 file:

const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
  if (!event.target.files) return;
  const file = event.target.files[0];
  if (file && file.type.startsWith("video/")) {
    setSourceVideo(file);
    setCompressedVideo(null);
  } else {
    message.error("Please select a valid video file.");
  }
};
Enter fullscreen mode Exit fullscreen mode

Extracting Video Metadata

Let's get the video metadata:

useEffect(() => {
  if (sourceVideo) {
    const video = document.createElement("video");
    video.onloadedmetadata = () => {
      setWidth(video.videoWidth.toString());
      setHeight(video.videoHeight.toString());
    };
    video.src = URL.createObjectURL(sourceVideo);
  }
}, [sourceVideo]);
Enter fullscreen mode Exit fullscreen mode

Video Compression

Here's where the magic happens:


  const compressVideo = async () => {
    if (!sourceVideo) {
      message.error("Please upload a video first.");
      return;
    }
    setIsCompressing(true);
    setProgress(0);
    try {
      const stream = videoRef.current?.captureStream();
      const mediaRecorder = new MediaRecorder(stream, {
        mimeType: "video/webm",
        videoBitsPerSecond: 1000000,
      });
      const chunks: BlobPart[] = [];
      mediaRecorder.ondataavailable = (event) => {
        if (event.data.size > 0) {
          chunks.push(event.data);
        }
      };
      mediaRecorder.onstop = () => {
        const blob = new Blob(chunks, { type: "video/webm" });
        setCompressedVideo(blob);
        setIsCompressing(false);
        setProgress(100);
      };
      if (!videoRef.current) return;
      videoRef.current.onloadedmetadata = () => {
        videoRef.current!.muted = true;
        videoRef.current?.play();
        mediaRecorder.start();
      };
      videoRef.current.onended = () => {
        mediaRecorder.stop();
        videoRef.current?.pause();
      };
      videoRef.current.ontimeupdate = () => {
        if (!videoRef.current) return;
        const progress =
          (videoRef.current.currentTime / videoRef.current.duration) * 100;
        setProgress(progress);
      };
      if (!videoRef.current) return;
      videoRef.current.width = Number.parseFloat(width);
      videoRef.current.height = Number.parseFloat(height);
      videoRef.current.src = URL.createObjectURL(sourceVideo);
    } catch (err) {
      message.error("Error compressing video: " + (err as Error).message);
      setIsCompressing(false);
    }
  };
Enter fullscreen mode Exit fullscreen mode

Downloading the Compressed Video

const downloadCompressedVideo = () => {
  if (compressedVideo) {
    const url = URL.createObjectURL(compressedVideo);
    const a = document.createElement("a");
    a.href = url;
    a.download = "compressed_video.webm";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }
};

Enter fullscreen mode Exit fullscreen mode

πŸš€ Launch Time: Putting It All Together

Here's a sneak peek of our complete work:

Image description

Deployment Link:
https://abhirup-99.github.io/browser-compression-webm/

Code Link:
https://github.com/Abhirup-99/browser-compression-webm

πŸŽ‰ Wrap-up: You're Now a Video Compression Wizard!

Congratulations! You've just built a powerful MP4 to WebM video compressor using React. Your web videos will now load faster than ever, delighting users and boosting your site's performance.

πŸš€ Next Steps:

  • I will be tinkering with the browser compression APIs further and hopefully there will be an blog out soon.

Top comments (0)