ストリーミング配信の学習備忘録
ストリーミング再生とは
ストリーミング再生は、データが端末に届くたびに再生する方法であり、ダウンロードとの違いはサーバー上に保管されている動画や音楽を端末にダウンロードしてから再生するかダウンロードしながら再生するかの違い
ストリーミング配信には以下の二つの配信方法がある
- オンデマンド配信
完成した動画をストリーミング配信する方法
事前に専用のストリーミングサーバーにファイルをアップロードし、ユーザーがファイルを読み込みながら再生する方法 - ライブ配信
リアルタイム配信とも呼ばれ、撮影内容をストリーミング配信としてエンコードし動画ファイルをリアルタイムで配信する方法
仕組み
オーディオと動画のデータ(ファイル)をデータパケットとして小さく分割し、クライアントデバイスのブラウザ内のオーディオプレーヤーまたは動画プレーヤーがデータパケットフローを取り出すことで動画またはオーディオとして解釈され配信される
データパケットを転送する上でTCP,UDPといったトランスポートプロトコルを用いる
ストリーミング再生のメリット・デメリットについて
メリット
- 端末のストレージ容量を圧迫しない
- 動画の全データをダウンロードせずとも再生ができる
デメリット
- インターネット回線によっては画質が影響を受ける
ちなみにダウンロードやストリーミング再生以外で映像配信方式としてプログレッシブダウンロードというのがあるらしいです(今回は触れません)
TCPとUDPの違いについて
TCPはデータの送受信を開始する前に送信者と受信者の間でハンドシェイクを行い、接続を確立した上でデータのやり取りを行うため、非常に信頼性が高く、パケットの損失やパケットの順序付けの問題を解決できます
一方、UDPは通信の高速化のためハンドシェイクを必要とせずにやり取りを開始するため、通信の信頼性のリスクが高くなり、再送信、パケットの順序付け、またはエラーチェックのサポートがないため、ネットワークの不具合等により途中でデータが破損する可能性がある
以下のケースに応じてTCPかUDPを選択するそうです
TCP
データ転送速度より信頼性を重視する場合
UDP
少数のデータパケットが失われることなどの信頼性よりも速度の方がはるかに重要なケース
例としてビデオ会議などリアルタイムでのやり取りが重要視されるケース
TCP/UDPのストリーミングプロトコルについて
TCP/UDPでのストリーミングプロトコルには以下のものがあるようです
TCP
- HLS
- MPEG-DASH
UDP
- RTCP
オンデマンド配信の実装
今回はHLSを使って軽くオンデマンド配信に関する実装を行ってみました
ディレクトリ構成
bunコマンドを使って作成しています
├── public
│ ├── hls_video
│ │ ├── output.m3u8
│ │ └── outputXXX.ts
│ └── video.mp4
├── src
│ ├── assets
│ │ └── video.mp4
│ ├── ffmpeg.js
│ └── index.ts
└── tsconfig.json
実装内容
HLSで配信する上で必要なファイルを作成
ffmpeg.jsを用意し実行することで今回必要なHLSファイルを作成します
// ffmpeg.js
const FfmpegInstaller = require("@ffmpeg-installer/ffmpeg");
const FluentFfmpeg = require("fluent-ffmpeg");
FluentFfmpeg.setFfmpegPath(FfmpegInstaller.path);
FluentFfmpeg("./src/assets/video.mp4", {
timeout: 60000,
})
.addOptions([
"-profile:v baseline",
"-level 3.0",
"-start_number 0",
"-hls_time 10",
"-hls_list_size 0",
"-f hls",
])
.output("./public/hls_video/output.m3u8")
.on("end", () => {
console.log("End");
})
.run();
node ./ffmpeg.js
サーバー側の実装(クライアントも)
// src/index.ts
import { Hono } from "hono";
import { html } from "hono/html";
import { serveStatic } from "hono/bun";
const app = new Hono();
app.use("/*", serveStatic({ root: "public/" }));
app.get("/", (c) => {
return c.html(html`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>HLS Player</title>
</head>
<body>
<video id="video" width="500" height="500" controls></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const video = document.getElementById("video");
const videoSrc = "/hls_video/output.m3u8";
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play();
});
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = videoSrc;
video.addEventListener("loadedmetadata", () => {
video.play();
});
}
</script>
<video src="/video.mp4" width="500" height="500" controls />
</body>
</html> `);
});
export default app;
クライアント側ではサーバー側で提供されたhls_video/output.m3u8をhls.jsを使って読み込んでいます
動作確認
ストリーミング再生(オンデマンド)
ダウンロード再生
参考資料
[1] ストリーミング再生とは?ダウンロードとの違いやメリット・デメリットを解説
[2] ビデオストリーミングプロトコル – 概要としくみ
[3] ストリーミングとは?| 動画ストリーミングの仕組み
[4] ストリーミング プロトコル: 知っておくべきこと
[5] Video Streaming with Node.js / HLS
[6] sushantrahate/hls-video-streaming-nodejs-ffmpeg
Discussion