From 989099c791d767341f80ce66e6ff684a178f71bd Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 27 Jun 2024 16:47:46 -0400 Subject: [PATCH] Refactors PlaybackService --- .../playback/CustomMediaSourceFactory.kt | 63 +++++++++++++++++++ .../playback/MultiPlayerPlaybackManager.kt | 14 ++--- .../service/playback/PlaybackService.kt | 52 +-------------- 3 files changed, 72 insertions(+), 57 deletions(-) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/CustomMediaSourceFactory.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/CustomMediaSourceFactory.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/CustomMediaSourceFactory.kt new file mode 100644 index 000000000..0b79bd061 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/CustomMediaSourceFactory.kt @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.service.playback + +import androidx.media3.common.MediaItem +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.okhttp.OkHttpDataSource +import androidx.media3.exoplayer.drm.DrmSessionManagerProvider +import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy +import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.ammolite.service.HttpClientManager + +/** + * HLS LiveStreams cannot use cache. + */ +@UnstableApi +class CustomMediaSourceFactory : MediaSource.Factory { + private var cachingFactory: MediaSource.Factory = + DefaultMediaSourceFactory(Amethyst.instance.videoCache.get(HttpClientManager.getHttpClient())) + private var nonCachingFactory: MediaSource.Factory = + DefaultMediaSourceFactory(OkHttpDataSource.Factory(HttpClientManager.getHttpClient())) + + override fun setDrmSessionManagerProvider(drmSessionManagerProvider: DrmSessionManagerProvider): MediaSource.Factory { + cachingFactory.setDrmSessionManagerProvider(drmSessionManagerProvider) + nonCachingFactory.setDrmSessionManagerProvider(drmSessionManagerProvider) + return this + } + + override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy): MediaSource.Factory { + cachingFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) + nonCachingFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) + return this + } + + override fun getSupportedTypes(): IntArray = nonCachingFactory.supportedTypes + + override fun createMediaSource(mediaItem: MediaItem): MediaSource { + if (mediaItem.mediaId.contains(".m3u8", true)) { + return nonCachingFactory.createMediaSource(mediaItem) + } + return cachingFactory.createMediaSource(mediaItem) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/MultiPlayerPlaybackManager.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/MultiPlayerPlaybackManager.kt index f87528a12..e1ba0d90a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/MultiPlayerPlaybackManager.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/MultiPlayerPlaybackManager.kt @@ -68,14 +68,13 @@ class MultiPlayerPlaybackManager( private fun getCallbackIntent( callbackUri: String, applicationContext: Context, - ): PendingIntent { - return PendingIntent.getActivity( + ): PendingIntent = + PendingIntent.getActivity( applicationContext, 0, Intent(Intent.ACTION_VIEW, callbackUri.toUri(), applicationContext, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, ) - } @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) fun getMediaSession( @@ -86,8 +85,6 @@ class MultiPlayerPlaybackManager( applicationContext: Context, ): MediaSession { val existingSession = playingMap.get(id) ?: cache.get(id) - // avoids saving positions for live streams otherwise caching goes crazy - val mustCachePositions = !uri.contains(".m3u8", true) if (existingSession != null) return existingSession val player = @@ -111,6 +108,9 @@ class MultiPlayerPlaybackManager( player.addListener( object : Player.Listener { + // avoids saving positions for live streams otherwise caching goes crazy + val mustCachePositions = !uri.contains(".m3u8", true) + override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) { player.setWakeMode(C.WAKE_MODE_NETWORK) @@ -180,7 +180,5 @@ class MultiPlayerPlaybackManager( } } - fun playingContent(): Collection { - return playingMap.values - } + fun playingContent(): Collection = playingMap.values } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackService.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackService.kt index 449055818..0c73a3c99 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackService.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/PlaybackService.kt @@ -23,70 +23,23 @@ package com.vitorpamplona.amethyst.service.playback import android.content.Intent import android.util.Log import androidx.annotation.OptIn -import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi -import androidx.media3.datasource.okhttp.OkHttpDataSource -import androidx.media3.exoplayer.drm.DrmSessionManagerProvider -import androidx.media3.exoplayer.source.DefaultMediaSourceFactory -import androidx.media3.exoplayer.source.MediaSource -import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy import androidx.media3.session.MediaSession import androidx.media3.session.MediaSessionService -import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.ammolite.service.HttpClientManager -/** - * HLS LiveStreams cannot use cache. - */ -@UnstableApi -class CustomMediaSourceFactory() : MediaSource.Factory { - private var cachingFactory: MediaSource.Factory = - DefaultMediaSourceFactory(Amethyst.instance.videoCache.get(HttpClientManager.getHttpClient())) - private var nonCachingFactory: MediaSource.Factory = - DefaultMediaSourceFactory(OkHttpDataSource.Factory(HttpClientManager.getHttpClient())) - - override fun setDrmSessionManagerProvider(drmSessionManagerProvider: DrmSessionManagerProvider): MediaSource.Factory { - cachingFactory.setDrmSessionManagerProvider(drmSessionManagerProvider) - nonCachingFactory.setDrmSessionManagerProvider(drmSessionManagerProvider) - return this - } - - override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy): MediaSource.Factory { - cachingFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) - nonCachingFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) - return this - } - - override fun getSupportedTypes(): IntArray { - return nonCachingFactory.supportedTypes - } - - override fun createMediaSource(mediaItem: MediaItem): MediaSource { - if (mediaItem.mediaId.contains(".m3u8", true)) { - return nonCachingFactory.createMediaSource(mediaItem) - } - return cachingFactory.createMediaSource(mediaItem) - } -} - class PlaybackService : MediaSessionService() { private var videoViewedPositionCache = VideoViewedPositionCache() private var managerAllInOne: MultiPlayerPlaybackManager? = null @OptIn(UnstableApi::class) - fun newAllInOneDataSource(): MediaSource.Factory { - // This might be needed for live kit. - // return WssOrHttpFactory(HttpClientManager.getHttpClient()) - return CustomMediaSourceFactory() - } - fun lazyDS(): MultiPlayerPlaybackManager { managerAllInOne?.let { return it } - val newInstance = MultiPlayerPlaybackManager(newAllInOneDataSource(), videoViewedPositionCache) + val newInstance = MultiPlayerPlaybackManager(CustomMediaSourceFactory(), videoViewedPositionCache) managerAllInOne = newInstance return newInstance } @@ -102,10 +55,11 @@ class PlaybackService : MediaSessionService() { HttpClientManager.proxyChangeListeners.add(this@PlaybackService::onProxyUpdated) } + @OptIn(UnstableApi::class) private fun onProxyUpdated() { val toDestroyAllInOne = managerAllInOne - managerAllInOne = MultiPlayerPlaybackManager(newAllInOneDataSource(), videoViewedPositionCache) + managerAllInOne = MultiPlayerPlaybackManager(CustomMediaSourceFactory(), videoViewedPositionCache) toDestroyAllInOne?.releaseAppPlayers() }