DEV Community

Abhirup Pal
Abhirup Pal

Posted on

4 1 1

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.

Image of Datadog

Master Mobile Monitoring for iOS Apps

Monitor your app’s health with real-time insights into crash-free rates, start times, and more. Optimize performance and prevent user churn by addressing critical issues like app hangs, and ANRs. Learn how to keep your iOS app running smoothly across all devices by downloading this eBook.

Get The eBook

Top comments (1)

Collapse
 
chen_ce2c53c4b554 profile image
FL • Edited

Hello, blogger! Why is the size of the downloaded video 0B after I compress it? What is the reason for this?

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay