Android 新時代のメディアプレイヤー ExoPlayer に関するメモ

最近、 Android のメディアプレイヤーをずっと調べたり弄ったりしているわけだけど、 Google が作っている ExoPlayer が割と本命だと思っている。しかし日本語で情報がほぼ無くて悲しいので、簡単に説明しつつ、簡単に使い方も書いておきます。

ExoPlayer とは

公式ガイドを読むのが正確かつ早い。

従来の MediaPlayer (Stagefright / NuPlayer) は C や C++ で実装され、かつ実装は Android フレームワークに組み込まれているので、 MediaPlayer 内部で不具合があったときに修正や、 MediaPlayer そのものを修正して新たなフォーマットやプロトコルに対応させることをアプリ開発者が自由に行なえず、端末や OS のアップデートが必要でした。

ExoPlayer は Google が開発している Android 向けの新しいメディアプレイヤーで、全て Java で実装されており、またアプリケーションに組み込んで利用するライブラリの形式で配布されています。

これにより従来の MediaPlayer で不可能だった、アプリ開発者によるプレイヤーの拡張や修正が可能になっています。

利点

  • MPEG-DASH や SmoothStreaming といった MediaPlayer が対応していないアダプティブストリーミングの形式に対応しています
  • HLS の #EXT-X-DISCONTINUITY タグに対応しています
  • アプリ開発者がプレイヤーをカスタマイズしやすいように設計されています
  • アプリにライブラリとしてプレイヤーを組み込むので、アプリ開発者は簡単にプレイヤーをアップデートすることが出来ます
  • プレイヤーがアプリに組み込まれているのでデバイス固有の問題が少ないでしょう
    • 従来の MediaPlayer は実装を端末メーカーが改造・拡張しているために、デバイス固有の問題が多い

欠点

  • ExoPlayer は Android 4.1 で実装された MediaCodec に依存しているため、それ以前の Android では動作しません
  • 自動的にメディアフォーマットを検出して再生することが出来ません (この欠点は改善予定です)

使い方

その他の概要などはガイドを読んでもらうとして、簡単な使い方でも書いておこうと思います。

現在の ExoPlayer は、再生したいメディア形式に最適な Renderer を指定する必要があります。これは、欠点にも書いてあるように「自動的にフォーマットを検出できない」という問題によるものです。

ExoPlayer のインスタンスを作る

ExoPlayer のインスタンスはファクトリーを通じて生成することが出来ます。インスタンスを生成する際にはレンダラー数を指定する必要があります。例えば、動画の場合は "映像" + "音声" で 2 を指定します。音楽プレイヤーをならば 1 で十分でしょう。

// 動画を再生するために rendererCount = 2 を指定
ExoPlayer player = ExoPlayer.Factory.newInstance(2); 

ウェブにある mp4 ファイルを再生する

ExoPlayer は再生するメディアの情報が格納された SampleSource というオブジェクトを生成する必要があります。 SampleSource は、メディアがどこにあるかを指定する DataSource を必要とします。この場合、ウェブから mp4 ファイルを取得してくるので、次のようになります。

// URI から取得してくる DataSource を生成
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);

// DataSource と URI を指定して SampleSource を生成する
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE);

これでデータの準備は完了です。

次にデータを再生するレンダラーを生成します。今回は動画なので、 "映像" と "音声" の 2 つが必要です。

MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(
    sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);

さて、最後にレンダラーを player 渡して動画を再生してもらいます。

player.prepare(videoRenderer, audioRenderer);

また、動画を再生する場合は、映像を表示するために Surface を指定する必要があります (SurfaceView または TextureView から取得したもの) 。これは何故か Interface が用意されていないので sendMessage する必要があります。

player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);

最後に、動画の準備が完了したら自動的に再生するよう setPlayWhenReady を true に指定します。

player.setPlayWhenReady(true);

ExoPlayer を使う際によくある勘違い

  • Play は自動的に行なわれたけど Pause がない

動画が再生できるならば再生するかどうかのフラグ setPlayWhenReady の値を変えることで play/pause をすることができます。バッファが詰まって再生 (Play) できていない状況で停止 (Pause) するのはおかしいという発想でしょうか。

// play
player.setPlayWhenReady(true);

// pause
player.setPlayWhenReady(false);

さらなる情報

ExoPlayer は開発途上なので、油断するとどんどんインターフェースが変わっていきますが、利点にもあるようにアプリに直接組み込めるので、アプリ開発者がバージョンを変えない限り動作しなくなることはありません。

使い方に関しては公式ガイドを読むのも良いんですが、 DemoPlayer を読むのが良い。というか、いちいち SampleSource を作って Renderer に食わせるのは地味に面倒なので、 DemoPlayer を取り込むのが良いですね。

DemoPlayer を使えば、ウェブから mp4 ファイルを取得して再生するコードが以下のようになります。

player = new DemoPlayer(new ExtractorRendererBuilder(context, userAgent, contentUri));
player.setSurface(surface);
player.prepare();

player.setPlayWhenReady(true);

簡単!!

追記

すっかり忘れてたけどライブラリを追加するには build.gradle の dependency に

compile 'com.google.android.exoplayer:exoplayer:r1.4.2'

と書けばよい (2015/9/17 現在) 。バージョンはわりとサクサクあがっていくと思います。