Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide simplified tracks management #495

Merged
merged 20 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import ch.srgssr.pillarbox.analytics.commandersact.CommandersAct
import ch.srgssr.pillarbox.analytics.commandersact.MediaEventType
import ch.srgssr.pillarbox.analytics.commandersact.TCMediaEvent
import ch.srgssr.pillarbox.core.business.tracker.TotalPlaytimeCounter
import ch.srgssr.pillarbox.player.extension.audio
import ch.srgssr.pillarbox.player.extension.isForced
import ch.srgssr.pillarbox.player.tracks.audioTracks
import ch.srgssr.pillarbox.player.utils.DebugLogger
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -233,15 +233,15 @@ internal class CommandersActStreaming(
}
}

@Suppress("SwallowedException")
private fun handleAudioTrack(event: TCMediaEvent) {
try {
val selectedAudioGroup = player.currentTracks.audio.first { it.isSelected }
val selectedFormat: Format = selectedAudioGroup.getTrackFormat(0)
event.audioTrackLanguage = selectedFormat.language ?: C.LANGUAGE_UNDETERMINED
} catch (e: NoSuchElementException) {
event.audioTrackLanguage = C.LANGUAGE_UNDETERMINED
}
val audioTrackLanguage = player.currentTracks
.audioTracks
.find { it.isSelected }
?.format
?.language
?: C.LANGUAGE_UNDETERMINED

event.audioTrackLanguage = audioTrackLanguage
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,37 @@ package ch.srgssr.pillarbox.demo.shared.ui.player.settings

import android.app.Application
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Audiotrack
import androidx.compose.material.icons.filled.Speed
import androidx.compose.material.icons.filled.Subtitles
import androidx.compose.material.icons.filled.ClosedCaption
import androidx.compose.material.icons.filled.RecordVoiceOver
import androidx.compose.material.icons.filled.SlowMotionVideo
import androidx.compose.material.icons.filled.Tune
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.media3.common.C
import androidx.media3.common.Player
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.Tracks.Group
import ch.srgssr.pillarbox.demo.shared.R
import ch.srgssr.pillarbox.player.extension.audio
import ch.srgssr.pillarbox.player.extension.disableAudioTrack
import ch.srgssr.pillarbox.player.extension.disableTextTrack
import ch.srgssr.pillarbox.player.extension.displayName
import ch.srgssr.pillarbox.player.extension.getPlaybackSpeed
import ch.srgssr.pillarbox.player.extension.isAudioTrackDisabled
import ch.srgssr.pillarbox.player.extension.isTextTrackDisabled
import ch.srgssr.pillarbox.player.extension.setDefaultAudioTrack
import ch.srgssr.pillarbox.player.extension.setDefaultTextTrack
import ch.srgssr.pillarbox.player.extension.setTrackOverride
import ch.srgssr.pillarbox.player.extension.text
import ch.srgssr.pillarbox.player.extension.isVideoTrackDisabled
import ch.srgssr.pillarbox.player.getCurrentTracksAsFlow
import ch.srgssr.pillarbox.player.getPlaybackSpeedAsFlow
import ch.srgssr.pillarbox.player.getTrackSelectionParametersAsFlow
import ch.srgssr.pillarbox.player.tracks.Track
import ch.srgssr.pillarbox.player.tracks.VideoTrack
import ch.srgssr.pillarbox.player.tracks.audioTracks
import ch.srgssr.pillarbox.player.tracks.disableAudioTrack
import ch.srgssr.pillarbox.player.tracks.disableTextTrack
import ch.srgssr.pillarbox.player.tracks.disableVideoTrack
import ch.srgssr.pillarbox.player.tracks.selectTrack
import ch.srgssr.pillarbox.player.tracks.setAutoAudioTrack
import ch.srgssr.pillarbox.player.tracks.setAutoTextTrack
import ch.srgssr.pillarbox.player.tracks.setAutoVideoTrack
import ch.srgssr.pillarbox.player.tracks.textTracks
import ch.srgssr.pillarbox.player.tracks.videoTracks
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
Expand All @@ -54,54 +60,6 @@ class PlayerSettingsViewModel(
private val playbackSpeed = player.getPlaybackSpeedAsFlow()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), player.getPlaybackSpeed())

/**
* All the available settings for the current [player].
*/
val settings = combine(
tracks,
trackSelectionParameters,
playbackSpeed
) { currentTracks, trackSelectionParameters, playbackSpeed ->
buildList {
add(
SettingItem(
title = application.getString(R.string.speed),
subtitle = getSpeedLabel(playbackSpeed),
icon = Icons.Default.Speed,
destination = SettingsRoutes.PlaybackSpeed
)
)

if (currentTracks.text.isNotEmpty()) {
add(
SettingItem(
title = application.getString(R.string.subtitles),
subtitle = getTracksSubtitle(
tracks = currentTracks.text,
disabled = trackSelectionParameters.isTextTrackDisabled
),
icon = Icons.Default.Subtitles,
destination = SettingsRoutes.Subtitles
)
)
}

if (currentTracks.audio.isNotEmpty()) {
add(
SettingItem(
title = application.getString(R.string.audio_track),
subtitle = getTracksSubtitle(
tracks = currentTracks.audio,
disabled = trackSelectionParameters.isAudioTrackDisabled
),
icon = Icons.Default.Audiotrack,
destination = SettingsRoutes.AudioTrack
)
)
}
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

/**
* All the available subtitle for the current [player].
*/
Expand All @@ -111,7 +69,7 @@ class PlayerSettingsViewModel(
) { tracks, trackSelectionParameters ->
TracksSettingItem(
title = application.getString(R.string.subtitles),
tracks = tracks.text,
tracks = tracks.textTracks,
disabled = trackSelectionParameters.isTextTrackDisabled
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
Expand All @@ -125,11 +83,29 @@ class PlayerSettingsViewModel(
) { tracks, trackSelectionParameters ->
TracksSettingItem(
title = application.getString(R.string.audio_track),
tracks = tracks.audio,
tracks = tracks.audioTracks,
disabled = trackSelectionParameters.isAudioTrackDisabled
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)

/**
* All the available video tracks for the current [player].
*/
val videoTracks = combine(
tracks,
trackSelectionParameters,
) { tracks, trackSelectionParameters ->
TracksSettingItem(
title = application.getString(R.string.video_tracks),
tracks = tracks.videoTracks
.sortedWith(
compareByDescending<VideoTrack> { it.format.height }
.thenByDescending { it.format.bitrate }
),
disabled = trackSelectionParameters.isVideoTrackDisabled,
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)

/**
* All the available playback speeds for the current [player].
*/
Expand All @@ -143,11 +119,84 @@ class PlayerSettingsViewModel(
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

/**
* All the available settings for the current [player].
*/
val settings = combine(
subtitles,
audioTracks,
videoTracks,
trackSelectionParameters,
playbackSpeed,
) { subtitles, audioTracks, videoTracks, trackSelectionParameters, playbackSpeed ->
buildList {
add(
SettingItem(
title = application.getString(R.string.speed),
subtitle = getSpeedLabel(playbackSpeed),
icon = Icons.Default.SlowMotionVideo,
destination = SettingsRoutes.PlaybackSpeed,
)
)

if (subtitles != null && subtitles.tracks.isNotEmpty()) {
add(
SettingItem(
title = application.getString(R.string.subtitles),
subtitle = getTracksSubtitle(
tracks = subtitles.tracks,
disabled = trackSelectionParameters.isTextTrackDisabled,
),
icon = Icons.Default.ClosedCaption,
destination = SettingsRoutes.Subtitles,
)
)
}

if (audioTracks != null && audioTracks.tracks.isNotEmpty()) {
add(
SettingItem(
title = application.getString(R.string.audio_track),
subtitle = getTracksSubtitle(
tracks = audioTracks.tracks,
disabled = trackSelectionParameters.isAudioTrackDisabled,
),
icon = Icons.Default.RecordVoiceOver,
destination = SettingsRoutes.AudioTrack,
)
)
}

if (videoTracks != null && videoTracks.tracks.isNotEmpty()) {
add(
SettingItem(
title = application.getString(R.string.video_tracks),
subtitle = getTracksSubtitle(
tracks = videoTracks.tracks,
disabled = trackSelectionParameters.isVideoTrackDisabled,
),
icon = Icons.Default.Tune,
destination = SettingsRoutes.VideoTrack,
)
)
}
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

/**
* Select a specific track.
*
* @param track The track to select.
*/
fun selectTrack(track: Track) {
player.selectTrack(track)
}

/**
* Reset the subtitles.
*/
fun resetSubtitles() {
player.setDefaultTextTrack(application)
player.setAutoTextTrack(application)
}

/**
Expand All @@ -157,21 +206,11 @@ class PlayerSettingsViewModel(
player.disableTextTrack()
}

/**
* Set the subtitles.
*
* @param group The selected group.
* @param trackIndex The index of the track in the provided group.
*/
fun setSubtitle(group: Group, trackIndex: Int) {
player.setTrackOverride(TrackSelectionOverride(group.mediaTrackGroup, trackIndex))
}

/**
* Reset the audio track.
*/
fun resetAudioTrack() {
player.setDefaultAudioTrack(application)
player.setAutoAudioTrack(application)
}

/**
Expand All @@ -182,13 +221,17 @@ class PlayerSettingsViewModel(
}

/**
* Set the audio track.
*
* @param group The selected group.
* @param trackIndex The index of the track in the provided group.
* Reset the video track.
*/
fun resetVideoTrack() {
player.setAutoVideoTrack()
}

/**
* Disable the video track.
*/
fun setAudioTrack(group: Group, trackIndex: Int) {
player.setTrackOverride(TrackSelectionOverride(group.mediaTrackGroup, trackIndex))
fun disableVideoTrack() {
player.disableVideoTrack()
}

/**
Expand All @@ -201,22 +244,15 @@ class PlayerSettingsViewModel(
}

private fun getTracksSubtitle(
tracks: List<Group>,
disabled: Boolean
tracks: List<Track>,
disabled: Boolean,
): String? {
return if (disabled) {
application.getString(R.string.disabled)
} else {
tracks.filter { it.isSelected }
.flatMap {
(0 until it.length).mapNotNull { trackIndex ->
if (it.isTrackSelected(trackIndex)) {
it.getTrackFormat(trackIndex).displayName
} else {
null
}
}
}
.map { it.format.displayName }
.filter { it != C.LANGUAGE_UNDETERMINED }
.firstOrNull()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ sealed class SettingsRoutes(val route: String) {
* The route for the audio track setting.
*/
data object AudioTrack : SettingsRoutes(route = "settings/audio_track")

/**
* The route for the video track setting.
*/
data object VideoTrack : SettingsRoutes(route = "settings/video_track")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
package ch.srgssr.pillarbox.demo.shared.ui.player.settings

import androidx.media3.common.Tracks.Group
import ch.srgssr.pillarbox.player.tracks.Track

/**
* The setting for a specific kind a track (audio/text/video).
Expand All @@ -18,7 +18,7 @@ data class TracksSettingItem(
/**
* The list of possible tracks.
*/
val tracks: List<Group>,
val tracks: List<Track>,

/**
* `true` if this kind of tracks is disabled, `false` otherwise.
Expand Down
3 changes: 2 additions & 1 deletion pillarbox-demo-shared/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
<string name="empty_search_query">Enter something to search</string>
<string name="duration"><xliff:g example="15" id="duration">%1$d</xliff:g> min</string>
<string name="settings">Settings</string>
<string name="audio_track">Audio track</string>
<string name="audio_track">Audio tracks</string>
<string name="video_tracks">Video tracks</string>
<string name="subtitles">Subtitles</string>
<string name="speed">Speed</string>
<string name="speed_normal">Normal</string>
Expand Down
Loading
Loading