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)