メインコンテンツへスキップ

FFmpegの動画がスマホで再生できない原因と解決策|ビットレート・Profile設定の実践ガイド

··2993 文字·6 分
目次

「PCのブラウザでは完璧に再生できる動画が、スマホで見ると動かない…」

動画配信機能を実装していると、必ずと言っていいほど直面する問題です。

筆者は個人開発の動画共有アプリで、GoのバックエンドからFFmpegを使って動画を変換し、S3にアップロードする機能を実装しました。開発中はMacのChromeで問題なく再生できていたのですが、いざ友人にiPhoneでテストしてもらうと「再生ボタンを押しても何も起きない」という報告が。Androidでも「ずっと読み込み中のまま」という状態でした。

原因を調査した結果、コーデックでもネットワークでもなく、動画のビットレートとH.264の Profile/Level設定が問題だったことが判明しました。

本記事では、この問題の原因特定の方法から、モバイル向けの最適化設定、Go言語での実装例まで、実体験をもとに徹底解説します。


1. 発生した症状
#

開発環境のPC(macOS Chrome / Safari)では何の問題もなく再生できていました。しかし、実機テストで以下の症状が発生しました。

デバイス症状
iPhone (Safari/Chrome)再生ボタンを押すと一瞬再生マークが出るが、すぐに停止する
Android (Chrome)読み込み中(スピナー)のまま進まない。まれにデコードエラーが表示される
共通ネットワークエラー(404等)は出ていない。動画のバイナリ自体はブラウザに届いている

2. 最初にやるべきこと:ffprobeで動画の状態を確認する
#

闇雲に設定を変える前に、まず動画の現在の状態を正しく把握することが重要です。ffprobe を使えば、動画のメタデータを詳細に確認できます。

ビットレートの確認
#

ffprobe -v error -select_streams v:0 \
  -show_entries stream=bit_rate \
  -of default=noprint_wrappers=1:nokey=1 output.mp4

Profile / Levelの確認
#

ffprobe -v error -select_streams v:0 \
  -show_entries stream=profile,level \
  -of default=noprint_wrappers=1:nokey=1 output.mp4

筆者のケースでの出力結果
#

# ビットレート
10485760    ← 約10Mbps(高すぎる!)

# Profile / Level
High        ← Highプロファイル
51          ← Level 5.1(高すぎる!)

ビットレートが 10Mbps以上 、Profileが High / Level 5.1 ——これがPCでは問題なく、モバイルでは致命的だった原因です。


3. 本当の原因:モバイル端末のデコード能力の限界
#

なぜPCでは再生できたのか?
#

最近のPCはCPU・GPUともに高性能です。ビットレートが10Mbpsを超えていても、ソフトウェアデコードで力技で再生できてしまいます。

なぜモバイルで失敗したのか?
#

モバイル端末(特にiPhone)は、バッテリー持ちと発熱を抑えるためにハードウェアデコーダーに頼って再生します。このチップには厳格な仕様制限があり、以下の条件を超えると「再生不可」と判定されます。

原因説明
ビットレートの過多帯域が足りていても、チップの処理能力を超えるとデコードできない
H.264 Profile/Level の不一致High / Level 5.1 は高画質向け設定。古い端末や安価な端末ではサポートされていない
ピクセルフォーマットの不一致yuv420p 以外のフォーマット(例: yuv444p)はiOSで再生できないことが多い

4. 解決策:モバイル向け最適化設定
#

改善後のFFmpegコマンド
#

ffmpeg -i input.mp4 \
  -c:v libx264 \
  -profile:v baseline \
  -level 3.0 \
  -b:v 1500k \
  -maxrate 1500k \
  -bufsize 3000k \
  -pix_fmt yuv420p \
  -movflags +faststart \
  -c:a aac \
  -b:a 128k \
  output.mp4

各パラメータの解説
#

パラメータ役割なぜ必要か
-profile:v baseline最も互換性の高いH.264プロファイル古いiPhone(SE等)でも確実に再生可能
-level 3.0デコーダーの性能要件を指定モバイルなら3.0〜4.1が安定
-b:v 1500k平均ビットレートを1.5Mbpsに制限モバイルでもスムーズに再生可能な範囲
-maxrate / -bufsize瞬間最大ビットレートを制限VBR(可変ビットレート)のスパイクを防止
-pix_fmt yuv420pピクセルフォーマットを指定iOSの標準フォーマット。省略すると再生不可になることが多い
-movflags +faststartmoovアトムを先頭に移動Webでの即座再生に必須(詳細はこちらの記事で解説

5. Go言語での実装例
#

GoのバックエンドからFFmpegを呼び出す際の実践的な実装例です。タイムアウト制御(context.Context)とエラーハンドリングを含めています。

package video

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
)

// ConvertForMobile は入力動画をモバイル再生に最適化された形式に変換します
func ConvertForMobile(ctx context.Context, inputPath, outputPath string) error {
	args := []string{
		"-i", inputPath,
		"-c:v", "libx264",
		"-profile:v", "baseline",
		"-level", "3.0",
		"-b:v", "1500k",
		"-maxrate", "1500k",
		"-bufsize", "3000k",
		"-pix_fmt", "yuv420p",
		"-movflags", "+faststart",
		"-c:a", "aac",
		"-b:a", "128k",
		"-y", // 出力ファイルの上書きを許可
		outputPath,
	}

	cmd := exec.CommandContext(ctx, "ffmpeg", args...)

	// FFmpegはログをstderrに出力するためキャプチャ
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("ffmpeg変換エラー: %w (stderr: %s)", err, stderr.String())
	}

	return nil
}

呼び出し側の例
#

// 30秒でタイムアウトするContextを作成
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := video.ConvertForMobile(ctx, "input.mp4", "output.mp4"); err != nil {
    log.Fatalf("動画変換に失敗: %v", err)
}

6. モバイル向け推奨ビットレート目安
#

用途に応じたビットレートの目安表です。

解像度推奨ビットレートProfile / Level主な用途
480p (SD)800 〜 1,000 kbpsBaseline / 3.0SNS投稿、低速回線対応
720p (HD)1,500 〜 2,500 kbpsMain / 3.1Webサイト背景動画、標準画質
1080p (FHD)3,000 〜 5,000 kbpsHigh / 4.1YouTube投稿、Wi-Fi前提

ポイント: 迷ったら720p / 1,500kbps / Baselineを選べば、ほぼすべてのモバイル端末で安定して再生できます。


7. 次のステップ:HLS配信の検討
#

ここまでの方法はプログレッシブダウンロード(1つのMP4ファイルを直接配信する方式)の最適化です。

以下のような要件がある場合は、HLS(HTTP Live Streaming) への移行を検討してください。

要件プログレッシブHLS
回線速度に応じた画質調整❌ 不可✅ アダプティブビットレート
長時間動画(10分以上)△ 初回読み込みが重い✅ セグメント分割で軽量
DRM(著作権保護)❌ 不可✅ 対応
実装の手軽さ✅ ファイル配置のみ△ セグメント生成が必要

ブログやポートフォリオの短い動画であればプログレッシブダウンロードで十分ですが、動画がサービスの中心になる場合はHLSの導入を検討しましょう。


8. まとめ:モバイル再生を安定させるチェックリスト
#

「PCで再生できる動画」と「モバイルで快適に再生できる動画」は別物です。サーバーサイドで動画を生成・保存する場合、以下のチェックリストを常に確認しましょう。

  • ビットレートが高すぎないか?(目安:720pなら2,500kbps以下)
  • H.264 ProfileBaseline または Main か?
  • ピクセルフォーマットyuv420p か?
  • moov atom(faststart) は先頭に配置されているか?
  • 実機テスト(最低限iPhoneとAndroid各1台)を行ったか?

よくある質問(FAQ)
#

Q. Baselineプロファイルだと画質が落ちませんか?
#

同じビットレートで比較すると、MainやHighプロファイルより圧縮効率はやや劣ります。ただし、1,500kbps以上であれば実用上問題ないレベルの画質を維持できます。

Q. faststartだけで解決しないのはなぜですか?
#

faststartはmoovアトムの配置問題(再生開始の遅延)を解決しますが、ビットレートやProfile/Levelの問題は解決しません。両方の対策が必要です。詳しくはFFmpegのfaststartに関する記事をご覧ください。

Q. WebMやAV1フォーマットではこの問題は起きませんか?
#

起きにくくなりますが、iOSのSafariはWebM/AV1のサポートが限定的です(iOS 16以降で部分的に対応)。現時点ではMP4 + H.264が最も安全な選択肢です。


著者
ゆーふー
Web開発、インフラ、AI技術に興味があるエンジニアです。日々の学びを記録しています。

関連記事

👤 運営者プロフィール

運営者プロフィール画像

ゆーふー

メガベンチャーで働く現役Webエンジニア(歴約2年)。
フロントエンドからインフラ構築、セキュリティ対策まで、実務で得た「現場のリアルな技術知見」を発信しています。