Skip to content

Commit

Permalink
Don't filter out unsupported/forced tracks (#487)
Browse files Browse the repository at this point in the history
  • Loading branch information
MGaetan89 authored Apr 5, 2024
1 parent d9e96c2 commit e3b6906
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import ch.srgssr.pillarbox.demo.shared.ui.player.settings.TracksSettingItem
import ch.srgssr.pillarbox.demo.tv.ui.theme.paddings
import ch.srgssr.pillarbox.player.extension.displayName
import ch.srgssr.pillarbox.player.extension.hasAccessibilityRoles
import ch.srgssr.pillarbox.player.extension.isForced

/**
* Drawer used to display a player's settings.
Expand Down Expand Up @@ -318,8 +319,10 @@ private fun NavigationDrawerScope.TracksSetting(

tracksSetting.tracks.forEach { group ->
items(group.length) { trackIndex ->
val format = group.getTrackFormat(trackIndex)
NavigationDrawerItem(
selected = group.isTrackSelected(trackIndex),
enabled = group.isTrackSupported(trackIndex) && !format.isForced(),
onClick = { onTrackClick(group, trackIndex) },
leadingContent = {
AnimatedVisibility(visible = group.isTrackSelected(trackIndex)) {
Expand All @@ -330,7 +333,6 @@ private fun NavigationDrawerScope.TracksSetting(
}
},
content = {
val format = group.getTrackFormat(trackIndex)
val label = buildString {
append(format.displayName)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import ch.srgssr.pillarbox.demo.shared.ui.player.settings.TracksSettingItem
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
import ch.srgssr.pillarbox.player.extension.displayName
import ch.srgssr.pillarbox.player.extension.hasAccessibilityRoles
import ch.srgssr.pillarbox.player.extension.isForced

/**
* Track selection settings
Expand Down Expand Up @@ -78,14 +79,15 @@ fun TrackSelectionSettings(
}
tracksSetting.tracks.forEach { group ->
items(group.length) { trackIndex ->
val format = group.getTrackFormat(trackIndex)
SettingsOption(
modifier = itemModifier,
selected = group.isTrackSelected(trackIndex),
enabled = group.isTrackSupported(trackIndex) && !format.isForced(),
onClick = {
onTrackClick(group, trackIndex)
},
content = {
val format = group.getTrackFormat(trackIndex)
when (group.type) {
C.TRACK_TYPE_AUDIO -> {
val str = StringBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,72 +4,28 @@
*/
package ch.srgssr.pillarbox.player.extension

import android.annotation.SuppressLint
import androidx.media3.common.C
import androidx.media3.common.C.TrackType
import androidx.media3.common.Format
import androidx.media3.common.TrackGroup
import androidx.media3.common.Tracks

/**
* Text tracks
* Text tracks.
*/
val Tracks.text: List<Tracks.Group>
get() = filterByTrackType(C.TRACK_TYPE_TEXT).mapNotNull { it.filterForcedAndUnsupported() }
get() = filterByTrackType(C.TRACK_TYPE_TEXT)

/**
* Audio tracks.
*/
val Tracks.audio: List<Tracks.Group>
get() = filterByTrackType(C.TRACK_TYPE_AUDIO).mapNotNull { it.filterUnsupported() }
get() = filterByTrackType(C.TRACK_TYPE_AUDIO)

/**
* Video tracks.
*/
val Tracks.video: List<Tracks.Group>
get() = filterByTrackType(C.TRACK_TYPE_VIDEO).mapNotNull { it.filterUnsupported() }
get() = filterByTrackType(C.TRACK_TYPE_VIDEO)

private fun Tracks.filterByTrackType(trackType: @TrackType Int): List<Tracks.Group> {
return groups.filter { it.type == trackType }
}

private fun Tracks.Group.filterForcedAndUnsupported(): Tracks.Group? {
return filterBy { group, i -> group.isTrackSupported(i) && !group.getTrackFormat(i).isForced() }
}

internal fun Tracks.Group.filterUnsupported(): Tracks.Group? {
return filterBy { group, i -> group.isTrackSupported(i) }
}

/**
* Filter [Format] that matching [predicate].
*
* @param predicate function that takes the index of an element and the element itself and returns the result of predicate evaluation on the element.
* @receiver
* @return element matching [predicate] or null if filtered items is empty because [TrackGroup] can not be empty.
*/
@SuppressLint("WrongConstant")
@Suppress("SpreadOperator", "ReturnCount")
internal fun Tracks.Group.filterBy(predicate: (Tracks.Group, Int) -> Boolean): Tracks.Group? {
val listIndexMatchingPredicate = ArrayList<Int>(length)
for (i in 0 until length) {
if (predicate(this, i)) {
listIndexMatchingPredicate.add(i)
}
}
// All format doesn't match predicate.
if (listIndexMatchingPredicate.isEmpty()) return null
// All format matching the predicate, nothing to change.
if (listIndexMatchingPredicate.size == length) return this
val count = listIndexMatchingPredicate.size
val formats = ArrayList<Format>(count)
val trackSupport = IntArray(count)
val trackSelect = BooleanArray(count)
for (i in 0 until count) {
val trackIndex = listIndexMatchingPredicate[i]
formats.add(getTrackFormat(trackIndex))
trackSupport[i] = getTrackSupport(trackIndex)
trackSelect[i] = isTrackSelected(trackIndex)
}
return Tracks.Group(TrackGroup(mediaTrackGroup.id, *formats.toTypedArray()), isAdaptiveSupported, trackSupport, trackSelect)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
class TracksTest {
Expand All @@ -25,7 +24,13 @@ class TracksTest {
createFormatTrackFormat("t2", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT),
),
createTrackGroup(
createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND),
formats = listOf(
createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND),
createFormatTrackFormat("t3", mimeType = TEXT_MIME_TYPE),
),
trackSupport = intArrayOf(
C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES,
),
),
)
private val forcedSubtitleTracks = listOf(
Expand All @@ -38,22 +43,34 @@ class TracksTest {
createFormatTrackFormat("a1", mimeType = AUDIO_MIME_TYPE),
),
createTrackGroup(
createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO),
formats = listOf(
createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO),
createFormatTrackFormat("a2", mimeType = AUDIO_MIME_TYPE),
),
trackSupport = intArrayOf(
C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES
),
),
)
private val videoTracks = listOf(
createTrackGroup(
createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE),
createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE),
createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE),
formats = listOf(
createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE),
createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE),
createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE),
createFormatTrackFormat("v4", mimeType = VIDEO_MIME_TYPE),
),
trackSupport = intArrayOf(
C.FORMAT_HANDLED, C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES, C.FORMAT_HANDLED,
),
),
)
private val tracks = Tracks(audioTracks + textTracks + videoTracks + forcedSubtitleTracks)

@Test
fun `text with text tracks`() {
val textTracks = tracks.text
assertEquals(this.textTracks, textTracks)
assertEquals(this.textTracks + forcedSubtitleTracks, textTracks)
}

@Test
Expand Down Expand Up @@ -86,85 +103,6 @@ class TracksTest {
assertEquals(emptyList(), videoTracks)
}

@Test
fun `filterUnsupported one supported track`() {
val format = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
val trackGroup = TrackGroup(format)
val selected = booleanArrayOf(false)
val trackSupport = intArrayOf(C.FORMAT_HANDLED)
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
val expectedTracksGroups = listOf(
Tracks.Group(
trackGroup,
false,
trackSupport,
selected,
)
)
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
}

@Test
fun `filterUnsupported only supported track`() {
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
val trackGroup = TrackGroup(format1, format2)
val selected = booleanArrayOf(false, false)
val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED)
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
val expectedTracksGroups = listOf(
Tracks.Group(
trackGroup,
false,
trackSupport,
selected,
)
)
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
}

@Test
fun `filterUnsupported multiple tracks, one unsupported`() {
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack)
val selected = booleanArrayOf(false, false, false)
val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED, C.FORMAT_UNSUPPORTED_SUBTYPE)
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
val expectedTracksGroups = listOf(
Tracks.Group(
TrackGroup(format1, format2),
false,
intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED),
booleanArrayOf(false, false),
)
)
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
}

@Test
fun `filterUnsupported multiple unsupported tracks`() {
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack)
val selected = booleanArrayOf(false, false, false)
val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE)
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty())
}

@Test
fun `filterUnsupported one unsupported track`() {
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
val trackGroup = TrackGroup(formatUnsupportedTrack)
val selected = booleanArrayOf(false)
val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE)
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty())
}

private companion object {
private const val TEXT_MIME_TYPE = MimeTypes.APPLICATION_TTML
private const val VIDEO_MIME_TYPE = MimeTypes.VIDEO_H265
Expand All @@ -187,10 +125,17 @@ class TracksTest {
}

private fun createTrackGroup(vararg formats: Format): Tracks.Group {
val trackGroup = TrackGroup(*formats)
val trackSupport = IntArray(formats.size) {
C.FORMAT_HANDLED
}
return createTrackGroup(formats.toList(), trackSupport)
}

private fun createTrackGroup(
formats: List<Format>,
trackSupport: IntArray,
): Tracks.Group {
val trackGroup = TrackGroup(*formats.toTypedArray())
val selected = BooleanArray(formats.size)
return Tracks.Group(trackGroup, false, trackSupport, selected)
}
Expand Down

0 comments on commit e3b6906

Please sign in to comment.