Upgrade ExoPlayer to 2.18 (#126)
* Upgrade ExoPlayer to 2.18 * Change the position polling from every 2 to every 5 sec * Make internalMediaItems non-nullable * Clean up mpv track names * Reduce explayer ffmpeg extension size by not including all decoders Removed decoders vorbis, opus, flac, pcm_mulaw, pcm_alaw, mp3, aac because these are already supported by Android. * Clean up preferredLanguage preferences
This commit is contained in:
parent
6f0d5a13a8
commit
bcdada538d
7 changed files with 70 additions and 197 deletions
|
@ -25,7 +25,7 @@ Home | Library | Movie | Season | Episode
|
||||||
- ExoPlayer
|
- ExoPlayer
|
||||||
- Video codecs: H.263, H.264, H.265, VP8, VP9, AV1
|
- Video codecs: H.263, H.264, H.265, VP8, VP9, AV1
|
||||||
- Support depends on Android device
|
- Support depends on Android device
|
||||||
- Audio codecs: Vorbis, Opus, FLAC, ALAC, PCM µ-law, PCM A-law, MP1, MP2, MP3, AMR-NB, AMR-WB, AAC, AC-3, E-AC-3, DTS, DTS-HD, TrueHD
|
- Audio codecs: Vorbis, Opus, FLAC, ALAC, PCM, MP3, AMR-NB, AMR-WB, AAC, AC-3, E-AC-3, DTS, DTS-HD, TrueHD
|
||||||
- Support provided by ExoPlayer FFmpeg extension
|
- Support provided by ExoPlayer FFmpeg extension
|
||||||
- Subtitle codecs: SRT, VTT, SSA/ASS, PGSSUB
|
- Subtitle codecs: SRT, VTT, SSA/ASS, PGSSUB
|
||||||
- SSA/ASS has limited styling support see [this issue](https://github.com/google/ExoPlayer/issues/8435)
|
- SSA/ASS has limited styling support see [this issue](https://github.com/google/ExoPlayer/issues/8435)
|
||||||
|
|
|
@ -109,7 +109,7 @@ dependencies {
|
||||||
kapt("com.google.dagger:hilt-compiler:$hiltVersion")
|
kapt("com.google.dagger:hilt-compiler:$hiltVersion")
|
||||||
|
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
val exoplayerVersion = "2.17.1"
|
val exoplayerVersion = "2.18.0"
|
||||||
implementation("com.google.android.exoplayer:exoplayer-core:$exoplayerVersion")
|
implementation("com.google.android.exoplayer:exoplayer-core:$exoplayerVersion")
|
||||||
implementation("com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion")
|
implementation("com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion")
|
||||||
implementation(files("libs/extension-ffmpeg-release.aar"))
|
implementation(files("libs/extension-ffmpeg-release.aar"))
|
||||||
|
|
Binary file not shown.
|
@ -103,8 +103,10 @@ class PlayerActivity : BasePlayerActivity() {
|
||||||
if (audioRenderer == null) return@setOnClickListener
|
if (audioRenderer == null) return@setOnClickListener
|
||||||
|
|
||||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||||
this, resources.getString(R.string.select_audio_track),
|
this,
|
||||||
viewModel.trackSelector, audioRenderer
|
resources.getString(R.string.select_audio_track),
|
||||||
|
viewModel.player,
|
||||||
|
C.TRACK_TYPE_AUDIO
|
||||||
)
|
)
|
||||||
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
||||||
trackSelectionDialog.show()
|
trackSelectionDialog.show()
|
||||||
|
@ -134,8 +136,10 @@ class PlayerActivity : BasePlayerActivity() {
|
||||||
if (subtitleRenderer == null) return@setOnClickListener
|
if (subtitleRenderer == null) return@setOnClickListener
|
||||||
|
|
||||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||||
this, resources.getString(R.string.select_subtile_track),
|
this,
|
||||||
viewModel.trackSelector, subtitleRenderer
|
resources.getString(R.string.select_subtile_track),
|
||||||
|
viewModel.player,
|
||||||
|
C.TRACK_TYPE_TEXT
|
||||||
)
|
)
|
||||||
trackSelectionDialogBuilder.setShowDisableOption(true)
|
trackSelectionDialogBuilder.setShowDisableOption(true)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
|
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||||
import dev.jdtech.jellyfin.mpv.TrackType
|
import dev.jdtech.jellyfin.mpv.TrackType
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
@ -14,21 +15,13 @@ class TrackSelectionDialogFragment(
|
||||||
private val viewModel: PlayerActivityViewModel
|
private val viewModel: PlayerActivityViewModel
|
||||||
) : DialogFragment() {
|
) : DialogFragment() {
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val trackNames: List<String>
|
|
||||||
when (type) {
|
when (type) {
|
||||||
TrackType.AUDIO -> {
|
TrackType.AUDIO -> {
|
||||||
trackNames = viewModel.currentAudioTracks.map { track ->
|
|
||||||
val nameParts: MutableList<String> = mutableListOf()
|
|
||||||
if (track.title.isNotEmpty()) nameParts.add(track.title)
|
|
||||||
if (track.lang.isNotEmpty()) nameParts.add(track.lang)
|
|
||||||
if (track.codec.isNotEmpty()) nameParts.add(track.codec)
|
|
||||||
nameParts.joinToString(separator = " - ")
|
|
||||||
}
|
|
||||||
return activity?.let { activity ->
|
return activity?.let { activity ->
|
||||||
val builder = MaterialAlertDialogBuilder(activity)
|
val builder = MaterialAlertDialogBuilder(activity)
|
||||||
builder.setTitle(getString(R.string.select_audio_track))
|
builder.setTitle(getString(R.string.select_audio_track))
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
trackNames.toTypedArray(),
|
getTrackNames(viewModel.currentAudioTracks),
|
||||||
viewModel.currentAudioTracks.indexOfFirst { it.selected }) { dialog, which ->
|
viewModel.currentAudioTracks.indexOfFirst { it.selected }) { dialog, which ->
|
||||||
viewModel.switchToTrack(
|
viewModel.switchToTrack(
|
||||||
TrackType.AUDIO,
|
TrackType.AUDIO,
|
||||||
|
@ -40,18 +33,11 @@ class TrackSelectionDialogFragment(
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
TrackType.SUBTITLE -> {
|
TrackType.SUBTITLE -> {
|
||||||
trackNames = viewModel.currentSubtitleTracks.map { track ->
|
|
||||||
val nameParts: MutableList<String> = mutableListOf()
|
|
||||||
if (track.title.isNotEmpty()) nameParts.add(track.title)
|
|
||||||
if (track.lang.isNotEmpty()) nameParts.add(track.lang)
|
|
||||||
if (track.codec.isNotEmpty()) nameParts.add(track.codec)
|
|
||||||
nameParts.joinToString(separator = " - ")
|
|
||||||
}
|
|
||||||
return activity?.let { activity ->
|
return activity?.let { activity ->
|
||||||
val builder = MaterialAlertDialogBuilder(activity)
|
val builder = MaterialAlertDialogBuilder(activity)
|
||||||
builder.setTitle(getString(R.string.select_subtile_track))
|
builder.setTitle(getString(R.string.select_subtile_track))
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
trackNames.toTypedArray(),
|
getTrackNames(viewModel.currentSubtitleTracks),
|
||||||
viewModel.currentSubtitleTracks.indexOfFirst { if (viewModel.disableSubtitle) it.ffIndex == -1 else it.selected }) { dialog, which ->
|
viewModel.currentSubtitleTracks.indexOfFirst { if (viewModel.disableSubtitle) it.ffIndex == -1 else it.selected }) { dialog, which ->
|
||||||
viewModel.switchToTrack(
|
viewModel.switchToTrack(
|
||||||
TrackType.SUBTITLE,
|
TrackType.SUBTITLE,
|
||||||
|
@ -67,4 +53,14 @@ class TrackSelectionDialogFragment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getTrackNames(tracks: List<MPVPlayer.Companion.Track>): Array<String> {
|
||||||
|
return tracks.map { track ->
|
||||||
|
val nameParts: MutableList<String> = mutableListOf()
|
||||||
|
if (track.title.isNotEmpty()) nameParts.add(track.title)
|
||||||
|
if (track.lang.isNotEmpty()) nameParts.add(track.lang)
|
||||||
|
if (track.codec.isNotEmpty()) nameParts.add(track.codec)
|
||||||
|
nameParts.joinToString(separator = " - ")
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,14 +17,10 @@ import androidx.core.content.getSystemService
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.*
|
||||||
import com.google.android.exoplayer2.Player.Commands
|
import com.google.android.exoplayer2.Player.Commands
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
|
||||||
import com.google.android.exoplayer2.source.TrackGroup
|
import com.google.android.exoplayer2.source.TrackGroup
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray
|
|
||||||
import com.google.android.exoplayer2.text.Cue
|
import com.google.android.exoplayer2.text.Cue
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
|
import com.google.android.exoplayer2.text.CueGroup
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters
|
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters
|
||||||
import com.google.android.exoplayer2.upstream.DataSource
|
|
||||||
import com.google.android.exoplayer2.util.*
|
import com.google.android.exoplayer2.util.*
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import com.google.android.exoplayer2.video.VideoSize
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
@ -153,7 +149,7 @@ class MPVPlayer(
|
||||||
CopyOnWriteArraySet<Player.Listener>()
|
CopyOnWriteArraySet<Player.Listener>()
|
||||||
|
|
||||||
// Internal state.
|
// Internal state.
|
||||||
private var internalMediaItems: List<MediaItem>? = null
|
private var internalMediaItems: List<MediaItem> = emptyList()
|
||||||
|
|
||||||
@Player.State
|
@Player.State
|
||||||
private var playbackState: Int = Player.STATE_IDLE
|
private var playbackState: Int = Player.STATE_IDLE
|
||||||
|
@ -161,7 +157,7 @@ class MPVPlayer(
|
||||||
|
|
||||||
@Player.RepeatMode
|
@Player.RepeatMode
|
||||||
private val repeatMode: Int = REPEAT_MODE_OFF
|
private val repeatMode: Int = REPEAT_MODE_OFF
|
||||||
private var tracksInfo: TracksInfo = TracksInfo.EMPTY
|
private var tracks: Tracks = Tracks.EMPTY
|
||||||
private var playbackParameters: PlaybackParameters = PlaybackParameters.DEFAULT
|
private var playbackParameters: PlaybackParameters = PlaybackParameters.DEFAULT
|
||||||
|
|
||||||
// MPV Custom
|
// MPV Custom
|
||||||
|
@ -170,7 +166,7 @@ class MPVPlayer(
|
||||||
private var currentPositionMs: Long? = null
|
private var currentPositionMs: Long? = null
|
||||||
private var currentDurationMs: Long? = null
|
private var currentDurationMs: Long? = null
|
||||||
private var currentCacheDurationMs: Long? = null
|
private var currentCacheDurationMs: Long? = null
|
||||||
var currentTracks: List<Track> = emptyList()
|
var currentMpvTracks: List<Track> = emptyList()
|
||||||
private var initialCommands = mutableListOf<Array<String>>()
|
private var initialCommands = mutableListOf<Array<String>>()
|
||||||
private var initialSeekTo: Long = 0L
|
private var initialSeekTo: Long = 0L
|
||||||
|
|
||||||
|
@ -183,18 +179,18 @@ class MPVPlayer(
|
||||||
handler.post {
|
handler.post {
|
||||||
when (property) {
|
when (property) {
|
||||||
"track-list" -> {
|
"track-list" -> {
|
||||||
val (tracks, newTracksInfo) = getMPVTracks(value)
|
val (mpvTracks, newTracks) = getMPVTracks(value)
|
||||||
tracks.forEach { Log.i("mpv", "${it.ffIndex} ${it.type} ${it.codec}") }
|
mpvTracks.forEach { Log.i("mpv", "${it.ffIndex} ${it.type} ${it.codec}") }
|
||||||
currentTracks = tracks
|
currentMpvTracks = mpvTracks
|
||||||
if (isPlayerReady) {
|
if (isPlayerReady) {
|
||||||
if (newTracksInfo != tracksInfo) {
|
if (newTracks != tracks) {
|
||||||
tracksInfo = newTracksInfo
|
tracks = newTracks
|
||||||
listeners.sendEvent(Player.EVENT_TRACKS_CHANGED) { listener ->
|
listeners.sendEvent(Player.EVENT_TRACKS_CHANGED) { listener ->
|
||||||
listener.onTracksInfoChanged(currentTracksInfo)
|
listener.onTracksChanged(currentTracks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracksInfo = newTracksInfo
|
tracks = newTracks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +202,7 @@ class MPVPlayer(
|
||||||
when (property) {
|
when (property) {
|
||||||
"eof-reached" -> {
|
"eof-reached" -> {
|
||||||
if (value && isPlayerReady) {
|
if (value && isPlayerReady) {
|
||||||
if (currentIndex < (internalMediaItems?.size ?: 0)) {
|
if (currentIndex < (internalMediaItems.size)) {
|
||||||
currentIndex += 1
|
currentIndex += 1
|
||||||
prepareMediaItem(currentIndex)
|
prepareMediaItem(currentIndex)
|
||||||
play()
|
play()
|
||||||
|
@ -299,7 +295,7 @@ class MPVPlayer(
|
||||||
if (!isPlayerReady) {
|
if (!isPlayerReady) {
|
||||||
isPlayerReady = true
|
isPlayerReady = true
|
||||||
listeners.sendEvent(Player.EVENT_TRACKS_CHANGED) { listener ->
|
listeners.sendEvent(Player.EVENT_TRACKS_CHANGED) { listener ->
|
||||||
listener.onTracksInfoChanged(currentTracksInfo)
|
listener.onTracksChanged(currentTracks)
|
||||||
}
|
}
|
||||||
seekTo(C.TIME_UNSET)
|
seekTo(C.TIME_UNSET)
|
||||||
if (playWhenReady) {
|
if (playWhenReady) {
|
||||||
|
@ -372,8 +368,8 @@ class MPVPlayer(
|
||||||
index: Int
|
index: Int
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (index != C.INDEX_UNSET) {
|
if (index != C.INDEX_UNSET) {
|
||||||
Log.i("mpv", "${currentTracks.size}")
|
Log.i("mpv", "${currentMpvTracks.size}")
|
||||||
currentTracks.firstOrNull {
|
currentMpvTracks.firstOrNull {
|
||||||
it.type == trackType && (if (isExternal) it.title else "${it.ffIndex}") == "$index"
|
it.type == trackType && (if (isExternal) it.title else "${it.ffIndex}") == "$index"
|
||||||
}.let { track ->
|
}.let { track ->
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
|
@ -386,7 +382,7 @@ class MPVPlayer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentTracks.indexOfFirst { it.type == trackType && it.selected } != C.INDEX_UNSET) {
|
if (currentMpvTracks.indexOfFirst { it.type == trackType && it.selected } != C.INDEX_UNSET) {
|
||||||
MPVLib.setPropertyString(trackType, "no")
|
MPVLib.setPropertyString(trackType, "no")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +395,7 @@ class MPVPlayer(
|
||||||
* Returns the number of windows in the timeline.
|
* Returns the number of windows in the timeline.
|
||||||
*/
|
*/
|
||||||
override fun getWindowCount(): Int {
|
override fun getWindowCount(): Int {
|
||||||
return internalMediaItems?.size ?: 0
|
return internalMediaItems.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -417,7 +413,7 @@ class MPVPlayer(
|
||||||
defaultPositionProjectionUs: Long
|
defaultPositionProjectionUs: Long
|
||||||
): Window {
|
): Window {
|
||||||
val currentMediaItem =
|
val currentMediaItem =
|
||||||
internalMediaItems?.get(windowIndex) ?: MediaItem.Builder().build()
|
internalMediaItems.getOrNull(windowIndex) ?: MediaItem.Builder().build()
|
||||||
return window.set(
|
return window.set(
|
||||||
/* uid= */ windowIndex,
|
/* uid= */ windowIndex,
|
||||||
/* mediaItem= */ currentMediaItem,
|
/* mediaItem= */ currentMediaItem,
|
||||||
|
@ -440,7 +436,7 @@ class MPVPlayer(
|
||||||
* Returns the number of periods in the timeline.
|
* Returns the number of periods in the timeline.
|
||||||
*/
|
*/
|
||||||
override fun getPeriodCount(): Int {
|
override fun getPeriodCount(): Int {
|
||||||
return internalMediaItems?.size ?: 0
|
return internalMediaItems.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -678,7 +674,7 @@ class MPVPlayer(
|
||||||
currentPositionMs = null
|
currentPositionMs = null
|
||||||
currentDurationMs = null
|
currentDurationMs = null
|
||||||
currentCacheDurationMs = null
|
currentCacheDurationMs = null
|
||||||
tracksInfo = TracksInfo.EMPTY
|
tracks = Tracks.EMPTY
|
||||||
playbackParameters = PlaybackParameters.DEFAULT
|
playbackParameters = PlaybackParameters.DEFAULT
|
||||||
initialCommands.clear()
|
initialCommands.clear()
|
||||||
//initialSeekTo = 0L
|
//initialSeekTo = 0L
|
||||||
|
@ -686,7 +682,7 @@ class MPVPlayer(
|
||||||
|
|
||||||
/** Prepares the player. */
|
/** Prepares the player. */
|
||||||
override fun prepare() {
|
override fun prepare() {
|
||||||
internalMediaItems?.forEach { mediaItem ->
|
internalMediaItems.forEach { mediaItem ->
|
||||||
MPVLib.command(
|
MPVLib.command(
|
||||||
arrayOf(
|
arrayOf(
|
||||||
"loadfile",
|
"loadfile",
|
||||||
|
@ -837,7 +833,7 @@ class MPVPlayer(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareMediaItem(index: Int) {
|
private fun prepareMediaItem(index: Int) {
|
||||||
internalMediaItems?.get(index)?.let { mediaItem ->
|
internalMediaItems.getOrNull(index)?.let { mediaItem ->
|
||||||
resetInternalState()
|
resetInternalState()
|
||||||
mediaItem.localConfiguration?.subtitleConfigurations?.forEach { subtitle ->
|
mediaItem.localConfiguration?.subtitleConfigurations?.forEach { subtitle ->
|
||||||
initialCommands.add(
|
initialCommands.add(
|
||||||
|
@ -926,33 +922,8 @@ class MPVPlayer(
|
||||||
currentIndex = 0
|
currentIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun getCurrentTracks(): Tracks {
|
||||||
* Returns the available track groups.
|
return tracks
|
||||||
*
|
|
||||||
* @see com.google.android.exoplayer2.Player.Listener.onTracksChanged
|
|
||||||
*/
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun getCurrentTrackGroups(): TrackGroupArray {
|
|
||||||
return TrackGroupArray.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current track selections.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* A concrete implementation may include null elements if it has a fixed number of renderer
|
|
||||||
* components, wishes to report a TrackSelection for each of them, and has one or more renderer
|
|
||||||
* components that is not assigned any selected tracks.
|
|
||||||
*
|
|
||||||
* @see com.google.android.exoplayer2.Player.Listener.onTracksChanged
|
|
||||||
*/
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun getCurrentTrackSelections(): TrackSelectionArray {
|
|
||||||
return TrackSelectionArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCurrentTracksInfo(): TracksInfo {
|
|
||||||
return tracksInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTrackSelectionParameters(): TrackSelectionParameters {
|
override fun getTrackSelectionParameters(): TrackSelectionParameters {
|
||||||
|
@ -1203,7 +1174,7 @@ class MPVPlayer(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the current [Cues][Cue]. This list may be empty. */
|
/** Returns the current [Cues][Cue]. This list may be empty. */
|
||||||
override fun getCurrentCues(): MutableList<Cue> {
|
override fun getCurrentCues(): CueGroup {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1258,78 +1229,6 @@ class MPVPlayer(
|
||||||
throw IllegalArgumentException("You should use global volume controls. Check out AUDIO_SERVICE.")
|
throw IllegalArgumentException("You should use global volume controls. Check out AUDIO_SERVICE.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private class CurrentTrackSelection(
|
|
||||||
private val currentTrackGroup: TrackGroup,
|
|
||||||
private val index: Int
|
|
||||||
) : TrackSelection {
|
|
||||||
/**
|
|
||||||
* Returns an integer specifying the type of the selection, or [.TYPE_UNSET] if not
|
|
||||||
* specified.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Track selection types are specific to individual applications, but should be defined
|
|
||||||
* starting from [.TYPE_CUSTOM_BASE] to ensure they don't conflict with any types that may
|
|
||||||
* be added to the library in the future.
|
|
||||||
*/
|
|
||||||
override fun getType(): Int {
|
|
||||||
return TrackSelection.TYPE_UNSET
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the [TrackGroup] to which the selected tracks belong. */
|
|
||||||
override fun getTrackGroup(): TrackGroup {
|
|
||||||
return currentTrackGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the number of tracks in the selection. */
|
|
||||||
override fun length(): Int {
|
|
||||||
return if (index != C.INDEX_UNSET) 1 else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format of the track at a given index in the selection.
|
|
||||||
*
|
|
||||||
* @param index The index in the selection.
|
|
||||||
* @return The format of the selected track.
|
|
||||||
*/
|
|
||||||
override fun getFormat(index: Int): Format {
|
|
||||||
return currentTrackGroup.getFormat(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index in the track group of the track at a given index in the selection.
|
|
||||||
*
|
|
||||||
* @param index The index in the selection.
|
|
||||||
* @return The index of the selected track.
|
|
||||||
*/
|
|
||||||
override fun getIndexInTrackGroup(index: Int): Int {
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index in the selection of the track with the specified format. The format is
|
|
||||||
* located by identity so, for example, `selection.indexOf(selection.getFormat(index)) ==
|
|
||||||
* index` even if multiple selected tracks have formats that contain the same values.
|
|
||||||
*
|
|
||||||
* @param format The format.
|
|
||||||
* @return The index in the selection, or [C.INDEX_UNSET] if the track with the specified
|
|
||||||
* format is not part of the selection.
|
|
||||||
*/
|
|
||||||
override fun indexOf(format: Format): Int {
|
|
||||||
return currentTrackGroup.indexOf(format)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index in the selection of the track with the specified index in the track group.
|
|
||||||
*
|
|
||||||
* @param indexInTrackGroup The index in the track group.
|
|
||||||
* @return The index in the selection, or [C.INDEX_UNSET] if the track with the specified
|
|
||||||
* index is not part of the selection.
|
|
||||||
*/
|
|
||||||
override fun indexOf(indexInTrackGroup: Int): Int {
|
|
||||||
return indexInTrackGroup
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Fraction to which audio volume is ducked on loss of audio focus
|
* Fraction to which audio volume is ducked on loss of audio focus
|
||||||
|
@ -1449,10 +1348,10 @@ class MPVPlayer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMPVTracks(trackList: String): Pair<List<Track>, TracksInfo> {
|
private fun getMPVTracks(trackList: String): Pair<List<Track>, Tracks> {
|
||||||
val tracks = mutableListOf<Track>()
|
val mpvTracks = mutableListOf<Track>()
|
||||||
var tracksInfo = TracksInfo.EMPTY
|
var tracks = Tracks.EMPTY
|
||||||
val trackGroupInfos = mutableListOf<TracksInfo.TrackGroupInfo>()
|
val trackGroups = mutableListOf<Tracks.Group>()
|
||||||
|
|
||||||
val trackListVideo = mutableListOf<Format>()
|
val trackListVideo = mutableListOf<Format>()
|
||||||
val trackListAudio = mutableListOf<Format>()
|
val trackListAudio = mutableListOf<Format>()
|
||||||
|
@ -1475,7 +1374,7 @@ class MPVPlayer(
|
||||||
width = null,
|
width = null,
|
||||||
height = null
|
height = null
|
||||||
)
|
)
|
||||||
tracks.add(emptyTrack)
|
mpvTracks.add(emptyTrack)
|
||||||
trackListText.add(emptyTrack.toFormat())
|
trackListText.add(emptyTrack.toFormat())
|
||||||
val currentTrackList = JSONArray(trackList)
|
val currentTrackList = JSONArray(trackList)
|
||||||
for (index in 0 until currentTrackList.length()) {
|
for (index in 0 until currentTrackList.length()) {
|
||||||
|
@ -1483,21 +1382,21 @@ class MPVPlayer(
|
||||||
val currentFormat = currentTrack.toFormat()
|
val currentFormat = currentTrack.toFormat()
|
||||||
when (currentTrack.type) {
|
when (currentTrack.type) {
|
||||||
TrackType.VIDEO -> {
|
TrackType.VIDEO -> {
|
||||||
tracks.add(currentTrack)
|
mpvTracks.add(currentTrack)
|
||||||
trackListVideo.add(currentFormat)
|
trackListVideo.add(currentFormat)
|
||||||
if (currentTrack.selected) {
|
if (currentTrack.selected) {
|
||||||
indexCurrentVideo = trackListVideo.indexOf(currentFormat)
|
indexCurrentVideo = trackListVideo.indexOf(currentFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TrackType.AUDIO -> {
|
TrackType.AUDIO -> {
|
||||||
tracks.add(currentTrack)
|
mpvTracks.add(currentTrack)
|
||||||
trackListAudio.add(currentFormat)
|
trackListAudio.add(currentFormat)
|
||||||
if (currentTrack.selected) {
|
if (currentTrack.selected) {
|
||||||
indexCurrentAudio = trackListAudio.indexOf(currentFormat)
|
indexCurrentAudio = trackListAudio.indexOf(currentFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TrackType.SUBTITLE -> {
|
TrackType.SUBTITLE -> {
|
||||||
tracks.add(currentTrack)
|
mpvTracks.add(currentTrack)
|
||||||
trackListText.add(currentFormat)
|
trackListText.add(currentFormat)
|
||||||
if (currentTrack.selected) {
|
if (currentTrack.selected) {
|
||||||
indexCurrentText = trackListText.indexOf(currentFormat)
|
indexCurrentText = trackListText.indexOf(currentFormat)
|
||||||
|
@ -1507,71 +1406,45 @@ class MPVPlayer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackListText.size == 1 && trackListText[0].id == emptyTrack.id.toString()) {
|
if (trackListText.size == 1 && trackListText[0].id == emptyTrack.id.toString()) {
|
||||||
tracks.remove(emptyTrack)
|
mpvTracks.remove(emptyTrack)
|
||||||
trackListText.removeFirst()
|
trackListText.removeFirst()
|
||||||
}
|
}
|
||||||
if (trackListVideo.isNotEmpty()) {
|
if (trackListVideo.isNotEmpty()) {
|
||||||
with(TrackGroup(*trackListVideo.toTypedArray())) {
|
with(TrackGroup(*trackListVideo.toTypedArray())) {
|
||||||
TracksInfo.TrackGroupInfo(
|
Tracks.Group(
|
||||||
this,
|
this,
|
||||||
|
true,
|
||||||
intArrayOf(C.FORMAT_HANDLED),
|
intArrayOf(C.FORMAT_HANDLED),
|
||||||
C.TRACK_TYPE_VIDEO,
|
|
||||||
BooleanArray(this.length) { it == indexCurrentVideo }
|
BooleanArray(this.length) { it == indexCurrentVideo }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackListAudio.isNotEmpty()) {
|
if (trackListAudio.isNotEmpty()) {
|
||||||
with(TrackGroup(*trackListAudio.toTypedArray())) {
|
with(TrackGroup(*trackListAudio.toTypedArray())) {
|
||||||
TracksInfo.TrackGroupInfo(
|
Tracks.Group(
|
||||||
this,
|
this,
|
||||||
|
true,
|
||||||
IntArray(this.length) { C.FORMAT_HANDLED },
|
IntArray(this.length) { C.FORMAT_HANDLED },
|
||||||
C.TRACK_TYPE_AUDIO,
|
|
||||||
BooleanArray(this.length) { it == indexCurrentAudio }
|
BooleanArray(this.length) { it == indexCurrentAudio }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackListText.isNotEmpty()) {
|
if (trackListText.isNotEmpty()) {
|
||||||
with(TrackGroup(*trackListText.toTypedArray())) {
|
with(TrackGroup(*trackListText.toTypedArray())) {
|
||||||
TracksInfo.TrackGroupInfo(
|
Tracks.Group(
|
||||||
this,
|
this,
|
||||||
|
true,
|
||||||
IntArray(this.length) { C.FORMAT_HANDLED },
|
IntArray(this.length) { C.FORMAT_HANDLED },
|
||||||
C.TRACK_TYPE_TEXT,
|
|
||||||
BooleanArray(this.length) { it == indexCurrentText }
|
BooleanArray(this.length) { it == indexCurrentText }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackGroupInfos.isNotEmpty()) {
|
if (trackGroups.isNotEmpty()) {
|
||||||
tracksInfo = TracksInfo(trackGroupInfos)
|
tracks = Tracks(trackGroups)
|
||||||
}
|
}
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
}
|
}
|
||||||
return Pair(tracks, tracksInfo)
|
return Pair(mpvTracks, tracks)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges multiple [subtitleSources] into a single [videoSource]
|
|
||||||
*/
|
|
||||||
fun mergeMediaSources(
|
|
||||||
videoSource: MediaSource,
|
|
||||||
subtitleSources: Array<MediaSource>,
|
|
||||||
dataSource: DataSource.Factory
|
|
||||||
): MediaSource {
|
|
||||||
return when {
|
|
||||||
subtitleSources.isEmpty() -> videoSource
|
|
||||||
else -> {
|
|
||||||
val subtitleConfigurations = mutableListOf<MediaItem.SubtitleConfiguration>()
|
|
||||||
subtitleSources.forEach { subtitleSource ->
|
|
||||||
subtitleSource.mediaItem.localConfiguration?.subtitleConfigurations?.forEach { subtitle ->
|
|
||||||
subtitleConfigurations.add(subtitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProgressiveMediaSource.Factory(dataSource)
|
|
||||||
.createMediaSource(
|
|
||||||
videoSource.mediaItem.buildUpon()
|
|
||||||
.setSubtitleConfigurations(subtitleConfigurations).build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,8 @@ constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val useMpv = sp.getBoolean("mpv_player", false)
|
val useMpv = sp.getBoolean("mpv_player", false)
|
||||||
val preferredAudioLanguage = sp.getString("audio_language", null) ?: ""
|
val preferredAudioLanguage = sp.getString("audio_language", "")!!
|
||||||
val preferredSubtitleLanguage = sp.getString("subtitle_language", null) ?: ""
|
val preferredSubtitleLanguage = sp.getString("subtitle_language", "")!!
|
||||||
|
|
||||||
if (useMpv) {
|
if (useMpv) {
|
||||||
val preferredLanguages = mapOf(
|
val preferredLanguages = mapOf(
|
||||||
|
@ -180,7 +180,7 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handler.postDelayed(this, 2000)
|
handler.postDelayed(this, 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handler.post(runnable)
|
handler.post(runnable)
|
||||||
|
@ -225,7 +225,7 @@ constructor(
|
||||||
currentSubtitleTracks.clear()
|
currentSubtitleTracks.clear()
|
||||||
when (player) {
|
when (player) {
|
||||||
is MPVPlayer -> {
|
is MPVPlayer -> {
|
||||||
player.currentTracks.forEach {
|
player.currentMpvTracks.forEach {
|
||||||
when (it.type) {
|
when (it.type) {
|
||||||
TrackType.AUDIO -> {
|
TrackType.AUDIO -> {
|
||||||
currentAudioTracks.add(it)
|
currentAudioTracks.add(it)
|
||||||
|
|
Loading…
Reference in a new issue