ffmpeg を使い、HTTP Live Streaming(HLS)をファイルに保存し、保存したファイルからストリーミングを行う方法について

# 追記

2016/02/16 に ffmpeg version 3.0 が[リリースされました](https://osdn.jp/magazine/16/02/16/155500)。

ver 3.0 では libaacplus libvo-aacenc が削除されるなど、コーデックとコンテナの統廃合が行われたようです。

このエントリは ffmpeg version 2.x について記述しているため、
ver 3.x ではそのままでは動かない部分があるかもしれません。ご留意ください。

また、 libfdk_aac が使えない場合は、 libfaac を使用してください。

# 本文

ffmpeg を使い HLS を使ったストリーミングを mp4 フォーマットで保存したり、その反対に mp4 からストリーミングを行う(m3u8 と ts ファイルを生成する)方法について説明します。


まずは、ffmpeg をインストールします。既にインストールされている場合は、AAC, H264, MP4, MPEG-2 TS が利用可能かどうかを確認します。

## ffmpeg で利用できる format と codec を確認する

`$ ffmpeg -formats` や `$ ffmpeg -codecs` で利用可能なフォーマットとコーデックを確認できます。

```
ffmpeg version 2.8.1 Copyright (c) 2000-2015 the FFmpeg developers
  built with Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.8.1 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libvo-aacenc --enable-libxvid --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfdk-aac --enable-libopenjpeg --disable-decoder=jpeg2000 --extra-cflags='-I/usr/local/Cellar/openjpeg/1.5.2_1/include/openjpeg-1.5 ' --enable-nonfree --enable-vda
  libavutil      54. 31.100 / 54. 31.100
  libavcodec     56. 60.100 / 56. 60.100
  libavformat    56. 40.101 / 56. 40.101
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 40.101 /  5. 40.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.101 /  1.  2.101
  libpostproc    53.  3.100 / 53.  3.100
File formats:
 D. = Demuxing supported
 .E = Muxing supported
 --
 D  aac             raw ADTS AAC (Advanced Audio Coding)
 DE h264            raw H.264 video
  E mp4             MP4 (MPEG-4 Part 14)
 DE mpegts          MPEG-TS (MPEG-2 Transport Stream)
```

```
ffmpeg version 2.8.1 Copyright (c) 2000-2015 the FFmpeg developers
  built with Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.8.1 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libvo-aacenc --enable-libxvid --enable-vda
  libavutil      54. 31.100 / 54. 31.100
  libavcodec     56. 60.100 / 56. 60.100
  libavformat    56. 40.101 / 56. 40.101
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 40.101 /  5. 40.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.101 /  1.  2.101
  libpostproc    53.  3.100 / 53.  3.100
Codecs:
 D..... = Decoding supported
 .E.... = Encoding supported
 ..V... = Video codec
 ..A... = Audio codec
 ..S... = Subtitle codec
 ...I.. = Intra frame-only codec
 ....L. = Lossy compression
 .....S = Lossless compression
 -------
 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_vda ) (encoders: libx264 libx264rgb )
 DEA.L. aac                  AAC (Advanced Audio Coding) (decoders: aac aac_fixed ) (encoders: aac libvo_aacenc )
```

利用できないコンテナやコーデックがある場合は、ffmpeg を再インストールする必要があります。

ffmpeg で AAC が利用できるように以下のオプションを付けてインストールしてください。既にインストール済みの場合は `brew uninstall ffmpeg` の後に再インストールします。

```sh
$ brew install ffmpeg --with-fdk-aac --with-libvo-aacenc
```

## ffmpeg options

ffmpeg の代表的なオプションについて、簡単に説明しておきます。

- MAIN OPTIONS
    - -i 入力ファイル名の指定。URLも指定できます(`-i http://domain:port/path/file.ext`)
        - 複数指定が可能です `-i a.ext -i b.ext`
        - 連番指定が可能です `-i "test_0%01d.png"` とすると `test_00.png` 〜 `test_09.png` を自動的に読み込みます(存在しないファイルはエラーにならず無視されます)
    - -f 出力フォーマットの指定です
    - -y 出力ファイル名と同名のファイルがある場合に上書します
    - -fs 出力ファイル名の最大サイズを指定します。単位はbyteです
    - 出力ファイル名。printf に似た書式で連番のファイル名を指定する事も可能です (例: media%03d.ts)

- VIDEO OPTIONS
    - -b 動画部分のビットレートを指定します。初期設定は 200kbps です
    - -r フレームレートを指定します。初期設定は25です
    - -s 動画のサイズを `幅x高さ`で指定します。(例: 800x600)
    - -aspect アスペクト比を指定します
    - -vn ビデオをカットします。音声だけを取り出します
    - -vcodec ビデオコーデックを指定します。指定しない場合は入力ファイルと同じコーデックになります
    - -movflags faststart 録画終了後にメタデータをmp4ファイルの先頭部分に移動します。再生開始までの時間が短縮されます

- AUDIO OPTIONS
    - -ar サンプリング周波数を指定します
    - -ac 音声のチャンネル数を指定します
    - -an オーディオをカットします。映像だけを取り出します
    - -acodec 音声コーデックを指定します。指定しない場合は入力ファイルと同じコーデックになります

ffmpeg ではオプションを指定する順番によって結果が異なります。映像系のオプションは音声系と混ぜずに連続で指定するようにしてください。  
また上手く行かない場合は、オプションの順番を変えてみてください。

## HLSのストリーム を mp4 に保存する

さてやっと本題です、以下のコマンドで、HLS のストリームを mp4 に直接保存できます。

```sh
$ ffmpeg -i https://example.com/.../playlist.m3u8 -movflags faststart -c copy -bsf:a aac_adtstoasc rec.mp4
```

<!--
以下は、[Mirrativ](https://www.mirrativ.com/dashboard) のライブストリームを mp4 に録画する例です。

```sh
$ ffmpeg -i https://wowza-edge0004.example.com:443/stream/xxx/playlist.m3u8 -movflags faststart -c copy -bsf:a aac_adtstoasc rec.mp4
```

-->

## 映像と音声を分離する

以下のコマンドを使うと、input.mp4 の映像と音声を別々のファイル(audio.m4a, video.mp4)に分離できます。

```sh
$ ffmpeg -i input.mp4 -bsf:a aac_adtstoasc -vn -acodec copy audio.m4a
```

```sh
$ ffmpeg -i input.mp4 -bsf:a aac_adtstoasc -vcodec copy -an video.mp4
```

以下のコマンドで、stream/a001.ts から stream/a001.aac を抽出できます。

```sh
$ ffmpeg -i stream/a000.ts -acodec copy stream/a000.aac
```



## mp4 ファイルをストリーミングする

input.mp4 を HLS でストリーミング可能なように stream/playlist.m3u8 と stream/a{nnn}.ts を生成します。
以下のコマンドを改行せず一行で書いてください。

```
$ ffmpeg
    -i input.mp4                  // input source
    -vcodec libx264               // video codec    (H264)
    -movflags faststart           // video fast decode(mov box moov to header)
    -vprofile baseline -level 3.0 // video profile  (Baseline, Lv 3.0)
    -g 150                        // ?
    -b:v 519k                     // video bitrate  (519kbps)
    -s 768x432                    // video size     (768 x 432 px)
//  -acodec libfaac               // audio codec    (AAC)
    -acodec libfdk_aac
    -b:a 63.4k                    // audio bitrate  (63.4kbps)
    -ar 44100                     // audio sampling rate (44.1kHz)
    -flags +loop-global_header
    -map 0
    -bsf h264_mp4toannexb
    -f segment
    -segment_format mpegts
    -segment_time 5               // ts duration
    -segment_list stream/playlist.m3u8  // playlist filename 
    stream/v%03d.ts               // ts file name format
```

例.

```sh
$ ffmpeg -i input.mp4 -vcodec libx264 -movflags faststart -vprofile baseline -level 3.0 -g 150 -b:v 519k -s 768x432 -acodec libfdk_aac -b:a 63.4k -ar 44100 -flags +loop-global_header -map 0 -bsf h264_mp4toannexb -f segment -segment_format mpegts -segment_time 5 -segment_list stream/playlist.m3u8 stream/a%03d.ts
```

## hls_time

`-segment_time n` でイメージ通りの秒数を持つtsを作成できない場合は、 `-segment_time` の代わりに [-hls_time](http://ffmpeg.org/ffmpeg-formats.html#hls-1) を使ってみてください。

```sh
$ ffmpeg -i input.mp4 -vcodec libx264 -movflags faststart -vprofile baseline -level 3.0 -g 150 -b:v 519k -s 768x432 -acodec libfdk_aac -b:a 63.4k -ar 44100 -flags +loop-global_header -map 0 -bsf h264_mp4toannexb -f segment -segment_format mpegts -hls_time 5 -segment_list stream/playlist.m3u8 stream/a%03d.ts
```

## 動画から連番画像を生成する

連番画像の生成は `ffmpeg -i input.mp4 -an -vsync 0 output-%03d.jpg` で可能です。このままではフレーム数に応じた大量の画像が生成されてしまうため、オプションを指定して変換範囲を絞っていきます。ここでは説明しませんが、scale, tile, qrop フィルターなども指定することで出力画像の加工も可能です。

- `-an` 連番画像生成処理を軽くするために付与しておきます
- `-vsync n` おまじないです。フレームレートを変換しない場合に `-vsync 0` を指定します。フレームレートを変換する場合は変換環境で差がでないように `-vsync 1` を指定します([指定しないと環境により異なる結果が生成されます](http://sogohiroaki.sblo.jp/article/178179366.html))
- `-qscale n` jpeg の品質を設定します。 `-qscale 2` で 80% 程度の品質になります
- `-ss n` 変換を開始する秒数を指定します。`-i` より前に設置する必要があります(後ろに置いても動作はしますがかなり遅くなります)
- `-vframes n` 変換するフレーム数を指定します。
- `-r {frame-rate}` は `-i` の前に書くか、後ろに書くかで意味が変わります。前に書く場合には入力ファイルのフレームレートを指定します。指定しない場合は `-i` で指定したファイルの既定値が使用されます。後ろに書く場合は出力ファイルのフレームレートを指定します。


`ffmpeg -ss 100 -i input.mp4 -vframes 30 -an -vsync 0 -f image2 output-%03d.jpg` で、input.mp4 の 100秒目から 30フレーム分の jpeg を生成します。

生成されるファイル名は連番になり `output-001.jpg` `output-002.jpg`, ... という名前になります。

## 連番画像から動画を生成する

連番画像から動画の生成は `ffmpeg -i framerate 30 -i input-%03d.jpg output.mp4` で可能です。

- フレームレートは `-r 30` でも一応指定できますが、連番画像のフレームレートの設定は `-r` ではなく `-framerate` を使うとより正確に指定が可能です。


### ファイルの情報を見る

ffprobe コマンドで mp4 ファイルの情報を見ることができます。
(ffprobe コマンドは ffmpeg をインストールすると一緒にインストールされます)

```sh
$ ffprobe input.mp4

> Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':
>   Metadata:
>     major_brand     : isom
>     minor_version   : 512
>     compatible_brands: isomiso2avc1mp41
>     encoder         : Lavf56.40.101
>   Duration: 00:02:02.81, start: 0.000000, bitrate: 591 kb/s
>     Stream #0:0(und): Video: h264 (Baseline) (avc1 / 0x31637661), yuv420p, 432x768, 519 kb/s, 26.89 fps, 90k tbr, 90k tbn, 180k tbc (default)
>     Metadata:
>       handler_name    : VideoHandler
>     Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 63 kb/s (default)
>     Metadata:
>       handler_name    : SoundHandler
```

以下のオプションも利用できます。

- `-show_format` Show information about the container format of the input multimedia stream.
- `-show_packets` Show information about each packet contained in the input multimedia stream.
- `-show_frames` Show information about each frame and subtitle contained in the input multimedia stream.
- `-show_streams` Show information about each media stream contained in the input multimedia stream.
- `-show_programs` Show information about programs and their streams contained in the input multimedia stream.
- `-count_frames` Count the number of frames per stream and report it in the corresponding stream section.
- `-count_packets` Count the number of packets per stream and report it in the corresponding stream section.
- `-of json` output to JSON format.

プログラムから簡単にチェックできるように、JSON で出力することもできます。

```sh
$ ffprobe -of json -show_format  rec.mp4  >  a.format.json    # format 情報を取得
$ ffprobe -of json -show_packets rec.mp4  >  a.packet.json    # パケット情報を取得
$ ffprobe -of json -show_streams rec.mp4  >  a.stream.json    # 映像と音声ストリームの情報を取得
```

ffprobe の他に [mediainfo](https://itunes.apple.com/app/mediainfo/id510620098) コマンドでも情報をみることができます。

```sh
$ mediainfo input.mp4

> General
> Complete name                            : input.mp4
> Format                                   : MPEG-4
> Format profile                           : Base Media
> Codec ID                                 : isom
> File size                                : 8.66 MiB
> Duration                                 : 2mn 2s
> Overall bit rate mode                    : Variable
> Overall bit rate                         : 591 Kbps
> Encoded date                             : UTC 1904-01-01 00:00:00
> Tagged date                              : UTC 1904-01-01 00:00:00
> Writing application                      : Lavf56.40.101
>
> Video
> ID                                       : 1
> Format                                   : AVC
> Format/Info                              : Advanced Video Codec
> Format profile                           : Baseline@L3
> Format settings, CABAC                   : No
> Format settings, ReFrames                : 1 frame
> Format settings, GOP                     : M=1, N=30
> Codec ID                                 : avc1
> Codec ID/Info                            : Advanced Video Coding
> Duration                                 : 2mn 2s
> Bit rate                                 : 519 Kbps
> Width                                    : 432 pixels
> Height                                   : 768 pixels
> Display aspect ratio                     : 0.562
> Frame rate mode                          : Variable
> Frame rate                               : 26.889 fps
> Minimum frame rate                       : 0.850 fps
> Maximum frame rate                       : 90 000.000 fps
> Color space                              : YUV
> Chroma subsampling                       : 4:2:0
> Bit depth                                : 8 bits
> Scan type                                : Progressive
> Bits/(Pixel*Frame)                       : 0.058
> Stream size                              : 7.60 MiB (88%)
> Encoded date                             : UTC 1904-01-01 00:00:00
> Tagged date                              : UTC 1904-01-01 00:00:00
>
> Audio
> ID                                       : 2
> Format                                   : AAC
> Format/Info                              : Advanced Audio Codec
> Format profile                           : LC
> Codec ID                                 : 40
> Duration                                 : 2mn 2s
> Bit rate mode                            : Variable
> Bit rate                                 : 63.4 Kbps
> Maximum bit rate                         : 65.1 Kbps
> Channel(s)                               : 2 channels
> Channel(s)_Original                      : 1 channel
> Channel positions                        : Front: C
> Sampling rate                            : 44.1 KHz
> Compression mode                         : Lossy
> Stream size                              : 950 KiB (11%)
> Encoded date                             : UTC 1904-01-01 00:00:00
> Tagged date                              : UTC 1904-01-01 00:00:00
```

MP4のコンテナ構造(box)をビジュアル化するツールもあります。

- [MP4 Reader](http://www.thinmultimedia.co.kr/products/MP4Reader.html) (Windows版のみ、Macの方は [VirtualBox](https://www.virtualbox.org/) + [modern.IE](https://dev.windows.com/en-us/microsoft-edge/tools/vms/mac/) など のインストールが別途必要になるでしょう)

![](https://gyazo.com/db8f3a5b806738f7187957c876924d2d.png)

ブラウザ上で動作するツールもあります。
- [MP4Box.js / ISOBMFF Box Structure Viewer](http://download.tsi.telecom-paristech.fr/gpac/mp4box.js/filereader.html)

![](https://gyazo.com/1992b5e94ad3bf2c1dc724f828e54511.png)

MP4 Reader と MP4Box.js の解析結果の細部(stbl以下のbox)が異なる場合が見られます。参考程度に見ておくと良いでしょう。



参考になさってください。