〈kotlin〉ExoPlayer
Android studioでkotlinを使ってAndroidアプリ作成の勉強中。
Androidで音声・動画の再生に使用できるExoPlayerライブラリを触ってみた。
ExoPlayerはMediaPlayer APIをもとに作られていて、
より多くの機能をサポートしており、拡張やカスタマイズも容易になっている。
exoplayer.dev
今回はこのcodelabをもとにした。
Media streaming with ExoPlayer | Android Developers
準備
ExoPlayerはGithubでホストされるオープンソースプロジェクトだったが、
Media3のライブラリに含められたのでMedia3から取得する。
dependencies { ... implementation "androidx.media3:media3-exoplayer:$mediaVersion" implementation "androidx.media3:media3-ui:$mediaVersion" implementation "androidx.media3:media3-exoplayer-dash:$mediaVersion" }
URLからインターネット上の動画でも再生できるようにManifestファイルにpermissionを追加。
<uses-permission android:name="android.permission.INTERNET"/>
レイアウト
<androidx.media3.ui.PlayerView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
基本の動画再生
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private var player: ExoPlayer? = null private var playWhenReady = true //再生・一時停止の状態を保存 private var currentItem = 0 //メディアアイテムのインデックスを保存 private var playbackPosition = 0L //再生位置を保存 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) } /* APIレベル24以上では、複数のウィンドウをサポートしており、分割ウィンドウモードではアプリは視認可能だがアクティブにならないため、onStartでプレーヤーを初期化する。 APIレベル23以下では、アプリがリソースを取得するまでできるだけ長く待機する必要があるため、プレーヤーを初期化する前に onResume まで待つ。 */ override fun onStart() { super.onStart() if(Util.SDK_INT > 23){ initializePlayer() } } override fun onResume() { super.onResume() hideSystemUi() //全画面表示にするために他のUIを非表示にする if ((Util.SDK_INT <= 23 || player == null)) { initializePlayer() } } private fun hideSystemUi() { WindowCompat.setDecorFitsSystemWindows(window, false) WindowInsetsControllerCompat(window, binding.videoView).let { controller -> controller.hide(WindowInsetsCompat.Type.systemBars()) controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE } } private fun initializePlayer(){ player = ExoPlayer.Builder(this) .build() .also { exoPlayer -> binding.videoView.player = exoPlayer val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp4)) exoPlayer.setMediaItem(mediaItem) exoPlayer.playWhenReady = playWhenReady exoPlayer.seekTo(currentItem, playbackPosition) //特定のアイテムの特定の位置から開始 exoPlayer.prepare() } } /* APIレベル23以下では、onStop が呼び出される保証がないため、onPause でできるだけ早くプレーヤーを解放する。 APIレベル24以上では、onStopが呼び出されることが保証される。一時停止状態ではアクティビティが引き続き表示されるため、onStopまで待ってからプレーヤーを解放する。 */ override fun onPause() { super.onPause() if(Util.SDK_INT <= 23){ releasePlayer() } } override fun onStop() { super.onStop() if(Util.SDK_INT > 23){ releasePlayer() } } private fun releasePlayer() { //playerを破棄する前に情報を保存しておき、中断したところから再開できるようにする。 player?.let { exoPlayer -> playbackPosition = exoPlayer.contentPosition currentItem = exoPlayer.currentMediaItemIndex playWhenReady = exoPlayer.playWhenReady exoPlayer.release() } player = null } }
playerは多くのリソースを占有する可能性があり、使用していない時には解放するよう、
playerのライフサイクルをアプリのライフサイクルに関連づけておく(onStart、onResumeでの初期化とonPause、onStopでの解放)。
プレイリストの作成
playerに対してMediaItemを追加することで複数のメディアアイテムの連続再生ができる。
val firstItem = MediaItem.fromUri(firstVideoUri) val secondItem = MediaItem.fromUri(secondVideoUri) player.addMediaItem(firstItem) player.addMediaItem(secondItem)
moveMediaItem, removeMediaItemなどで編集をすることも可能。
詳細はhttps://exoplayer.dev/playlists.html
アダプティブストリーミング
利用可能なネットワーク帯域幅に基づいて動画の再生品質を変え、ユーザーが快適に再生を続けることができるようにするストリーミング。
メディアコンテンツを品質(ビットレートと解像度)が異なる複数のトラックに分割して用意して、プレーヤーが利用可能なネットワーク帯域幅に基づいてトラックを選択する。
アダプティブ ストリーミングにはDASHというフォーマットがよく使われる。
DASH URIにはファイル拡張子がなくMIMEタイプを指定する必要があるため、MediaItem.Builder()を使用してMediaItemを作成する。
private fun initializePlayer() { //メディアアイテム内のトラックを選択するtrackSelectorを作成、パラメータで選択条件を指定する val trackSelector = DefaultTrackSelector(this).apply { setParameters(buildUponParameters().setMaxVideoSizeSd()) } player = ExoPlayer.Builder(this) .setTrackSelector(trackSelector) .build() .also { exoPlayer -> binding.videoView.player = exoPlayer val mediaItem = MediaItem.Builder() .setUri(getString(R.string.media_url_dash)) .setMimeType(MimeTypes.APPLICATION_MPD) .build() exoPlayer.setMediaItem(mediaItem) ... }