While Vercel excels at frontend delivery, it's not designed for hosting or streaming large media assets like Video on Demand (VOD). To ensure fast, reliable video playback, you should offload video storage, processing, and delivery to specialized services. This will allow Vercel to focus on delivering dynamic user interfaces.

Choose the Right Storage and Delivery Setup

Avoid storing .mp4 files in your Vercel project or static assets. Doing so increases build times and degrades performance. Instead, use a dedicated media pipeline:

ComponentRecommended ToolPurpose
StorageGoogle Cloud Storage, CincopaStore raw or encoded video assets.
TranscodingFFmpeg, CincopaConvert videos to adaptive formats (e.g., HLS).
CDN DeliveryCloudFront, CincopaDistribute video segments with low latency.

Implement Adaptive Streaming with HLS

To improve playback reliability across varying bandwidth conditions, transcode each video into multiple resolutions (360p, 720p, 1080p, etc.) and generate an HLS master playlist (.m3u8).

FFmpeg Example: Creating HLS Segments

code
ffmpeg -i input.mp4 \
-preset fast -g 48 -sc_threshold 0 \
-map 0:v -map 0:a \
-s:v:0 1920x1080 -b:v:0 5000k \
-s:v:1 1280x720 -b:v:1 2800k \
-s:v:2 854x480 -b:v:2 1400k \
-s:v:3 640x360 -b:v:3 800k \
-c:v libx264 -c:a aac -f hls \
-hls_time 6 -hls_playlist_type vod \
-hls_segment_filename "output_%v/data%02d.ts" \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0 v:3,a:0" output_%v.m3u8

Explanations:

  • -g 48: Sets keyframe interval (important for smooth switching)
  • -sc_threshold 0: Disables scene change detection to keep GOP size consistent
  • -s:v:N and -b:v:N: Set resolution and bitrate for each rendition
  • -f hls: Output format set to HLS
  • -hls_time 6: Splits video into 6-second chunks
  • -var_stream_map: Maps video and audio renditions to the master playlist

Once transcoding is done, upload the segments and playlists to your CDN for delivery.

Adaptive Bitrate Streaming

Optimize Video Player Performance in Next.js

Use getStaticProps with ISR (Incremental Static Regeneration) to pre-render video pages while keeping them up-to-date without full redeploys.

Example: Static Generation with ISR

code
// pages/video/[id].js
export async function getStaticProps({ params }) {
const res = await fetch(`https://your-api.com/videos/${params.id}`);
const video = await res.json();

return {
props: {
videoUrl: video.hls_url,
},
revalidate: 60, // Revalidate every 60 seconds
};
}

Explanations:

  • getStaticProps: Generates static video pages at build time or on-demand
  • revalidate: 60: Keeps content fresh every minute
  • Reduces server load and improves performance on frequently accessed videos.

Edge Caching and Revalidation

Use edge caching for video metadata and API responses to reduce latency and improve scalability.

Example: Set Cache-Control Headers in API Routes

code
// pages/api/videos/[id].ts
export default async function handler(req, res) {
const video = await getVideoFromCMS(req.query.id);

res.setHeader('Cache-Control', 's-maxage=300, stale-while-revalidate');
res.status(200).json(video);
}

Explanations:

  • s-maxage=300: CDN caches the response for 5 minutes.
  • stale-while-revalidate: Allows serving cached data while a new version is fetched in the background.

Tip: Configure your CDN to cache HLS segments for longer durations (e.g., 1 year) since they don't change after upload.