Select audio and subtitle tracks
This commit is contained in:
parent
6d340bd7ab
commit
9cddd50d0e
6 changed files with 188 additions and 11 deletions
|
@ -4,11 +4,19 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.navigation.navArgs
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector
|
||||
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
|
||||
import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment
|
||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -37,6 +45,71 @@ class PlayerActivity : AppCompatActivity() {
|
|||
videoNameTextView.text = title
|
||||
})
|
||||
|
||||
val audioButton = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track)
|
||||
val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
|
||||
|
||||
audioButton.setOnClickListener {
|
||||
when (viewModel.player) {
|
||||
is MPVPlayer -> {
|
||||
TrackSelectionDialogFragment(TrackType.AUDIO, viewModel).show(
|
||||
supportFragmentManager,
|
||||
"trackselectiondialog"
|
||||
)
|
||||
}
|
||||
is SimpleExoPlayer -> {
|
||||
val mappedTrackInfo =
|
||||
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
|
||||
|
||||
var audioRenderer: Int? = null
|
||||
for (i in 0 until mappedTrackInfo.rendererCount) {
|
||||
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_AUDIO)) {
|
||||
audioRenderer = i
|
||||
}
|
||||
}
|
||||
|
||||
if (audioRenderer == null) return@setOnClickListener
|
||||
|
||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||
this, "Select audio track",
|
||||
viewModel.trackSelector, audioRenderer
|
||||
)
|
||||
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
||||
trackSelectionDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subtitleButton.setOnClickListener {
|
||||
when (viewModel.player) {
|
||||
is MPVPlayer -> {
|
||||
TrackSelectionDialogFragment(TrackType.SUBTITLE, viewModel).show(
|
||||
supportFragmentManager,
|
||||
"trackselectiondialog"
|
||||
)
|
||||
}
|
||||
is SimpleExoPlayer -> {
|
||||
val mappedTrackInfo =
|
||||
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
|
||||
|
||||
var subtitleRenderer: Int? = null
|
||||
for (i in 0 until mappedTrackInfo.rendererCount) {
|
||||
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_TEXT)) {
|
||||
subtitleRenderer = i
|
||||
}
|
||||
}
|
||||
|
||||
if (subtitleRenderer == null) return@setOnClickListener
|
||||
|
||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||
this, "Select subtitle track",
|
||||
viewModel.trackSelector, subtitleRenderer
|
||||
)
|
||||
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
||||
trackSelectionDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.navigateBack.observe(this, {
|
||||
if (it) {
|
||||
onBackPressed()
|
||||
|
@ -68,5 +141,18 @@ class PlayerActivity : AppCompatActivity() {
|
|||
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
|
||||
private fun isRendererType(
|
||||
mappedTrackInfo: MappingTrackSelector.MappedTrackInfo,
|
||||
rendererIndex: Int,
|
||||
type: Int
|
||||
): Boolean {
|
||||
val trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex)
|
||||
if (trackGroupArray.length == 0) {
|
||||
return false
|
||||
}
|
||||
val trackType = mappedTrackInfo.getRendererType(rendererIndex)
|
||||
return type == trackType
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package dev.jdtech.jellyfin.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
class TrackSelectionDialogFragment(
|
||||
private val type: String,
|
||||
private val viewModel: PlayerActivityViewModel
|
||||
): DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val trackNames: List<String>
|
||||
when (type) {
|
||||
TrackType.AUDIO -> {
|
||||
trackNames = viewModel.currentAudioTracks.map {
|
||||
if (it.title.isEmpty()) {
|
||||
"${it.lang} - ${it.codec}"
|
||||
} else {
|
||||
"${it.title} - ${it.lang} - ${it.codec}"
|
||||
}
|
||||
}
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setTitle("Select audio track")
|
||||
.setItems(trackNames.toTypedArray()) { _, which ->
|
||||
viewModel.switchToTrack(TrackType.AUDIO, viewModel.currentAudioTracks[which])
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
TrackType.SUBTITLE -> {
|
||||
trackNames = viewModel.currentSubtitleTracks.map {
|
||||
if (it.title.isEmpty()) {
|
||||
"${it.lang} - ${it.codec}"
|
||||
} else {
|
||||
"${it.title} - ${it.lang} - ${it.codec}"
|
||||
}
|
||||
}
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setTitle("Select subtitle track")
|
||||
.setItems(trackNames.toTypedArray()) { _, which ->
|
||||
viewModel.switchToTrack(TrackType.SUBTITLE, viewModel.currentSubtitleTracks[which])
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
else -> {
|
||||
trackNames = listOf()
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setTitle("Select ? track")
|
||||
.setMessage("Unknown track type")
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -140,7 +140,7 @@ class MPVPlayer(
|
|||
private var currentPositionMs: Long? = null
|
||||
private var currentDurationMs: Long? = null
|
||||
private var currentCacheDurationMs: Long? = null
|
||||
private var currentTracks: List<Track> = emptyList()
|
||||
var currentTracks: List<Track> = emptyList()
|
||||
private var initialCommands = mutableListOf<Array<String>>()
|
||||
private var initialSeekTo: Long = 0L
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
|||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.jdtech.jellyfin.models.PlayerItem
|
||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -27,7 +28,7 @@ constructor(
|
|||
application: Application,
|
||||
private val jellyfinRepository: JellyfinRepository
|
||||
) : ViewModel(), Player.Listener {
|
||||
var player: BasePlayer
|
||||
val player: BasePlayer
|
||||
|
||||
private val _navigateBack = MutableLiveData<Boolean>()
|
||||
val navigateBack: LiveData<Boolean> = _navigateBack
|
||||
|
@ -35,8 +36,12 @@ constructor(
|
|||
private val _currentItemTitle = MutableLiveData<String>()
|
||||
val currentItemTitle: LiveData<String> = _currentItemTitle
|
||||
|
||||
var currentAudioTracks: MutableList<MPVPlayer.Companion.Track> = mutableListOf()
|
||||
var currentSubtitleTracks: MutableList<MPVPlayer.Companion.Track> = mutableListOf()
|
||||
|
||||
private var items: Array<PlayerItem> = arrayOf()
|
||||
|
||||
val trackSelector = DefaultTrackSelector(application)
|
||||
var playWhenReady = true
|
||||
private var currentWindow = 0
|
||||
private var playbackPosition: Long = 0
|
||||
|
@ -51,7 +56,6 @@ constructor(
|
|||
} else {
|
||||
val renderersFactory =
|
||||
DefaultRenderersFactory(application).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
|
||||
val trackSelector = DefaultTrackSelector(application)
|
||||
trackSelector.setParameters(
|
||||
trackSelector.buildUponParameters()
|
||||
.setTunnelingEnabled(true)
|
||||
|
@ -175,6 +179,25 @@ constructor(
|
|||
}
|
||||
ExoPlayer.STATE_READY -> {
|
||||
stateString = "ExoPlayer.STATE_READY -"
|
||||
currentAudioTracks.clear()
|
||||
currentSubtitleTracks.clear()
|
||||
when (player) {
|
||||
is MPVPlayer -> {
|
||||
player.currentTracks.forEach {
|
||||
when (it.type) {
|
||||
TrackType.AUDIO -> {
|
||||
currentAudioTracks.add(it)
|
||||
}
|
||||
TrackType.SUBTITLE -> {
|
||||
currentSubtitleTracks.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is SimpleExoPlayer -> {
|
||||
Timber.d(player.currentTrackGroups.length.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
ExoPlayer.STATE_ENDED -> {
|
||||
stateString = "ExoPlayer.STATE_ENDED -"
|
||||
|
@ -189,4 +212,12 @@ constructor(
|
|||
Timber.d("Clearing Player ViewModel")
|
||||
releasePlayer()
|
||||
}
|
||||
|
||||
fun switchToTrack(trackType: String, track: MPVPlayer.Companion.Track) {
|
||||
if (player is MPVPlayer) {
|
||||
player.selectTrack(trackType, isExternal = false, index = track.ffIndex)
|
||||
} else if (player is SimpleExoPlayer) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,14 +6,11 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".PlayerActivity">
|
||||
|
||||
<com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
<com.google.android.exoplayer2.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
app:show_subtitle_button="true"
|
||||
app:show_buffering="always"
|
||||
app:controller_layout_id="@layout/exo_player_styled_control_view"
|
||||
app:animation_enabled="false"/>
|
||||
app:show_buffering="always" />
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_audio_track"
|
||||
android:id="@+id/btn_audio_track"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
|
@ -68,7 +68,7 @@
|
|||
android:layout_height="0dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_subtitle"
|
||||
android:id="@+id/btn_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
|
@ -106,7 +106,7 @@
|
|||
android:src="@drawable/ic_rewind" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_play_pause"
|
||||
android:id="@+id/exo_play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/circle_background"
|
Loading…
Reference in a new issue