diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 22603407..57169ef1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -7,9 +7,13 @@ import android.util.Log import android.widget.FrameLayout import com.google.android.exoplayer2.* 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.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.SubtitleExtractor import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.TrackSelector import com.google.android.exoplayer2.ui.SubtitleView @@ -367,7 +371,7 @@ class CS3IPlayer : IPlayer { private fun buildExoPlayer( context: Context, mediaItem: MediaItem, - subSources: List, + subSources: List, currentWindow: Int, playbackPosition: Long, playBackSpeed: Float, @@ -466,7 +470,7 @@ class CS3IPlayer : IPlayer { private fun loadExo( context: Context, mediaItem: MediaItem, - subSources: List, + subSources: List, cacheFactory: CacheDataSource.Factory? = null ) { Log.i(TAG, "loadExo") @@ -579,6 +583,39 @@ 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( + subSources: List, + factory: DataSource.Factory + ): List { + val extractorFactory = ExtractorsFactory { + subSources.map { + val currentSub = it.mediaItem.localConfiguration?.subtitleConfigurations?.firstOrNull() + val format = Format.Builder() + .setSampleMimeType(currentSub?.mimeType) + .setLanguage(currentSub?.language) + .setLabel(currentSub?.label) + .build() + + SubtitleExtractor( + CustomSubtitleDecoderFactory().createDecoder(format), format + ) + }.toTypedArray() + } + + return subSources.mapNotNull { + val uri = it.mediaItem.localConfiguration?.subtitleConfigurations?.firstOrNull()?.uri ?: return@mapNotNull null + if (uri.toString().isBlank()) return@mapNotNull null + + ProgressiveMediaSource.Factory(factory, extractorFactory) + .createMediaSource( + MediaItem.fromUri(uri) + ) + } + } + private fun loadOfflinePlayer(context: Context, data: ExtractorUri) { Log.i(TAG, "loadOfflinePlayer") try { @@ -591,8 +628,12 @@ class CS3IPlayer : IPlayer { offlineSourceFactory, subtitleHelper ) + + val progressiveMediaSources = + createProgressiveMediaSources(subSources, offlineSourceFactory) + subtitleHelper.setActiveSubtitles(activeSubtitles.toSet()) - loadExo(context, mediaItem, subSources) + loadExo(context, mediaItem, progressiveMediaSources) } catch (e: Exception) { Log.e(TAG, "loadOfflinePlayer error", e) playerError?.invoke(e) @@ -630,6 +671,10 @@ class CS3IPlayer : IPlayer { offlineSourceFactory, subtitleHelper ) + + val progressiveMediaSources = + createProgressiveMediaSources(subSources, onlineSourceFactory) + subtitleHelper.setActiveSubtitles(activeSubtitles.toSet()) if (simpleCache == null) @@ -640,7 +685,7 @@ class CS3IPlayer : IPlayer { setUpstreamDataSourceFactory(onlineSourceFactory) } - loadExo(context, mediaItem, subSources, cacheFactory) + loadExo(context, mediaItem, progressiveMediaSources, cacheFactory) } catch (e: Exception) { Log.e(TAG, "loadOnlinePlayer error", e) playerError?.invoke(e) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt index 25015c10..875db647 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt @@ -1,49 +1,77 @@ package com.lagradost.cloudstream3.ui.player +import android.util.Log import com.google.android.exoplayer2.Format import com.google.android.exoplayer2.text.* -import com.google.android.exoplayer2.util.MimeTypes -import com.google.android.exoplayer2.text.webvtt.WebvttDecoder import com.google.android.exoplayer2.text.ssa.SsaDecoder -import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder -import com.google.android.exoplayer2.text.ttml.TtmlDecoder import com.google.android.exoplayer2.text.subrip.SubripDecoder -import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder -import com.google.android.exoplayer2.text.cea.Cea608Decoder -import com.google.android.exoplayer2.text.cea.Cea708Decoder -import com.google.android.exoplayer2.text.dvb.DvbDecoder -import com.google.android.exoplayer2.text.pgs.PgsDecoder -import java.lang.IllegalArgumentException +import com.google.android.exoplayer2.text.ttml.TtmlDecoder +import com.google.android.exoplayer2.text.webvtt.WebvttDecoder +import com.google.android.exoplayer2.util.MimeTypes +import okio.ByteString.Companion.toByteString +import java.nio.charset.Charset -//class CustomDecoder : SubtitleDecoder { -// override fun getName(): String { -// -// } -// -// override fun dequeueInputBuffer(): SubtitleInputBuffer? { -// -// } -// -// override fun queueInputBuffer(inputBuffer: SubtitleInputBuffer) { -// -// } -// -// override fun dequeueOutputBuffer(): SubtitleOutputBuffer? { -// -// } -// -// override fun flush() { -// -// } -// -// override fun release() { -// -// } -// -// override fun setPositionUs(positionUs: Long) { -// -// } -//} + +class CustomDecoder : SubtitleDecoder { + + companion object { + private const val TAG = "CustomDecoder" + } + + var realDecoder: SimpleSubtitleDecoder? = null + + override fun getName(): String { + return realDecoder?.name ?: this::class.java.name + } + + override fun dequeueInputBuffer(): SubtitleInputBuffer { + return realDecoder?.dequeueInputBuffer() ?: SubtitleInputBuffer() + } + + override fun queueInputBuffer(inputBuffer: SubtitleInputBuffer) { + println("STARTTT ${inputBuffer.format?.language}") + if (realDecoder == null) + inputBuffer.data?.let { data -> + + val pos = data.position() + data.position(0) + val arr = ByteArray(minOf(data.remaining(), 100)) + data.get(arr) + data.position(pos) + + val str = arr.decodeToString().trimStart() + Log.i(TAG, "Got data from queueInputBuffer") + Log.i(TAG, "first string is $str") + + //https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388 + realDecoder = when { + str.startsWith("WEBVTT") -> WebvttDecoder() + str.startsWith(" TtmlDecoder() + str.startsWith("[Script Info]") || str.startsWith("Title:") -> SsaDecoder() + str.startsWith("1") -> SubripDecoder() + else -> null + } + } + + realDecoder?.dequeueInputBuffer().also { println("RETURNED ddddd") } + } + + override fun dequeueOutputBuffer(): SubtitleOutputBuffer? { + return realDecoder?.dequeueOutputBuffer() + } + + override fun flush() { + realDecoder?.flush() + } + + override fun release() { + realDecoder?.release() + } + + override fun setPositionUs(positionUs: Long) { + realDecoder?.setPositionUs(positionUs) + } +} /** See https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java */ class CustomSubtitleDecoderFactory : SubtitleDecoderFactory { @@ -55,38 +83,39 @@ class CustomSubtitleDecoderFactory : SubtitleDecoderFactory { MimeTypes.APPLICATION_TTML, MimeTypes.APPLICATION_MP4VTT, MimeTypes.APPLICATION_SUBRIP, - MimeTypes.APPLICATION_TX3G, - MimeTypes.APPLICATION_CEA608, - MimeTypes.APPLICATION_MP4CEA608, - MimeTypes.APPLICATION_CEA708, - MimeTypes.APPLICATION_DVBSUBS, - MimeTypes.APPLICATION_PGS, - MimeTypes.TEXT_EXOPLAYER_CUES + //MimeTypes.APPLICATION_TX3G, + //MimeTypes.APPLICATION_CEA608, + //MimeTypes.APPLICATION_MP4CEA608, + //MimeTypes.APPLICATION_CEA708, + //MimeTypes.APPLICATION_DVBSUBS, + //MimeTypes.APPLICATION_PGS, + //MimeTypes.TEXT_EXOPLAYER_CUES ).contains(format.sampleMimeType) } override fun createDecoder(format: Format): SubtitleDecoder { - return when (val mimeType = format.sampleMimeType) { - MimeTypes.TEXT_VTT -> WebvttDecoder() - MimeTypes.TEXT_SSA -> SsaDecoder(format.initializationData) - MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() - MimeTypes.APPLICATION_TTML -> TtmlDecoder() - MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() - MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(format.initializationData) - MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> return Cea608Decoder( - mimeType, - format.accessibilityChannel, - Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS - ) - MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( - format.accessibilityChannel, - format.initializationData - ) - MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(format.initializationData) - MimeTypes.APPLICATION_PGS -> PgsDecoder() - MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() - // Default WebVttDecoder - else -> WebvttDecoder() - } + return CustomDecoder() + //return when (val mimeType = format.sampleMimeType) { + // MimeTypes.TEXT_VTT -> WebvttDecoder() + // MimeTypes.TEXT_SSA -> SsaDecoder(format.initializationData) + // MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() + // MimeTypes.APPLICATION_TTML -> TtmlDecoder() + // MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() + // MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(format.initializationData) + // MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> return Cea608Decoder( + // mimeType, + // format.accessibilityChannel, + // Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS + // ) + // MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( + // format.accessibilityChannel, + // format.initializationData + // ) + // MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(format.initializationData) + // MimeTypes.APPLICATION_PGS -> PgsDecoder() + // MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() + // // Default WebVttDecoder + // else -> WebvttDecoder() + //} } } \ No newline at end of file