rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

ExoPlayerを使ってActivity内で音声再生 android開発

2020 Sep. 20.
2020 Jul. 24.

出典元
https://gumiossan.hatenablog.com/entry/2020/03/06/002802
https://github.com/Gumio/exo-sample-app
https://qiita.com/niusounds/items/cce4ff69f5911908259b
https://codelabs.developers.google.com/codelabs/exoplayer-intro/#0

build.gradle(Module: app)の編集

build.gradle(Module: app)のdependencies { }にExoPlayer2を追記する

implementation 'com.google.android.exoplayer:exoplayer:2.x.x'

  最新バージョンサイト exoplayer - Maven - Bintray

build.gradle(Module: app)のandroid { }にJava8を記述する

compileOptions { targetCompatibility JavaVersion.VERSION_1_8 }

// build.gradle(Module: app)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "fairway.rokkosan.mylearnexoplayer"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
    }

}


dependencies {
    implementation 'com.google.android.exoplayer:exoplayer:2.11.7'
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/video_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>


MainActivity.kt

package YOUR.PACKAGE.PROJECT

/*
 * Almost of these codes are copy from
 *   https://gumiossan.hatenablog.com/entry/2020/03/06/002802
 *   https://github.com/Gumio/exo-sample-app
*/

import android.annotation.SuppressLint
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View.*
import android.widget.Toast
import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.dash.DashMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private val TAG = "MyLog"
    private lateinit var playerView: PlayerView
    private var mySimpleExoPlayer: SimpleExoPlayer? = null
    private var myPlayWhenReady: Boolean = false
    private var playbackPosition = 0L
    private var currentWindow = 0

    // set eventListener-object of ExoPlayer2 to a variable.
    /*
     * Player.EventListener is public static interface
     * about Player.EventListener ->
     *   https://exoplayer.dev/listening-to-player-events.html
     *   https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html
     *   https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.EventListener.html
     *   https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/package-summary.html
     * ExoPlayer2 is version 2 of ExoPlayer.
     *     Exoplayer version 1 is not used.
     */
    private val playerEventListener = object : Player.EventListener {
        override fun onIsPlayingChanged(isPlaying: Boolean) {
            val state = if (isPlaying) "STATE_PLAYING" else "STATE_NOT_PLAYING"
            Log.d(TAG, "isPlayingChanged: $state")
        }

        override fun onPlayerError(error: ExoPlaybackException) {
            Log.d(TAG, "Error: ${error.message ?: ""}")
        }

        override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
            val stateStr = when(playbackState) {
                SimpleExoPlayer.STATE_IDLE -> "STATE_IDLE"
                SimpleExoPlayer.STATE_BUFFERING -> "STATE_BUFFERING"
                SimpleExoPlayer.STATE_READY -> "STATE_READY"
                SimpleExoPlayer.STATE_ENDED ->  "STATE_ENDED"
                else -> "UNKNOWN"
            }

            val strBool = if (playWhenReady) "true" else "false"
            Log.d(TAG, "state is $stateStr, playWhenReady is ${strBool}")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // get savedInstances if the Activity is being re-created.
        if (savedInstanceState != null) {
            myPlayWhenReady  = savedInstanceState.getBoolean("keyPlayWhenReady")
            playbackPosition = savedInstanceState.getLong("keyPlaybackPositon")
            Toast.makeText(this, "got playbackPosition: ${playbackPosition.toString()}",
                Toast.LENGTH_LONG).show()
        }

        val strBool: String = if ( myPlayWhenReady) "true" else "false"
        Log.d(TAG, "onCreating. myPlayWhenReady is ${strBool}")
        playerView = video_view
    }

    override fun onResume() {
        super.onResume()
        hideSystemUi()
        if (mySimpleExoPlayer == null) {
            initPlayer()
        }
    }

    override fun onStop() {
        releasePlayer()
        super.onStop()
    }

    override fun onPause() {
        releasePlayer()
        super.onPause()
    }

    // save values for re-creation of Activity
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putBoolean("keyPlayWhenReady", myPlayWhenReady)
        outState.putLong("keyPlaybackPositon", playbackPosition)
        Toast.makeText(this, "saved playbackPosition: ${playbackPosition.toString()}",
            Toast.LENGTH_LONG).show()
    }

    // create ExoPlayer-instance
    /*
     * SimpleExoPlayer extends ExoPlayer to add additional high level player functionality.
     * It prepares and plays media from a variety of sources.
     * about SimpleExoPlayer ->
     *   https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/SimpleExoPlayer.html
     */
    private fun initPlayer() {
        val mediaSource = buildMediaSource()
        val trackSelector = DefaultTrackSelector(this).apply {
            setParameters(buildUponParameters().setMaxVideoSizeSd())
        }
        mySimpleExoPlayer = SimpleExoPlayer.Builder(this)
            .setTrackSelector(trackSelector)
            .build()
            .apply {
                setPlayWhenReady(myPlayWhenReady)
                seekTo(currentWindow, playbackPosition)
                addListener(playerEventListener)
                prepare(mediaSource, false, false)
            }

        // connect ExoPlayer-instance to UI-PlayerView
        playerView.player = mySimpleExoPlayer
    }

    private fun buildMediaSource(): MediaSource {
        val dataSourceFactory = DefaultDataSourceFactory(this, "exoplayer-sample-app")

        // dash media
        /*
        val uri = Uri.parse(getString(R.string.media_url_dash))
        return DashMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
        */

        // mp4 media
        val uri = Uri.parse(getString(R.string.media_url_mp4))
        return ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
    }

    private fun releasePlayer() {
        mySimpleExoPlayer?.let {
            myPlayWhenReady = it.playWhenReady
            playbackPosition = it.currentPosition
            currentWindow = it.currentWindowIndex
            it.removeListener(playerEventListener)
            it.release()
            mySimpleExoPlayer = null
        }
    }

    // Make android-device-UI-screen disappeared for ExoPlayer to use all of screen.
    // Set off lint-check for "InlinedApi": Using inlined constants on older versions.
    @SuppressLint("InlinedApi")
    private fun hideSystemUi() {
        playerView.systemUiVisibility = (
                SYSTEM_UI_FLAG_LOW_PROFILE
                        or SYSTEM_UI_FLAG_FULLSCREEN
                        or SYSTEM_UI_FLAG_LAYOUT_STABLE
                        or SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        or SYSTEM_UI_FLAG_HIDE_NAVIGATION
        )
    }
}


strings.xml

<resources>
    <string name="app_name">MyLearnExoPlayer</string>
    <string name="media_url_dash">https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd</string>
    <string name="media_url_mp4">https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4</string>
</resources>