forked from recloudstream/cloudstream
Bumped the exoplayer version and fixed the audio & video track selection
This commit is contained in:
parent
7e750a40e0
commit
93cbd29f3d
8 changed files with 108 additions and 43 deletions
|
@ -155,10 +155,10 @@ dependencies {
|
||||||
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
|
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
|
||||||
|
|
||||||
// Exoplayer
|
// Exoplayer
|
||||||
implementation("com.google.android.exoplayer:exoplayer:2.16.1")
|
implementation("com.google.android.exoplayer:exoplayer:2.18.1")
|
||||||
implementation("com.google.android.exoplayer:extension-cast:2.16.1")
|
implementation("com.google.android.exoplayer:extension-cast:2.18.1")
|
||||||
implementation("com.google.android.exoplayer:extension-mediasession:2.16.1")
|
implementation("com.google.android.exoplayer:extension-mediasession:2.18.1")
|
||||||
implementation("com.google.android.exoplayer:extension-okhttp:2.16.1")
|
implementation("com.google.android.exoplayer:extension-okhttp:2.18.1")
|
||||||
|
|
||||||
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
|
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
} else {
|
} else {
|
||||||
ChromecastSubtitlesFragment.getCurrentSavedStyle().apply {
|
ChromecastSubtitlesFragment.getCurrentSavedStyle().apply {
|
||||||
val font = TextTrackStyle()
|
val font = TextTrackStyle()
|
||||||
font.fontFamily = fontFamily ?: "Google Sans"
|
font.setFontFamily(fontFamily ?: "Google Sans")
|
||||||
fontGenericFamily?.let {
|
fontGenericFamily?.let {
|
||||||
font.fontGenericFamily = it
|
font.fontGenericFamily = it
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,9 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl
|
val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl
|
||||||
?: remoteMediaClient?.currentItem?.media?.contentId)
|
?: remoteMediaClient?.currentItem?.media?.contentId)
|
||||||
|
|
||||||
val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }.toTypedArray()
|
val sortingMethods =
|
||||||
|
items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }
|
||||||
|
.toTypedArray()
|
||||||
val sotringIndex = items.indexOfFirst { it.url == contentUrl }
|
val sotringIndex = items.indexOfFirst { it.url == contentUrl }
|
||||||
|
|
||||||
val arrayAdapter =
|
val arrayAdapter =
|
||||||
|
@ -358,10 +360,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSessionConnected(castSession: CastSession?) {
|
override fun onSessionConnected(castSession: CastSession) {
|
||||||
castSession?.let {
|
super.onSessionConnected(castSession)
|
||||||
super.onSessionConnected(it)
|
|
||||||
}
|
|
||||||
remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject())
|
remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
||||||
import com.google.android.exoplayer2.source.*
|
import com.google.android.exoplayer2.source.*
|
||||||
import com.google.android.exoplayer2.text.TextRenderer
|
import com.google.android.exoplayer2.text.TextRenderer
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
|
import com.google.android.exoplayer2.trackselection.TrackSelectionOverride
|
||||||
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
|
||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.*
|
||||||
|
@ -218,7 +219,43 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
var currentSubtitles: SubtitleData? = null
|
var currentSubtitles: SubtitleData? = null
|
||||||
|
|
||||||
override fun setMaxVideoSize(width: Int, height: Int) {
|
private fun List<Tracks.Group>.getTrack(id: String?): Pair<TrackGroup, Int>? {
|
||||||
|
if (id == null) return null
|
||||||
|
// This beast of an expression does:
|
||||||
|
// 1. Filter all audio tracks
|
||||||
|
// 2. Get all formats in said audio tacks
|
||||||
|
// 3. Gets all ids of the formats
|
||||||
|
// 4. Filters to find the first audio track with the same id as the audio track we are looking for
|
||||||
|
// 5. Returns the media group and the index of the audio track in the group
|
||||||
|
return this.firstNotNullOfOrNull { group ->
|
||||||
|
(0 until group.mediaTrackGroup.length).map {
|
||||||
|
group.getTrackFormat(it) to it
|
||||||
|
}.firstOrNull { it.first.id == id }
|
||||||
|
?.let { group.mediaTrackGroup to it.second }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setMaxVideoSize(width: Int, height: Int, id: String?) {
|
||||||
|
if (id != null) {
|
||||||
|
val videoTrack =
|
||||||
|
exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_VIDEO }
|
||||||
|
?.getTrack(id)
|
||||||
|
|
||||||
|
if (videoTrack != null) {
|
||||||
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
|
?.buildUpon()
|
||||||
|
?.setOverrideForType(
|
||||||
|
TrackSelectionOverride(
|
||||||
|
videoTrack.first,
|
||||||
|
videoTrack.second
|
||||||
|
)
|
||||||
|
)
|
||||||
|
?.build()
|
||||||
|
?: return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
?.buildUpon()
|
?.buildUpon()
|
||||||
?.setMaxVideoSize(width, height)
|
?.setMaxVideoSize(width, height)
|
||||||
|
@ -226,8 +263,29 @@ class CS3IPlayer : IPlayer {
|
||||||
?: return
|
?: return
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setPreferredAudioTrack(trackLanguage: String?) {
|
override fun setPreferredAudioTrack(trackLanguage: String?, id: String?) {
|
||||||
preferredAudioTrackLanguage = trackLanguage
|
preferredAudioTrackLanguage = trackLanguage
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
val audioTrack =
|
||||||
|
exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_AUDIO }
|
||||||
|
?.getTrack(id)
|
||||||
|
|
||||||
|
if (audioTrack != null) {
|
||||||
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
|
?.buildUpon()
|
||||||
|
?.setOverrideForType(
|
||||||
|
TrackSelectionOverride(
|
||||||
|
audioTrack.first,
|
||||||
|
audioTrack.second
|
||||||
|
)
|
||||||
|
)
|
||||||
|
?.build()
|
||||||
|
?: return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
?.buildUpon()
|
?.buildUpon()
|
||||||
?.setPreferredAudioLanguage(trackLanguage)
|
?.setPreferredAudioLanguage(trackLanguage)
|
||||||
|
@ -239,11 +297,11 @@ class CS3IPlayer : IPlayer {
|
||||||
/**
|
/**
|
||||||
* Gets all supported formats in a list
|
* Gets all supported formats in a list
|
||||||
* */
|
* */
|
||||||
private fun List<TracksInfo.TrackGroupInfo>.getFormats(): List<Format> {
|
private fun List<Tracks.Group>.getFormats(): List<Pair<Format, Int>> {
|
||||||
return this.map {
|
return this.map {
|
||||||
(0 until it.trackGroup.length).mapNotNull { i ->
|
(0 until it.mediaTrackGroup.length).mapNotNull { i ->
|
||||||
if (it.isSupported)
|
if (it.isSupported)
|
||||||
it.trackGroup.getFormat(i) // to it.isSelected
|
it.mediaTrackGroup.getFormat(i) to i
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
}.flatten()
|
}.flatten()
|
||||||
|
@ -270,11 +328,12 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVideoTracks(): CurrentTracks {
|
override fun getVideoTracks(): CurrentTracks {
|
||||||
val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList()
|
val allTracks = exoPlayer?.currentTracks?.groups ?: emptyList()
|
||||||
val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats()
|
val videoTracks = allTracks.filter { it.type == TRACK_TYPE_VIDEO }
|
||||||
.map { it.toVideoTrack() }
|
.getFormats()
|
||||||
val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats()
|
.map { it.first.toVideoTrack() }
|
||||||
.map { it.toAudioTrack() }
|
val audioTracks = allTracks.filter { it.type == TRACK_TYPE_AUDIO }.getFormats()
|
||||||
|
.map { it.first.toAudioTrack() }
|
||||||
|
|
||||||
return CurrentTracks(
|
return CurrentTracks(
|
||||||
exoPlayer?.videoFormat?.toVideoTrack(),
|
exoPlayer?.videoFormat?.toVideoTrack(),
|
||||||
|
@ -611,7 +670,12 @@ class CS3IPlayer : IPlayer {
|
||||||
} else it
|
} else it
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
}
|
}
|
||||||
.setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight))
|
.setTrackSelector(
|
||||||
|
trackSelector ?: getTrackSelector(
|
||||||
|
context,
|
||||||
|
maxVideoHeight
|
||||||
|
)
|
||||||
|
)
|
||||||
.setLoadControl(
|
.setLoadControl(
|
||||||
DefaultLoadControl.Builder()
|
DefaultLoadControl.Builder()
|
||||||
.setTargetBufferBytes(
|
.setTargetBufferBytes(
|
||||||
|
@ -781,10 +845,7 @@ class CS3IPlayer : IPlayer {
|
||||||
isPlaying = exo.isPlaying
|
isPlaying = exo.isPlaying
|
||||||
}
|
}
|
||||||
exoPlayer?.addListener(object : Player.Listener {
|
exoPlayer?.addListener(object : Player.Listener {
|
||||||
/**
|
override fun onTracksChanged(tracks: Tracks) {
|
||||||
* Records the current used subtitle/track. Needed as exoplayer seems to have loose track language selection.
|
|
||||||
* */
|
|
||||||
override fun onTracksInfoChanged(tracksInfo: TracksInfo) {
|
|
||||||
fun Format.isSubtitle(): Boolean {
|
fun Format.isSubtitle(): Boolean {
|
||||||
return this.sampleMimeType?.contains("video/") == false &&
|
return this.sampleMimeType?.contains("video/") == false &&
|
||||||
this.sampleMimeType?.contains("audio/") == false
|
this.sampleMimeType?.contains("audio/") == false
|
||||||
|
@ -792,17 +853,17 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
normalSafeApiCall {
|
normalSafeApiCall {
|
||||||
exoPlayerSelectedTracks =
|
exoPlayerSelectedTracks =
|
||||||
tracksInfo.trackGroupInfos.mapNotNull {
|
tracks.groups.mapNotNull {
|
||||||
val format = it.trackGroup.getFormat(0)
|
val format = it.mediaTrackGroup.getFormat(0)
|
||||||
if (format.isSubtitle())
|
if (format.isSubtitle())
|
||||||
format.language?.let { lang -> lang to it.isSelected }
|
format.language?.let { lang -> lang to it.isSelected }
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
val exoPlayerReportedTracks = tracksInfo.trackGroupInfos.mapNotNull {
|
val exoPlayerReportedTracks = tracks.groups.mapNotNull {
|
||||||
// Filter out unsupported tracks
|
// Filter out unsupported tracks
|
||||||
if (it.isSupported)
|
if (it.isSupported)
|
||||||
it.trackGroup.getFormat(0)
|
it.mediaTrackGroup.getFormat(0)
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
}.mapNotNull {
|
}.mapNotNull {
|
||||||
|
@ -827,7 +888,6 @@ class CS3IPlayer : IPlayer {
|
||||||
onTracksInfoChanged?.invoke()
|
onTracksInfoChanged?.invoke()
|
||||||
subtitlesUpdates?.invoke()
|
subtitlesUpdates?.invoke()
|
||||||
}
|
}
|
||||||
super.onTracksInfoChanged(tracksInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
|
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
|
||||||
|
|
|
@ -796,15 +796,16 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
tracksDialog.apply_btt?.setOnClickListener {
|
tracksDialog.apply_btt?.setOnClickListener {
|
||||||
|
val currentTrack = currentAudioTracks.getOrNull(audioIndexStart)
|
||||||
player.setPreferredAudioTrack(
|
player.setPreferredAudioTrack(
|
||||||
currentAudioTracks.getOrNull(audioIndexStart)?.language
|
currentTrack?.language, currentTrack?.id
|
||||||
)
|
)
|
||||||
|
|
||||||
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
|
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
|
||||||
val width = currentVideo?.width ?: NO_VALUE
|
val width = currentVideo?.width ?: NO_VALUE
|
||||||
val height = currentVideo?.height ?: NO_VALUE
|
val height = currentVideo?.height ?: NO_VALUE
|
||||||
if (width != NO_VALUE && height != NO_VALUE) {
|
if (width != NO_VALUE && height != NO_VALUE) {
|
||||||
player.setMaxVideoSize(width, height)
|
player.setMaxVideoSize(width, height, currentVideo?.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
tracksDialog.dismissSafe(activity)
|
tracksDialog.dismissSafe(activity)
|
||||||
|
|
|
@ -161,9 +161,9 @@ interface IPlayer {
|
||||||
|
|
||||||
fun getVideoTracks(): CurrentTracks
|
fun getVideoTracks(): CurrentTracks
|
||||||
|
|
||||||
/** If no parameters are set it'll default to no set size */
|
/** If no parameters are set it'll default to no set size, Specifying the id allows for track overrides to force the player to pick the quality. */
|
||||||
fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE)
|
fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE, id: String? = null)
|
||||||
|
|
||||||
/** If no trackLanguage is set it'll default to first track */
|
/** If no trackLanguage is set it'll default to first track. Specifying the id allows for track overrides as the language can be identical. */
|
||||||
fun setPreferredAudioTrack(trackLanguage: String?)
|
fun setPreferredAudioTrack(trackLanguage: String?, id: String? = null)
|
||||||
}
|
}
|
|
@ -75,7 +75,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
|
||||||
return TAG
|
return TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
@RendererCapabilities.Capabilities
|
// @RendererCapabilities.Capabilities
|
||||||
override fun supportsFormat(format: Format): Int {
|
override fun supportsFormat(format: Format): Int {
|
||||||
return if (decoderFactory.supportsFormat(format)) {
|
return if (decoderFactory.supportsFormat(format)) {
|
||||||
RendererCapabilities.create(
|
RendererCapabilities.create(
|
||||||
|
@ -202,7 +202,8 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Try and read the next subtitle from the source.
|
// Try and read the next subtitle from the source.
|
||||||
@ReadDataResult val result =
|
// @ReadDataResult
|
||||||
|
val result =
|
||||||
readSource(formatHold, nextInputBuffer, /* readFlags= */0)
|
readSource(formatHold, nextInputBuffer, /* readFlags= */0)
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
if (nextInputBuffer.isEndOfStream) {
|
if (nextInputBuffer.isEndOfStream) {
|
||||||
|
|
|
@ -49,7 +49,7 @@ data class SaveCaptionStyle(
|
||||||
@JsonProperty("foregroundColor") var foregroundColor: Int,
|
@JsonProperty("foregroundColor") var foregroundColor: Int,
|
||||||
@JsonProperty("backgroundColor") var backgroundColor: Int,
|
@JsonProperty("backgroundColor") var backgroundColor: Int,
|
||||||
@JsonProperty("windowColor") var windowColor: Int,
|
@JsonProperty("windowColor") var windowColor: Int,
|
||||||
@CaptionStyleCompat.EdgeType
|
// @CaptionStyleCompat.EdgeType
|
||||||
@JsonProperty("edgeType") var edgeType: Int,
|
@JsonProperty("edgeType") var edgeType: Int,
|
||||||
@JsonProperty("edgeColor") var edgeColor: Int,
|
@JsonProperty("edgeColor") var edgeColor: Int,
|
||||||
@FontRes
|
@FontRes
|
||||||
|
|
|
@ -20,10 +20,13 @@ class CastOptionsProvider : OptionsProvider {
|
||||||
MediaIntentReceiver.ACTION_FORWARD,
|
MediaIntentReceiver.ACTION_FORWARD,
|
||||||
MediaIntentReceiver.ACTION_STOP_CASTING
|
MediaIntentReceiver.ACTION_STOP_CASTING
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val name = ControllerActivity::class.qualifiedName!!
|
||||||
|
|
||||||
val compatButtonAction = intArrayOf(1, 3)
|
val compatButtonAction = intArrayOf(1, 3)
|
||||||
val notificationOptions =
|
val notificationOptions =
|
||||||
NotificationOptions.Builder()
|
NotificationOptions.Builder()
|
||||||
.setTargetActivityClassName(ControllerActivity::class.qualifiedName)
|
.setTargetActivityClassName(name)
|
||||||
.setActions(buttonActions, compatButtonAction)
|
.setActions(buttonActions, compatButtonAction)
|
||||||
.setForward30DrawableResId(R.drawable.go_forward_30)
|
.setForward30DrawableResId(R.drawable.go_forward_30)
|
||||||
.setRewind30DrawableResId(R.drawable.go_back_30)
|
.setRewind30DrawableResId(R.drawable.go_back_30)
|
||||||
|
@ -32,7 +35,7 @@ class CastOptionsProvider : OptionsProvider {
|
||||||
|
|
||||||
val mediaOptions = CastMediaOptions.Builder()
|
val mediaOptions = CastMediaOptions.Builder()
|
||||||
.setNotificationOptions(notificationOptions)
|
.setNotificationOptions(notificationOptions)
|
||||||
.setExpandedControllerActivityClassName(ControllerActivity::class.qualifiedName)
|
.setExpandedControllerActivityClassName(name)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return CastOptions.Builder()
|
return CastOptions.Builder()
|
||||||
|
@ -44,7 +47,7 @@ class CastOptionsProvider : OptionsProvider {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAdditionalSessionProviders(p0: Context?): MutableList<SessionProvider> {
|
override fun getAdditionalSessionProviders(p0: Context): MutableList<SessionProvider> {
|
||||||
return Collections.emptyList()
|
return Collections.emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue