この記事は Android スマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML 構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、記事の最後で紹介します)
ExoPlayerは、Github でホストされるオープンソースプロジェクトでしたが、Jetpack Media3ライブラリとして統合され、今後はMedia3版が主流となる予定です。
基本的な使い方は同じですので、Media3版を使用することをおすすめします。
◎オープンソース版
ExoPlayerは動画・音楽ファイルのローカル再生のほか、動的適応型 HTTP ストリーミング(DASH)、SmoothStreaming、共通暗号化など、MediaPlayerではサポートされていない機能をサポートしています。
動画を再生表示するビュー画面、動画や音楽の再生や一時停止などを操作するためのコントローラなどカスタマイズ可能なコンポーネントをもつライブラリです。
今回はMedia3版のExoPlayerを使用して、動画や音楽をローカル再生する方法を紹介いたします。
ExoPlayerを使用するための準備
ExoPlayerを使用するには、モジュールのbuild.gradleファイルに定義の追加が必要です。
◎build.gradle(モジュール)
2024年3月現在の最新バージョンは1.3.0です。
dependencies {
…
implementation 'androidx.media3:media3-exoplayer:1.3.0'
implementation 'androidx.media3:media3-ui:1.3.'
implementation 'androidx.media3:media3-session:1.3.0'
…
}
メディアストアの音楽や動画にアクセスする
アプリで使用する動画や音楽ですが、メディアストアでハンドリングする方法が一般的です。
メディアストアの動画や音楽ファイルにアクセスする場合、きめ細かいメディア権限を設定する必要があります。
詳細は、Android13対応(ファイルのメディア権限)で紹介しています。
ExoPlayerで動画を再生する
ExoPlayerで動画を再生するには、動画ファイルのアクセスに必要なUriが必要です。
MP4形式以外の動画ファイルをMP4形式に変換する方法はこちらで紹介しています↓↓↓
動画ファイルのメタ情報を取得する
メディアストアはスマホ本体の画像・音楽・動画をコンテンツリゾルバを経由してアクセスができます。
下記のサンプリでは、メディアストアで管理している動画をコンテンツリゾルバにクエリを指定して、mp4形式の動画ファイルを登録の降順で取得しています。
動画ファイルのアクセスに必要なUriは、ContentUrisを使用して取得しています。
取得した動画ファイルのメタ情報はエンティティ(VideoItem)に格納しています。
private Map<String, VideoItem> videoItemMap = new LinkedHashMap<>();
…
try {
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, "MIME_TYPE == 'video/mp4'", null, "_ID DESC");
while (cursor.moveToNext()) {
Uri contentUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, Long.parseLong(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))));
videoItemMap.put(cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.DISPLAY_NAME)),
new VideoItem(cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.TITLE)),
Long.parseLong(cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.DURATION))),
contentUri, cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.DISPLAY_NAME))));
}
}
}
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
…
◎エンティティ(VideoItem)
public class VideoItem {
public String title; // タイトル(xxx)
public long duration; // 再生時間(ms)
public Uri videoUri; // 動画ファイルUri
public String source; // 動画ファイル(xxx.mp4)
public VideoItem(String title, long duration, Uri videoUri, String source) {
this.title = title;
this.duration = duration;
this.videoUri = videoUri;
this.source = source;
}
}
コントローラを標準のまま使用する
PlayerViewは動画を再生表示するビュー画面です。
PlayerViewには動画の再生や一時停止などを操作するためのコントローラが標準で実装されているため、ExoPlayerをアタッチするだけで、基本的な操作ができるようになります。
標準コントローラの構成は、先頭に戻る(前の動画)、5秒戻る、再生(一時停止)、15秒進む、次の動画を操作できるボタン、動画の再生位置の操作できるスライダー、再生位置(秒)と再生時間(秒)の表示、再生オプションのメニューです。
ExoPlayerはBuilderでインスタンス化します。
サンプルではイヤホンが外れた時に再生を止めるsetHandleAudioBecomingNoisyを指定しています。
標準コントローラの再生オプションで再生速度を変更することができます。
サンプルではインスタンス化したExoPlayerにリスナーを追加し、onPlaybackParametersChangedで再生速度が標準以外に変更された場合にミュートする機能を実装しています。
setPlayerで、PlayerViewにExoPlayerをアタッチします。
再生する動画の指定は、MediaItemを使用します。
動画ファイルのUriを使用してMediaItemを生成します。
再生位置(ミリ秒)を引数にsetMediaItemでExoPlayerにセットします。
セットしたMediaItemのロードは、prepareを使用します。
…
PlayerView playerView = dialog.findViewById(R.id.video);
ExoPlayer exoPlayer = new ExoPlayer.Builder(context)
.setHandleAudioBecomingNoisy(true)
.build();
exoPlayer.addListener(new Player.Listener() {
@Override
public void onPlaybackParametersChanged(@NonNull PlaybackParameters playbackParameters) {
exoPlayer.setVolume(playbackParameters.speed != 1 ? 0 : 1.0);
Player.Listener.super.onPlaybackParametersChanged(playbackParameters);
}
});
playerView.setPlayer(exoPlayer);
exoPlayer.setMediaItem(MediaItem.fromUri(uri), 0);
exoPlayer.prepare();
…
// 現在値設定
dialog.findViewById(R.id.set).setOnClickListener(view -> {
dialog.findViewById(R.id.set).requestFocus();
exoPlayer.pause();
exoPlayer.seekTo(exoPlayer.getContentPosition());
…
});
…
// closeボタン
dialog.findViewById(R.id.close).setOnClickListener(view -> {
exoPlayer.stop();
exoPlayer.release();
…
});
return dialog;
}
…
再生の一時停止や再生位置の変更はコントローラで行いますが、一時停止はPause、再生位置はseekToにミリ秒を指定することでも可能です。
終了時(画面を閉じる)に停止(stop)とリリース(release)します。
◎レイアウト(dailog.xml)
…
<androidx.media3.ui.PlayerView
android:id="@+id/video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:resize_mode="fill"
app:show_timeout="500"/>
…
<Button
android:id="@+id/set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
…
<Button
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
…
PlayerViewに動画を再生表示するビュー画面のサイズを指定します。
全体表示はresize_modeに “fill” を指定します。
動画の再生中のコントローラ表示の時間は、show_timeoutにミリ秒を指定します。
コントローラを表示しない場合は0を指定します。
◎標準コントローラの黒の透過表示をキャンセル
標準コントローラは表示中、動画の画面は黒色の透過表示です。
colors.xmlに定義を追加するだけで黒色の透過表示をキャンセルできます。
<resources
xmlns:tools="http://schemas.android.com/tools">
…
<color name="exo_bottom_bar_background" tools:override="true" >#00FFFFFF</color>
<color name="exo_black_opacity_60" tools:override="true" >#00FFFFFF</color>
</resources>
コントローラをカスタムする
標準のコントローラを使用しない場合、レイアウトXMLのPlayerViewの属性(show_timeout)に0を指定し、controller_layout_idにカスタムしたコントローラのレイアウトを指定します。
ExoPlayerのインスタンス化、再生する動画の指定は、コントローラを標準のまま使用する場合と同じです。
サンプルでは動画の再生位置の操作できるスライダー、再生位置(秒)と再生時間(秒)はカスタムしたコントローラに実装するため、DefaultTimeBarを非表示にしています。
private mode = 0;
…
ExoPlayer exoPlayer = new ExoPlayer.Builder(context)
.setHandleAudioBecomingNoisy(true)
.build();
exoPlayer.setMediaItem(MediaItem.fromUri(uri), 0);
exoPlayer.prepare();
videoView = view.findViewById(R.id.videoView);
DefaultTimeBar timeBar = videoView.findViewById(R.id.exo_progress);
timeBar.setVisibility(View.GONE);
videoView.setPlayer(exoPlayer);
// PLAY & PAUSE
control = view.findViewById(R.id.control);
control.setOnClickListener(view -> {
if (mode == 1) {
// PAUSE
exoPlayer.pause();
mode = 0;
…
} else {
// PLAY
exoPlayer.play();
mode = 1;
…
}
});
return view;
}
…
// onPause //
@Override
public void onPause() {
if (exoPlayer != null) {
exoPlayer.pause();
}
super.onPause();
}
// onDestroy //
@Override
public void onDestroy() {
if (exoPlayer != null) {
exoPlayer.stop();
exoPlayer.release();
}
super.onDestroy();
}
…
サンプルでは再生と一時停止を操作するボタンで用意し、modeで再生と一時停止を切り替えています。
一時停止はPause、再生はplayを使用します。
終了時(画面を閉じる)に停止(stop)とリリース(release)します。
◎レイアウト(fragment.xml)
…
<a