mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Calculate track indices for mpv
This commit is contained in:
parent
3a4a22ec94
commit
0597e93178
2 changed files with 127 additions and 21 deletions
|
|
@ -424,12 +424,14 @@ class PlaybackViewModel
|
|||
enableDirectStream: Boolean = true,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
val itemId = item.id
|
||||
val playerBackend = preferences.appPreferences.playbackPreferences.playerBackend
|
||||
|
||||
val currentPlayback = this@PlaybackViewModel.currentPlayback.value
|
||||
if (currentPlayback != null && currentPlayback.playMethod == PlayMethod.DIRECT_PLAY) {
|
||||
// If direct playing, can try to switch tracks without playback restarting
|
||||
// Except for external subtitles
|
||||
// TODO there's probably no reason why we can't add external subtitles?
|
||||
Timber.v("changeStreams direct play")
|
||||
|
||||
val source = currentPlayback.mediaSourceInfo
|
||||
val externalSubtitleCount = source.externalSubtitlesCount
|
||||
|
|
@ -439,14 +441,18 @@ class PlaybackViewModel
|
|||
withContext(Dispatchers.Main) {
|
||||
applyTrackSelections(
|
||||
player,
|
||||
playerBackend,
|
||||
true,
|
||||
audioIndex,
|
||||
subtitleIndex,
|
||||
source.videoStreamCount,
|
||||
source.audioStreamCount,
|
||||
externalSubtitleCount,
|
||||
externalSubtitle != null,
|
||||
)
|
||||
}
|
||||
if (result.bothSelected) {
|
||||
// TODO lots of duplicate code in this block
|
||||
Timber.d("Changes tracks audio=$audioIndex, subtitle=$subtitleIndex")
|
||||
val itemPlayback =
|
||||
currentItemPlayback.copy(
|
||||
|
|
@ -489,7 +495,6 @@ class PlaybackViewModel
|
|||
"changeStreams: userInitiated=$userInitiated, audioIndex=$audioIndex, subtitleIndex=$subtitleIndex, " +
|
||||
"enableDirectPlay=$enableDirectPlay, enableDirectStream=$enableDirectStream",
|
||||
)
|
||||
val playerBackend = preferences.appPreferences.playbackPreferences.playerBackend
|
||||
// TODO if the new audio or subtitle index is already in the streams (eg direct play), should toggle in the player instead
|
||||
val maxBitrate =
|
||||
preferences.appPreferences.playbackPreferences.maxBitrate
|
||||
|
|
@ -618,15 +623,16 @@ class PlaybackViewModel
|
|||
player.removeListener(it)
|
||||
}
|
||||
|
||||
val activityListener =
|
||||
TrackActivityPlaybackListener(
|
||||
api = api,
|
||||
player = player,
|
||||
playback = playback,
|
||||
itemPlayback = itemPlayback,
|
||||
)
|
||||
player.addListener(activityListener)
|
||||
this@PlaybackViewModel.activityListener = activityListener
|
||||
// TODO temporarily disable
|
||||
// val activityListener =
|
||||
// TrackActivityPlaybackListener(
|
||||
// api = api,
|
||||
// player = player,
|
||||
// playback = playback,
|
||||
// itemPlayback = itemPlayback,
|
||||
// )
|
||||
// player.addListener(activityListener)
|
||||
// this@PlaybackViewModel.activityListener = activityListener
|
||||
|
||||
duration.value = source.runTimeTicks?.ticks
|
||||
loading.value = LoadingState.Success
|
||||
|
|
@ -644,9 +650,12 @@ class PlaybackViewModel
|
|||
if (tracks.groups.isNotEmpty()) {
|
||||
applyTrackSelections(
|
||||
player,
|
||||
playerBackend,
|
||||
source.supportsDirectPlay,
|
||||
audioIndex,
|
||||
subtitleIndex,
|
||||
source.videoStreamCount,
|
||||
source.audioStreamCount,
|
||||
externalSubtitleCount,
|
||||
externalSubtitle != null,
|
||||
)
|
||||
|
|
@ -1143,6 +1152,22 @@ val MediaSourceInfo.externalSubtitlesCount: Int
|
|||
mediaStreams
|
||||
?.count { it.type == MediaStreamType.SUBTITLE && it.isExternal } ?: 0
|
||||
|
||||
/**
|
||||
* Returns the number of video streams there are
|
||||
*/
|
||||
val MediaSourceInfo.videoStreamCount: Int
|
||||
get() =
|
||||
mediaStreams
|
||||
?.count { it.type == MediaStreamType.VIDEO } ?: 0
|
||||
|
||||
/**
|
||||
* Returns the number of audio streams there are
|
||||
*/
|
||||
val MediaSourceInfo.audioStreamCount: Int
|
||||
get() =
|
||||
mediaStreams
|
||||
?.count { it.type == MediaStreamType.AUDIO } ?: 0
|
||||
|
||||
/**
|
||||
* Returns the [MediaStream] for the given subtitle index iff it is external
|
||||
*/
|
||||
|
|
@ -1164,9 +1189,12 @@ data class TrackSelectionResult(
|
|||
@OptIn(UnstableApi::class)
|
||||
private fun applyTrackSelections(
|
||||
player: Player,
|
||||
playerBackend: PlayerBackend,
|
||||
supportsDirectPlay: Boolean,
|
||||
audioIndex: Int?,
|
||||
subtitleIndex: Int?,
|
||||
videoStreamCount: Int,
|
||||
audioStreamCount: Int,
|
||||
externalSubtitleCount: Int,
|
||||
subtitleIsExternal: Boolean,
|
||||
): TrackSelectionResult {
|
||||
|
|
@ -1182,7 +1210,18 @@ private fun applyTrackSelections(
|
|||
}.any { it.endsWith("e:$subtitleIndex") }
|
||||
}
|
||||
} else {
|
||||
val indexToFind = subtitleIndex - externalSubtitleCount + 1
|
||||
// TODO this might be incorrect
|
||||
val indexToFind =
|
||||
calculateIndexToFind(
|
||||
subtitleIndex,
|
||||
MediaStreamType.SUBTITLE,
|
||||
playerBackend,
|
||||
videoStreamCount,
|
||||
audioStreamCount,
|
||||
externalSubtitleCount,
|
||||
)
|
||||
Timber.v("Chosen subtitle ($subtitleIndex/$indexToFind) track")
|
||||
// subtitleIndex - externalSubtitleCount + 1
|
||||
player.currentTracks.groups.firstOrNull { group ->
|
||||
group.type == C.TRACK_TYPE_TEXT && group.isSupported &&
|
||||
(0..<group.mediaTrackGroup.length)
|
||||
|
|
@ -1217,7 +1256,14 @@ private fun applyTrackSelections(
|
|||
val audioSelected =
|
||||
if (audioIndex != null && supportsDirectPlay) {
|
||||
val indexToFind =
|
||||
audioIndex - externalSubtitleCount + 1
|
||||
calculateIndexToFind(
|
||||
audioIndex,
|
||||
MediaStreamType.AUDIO,
|
||||
playerBackend,
|
||||
videoStreamCount,
|
||||
audioStreamCount,
|
||||
externalSubtitleCount,
|
||||
)
|
||||
val chosenTrack =
|
||||
player.currentTracks.groups.firstOrNull { group ->
|
||||
group.type == C.TRACK_TYPE_AUDIO && group.isSupported &&
|
||||
|
|
@ -1245,3 +1291,32 @@ private fun applyTrackSelections(
|
|||
}
|
||||
return TrackSelectionResult(audioSelected, subtitleSelected)
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the server provided index to the track index based on the [PlayerBackend] and other stream information
|
||||
*/
|
||||
private fun calculateIndexToFind(
|
||||
serverIndex: Int,
|
||||
type: MediaStreamType,
|
||||
playerBackend: PlayerBackend,
|
||||
videoStreamCount: Int,
|
||||
audioStreamCount: Int,
|
||||
externalSubtitleCount: Int,
|
||||
): Int =
|
||||
when (playerBackend) {
|
||||
PlayerBackend.EXO_PLAYER,
|
||||
PlayerBackend.UNRECOGNIZED,
|
||||
-> {
|
||||
serverIndex - externalSubtitleCount + 1
|
||||
}
|
||||
|
||||
// TODO MPV could use literal indexes because they are stored in the track format ID
|
||||
PlayerBackend.MPV -> {
|
||||
when (type) {
|
||||
MediaStreamType.VIDEO -> serverIndex - externalSubtitleCount + 1
|
||||
MediaStreamType.AUDIO -> serverIndex - externalSubtitleCount - videoStreamCount + 1
|
||||
MediaStreamType.SUBTITLE -> serverIndex - externalSubtitleCount - videoStreamCount - audioStreamCount + 1
|
||||
else -> throw UnsupportedOperationException("Cannot calculate index for $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,11 @@ import androidx.media3.common.util.Util
|
|||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelector
|
||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
|
||||
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_AUDIO_RECONFIG
|
||||
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_END_FILE
|
||||
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_FILE_LOADED
|
||||
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_PLAYBACK_RESTART
|
||||
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_VIDEO_RECONFIG
|
||||
import timber.log.Timber
|
||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
|
@ -295,7 +298,7 @@ class MpvPlayer(
|
|||
}
|
||||
|
||||
override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
|
||||
if (DEBUG) Timber.v("setTrackSelectionParameters")
|
||||
Timber.v("TrackSelection: setTrackSelectionParameters %s", parameters)
|
||||
val tracks = getTracks()
|
||||
if (C.TRACK_TYPE_TEXT in parameters.disabledTrackTypes) {
|
||||
// Subtitles disabled
|
||||
|
|
@ -522,14 +525,14 @@ class MpvPlayer(
|
|||
}
|
||||
|
||||
override fun eventProperty(property: String) {
|
||||
Timber.v("eventProperty: $property")
|
||||
if (DEBUG) Timber.v("eventProperty: $property")
|
||||
}
|
||||
|
||||
override fun eventProperty(
|
||||
property: String,
|
||||
value: Long,
|
||||
) {
|
||||
Timber.v("eventPropertyLong: $property=$value")
|
||||
if (DEBUG) Timber.v("eventPropertyLong: $property=$value")
|
||||
when (property) {
|
||||
MPVProperty.POSITION -> positionMs = value.seconds.inWholeMilliseconds
|
||||
}
|
||||
|
|
@ -539,7 +542,7 @@ class MpvPlayer(
|
|||
property: String,
|
||||
value: Boolean,
|
||||
) {
|
||||
Timber.v("eventPropertyBoolean: $property=$value")
|
||||
if (DEBUG) Timber.v("eventPropertyBoolean: $property=$value")
|
||||
when (property) {
|
||||
MPVProperty.PAUSED -> {
|
||||
isPaused = value
|
||||
|
|
@ -552,7 +555,7 @@ class MpvPlayer(
|
|||
property: String,
|
||||
value: String,
|
||||
) {
|
||||
Timber.v("eventPropertyString: $property=$value")
|
||||
if (DEBUG) Timber.v("eventPropertyString: $property=$value")
|
||||
}
|
||||
|
||||
override fun eventProperty(
|
||||
|
|
@ -566,20 +569,48 @@ class MpvPlayer(
|
|||
}
|
||||
|
||||
override fun event(eventId: Int) {
|
||||
Timber.v("event: $eventId")
|
||||
when (eventId) {
|
||||
MPV_EVENT_FILE_LOADED -> {
|
||||
Timber.d("Seeking to $startPositionMs")
|
||||
Timber.d("event: MPV_EVENT_FILE_LOADED")
|
||||
if (startPositionMs > 0) {
|
||||
Timber.d("Seeking to $startPositionMs")
|
||||
seekTo(startPositionMs)
|
||||
}
|
||||
getTracks().let {
|
||||
notifyListeners(EVENT_TRACKS_CHANGED) { onTracksChanged(it) }
|
||||
}
|
||||
}
|
||||
|
||||
MPV_EVENT_PLAYBACK_RESTART -> {
|
||||
Timber.d("event: MPV_EVENT_PLAYBACK_RESTART")
|
||||
}
|
||||
|
||||
MPV_EVENT_AUDIO_RECONFIG -> {
|
||||
Timber.d("event: MPV_EVENT_AUDIO_RECONFIG")
|
||||
getTracks().let {
|
||||
notifyListeners(EVENT_TRACKS_CHANGED) { onTracksChanged(it) }
|
||||
}
|
||||
}
|
||||
|
||||
MPV_EVENT_VIDEO_RECONFIG -> {
|
||||
Timber.d("event: MPV_EVENT_VIDEO_RECONFIG")
|
||||
getTracks().let {
|
||||
notifyListeners(EVENT_TRACKS_CHANGED) { onTracksChanged(it) }
|
||||
}
|
||||
}
|
||||
|
||||
MPV_EVENT_END_FILE -> {
|
||||
playbackState = STATE_ENDED
|
||||
notifyListeners(EVENT_PLAYBACK_STATE_CHANGED) { onPlaybackStateChanged(STATE_ENDED) }
|
||||
Timber.d("event: MPV_EVENT_END_FILE")
|
||||
if (positionMs >= durationMs) {
|
||||
playbackState = STATE_ENDED
|
||||
notifyListeners(EVENT_PLAYBACK_STATE_CHANGED) {
|
||||
onPlaybackStateChanged(STATE_ENDED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Timber.v("event: $eventId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue