I am using media3 exoplayer with google cast. I have implemented the casting. I am working with a composable project. Where I init the cast context in the on create of the composable. The casting seems to be working fine for the first time we open the screen.But once disconnected and reconnected the media item is not getting sent.Its showing no media is selected. Here is the class which handles the casting. When disconnected and reconnected, the cast player is not even getting the session availability call backs also. I am struggling to understand what went wrong. please help me do this.
@OptIn(UnstableApi::class)
class CastHelper(
private val playerView: PlayerView,
private val exoPlayer: ExoPlayer,
) {
private lateinit var castPlayer: CastPlayer
private lateinit var castContext: CastContext
var mediaItem: MediaItem? = null
var isPipDisplayedInExoplayer: Boolean? = null
val pipButton: ImageButton = playerView.findViewById<ImageButton>(R.id.exo_pip_custom)
var openedFullScreen = false
var playFrom = 0L
val castPlayerListener = object : Player.Listener {
override fun onIsLoadingChanged(isLoading: Boolean) {
super.onIsLoadingChanged(isLoading)
Log.d(LogTag, "CastPlayer Loading Changed $isLoading")
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
Log.d(LogTag, "Cast Player error ${error.message}")
}
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
Log.i(LogTag, "Cast Player playback state changed $playbackState")
if (playbackState == Player.STATE_READY && isCasting() && openedFullScreen.not()) {
openFullScreenControlsActivity()
castPlayer.seekTo(playFrom)
}
}
}
val sessionAvailabilityListener = object : SessionAvailabilityListener {
override fun onCastSessionAvailable() {
startCastPlayer()
}
override fun onCastSessionUnavailable() {
startExoPlayer()
releaseCastPlayer()
}
}
private fun initializeCastContext(context: Context) {
(context as Activity).findViewById<MediaRouteButton>(R.id.exo_cast)?.apply {
CastButtonFactory.setUpMediaRouteButton(context, this)
dialogFactory = CustomCastThemeFactory()
}
castContext = CastContext.getSharedInstance(context).also {
it.addSessionTransferCallback(
CastSessionTransferCallback(playerView.context.applicationContext)
)
}
}
private fun initializeCastPlayer() {
if (!::castContext.isInitialized) {
throw IllegalStateException("CastContext is not initialized")
}
castPlayer = CastPlayer(castContext).also {
it.setSessionAvailabilityListener(sessionAvailabilityListener)
}
}
fun startCastPlayer() {
// Clear previous media item if any
castPlayer.clearMediaItems()
playFrom = exoPlayer.currentPosition
mediaItem?.let { item ->
castPlayer.setMediaItem(item)
castPlayer.addListener(castPlayerListener)
castPlayer.prepare()
castPlayer.playWhenReady = true
playerView.player = castPlayer
exoPlayer.stop()
pipButton.isVisible(false)
configurePosterView(shouldShow = true)
}
}
private fun startExoPlayer() {
mediaItem?.let { item ->
exoPlayer.setMediaItem(item)
exoPlayer.prepare()
playerView.player = exoPlayer.also {
it.seekTo(castPlayer.currentPosition)
it.playWhenReady = true
}
castPlayer.stop()
castPlayer.clearMediaItems()
pipButton.isVisible(isPipDisplayedInExoplayer == true)
configurePosterView(shouldShow = false)
}
}
fun releaseCastPlayer() {
if (::castPlayer.isInitialized) {
castPlayer.clearMediaItems()
castPlayer.release()
Log.i(LogTag, "Cast Player released.")
}
}
fun initCastMembers() {
if (!DeviceType.isTv(playerView.context)) {
initializeCastContext(playerView.context)
initializeCastPlayer()
Log.d(LogTag, "Cast Player ${getCastPlayer()} ${isCasting()}")
}
}
fun isCasting(): Boolean {
val castSession = castContext.sessionManager.currentCastSession
return castSession != null && castSession.isConnected
}
fun getCastPlayer(): CastPlayer? {
return if (::castPlayer.isInitialized && isCasting())
castPlayer
else null
}
fun openFullScreenControlsActivity() {
val context = playerView.context
openedFullScreen = true
context.startActivity(Intent(context, FullScreenCastControllerActivity::class.java))
}
@SuppressLint("CheckResult")
private fun configurePosterView(shouldShow: Boolean) {
val posterUri = mediaItem?.mediaMetadata?.artworkUri
playerView.findPosterView()?.let { poster ->
Glide.with(poster)
.load(posterUri)
.error(R.drawable.logo_screen_error_placeholder)
.into(poster)
poster.isVisible(shouldShow)
playerView.showController()
playerView.controllerShowTimeoutMs = if (shouldShow) -1 else 2000
}
}
fun keepControlViewWhenCasting() {
playerView.doIfOnPhone {
if (isCasting())
playerView.showController()
}
}
}