AbemaTV の MPEG-DASH 対応

# はじめに

これは [AbemaTV Advent Calendar 2017](https://adventar.org/calendars/2216) 12 月 7 日の記事として用意したものです。

[AbemaTV](https://abema.tv/) は「インターネットテレビ局」と銘打つ動画配信サービスで、いわゆる地上波のような番組表に沿って放送されるリニア型配信をインターネットサービスとして提供しています。

ここでは AbemaTV が MPEG-DASH という配信規格を導入したことについて、 AbemaTV における事情と技術的な課題、それらにどう対処したかを書きます。

# HLS と MPEG-DASH

現在多くの動画サービスで HTTP 上で動画を配信する技術が用いられています。
HTTP を用いることで Web 関連のライブラリや、 CDN など既存の技術資源を有効に活用できます。
(ちなみに、特定の相手に映像を送信する場合などは RTP や RTMP など Push 型の手法が用いられ、 AbemaTV でも撮影現場からサーバーへ映像を送る際などに使用しています。)

AbemaTV では **HLS(HTTP Live Streaming)** と **MPEG-DASH(Dynamic Adaptive Streaming over HTTP)** という 2 種類の規格それぞれで配信をしています。
2016 年春時点では HLS のみでサービスをスタートさせましたが、コンテンツ保護の技術の選択肢を増やすなどの目的から遅れて MPEG-DASH への対応を行いました。
現在ではデバイスの種類やチャンネルに応じて 2 通りの配信方式を使い分けています。

HLS は Apple が作った規格です。 Apple が App Review Guildline で長時間の再生に HLS を使うべきとしていることもあってなのか、同種の技術の中でもかなり普及していると言えます。

一方 MPEG-DASH は ISO/IEC の規格です。 HLS, Smooth, HDS などの各企業が作った規格に代わって統一した規格としての普及を目指しています。

HLS, MPEG-DASH ともに大きく 2 種類のファイルから構成されます。

1 つは Video や Audio などのメディアデータのファイルです。大まかには単体で提供される動画ファイルと同じようなデータが入っていますが、数秒程度で fragment されていることによって動画全体をダウンロードせずとも任意の位置から再生が可能です。これらのデータは fragment されたそれぞれの chunk 毎に 1 ファイルとして扱う場合もあれば、大きなファイルから Range 取得する場合もあります。

もう 1 つは **Manifest** など [^manifest] と呼ばれるファイルで、メディアデータへのアクセス方法や再生方法が記述されます。 HLS の場合は M3U というフォーマットで書かれるテキストファイルで **Playlist** [^playlist] と呼びます。 MPEG-DASH の場合は XML で記述される **MPD(Media Presentation Description)** と呼ばれるファイルを用います。

AbemaTV 立ち上げ当時、既存のプレイヤーの対応状況やまた iOS の審査の事情等から HLS のみでスタートしました。しかし、コンテンツ保護などに関連する技術的な背景から一部デバイスで MPEG-DASH の対応を行う事になりました。

# AbemaTV の特殊なリニア型配信

AbemaTV では **リニア型配信** [^linear-tv] という番組表の時間の流れに沿った配信を行なっています。

多くの動画サービスでは、生配信とそうではない映画やドラマなどのオンデマンドコンテンツを区別して提供しています。この場合、ユーザはオンデマンドのコンテンツに対して好きなときに好きな位置を再生します。
しかし、 AbemaTV ではチャンネルという単位でそれぞれ番組をスケジュールし、生配信のときに用いられる HLS や MPEG-DASH の機能を使って配信しています。こうすることで再生端末は過去や未来の映像を知ることができず、あたかもテレビのように全てのユーザが時間的にほぼ同じ位置を再生します。

リニア型配信は単なる生配信と違ってスケジュールに従って映像ソースを切り替える必要があります。それを実現するためにテレビ局では APC システム (Automatic Program Control System) などと呼ばれるシステムが用いられるようで、 AbemaTV でも AbemaNews チャンネルのスタジオのみ使用されています [^ocris] 。 しかし数十チャンネルを独自に運用する AbemaTV において、全てそういった専用機材やオペレータを設けることは難しくまた広告挿入も含めてソフトウェアによる実現が必要でした。加えて、 2015 〜 2016 年当時はまだ即座に導入可能なリニア型配信ソリューションが見つからず、独自開発するに至りました。

大まかな構成は下図のようになっています。

<img width="1048" alt="システム構成図.png" src="https://qiita-image-store.s3.amazonaws.com/0/78005/b8a791be-525f-9517-b8f7-cf5e9fd7b135.png">

先述の通り　AbemaNews チャンネルは特別扱いで、番組表に関係なく APC システムで制御されます。また SDI 対応のインサータを備えていてフレーム単位での広告挿入が可能です。

AbemaNews チャンネル以外は大きく 2 種類の番組に分類され、番組ごとにエンコードされています。

まず 1 つは収録済み番組です。収録済みの場合、事前に時間を掛けて処理できるため同じビットレートでも生配信と比べて高い品質を実現でき、広告もフレーム単位の挿入位置制御が可能です。

もう 1 つは生配信番組です。生配信は映像の品質はやや落ちます。また AbemaNews のような専用機材を全ての現場で用意することが難しい等の理由から、広告挿入操作は PC から独自ツールで行い、サーバー上で挿入位置を決定します。現場設備で広告信号を差し込むわけではないため、意図したタイミングから数秒程度の誤差が生じます。

このように AbemaTV の配信システムは映像ソースの種類ごとに別でパッケージングされ、幅広い要件を吸収できることや映像種別ごとにチューニングできる点はメリットと言えます。しかし全体としてはかなり複雑なシステム構成です。特に MPEG-DASH への対応や新しいビットレートの追加など全映像種別に関わる変更を実施するコストは大きいといえます。

# AbemaTV の MPEG-DASH 対応

## プレイヤー側の対応

MPEG-DASH は極めて高機能で様々な表現方法を備えています。それ故、あらゆる表現にプレイヤーが網羅的に対応するのは困難です。

そこで、 MPEG-DASH には Profile という概念があります。オンデマンドや生放送のような配信形態やあるいはコンテナタイプなど用途ごとに Profile が定義されていて、それぞれ使える表現に制約が設けられています。これによってプレイヤーは主要な Profile に焦点を絞って開発することができます。

しかし、それでも MPEG-DASH への対応は難しいようです。調査開始当時に我々が求める機能全てに対して十分な実績のあるプレイヤーモジュールは見つからず、開発元に一部機能の改修を依頼するなどの対応をしながら導入を行いました。

以下では特に問題になった点を 2 つ取り上げます。

### Multi Period

番組や広告ごとにエンコーディング・パッケージングしているため、境界部分はコンテナあるいはコーデックレベルでの不連続点となります。この場合、プレイヤーに対してそれを認識させる必要があります。

HLS の場合、 Playlist に `#EXT-X-DISCONTINUITY` [^disc] と 1 行記述することで明示できます。これはほぼ全てのプレイヤーが対応しており、生放送でもオンデマンドでも問題なく使用できました。

一方 MPEG-DASH の場合、次のように Period タグを複数並べることで異なるコンテンツを直列に並べることが出来ます。（属性や他のタグは省略しています。）

```xml
<MPD>
    <Period> <!-- 映画 --> </Period>
    <Period> <!-- 広告1 --> </Period>
    <Period> <!-- 広告2 --> </Period>
    <Period> <!-- ドラマ --> </Period>
</MPD>
```

Static 形式(オンデマンド)の Multi Period MPD に対応しているプレイヤーは多いようです。しかし、リニア型配信には Dynamic 形式(ライブ)の Multi Period MPD が必要です。調査した幾つかのプレイヤーは対応が不完全で再生が止まってしまうトラブルも見つかりました。

### emsg Box

AbemaTV では映像に合わせて番組タイトルや視聴数、コメント、番組予約ボタン、広告アイキャッチ、投票など様々な UI の制御が行われています。これらは端末の時計で大雑把に推測しているわけではなく、映像に埋め込まれたメタデータを再生中にハンドリングして実現しています。 HLS の場合は Timed Metadata と呼ばれるものを MPEG2-TS のパケットとして埋め込んでいます。

MPEG-DASH の場合は MP4 の emsg Box を使用します。しかし、あるプレイヤーでは一部の emsg Box が取れなかったり、別のプレイヤーではそもそも対応していないなど問題が相次いで発生しました。
一部の emsg Box が取れないケースは emsg の event-duration が特定の値の範囲の場合に取得できなくなるというプレイヤーモジュールの不具合だということが開発元への問い合わせで判明し、下駄を履かせてその範囲を外すという暫定対応でしのぎました。

## サーバー側の対応

HLS において、現在では fMP4 を使うこともできますが、 AbemaTV 立ち上げ当初は MPEG2-TS 一択でした。一方で後発の MPEG-DASH では fMP4 が基本です。 MPEG2-TS 用の Profile もありますが、プレイヤーの対応は期待できません。そのため fMP4 への変換が必要になりました。

また先述の通り、 AbemaTV では独自の配信サーバーを作りリニア型配信を実現しています。そのため、独自の中間表現から HLS の Playlist や MPEG-DASH の MPD を生成しています。

MPD や fMP4 のサーバー上での取り扱いについて苦労した点を取り上げます。

### Segment の記述方法

HLS での基本的な Playlist の記述は TS ファイルのパスが並ぶだけの単純なものです。

```
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:5
#EXT-X-MEDIA-SEQUENCE:100
#EXTINF:5.0,
100.ts
#EXTINF:5.0,
101.ts
#EXTINF:5.0,
102.ts
```

この例の場合、現在時刻付近の chunk が古い順に 3 件示されています。最小限の構成なら極々簡単な記述だけでできてしまいます。 Byte Range を使う書き方もありますが、それも大きくは変わりません。

一方、 MPD には様々な情報を書かなければいけません。 MPD, Period, AdaptationSet, Representation の 4 つの階層が基本的な構成です。さらにセグメントの記述方式によって、 SegmentList, SegmentURL, SegmentTemplate, SegmentTimeline などのタグを使い分けます。
HLS に近いのは SegmentList と SegmentURL を使った記述だと思います。しかし、これらは生放送の Profile では使用できません。そのため SegmentTemplate, SegmentTimeline を使うことにしました。

```
    <SegmentTemplate timescale="90000" media="video/$RepresentationID$/$Time$.mp4" initialization="video/$RepresentationID$/init.mp4">
        <SegmentTimeline>
            <S d="720000" t="9360000"></S>
            <S d="810000"></S>
            <S d="540000"></S>
        </SegmentTimeline>
    </SegmentTemplate>
```

S タグが Segment 1 つを表しています。初めの Segment だけ Timecode が属性 t で書かれ、属性 d で示される duration を足していくと 2 件目以降の Timecode になります。 SegmentTemplate の media 属性が各 Segment のファイルパス、 initialization 属性が Initialization Segment のファイルパスを示しています。これらは `$~$` の形式の template で記述され、上図中 1 件目の Segment であれば `$Time$.mp4` は `9360000.mp4` となります。 Template で Segment ごとに変化するものは `$Time$` と　`$Number$` しか無く、パス中のその他の文字列は Period 内で変えることができません。 Segment のパスに対してサーバーは Time からデータを引き当てて返却することになります。

しかし、 HLS の場合を基準に設計したこともあり、そもそも Period という概念とその中での経過時間を取り扱いにくい状況に陥っていました。 HLS の場合には各 Segment の Duration を記述するものの、絶対的な時刻や最後に DISCONTINUITY が挿入された位置などを取り扱う必要はありません。そのため当初の想定より HLS の実装を流用できず、大幅な追加開発をすることになってしまいました。

### 生配信の fMP4 生成

MPEG-DASH 関連の調査開始当初は、先程の図の既製のパッケージャに MPEG-DASH を出力させる想定でした。しかし、調べていくと広告挿入などに不都合が出てきました。また、その製品で HLS の配信時にトラブルが相次いだこともあり、 MPEG-DASH のパッケージングをこのレイヤで行うことには未知の不具合への懸念がありました。

そこで、 1 つ後段の Watchman (ストリームを拾い上げて永続化やメタデータの処理、中間データの出力等を行う内製システム) のレイヤーで行うことにしました。

オンデマンドのコンテンツを MPEG2-TS から fMP4 変換することは比較的簡単ですが、生配信ですので chunk ごとにオンラインで変換する必要がありました。その際、例えば FFmpeg を使えば 1 つの TS ファイルから fMP4 を出力することは簡単です。しかし、 Timecode が累積した値にならず、それぞれ 0 に近い値で始まってしまいます。

そこで TS の Video, Audio それぞれ最初の PES Packet から PTS(Presentation Time Stamp) を取り出し、それを MP4 の tfdt Box の base-media-decode-time にセットする実装をしました。 MPEG2-TS では 1 秒を `27000000 / 300 = 90000` で表します。一方 MP4 では mdhd Box に timescale が書かれていてこの値が 1 秒になります。そこで chunk を　fMP4 に変換した後に `pts * mdhd_timescale / 90000` の計算結果を tfdt Box に上書きすることで時間的な整合性の取れたデータを構築できました。

# おわりに

色々つまずきはしたものの、 2017 年 2 月に一部 MPEG-DASH での配信を無事開始できました。
もともと 2016 年の秋頃を予定していたためだいぶ遅れてしまいましたが、なんとか終えることができて良かったです。

動画関連の専門知識が乏しかった中での MPEG-DASH 対応、それも独自のサーバー開発は少々厳しい点もありましたが、これをきっかけに色々と学ぶことができたのではないかと思っています。

やたらと苦労したような話を書きましたが、これは AbemaTV が特殊なケースだということもあり、また動画関連のソリューションは徐々に増えてきて、これから先は個々のサービスに携わるエンジニアが細かいことに悩まなくて良い方向に進んでいくのではないかと思います。

MPEG-DASH も徐々に普及してきている印象ですので、興味のある方は調べてみると面白いかもしれません。

[^manifest]: Manifest という呼び方はよく使われますが、この言葉の定義について言及している適当な文献が見当たりませんでした。ある動画配信事業のエンジニアからは Playlist は Manifest に含まれないと言われたこともあり、このあたりについて参考になる文献があれば教えてください。
[^playlist]: Playlist は UTF-8 で書かれ、 .m3u8 という拡張子を用いることが多いようです。
[^linear-tv]: リニア型配信、リニア配信、 Linear TV 等の表記を見たことが有りますが、言葉の使われ方について深くは調査していません。
[^ocris]: [「AbemaNews」向け自動番組送出・コンテンツ制作システムを納入](https://www.ikegami.co.jp/archives/8009)
[^disc]: https://tools.ietf.org/html/rfc8216#section-4.3.2.3