m3u8 プレイリストを mp4 動画ファイルに変換する方法

# はじめに
ストリーミング配信環境において、動画コンテンツは HLS (HTTP Live Streaming) 形式で配信されることが非常に多いです。
HLS 形式では、動画と音声が TS セグメントという小さなファイルに分割され、これらを制御する m3u8 プレイリストにより連続再生を実現しています。
この m3u8 プレイリストに書かれた TS ファイルから、配信後の動画ファイルを MP4 ファイルとして書き戻したいケースがあり、これを実現するスクリプトを紹介します。

# M3U8 to MP4 Stream Merger Script
動画用および（オプションで）音声用 m3u8 ストリームから TS セグメントを読み込み、最終的に動画と音声を結合して MP4 ファイルに変換するための bash スクリプトです。 
最終出力ファイルの名前は、動画 m3u8 ファイルのファイル名（拡張子を .mp4 に変更したもの）となります。

## 前提条件

- **ffmpeg** がライセンス的に問題ない形でシステムにインストールされていること  
- スクリプトは bash で実行されること  
- 入力として動画用 m3u8 は必須、音声用 m3u8 はオプションとして指定可能

## 使用方法

```bash
./merge_m3u8.sh video_m3u8 [audio_m3u8]
```

- `video_m3u8` : 動画用 m3u8 ファイルのパスまたは URL  
- `audio_m3u8` : （オプション）音声用 m3u8 ファイルのパスまたは URL  

例えば、動画と音声の両方を結合する場合は次のように実行します。

```bash
./merge_m3u8.sh video_playlist.m3u8 audio_playlist.m3u8
```

動画のみの場合は、

```bash
./merge_m3u8.sh video_playlist.m3u8
```

## スクリプト解説

以下にスクリプトの全体コードと各部分の説明を示します。

### コード全体

```bash
#!/bin/bash
set -e

usage() {
    echo "Usage: $0 video_m3u8 [audio_m3u8]"
    echo "  video_m3u8 : 動画用 m3u8 ファイルのパスまたは URL"
    echo "  audio_m3u8 : （オプション）音声用 m3u8 ファイルのパスまたは URL"
    exit 1
}

if [ "$#" -lt 1 ]; then
    usage
fi

VIDEO_M3U8="$1"
AUDIO_M3U8="$2"

# 動画 m3u8 のファイル名から拡張子を除いた名前を取得し、最終出力ファイル名を設定
BASE=$(basename "$VIDEO_M3U8")
OUTPUT_MP4="${BASE%.*}.mp4"

VIDEO_TS="video.ts"
AUDIO_TS="audio.ts"

echo "【動画ストリームの読み込み】"
ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_M3U8" -c copy "$VIDEO_TS"

if [ -n "$AUDIO_M3U8" ]; then
    echo "【音声ストリームの読み込み】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$AUDIO_M3U8" -c copy "$AUDIO_TS"

    echo "【動画と音声を結合して MP4 に変換】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_TS" -i "$AUDIO_TS" -c:v copy -c:a aac "$OUTPUT_MP4"

    # TS ファイルの削除
    rm "$VIDEO_TS" "$AUDIO_TS"
else
    echo "【音声用 m3u8 が指定されなかったため、動画のみを MP4 に変換】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_TS" -c copy "$OUTPUT_MP4"
    rm "$VIDEO_TS"
fi

echo "最終出力ファイル: $OUTPUT_MP4"
```

### 各部分の詳細説明

#### 使用方法の表示

```bash
usage() {
    echo "Usage: $0 video_m3u8 [audio_m3u8]"
    echo "  video_m3u8 : 動画用 m3u8 ファイルのパスまたは URL"
    echo "  audio_m3u8 : （オプション）音声用 m3u8 ファイルのパスまたは URL"
    exit 1
}
```

- 引数が足りない場合に、正しい使い方を表示して終了します。

#### 入力引数の取得

```bash
if [ "$#" -lt 1 ]; then
    usage
fi

VIDEO_M3U8="$1"
AUDIO_M3U8="$2"
```

- 少なくとも動画用 m3u8 ファイルのパスが必要なため、チェックを行っています。

#### 出力ファイル名の設定

```bash
BASE=$(basename "$VIDEO_M3U8")
OUTPUT_MP4="${BASE%.*}.mp4"
```

- 動画用 m3u8 のファイル名から拡張子を除去し、最終出力ファイル名として MP4 の名前を生成します。  
  例：`video_playlist.m3u8` → `video_playlist.mp4`

#### 一時 TS ファイルの設定

```bash
VIDEO_TS="video.ts"
AUDIO_TS="audio.ts"
```

- 動画 (映像) ファイルと音声ファイルが分かれているケースに対応します。
- 動画および音声の TS ファイルを一時的に保存するためのファイル名を設定します。

#### 動画ストリームの読み込み

```bash
echo "【動画ストリームの読み込み】"
ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_M3U8" -c copy "$VIDEO_TS"
```

- ffmpeg を利用して、動画用 m3u8 から TS セグメントを読み込み、`video.ts` として出力します。  
- `-protocol_whitelist` オプションにより、暗号化された TS セグメントも含む各種プロトコルを許可しています。

#### 音声ストリームの読み込み（オプション）

```bash
if [ -n "$AUDIO_M3U8" ]; then
    echo "【音声ストリームの読み込み】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$AUDIO_M3U8" -c copy "$AUDIO_TS"
```

- 音声用 m3u8 ファイルが指定された場合、同様に TS セグメントを読み込み、`audio.ts` として出力します。

#### 動画と音声の結合

```bash
    echo "【動画と音声を結合して MP4 に変換】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_TS" -i "$AUDIO_TS" -c:v copy -c:a aac "$OUTPUT_MP4"
```

- 読み込んだ動画と音声の TS ファイルを ffmpeg で結合し、最終的に MP4 ファイルに変換します。  
- 映像は再エンコードせずコピー（`-c:v copy`）、音声は AAC にエンコード（`-c:a aac`）します。

#### 一時ファイルの削除とイレギュラー処理

```bash
    rm "$VIDEO_TS" "$AUDIO_TS"
else
    echo "【音声用 m3u8 が指定されなかったため、動画のみを MP4 に変換】"
    ffmpeg -protocol_whitelist "file,crypto,http,https,data,tls,tcp" -i "$VIDEO_TS" -c copy "$OUTPUT_MP4"
    rm "$VIDEO_TS"
fi
```

- 結合処理後、不要となった一時ファイル（TS ファイル）を削除します。
- なお、音声ファイルが存在しないケースについては、動画ファイルのプレイリストのみ指定することで変換が可能です。

#### 最終出力の表示

```bash
echo "最終出力ファイル: $OUTPUT_MP4"
```

- 最終的に生成された MP4 ファイル名を表示します。

## まとめ

この bash スクリプトは、  
- 動画および（オプションで）音声の m3u8 ストリームから TS セグメントを ffmpeg で読み込み、  
- `-protocol_whitelist` オプションで暗号化プロトコルなどを許可しながら処理を行い、  
- 読み込んだ TS ファイルを結合して最終的に動画 m3u8 のファイル名に基づいた MP4 ファイル（例: video_playlist.mp4）を生成し、  
- 最後に一時ファイルを削除するという一連の処理を実現します。

このスクリプトを利用することで、個別の TS セグメントを手動で読み込んで連結する手間を省き、簡単に m3u8 ストリームから MP4 ファイルへの変換が可能となります。

## 補足
このスクリプトは、動画および音声の m3u8 ストリームを処理するために、外部コマンドとして ffmpeg を利用しております。
ffmpeg は、通常 GNU Lesser General Public License (LGPL) バージョン 2.1 以降、または GNU General Public License (GPL) バージョン 2.0 以降のいずれかの条件の下でライセンスされています。
本スクリプト自体は独自に作成されたものであり、ffmpeg のソースコードを含んでいるわけではありませんが、本スクリプトの利用に際しては、利用者ご自身が ffmpeg のライセンス条件に従い、適切なライセンス形態の ffmpeg を使用していることをご確認いただく必要があります。
詳細は、[ffmpeg の公式ライセンスページ](https://ffmpeg.org/legal.html)をご参照ください。

## 参考

* [ライセンス的にクリーンなH.264動画エンコードのやり方](https://qiita.com/engineer/items/54e33bdacc173c84cc8a)