Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> HTML

Stream video đến HTML5 player với Node.js (hỗ trợ seek/controls)

Để video player HTML5 hoạt động đầy đủ (seek, pause, volume, duration), server phải hỗ trợ HTTP Range Requests (partial content - status 206). Node.js dùng fs.createReadStream để stream phần dữ liệu được yêu cầu.

Server Node.js - Express (Complete)

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();

app.get('/video', (req, res) => {
  const videoPath = path.join(__dirname, 'videos', 'sample.mp4');
  const stat = fs.statSync(videoPath);
  const fileSize = stat.size;
  const range = req.headers.range;
  
  if (!range) {
    // Không có header Range -> gửi toàn bộ (hoặc redirect)
    res.writeHead(200, {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4'
    });
    fs.createReadStream(videoPath).pipe(res);
    return;
  }
  
  // Parse Range header: "bytes=start-end"
  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 head = {
    'Content-Range': `bytes ${start}-${end}/${fileSize}`,
    'Accept-Ranges': 'bytes',
    'Content-Length': chunkSize,
    'Content-Type': 'video/mp4'
  };
  
  res.writeHead(206, head); // 206 Partial Content
  
  const stream = fs.createReadStream(videoPath, { start, end });
  stream.pipe(res);
});

app.listen(3000, () => console.log('Server chạy cổng 3000'));

Client HTML5

<video width="640" height="360" controls>
  <source src="/video" type="video/mp4">
</video>

Giải thích Range Request

  1. Browser gửi request kèm header Range: bytes=0- (lúc đầu) hoặc Range: bytes=1000000- (khi seek).
  2. Server parse start/end, đọc file từ vị trí đó.
  3. Server trả về 206 Partial Content kèm header Content-Range, Content-Length của chunk.
  4. Browser cập nhật duration, cho phép seek mượt mà.

Nâng cao: Middleware reusable

function videoStreamMiddleware(videoDir) {
  return (req, res, next) => {
    const videoPath = path.join(videoDir, req.params.filename);
    // ... logic range request như trên ...
  };
}

app.get('/video/:filename', videoStreamMiddleware('./videos'));

Thư viện sẵn có (Khuyên dùng production)

  • express-range - Middleware range request đơn giản.
  • h5p-video-stream - Hỗ trợ đầy đủ.
  • node-media-server - RTMP/HTTP-FLV/HLS/DASH server.

Lưu ý quan trọng

  • Video phải được encode với moov atom ở đầu (faststart) để stream được. Dùng ffmpeg: ffmpeg -i input.mp4 -movflags faststart output.mp4.
  • Hỗ trợ Accept-Ranges: bytes header cho browser biết server hỗ trợ range.
  • CORS header nếu client khác domain: Access-Control-Allow-Origin: *.
  • Cache-Control: public, max-age=31536000, immutable cho video tĩnh.