Select audio and subtitle tracks

This commit is contained in:
jarnedemeulemeester 2021-09-15 23:13:59 +02:00
parent 6d340bd7ab
commit 9cddd50d0e
No known key found for this signature in database
GPG key ID: B61B7B150DB6A6D2
6 changed files with 188 additions and 11 deletions

View file

@ -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
}
}

View file

@ -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")
}
}
}
}

View file

@ -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

View file

@ -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) {
}
}
}

View file

@ -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>

View file

@ -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"