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

Don't filter out unsupported/forced tracks #487

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