Skip to content

Commit

Permalink
feat(player): migrate ExoPlayer to media3
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibaultBee committed Aug 11, 2023
1 parent 9a2e8cc commit f4c206b
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 82 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ buildscript {
ext {
kotlinVersion = '1.8.0'
dokkaVersion = '1.8.20'
exoPlayerVersion = '1.1.0'
analyticsVersion = '1.4.0'
}

dependencies {
Expand All @@ -15,6 +17,6 @@ plugins {
id 'org.jetbrains.kotlin.android' version "$kotlinVersion" apply false
}

task clean(type: Delete) {
tasks.register('clean', Delete) {
delete rootProject.buildDir
}
9 changes: 5 additions & 4 deletions player/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ dependencies {

implementation 'androidx.core:core-ktx:1.10.1'
api 'com.android.volley:volley:1.2.1'
implementation 'com.google.android.exoplayer:exoplayer:2.19.0'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.0'
implementation 'video.api:android-player-analytics:1.3.1'
implementation 'video.api:android-player-analytics-exoplayer:1.3.1'
implementation "video.api:android-player-analytics-exoplayer:${analyticsVersion}"
implementation "androidx.media3:media3-ui:${exoPlayerVersion}"
implementation "androidx.media3:media3-exoplayer:${exoPlayerVersion}"
implementation "androidx.media3:media3-exoplayer-hls:${exoPlayerVersion}"
implementation "androidx.media:media:1.6.0"

testImplementation 'org.robolectric:robolectric:4.8.1'
testImplementation 'org.robolectric:shadows-httpclient:4.5.1'
Expand Down
89 changes: 59 additions & 30 deletions player/src/main/java/video/api/player/ApiVideoPlayerController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.SurfaceView
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.Player.*
import com.google.android.exoplayer2.analytics.AnalyticsListener
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime
import com.google.android.exoplayer2.source.LoadEventInfo
import com.google.android.exoplayer2.source.MediaLoadData
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.android.exoplayer2.video.VideoSize
import androidx.annotation.OptIn
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK
import androidx.media3.common.Player.REPEAT_MODE_ALL
import androidx.media3.common.Player.REPEAT_MODE_OFF
import androidx.media3.common.Player.STATE_ENDED
import androidx.media3.common.Player.STATE_READY
import androidx.media3.common.Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.analytics.AnalyticsListener
import androidx.media3.exoplayer.source.LoadEventInfo
import androidx.media3.exoplayer.source.MediaLoadData
import androidx.media3.ui.PlayerView
import video.api.analytics.exoplayer.ApiVideoAnalyticsListener
import video.api.player.extensions.currentVideoOptions
import video.api.player.extensions.setMediaSource
Expand All @@ -31,7 +41,7 @@ import java.io.IOException
*
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener a [Player.Listener] to listen to player events
* @param listener a [ApiVideoPlayerController.Listener] to listen to player events
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
class ApiVideoPlayerController
Expand All @@ -46,8 +56,8 @@ internal constructor(
/**
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener the [Player.Listener] to listen to player events
* @param playerView the [IExoPlayerBasedPlayerView] interface for ExoPlayer [StyledPlayerView] based player view
* @param listener the [ApiVideoPlayerController.Listener] to listen to player events
* @param playerView the [IExoPlayerBasedPlayerView] interface for ExoPlayer [PlayerView] based player view
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
constructor(
Expand All @@ -65,7 +75,7 @@ internal constructor(
initialVideoOptions,
initialAutoplay,
listener,
playerView.styledPlayerView,
playerView.playerView,
looper,
notificationController
) {
Expand All @@ -75,7 +85,7 @@ internal constructor(
/**
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener the [Player.Listener] to listen to player events
* @param listener the [ApiVideoPlayerController.Listener] to listen to player events
* @param playerView the [ISurfaceViewBasedPlayerView] interface for [SurfaceView] based player view
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
Expand Down Expand Up @@ -104,16 +114,16 @@ internal constructor(
/**
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener the [Player.Listener] to listen to player events
* @param styledPlayerView the [StyledPlayerView] to use to display the player
* @param listener the [ApiVideoPlayerController.Listener] to listen to player events
* @param playerView the [PlayerView] to use to display the player
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
constructor(
context: Context,
initialVideoOptions: VideoOptions? = null,
initialAutoplay: Boolean = false,
listener: Listener? = null,
styledPlayerView: StyledPlayerView,
playerView: PlayerView,
looper: Looper = Looper.myLooper() ?: Looper.getMainLooper(),
notificationController: ApiVideoPlayerNotificationController? = ApiVideoPlayerNotificationController(
context
Expand All @@ -126,13 +136,13 @@ internal constructor(
looper,
notificationController
) {
styledPlayerView.player = exoplayer
playerView.player = exoplayer
}

/**
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener the [Player.Listener] to listen to player events
* @param listener the [ApiVideoPlayerController.Listener] to listen to player events
* @param surfaceView the [SurfaceView] to use to display the video
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
Expand Down Expand Up @@ -160,7 +170,7 @@ internal constructor(
/**
* @param context the application context
* @param initialVideoOptions initial video options
* @param listener the [Player.Listener] to listen to player events
* @param listener the [ApiVideoPlayerController.Listener] to listen to player events
* @param surface the [Surface] to use to display the video
* @param looper the looper where call to the player are executed. By default, it is the current looper or the main looper.
*/
Expand Down Expand Up @@ -212,12 +222,17 @@ internal constructor(
}

private val exoplayerListener: AnalyticsListener = object : AnalyticsListener {
override fun onPlayerError(eventTime: EventTime, error: PlaybackException) {
@OptIn(UnstableApi::class)
override fun onPlayerError(
eventTime: AnalyticsListener.EventTime,
error: PlaybackException
) {
listeners.forEach { listener -> listener.onError(error) }
}

@OptIn(UnstableApi::class)
override fun onMediaItemTransition(
eventTime: EventTime,
eventTime: AnalyticsListener.EventTime,
mediaItem: MediaItem?,
reason: Int
) {
Expand All @@ -231,15 +246,17 @@ internal constructor(
}
}

override fun onTimelineChanged(eventTime: EventTime, reason: Int) {
@OptIn(UnstableApi::class)
override fun onTimelineChanged(eventTime: AnalyticsListener.EventTime, reason: Int) {
if (reason == TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
firstPlay = true
isReady = false
}
}

@OptIn(UnstableApi::class)
override fun onLoadError(
eventTime: EventTime,
eventTime: AnalyticsListener.EventTime,
loadEventInfo: LoadEventInfo,
mediaLoadData: MediaLoadData,
error: IOException,
Expand All @@ -255,7 +272,11 @@ internal constructor(
} ?: listeners.forEach { listener -> listener.onError(error) }
}

override fun onIsPlayingChanged(eventTime: EventTime, isPlaying: Boolean) {
@OptIn(UnstableApi::class)
override fun onIsPlayingChanged(
eventTime: AnalyticsListener.EventTime,
isPlaying: Boolean
) {
if (isPlaying) {
if (firstPlay) {
firstPlay = false
Expand All @@ -269,7 +290,8 @@ internal constructor(
}
}

override fun onPlaybackStateChanged(eventTime: EventTime, state: Int) {
@OptIn(UnstableApi::class)
override fun onPlaybackStateChanged(eventTime: AnalyticsListener.EventTime, state: Int) {
if (state == STATE_READY) {
if (!isReady) {
isReady = true
Expand All @@ -280,18 +302,23 @@ internal constructor(
}
}

@OptIn(UnstableApi::class)
override fun onPositionDiscontinuity(
eventTime: EventTime,
oldPosition: PositionInfo,
newPosition: PositionInfo,
eventTime: AnalyticsListener.EventTime,
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
reason: Int
) {
if (reason == DISCONTINUITY_REASON_SEEK) {
listeners.forEach { listener -> listener.onSeek() }
}
}

override fun onVideoSizeChanged(eventTime: EventTime, videoSize: VideoSize) {
@OptIn(UnstableApi::class)
override fun onVideoSizeChanged(
eventTime: AnalyticsListener.EventTime,
videoSize: VideoSize
) {
listeners.forEach { listener ->
listener.onVideoSizeChanged(
Size(
Expand All @@ -303,6 +330,7 @@ internal constructor(
}
}

@OptIn(UnstableApi::class)
private val exoplayer =
ExoPlayer.Builder(context).setLooper(looper).build().apply {
addAnalyticsListener(exoplayerListener)
Expand Down Expand Up @@ -399,6 +427,7 @@ internal constructor(
* @return the video size
*/
val videoSize: Size?
@OptIn(UnstableApi::class)
get() = exoplayer.videoFormat?.let { Size(it.width, it.height) }

/**
Expand Down Expand Up @@ -442,7 +471,7 @@ internal constructor(
exoplayer.repeatMode = REPEAT_MODE_OFF
}
}

var playbackSpeed: Float
/**
* Get the playback speed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import android.content.Context
import android.support.v4.media.session.MediaSessionCompat
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.OptIn
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.ui.PlayerNotificationManager
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerNotificationManager
import video.api.player.models.ApiVideoMediaDescriptionAdapter


Expand All @@ -17,11 +19,13 @@ import video.api.player.models.ApiVideoMediaDescriptionAdapter
* @param context the application context
*/
class ApiVideoPlayerNotificationController
@OptIn(UnstableApi::class)
constructor(
private val context: Context,
private val playerNotificationManager: PlayerNotificationManager,
private val mediaSession: MediaSessionCompat = MediaSessionCompat(context, TAG),
) {
@OptIn(UnstableApi::class)
constructor(
context: Context,
mediaSession: MediaSessionCompat = MediaSessionCompat(context, TAG),
Expand Down Expand Up @@ -57,10 +61,12 @@ constructor(
mediaSession.isActive = value
}

@OptIn(UnstableApi::class)
fun hideNotification() {
playerNotificationManager.setPlayer(null)
}

@OptIn(UnstableApi::class)
fun showNotification(player: ExoPlayer) {
playerNotificationManager.setPlayer(player)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package video.api.player.extensions

import android.util.Log
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.Player
import androidx.annotation.OptIn
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource
import video.api.player.models.ApiVideoExoPlayerMediaFactory
import video.api.player.models.VideoOptions

Expand Down Expand Up @@ -34,6 +36,7 @@ fun ExoPlayer.setMediaSource(
*
* @param mediaSourceFactory The [ApiVideoExoPlayerMediaFactory] to play
*/
@OptIn(UnstableApi::class)
fun ExoPlayer.setMediaSource(
mediaSourceFactory: ApiVideoExoPlayerMediaFactory,
) {
Expand Down Expand Up @@ -65,6 +68,7 @@ fun ExoPlayer.setMp4MediaSource(
*
* @param mediaSourceFactory The [ApiVideoExoPlayerMediaFactory] to play
*/
@OptIn(UnstableApi::class)
fun ExoPlayer.setMp4MediaSource(
mediaSourceFactory: ApiVideoExoPlayerMediaFactory,
) {
Expand Down Expand Up @@ -96,6 +100,7 @@ fun ExoPlayer.addMediaSource(
*
* @param mediaSourceFactory The [ApiVideoExoPlayerMediaFactory] to play
*/
@OptIn(UnstableApi::class)
fun ExoPlayer.addMediaSource(
mediaSourceFactory: ApiVideoExoPlayerMediaFactory,
) {
Expand Down Expand Up @@ -127,6 +132,7 @@ fun ExoPlayer.addMp4MediaSource(
*
* @param mediaSourceFactory The [ApiVideoExoPlayerMediaFactory] to play
*/
@OptIn(UnstableApi::class)
fun ExoPlayer.addMp4MediaSource(
mediaSourceFactory: ApiVideoExoPlayerMediaFactory,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package video.api.player.interfaces

import com.google.android.exoplayer2.ui.StyledPlayerView
import androidx.media3.ui.PlayerView
import video.api.player.ApiVideoPlayerController

interface IExoPlayerBasedPlayerView : ApiVideoPlayerController.Listener {
val styledPlayerView: StyledPlayerView
val playerView: PlayerView
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package video.api.player.interfaces

import android.view.SurfaceView
import com.google.android.exoplayer2.ui.StyledPlayerView
import video.api.player.ApiVideoPlayerController

interface ISurfaceViewBasedPlayerView : ApiVideoPlayerController.Listener {
Expand Down
Loading

0 comments on commit f4c206b

Please sign in to comment.