Search code examples
node.jsvideonext.jsvideo-streaming

Video Streaming 500 error in production (NextJS)


I have been developing this site: https://mirobod-tumani.vercel.app. On the home page there is a video background. To optimize the initial load, i optimized the video itself and decided to stream it. This is the code:

// pages/api/video

import fs from 'fs'

export default function handler(req, res) {
if (req.method === 'GET') {
    const range = req.headers.range
    const videoPath = 'public/assets/mirobod.webm'
    const videoSize = fs.statSync(videoPath).size
    const chunkSize = 1 * 1e6
    const start = Number(range.replace(/\D/g, ''))
    const end = Math.min(start + chunkSize, videoSize - 1)
    const contentLength = end - start + 1
    const headers = {
      'Content-Range': `bytes ${start}-${end}/${videoSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': contentLength,
      'Content-Type': 'video/webm',
    }
    res.writeHead(206, headers)
    const stream = fs.createReadStream(videoPath, { start, end })
    stream.pipe(res)
}

// pages/index.js

<video autoPlay muted loop>
 <source src='/api/video' type='video/webm' />
</video>

Locally, when i navigate to root localhost:3000/ it is working as i expect, but when i try to open that /api/video api differently, for example, when i navigate to localhost:3000/api/video or call it in Postman, there is an error saying Cannot read property 'replace' of undefined, i undestand that req.headers.range is undefined. Also in production there is a 500 error with that api and not showing the video as you can see from the site link above.

I tried:

if(!req.headers.range) // send the whole video at one

else ...

But in this case, this is not what i want from streaming and also this leads Exceeding Serverless Function Payload Size Limit

So what is the problem, what should i do? Thank you in advance!


Solution

  • I found an answer, posting it for anyone having the same issue.

    For what i understood by this https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27

    req.headers.range may be not set for the initial request and be available during stream goes.
    Actually, initial range is equal to 'bytes=0-', and updates during the stream depending on your defined chunk size

    enter image description here

    So it did:

    let range = req.headers.range
    if(!range) range = 'bytes=0-'
    
    ...
    
    

    But there still was a problem I thought it was about the path, then i changed this:

    const videoPath = 'public/assets/mirobod.webm'
    

    to this:

    const videoName = 'mirobod.webm'
    const videoPath = path.resolve('public/assets/', videoName)
    

    And it succeeded!