Efficient Video Streaming in Next.js: Handling Range Requests

Efficient Video Streaming in Next.js: Handling Range Requests

·

3 min read

In modern web applications, delivering large media files like videos efficiently is crucial for performance and user experience. Next.js provides the flexibility to implement server-side logic, making it possible to handle video streaming with support for range requests. This article explains how to create a robust video streaming endpoint in Next.js using server-side logic.


Code Breakdown

Below is the implementation of a video streaming handler in Next.js, It’s more optimal to implement this as server-side logic to make abstraction easier:

import { NextResponse } from 'next/server';
import fs from 'fs';
import path from 'path';

export async function GET(request: Request) {
  const videoPath = path.resolve("public/assets/ngr-hero.mp4");
  const stat = fs.statSync(videoPath);
  const fileSize = stat.size;

  const range = request.headers.get("range");

  if (range) {
    const parts = range.replace(/bytes=/, "").split("-");
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

    const chunkSize = end - start + 1;
    const fileStream = fs.createReadStream(videoPath, { start, end });

    const stream = new ReadableStream({
      start(controller) {
        fileStream.on("data", (chunk) => controller.enqueue(chunk));
        fileStream.on("end", () => controller.close());
        fileStream.on("error", (err) => controller.error(err));
      },
    });

    return new NextResponse(stream, {
      status: 206,
      headers: {
        "Content-Range": `bytes ${start}-${end}/${fileSize}`,
        "Accept-Ranges": "bytes",
        "Content-Length": chunkSize.toString(),
        "Content-Type": "video/mp4",
      },
    });
  }

  const fileStream = fs.createReadStream(videoPath);

  const stream = new ReadableStream({
    start(controller) {
      fileStream.on("data", (chunk) => controller.enqueue(chunk));
      fileStream.on("end", () => controller.close());
      fileStream.on("error", (err) => controller.error(err));
    },
  });

  return new NextResponse(stream, {
    headers: {
      "Content-Length": fileSize.toString(),
      "Content-Type": "video/mp4",
    },
  });
}

How It Works

  1. File Path and Metadata:

    • The path to the video file is resolved using path.resolve.

    • File metadata, such as size, is retrieved using fs.statSync.

  2. Handling Range Requests:

    • If the client requests a specific range of bytes (using the Range header), the server parses the range to determine the start and end of the requested chunk.

    • A stream is created for the specific byte range using fs.createReadStream.

  3. Partial Content Response (206):

    • The server responds with a 206 Partial Content status when serving a chunk of the video.

    • The Content-Range header informs the client about the served range and the total file size.

  4. Full Video Stream:

    • If no range is specified, the entire video is streamed using a readable stream.
  5. Readable Streams:

    • A ReadableStream wraps the file stream to manage data flow efficiently, handling events like data, end, and error.

Key Headers in the Response

  • Content-Range: Specifies the range of bytes served (e.g., bytes 0-1023/204800).

  • Accept-Ranges: Indicates that the server supports byte-range requests.

  • Content-Length: Specifies the size of the served content.

  • Content-Type: Indicates the MIME type (video/mp4 in this case).


Benefits of This Approach

  • Efficient Bandwidth Usage: Only the requested portion of the video is transmitted, saving bandwidth.

  • Faster Playback: Clients can start playing videos immediately by requesting only the initial bytes.

  • Scalability: Streaming large files in chunks avoids overloading the server.


Use Cases

  • Video-on-demand platforms.

  • Progressive playback of large media files.

  • Efficient media delivery in web applications.


This approach gives you the freedom to handle large video streaming via your frontend app, taking off the hassle of having to upload the video to a remote server and access the content via a content delivery network. It's done with Nextjs app router in mind which was released for v13 above.

By implementing this video streaming handler in Next.js, you can enhance the performance and user experience of your media-heavy applications. Please don’t forget to like, share and subscribe