本当に読んだときの自分のメモ書きなので、英語読める方はそちらのほうが良いと思います。
https://medium.com/google-developers/building-a-video-player-app-in-android-part-1-5-d95770ef762d
本当はPart5まで見たかったのですが、時間切れしました。もしかしたら追加するかもです。
Listening to player events for UX
が良かったです。もう少し深く見ていきたいです。
ExoPlayer introduction(Part 1)
Overview of using ExoPlayer
SimpleExoPlayer
とMediaSource
がビデオファイルを再生するのに必要になる。
SimpleExoPlayer
を作成するにはtrack selector
(どのファイルやどの言語でどの音声で再生するか決める)がいる。
MediaSource
クラスを使うことでmp4とかmp3とかのUriを読み込める。(HLSを再生できるHlsMediaSource
とかもある。)player.playWhenReadyをtrueにするとデータが有ると再生する。
そしてPlayerView
にプレーヤーをアタッチしてあげる必要がある。PlayerView
はUIコントロールも提供する。動画のソースを提供する前にUIを用意する方が良い。なぜなら再生されている間にアタッチされるようなことがあり得るため。
またplayerの準備が必要でロードのスタートを知らせる。(バッファリングに時間がかかる可能性があるため。)
playerのplayWhenReadyにtrueをセットするとデータが読み込まれた時に再生を行う。
Proguardをかけてサイズを減らそう。
Create the ExoPlayer instance
このコードはplayerをデフォルトのオプションで作っている。そしてPlayerView
にアタッチしている。
https://gist.github.com/nazmulidris/6a40763185ed0653320d8efae66256bc#file-create-ep-instance-kt
Loading files locally from APK using ExoPlayer
DefaultDataSource
は以下のUriのロードに対応している。
file:///
asset:///
content:///
rtmp
data
http(s)
以下のようにローカルファイルをロードできる(assetフォルダーにネストした形でフォルダを作れる)
Uri.parse(“file:///android_asset/video/video.mp4”)
Uri.parse(“asset:///video/video.mp4”)
RawResourceDataSourceを使うとres/rawのなかに置いてアクセスできる
RawResourceDataSource.buildRawResourceUri(R.raw.my_media_file)
Release ExoPlayer resources
コーデックやネットワークやメモリを利用しているので再生が完了したときplayerを終了させる必要がある。システムコーデックはシステム内でシェアされていて、OSや端末によって限られているため、それを開放することは重要である。以下のコードで同じExoPlayerを再利用できる。
https://gist.github.com/nazmulidris/6bb12dcf8e0b67e43f4d47ae0f18176b#file-ep-release-kt
これはplayerにより確保されている全てのリソースを開放している(コーデックやメディアソースなど)。もう一度playerを利用するときはprepare(MediaSource)
を呼び、そしてplayWhenReady
をセットする。
ExoPlayer.release()
は1つを除いてstop()と同じ動きをします。再生スレッドを停止し、ExpPlayerのインスタンスの再利用を防ぐ。ReleaseはActivity.onDestroyやService.onDestroyなどplayerがもう使われない時に呼び出すべき。
Activity Lifecycle Integration
作成して、利用し、リリースするためにActivityのライフサイクルを利用する必要がある。
シンプルな例が以下になる。
https://gist.github.com/nazmulidris/8e1e8b3cf67ae9b563e4dbe699847d01#file-ep-video-activity-kt
Saving player state between onStart() and onStop()
PlayerStateデータクラスはplayerが最初に読み込まれる前に、playerの状態を読み込むために利用される。Playerがリリースされた時Playerの状態はこのクラスのオブジェクトに保存される。新しいPlayerを作成した時にこのシンプルな状態オブジェクトを利用して前回やめた位置から再開する。
https://gist.github.com/nazmulidris/62f45c3eaa9c77bb7a8cb8d77369261f#file-player-state-kt
UIの観点からはこれはアプリの実行中何かを再生していて、ホームボタンを押し、playerリソースがリリースされ、アプリに戻ってきた時にplayerが再度初期化され、前回の状態がリストアされ、やめたところから再開できる。
どのようにその保存と復元をやるかは以下のコードで行っている。
https://gist.github.com/nazmulidris/6d02ac7e4f64ad86c61a2d081c18ca49#file-playerholder-release-kt
A little more control over player creation
playerをカスタマイズするためにExoPlayerFactor.newSimpleInstanceの別のシグネチャを利用できる。例えばDefaultLoadControl
クラスを使うことでExoPlayerのbuffering policyを変更することが出来る。
https://gist.github.com/nazmulidris/f1d66dd5f5962aa187909fec9eaaf687#file-ep-creation-extra-kt
もっと複雑なユースケースではExoPlayerFactory.newSimpleInstance(…)
ファクトリーメソッドの全ての引数に、自分の実装を渡すことが出来る。これによりExoPlayerで大きく拡張性を得ることが出来る。
ExoPlayer playlists, UI customization, and events(Part 2)
Creating playlists
一つのExtractorMediaSource(buildMediaSource(Uri)を使う方法)を使う代わりに、DynamicConcatenatingMediaSourceを使うことが出来る。MediaSourceオブジェクトをつなげて利用することが出来る。これがサンプルになる。
https://gist.github.com/nazmulidris/82cebffca8fbd159382fb2c0d89caf69#file-ep-playlist-kt
DynamicConcatenatingMediaSource
は動的にプレイリストを作る。もし静的なプレイリストを作りたければ、ContactenatingMediaSource
を使うことが出来る。どちらもシームレスにメディアをつなげ、プレイリスト全体のバッファリングを処理する。MediaSource
の構成の詳細を知るためにはこの記事を読むと良い。
Customizing the UI
ExoPlayerインスタンスをPlayerViewにアタッチ出来る。PlayerViewのレイアウトの例を示す。
ビデオの早送りや巻き戻しそして他のもっと多くの事ができる。ExoPlayerのJavaDocで全てをみることができる。
https://gist.github.com/nazmulidris/f4a1fc64aa52d01e2844ee4218ca890c#file-ep-custom-layout-xml
コントローラーを使わない場合はapp:use_controller=”false”
を利用する。PlayerView
は自動的にmedia controller UIを表示する。これらはユーザーに再生、一時停止、スキップなどを提供する。show_timeout
を使うことで、このコントローラーがどのぐらいの間表示されるかを設定できる。
コントローラーのUIをカスタマイズするために、controller_layout_id
を使うことが出来る。これがサンプルとなる。
https://gist.github.com/nazmulidris/d9c92c56bc6d7fb5c83759537575257a#file-ep-controller-ui-xml
PlayerView自身の前に覆って表示される。再生、一時停止、スキップなどを追加したり、削除したりすることができる。ExoPlayerによって提供されているIDを利用することで、ExoPlayerが知ることが出来る。PlayerViewのカスタマイズに関してはこの記事を読んで欲しい。
Listening to player events for UX
ExoPlayer.EventListener
インターフェースを介して、たくさんの価値のある情報を提供する。これをインターフェースを実装するかサブクラスのPlayer.DefaultEventListener
を実装することで、Playerの状態が変わった時に受け取ることが出来る。また、VideoRendererEventListener
とAudioRendererEventListener
を実装することができ、これによりaudio
やvideo
のレンダリングの詳細を取得することが出来る。
それぞれを利用することで、アプリのユーザーにとってquality of experience (QoE)がどのように影響を受けているか知ることができる。
これらを利用するサンプル
-
STATE_BUFFERING
とともにExoPlayer.EventListener.OnPlaybackStateChanged()
が呼ばれた時に、もし再生開始時や、ユーザーがまだ再生可能でない場所を指定した場合でなければ、QoEにとって有害であると考えられる。 - ユーザーによって、再生が開始されてから、
ExoPlayer.VideListener.onFirstFrameRendered
が呼ばれるまでにかかった時間は、再生の最初の時間となる。良いQoEのためには少ないほうが望ましい。 -
VideoRendererEventListener.onDroppedFrames
はframe落ちに関しての情報を提供する。とても多くのこのイベントはQoEに悪い影響をもたらす。
この記事では詳細をカバーしない。ExoPlayerのイベントとQoEシグナルをもっと知るためにはExoPlayer codelabのMeasure QoE sectionを必ず行ってください。Playerの再生、バッファリング、一時停止中を深く理解するためにはこのStackOverflowを参照してください。
Adaptive streaming
ExoPlayerはAPKやネットワークからメディアファイルを読み込むだけでなく、adaptive streamingを広範囲にサポートしている。adaptive streamingはビデオと音声を所定の期間の小さいチャンクにカットする。ExoPlayerはそれを再生のためにつなげる。それぞれのチャンクは異なる品質(サイズまたはビットレート)が利用できる。プレーヤーはデバイスの性能やネットワークの帯域によってその品質を選択する。プレーヤーは低画質から再生を開始し、帯域が利用可能になれば良い画質に切り替える(例えば遅いモバイルネットワークからWiFiに切り替えるなど)
HLS(HTTP Live Streaming)やMPEG-DASH (Dynamic Adaptive Streaming over HTTP)やSmoothStreamingについてMDN記事で学ぶことが出来る。DASH and HLSの違いについて学ぶにはこのExoplayerブログを確認してほしい。これらの記事ではadaptive streamingにExoPlayerを利用することはないが、もっと学びたい場合は、ExoPlayer codelabを確認して欲しい。