Androidの動画プレイヤー「ExoPlayer」との付き合い方


# ExoPlayerとは

Androidの動画再生用のライブラリで、Googleが提供しており、オープンソースです。

https://github.com/google/ExoPlayer

公式ドキュメントは[こちら](http://google.github.io/ExoPlayer/guide.html)にあります。
またGoogleIO 2016にも[セッション](https://www.youtube.com/watch?v=vOzOZ7hRr00)があるので、動画で見たい方はそちらが良いでしょう。基本的な使い方や、使うメリットなどが解説されています。

本気で使いたい方は公式ドキュメントを読み込み、[デモアプリ](https://github.com/google/ExoPlayer/tree/master/demo)を動かし、そのコードを参考にしながら作り込めば良いでしょう。

本記事では、動画アプリを作るために業務で約1年半このExoPlayerと付き合い続けた一人のAndroidエンジニアのExoPlayerとの付き合い方を紹介したいと思います。

# 使い方について

クラス数も多く、実際の動画再生までわりとコードを書かないといけませんが、デモアプリや公式ドキュメントが充実しているので、理解は容易いと思います。

その中で特に理解しておくべき事項としては[PlayerState](http://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/ExoPlayer.html#State)です。
この状態を受け取るための[コールバック](http://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/ExoPlayer.Listener.html#onPlayerStateChanged(boolean,%20int))があるので、再生したい動画フォーマットで実際に動かしてみて、この状態がどのように変化しているか見て理解することが一番重要だと思います。
こうすることでprepareやsetPlayWhenReadyなどを呼ぶと状態がどのように変化して、どのタイミングでbufferingが始まって、再生が始まっているなどが理解できると思います。

![exoplayer_playbackstate.png](https://qiita-image-store.s3.amazonaws.com/0/36413/39bd86ad-b7c3-07f5-aaeb-9579326d0f18.png)

# SurfaceView or TextureView

どちらを使うべきかは公式ドキュメントの[FAQ](https://google.github.io/ExoPlayer/faqs.html)に書かれております。
私が作ったアプリはminSdkVersionが16で、かつ、ViewPagerの中で動画を再生する必要があったため、TextureViewを採用することになりました。

# コールバックの多様さ

デモアプリの[DemoPlayerクラス](https://github.com/google/ExoPlayer/blob/master/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java#L61)を見るとコールバック用のインタフェースがたくさんあります。

```
public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener,
    HlsSampleSource.EventListener, ExtractorSampleSource.EventListener,
    SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
    MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
    StreamingDrmSessionManager.EventListener, DashChunkSource.EventListener, TextRenderer,
    MetadataRenderer<List<Id3Frame>>, DebugTextViewHelper.Provider
```

・・・・とにかく、これとうまく付き合っていかないといけません。
クラス名を見ればなんとなくわかりますが、HLSやMPEG-DASHなど、特定のAdaptive-Streamingの場合にしか使わないインタフェースがいくつかあります。まず、特定の場合にしか必要のないものは分離して、[共通のものだけ実装する](https://github.com/satorufujiwara/exoplayer-textureview/blob/master/player/src/main/java/jp/satorufujiwara/player/EventProxy.java#L22)のが良いと思います。
共通のものの中でも、stremingのtimed textやid3metatagのように特定の機能を使わない限り必要のないものもあります。

そうして特定の場合を省いていくと、アプリケーションのビジネスロジック側からは[ExoPlayer.Listener](https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/ExoPlayer.Listener.html)さえハンドリングできていれば、再生、停止などの処理、エラー処理などに対応できます。

以上のことに着眼しつつ、[DemoPlayerがExoPlayer.Listenerだけを実装するように再設計してPlayerを作る](https://github.com/satorufujiwara/exoplayer-textureview/blob/master/player/src/main/java/jp/satorufujiwara/player/Player.java#L23)ことをおすすめします。



# バージョンアップの多さ

ExoPlayerは[ojw28](https://github.com/ojw28)さんが[たった一人で作っているように見えます](https://github.com/google/ExoPlayer/graphs/contributors)。
にもかかわらず、[バージョンアップの頻度がすごく高い](https://github.com/google/ExoPlayer/releases)です。

彼がよりよい設計を模索し続けているのだと思いますが、バージョンアップごとにコールバックやクラスのコンストラクタといった、外部から呼ぶインタフェースに破壊的変更が入ります。

たとえばデモアプリ内にある、HLSを再生するときに使う[HlsRendererBuilder](https://github.com/google/ExoPlayer/blob/master/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java)というクラスは中の実装がバージョンアップごとに変更されます。
呼び出すクラスのコンストラクタの引数が増えたり、減ったりします。
[v1.5.7->v1.5.8の変更点](https://github.com/google/ExoPlayer/compare/r1.5.7...r1.5.8)の中のHlsRendererBuilderなどを眺めていると、クラスのコンストラクタに変更が入っているのがわかります。

この変更とうまく付き合うためには、動画の再生部分とアプリケーションを分離するラッパーを作ることを強くおすすめします。

最近、ExoPlayer用に[ブログ](https://medium.com/google-exoplayer)がオープンしました。こちらもたった一人で運用されているように見えますが、今後の変更はこれを追っかければよさそうに思います。

# まとめ

以上全てが面倒くさい！、さっさと動画再生したいよ！って方は以下のライブラリをお使いください。
気まぐれなExoPlayerのバージョンアップに1営業日(ベストエフォート)で対応させていただきます。

https://github.com/satorufujiwara/exoplayer-textureview