I am attempting to take a set of URLs, and their metadata then using that to generate a DASH manifest i can pass on to my player. I've got the majority of the functionality done, the problem I'm having is every single track being marked as NO_UNSUPPORTED_TYPE
.
I've tested on multiple devices, and also tried with vp9 and ffmpeg decoders to confirm that it's not a device limitation.
I also tried a dash manifest online and it loads fine, fully supported. So the problem must be with my manifest generation.
This is my media source factory that I'm using to generate the manifest from a video ID:
@UnstableApi
class DashMediaSourceFactory(
val dataSourceFactory: DataSource.Factory,
private val repository: InnerTubeRepository
) : MediaSource.Factory {
private var drmSessionManagerProvider: DrmSessionManagerProvider =
DefaultDrmSessionManagerProvider()
private var loadErrorHandlingPolicy: LoadErrorHandlingPolicy = DefaultLoadErrorHandlingPolicy()
private val factory = DashMediaSource.Factory(dataSourceFactory)
.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
.setDrmSessionManagerProvider(drmSessionManagerProvider)
override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy) =
apply {
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy
}
override fun setDrmSessionManagerProvider(drmSessionManagerProvider: DrmSessionManagerProvider) =
apply {
this.drmSessionManagerProvider = drmSessionManagerProvider
}
override fun createMediaSource(mediaItem: MediaItem): MediaSource {
val res = runBlocking(Dispatchers.IO) {
repository.getVideo(mediaItem.mediaId)
}
val cpn = generateCpn()
val groups = res.formats.groupBy(DomainFormat::mimeType)
val sets = groups.entries.mapIndexed { index, (_, items) ->
if (items.first() is DomainFormat.Audio && (items.first() as DomainFormat.Audio).audioTrack != null) {
AdaptationSet(
/* id= */ index,
/* type = */ C.TRACK_TYPE_DEFAULT, // maybe the cause of the issue?
/* representations = */ items.map { it.generateRepresentation(cpn) },
/* accessibilityDescriptors = */ emptyList(),
/* essentialProperties = */ emptyList(),
/* supplementalProperties = */ emptyList(),
)
} else {
AdaptationSet(
/* id= */ index,
/* type = */ C.TRACK_TYPE_DEFAULT, // maybe the cause of the issue?
/* representations = */ items.map { it.generateRepresentation(cpn) },
/* accessibilityDescriptors = */ emptyList(),
/* essentialProperties = */ emptyList(),
/* supplementalProperties = */ emptyList(),
)
}
}
val manifest = DashManifest(
/* availabilityStartTimeMs = */ C.TIME_UNSET,
/* durationMs = */ res.formats.first().approxDurationMs,
/* minBufferTimeMs = */ 1500,
/* dynamic = */ false,
/* minUpdatePeriodMs = */ C.TIME_UNSET,
/* timeShiftBufferDepthMs = */ C.TIME_UNSET,
/* suggestedPresentationDelayMs = */ C.TIME_UNSET,
/* publishTimeMs = */ C.TIME_UNSET,
/* programInformation = */ null,
/* utcTiming = */ null,
/* serviceDescription = */ null,
/* location = */ null,
/* periods = */ listOf(Period(null, 0, sets))
)
return factory.createMediaSource(manifest)
}
override fun getSupportedTypes() = intArrayOf(C.CONTENT_TYPE_DASH, C.CONTENT_TYPE_OTHER)
private fun generateCpn(): String {
val chars = ('A'..'Z').toList()
return List(16) { chars.random() }.joinToString("")
}
private fun DomainFormat.generateRepresentation(cpn: String) = when (this) {
is DomainFormat.Video -> generateRepresentation(cpn)
is DomainFormat.Audio -> generateRepresentation(cpn)
else -> throw IllegalArgumentException("Unknown format type: $this")
}
private fun DomainFormat.Video.generateRepresentation(cpn: String) = generateRepresentation(
formatBuilder = Format.Builder()
.setWidth(width)
.setHeight(height)
.setFrameRate(fps),
cpn = cpn
)
private fun DomainFormat.Audio.generateRepresentation(cpn: String) = generateRepresentation(
formatBuilder = Format.Builder()
.setChannelCount(audioChannels)
.setSampleRate(audioSampleRate),
cpn = cpn
)
private fun DomainFormat.generateRepresentation(
formatBuilder: Format.Builder,
cpn: String
): Representation {
// "video/webm; codecs=\"vp9\""
val (mimeType, end) = mimeType.split("; codecs=\"")
val codecs = end.dropLast(1)
val uri = Uri.Builder()
.path(url)
.appendQueryParameter("cpn", cpn)
.build()
.toString()
val segmentBase = SegmentBase.SingleSegmentBase(
/* initialization = */ RangedUri(
null,
initRange.start,
initRange.endInclusive
),
/* timescale = */ 1,
/* presentationTimeOffset = */ 0,
/* indexStart = */ indexRange.start,
/* indexLength = */ indexRange.endInclusive
)
return Representation.newInstance(
/* revisionId = */ Representation.REVISION_ID_DEFAULT,
/* format = */ formatBuilder
.setId(itag)
.setCodecs(codecs)
.setAverageBitrate(averageBitrate)
.setPeakBitrate(bitrate)
.setSampleMimeType(mimeType)
.build(),
/* baseUrls = */ listOf(BaseUrl(uri)),
/* segmentBase = */ segmentBase
)
}
}
And I pass it to my player in its builder like this
.setMediaSourceFactory(
DashMediaSourceFactory(
dataSourceFactory = CacheDataSource.Factory()
.setCache(cacheManager.cache)
.setCacheWriteDataSinkFactory(
CacheDataSink.Factory().setCache(cacheManager.cache)
)
.setUpstreamDataSourceFactory(DefaultHttpDataSource.Factory())
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR),
repository = innerTube
)
)
This is what gets logged by the EventLogger
tracks [eventTime=1.91, mediaPos=0.00, window=0, period=0
group [
[ ] Track:0, id=299, mimeType=video/mp4, bitrate=5849715, codecs=avc1.64002a, res=1920x1080, fps=60.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=303, mimeType=video/webm, bitrate=4433719, codecs=vp9, res=1920x1080, fps=60.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:1, id=247, mimeType=video/webm, bitrate=1537674, codecs=vp9, res=1280x720, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:2, id=302, mimeType=video/webm, bitrate=2585000, codecs=vp9, res=1280x720, fps=60.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:3, id=244, mimeType=video/webm, bitrate=764157, codecs=vp9, res=854x480, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:4, id=243, mimeType=video/webm, bitrate=422195, codecs=vp9, res=640x360, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:5, id=242, mimeType=video/webm, bitrate=233294, codecs=vp9, res=426x240, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:6, id=278, mimeType=video/webm, bitrate=106957, codecs=vp9, res=256x144, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:7, id=598, mimeType=video/webm, bitrate=42902, codecs=vp9, res=256x144, fps=15.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=399, mimeType=video/mp4, bitrate=5675869, codecs=av01.0.09M.08, res=1920x1080, fps=60.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=136, mimeType=video/mp4, bitrate=3181485, codecs=avc1.4d401f, res=1280x720, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:1, id=135, mimeType=video/mp4, bitrate=893048, codecs=avc1.4d401f, res=854x480, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=298, mimeType=video/mp4, bitrate=1776967, codecs=avc1.4d4020, res=1280x720, fps=60.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=398, mimeType=video/mp4, bitrate=2819550, codecs=av01.0.08M.08, res=1280x720, fps=60.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=397, mimeType=video/mp4, bitrate=832484, codecs=av01.0.04M.08, res=854x480, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=134, mimeType=video/mp4, bitrate=653038, codecs=avc1.4d401e, res=640x360, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=396, mimeType=video/mp4, bitrate=406931, codecs=av01.0.01M.08, res=640x360, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=133, mimeType=video/mp4, bitrate=305782, codecs=avc1.4d4015, res=426x240, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=395, mimeType=video/mp4, bitrate=222810, codecs=av01.0.00M.08, res=426x240, fps=30.0, supported=NO_UNSUPPORTED_TYPE
[ ] Track:1, id=394, mimeType=video/mp4, bitrate=109692, codecs=av01.0.00M.08, res=256x144, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=160, mimeType=video/mp4, bitrate=116852, codecs=avc1.4d400c, res=256x144, fps=30.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=597, mimeType=video/mp4, bitrate=41785, codecs=avc1.4d400b, res=256x144, fps=15.0, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=140, mimeType=audio/mp4, bitrate=131231, codecs=mp4a.40.2, channels=2, sample_rate=44100, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=249, mimeType=audio/webm, bitrate=59339, codecs=opus, channels=2, sample_rate=48000, supported=NO_UNSUPPORTED_TYPE
[ ] Track:1, id=250, mimeType=audio/webm, bitrate=77919, codecs=opus, channels=2, sample_rate=48000, supported=NO_UNSUPPORTED_TYPE
[ ] Track:2, id=251, mimeType=audio/webm, bitrate=151404, codecs=opus, channels=2, sample_rate=48000, supported=NO_UNSUPPORTED_TYPE
[ ] Track:3, id=600, mimeType=audio/webm, bitrate=40891, codecs=opus, channels=2, sample_rate=48000, supported=NO_UNSUPPORTED_TYPE
]
group [
[ ] Track:0, id=599, mimeType=audio/mp4, bitrate=32824, codecs=mp4a.40.5, channels=2, sample_rate=22050, supported=NO_UNSUPPORTED_TYPE
]
]
NO_UNSUPPORTED_TYPE seems to imply that audio/mp4, video/mp4, etc is unsupported, which doesn't really make sense