mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Update ExoPlayer with better audio configuration (#1258)
## Description Makes some changes when building ExoPlayer to help improve pass through audio. The major changes are enabled constant bitrate seeking and audio offloading. Documentation is thin on these features, but [this blog post](https://www.qualcomm.com/developer/blog/2023/08/audio-offload-support-exoplayer) while focused on ExoPlayer2 and power usage, does explain a bit about audio offloading. Small section on constant bitrate seeking: https://developer.android.com/media/media3/exoplayer/customization#enabling-constant-bitrate-seeking ### Related issues N/A ### Testing Emulator, nvidia shield w/ AVR to check pass through ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
790069d818
commit
08836e3c5d
2 changed files with 77 additions and 26 deletions
|
|
@ -5,20 +5,14 @@ import androidx.annotation.OptIn
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.Timeline
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||
import androidx.media3.session.MediaSession
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.AudioItem
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
|
||||
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
|
||||
|
|
@ -44,7 +38,6 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.instantMixApi
|
||||
import org.jellyfin.sdk.api.client.extensions.universalAudioApi
|
||||
|
|
@ -74,9 +67,9 @@ class MusicService
|
|||
@Inject
|
||||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
@param:AuthOkHttpClient private val authOkHttpClient: OkHttpClient,
|
||||
@param:DefaultCoroutineScope private val defaultScope: CoroutineScope,
|
||||
private val api: ApiClient,
|
||||
private val playerFactory: PlayerFactory,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val imageUrlService: ImageUrlService,
|
||||
) {
|
||||
|
|
@ -86,21 +79,9 @@ class MusicService
|
|||
private val audioFormats by lazy { listOf(*supportedAudioCodecs) }
|
||||
|
||||
val player: Player by lazy {
|
||||
ExoPlayer
|
||||
.Builder(context)
|
||||
.setMediaSourceFactory(
|
||||
DefaultMediaSourceFactory(
|
||||
OkHttpDataSource.Factory(authOkHttpClient),
|
||||
),
|
||||
).build()
|
||||
playerFactory
|
||||
.createAudioPlayer()
|
||||
.also {
|
||||
it.setAudioAttributes(
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||
.build(),
|
||||
false,
|
||||
)
|
||||
it.addListener(MusicPlayerListener(it, _state))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,29 @@ import android.content.Context
|
|||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences
|
||||
import androidx.media3.common.util.ExperimentalApi
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.datasource.DefaultDataSource
|
||||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.Renderer
|
||||
import androidx.media3.exoplayer.RenderersFactory
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer
|
||||
import androidx.media3.exoplayer.video.VideoRendererEventListener
|
||||
import androidx.media3.extractor.DefaultExtractorsFactory
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.AssPlaybackMode
|
||||
import com.github.damontecres.wholphin.preferences.MediaExtensionStatus
|
||||
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
|
||||
import com.github.damontecres.wholphin.preferences.PlayerBackend
|
||||
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
|
||||
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.peerless2012.ass.media.AssHandler
|
||||
|
|
@ -35,6 +38,7 @@ import io.github.peerless2012.ass.media.parser.AssSubtitleParserFactory
|
|||
import io.github.peerless2012.ass.media.type.AssRenderType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.Constructor
|
||||
import javax.inject.Inject
|
||||
|
|
@ -48,7 +52,7 @@ class PlayerFactory
|
|||
@Inject
|
||||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
private val appPreferences: DataStore<AppPreferences>,
|
||||
@param:AuthOkHttpClient private val authOkHttpClient: OkHttpClient,
|
||||
) {
|
||||
@Volatile
|
||||
var currentPlayer: Player? = null
|
||||
|
|
@ -95,7 +99,7 @@ class PlayerFactory
|
|||
else -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
|
||||
}
|
||||
val dataSourceFactory = DefaultDataSource.Factory(context)
|
||||
val extractorsFactory = DefaultExtractorsFactory()
|
||||
val extractorsFactory = createExtractorsFactory()
|
||||
var renderersFactory: RenderersFactory =
|
||||
WholphinRenderersFactory(context, decodeAv1)
|
||||
.setEnableDecoderFallback(true)
|
||||
|
|
@ -118,13 +122,25 @@ class PlayerFactory
|
|||
extractorsFactory,
|
||||
)
|
||||
}
|
||||
val trackSelector = createTrackSelector()
|
||||
|
||||
ExoPlayer
|
||||
.Builder(context)
|
||||
.setMediaSourceFactory(mediaSourceFactory)
|
||||
.setRenderersFactory(renderersFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.build()
|
||||
.apply {
|
||||
assHandler?.init(this)
|
||||
withContext(Dispatchers.Main) {
|
||||
setAudioAttributes(
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
|
||||
.build(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +151,60 @@ class PlayerFactory
|
|||
currentPlayer = newPlayer
|
||||
return PlayerCreation(newPlayer, assHandler)
|
||||
}
|
||||
|
||||
fun createAudioPlayer(extensions: MediaExtensionStatus = MediaExtensionStatus.MES_FALLBACK): ExoPlayer {
|
||||
val rendererMode =
|
||||
when (extensions) {
|
||||
MediaExtensionStatus.MES_FALLBACK -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
|
||||
MediaExtensionStatus.MES_PREFERRED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
||||
MediaExtensionStatus.MES_DISABLED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF
|
||||
else -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
|
||||
}
|
||||
val extractorsFactory = createExtractorsFactory()
|
||||
val renderersFactory: RenderersFactory =
|
||||
WholphinRenderersFactory(context, false)
|
||||
.setEnableDecoderFallback(true)
|
||||
.setExtensionRendererMode(rendererMode)
|
||||
val mediaSourceFactory =
|
||||
DefaultMediaSourceFactory(
|
||||
OkHttpDataSource.Factory(authOkHttpClient),
|
||||
extractorsFactory,
|
||||
)
|
||||
val trackSelector = createTrackSelector()
|
||||
return ExoPlayer
|
||||
.Builder(context)
|
||||
.setMediaSourceFactory(mediaSourceFactory)
|
||||
.setRenderersFactory(renderersFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.build()
|
||||
.also {
|
||||
it.setAudioAttributes(
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||
.build(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createExtractorsFactory() =
|
||||
DefaultExtractorsFactory()
|
||||
.setConstantBitrateSeekingEnabled(true)
|
||||
.setConstantBitrateSeekingAlwaysEnabled(true)
|
||||
|
||||
private fun createTrackSelector() =
|
||||
DefaultTrackSelector(context).apply {
|
||||
setParameters(
|
||||
buildUponParameters()
|
||||
.setAudioOffloadPreferences(
|
||||
AudioOffloadPreferences
|
||||
.Builder()
|
||||
.setAudioOffloadMode(AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED)
|
||||
.build(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val Player.isReleased: Boolean
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue