mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Improved video loading speed by fixing regression caused by ProgressiveMediaSource
This commit is contained in:
parent
ff63d54412
commit
e04e6e686e
2 changed files with 68 additions and 76 deletions
|
@ -7,12 +7,10 @@ import android.util.Log
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.*
|
||||||
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory
|
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource
|
import com.google.android.exoplayer2.source.MergingMediaSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
import com.google.android.exoplayer2.source.SingleSampleMediaSource
|
||||||
import com.google.android.exoplayer2.text.SubtitleDecoderFactory
|
import com.google.android.exoplayer2.text.TextRenderer
|
||||||
import com.google.android.exoplayer2.text.SubtitleExtractor
|
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelector
|
import com.google.android.exoplayer2.trackselection.TrackSelector
|
||||||
import com.google.android.exoplayer2.ui.SubtitleView
|
import com.google.android.exoplayer2.ui.SubtitleView
|
||||||
|
@ -37,7 +35,7 @@ const val TAG = "CS3ExoPlayer"
|
||||||
|
|
||||||
/** Cache */
|
/** Cache */
|
||||||
|
|
||||||
class CS3IPlayer : IPlayer {
|
class CS3IPlayer() : IPlayer {
|
||||||
private var isPlaying = false
|
private var isPlaying = false
|
||||||
private var exoPlayer: ExoPlayer? = null
|
private var exoPlayer: ExoPlayer? = null
|
||||||
var cacheSize = 300L * 1024L * 1024L // 300 mb
|
var cacheSize = 300L * 1024L * 1024L // 300 mb
|
||||||
|
@ -373,7 +371,7 @@ class CS3IPlayer : IPlayer {
|
||||||
private fun buildExoPlayer(
|
private fun buildExoPlayer(
|
||||||
context: Context,
|
context: Context,
|
||||||
mediaItem: MediaItem,
|
mediaItem: MediaItem,
|
||||||
subSources: List<ProgressiveMediaSource>,
|
subSources: List<SingleSampleMediaSource>,
|
||||||
currentWindow: Int,
|
currentWindow: Int,
|
||||||
playbackPosition: Long,
|
playbackPosition: Long,
|
||||||
playBackSpeed: Float,
|
playBackSpeed: Float,
|
||||||
|
@ -383,6 +381,21 @@ class CS3IPlayer : IPlayer {
|
||||||
): ExoPlayer {
|
): ExoPlayer {
|
||||||
val exoPlayerBuilder =
|
val exoPlayerBuilder =
|
||||||
ExoPlayer.Builder(context)
|
ExoPlayer.Builder(context)
|
||||||
|
.setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput ->
|
||||||
|
DefaultRenderersFactory(context).createRenderers(
|
||||||
|
eventHandler,
|
||||||
|
videoRendererEventListener,
|
||||||
|
audioRendererEventListener,
|
||||||
|
textRendererOutput,
|
||||||
|
metadataRendererOutput
|
||||||
|
).map {
|
||||||
|
if (it is TextRenderer) TextRenderer(
|
||||||
|
textRendererOutput,
|
||||||
|
eventHandler.looper,
|
||||||
|
CustomSubtitleDecoderFactory()
|
||||||
|
) else it
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
.setTrackSelector(trackSelector ?: getTrackSelector(context))
|
.setTrackSelector(trackSelector ?: getTrackSelector(context))
|
||||||
|
|
||||||
val videoMediaSource =
|
val videoMediaSource =
|
||||||
|
@ -472,7 +485,7 @@ class CS3IPlayer : IPlayer {
|
||||||
private fun loadExo(
|
private fun loadExo(
|
||||||
context: Context,
|
context: Context,
|
||||||
mediaItem: MediaItem,
|
mediaItem: MediaItem,
|
||||||
subSources: List<ProgressiveMediaSource>,
|
subSources: List<SingleSampleMediaSource>,
|
||||||
cacheFactory: CacheDataSource.Factory? = null
|
cacheFactory: CacheDataSource.Factory? = null
|
||||||
) {
|
) {
|
||||||
Log.i(TAG, "loadExo")
|
Log.i(TAG, "loadExo")
|
||||||
|
@ -595,68 +608,6 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/google/ExoPlayer/blob/029a2b27cbdc27cf9d51d4a73ebeb503968849f6/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java
|
|
||||||
* */
|
|
||||||
private fun createProgressiveMediaSources(
|
|
||||||
subHelper: PlayerSubtitleHelper,
|
|
||||||
offlineSourceFactory: DataSource.Factory?,
|
|
||||||
onlineSourceFactory: DataSource.Factory?,
|
|
||||||
): Pair<List<SubtitleData>, List<ProgressiveMediaSource>> {
|
|
||||||
val activeSubtitles = ArrayList<SubtitleData>()
|
|
||||||
|
|
||||||
return Pair(activeSubtitles, subHelper.getAllSubtitles().mapNotNull { sub ->
|
|
||||||
val format = Format.Builder()
|
|
||||||
.setSampleMimeType(sub.mimeType)
|
|
||||||
.setLanguage("_${sub.name}")
|
|
||||||
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val ownFactory = CustomSubtitleDecoderFactory()
|
|
||||||
val extractorFactory = ExtractorsFactory {
|
|
||||||
arrayOf(
|
|
||||||
if (ownFactory.supportsFormat(format)) {
|
|
||||||
SubtitleExtractor(
|
|
||||||
ownFactory.createDecoder(format), format
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if (SubtitleDecoderFactory.DEFAULT.supportsFormat(format)) {
|
|
||||||
SubtitleExtractor(
|
|
||||||
SubtitleDecoderFactory.DEFAULT.createDecoder(format), format
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// ye we guess if not found instead of using UnknownSubtitlesExtractor,
|
|
||||||
// this way you can hopefully load a .txt file that is an srt and it will work
|
|
||||||
SubtitleExtractor(
|
|
||||||
ownFactory.createDecoder(format), format
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val factory = when (sub.origin) {
|
|
||||||
SubtitleOrigin.DOWNLOADED_FILE -> {
|
|
||||||
activeSubtitles.add(sub)
|
|
||||||
offlineSourceFactory
|
|
||||||
}
|
|
||||||
SubtitleOrigin.URL -> {
|
|
||||||
activeSubtitles.add(sub)
|
|
||||||
onlineSourceFactory
|
|
||||||
}
|
|
||||||
SubtitleOrigin.OPEN_SUBTITLES -> {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} ?: return@mapNotNull null
|
|
||||||
|
|
||||||
return@mapNotNull ProgressiveMediaSource.Factory(factory, extractorFactory)
|
|
||||||
.createMediaSource(
|
|
||||||
MediaItem.fromUri(sub.url)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadOfflinePlayer(context: Context, data: ExtractorUri) {
|
private fun loadOfflinePlayer(context: Context, data: ExtractorUri) {
|
||||||
Log.i(TAG, "loadOfflinePlayer")
|
Log.i(TAG, "loadOfflinePlayer")
|
||||||
try {
|
try {
|
||||||
|
@ -665,20 +616,60 @@ class CS3IPlayer : IPlayer {
|
||||||
val mediaItem = getMediaItem(MimeTypes.VIDEO_MP4, data.uri)
|
val mediaItem = getMediaItem(MimeTypes.VIDEO_MP4, data.uri)
|
||||||
val offlineSourceFactory = context.createOfflineSource()
|
val offlineSourceFactory = context.createOfflineSource()
|
||||||
|
|
||||||
val (activeSubtitles, progressiveMediaSources) = createProgressiveMediaSources(
|
val (subSources, activeSubtitles) = getSubSources(
|
||||||
|
offlineSourceFactory,
|
||||||
|
offlineSourceFactory,
|
||||||
subtitleHelper,
|
subtitleHelper,
|
||||||
offlineSourceFactory,
|
|
||||||
offlineSourceFactory,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
subtitleHelper.setActiveSubtitles(activeSubtitles.toSet())
|
subtitleHelper.setActiveSubtitles(activeSubtitles.toSet())
|
||||||
loadExo(context, mediaItem, progressiveMediaSources)
|
loadExo(context, mediaItem, subSources)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "loadOfflinePlayer error", e)
|
Log.e(TAG, "loadOfflinePlayer error", e)
|
||||||
playerError?.invoke(e)
|
playerError?.invoke(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSubSources(
|
||||||
|
onlineSourceFactory: DataSource.Factory?,
|
||||||
|
offlineSourceFactory: DataSource.Factory?,
|
||||||
|
subHelper: PlayerSubtitleHelper,
|
||||||
|
): Pair<List<SingleSampleMediaSource>, List<SubtitleData>> {
|
||||||
|
val activeSubtitles = ArrayList<SubtitleData>()
|
||||||
|
val subSources = subHelper.getAllSubtitles().mapNotNull { sub ->
|
||||||
|
val subConfig = MediaItem.SubtitleConfiguration.Builder(Uri.parse(sub.url))
|
||||||
|
.setMimeType(sub.mimeType)
|
||||||
|
.setLanguage("_${sub.name}")
|
||||||
|
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
||||||
|
.build()
|
||||||
|
when (sub.origin) {
|
||||||
|
SubtitleOrigin.DOWNLOADED_FILE -> {
|
||||||
|
if (offlineSourceFactory != null) {
|
||||||
|
activeSubtitles.add(sub)
|
||||||
|
SingleSampleMediaSource.Factory(offlineSourceFactory)
|
||||||
|
.createMediaSource(subConfig, C.TIME_UNSET)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubtitleOrigin.URL -> {
|
||||||
|
if (onlineSourceFactory != null) {
|
||||||
|
activeSubtitles.add(sub)
|
||||||
|
SingleSampleMediaSource.Factory(onlineSourceFactory)
|
||||||
|
.createMediaSource(subConfig, C.TIME_UNSET)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubtitleOrigin.OPEN_SUBTITLES -> {
|
||||||
|
// TODO
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(subSources, activeSubtitles)
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadOnlinePlayer(context: Context, link: ExtractorLink) {
|
private fun loadOnlinePlayer(context: Context, link: ExtractorLink) {
|
||||||
Log.i(TAG, "loadOnlinePlayer")
|
Log.i(TAG, "loadOnlinePlayer")
|
||||||
try {
|
try {
|
||||||
|
@ -705,10 +696,10 @@ class CS3IPlayer : IPlayer {
|
||||||
val onlineSourceFactory = createOnlineSource(link)
|
val onlineSourceFactory = createOnlineSource(link)
|
||||||
val offlineSourceFactory = context.createOfflineSource()
|
val offlineSourceFactory = context.createOfflineSource()
|
||||||
|
|
||||||
val (activeSubtitles, progressiveMediaSources) = createProgressiveMediaSources(
|
val (subSources, activeSubtitles) = getSubSources(
|
||||||
subtitleHelper,
|
|
||||||
offlineSourceFactory,
|
offlineSourceFactory,
|
||||||
onlineSourceFactory,
|
onlineSourceFactory,
|
||||||
|
subtitleHelper
|
||||||
)
|
)
|
||||||
|
|
||||||
subtitleHelper.setActiveSubtitles(activeSubtitles.toSet())
|
subtitleHelper.setActiveSubtitles(activeSubtitles.toSet())
|
||||||
|
@ -721,7 +712,7 @@ class CS3IPlayer : IPlayer {
|
||||||
setUpstreamDataSourceFactory(onlineSourceFactory)
|
setUpstreamDataSourceFactory(onlineSourceFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadExo(context, mediaItem, progressiveMediaSources, cacheFactory)
|
loadExo(context, mediaItem, subSources, cacheFactory)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "loadOnlinePlayer error", e)
|
Log.e(TAG, "loadOnlinePlayer error", e)
|
||||||
playerError?.invoke(e)
|
playerError?.invoke(e)
|
||||||
|
|
|
@ -126,6 +126,7 @@ class RepoLinkGenerator(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ link ->
|
{ link ->
|
||||||
|
Log.d(TAG, "Loaded ExtractorLink: $link")
|
||||||
if (!currentLinks.contains(link.url)) {
|
if (!currentLinks.contains(link.url)) {
|
||||||
if (!currentLinkCache.contains(link)) {
|
if (!currentLinkCache.contains(link)) {
|
||||||
currentLinks.add(link.url)
|
currentLinks.add(link.url)
|
||||||
|
|
Loading…
Reference in a new issue