動画パフォーマンス指標と最適化(LCP/INP/帯域/CPU)
TL;DR: LCP 影響を最小化する基本形: 小さな LQIP + aspect-ratio 予約 + 遅延取得。視認 150px 手前で <link rel="preload" as="video">
を注入し帯域競合を局所化。INP 悪化の多くは同期デコード & レイアウトジャンプ由来なので preload=metadata
/ 明示幅 / 低ビットレート起点で緩和。RUM で LCP 候補種別・再生遅延・Dropped Frames を継続監視する。
1. 指標セット (Why)
- LCP: 最大要素描画が動画 poster / 初期フレーム化して遅延していないか
- INP: 再生トリガークリック → 初回音/動開始までの総合入力遅延
- 初期転送量: TTFB 後 2s 内のバイト (CSS/JS 競合影響評価)
- Dropped Frames: requestVideoFrameCallback で実際 vs 期待
- Decode Time: canplay までのデコード所要 (メタデータ→可視化)
- 再生放棄率: 再生ボタン可視後 5s 内未クリック割合
2. 計測実装 (RUM)
// LCP 候補種別
new PerformanceObserver(list => {
for (const e of list.getEntries()) {
const el = (e as any).element as HTMLElement | null;
window.__rum?.queue({ t: 'lcp', ms: e.renderTime || e.loadTime, tag: el?.tagName?.toLowerCase() });
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
// 再生開始遅延
const video = document.querySelector<HTMLVideoElement>('video[data-rum]');
let clickTs: number | undefined;
video?.addEventListener('pointerdown', () => clickTs = performance.now(), { once: true });
video?.addEventListener('playing', () => clickTs && window.__rum?.queue({ t: 'playDelay', ms: performance.now() - clickTs }));
// Dropped Frames 粗推定
if (video && 'requestVideoFrameCallback' in HTMLVideoElement.prototype) {
let expected = 0, actual = 0, last: DOMHighResTimeStamp | undefined;
const targetFps = 30;
const loop = (now: number) => {
if (last) { const delta = now - last; expected += delta / (1000 / targetFps); actual += 1; }
last = now; video.requestVideoFrameCallback(loop);
};
video.requestVideoFrameCallback(loop);
setInterval(() => {
const drop = Math.max(0, expected - actual);
window.__rum?.queue({ t: 'dropRate', value: drop / Math.max(1, expected) });
}, 5000);
}
高精度なフレームロスは配信 Stats API / MSE メトリクスと統合し補正。
3. 最適化レバレッジ
- 解像度最適化: 埋め込み最大幅 = エンコード目標。UI 解説は 720p 未満多い。
- コーデック段階導入: H.264 → VP9 → AV1 (累積再生時間しきい値で切替)
- ビットレート調整: CRF/CQ 起点 → 客観指標 (SSIM/VMAF) 閾値下回りで再試行
- 取得タイミング: IntersectionObserver で視認 150px 手前 preload
- デコード並列性:
decoding=async
/preload=metadata
で初期軽量化 - キャッシュ戦略: 長寿命 (指紋付与) + 変更頻度低なら immutable
4. 推奨しきい値例
LCP: 2.5s 以内 (poster 由来なら構成再検討)
再生遅延: 500ms p75 / 800ms p95 以下
初期転送量: 350KB (HTML+CSS+JS+Poster+LQIP) p75 以下
Dropped Frames: <2% p75
Abandon Rate: 可視後 5s 未再生 < 35%
5. 失敗とアンチパターン
巨大 poster 即取得
LCP 候補化 + CSS/JS 遅延。→ LQIP + 条件 preload。
autoplay ミュート乱用
帯域浪費 + CPU 常時使用。→ ユーザ意図再生。
解像度過剰 (1080p)
実効表示 800px で浪費。→ 埋め込み最大幅測定。
早すぎる preload
他リソースと競合。→ 可視 150–200px 手前。
6. チェックリスト
- 最大表示幅 = エンコード解像度
- LQIP / aspect-ratio 予約済
- 条件 preload 実装 (rootMargin 150px)
- 再生遅延 RUM 収集 & p75 < 500ms
- Dropped Frames 定期報告
- poster が LCP 候補化していない
7. FAQ
- 再生開始遅延の主因計測?
- PerformanceTimeline + now で click→playing 差分。Network タイミングと decode イベントを分離収集しボトルネック特定。
- 複数動画一覧での最適化優先度?
- Above-the-fold のみ LQIP + early preload。下部は 静的サムネ + 明示 play button。
公開日: 2025-09-06編集: gazou-compressor.jp