AbemaTVにおける映像ファイルのトランスコードについて

# はじめに

この記事は[AbemaTV Advent Calendar 2017](https://adventar.org/calendars/2216) の12/22分の内容となります。

はじめまして、 [@hase-yuu](https://twitter.com/hase_xpw) です。
AbemaTVのサーバーチームで、機能開発や映像ファイルのエンコードなど、雑多な業務をしています。

AbemaTVの映像配信については [こちら](https://qiita.com/sunfish-shogi/items/008b3da9309b08465604) の記事で触れられており、その中でHLS, MPEG-DASHの配信方式がAbemaTVで採用されていることや、生放送における映像ファイルのエンコードについて書かれています。

この記事では、生放送以外の納品されたファイルについて、
配信形式ごとに、ファイルをトランスコードする方法について書きたいと思います。

# AbemaTVの映像ファイルフォーマットについて

AbemaTVにおいては、HLS, MPEG-DASHの 2 種類の配信形式を採用しています。

それぞれの配信形式について、　HLSは `MPEG2-TS` , `fMP4` ,(ADTS, MP3)のファイルフォーマットに対応しています。
MPEG-DASH　については、仕様自体はファイルフォーマットを限定していませんが、`ISO-BMFF` と `MPEG-2 TS` 用のサブセット・プロファイルがあるため、事実上この 2 つのフォーマットを利用します。

ただし、実際のところ、プレイヤーが全ての仕様に対応しているわけではなく、
配信形式ごとに、再生可能なファイルフォーマットは限られてきます。

プレイヤーごとのファイルフォーマットの対応状況を、以下にまとめました。

#### HLSファイルフォーマットのプレイヤーのサポート状況(2017/12/22現在)

| | Android(ExoPlayer) | iOS(AVPlayer) | Web(hls.js) |
| --- | --- | --- | --- |
|  MPEG-2 TS | o | OS X v10.7 or later, iOS 6 or later, and tvOS 9 or later | o |
| fMP4 | o | macOS v10.12 or later, iOS 10 or later, and tvOS 10 or later | o |

#### MPEG-DASHファイルフォーマットのプレイヤーのサポート状況(2017/12/22現在)

| | Android(ExoPlayer) | Web(dash.js) |
| --- | --- | --- |
| MPEG-2 TS | x |  x |
| fMP4 | o |  o |
| WebM | o |  o |
| Matroska | o | o |

※ web(hls.js, dash.js)は、ブラウザによって対応状況が変わります。

WWDC16においてAppleがHLSにおけるfMP4のサポートを発表し、MPEG-2 TSだけでなく、fMP4のプレイヤー対応も進んできており、今後は、HLSとMPEG-DASHの配信において[CMAF](https://mpeg.chiariglione.org/standards/mpeg-a/common-media-application-format)に準拠する、fMP4が一般的になるのではないかと予想されます。

AbemaTVにおいては、fMP4へのサポートが進んでいない時期から配信していることもあり、
HLSは `MPEG-2 TS` を、MPEG-DASHは `fMP4` を利用して配信しています。

# 映像ファイルのエンコード

AbemaTVでは、納品ファイルを [ffmpeg](https://www.ffmpeg.org/) を利用してエンコードしています。
ここでは、実際にファイルをエンコードしてみます。

## HLS配信ファイル(MPEG-2 TS)の生成

サンプルとして、AVIのファイルから、5s単位にセグメントしたMPEG-2 TSを生成します。

実際のエンコードは、映像品質を上げるために、パラメータを工夫していますが、
わかりやすくするため、細かいパラメータを省略しています。

```
% ffmpeg -i big_buck_bunny.avi -hls_time 5 -hls_list_size 0 -hls_flags single_file -c:v libx264 -c:a aac -hls_segment_type "mpegts" master.m3u8
```

| option | 説明 |
| --- | --- | 
| hls_time | 1セグメントの長さを指定します |
| hls_list_size | プレイリスト(m3u8)を構成する最大のセグメント数、0で全てのキーフレームを記載します |
| hls_flags single_file | 一つのtsファイルとして出力します、プレイリストにはセグメントのバイト範囲が記載されます |
| hls_segment_type | セグメントのファイルフォーマットを記載します(mpegts / fmp4) |

これにより、HLSのプレイリストであるm3u8ファイルと、tsファイルが生成されます。

## MPEG-DASH配信ファイル(fMP4)の生成

fMP4のファイルは上記の `hls_segment_type` を `fmp4` にすることで生成するか、
以下のコマンドで、fMP4を生成できます。

```
% ffmpeg -i big_buck_bunny.avi  -movflags frag_keyframe+empty_moov+separate_moof+default_base_moof big_buck_bunny.mp4
```

生成されたファイルは、mux(映像ファイルと音声ファイルが混ざっている)となります。
現在MPEG-DASHを再生するプレイヤーは、mux形式のファイルに対応するものはほぼ存在せず、
配信するためには、demux(映像ファイルと音声ファイルが別々)にする必要があります。

[MP4Box](https://gpac.wp.imt.fr/mp4box/) などのコマンドを利用することによって、fMP4からdemuxのファイルを生成することができますが、セグメントの長さを固定長でしか生成することができません。

```
$ MP4Box -dash 5000 big_buck_bunny.mp4#video big_buck_bunny.mp4#audio
```

AbemaTVにおいては、後述するCM挿入のため、セグメントの切れる位置をコントロール必要があり、
fMP4のファイルを自前で分解することにより、demuxファイルを生成しています。

# CM挿入のための工夫

AbemaTVでは、 SSAI(Server Side Ad Insertion)方式を採用しており、映像ファイルのセグメントの間にCMのセグメントを挟み込むことにより、広告配信を行なっています。

CMを差し込む際に、セグメントの切れ目が、シーンの切り替わりと一致していない場合、不自然なCMの入りとなってしまうため、違和感なくCMのセグメントを差し込むために、シーンの切り替わり位置にセグメントの切れ目が来るように調整を行なっています。

![keyframe3.png](https://qiita-image-store.s3.amazonaws.com/0/225657/4ecb209e-8629-d289-0a8c-122d2f3e105c.png)


具体的には、ffmpegのシーン検出オプション(`-sc_threshold`)を利用した、機械的なシーンの切り替わり検出と、オペレーターが手動でセグメントの切れ目を指定(`-force_key_frames`)することにより、CM差し込みに適した位置で、セグメントを分割しています。

```
ffmpeg -i big_buck_bunny.avi -hls_time 5 -hls_list_size 0 -hls_flags single_file -c:v libx264 -force_key_frames "00:00:12" -sc_threshold 0  -c:a aac master.m3u8
```


# まとめ

HLS、MPEG-DASHを配信するためのファイルトランスコーディングの方法について書きました。

映像ファイルの最適化については、まだまだやりたいことが全然やれてませんが、 [spanky346](https://ameblo.jp/spanky346/entry-12333117843.html) や [ygoto3_](https://twitter.com/ygoto3_) と共に盛り上げていきたいと思います。

それでは！


参考

- https://github.com/video-dev/hls.js/
- https://github.com/Dash-Industry-Forum/dash.js/wiki
- https://developer.apple.com/streaming/examples/
- https://google.github.io/ExoPlayer/supported-formats.html
- https://msdn.microsoft.com/ja-jp/library/dn594470(v=vs.85).aspx