merge StreamVariant and stream part 2
This commit is contained in:
parent
ac97d4ee1f
commit
1b4e1d4f13
|
@ -2,18 +2,14 @@ package net.newpipe.newplayer.utils
|
||||||
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Tracks
|
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.datasource.HttpDataSource
|
import androidx.media3.datasource.HttpDataSource
|
||||||
import androidx.media3.exoplayer.dash.DashMediaSource
|
import androidx.media3.exoplayer.dash.DashMediaSource
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
|
||||||
import androidx.media3.exoplayer.source.MediaSource
|
|
||||||
import androidx.media3.exoplayer.source.MergingMediaSource
|
|
||||||
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import net.newpipe.newplayer.MediaRepository
|
import net.newpipe.newplayer.MediaRepository
|
||||||
import net.newpipe.newplayer.StreamType
|
import net.newpipe.newplayer.StreamType
|
||||||
import net.newpipe.newplayer.StreamVariant
|
import net.newpipe.newplayer.Stream
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class MediaSourceBuilder(
|
class MediaSourceBuilder(
|
||||||
|
@ -23,31 +19,30 @@ class MediaSourceBuilder(
|
||||||
private val httpDataSourceFactory: HttpDataSource.Factory
|
private val httpDataSourceFactory: HttpDataSource.Factory
|
||||||
) {
|
) {
|
||||||
suspend fun buildMediaSource(item: String) {
|
suspend fun buildMediaSource(item: String) {
|
||||||
val availableStreamVariants = repository.getAvailableStreamVariants(item)
|
val availableStreams = repository.getStreams(item)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
private suspend
|
private
|
||||||
fun toMediaItem(item: String, streamVariant: StreamVariant): MediaItem {
|
fun toMediaItem(item: String, stream: Stream): MediaItem {
|
||||||
val dataStream = repository.getStream(item, streamVariant)
|
|
||||||
|
|
||||||
val uniqueId = Random.nextLong()
|
val uniqueId = Random.nextLong()
|
||||||
uniqueIdToIdLookup[uniqueId] = item
|
uniqueIdToIdLookup[uniqueId] = item
|
||||||
val mediaItemBuilder = MediaItem.Builder()
|
val mediaItemBuilder = MediaItem.Builder()
|
||||||
.setMediaId(uniqueId.toString())
|
.setMediaId(uniqueId.toString())
|
||||||
.setUri(dataStream.streamUri)
|
.setUri(stream.streamUri)
|
||||||
|
|
||||||
if (dataStream.mimeType != null) {
|
if (stream.mimeType != null) {
|
||||||
mediaItemBuilder.setMimeType(dataStream.mimeType)
|
mediaItemBuilder.setMimeType(stream.mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mediaItemBuilder.build()
|
return mediaItemBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
private fun toMediaSource(mediaItem: MediaItem, streamVariant: StreamVariant) =
|
private fun toMediaSource(mediaItem: MediaItem, stream: Stream) =
|
||||||
if (streamVariant.streamType == StreamType.DYNAMIC)
|
if (stream.streamType == StreamType.DYNAMIC)
|
||||||
DashMediaSource.Factory(httpDataSourceFactory)
|
DashMediaSource.Factory(httpDataSourceFactory)
|
||||||
.createMediaSource(mediaItem)
|
.createMediaSource(mediaItem)
|
||||||
else
|
else
|
||||||
|
|
|
@ -4,29 +4,29 @@ package net.newpipe.newplayer.utils
|
||||||
import net.newpipe.newplayer.NewPlayerException
|
import net.newpipe.newplayer.NewPlayerException
|
||||||
import net.newpipe.newplayer.PlayMode
|
import net.newpipe.newplayer.PlayMode
|
||||||
import net.newpipe.newplayer.StreamType
|
import net.newpipe.newplayer.StreamType
|
||||||
import net.newpipe.newplayer.StreamVariant
|
import net.newpipe.newplayer.Stream
|
||||||
|
|
||||||
object StreamSelect {
|
object StreamSelect {
|
||||||
|
|
||||||
interface StreamSelection
|
interface StreamSelection
|
||||||
|
|
||||||
data class SingleSelection(
|
data class SingleSelection(
|
||||||
val streamVariant: StreamVariant
|
val stream: Stream
|
||||||
) : StreamSelection
|
) : StreamSelection
|
||||||
|
|
||||||
data class MultiSelection(
|
data class MultiSelection(
|
||||||
val videoStream: StreamVariant,
|
val videoStream: Stream,
|
||||||
val audioStream: StreamVariant
|
val audioStream: Stream
|
||||||
) : StreamSelection
|
) : StreamSelection
|
||||||
|
|
||||||
|
|
||||||
private fun getBestLanguageFit(
|
private fun getBestLanguageFit(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
preferredLanguages: List<String>
|
preferredLanguages: List<String>
|
||||||
): String? {
|
): String? {
|
||||||
for (preferredLanguage in preferredLanguages) {
|
for (preferredLanguage in preferredLanguages) {
|
||||||
for (availableVariant in availableStreamVariants) {
|
for (available in availableStreams) {
|
||||||
if (availableVariant.language == preferredLanguage) {
|
if (available.language == preferredLanguage) {
|
||||||
return preferredLanguage
|
return preferredLanguage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,21 +34,21 @@ object StreamSelect {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filterVariantsByLanguage(
|
private fun filtersByLanguage(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
language: String
|
language: String
|
||||||
) =
|
) =
|
||||||
availableStreamVariants.filter { it.language == language }
|
availableStreams.filter { it.language == language }
|
||||||
|
|
||||||
private fun getBestFittingVideoIdentifier(
|
private fun getBestFittingVideoIdentifier(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
preferredVideoIdentifier: List<String>
|
preferredVideoIdentifier: List<String>
|
||||||
): String? {
|
): String? {
|
||||||
for (preferredStream in preferredVideoIdentifier) {
|
for (preferredStream in preferredVideoIdentifier) {
|
||||||
for (availableVariant in availableStreamVariants) {
|
for (available in availableStreams) {
|
||||||
if ((availableVariant.streamType == StreamType.AUDIO_AND_VIDEO ||
|
if ((available.streamType == StreamType.AUDIO_AND_VIDEO ||
|
||||||
availableVariant.streamType == StreamType.VIDEO)
|
available.streamType == StreamType.VIDEO)
|
||||||
&& preferredStream == availableVariant.streamVariantIdentifier
|
&& preferredStream == available.identifier
|
||||||
) {
|
) {
|
||||||
return preferredStream
|
return preferredStream
|
||||||
}
|
}
|
||||||
|
@ -57,25 +57,25 @@ object StreamSelect {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFirstVariantMatchingIdentifier(
|
private fun getFirstMatchingIdentifier(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
identifier: String
|
identifier: String
|
||||||
): StreamVariant? {
|
): Stream? {
|
||||||
for (variant in availableStreamVariants) {
|
for (variant in availableStreams) {
|
||||||
if (variant.streamVariantIdentifier == identifier)
|
if (variant.identifier == identifier)
|
||||||
return variant
|
return variant
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBestFittingAudioVariant(
|
private fun getBestFittingAudio(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
preferredAudioIdentifier: List<String>
|
preferredAudioIdentifier: List<String>
|
||||||
): StreamVariant? {
|
): Stream? {
|
||||||
for (preferredStream in preferredAudioIdentifier) {
|
for (preferredStream in preferredAudioIdentifier) {
|
||||||
for (availableStream in availableStreamVariants) {
|
for (availableStream in availableStreams) {
|
||||||
if (availableStream.streamType == StreamType.AUDIO
|
if (availableStream.streamType == StreamType.AUDIO
|
||||||
&& preferredStream == availableStream.streamVariantIdentifier
|
&& preferredStream == availableStream.identifier
|
||||||
) {
|
) {
|
||||||
return availableStream
|
return availableStream
|
||||||
}
|
}
|
||||||
|
@ -84,21 +84,21 @@ object StreamSelect {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVideoOnlyVariantWithMatchingIdentifier(
|
private fun getVideoOnlyWithMatchingIdentifier(
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
identifier: String
|
identifier: String
|
||||||
): StreamVariant? {
|
): Stream? {
|
||||||
for (variant in availableStreamVariants) {
|
for (variant in availableStreams) {
|
||||||
if (variant.streamType == StreamType.VIDEO
|
if (variant.streamType == StreamType.VIDEO
|
||||||
&& variant.streamVariantIdentifier == identifier
|
&& variant.identifier == identifier
|
||||||
)
|
)
|
||||||
return variant
|
return variant
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDynamicStream(availableStreamVariants: List<StreamVariant>): StreamVariant? {
|
private fun getDynamicStream(availableStreams: List<Stream>): Stream? {
|
||||||
for (variant in availableStreamVariants) {
|
for (variant in availableStreams) {
|
||||||
if (variant.streamType == StreamType.DYNAMIC) {
|
if (variant.streamType == StreamType.DYNAMIC) {
|
||||||
return variant
|
return variant
|
||||||
}
|
}
|
||||||
|
@ -106,16 +106,16 @@ object StreamSelect {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getNonDynamicVideoVariants(availableStreamVariants: List<StreamVariant>) =
|
private fun getNonDynamicVideos(availableStreams: List<Stream>) =
|
||||||
availableStreamVariants.filter {
|
availableStreams.filter {
|
||||||
it.streamType == StreamType.VIDEO || it.streamType == StreamType.AUDIO_AND_VIDEO
|
it.streamType == StreamType.VIDEO || it.streamType == StreamType.AUDIO_AND_VIDEO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getNonDynamicAudioVariants(availableStreamVariants: List<StreamVariant>) =
|
private fun getNonDynamicAudios(availableStreams: List<Stream>) =
|
||||||
availableStreamVariants.filter { it.streamType == StreamType.AUDIO }
|
availableStreams.filter { it.streamType == StreamType.AUDIO }
|
||||||
|
|
||||||
private fun hasVideoStreamVariants(availableStreamVariants: List<StreamVariant>): Boolean {
|
private fun hasVideoStreams(availableStreams: List<Stream>): Boolean {
|
||||||
for (variant in availableStreamVariants) {
|
for (variant in availableStreams) {
|
||||||
if (variant.streamType == StreamType.AUDIO_AND_VIDEO || variant.streamType == StreamType.VIDEO || variant.streamType == StreamType.DYNAMIC)
|
if (variant.streamType == StreamType.AUDIO_AND_VIDEO || variant.streamType == StreamType.VIDEO || variant.streamType == StreamType.DYNAMIC)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ object StreamSelect {
|
||||||
fun selectStream(
|
fun selectStream(
|
||||||
item: String,
|
item: String,
|
||||||
playMode: PlayMode,
|
playMode: PlayMode,
|
||||||
availableStreamVariants: List<StreamVariant>,
|
availableStreams: List<Stream>,
|
||||||
preferredVideoIdentifier: List<String>,
|
preferredVideoIdentifier: List<String>,
|
||||||
preferredAudioIdentifier: List<String>,
|
preferredAudioIdentifier: List<String>,
|
||||||
preferredLanguage: List<String>
|
preferredLanguage: List<String>
|
||||||
|
@ -133,10 +133,10 @@ object StreamSelect {
|
||||||
|
|
||||||
// filter for best fitting language stream variants
|
// filter for best fitting language stream variants
|
||||||
|
|
||||||
val bestFittingLanguage = getBestLanguageFit(availableStreamVariants, preferredLanguage)
|
val bestFittingLanguage = getBestLanguageFit(availableStreams, preferredLanguage)
|
||||||
val availableVariantsInPreferredLanguage =
|
val availablesInPreferredLanguage =
|
||||||
if (bestFittingLanguage != null) filterVariantsByLanguage(
|
if (bestFittingLanguage != null) filtersByLanguage(
|
||||||
availableStreamVariants,
|
availableStreams,
|
||||||
bestFittingLanguage
|
bestFittingLanguage
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
|
@ -145,12 +145,12 @@ object StreamSelect {
|
||||||
|
|
||||||
|
|
||||||
// is it a video stream or a pure audio stream?
|
// is it a video stream or a pure audio stream?
|
||||||
if (hasVideoStreamVariants(availableStreamVariants)) {
|
if (hasVideoStreams(availableStreams)) {
|
||||||
|
|
||||||
// first: try and get a dynamic stream variant
|
// first: try and get a dynamic stream variant
|
||||||
getDynamicStream(availableVariantsInPreferredLanguage)
|
getDynamicStream(availablesInPreferredLanguage)
|
||||||
?: getDynamicStream(
|
?: getDynamicStream(
|
||||||
availableStreamVariants
|
availableStreams
|
||||||
)?.let {
|
)?.let {
|
||||||
return SingleSelection(it)
|
return SingleSelection(it)
|
||||||
}
|
}
|
||||||
|
@ -159,35 +159,35 @@ object StreamSelect {
|
||||||
|
|
||||||
val bestVideoIdentifier =
|
val bestVideoIdentifier =
|
||||||
getBestFittingVideoIdentifier(
|
getBestFittingVideoIdentifier(
|
||||||
availableVariantsInPreferredLanguage,
|
availablesInPreferredLanguage,
|
||||||
preferredVideoIdentifier
|
preferredVideoIdentifier
|
||||||
)?.let {
|
)?.let {
|
||||||
val videoVariants =
|
val videos =
|
||||||
getNonDynamicVideoVariants(availableVariantsInPreferredLanguage)
|
getNonDynamicVideos(availablesInPreferredLanguage)
|
||||||
videoVariants[videoVariants.size / 2].streamVariantIdentifier
|
videos[videos.size / 2].identifier
|
||||||
} ?: getBestFittingVideoIdentifier(
|
} ?: getBestFittingVideoIdentifier(
|
||||||
availableStreamVariants,
|
availableStreams,
|
||||||
preferredVideoIdentifier
|
preferredVideoIdentifier
|
||||||
)
|
)
|
||||||
?: run {
|
?: run {
|
||||||
val videoVariants = getNonDynamicVideoVariants(availableStreamVariants)
|
val videos = getNonDynamicVideos(availableStreams)
|
||||||
videoVariants[videoVariants.size / 2].streamVariantIdentifier
|
videos[videos.size / 2].identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
val videoOnlyStream =
|
val videoOnlyStream =
|
||||||
getVideoOnlyVariantWithMatchingIdentifier(
|
getVideoOnlyWithMatchingIdentifier(
|
||||||
availableVariantsInPreferredLanguage,
|
availablesInPreferredLanguage,
|
||||||
bestVideoIdentifier
|
bestVideoIdentifier
|
||||||
) ?: getVideoOnlyVariantWithMatchingIdentifier(
|
) ?: getVideoOnlyWithMatchingIdentifier(
|
||||||
availableStreamVariants,
|
availableStreams,
|
||||||
bestVideoIdentifier
|
bestVideoIdentifier
|
||||||
)
|
)
|
||||||
|
|
||||||
if (videoOnlyStream != null) {
|
if (videoOnlyStream != null) {
|
||||||
getBestFittingAudioVariant(
|
getBestFittingAudio(
|
||||||
availableVariantsInPreferredLanguage,
|
availablesInPreferredLanguage,
|
||||||
preferredAudioIdentifier
|
preferredAudioIdentifier
|
||||||
) ?: getBestFittingAudioVariant(availableStreamVariants, preferredAudioIdentifier)
|
) ?: getBestFittingAudio(availableStreams, preferredAudioIdentifier)
|
||||||
?.let {
|
?.let {
|
||||||
return MultiSelection(videoOnlyStream, it)
|
return MultiSelection(videoOnlyStream, it)
|
||||||
}
|
}
|
||||||
|
@ -195,12 +195,12 @@ object StreamSelect {
|
||||||
|
|
||||||
// fourth: try to get a video and audio stream variant with the best fitting identifier
|
// fourth: try to get a video and audio stream variant with the best fitting identifier
|
||||||
|
|
||||||
getFirstVariantMatchingIdentifier(
|
getFirstMatchingIdentifier(
|
||||||
availableVariantsInPreferredLanguage,
|
availablesInPreferredLanguage,
|
||||||
bestVideoIdentifier
|
bestVideoIdentifier
|
||||||
)
|
)
|
||||||
?: getFirstVariantMatchingIdentifier(
|
?: getFirstMatchingIdentifier(
|
||||||
availableStreamVariants,
|
availableStreams,
|
||||||
bestVideoIdentifier
|
bestVideoIdentifier
|
||||||
)?.let {
|
)?.let {
|
||||||
return SingleSelection(it)
|
return SingleSelection(it)
|
||||||
|
@ -209,28 +209,28 @@ object StreamSelect {
|
||||||
// fifth: try and get the median video and audio stream variant
|
// fifth: try and get the median video and audio stream variant
|
||||||
|
|
||||||
return SingleSelection(run {
|
return SingleSelection(run {
|
||||||
val videoVariants =
|
val videos =
|
||||||
getNonDynamicVideoVariants(availableVariantsInPreferredLanguage).ifEmpty {
|
getNonDynamicVideos(availablesInPreferredLanguage).ifEmpty {
|
||||||
getNonDynamicVideoVariants(availableStreamVariants)
|
getNonDynamicVideos(availableStreams)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoVariants.isNotEmpty()) {
|
if (videos.isNotEmpty()) {
|
||||||
return@run videoVariants[videoVariants.size / 2]
|
return@run videos[videos.size / 2]
|
||||||
} else {
|
} else {
|
||||||
throw NewPlayerException("No fitting video stream could be found for stream item item: ${item}")
|
throw NewPlayerException("No fitting video stream could be found for stream item item: ${item}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
} else { /* if(hasVideoStreamVariants(availableStreamVariants)) */
|
} else { /* if(hasVideoStreams(availableStreams)) */
|
||||||
|
|
||||||
// first: try to get an audio stream variant with the best fitting identifier
|
// first: try to get an audio stream variant with the best fitting identifier
|
||||||
|
|
||||||
getBestFittingAudioVariant(
|
getBestFittingAudio(
|
||||||
availableVariantsInPreferredLanguage,
|
availablesInPreferredLanguage,
|
||||||
preferredAudioIdentifier
|
preferredAudioIdentifier
|
||||||
)
|
)
|
||||||
?: getBestFittingAudioVariant(
|
?: getBestFittingAudio(
|
||||||
availableStreamVariants,
|
availableStreams,
|
||||||
preferredAudioIdentifier
|
preferredAudioIdentifier
|
||||||
)?.let {
|
)?.let {
|
||||||
return SingleSelection(it)
|
return SingleSelection(it)
|
||||||
|
@ -239,16 +239,16 @@ object StreamSelect {
|
||||||
// second: try and get the median audio stream variant
|
// second: try and get the median audio stream variant
|
||||||
|
|
||||||
return SingleSelection(run {
|
return SingleSelection(run {
|
||||||
val audioVariants =
|
val audios =
|
||||||
getNonDynamicAudioVariants(availableVariantsInPreferredLanguage).let {
|
getNonDynamicAudios(availablesInPreferredLanguage).let {
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
it
|
it
|
||||||
} else {
|
} else {
|
||||||
getNonDynamicAudioVariants(availableStreamVariants)
|
getNonDynamicAudios(availableStreams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (audioVariants.isNotEmpty()) {
|
if (audios.isNotEmpty()) {
|
||||||
return@run audioVariants[audioVariants.size / 2]
|
return@run audios[audios.size / 2]
|
||||||
} else {
|
} else {
|
||||||
throw NewPlayerException("No fitting audio stream could be found for stream item item: ${item}")
|
throw NewPlayerException("No fitting audio stream could be found for stream item item: ${item}")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue