Fix some track selection bugs (#649)

## Description
Fixes track selection issues with MPV. This was especially focused on
libraries that disable embedded subtitles, but also fixes issues where
the audio streams are listed before video streams.

Also includes test media source JSON and unit tests to verify many
scenarios.

### Related issues
Fixes #494
This commit is contained in:
Ray 2026-01-07 17:27:54 -05:00 committed by GitHub
parent 6906813bbc
commit d2e0d527dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 1278 additions and 28 deletions

View file

@ -0,0 +1,9 @@
package android.text;
// Mocks static class for non-instrumented unit tests
public class TextUtils {
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
}

View file

@ -0,0 +1,591 @@
package com.github.damontecres.wholphin.test
import androidx.media3.common.C
import androidx.media3.common.Format
import androidx.media3.common.TrackGroup
import androidx.media3.common.TrackSelectionParameters
import androidx.media3.common.Tracks
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.preferences.PlayerBackend
import com.github.damontecres.wholphin.ui.playback.TrackSelectionUtils
import kotlinx.serialization.json.Json
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.junit.Assert
import org.junit.Test
import java.nio.file.Paths
import kotlin.io.path.readText
class TestTrackSelection {
/**
* Builds the tracks for the `embedded_subs.json` for the given backend
*
* Note: This is manual based on observation & code review of the playback for that file
*/
private fun buildEmbeddedTracks(backend: PlayerBackend): Tracks {
val formats =
if (backend == PlayerBackend.MPV) {
val video =
Format
.Builder()
.setId("0:1")
.setSampleMimeType("video/default")
.build()
val audios =
(1..3).map {
Format
.Builder()
.setId("$it:$it")
.setSampleMimeType("audio/default")
.build()
}
val subtitles =
(1..3).map {
Format
.Builder()
.setId("${it + 3}:$it")
.setSampleMimeType("text/default")
.build()
}
(listOf(video) + audios + subtitles)
} else {
val video =
Format
.Builder()
.setId("1")
.setSampleMimeType("video/default")
.build()
val audios =
(2..4).map {
Format
.Builder()
.setId("$it")
.setSampleMimeType("audio/default")
.build()
}
val subtitles =
(5..7).map {
Format
.Builder()
.setId("$it")
.setSampleMimeType("text/default")
.build()
}
(listOf(video) + audios + subtitles)
}
val groups =
formats
.map { TrackGroup(it) }
.map { Tracks.Group(it, false, intArrayOf(C.FORMAT_HANDLED), booleanArrayOf(false)) }
return Tracks(groups)
}
/**
* Builds the tracks for the `no_embedded_subs.json` for the given backend
*
* Note: This is manual based on observation & code review of the playback for that file
*/
private fun buildNoEmbeddedTracks(backend: PlayerBackend): Tracks {
val formats =
if (backend == PlayerBackend.MPV) {
val video =
Format
.Builder()
.setId("0:1")
.setSampleMimeType("video/default")
.build()
val audios =
(1..3).map {
Format
.Builder()
.setId("$it:$it")
.setSampleMimeType("audio/default")
.build()
}
val subtitles =
(1..3).map {
Format
.Builder()
.setId("${it + 3}:$it")
.setSampleMimeType("text/default")
.build()
} +
listOf(
Format
.Builder()
.setId("7:e:4")
.setSampleMimeType("text/default")
.build(),
)
(listOf(video) + audios + subtitles)
} else {
// ExoPlayer
val video =
Format
.Builder()
.setId("0:1")
.setSampleMimeType("video/default")
.build()
val audios =
(2..4).map {
Format
.Builder()
.setId("0:$it")
.setSampleMimeType("audio/default")
.build()
}
val subtitles =
(5..7).map {
Format
.Builder()
.setId("0:$it")
.setSampleMimeType("text/default")
.build()
} +
listOf(
Format
.Builder()
.setId("1:e:0")
.setSampleMimeType("text/default")
.build(),
)
(listOf(video) + audios + subtitles)
}
val groups =
formats
.map { TrackGroup(it) }
.map { Tracks.Group(it, false, intArrayOf(C.FORMAT_HANDLED), booleanArrayOf(false)) }
return Tracks(groups)
}
/**
* Builds the tracks for the `external_subs.json` for the given backend.
*
* Must supply the desired subtitle index because ExoPlayer uses it.
*
* Note: This is manual based on observation & code review of the playback for that file
*/
private fun buildExternalTracks(
backend: PlayerBackend,
selectedIndex: Int,
): Tracks {
val formats =
if (backend == PlayerBackend.MPV) {
val video =
Format
.Builder()
.setId("1:1")
.setSampleMimeType("video/default")
.build()
val audios =
listOf(
Format
.Builder()
.setId("0:1")
.setSampleMimeType("audio/default")
.build(),
)
val subtitles =
listOf(
Format
.Builder()
.setId("2:1")
.setSampleMimeType("text/default")
.build(),
Format
.Builder()
.setId("3:e:2")
.setSampleMimeType("text/default")
.build(),
)
(listOf(video) + audios + subtitles)
} else {
val video =
Format
.Builder()
.setId("0:2")
.setSampleMimeType("video/default")
.build()
val audios =
listOf(
Format
.Builder()
.setId("0:1")
.setSampleMimeType("audio/default")
.build(),
)
val subtitles =
listOf(
Format
.Builder()
.setId("0:3") // Embedded
.setSampleMimeType("text/default")
.build(),
Format
.Builder()
.setId("1:e:$selectedIndex") // External
.setSampleMimeType("text/default")
.build(),
)
(listOf(video) + audios + subtitles)
}
val groups =
formats
.map { TrackGroup(it) }
.map { Tracks.Group(it, false, intArrayOf(C.FORMAT_HANDLED), booleanArrayOf(false)) }
return Tracks(groups)
}
private fun TrackSelectionParameters.getAudioOverride(): Format? {
this.overrides.forEach { (trackGroup, trackSelectionOverride) ->
if (trackGroup.type == C.TRACK_TYPE_AUDIO) {
return trackGroup.getFormat(trackSelectionOverride.trackIndices.first())
}
}
return null
}
private fun TrackSelectionParameters.getSubtitleOverride(): Format? {
this.overrides.forEach { (trackGroup, trackSelectionOverride) ->
if (trackGroup.type == C.TRACK_TYPE_TEXT) {
return trackGroup.getFormat(trackSelectionOverride.trackIndices.first())
}
}
return null
}
@Test
fun `test MPV embedded`() {
val resource = javaClass.classLoader?.getResource("embedded_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
val tracks = buildEmbeddedTracks(PlayerBackend.MPV)
Assert.assertEquals(7, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 1,
subtitleIndex = 4,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("1:1", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("4:1", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 2,
subtitleIndex = 4,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("2:2", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("4:1", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 1,
subtitleIndex = TrackIndex.DISABLED,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("1:1", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals(null, result.trackSelectionParameters.getSubtitleOverride()?.id)
}
}
@Test
fun `test ExoPlayer embedded`() {
val resource = javaClass.classLoader?.getResource("embedded_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
val tracks = buildEmbeddedTracks(PlayerBackend.EXO_PLAYER)
Assert.assertEquals(7, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 1,
subtitleIndex = 4,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("2", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("5", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 2,
subtitleIndex = 4,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("3", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("5", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 2,
subtitleIndex = 6,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("3", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("7", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 1,
subtitleIndex = TrackIndex.DISABLED,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("2", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals(null, result.trackSelectionParameters.getSubtitleOverride()?.id)
}
}
@Test
fun `test MPV no embedded`() {
val resource = javaClass.classLoader?.getResource("no_embedded_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
val tracks = buildNoEmbeddedTracks(PlayerBackend.MPV)
Assert.assertEquals(5, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 2,
subtitleIndex = 0,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("1:1", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("7:e:4", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 0,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("2:2", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("7:e:4", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
}
@Test
fun `test ExoPlayer no embedded`() {
val resource = javaClass.classLoader?.getResource("no_embedded_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
val tracks = buildNoEmbeddedTracks(PlayerBackend.EXO_PLAYER)
Assert.assertEquals(5, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 2,
subtitleIndex = 0,
source = source,
).also { result ->
Assert.assertTrue(result.bothSelected)
Assert.assertEquals("0:2", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("1:e:0", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
}
@Test
fun `test MPV external`() {
val resource = javaClass.classLoader?.getResource("external_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
val tracks = buildExternalTracks(PlayerBackend.MPV, 0)
Assert.assertEquals(6, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 0,
source = source,
).also { result ->
Assert.assertTrue(result.audioSelected)
Assert.assertTrue(result.subtitleSelected)
Assert.assertEquals("0:1", result.trackSelectionParameters.getAudioOverride()?.id)
Assert.assertEquals("3:e:2", result.trackSelectionParameters.getSubtitleOverride()?.id)
}
// Select embedded subtitles
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.MPV,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 5,
source = source,
).also { result ->
Assert.assertTrue(result.audioSelected)
Assert.assertTrue(result.subtitleSelected)
Assert.assertEquals(
"0:1",
result.trackSelectionParameters.getAudioOverride()?.id,
)
Assert.assertEquals(
"2:1",
result.trackSelectionParameters.getSubtitleOverride()?.id,
)
}
}
@Test
fun `test ExoPlayer external`() {
val resource = javaClass.classLoader?.getResource("external_subs.json")
Assert.assertNotNull(resource)
val fileContents = Paths.get(resource!!.toURI()).readText()
val source = Json.decodeFromString<MediaSourceInfo>(fileContents)
buildExternalTracks(PlayerBackend.EXO_PLAYER, 0).also { tracks ->
Assert.assertEquals(6, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 0,
source = source,
).also { result ->
Assert.assertTrue(result.audioSelected)
Assert.assertTrue(result.subtitleSelected)
Assert.assertEquals(
"0:1",
result.trackSelectionParameters.getAudioOverride()?.id,
)
Assert.assertEquals(
"1:e:0",
result.trackSelectionParameters.getSubtitleOverride()?.id,
)
}
// Select embedded subtitles
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 5,
source = source,
).also { result ->
Assert.assertTrue(result.audioSelected)
Assert.assertTrue(result.subtitleSelected)
Assert.assertEquals(
"0:1",
result.trackSelectionParameters.getAudioOverride()?.id,
)
Assert.assertEquals(
"0:3",
result.trackSelectionParameters.getSubtitleOverride()?.id,
)
}
}
buildExternalTracks(PlayerBackend.EXO_PLAYER, 2).also { tracks ->
Assert.assertEquals(6, source.mediaStreams?.size)
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = tracks,
playerBackend = PlayerBackend.EXO_PLAYER,
supportsDirectPlay = true,
audioIndex = 3,
subtitleIndex = 2,
source = source,
).also { result ->
Assert.assertTrue(result.audioSelected)
Assert.assertTrue(result.subtitleSelected)
Assert.assertEquals(
"0:1",
result.trackSelectionParameters.getAudioOverride()?.id,
)
Assert.assertEquals(
"1:e:2",
result.trackSelectionParameters.getSubtitleOverride()?.id,
)
}
}
}
}

View file

@ -0,0 +1,235 @@
{
"Protocol": "File",
"Id": "",
"Path": "",
"Type": "Default",
"Container": "mkv",
"Size": 651217902,
"Name": "",
"IsRemote": false,
"ETag": "",
"RunTimeTicks": 14919360000,
"ReadAtNativeFramerate": false,
"IgnoreDts": false,
"IgnoreIndex": false,
"GenPtsInput": false,
"SupportsTranscoding": true,
"SupportsDirectStream": true,
"SupportsDirectPlay": true,
"IsInfiniteStream": false,
"UseMostCompatibleTranscodingProfile": false,
"RequiresOpening": false,
"RequiresClosing": false,
"RequiresLooping": false,
"SupportsProbing": true,
"VideoType": "VideoFile",
"MediaStreams": [
{
"Codec": "hevc",
"Language": "jpn",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "SDR",
"VideoRangeType": "SDR",
"AudioSpatialFormat": "None",
"DisplayTitle": "1080p - HEVC - SDR",
"IsInterlaced": false,
"IsAVC": false,
"BitRate": 3491934,
"BitDepth": 10,
"RefFrames": 1,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 1080,
"Width": 1448,
"AverageFrameRate": 23.809525,
"RealFrameRate": 23.809525,
"ReferenceFrameRate": 23.809525,
"Profile": "Main 10",
"Type": "Video",
"AspectRatio": "4:3",
"Index": 0,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"PixelFormat": "yuv420p10le",
"Level": 120,
"IsAnamorphic": false
},
{
"Codec": "opus",
"Language": "jpn",
"TimeBase": "1/1000",
"Title": "JAP Stereo (Opus 112Kbps)",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "JAP Stereo (Opus 112Kbps) - Japanese",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "stereo",
"BitRate": 101618,
"Channels": 2,
"SampleRate": 48000,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Type": "Audio",
"Index": 1,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "aac",
"Language": "por",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "Portuguese - AAC - Stereo",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "stereo",
"BitRate": 249225,
"Channels": 2,
"SampleRate": 44100,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Profile": "LC",
"Type": "Audio",
"Index": 2,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "eac3",
"Language": "por",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "Portuguese - Dolby Digital+ - 5.1",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "5.1",
"BitRate": 640000,
"Channels": 6,
"SampleRate": 48000,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Type": "Audio",
"Index": 3,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "ass",
"Language": "por",
"TimeBase": "1/1000",
"Title": "ptBR",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "ptBR - Portuguese - Default - ASS",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 4,
"IsExternal": false,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Level": 0
},
{
"Codec": "ass",
"Language": "por",
"TimeBase": "1/1000",
"Title": "ptBR FORCED",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "ptBR FORCED - Portuguese - ASS",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": true,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 5,
"IsExternal": false,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Level": 0
},
{
"Codec": "ass",
"Language": "eng",
"TimeBase": "1/1000",
"Title": "enUS",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "enUS - English - ASS",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 6,
"IsExternal": false,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Level": 0
}
],
"MediaAttachments": [],
"Formats": [],
"Bitrate": 4482777,
"RequiredHttpHeaders": {},
"TranscodingSubProtocol": "http",
"DefaultAudioStreamIndex": 1,
"DefaultSubtitleStreamIndex": 6,
"HasSegments": true
}

View file

@ -0,0 +1,208 @@
{
"Protocol": "File",
"Id": "",
"Path": "",
"Type": "Default",
"Container": "mkv",
"Size": 2147179830,
"Name": "",
"IsRemote": false,
"ETag": "",
"RunTimeTicks": 12793200000,
"ReadAtNativeFramerate": false,
"IgnoreDts": false,
"IgnoreIndex": false,
"GenPtsInput": false,
"SupportsTranscoding": true,
"SupportsDirectStream": true,
"SupportsDirectPlay": true,
"IsInfiniteStream": false,
"UseMostCompatibleTranscodingProfile": false,
"RequiresOpening": false,
"RequiresClosing": false,
"RequiresLooping": false,
"SupportsProbing": true,
"VideoType": "VideoFile",
"MediaStreams": [
{
"Codec": "subrip",
"Language": "eng",
"TimeBase": "1/1000",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "English - SUBRIP - External",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 0,
"IsExternal": true,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Path": "",
"Level": 0
},
{
"Codec": "subrip",
"Language": "eng",
"TimeBase": "1/1000",
"Title": "0",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "0 - English - SUBRIP - External",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 1,
"IsExternal": true,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Path": "",
"Level": 0
},
{
"Codec": "subrip",
"Language": "eng",
"TimeBase": "1/1000",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "English - SUBRIP - External",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 2,
"IsExternal": true,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Path": "",
"Level": 0
},
{
"Codec": "eac3",
"Language": "eng",
"TimeBase": "1/1000",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "English - Dolby Digital+ - 5.1 - Default",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "5.1",
"BitRate": 640000,
"Channels": 6,
"SampleRate": 48000,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Type": "Audio",
"Index": 3,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "h264",
"ColorSpace": "bt709",
"ColorTransfer": "bt709",
"ColorPrimaries": "bt709",
"TimeBase": "1/1000",
"VideoRange": "SDR",
"VideoRangeType": "SDR",
"AudioSpatialFormat": "None",
"DisplayTitle": "1080p H264 SDR",
"NalLengthSize": "4",
"IsInterlaced": false,
"IsAVC": true,
"BitRate": 13427007,
"BitDepth": 8,
"RefFrames": 1,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 1080,
"Width": 1920,
"AverageFrameRate": 23.976025,
"RealFrameRate": 23.976025,
"ReferenceFrameRate": 23.976025,
"Profile": "High",
"Type": "Video",
"AspectRatio": "16:9",
"Index": 4,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"PixelFormat": "yuv420p",
"Level": 40,
"IsAnamorphic": false
},
{
"Codec": "subrip",
"TimeBase": "1/1000",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "Undefined - Default - SUBRIP",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 5,
"IsExternal": false,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Level": 0
}
],
"MediaAttachments": [],
"Formats": [],
"Bitrate": 14067007,
"RequiredHttpHeaders": {},
"TranscodingSubProtocol": "http",
"DefaultAudioStreamIndex": 3,
"DefaultSubtitleStreamIndex": 0,
"HasSegments": true
}

View file

@ -0,0 +1,178 @@
{
"Protocol": "File",
"Id": "",
"Path": "",
"Type": "Default",
"Container": "mkv",
"Size": 651217902,
"Name": "",
"IsRemote": false,
"ETag": "",
"RunTimeTicks": 14919360000,
"ReadAtNativeFramerate": false,
"IgnoreDts": false,
"IgnoreIndex": false,
"GenPtsInput": false,
"SupportsTranscoding": true,
"SupportsDirectStream": true,
"SupportsDirectPlay": true,
"IsInfiniteStream": false,
"UseMostCompatibleTranscodingProfile": false,
"RequiresOpening": false,
"RequiresClosing": false,
"RequiresLooping": false,
"SupportsProbing": true,
"VideoType": "VideoFile",
"MediaStreams": [
{
"Codec": "subrip",
"Language": "spa",
"TimeBase": "1/1000",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedUndefined": "Undefined",
"LocalizedDefault": "Default",
"LocalizedForced": "Forced",
"LocalizedExternal": "External",
"LocalizedHearingImpaired": "Hearing Impaired",
"DisplayTitle": "Spanish - SUBRIP - External",
"IsInterlaced": false,
"IsAVC": false,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 0,
"Width": 0,
"Type": "Subtitle",
"Index": 0,
"IsExternal": true,
"IsTextSubtitleStream": true,
"SupportsExternalStream": true,
"Path": "",
"Level": 0
},
{
"Codec": "hevc",
"Language": "jpn",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "SDR",
"VideoRangeType": "SDR",
"AudioSpatialFormat": "None",
"DisplayTitle": "1080p - HEVC - SDR",
"IsInterlaced": false,
"IsAVC": false,
"BitRate": 3491934,
"BitDepth": 10,
"RefFrames": 1,
"IsDefault": true,
"IsForced": false,
"IsHearingImpaired": false,
"Height": 1080,
"Width": 1448,
"AverageFrameRate": 23.809525,
"RealFrameRate": 23.809525,
"ReferenceFrameRate": 23.809525,
"Profile": "Main 10",
"Type": "Video",
"AspectRatio": "4:3",
"Index": 1,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"PixelFormat": "yuv420p10le",
"Level": 120,
"IsAnamorphic": false
},
{
"Codec": "opus",
"Language": "jpn",
"TimeBase": "1/1000",
"Title": "Stereo (Opus 112Kbps)",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "Stereo (Opus 112Kbps) - Japanese",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "stereo",
"BitRate": 101618,
"Channels": 2,
"SampleRate": 48000,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Type": "Audio",
"Index": 2,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "aac",
"Language": "por",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "Portuguese - AAC - Stereo",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "stereo",
"BitRate": 249225,
"Channels": 2,
"SampleRate": 44100,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Profile": "LC",
"Type": "Audio",
"Index": 3,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
},
{
"Codec": "eac3",
"Language": "por",
"TimeBase": "1/1000",
"Title": "",
"VideoRange": "Unknown",
"VideoRangeType": "Unknown",
"AudioSpatialFormat": "None",
"LocalizedDefault": "Default",
"LocalizedExternal": "External",
"DisplayTitle": "Portuguese - Dolby Digital+ - 5.1",
"IsInterlaced": false,
"IsAVC": false,
"ChannelLayout": "5.1",
"BitRate": 640000,
"Channels": 6,
"SampleRate": 48000,
"IsDefault": false,
"IsForced": false,
"IsHearingImpaired": false,
"Type": "Audio",
"Index": 4,
"IsExternal": false,
"IsTextSubtitleStream": false,
"SupportsExternalStream": false,
"Level": 0
}
],
"MediaAttachments": [],
"Formats": [],
"Bitrate": 4482777,
"RequiredHttpHeaders": {},
"TranscodingSubProtocol": "http",
"DefaultAudioStreamIndex": 2,
"HasSegments": true
}