Skip to content

Commit e3b6906

Browse files
authored
Don't filter out unsupported/forced tracks (#487)
1 parent d9e96c2 commit e3b6906

File tree

4 files changed

+42
-137
lines changed

4 files changed

+42
-137
lines changed

pillarbox-demo-tv/src/main/java/ch/srgssr/pillarbox/demo/tv/ui/player/compose/settings/PlaybackSettingsDrawer.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import ch.srgssr.pillarbox.demo.shared.ui.player.settings.TracksSettingItem
5858
import ch.srgssr.pillarbox.demo.tv.ui.theme.paddings
5959
import ch.srgssr.pillarbox.player.extension.displayName
6060
import ch.srgssr.pillarbox.player.extension.hasAccessibilityRoles
61+
import ch.srgssr.pillarbox.player.extension.isForced
6162

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

319320
tracksSetting.tracks.forEach { group ->
320321
items(group.length) { trackIndex ->
322+
val format = group.getTrackFormat(trackIndex)
321323
NavigationDrawerItem(
322324
selected = group.isTrackSelected(trackIndex),
325+
enabled = group.isTrackSupported(trackIndex) && !format.isForced(),
323326
onClick = { onTrackClick(group, trackIndex) },
324327
leadingContent = {
325328
AnimatedVisibility(visible = group.isTrackSelected(trackIndex)) {
@@ -330,7 +333,6 @@ private fun NavigationDrawerScope.TracksSetting(
330333
}
331334
},
332335
content = {
333-
val format = group.getTrackFormat(trackIndex)
334336
val label = buildString {
335337
append(format.displayName)
336338

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/player/settings/TrackSelectionSettings.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import ch.srgssr.pillarbox.demo.shared.ui.player.settings.TracksSettingItem
3232
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
3333
import ch.srgssr.pillarbox.player.extension.displayName
3434
import ch.srgssr.pillarbox.player.extension.hasAccessibilityRoles
35+
import ch.srgssr.pillarbox.player.extension.isForced
3536

3637
/**
3738
* Track selection settings
@@ -78,14 +79,15 @@ fun TrackSelectionSettings(
7879
}
7980
tracksSetting.tracks.forEach { group ->
8081
items(group.length) { trackIndex ->
82+
val format = group.getTrackFormat(trackIndex)
8183
SettingsOption(
8284
modifier = itemModifier,
8385
selected = group.isTrackSelected(trackIndex),
86+
enabled = group.isTrackSupported(trackIndex) && !format.isForced(),
8487
onClick = {
8588
onTrackClick(group, trackIndex)
8689
},
8790
content = {
88-
val format = group.getTrackFormat(trackIndex)
8991
when (group.type) {
9092
C.TRACK_TYPE_AUDIO -> {
9193
val str = StringBuilder()

pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt

Lines changed: 4 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,28 @@
44
*/
55
package ch.srgssr.pillarbox.player.extension
66

7-
import android.annotation.SuppressLint
87
import androidx.media3.common.C
98
import androidx.media3.common.C.TrackType
10-
import androidx.media3.common.Format
11-
import androidx.media3.common.TrackGroup
129
import androidx.media3.common.Tracks
1310

1411
/**
15-
* Text tracks
12+
* Text tracks.
1613
*/
1714
val Tracks.text: List<Tracks.Group>
18-
get() = filterByTrackType(C.TRACK_TYPE_TEXT).mapNotNull { it.filterForcedAndUnsupported() }
15+
get() = filterByTrackType(C.TRACK_TYPE_TEXT)
1916

2017
/**
2118
* Audio tracks.
2219
*/
2320
val Tracks.audio: List<Tracks.Group>
24-
get() = filterByTrackType(C.TRACK_TYPE_AUDIO).mapNotNull { it.filterUnsupported() }
21+
get() = filterByTrackType(C.TRACK_TYPE_AUDIO)
2522

2623
/**
2724
* Video tracks.
2825
*/
2926
val Tracks.video: List<Tracks.Group>
30-
get() = filterByTrackType(C.TRACK_TYPE_VIDEO).mapNotNull { it.filterUnsupported() }
27+
get() = filterByTrackType(C.TRACK_TYPE_VIDEO)
3128

3229
private fun Tracks.filterByTrackType(trackType: @TrackType Int): List<Tracks.Group> {
3330
return groups.filter { it.type == trackType }
3431
}
35-
36-
private fun Tracks.Group.filterForcedAndUnsupported(): Tracks.Group? {
37-
return filterBy { group, i -> group.isTrackSupported(i) && !group.getTrackFormat(i).isForced() }
38-
}
39-
40-
internal fun Tracks.Group.filterUnsupported(): Tracks.Group? {
41-
return filterBy { group, i -> group.isTrackSupported(i) }
42-
}
43-
44-
/**
45-
* Filter [Format] that matching [predicate].
46-
*
47-
* @param predicate function that takes the index of an element and the element itself and returns the result of predicate evaluation on the element.
48-
* @receiver
49-
* @return element matching [predicate] or null if filtered items is empty because [TrackGroup] can not be empty.
50-
*/
51-
@SuppressLint("WrongConstant")
52-
@Suppress("SpreadOperator", "ReturnCount")
53-
internal fun Tracks.Group.filterBy(predicate: (Tracks.Group, Int) -> Boolean): Tracks.Group? {
54-
val listIndexMatchingPredicate = ArrayList<Int>(length)
55-
for (i in 0 until length) {
56-
if (predicate(this, i)) {
57-
listIndexMatchingPredicate.add(i)
58-
}
59-
}
60-
// All format doesn't match predicate.
61-
if (listIndexMatchingPredicate.isEmpty()) return null
62-
// All format matching the predicate, nothing to change.
63-
if (listIndexMatchingPredicate.size == length) return this
64-
val count = listIndexMatchingPredicate.size
65-
val formats = ArrayList<Format>(count)
66-
val trackSupport = IntArray(count)
67-
val trackSelect = BooleanArray(count)
68-
for (i in 0 until count) {
69-
val trackIndex = listIndexMatchingPredicate[i]
70-
formats.add(getTrackFormat(trackIndex))
71-
trackSupport[i] = getTrackSupport(trackIndex)
72-
trackSelect[i] = isTrackSelected(trackIndex)
73-
}
74-
return Tracks.Group(TrackGroup(mediaTrackGroup.id, *formats.toTypedArray()), isAdaptiveSupported, trackSupport, trackSelect)
75-
}

pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/TracksTest.kt

Lines changed: 32 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
1515
import org.junit.runner.RunWith
1616
import kotlin.test.Test
1717
import kotlin.test.assertEquals
18-
import kotlin.test.assertTrue
1918

2019
@RunWith(AndroidJUnit4::class)
2120
class TracksTest {
@@ -25,7 +24,13 @@ class TracksTest {
2524
createFormatTrackFormat("t2", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT),
2625
),
2726
createTrackGroup(
28-
createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND),
27+
formats = listOf(
28+
createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND),
29+
createFormatTrackFormat("t3", mimeType = TEXT_MIME_TYPE),
30+
),
31+
trackSupport = intArrayOf(
32+
C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES,
33+
),
2934
),
3035
)
3136
private val forcedSubtitleTracks = listOf(
@@ -38,22 +43,34 @@ class TracksTest {
3843
createFormatTrackFormat("a1", mimeType = AUDIO_MIME_TYPE),
3944
),
4045
createTrackGroup(
41-
createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO),
46+
formats = listOf(
47+
createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO),
48+
createFormatTrackFormat("a2", mimeType = AUDIO_MIME_TYPE),
49+
),
50+
trackSupport = intArrayOf(
51+
C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES
52+
),
4253
),
4354
)
4455
private val videoTracks = listOf(
4556
createTrackGroup(
46-
createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE),
47-
createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE),
48-
createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE),
57+
formats = listOf(
58+
createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE),
59+
createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE),
60+
createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE),
61+
createFormatTrackFormat("v4", mimeType = VIDEO_MIME_TYPE),
62+
),
63+
trackSupport = intArrayOf(
64+
C.FORMAT_HANDLED, C.FORMAT_HANDLED, C.FORMAT_EXCEEDS_CAPABILITIES, C.FORMAT_HANDLED,
65+
),
4966
),
5067
)
5168
private val tracks = Tracks(audioTracks + textTracks + videoTracks + forcedSubtitleTracks)
5269

5370
@Test
5471
fun `text with text tracks`() {
5572
val textTracks = tracks.text
56-
assertEquals(this.textTracks, textTracks)
73+
assertEquals(this.textTracks + forcedSubtitleTracks, textTracks)
5774
}
5875

5976
@Test
@@ -86,85 +103,6 @@ class TracksTest {
86103
assertEquals(emptyList(), videoTracks)
87104
}
88105

89-
@Test
90-
fun `filterUnsupported one supported track`() {
91-
val format = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
92-
val trackGroup = TrackGroup(format)
93-
val selected = booleanArrayOf(false)
94-
val trackSupport = intArrayOf(C.FORMAT_HANDLED)
95-
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
96-
val expectedTracksGroups = listOf(
97-
Tracks.Group(
98-
trackGroup,
99-
false,
100-
trackSupport,
101-
selected,
102-
)
103-
)
104-
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
105-
}
106-
107-
@Test
108-
fun `filterUnsupported only supported track`() {
109-
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
110-
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
111-
val trackGroup = TrackGroup(format1, format2)
112-
val selected = booleanArrayOf(false, false)
113-
val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED)
114-
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
115-
val expectedTracksGroups = listOf(
116-
Tracks.Group(
117-
trackGroup,
118-
false,
119-
trackSupport,
120-
selected,
121-
)
122-
)
123-
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
124-
}
125-
126-
@Test
127-
fun `filterUnsupported multiple tracks, one unsupported`() {
128-
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
129-
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
130-
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
131-
val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack)
132-
val selected = booleanArrayOf(false, false, false)
133-
val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED, C.FORMAT_UNSUPPORTED_SUBTYPE)
134-
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
135-
val expectedTracksGroups = listOf(
136-
Tracks.Group(
137-
TrackGroup(format1, format2),
138-
false,
139-
intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED),
140-
booleanArrayOf(false, false),
141-
)
142-
)
143-
assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() })
144-
}
145-
146-
@Test
147-
fun `filterUnsupported multiple unsupported tracks`() {
148-
val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE)
149-
val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE)
150-
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
151-
val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack)
152-
val selected = booleanArrayOf(false, false, false)
153-
val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE)
154-
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
155-
assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty())
156-
}
157-
158-
@Test
159-
fun `filterUnsupported one unsupported track`() {
160-
val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE)
161-
val trackGroup = TrackGroup(formatUnsupportedTrack)
162-
val selected = booleanArrayOf(false)
163-
val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE)
164-
val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected)))
165-
assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty())
166-
}
167-
168106
private companion object {
169107
private const val TEXT_MIME_TYPE = MimeTypes.APPLICATION_TTML
170108
private const val VIDEO_MIME_TYPE = MimeTypes.VIDEO_H265
@@ -187,10 +125,17 @@ class TracksTest {
187125
}
188126

189127
private fun createTrackGroup(vararg formats: Format): Tracks.Group {
190-
val trackGroup = TrackGroup(*formats)
191128
val trackSupport = IntArray(formats.size) {
192129
C.FORMAT_HANDLED
193130
}
131+
return createTrackGroup(formats.toList(), trackSupport)
132+
}
133+
134+
private fun createTrackGroup(
135+
formats: List<Format>,
136+
trackSupport: IntArray,
137+
): Tracks.Group {
138+
val trackGroup = TrackGroup(*formats.toTypedArray())
194139
val selected = BooleanArray(formats.size)
195140
return Tracks.Group(trackGroup, false, trackSupport, selected)
196141
}

0 commit comments

Comments
 (0)