ブラウザ動画再生の原理:videoタグからMSE、EME、WebCodecsまで
ChromeがM3U8を再生できないのはなぜ?HLSがMP4より遅いのはなぜ?BilibiliはどうやってFLVを再生している?Netflixのビデオはなぜ海賊版にされない?答えはブラウザ動画再生技術にあります。このガイドでは、基本的なvideoタグからMSE、EME、WebCodecsまでをカバーします。
ブラウザ動画再生の進化
ブラウザ動画は三世代を経てきました:
第1世代:HTML5 videoタグ(2010年以前)
最もシンプルな形式:
<video src="video.mp4" controls></video>ブラウザネイティブサポート、MP4/WebM単一ファイルを直接再生。制限:
- 「完全なファイル」のみ、ストリーミングなし
- ブラウザネイティブコーデックのみ(H.264、VP8/9)
- 暗号化コンテンツなし
- ライブ配信なし
Flashプラグインがこれらのギャップを埋めていましたが、Flashは終了しました。
第2世代:MSE + EME(2013年〜現在)
Media Source Extensions(MSE)がストリーミング再生を解決:
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
// セグメントデータを動的に追加
fetch('chunk-1.m4s')
.then(res => res.arrayBuffer())
.then(data => sourceBuffer.appendBuffer(data));
});Encrypted Media Extensions(EME)がDRM暗号化を解決、Netflixなどがブラウザで著作権コンテンツを再生可能に。
第3世代:WebCodecs(2021年〜現在)
WebCodecs APIがJSでブラウザのハードウェアコーデックを直接呼び出し可能に、videoタグの制限を突破:
// ビデオフレームをデコード
const decoder = new VideoDecoder({
output: (frame) => {
// 生のVideoFrameを取得、エフェクト適用可能
canvas.getContext('2d').drawImage(frame, 0, 0);
frame.close();
},
error: (e) => console.error(e),
});
decoder.configure({
codec: 'avc1.42E01E',
hardwareAcceleration: 'prefer-hardware',
});
// エンコードデータを供給
decoder.decode(new EncodedVideoChunk({
type: 'key',
timestamp: 0,
data: h264Data,
}));WebCodecsは画面録画、配信、ビデオエフェクト、カスタムプレーヤーを可能にします。
MSE:ストリーミング再生のコア
MSEはモダンウェブビデオの基盤です。すべての主要プレーヤーライブラリ(hls.js、dash.js、flv.js、Shaka Player)がこれに基づいて構築されています。
MSEの仕組み
- MediaSourceオブジェクト作成:JSが仮想「メディアソース」を作成
- videoタグにバインド:
URL.createObjectURL()でblob URLを生成、video.srcに割り当て - SourceBuffer作成:各ストリーム(ビデオ、オーディオ)に独立したバッファ
- セグメントデータ追加:JSがセグメントをダウンロード、ArrayBufferに変換、
sourceBuffer.appendBuffer()を呼び出し - ブラウザがデコードして再生:videoタグがSourceBufferからデータを取得、デコードしてレンダリング
MSEの制限
- fMP4またはWebM必須:ネイティブMSEはTSセグメントをサポートしません — hls.jsがTSをfMP4に変換してからMSEに供給
- コーデックを事前宣言必須:SourceBuffer作成時にコーデックを指定、途中で変更不可
- メモリ管理:追加したデータを手動で削除してメモリ解放
- 厳密な順序:タイムスタンプ順に追加必須、そうでなければエラー
MSEコーデックサポート
| コーデック | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| H.264 (avc1) | ✅ | ✅ | ✅ | ✅ |
| H.265 (hevc) | 🟡 | ❌ | ✅ | 🟡 |
| VP9 | ✅ | ✅ | ✅ (iOS 14+) | ✅ |
| AV1 | ✅ (85+) | ✅ (68+) | ✅ (16.4+) | ✅ |
| AAC | ✅ | ✅ | ✅ | ✅ |
| Opus | ✅ | ✅ | ✅ (部分) | ✅ |
hls.js、dash.js、flv.jsの仕組み
hls.js:非SafariブラウザでHLS再生
hls.jsは典型的なMSEアプリケーション:
- M3U8ダウンロード:fetchがM3U8ファイルをリクエスト
- M3U8解析:JSがテキスト形式を解析、セグメントURLを抽出
- TSセグメントダウンロード:順次に各.tsファイルをフェッチ
- TS → fMP4変換:MPEG-TSコンテナをfMP4に変換(重要ステップ、MSEはTSを受け付けない)
- MSEに追加:
sourceBuffer.appendBuffer()を呼び出してvideoに供給 - アダプティブビットレート:ダウンロード速度とバッファを監視、M3U8品質を切り替え
プロセス全体は開発者に透過です。hls.jsをインポート後、数行で:
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource('https://example.com/stream.m3u8');
hls.attachMedia(video);
}詳細はHLS.js完全ガイド をご覧ください。
dash.js:ブラウザでMPEG-DASH再生
dash.jsはhls.jsと似た動作、違い:
- M3U8(テキスト)ではなくMPD(XML)を解析
- セグメントは通常TSではなくfMP4 — コンテナ変換不要
- fMP4を直接MSEに追加
flv.js:ブラウザでFLV再生
Bilibiliのオープンソースflv.jsはChromeでFLVストリームを再生:
- FLVコンテナヘッダーを解析
- FLVからAACオーディオとH.264ビデオストリームを抽出
- fMP4に再パッケージ
- MSEに追加して再生
詳細はflv.js RTMP/FLVプレイヤーガイド をご覧ください。
EME:DRM暗号化再生
Netflix、Disney+のビデオが海賊版にされないのはEME(Encrypted Media Extensions) + DRMシステムのおかげです。
EMEワークフロー
- ブラウザが暗号化コンテンツをリクエスト:ビデオセグメントは暗号化、直接デコード不可
- JSがEME APIを呼び出し:
MediaKeySystemAccess.createMediaKeys()でDRMセッション作成 - ライセンスリクエスト生成:ブラウザがDRMライセンスサーバーに復号キーをリクエスト
- サーバーがユーザーを検証:Cookie、Tokenなどでユーザー権限を確認
- キー配信:サーバーが暗号化キーを返却
- ブラウザが内部で復号:セキュアエリア(CDM、Content Decryption Module)で
- デコードして再生:復号データがデコーダーに、通常再生
三大DRMシステム
| DRM | ベンダー | ブラウザサポート | 使用ケース |
|---|---|---|---|
| Widevine | Chrome、Firefox、Edge、Android | ウェブデフォルト | |
| PlayReady | Microsoft | Edge、IE、Xbox | Windowsエコシステム |
| FairPlay | Apple | Safari、iOS | Appleエコシステム |
商用プラットフォームは三つのDRMすべてをサポートする必要があります。オープンソース参考:Shaka PlayerのDRM実装。
WebCodecs:ブラウザ動画処理の新時代
WebCodecsはJSがエンコード/デコードを直接制御可能に、videoタグの制限を突破。
WebCodecsでできること
- 画面録画と共有:画面フレームをキャプチャ、H.264/VP9にエンコード、ストリーミングまたは保存
- ビデオエフェクト:ビデオフレームをCanvasにデコード、フィルター適用、再エンコード
- 低遅延再生:カスタムプレーヤー、videoタグのオーバーヘッドを回避
- ビデオトランスコード:ブラウザでトランスコード、サーバー不要
- AIビデオ処理:ビデオフレームをTensorFlow.jsに供給して認識
画面録画配信の例
// 画面をキャプチャ
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
// WebCodecsでエンコード
const track = stream.getVideoTracks()[0];
const processor = new MediaStreamTrackProcessor({ track });
const reader = processor.readable.getReader();
const encoder = new VideoEncoder({
output: (chunk, meta) => {
// エンコードされたH.264データをWebSocket/WebRTCで送信
sendChunk(chunk);
},
error: (e) => console.error(e),
});
encoder.configure({
codec: 'avc1.42E01E',
width: 1920,
height: 1080,
bitrate: 4_000_000,
framerate: 30,
});
while (true) {
const { done, value: frame } = await reader.read();
if (done) break;
encoder.encode(frame, { keyFrame: false });
frame.close();
}動画再生カクつきの根本原因
原理を理解すればカクつきの原因を特定しやすくなります:
1. ネットワークボトルネック
- ダウンロード速度 < ビデオビットレート:バッファが枯渇、カクつき不可避
- 診断:
video.bufferedでバッファ長を確認、2秒未満は危険 - 解決策:ビットレートを下げる、ABRで低品質に切り替え、CDNアクセラレーション
2. デコードボトルネック
- CPU/GPU使用率高:デコード速度が再生に追いつかない
- 診断:Chrome DevTools Performanceパネル
- 解決策:ハードウェアデコード、解像度を下げる、フレームレートを下げる
3. MSEバッファ管理
- SourceBuffer満杯:追加失敗、再生中断
- 診断:
sourceBuffer.updateendイベントを監視、sourceBuffer.bufferedを確認 - 解決策:定期的に
sourceBuffer.remove()で再生済みコンテンツをクリーンアップ
4. メインスレッドブロック
- JS実行がビデオレンダリングをブロック:大きな計算、長いタスクがメインスレッドをブロック
- 診断:PerformanceパネルでLong Tasksを確認
- 解決策:計算をWeb Workerに移動、
requestIdleCallback使用
5. GCポーズ
- 大きなArrayBufferがGCをトリガー:セグメント追加で大量のメモリ割り当て
- 診断:PerformanceパネルのMemoryビュー
- 解決策:ArrayBufferを再利用、セグメントサイズを制御
ブラウザ動画技術の境界
ウェブ動画再生で何ができるか?
| 機能 | videoタグ | MSE | WebCodecs |
|---|---|---|---|
| MP4単一ファイル再生 | ✅ | ✅ | ✅ |
| HLSストリーム再生 | ❌ | ✅ (hls.js) | ✅ |
| DASHストリーム再生 | ❌ | ✅ (dash.js) | ✅ |
| DRMコンテンツ再生 | ❌ | ✅ (EME) | ✅ |
| リアルタイム画面配信 | ❌ | ❌ | ✅ |
| ビデオフレームエフェクト | ❌ | ❌ | ✅ |
| カスタムコーデック | ❌ | ❌ | ✅ |
| ハードウェアアクセラレーテッドデコード | ✅ | ✅ | ✅ |
MP4ネイティブ vs HLS再生
HLSがMP4より遅いのはなぜ?原理から:
| 次元 | MP4ネイティブ | HLS (hls.js) |
|---|---|---|
| 起動リクエスト | 1(直接MP4) | 2(M3U8 + 最初のセグメント) |
| 起動遅延 | ほぼ0(プログレッシブ) | 2-10秒(セグメント待ち) |
| バッファ戦略 | ブラウザ内部 | JS手動MSE管理 |
| メインスレッドオーバーヘッド | 最小 | 大(解析、再マルチプレクス) |
| ABRサポート | ❌ | ✅ |
| ライブサポート | ❌ | ✅ |
| 暗号化サポート | ❌ | ✅ (DRM) |
シンプルなシナリオではMP4ネイティブが最速で最も安定。複雑なシナリオ(ライブ、ABR、DRM)ではMSEソリューションが必要。
まとめ
ブラウザ動画はシンプルなvideoタグからMSE+EME+WebCodecsへ進化し、デスクトッププレーヤーができるほぼすべてのことが可能になりました。
- videoタグ:ローカルMP4/WebM再生、最もシンプル
- MSE:ストリーミング再生のコア、hls.js/dash.js/flv.jsの基盤
- EME:DRM暗号化再生、Netflix/Disney+が使用
- WebCodecs:直接コーデック制御、画面録画、配信、エフェクトの新時代
この技術スタックを理解すれば、再生問題の原因を特定し、製品の技術選定で落とし穴を避けることができます。
クイックリファレンス:
- videoタグ:MP4単一ファイル再生
- MSE:ストリーミングのコア、hls.js/dash.js/flv.jsの基盤
- EME:DRM暗号化再生
- WebCodecs:ブラウザコーデック制御の新時代
- 再生カクつき:ネットワーク/デコード/MSEバッファ/メインスレッド/GC — 五つの原因