8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Kotlin研修10日目】メディアファイルの再生、MediaPlayerのライフサイクル

Posted at

メディアファイルの再生

メディアファイル(=音声動画ファイル)は、MediaPlayerクラスを用いて再生する。
メディアファイルを再生する手順は、以下の通り。

  1. MediaPlayerオブジェクトの生成
  2. メディアファイルURIを指定
  3. MediaPlayerオブジェクトのリスナ定義
  4. 非同期による再生準備
  5. MediaPlayerオブジェクトの解放

MediaPlayerオブジェクトの生成

MediaPlayer()でのMediaPlayerオブジェクトの生成時、MediaPlayerオブジェクトはIdle状態から始まる。

MediaPlayer

メディアファイルを制御するプレーヤーを定義するクラス。

MediaPlayerのライフサイクル

ライフサイクル メソッド 呼び出しタイミング
Idle MediaPlayer()
または
reset()
オブジェクトの生成
Initialized setDataSource() メディアファイルの定義
Preparing prepareAsync() 非同期でのメディアファイルの準備開始
Prepared OnPreparedListener.onPrepared()
または
prepare()
メディアファイルの準備完了
Started start() メディアファイル再生
Paused pause() メディアファイル一時停止
PlaybackCompleted OnCompletionListener.onCompletion() メディアファイル再生完了
Stopped stop() メディアファイル停止
End release() オブジェクトの解放
Error OnErrorListener.onError() エラーの発生

MediaPlayer.png
出典: MediaPlayer

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    // -> オブジェクト解放時はnullにするため、Nullable型で宣言
    private var _player: MediaPlayer? = null

    // アクティビティ初期化時の処理
    override fun onCreate(savedInstanceState: Bundle?) {
        // MediaPlayerオブジェクトの生成
        _player = MediaPlayer()
        ...
    }
    ...
}

再生するメディアファイルのURI指定

生成したMediaPlayerオブジェクトに制御対象のリソースファイルを定義することで、Initialized状態に遷移させる。

なお、リソースファイルresフォルダ内にResource Type: rawAndroid Resource Directoryを作成し、作成したディレクトリ内に配置しておく。

定義

MediaPlayer.setDataSource(
    context: Context,
    uri: Uri
): Unit
// パラメータ
// context: MediaPlayerを利用するアクティビティオブジェクト(コンテキスト)
// uri: メディアファイルのURI

String型→Uri型の変換

定義

Uri.parse(uriString: String!): Uri!
// パラメータ
// uriString: Uri型に変換するUri文字列

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    private var _player: MediaPlayer? = null

    // アクティビティ初期化時の処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // メディアファイルのURI文字列
        val mediaFileUriStr = "android.resource://${packageName}/${R.raw.<メディアファイル名>}"

        // String型 → URI型 への変換
        val mediaFileUri = Uri.parse(mediaFileUriStr)

        // MediaPlayerがnullでない場合の処理
        _player?.let {
            // メディアファイルの指定
            it.setDataSource(this@MainActivity, mediaFileUri)
            ...
        }
    }
}

MediaPlayerオブジェクトへのリスナ定義

メディアファイル準備完了(イベント)を検知するMediaPlayer.OnPreparedListenerインタフェースを実装したリスナクラスと、
メディアファイル再生完了(イベント)を検知するMediaPlayer.OnCompletionListenerインタフェースを実装したリスナクラスを定義する。

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    private var _player: MediaPlayer? = null

    // アクティビティ初期化時の処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // MediaPlayerがnullでない場合の処理
        _player?.let {
            ...
            // メディアファイルの準備完了時のリスナ定義
            it.setOnPreparedListener(PlayerPreparedListener())

            // メディアファイルの再生完了時のリスナ定義
            it.setOnCompletionListener(PlayerCompletionListener())
            ...
        }

    // "メディアファイルの準備完了"イベントを検知するリスナクラス
    private inner class PlayerPreparedListener: MediaPlayer.OnPreparedListener {
        // "メディアファイルの準備"完了時の処理
        override fun onPrepared(mp: MediaPlayer?) {
            ...
        }
    }

    // "メディアファイルの再生完了"イベントを検知するリスナクラス
    private inner class PlayerCompletionListener: MediaPlayer.OnCompletionListener {
        // "メディアファイルの再生"完了時の処理
        override fun onCompletion(mp: MediaPlayer?) {
            ...
        }
    }
    ...
}

非同期によるメディアファイルの再生準備

インターネットのストリーミング再生は一般的に処理に負荷がかかるため、
同期でメディアの再生準備を行うprepare()メソッドではなく、非同期で再生準備を行うprepareAsync()メソッドを用いる。

定義

MediaPlayer.prepareAsync()

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    private var _player: MediaPlayer? = null

    // アクティビティ初期化時の処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // MediaPlayerがnullでない場合の処理
        _player?.let {
            ...
            // 非同期でのMediaPlayerオブジェクトの準備
            it.prepareAsync()
        }
        ...
    }
    ...
}

MediaPlayerオブジェクトの解放

MediaPlayerシステムリソースを消費するため、アクティビティの終了時(=onDestroy())にMediaPlayerオブジェクトを解放する必要がある。

また、アクティビティクラスMediaPlayerをプロパティとして宣言している場合、MediaPlayerオブジェクトを確実に解放すると同時にプロパティの値をnullにしておくことが推奨される。

定義

MediaPlayer.release()

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    // -> オブジェクト解放時はnullにするため、Nullable型で宣言
    private var _player: MediaPlayer? = null
    ...

    // アクティビティ終了時の処理
    override fun onDestroy() {

        // MediaPlayerがnullでない場合の処理
        // -> let関数ブロック内のitは非Nullable型であるため、
        //    let関数内では_playerの値をnullにできない
        _player?.let {

            // MediaPlayerが再生中であれば、安全のために停止
            if (it.isPlaying) {
                // MediaPlayerの停止
                it.stop()
            }

            // MediaPlayerオブジェクトの解放
            it.release()
        }

        // MediaPlayerをnullに変更
        _player = null

        // アクティビティの終了
        super.onDestroy()
    }
}

プレーヤーの操作

メディアファイルの制御を行うMediaPlayerクラスのメソッドを用いてプレーヤーを操作する。

ただし、メディアファイルの停止を行うstop()メソッドを用いた場合、
再度再生する場合はprepare()メソッドを用いて再生準備を行う必要がある。
上図を参照

メディアファイルの再生・一時停止・停止

メソッド 内容
start() 再生
pause() 一時停止
stop() 停止

再生位置(シーク)の操作

メディアファイル再生位置(=シーク)を変更する場合は、MediaPlayerseekTo()メソッドを用いる。

定義

MediaPlayer.seekTo(msec: Int): Unit
// パラメータ
// msec: 開始位置からのオフセット[ms]

メディアファイルの先頭に戻る

サンプルコード

MainActivity.kt
MediaPlayer.seekTo(0)

メディアファイルの末尾に進む(再生を完了させる)

メディアファイルの再生を完了させるには、MediaPlayerクラスの現在セットされているメディアファイルの再生時間を保持するdurationプロパティを用いてシーク操作を行う。

なお、メディアファイルの一時停止(=Paused状態)中に終了位置までシーク操作を行った場合、
MediaPlayerは依然として再生完了(=PlaybackCompleted状態)直前のPaused状態であるため、
start()メソッドを用いてPlaybackCompleted状態に遷移させる必要がある。

サンプルコード

kotlin.MainActivity.kt
// MediaPlayerがnullでない場合の処理
MediaPlayer?.let {

    // 再生中のメディアファイルの長さ
    val duration = it.duration

    // 再生位置を末尾に変更
    it.seekTo(duration)

    // MediaPlayerが再生中でない場合の処理
    if (!it.isPlaying) {
        // MediaPlayerの再生
        it.start()
    }
}

ループ機能

現在セットされているメディアファイルループさせるには、MediaPlayerクラスのisLoopingプロパティを操作する。

ループ機能の実装にあたって、一般的にはトグルスイッチが用いられる。

トグルスイッチの実装

トグルスイッチBoolean型の値を保持するボタンであり、チェックボックスラジオボタンと同様、CompoundButtonクラスを継承している。

CompoundButtonクラス

Boolean型の値を保持するボタンを定義するクラス。

サンプルコード

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // MediaPlayerプロパティ
    // -> オブジェクト解放時はnullにするため、Nullable型で宣言
    private var _player: MediaPlayer? = null

    // アクティビティ初期化時の処理
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // MediaPlayerオブジェクトの生成
        _player = MediaPlayer()

        // トグルスイッチ
        val loopSwitch = findViewById<SwitchMaterial>(R.id.swLoop)
        ...
        // トグルスイッチをリスナとしてセット
        loopSwitch.setOnCheckedChangeListener(LoopSwitchChangedListener())
    }

    // ループスイッチの"状態遷移"イベントを検知するリスナクラス
    private inner class LoopSwitchChangedListener: CompoundButton.OnCheckedChangeListener {
        // "状態遷移"イベント検知時の処理
        override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
            // ループ機能の変更
            _player?.isLooping = isChecked
        }
    }
}
8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?