From a302eb04aa3ca90221686e8a397f77dc821b105e Mon Sep 17 00:00:00 2001 From: lovegaoshi Date: Tue, 5 Sep 2023 21:44:04 +0000 Subject: [PATCH 1/8] feat: 2nd exoplayer --- .../kotlinaudio/players/BaseAudioPlayer.kt | 95 ++++++++++++------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 8d7ce7cb..0cdae605 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -86,16 +86,32 @@ abstract class BaseAudioPlayer internal constructor( ) : AudioManager.OnAudioFocusChangeListener { protected val exoPlayer: ExoPlayer + private var cache: SimpleCache? = null private val scope = MainScope() private var playerConfig: PlayerConfig = playerConfig + private var currentPlayer = 0 + protected val exoPlayer2: ExoPlayer + + private fun currentPlayer(): ExoPlayer { + if(currentPlayer == 0) return exoPlayer; + else return exoPlayer2; + } + + fun switchPlayer() { + currentPlayer += 1; + if (currentPlayer > 1) { + currentPlayer = 0; + } + } + val notificationManager: NotificationManager open val playerOptions: PlayerOptions = DefaultPlayerOptions() open val currentItem: AudioItem? - get() = exoPlayer.currentMediaItem?.getAudioItemHolder()?.audioItem + get() = currentPlayer().currentMediaItem?.getAudioItemHolder()?.audioItem var playbackError: PlaybackError? = null var playerState: AudioPlayerState = AudioPlayerState.IDLE @@ -115,44 +131,44 @@ abstract class BaseAudioPlayer internal constructor( } var playWhenReady: Boolean - get() = exoPlayer.playWhenReady + get() = currentPlayer().playWhenReady set(value) { - exoPlayer.playWhenReady = value + currentPlayer().playWhenReady = value } val duration: Long get() { - return if (exoPlayer.duration == C.TIME_UNSET) 0 - else exoPlayer.duration + return if (currentPlayer().duration == C.TIME_UNSET) 0 + else currentPlayer().duration } val isCurrentMediaItemLive: Boolean - get() = exoPlayer.isCurrentMediaItemLive + get() = currentPlayer().isCurrentMediaItemLive private var oldPosition = 0L val position: Long get() { - return if (exoPlayer.currentPosition == C.POSITION_UNSET.toLong()) 0 - else exoPlayer.currentPosition + return if (currentPlayer().currentPosition == C.POSITION_UNSET.toLong()) 0 + else currentPlayer().currentPosition } val bufferedPosition: Long get() { - return if (exoPlayer.bufferedPosition == C.POSITION_UNSET.toLong()) 0 - else exoPlayer.bufferedPosition + return if (currentPlayer().bufferedPosition == C.POSITION_UNSET.toLong()) 0 + else currentPlayer().bufferedPosition } var volume: Float - get() = exoPlayer.volume + get() = currentPlayer().volume set(value) { - exoPlayer.volume = value * volumeMultiplier + currentPlayer().volume = value * volumeMultiplier } var playbackSpeed: Float - get() = exoPlayer.playbackParameters.speed + get() = currentPlayer().playbackParameters.speed set(value) { - exoPlayer.setPlaybackSpeed(value) + currentPlayer().setPlaybackSpeed(value) } var automaticallyUpdateNotificationMetadata: Boolean = true @@ -164,7 +180,7 @@ abstract class BaseAudioPlayer internal constructor( } val isPlaying - get() = exoPlayer.isPlaying + get() = currentPlayer().isPlaying private val notificationEventHolder = NotificationEventHolder() private val playerEventHolder = PlayerEventHolder() @@ -225,10 +241,16 @@ abstract class BaseAudioPlayer internal constructor( } .build() + exoPlayer2 = ExoPlayer.Builder(context) + .setHandleAudioBecomingNoisy(playerConfig.handleAudioBecomingNoisy) + .apply { + if (bufferConfig != null) setLoadControl(setupBuffer(bufferConfig)) + } + .build() mediaSession.isActive = true val playerToUse = - if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else exoPlayer + if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else currentPlayer() notificationManager = NotificationManager( context, @@ -239,7 +261,7 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder ) - exoPlayer.addListener(PlayerListener()) + currentPlayer().addListener(PlayerListener()) scope.launch { // Whether ExoPlayer should manage audio focus for us automatically @@ -256,7 +278,7 @@ abstract class BaseAudioPlayer internal constructor( } ) .build(); - exoPlayer.setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); + currentPlayer().setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); mediaSessionConnector.setPlayer(playerToUse) mediaSessionConnector.setMediaMetadataProvider { notificationManager.getMediaMetadataCompat() @@ -267,7 +289,7 @@ abstract class BaseAudioPlayer internal constructor( } private fun createForwardingPlayer(): ForwardingPlayer { - return object : ForwardingPlayer(exoPlayer) { + return object : ForwardingPlayer(currentPlayer()) { override fun play() { playerEventHolder.updateOnPlayerActionTriggeredExternally(MediaSessionCallback.PLAY) } @@ -346,7 +368,7 @@ abstract class BaseAudioPlayer internal constructor( * @param playWhenReady Whether playback starts automatically. */ open fun load(item: AudioItem, playWhenReady: Boolean = true) { - exoPlayer.playWhenReady = playWhenReady + currentPlayer().playWhenReady = playWhenReady load(item) } @@ -356,12 +378,12 @@ abstract class BaseAudioPlayer internal constructor( */ open fun load(item: AudioItem) { val mediaSource = getMediaSourceFromAudioItem(item) - exoPlayer.addMediaSource(mediaSource) - exoPlayer.prepare() + currentPlayer().addMediaSource(mediaSource) + currentPlayer().prepare() } fun togglePlaying() { - if (exoPlayer.isPlaying) { + if (currentPlayer().isPlaying) { pause() } else { play() @@ -369,26 +391,26 @@ abstract class BaseAudioPlayer internal constructor( } var skipSilence: Boolean - get() = exoPlayer.skipSilenceEnabled + get() = currentPlayer().skipSilenceEnabled set(value) { - exoPlayer.skipSilenceEnabled = value; + currentPlayer().skipSilenceEnabled = value; } fun play() { - exoPlayer.play() + currentPlayer().play() if (currentItem != null) { - exoPlayer.prepare() + currentPlayer().prepare() } } fun prepare() { if (currentItem != null) { - exoPlayer.prepare() + currentPlayer().prepare() } } fun pause() { - exoPlayer.pause() + currentPlayer().pause() } /** @@ -399,20 +421,20 @@ abstract class BaseAudioPlayer internal constructor( @CallSuper open fun stop() { playerState = AudioPlayerState.STOPPED - exoPlayer.playWhenReady = false - exoPlayer.stop() + currentPlayer().playWhenReady = false + currentPlayer().stop() } @CallSuper open fun clear() { - exoPlayer.clearMediaItems() + currentPlayer().clearMediaItems() } /** * Pause playback whenever an item plays to its end. */ fun setPauseAtEndOfItem(pause: Boolean) { - exoPlayer.pauseAtEndOfMediaItems = pause + currentPlayer().pauseAtEndOfMediaItems = pause } /** @@ -424,6 +446,7 @@ abstract class BaseAudioPlayer internal constructor( stop() notificationManager.destroy() exoPlayer.release() + exoPlayer2.release() cache?.release() cache = null mediaSession.isActive = false @@ -431,12 +454,12 @@ abstract class BaseAudioPlayer internal constructor( open fun seek(duration: Long, unit: TimeUnit) { val positionMs = TimeUnit.MILLISECONDS.convert(duration, unit) - exoPlayer.seekTo(positionMs) + currentPlayer().seekTo(positionMs) } open fun seekBy(offset: Long, unit: TimeUnit) { - val positionMs = exoPlayer.currentPosition + TimeUnit.MILLISECONDS.convert(offset, unit) - exoPlayer.seekTo(positionMs) + val positionMs = currentPlayer().currentPosition + TimeUnit.MILLISECONDS.convert(offset, unit) + currentPlayer().seekTo(positionMs) } protected fun getMediaSourceFromAudioItem(audioItem: AudioItem): MediaSource { From 021aed6f62306f7ab96e85b8b929208f8796906f Mon Sep 17 00:00:00 2001 From: lovegaoshi Date: Tue, 5 Sep 2023 22:49:17 +0000 Subject: [PATCH 2/8] feat: exoplayer2 --- .../kotlinaudio/players/BaseAudioPlayer.kt | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 0cdae605..ba054b20 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -99,14 +99,7 @@ abstract class BaseAudioPlayer internal constructor( else return exoPlayer2; } - fun switchPlayer() { - currentPlayer += 1; - if (currentPlayer > 1) { - currentPlayer = 0; - } - } - - val notificationManager: NotificationManager + var notificationManager: NotificationManager open val playerOptions: PlayerOptions = DefaultPlayerOptions() @@ -288,6 +281,50 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) } + fun switchExoplayer() { + currentPlayer += 1; + if (currentPlayer > 1) { + currentPlayer = 0; + } + val playerToUse = + if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else currentPlayer() + + notificationManager = NotificationManager( + context, + playerToUse, + mediaSession, + mediaSessionConnector, + notificationEventHolder, + playerEventHolder + ) + + currentPlayer().addListener(PlayerListener()) + + scope.launch { + // Whether ExoPlayer should manage audio focus for us automatically + // see https://medium.com/google-exoplayer/easy-audio-focus-with-exoplayer-a2dcbbe4640e + val audioAttributes = AudioAttributes.Builder() + .setUsage(C.USAGE_MEDIA) + .setContentType( + when (playerConfig.audioContentType) { + AudioContentType.MUSIC -> C.AUDIO_CONTENT_TYPE_MUSIC + AudioContentType.SPEECH -> C.AUDIO_CONTENT_TYPE_SPEECH + AudioContentType.SONIFICATION -> C.AUDIO_CONTENT_TYPE_SONIFICATION + AudioContentType.MOVIE -> C.AUDIO_CONTENT_TYPE_MOVIE + AudioContentType.UNKNOWN -> C.AUDIO_CONTENT_TYPE_UNKNOWN + } + ) + .build(); + currentPlayer().setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); + mediaSessionConnector.setPlayer(playerToUse) + mediaSessionConnector.setMediaMetadataProvider { + notificationManager.getMediaMetadataCompat() + } + } + + playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) + } + private fun createForwardingPlayer(): ForwardingPlayer { return object : ForwardingPlayer(currentPlayer()) { override fun play() { From 74eeef3f2dd1b437cb7bf9cc637dad39c95eb9de Mon Sep 17 00:00:00 2001 From: lovegaoshi Date: Wed, 6 Sep 2023 22:21:24 +0000 Subject: [PATCH 3/8] feat: exoplayer2 --- .../kotlinaudio/players/BaseAudioPlayer.kt | 33 ++++++++++--- .../kotlinaudio/players/QueuedAudioPlayer.kt | 47 ++++++++++++------- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index ba054b20..7dffcdd9 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -73,6 +73,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource import com.google.android.exoplayer2.upstream.cache.SimpleCache import com.google.android.exoplayer2.util.Util import kotlinx.coroutines.MainScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import timber.log.Timber import java.util.Locale @@ -93,10 +94,16 @@ abstract class BaseAudioPlayer internal constructor( private var currentPlayer = 0 protected val exoPlayer2: ExoPlayer + val playerListener = PlayerListener() - private fun currentPlayer(): ExoPlayer { - if(currentPlayer == 0) return exoPlayer; - else return exoPlayer2; + fun currentPlayer(): ExoPlayer { + return if(currentPlayer == 0) exoPlayer; + else exoPlayer2; + } + + private fun otherPlayer(): ExoPlayer { + return if(currentPlayer == 1) exoPlayer; + else exoPlayer2; } var notificationManager: NotificationManager @@ -254,7 +261,7 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder ) - currentPlayer().addListener(PlayerListener()) + currentPlayer().addListener(playerListener) scope.launch { // Whether ExoPlayer should manage audio focus for us automatically @@ -286,6 +293,19 @@ abstract class BaseAudioPlayer internal constructor( if (currentPlayer > 1) { currentPlayer = 0; } + scope.launch { + val fadeOutPlayer = otherPlayer(); + fadeOutPlayer.removeListener(playerListener) + val volume = fadeOutPlayer.volume; + var fadeOutDuration = 500L; + val fadeOutInterval = 20L; + while (fadeOutDuration > 0) { + fadeOutDuration -= fadeOutInterval; + fadeOutPlayer.volume -= volume * fadeOutInterval / fadeOutDuration; + delay(fadeOutInterval); + } + fadeOutPlayer.pause() + } val playerToUse = if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else currentPlayer() @@ -298,7 +318,7 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder ) - currentPlayer().addListener(PlayerListener()) + currentPlayer().addListener(playerListener) scope.launch { // Whether ExoPlayer should manage audio focus for us automatically @@ -464,7 +484,8 @@ abstract class BaseAudioPlayer internal constructor( @CallSuper open fun clear() { - currentPlayer().clearMediaItems() + exoPlayer.clearMediaItems() + exoPlayer2.clearMediaItems() } /** diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt index b8550c93..1c324007 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt @@ -20,21 +20,21 @@ class QueuedAudioPlayer( override val playerOptions = DefaultQueuedPlayerOptions(exoPlayer) val currentIndex - get() = exoPlayer.currentMediaItemIndex + get() = currentPlayer().currentMediaItemIndex override val currentItem: AudioItem? get() = queue.getOrNull(currentIndex)?.mediaItem?.getAudioItemHolder()?.audioItem val nextIndex: Int? get() { - return if (exoPlayer.nextMediaItemIndex == C.INDEX_UNSET) null - else exoPlayer.nextMediaItemIndex + return if (currentPlayer().nextMediaItemIndex == C.INDEX_UNSET) null + else currentPlayer().nextMediaItemIndex } val previousIndex: Int? get() { - return if (exoPlayer.previousMediaItemIndex == C.INDEX_UNSET) null - else exoPlayer.previousMediaItemIndex + return if (currentPlayer().previousMediaItemIndex == C.INDEX_UNSET) null + else currentPlayer().previousMediaItemIndex } val items: List @@ -44,7 +44,7 @@ class QueuedAudioPlayer( get() { return if (queue.isEmpty()) emptyList() else queue - .subList(0, exoPlayer.currentMediaItemIndex) + .subList(0, currentPlayer().currentMediaItemIndex) .map { it.mediaItem.getAudioItemHolder().audioItem } } @@ -52,7 +52,7 @@ class QueuedAudioPlayer( get() { return if (queue.isEmpty()) emptyList() else queue - .subList(exoPlayer.currentMediaItemIndex, queue.lastIndex) + .subList(currentPlayer().currentMediaItemIndex, queue.lastIndex) .map { it.mediaItem.getAudioItemHolder().audioItem } } @@ -63,7 +63,7 @@ class QueuedAudioPlayer( get() = items.getOrNull(currentIndex - 1) override fun load(item: AudioItem, playWhenReady: Boolean) { - exoPlayer.playWhenReady = playWhenReady + currentPlayer().playWhenReady = playWhenReady load(item) } @@ -76,6 +76,9 @@ class QueuedAudioPlayer( exoPlayer.addMediaSource(currentIndex + 1, mediaSource) exoPlayer.removeMediaItem(currentIndex) exoPlayer.prepare() + exoPlayer2.addMediaSource(currentIndex + 1, mediaSource) + exoPlayer2.removeMediaItem(currentIndex) + exoPlayer2.prepare() } } @@ -84,7 +87,7 @@ class QueuedAudioPlayer( * @param item The [AudioItem] to add. */ fun add(item: AudioItem, playWhenReady: Boolean) { - exoPlayer.playWhenReady = playWhenReady + currentPlayer().playWhenReady = playWhenReady add(item) } @@ -98,6 +101,8 @@ class QueuedAudioPlayer( queue.add(mediaSource) exoPlayer.addMediaSource(mediaSource) exoPlayer.prepare() + exoPlayer2.addMediaSource(mediaSource) + exoPlayer2.prepare() } /** @@ -106,7 +111,7 @@ class QueuedAudioPlayer( * @param playWhenReady Whether playback starts automatically. */ fun add(items: List, playWhenReady: Boolean) { - exoPlayer.playWhenReady = playWhenReady + currentPlayer().playWhenReady = playWhenReady add(items) } @@ -119,6 +124,8 @@ class QueuedAudioPlayer( queue.addAll(mediaSources) exoPlayer.addMediaSources(mediaSources) exoPlayer.prepare() + exoPlayer2.addMediaSources(mediaSources) + exoPlayer2.prepare() } @@ -132,6 +139,8 @@ class QueuedAudioPlayer( queue.addAll(atIndex, mediaSources) exoPlayer.addMediaSources(atIndex, mediaSources) exoPlayer.prepare() + exoPlayer2.addMediaSources(atIndex, mediaSources) + exoPlayer2.prepare() } /** @@ -141,6 +150,7 @@ class QueuedAudioPlayer( fun remove(index: Int) { queue.removeAt(index) exoPlayer.removeMediaItem(index) + exoPlayer2.removeMediaItem(index) } /** @@ -162,8 +172,8 @@ class QueuedAudioPlayer( * Does nothing if there is no next item to skip to. */ fun next() { - exoPlayer.seekToNextMediaItem() - exoPlayer.prepare() + currentPlayer().seekToNextMediaItem() + currentPlayer().prepare() } /** @@ -171,8 +181,8 @@ class QueuedAudioPlayer( * Does nothing if there is no previous item to skip to. */ fun previous() { - exoPlayer.seekToPreviousMediaItem() - exoPlayer.prepare() + currentPlayer().seekToPreviousMediaItem() + currentPlayer().prepare() } /** @@ -182,6 +192,7 @@ class QueuedAudioPlayer( */ fun move(fromIndex: Int, toIndex: Int) { exoPlayer.moveMediaItem(fromIndex, toIndex) + exoPlayer2.moveMediaItem(fromIndex, toIndex) val item = queue[fromIndex] queue.removeAt(fromIndex) queue.add(max(0, min(items.size, if (toIndex > fromIndex) toIndex else toIndex - 1)), item) @@ -193,7 +204,7 @@ class QueuedAudioPlayer( * @param playWhenReady Whether playback starts automatically. */ fun jumpToItem(index: Int, playWhenReady: Boolean) { - exoPlayer.playWhenReady = playWhenReady + currentPlayer().playWhenReady = playWhenReady jumpToItem(index) } @@ -203,8 +214,8 @@ class QueuedAudioPlayer( */ fun jumpToItem(index: Int) { try { - exoPlayer.seekTo(index, C.TIME_UNSET) - exoPlayer.prepare() + currentPlayer().seekTo(index, C.TIME_UNSET) + currentPlayer().prepare() } catch (e: IllegalSeekPositionException) { throw Error("This item index $index does not exist. The size of the queue is ${queue.size} items.") } @@ -231,6 +242,7 @@ class QueuedAudioPlayer( val fromIndex = currentIndex + 1 exoPlayer.removeMediaItems(fromIndex, lastIndex) + exoPlayer2.removeMediaItems(fromIndex, lastIndex) queue.subList(fromIndex, lastIndex).clear() } @@ -239,6 +251,7 @@ class QueuedAudioPlayer( */ fun removePreviousItems() { exoPlayer.removeMediaItems(0, currentIndex) + exoPlayer2.removeMediaItems(0, currentIndex) queue.subList(0, currentIndex).clear() } From 9d8821454ecac47bfd684d00107027cbb78bd37f Mon Sep 17 00:00:00 2001 From: lovegaoshi Date: Thu, 7 Sep 2023 21:50:37 +0000 Subject: [PATCH 4/8] feat: 2 exoplayer --- .../kotlin_audio_example/MainActivity.kt | 3 +++ .../ui/component/PlayerControls.kt | 22 +++++++++++++++++++ .../kotlinaudio/players/BaseAudioPlayer.kt | 21 +++++++++++------- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt index 7cf74eef..dd6ed2f8 100644 --- a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt @@ -114,6 +114,7 @@ class MainActivity : ComponentActivity() { player.play() } }, + onSwitchExoPlayer = { player.switchExoplayer() }, onSeek = { player.seek(it, TimeUnit.MILLISECONDS) } ) } @@ -242,6 +243,7 @@ fun MainScreen( onTopBarAction: () -> Unit = {}, onPlayPause: () -> Unit = {}, onSeek: (Long) -> Unit = {}, + onSwitchExoPlayer: () -> Unit = {}, ) { Surface( modifier = Modifier.fillMaxSize(), @@ -282,6 +284,7 @@ fun MainScreen( onNext = onNext, isPaused = isPaused, onPlayPause = onPlayPause, + onSwitchExoPlayer = onSwitchExoPlayer, modifier = Modifier .fillMaxWidth() .padding(bottom = 60.dp) diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt index 2afb5a46..13d918fb 100644 --- a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt @@ -12,6 +12,7 @@ import androidx.compose.material.icons.rounded.FastForward import androidx.compose.material.icons.rounded.FastRewind import androidx.compose.material.icons.rounded.PauseCircle import androidx.compose.material.icons.rounded.PlayCircle +import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable @@ -29,6 +30,7 @@ fun PlayerControls( onNext: () -> Unit = {}, isPaused: Boolean, onPlayPause: () -> Unit = {}, + onSwitchExoPlayer: () -> Unit = {}, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -85,6 +87,26 @@ fun PlayerControls( ) } } + Row ( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = modifier + ) { + IconButton( + onClick = onSwitchExoPlayer, + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) { + Icon( + Icons.Rounded.Refresh, + contentDescription = "changeExoPlayer", + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) + } + } } @Preview(showBackground = true) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 7dffcdd9..56eb5f75 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -297,17 +297,19 @@ abstract class BaseAudioPlayer internal constructor( val fadeOutPlayer = otherPlayer(); fadeOutPlayer.removeListener(playerListener) val volume = fadeOutPlayer.volume; - var fadeOutDuration = 500L; - val fadeOutInterval = 20L; + var fadeOutDuration = 2500L + val fadeOutInterval = 20L while (fadeOutDuration > 0) { - fadeOutDuration -= fadeOutInterval; - fadeOutPlayer.volume -= volume * fadeOutInterval / fadeOutDuration; - delay(fadeOutInterval); + fadeOutDuration -= fadeOutInterval + fadeOutPlayer.volume -= volume * fadeOutInterval / fadeOutDuration + delay(fadeOutInterval) } + fadeOutPlayer.seekToNextMediaItem() fadeOutPlayer.pause() } + val playerObject = currentPlayer() val playerToUse = - if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else currentPlayer() + if (playerConfig.interceptPlayerActionsTriggeredExternally) createForwardingPlayer() else playerObject notificationManager = NotificationManager( context, @@ -318,7 +320,7 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder ) - currentPlayer().addListener(playerListener) + playerObject.addListener(playerListener) scope.launch { // Whether ExoPlayer should manage audio focus for us automatically @@ -335,11 +337,14 @@ abstract class BaseAudioPlayer internal constructor( } ) .build(); - currentPlayer().setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); + playerObject.setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); mediaSessionConnector.setPlayer(playerToUse) mediaSessionConnector.setMediaMetadataProvider { notificationManager.getMediaMetadataCompat() } + playerObject.playWhenReady = true + playerObject.seekToNextMediaItem() + playerObject.prepare() } playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) From 5a7532a37a346cd5c50a45904ebcbc884b225653 Mon Sep 17 00:00:00 2001 From: lovegaoshi Date: Thu, 7 Sep 2023 22:03:55 +0000 Subject: [PATCH 5/8] feat: 2 exoplayer --- .../kotlinaudio/players/QueuedAudioPlayer.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt index 1c324007..210a76c1 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt @@ -75,10 +75,9 @@ class QueuedAudioPlayer( queue[currentIndex] = mediaSource exoPlayer.addMediaSource(currentIndex + 1, mediaSource) exoPlayer.removeMediaItem(currentIndex) - exoPlayer.prepare() exoPlayer2.addMediaSource(currentIndex + 1, mediaSource) exoPlayer2.removeMediaItem(currentIndex) - exoPlayer2.prepare() + currentPlayer().prepare() } } @@ -100,9 +99,8 @@ class QueuedAudioPlayer( val mediaSource = getMediaSourceFromAudioItem(item) queue.add(mediaSource) exoPlayer.addMediaSource(mediaSource) - exoPlayer.prepare() exoPlayer2.addMediaSource(mediaSource) - exoPlayer2.prepare() + currentPlayer().prepare() } /** @@ -123,9 +121,8 @@ class QueuedAudioPlayer( val mediaSources = items.map { getMediaSourceFromAudioItem(it) } queue.addAll(mediaSources) exoPlayer.addMediaSources(mediaSources) - exoPlayer.prepare() exoPlayer2.addMediaSources(mediaSources) - exoPlayer2.prepare() + currentPlayer().prepare() } @@ -138,9 +135,8 @@ class QueuedAudioPlayer( val mediaSources = items.map { getMediaSourceFromAudioItem(it) } queue.addAll(atIndex, mediaSources) exoPlayer.addMediaSources(atIndex, mediaSources) - exoPlayer.prepare() exoPlayer2.addMediaSources(atIndex, mediaSources) - exoPlayer2.prepare() + currentPlayer().prepare() } /** @@ -172,7 +168,8 @@ class QueuedAudioPlayer( * Does nothing if there is no next item to skip to. */ fun next() { - currentPlayer().seekToNextMediaItem() + exoPlayer.seekToNextMediaItem() + exoPlayer2.seekToNextMediaItem() currentPlayer().prepare() } @@ -181,7 +178,8 @@ class QueuedAudioPlayer( * Does nothing if there is no previous item to skip to. */ fun previous() { - currentPlayer().seekToPreviousMediaItem() + exoplayer.seekToPreviousMediaItem() + exoplayer2.seekToPreviousMediaItem() currentPlayer().prepare() } @@ -214,7 +212,8 @@ class QueuedAudioPlayer( */ fun jumpToItem(index: Int) { try { - currentPlayer().seekTo(index, C.TIME_UNSET) + exoplayer.seekTo(index, C.TIME_UNSET) + exoPlayer2.seekTo(index, C.TIME_UNSET) currentPlayer().prepare() } catch (e: IllegalSeekPositionException) { throw Error("This item index $index does not exist. The size of the queue is ${queue.size} items.") From d9e4d5329e96442690e4240ae70b6b3651947c72 Mon Sep 17 00:00:00 2001 From: unknown <106490582+lovegaoshi@users.noreply.github.com> Date: Fri, 8 Sep 2023 06:11:59 -0700 Subject: [PATCH 6/8] feat: 2 exoplayer --- .../doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt | 7 +++++-- .../kotlinaudio/players/QueuedAudioPlayer.kt | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 56eb5f75..0e9ed9c2 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -8,6 +8,7 @@ import android.os.Bundle import android.os.ResultReceiver import android.support.v4.media.RatingCompat import android.support.v4.media.session.MediaSessionCompat +import android.util.Log import androidx.annotation.CallSuper import androidx.core.content.ContextCompat import androidx.media.AudioAttributesCompat @@ -78,6 +79,7 @@ import kotlinx.coroutines.launch import timber.log.Timber import java.util.Locale import java.util.concurrent.TimeUnit +import kotlin.math.log abstract class BaseAudioPlayer internal constructor( internal val context: Context, @@ -297,7 +299,7 @@ abstract class BaseAudioPlayer internal constructor( val fadeOutPlayer = otherPlayer(); fadeOutPlayer.removeListener(playerListener) val volume = fadeOutPlayer.volume; - var fadeOutDuration = 2500L + var fadeOutDuration = 1500L val fadeOutInterval = 20L while (fadeOutDuration > 0) { fadeOutDuration -= fadeOutInterval @@ -337,7 +339,7 @@ abstract class BaseAudioPlayer internal constructor( } ) .build(); - playerObject.setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus); + playerObject.setAudioAttributes(audioAttributes, false); mediaSessionConnector.setPlayer(playerToUse) mediaSessionConnector.setMediaMetadataProvider { notificationManager.getMediaMetadataCompat() @@ -345,6 +347,7 @@ abstract class BaseAudioPlayer internal constructor( playerObject.playWhenReady = true playerObject.seekToNextMediaItem() playerObject.prepare() + playerObject.volume = 1f } playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt index 210a76c1..6c1bd108 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt @@ -178,8 +178,8 @@ class QueuedAudioPlayer( * Does nothing if there is no previous item to skip to. */ fun previous() { - exoplayer.seekToPreviousMediaItem() - exoplayer2.seekToPreviousMediaItem() + exoPlayer.seekToPreviousMediaItem() + exoPlayer2.seekToPreviousMediaItem() currentPlayer().prepare() } @@ -212,7 +212,7 @@ class QueuedAudioPlayer( */ fun jumpToItem(index: Int) { try { - exoplayer.seekTo(index, C.TIME_UNSET) + exoPlayer.seekTo(index, C.TIME_UNSET) exoPlayer2.seekTo(index, C.TIME_UNSET) currentPlayer().prepare() } catch (e: IllegalSeekPositionException) { From a728f5e387199e73c75b5a543653d6a2688090b2 Mon Sep 17 00:00:00 2001 From: unknown <106490582+lovegaoshi@users.noreply.github.com> Date: Sun, 10 Sep 2023 06:39:42 -0700 Subject: [PATCH 7/8] feat: exoplayer 2 --- .../kotlinaudio/players/BaseAudioPlayer.kt | 43 +++++++++++++++---- .../kotlinaudio/players/QueuedAudioPlayer.kt | 6 +-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 0e9ed9c2..6bd9a414 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -290,7 +290,24 @@ abstract class BaseAudioPlayer internal constructor( playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) } - fun switchExoplayer() { + fun playNext(exoplayer: ExoPlayer) { + exoplayer.seekToNextMediaItem() + } + + fun playPrevious(exoplayer: ExoPlayer) { + exoplayer.seekToPreviousMediaItem() + } + + fun playAtIndex(index: Int, exoplayer: ExoPlayer) { + exoplayer.seekTo(index, C.TIME_UNSET) + } + + fun switchExoplayer( + playerOperation: (exoplayer: ExoPlayer) -> Unit = ::playNext, + fadeDuration: Long = 1500, + fadeInterval: Long = 20, + fadeToVolume: Float = 1f + ){ currentPlayer += 1; if (currentPlayer > 1) { currentPlayer = 0; @@ -298,15 +315,16 @@ abstract class BaseAudioPlayer internal constructor( scope.launch { val fadeOutPlayer = otherPlayer(); fadeOutPlayer.removeListener(playerListener) - val volume = fadeOutPlayer.volume; - var fadeOutDuration = 1500L - val fadeOutInterval = 20L + var fadeOutDuration = fadeDuration + val volumeDiff = -fadeOutPlayer.volume * fadeInterval / fadeOutDuration; while (fadeOutDuration > 0) { - fadeOutDuration -= fadeOutInterval - fadeOutPlayer.volume -= volume * fadeOutInterval / fadeOutDuration - delay(fadeOutInterval) + fadeOutDuration -= fadeInterval + fadeOutPlayer.volume += volumeDiff + delay(fadeInterval) } - fadeOutPlayer.seekToNextMediaItem() + fadeOutPlayer.volume = 0f + fadeOutPlayer.playWhenReady = false + playerOperation(fadeOutPlayer) fadeOutPlayer.pause() } val playerObject = currentPlayer() @@ -347,7 +365,14 @@ abstract class BaseAudioPlayer internal constructor( playerObject.playWhenReady = true playerObject.seekToNextMediaItem() playerObject.prepare() - playerObject.volume = 1f + playerObject.volume = 0f + var fadeInDuration = fadeDuration + val volumeDiff = fadeToVolume * fadeInterval / fadeInDuration; + while (fadeInDuration > 0) { + fadeInDuration -= fadeInterval + playerObject.volume += volumeDiff + delay(fadeInterval) + } } playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt index 6c1bd108..c9ae62e4 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/QueuedAudioPlayer.kt @@ -75,7 +75,7 @@ class QueuedAudioPlayer( queue[currentIndex] = mediaSource exoPlayer.addMediaSource(currentIndex + 1, mediaSource) exoPlayer.removeMediaItem(currentIndex) - exoPlayer2.addMediaSource(currentIndex + 1, mediaSource) + exoPlayer2.addMediaSource(currentIndex + 1, getMediaSourceFromAudioItem(item)) exoPlayer2.removeMediaItem(currentIndex) currentPlayer().prepare() } @@ -99,7 +99,7 @@ class QueuedAudioPlayer( val mediaSource = getMediaSourceFromAudioItem(item) queue.add(mediaSource) exoPlayer.addMediaSource(mediaSource) - exoPlayer2.addMediaSource(mediaSource) + exoPlayer2.addMediaSource(getMediaSourceFromAudioItem(item)) currentPlayer().prepare() } @@ -121,7 +121,7 @@ class QueuedAudioPlayer( val mediaSources = items.map { getMediaSourceFromAudioItem(it) } queue.addAll(mediaSources) exoPlayer.addMediaSources(mediaSources) - exoPlayer2.addMediaSources(mediaSources) + exoPlayer2.addMediaSources(items.map { getMediaSourceFromAudioItem(it) }) currentPlayer().prepare() } From efcaf2b45e6d9c4426053df7310533b321233f7a Mon Sep 17 00:00:00 2001 From: unknown <106490582+lovegaoshi@users.noreply.github.com> Date: Sun, 10 Sep 2023 06:42:02 -0700 Subject: [PATCH 8/8] fix: remove unused imports --- .../com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt index 6bd9a414..f2bf72d7 100644 --- a/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt +++ b/kotlin-audio/src/main/java/com/doublesymmetry/kotlinaudio/players/BaseAudioPlayer.kt @@ -8,7 +8,6 @@ import android.os.Bundle import android.os.ResultReceiver import android.support.v4.media.RatingCompat import android.support.v4.media.session.MediaSessionCompat -import android.util.Log import androidx.annotation.CallSuper import androidx.core.content.ContextCompat import androidx.media.AudioAttributesCompat @@ -79,7 +78,6 @@ import kotlinx.coroutines.launch import timber.log.Timber import java.util.Locale import java.util.concurrent.TimeUnit -import kotlin.math.log abstract class BaseAudioPlayer internal constructor( internal val context: Context,