casting: add helper functions for dispatching to active backend

This commit is contained in:
Marcus Hanestad 2025-08-28 10:37:14 +02:00
commit 58e08f5ea3
6 changed files with 236 additions and 293 deletions

View file

@ -21,6 +21,7 @@ import com.futo.platformplayer.casting.ChromecastCastingDevice
import com.futo.platformplayer.casting.FCastCastingDevice import com.futo.platformplayer.casting.FCastCastingDevice
import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.experimental_casting.ExpStateCasting import com.futo.platformplayer.experimental_casting.ExpStateCasting
import com.futo.platformplayer.experimental_casting.StateCastingDispatcher
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
@ -74,30 +75,18 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
_buttonPlay = findViewById(R.id.button_play); _buttonPlay = findViewById(R.id.button_play);
_buttonPlay.setOnClickListener { _buttonPlay.setOnClickListener {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.resumeVideo()
ExpStateCasting.instance.activeDevice?.device?.resumePlayback()
} else {
StateCasting.instance.activeDevice?.resumeVideo()
}
} }
_buttonPause = findViewById(R.id.button_pause); _buttonPause = findViewById(R.id.button_pause);
_buttonPause.setOnClickListener { _buttonPause.setOnClickListener {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.pauseVideo()
ExpStateCasting.instance.activeDevice?.device?.pausePlayback()
} else {
StateCasting.instance.activeDevice?.pauseVideo()
}
} }
_buttonStop = findViewById(R.id.button_stop); _buttonStop = findViewById(R.id.button_stop);
_buttonStop.setOnClickListener { _buttonStop.setOnClickListener {
(ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.closeVideoDetails() (ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.closeVideoDetails()
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.stopVideo()
ExpStateCasting.instance.activeDevice?.device?.stopPlayback()
} else {
StateCasting.instance.activeDevice?.stopVideo()
}
} }
_buttonNext = findViewById(R.id.button_next); _buttonNext = findViewById(R.id.button_next);
@ -125,21 +114,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
return@OnChangeListener return@OnChangeListener
} }
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.videoSeekTo(value.toDouble())
val activeDevice = ExpStateCasting.instance.activeDevice ?: return@OnChangeListener;
try {
activeDevice.device.seek(value.toDouble());
} catch (e: Throwable) {
Logger.e(TAG, "Failed to seek.", e);
}
} else {
val activeDevice = StateCasting.instance.activeDevice ?: return@OnChangeListener;
try {
activeDevice.seekVideo(value.toDouble());
} catch (e: Throwable) {
Logger.e(TAG, "Failed to seek.", e);
}
}
}); });
//TODO: Check if volume slider is properly hidden in all cases //TODO: Check if volume slider is properly hidden in all cases
@ -148,25 +123,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
return@OnChangeListener return@OnChangeListener
} }
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.changeVolume(value.toDouble())
val activeDevice = ExpStateCasting.instance.activeDevice ?: return@OnChangeListener;
if (activeDevice.device.supportsFeature(DeviceFeature.SET_VOLUME)) {
try {
activeDevice.device.changeVolume(value.toDouble());
} catch (e: Throwable) {
Logger.e(TAG, "Failed to change volume.", e);
}
}
} else {
val activeDevice = StateCasting.instance.activeDevice ?: return@OnChangeListener;
if (activeDevice.canSetVolume) {
try {
activeDevice.changeVolume(value.toDouble());
} catch (e: Throwable) {
Logger.e(TAG, "Failed to change volume.", e);
}
}
}
}); });
setLoading(false); setLoading(false);

View file

@ -110,12 +110,17 @@ class CastingDeviceHandle {
contentType: String, contentType: String,
contentId: String, contentId: String,
resumePosition: Double, resumePosition: Double,
duration: Double,
speed: Double?, speed: Double?,
metadata: Metadata? = null metadata: Metadata? = null
) { ) {
try { try {
device.load(LoadRequest.Video(contentType, contentId, resumePosition, speed, duration, metadata)) device.load(LoadRequest.Video(
contentType = contentType,
url = contentId,
resumePosition = resumePosition,
speed = speed,
metadata = metadata
))
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "Failed to load video: $e") Logger.e(TAG, "Failed to load video: $e")
} }
@ -125,11 +130,15 @@ class CastingDeviceHandle {
contentType: String, contentType: String,
content: String, content: String,
resumePosition: Double, resumePosition: Double,
duration: Double,
speed: Double? speed: Double?
) { ) {
try { try {
device.load(LoadRequest.Content(contentType, content, resumePosition, duration, speed)) device.load(LoadRequest.Content(
contentType =contentType,
content = content,
resumePosition = resumePosition,
speed = speed
))
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "Failed to load content: $e") Logger.e(TAG, "Failed to load content: $e")
} }

View file

@ -569,7 +569,6 @@ class ExpStateCasting {
videoSource.container, videoSource.container,
videoUrl, videoUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
) )
@ -581,7 +580,6 @@ class ExpStateCasting {
audioSource.container, audioSource.container,
audioUrl, audioUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -601,7 +599,6 @@ class ExpStateCasting {
videoSource.container, videoSource.container,
videoSource.url, videoSource.url,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -622,7 +619,6 @@ class ExpStateCasting {
audioSource.container, audioSource.container,
audioSource.url, audioSource.url,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -738,7 +734,6 @@ class ExpStateCasting {
videoSource.container, videoSource.container,
videoUrl, videoUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -769,7 +764,6 @@ class ExpStateCasting {
audioSource.container, audioSource.container,
audioUrl, audioUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -963,7 +957,6 @@ class ExpStateCasting {
"application/vnd.apple.mpegurl", "application/vnd.apple.mpegurl",
hlsUrl, hlsUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
) )
@ -1043,7 +1036,6 @@ class ExpStateCasting {
"application/dash+xml", "application/dash+xml",
dashUrl, dashUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -1139,7 +1131,6 @@ class ExpStateCasting {
"application/dash+xml", "application/dash+xml",
content, content,
resumePosition, resumePosition,
video.duration.toDouble(),
speed speed
); );
@ -1319,7 +1310,6 @@ class ExpStateCasting {
"application/vnd.apple.mpegurl", "application/vnd.apple.mpegurl",
hlsUrl, hlsUrl,
hackfixResumePosition, hackfixResumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -1487,7 +1477,6 @@ class ExpStateCasting {
"application/dash+xml", "application/dash+xml",
dashUrl, dashUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );
@ -1756,7 +1745,6 @@ class ExpStateCasting {
"application/dash+xml", "application/dash+xml",
dashUrl, dashUrl,
resumePosition, resumePosition,
video.duration.toDouble(),
speed, speed,
metadataFromVideo(video) metadataFromVideo(video)
); );

View file

@ -0,0 +1,134 @@
package com.futo.platformplayer.experimental_casting
import com.futo.platformplayer.Settings
import com.futo.platformplayer.casting.CastConnectionState
import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.dialogs.ConnectedCastingDialog.Companion.TAG
import com.futo.platformplayer.logging.Logger
import org.fcast.sender_sdk.DeviceFeature
class StateCastingDispatcher {
companion object {
fun canActiveDeviceSetSpeed(): Boolean {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.device?.supportsFeature(DeviceFeature.SET_SPEED) == true
} else {
StateCasting.instance.activeDevice?.canSetSpeed == true
}
}
fun getActiveDeviceSpeed(): Double? {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.speed
} else {
StateCasting.instance.activeDevice?.speed
}
}
fun activeDeviceSetSpeed(speed: Double) {
if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.device?.changeSpeed(speed)
} else {
StateCasting.instance.activeDevice?.changeSpeed(speed)
}
}
fun resumeVideo(): Boolean {
return try {
if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.resumeVideo()
} else {
StateCasting.instance.resumeVideo()
}
} catch (_: Throwable) {
false
}
}
fun pauseVideo(): Boolean {
return try {
if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.pauseVideo()
} else {
StateCasting.instance.pauseVideo()
}
} catch (_: Throwable) {
false
}
}
fun videoSeekTo(timeSeconds: Double): Boolean {
return try {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.videoSeekTo(timeSeconds)
} else {
StateCasting.instance.videoSeekTo(timeSeconds)
}
} catch (_: Throwable) {
false
}
}
fun stopVideo(): Boolean {
return try {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.stopVideo()
} else {
StateCasting.instance.stopVideo()
}
} catch (_: Throwable) {
false
}
}
fun isCasting(): Boolean {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.isCasting
} else {
StateCasting.instance.isCasting
}
}
fun isConnected(): Boolean {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED
} else {
StateCasting.instance.activeDevice?.connectionState == CastConnectionState.CONNECTED
}
}
fun isPlaying(): Boolean {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.isPlaying == true
} else {
StateCasting.instance.activeDevice?.isPlaying == true
}
}
fun getExpectedCurrentTime(): Double? {
return if (Settings.instance.casting.experimentalCasting) {
ExpStateCasting.instance.activeDevice?.expectedCurrentTime
} else {
StateCasting.instance.activeDevice?.expectedCurrentTime
}
}
fun changeVolume(volume: Double) {
try {
if (Settings.instance.casting.experimentalCasting) {
val activeDevice =
ExpStateCasting.instance.activeDevice ?: return;
if (activeDevice.device.supportsFeature(DeviceFeature.SET_VOLUME)) {
activeDevice.device.changeVolume(volume);
}
} else {
val activeDevice =
StateCasting.instance.activeDevice ?: return;
if (activeDevice.canSetVolume) {
activeDevice.changeVolume(volume);
}
}
} catch (_: Throwable) {}
}
}
}

View file

@ -99,6 +99,7 @@ import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
import com.futo.platformplayer.exceptions.UnsupportedCastException import com.futo.platformplayer.exceptions.UnsupportedCastException
import com.futo.platformplayer.experimental_casting.ExpStateCasting import com.futo.platformplayer.experimental_casting.ExpStateCasting
import com.futo.platformplayer.experimental_casting.StateCastingDispatcher
import com.futo.platformplayer.fixHtmlLinks import com.futo.platformplayer.fixHtmlLinks
import com.futo.platformplayer.fixHtmlWhitespace import com.futo.platformplayer.fixHtmlWhitespace
import com.futo.platformplayer.getNowDiffSeconds import com.futo.platformplayer.getNowDiffSeconds
@ -1217,12 +1218,8 @@ class VideoDetailView : ConstraintLayout {
_onPauseCalled = true; _onPauseCalled = true;
_taskLoadVideo.cancel(); _taskLoadVideo.cancel();
if (Settings.instance.casting.experimentalCasting) { if (StateCastingDispatcher.isCasting()) {
if(ExpStateCasting.instance.isCasting) return
return;
} else {
if(StateCasting.instance.isCasting)
return;
} }
if(allowBackground) if(allowBackground)
@ -2014,12 +2011,7 @@ class VideoDetailView : ConstraintLayout {
return; return;
} }
val isCasting = if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.isCasting()) {
ExpStateCasting.instance.isCasting
} else {
StateCasting.instance.isCasting
}
if (!isCasting) {
setCastEnabled(false); setCastEnabled(false);
val isLimitedVersion = StatePlatform.instance.getContentClientOrNull(video.url)?.let { val isLimitedVersion = StatePlatform.instance.getContentClientOrNull(video.url)?.let {
@ -2303,11 +2295,7 @@ class VideoDetailView : ConstraintLayout {
} }
val currentPlaybackRate = (if (_isCasting) { val currentPlaybackRate = (if (_isCasting) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.getActiveDeviceSpeed()
ExpStateCasting.instance.activeDevice?.speed
} else {
StateCasting.instance.activeDevice?.speed
}
} else _player.getPlaybackRate()) ?: 1.0 } else _player.getPlaybackRate()) ?: 1.0
_overlay_quality_selector?.groupItems?.firstOrNull { it is SlideUpMenuButtonList && it.id == "playback_rate" }?.let { _overlay_quality_selector?.groupItems?.firstOrNull { it is SlideUpMenuButtonList && it.id == "playback_rate" }?.let {
(it as SlideUpMenuButtonList).setSelected(currentPlaybackRate.toString()) (it as SlideUpMenuButtonList).setSelected(currentPlaybackRate.toString())
@ -2426,18 +2414,12 @@ class VideoDetailView : ConstraintLayout {
?.distinct() ?.distinct()
?.toList() ?: listOf() else audioSources?.toList() ?: listOf(); ?.toList() ?: listOf() else audioSources?.toList() ?: listOf();
val canSetSpeed = !_isCasting || if (Settings.instance.casting.experimentalCasting) { val canSetSpeed = !_isCasting || StateCastingDispatcher.canActiveDeviceSetSpeed();
ExpStateCasting.instance.activeDevice?.device?.supportsFeature(DeviceFeature.SET_SPEED) == true
} else {
StateCasting.instance.activeDevice?.canSetSpeed == true
}
val currentPlaybackRate = if (_isCasting) { val currentPlaybackRate = if (_isCasting) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.getActiveDeviceSpeed()
ExpStateCasting.instance.activeDevice?.speed } else {
} else { _player.getPlaybackRate()
StateCasting.instance.activeDevice?.speed }
}
} else _player.getPlaybackRate()
val qualityPlaybackSpeedTitle = if (canSetSpeed) SlideUpMenuTitle(this.context).apply { setTitle(context.getString(R.string.playback_rate) + " (${String.format("%.2f", currentPlaybackRate)})"); } else null; val qualityPlaybackSpeedTitle = if (canSetSpeed) SlideUpMenuTitle(this.context).apply { setTitle(context.getString(R.string.playback_rate) + " (${String.format("%.2f", currentPlaybackRate)})"); } else null;
_overlay_quality_selector = SlideUpMenuOverlay(this.context, _overlay_quality_container, context.getString( _overlay_quality_selector = SlideUpMenuOverlay(this.context, _overlay_quality_container, context.getString(
R.string.quality), null, true, R.string.quality), null, true,
@ -2452,11 +2434,7 @@ class VideoDetailView : ConstraintLayout {
setButtons(playbackLabels, String.format(Locale.US, format, currentPlaybackRate)); setButtons(playbackLabels, String.format(Locale.US, format, currentPlaybackRate));
onClick.subscribe { v -> onClick.subscribe { v ->
val currentPlaybackSpeed = if (_isCasting) { val currentPlaybackSpeed = if (_isCasting) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.getActiveDeviceSpeed()
ExpStateCasting.instance.activeDevice?.speed
} else {
StateCasting.instance.activeDevice?.speed
}
} else _player.getPlaybackRate(); } else _player.getPlaybackRate();
var playbackSpeedString = v; var playbackSpeedString = v;
val stepSpeed = Settings.instance.playback.getPlaybackSpeedStep(); val stepSpeed = Settings.instance.playback.getPlaybackSpeedStep();
@ -2465,26 +2443,10 @@ class VideoDetailView : ConstraintLayout {
else if(v == "-") else if(v == "-")
playbackSpeedString = String.format(Locale.US, "%.2f", Math.max(0.1, (currentPlaybackSpeed?.toDouble() ?: 1.0) - stepSpeed)).toString(); playbackSpeedString = String.format(Locale.US, "%.2f", Math.max(0.1, (currentPlaybackSpeed?.toDouble() ?: 1.0) - stepSpeed)).toString();
val newPlaybackSpeed = playbackSpeedString.toDouble(); val newPlaybackSpeed = playbackSpeedString.toDouble();
if (_isCasting) { if (_isCasting && StateCastingDispatcher.canActiveDeviceSetSpeed()) {
if (Settings.instance.casting.experimentalCasting) { qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");
val ad = ExpStateCasting.instance.activeDevice ?: return@subscribe StateCastingDispatcher.activeDeviceSetSpeed(newPlaybackSpeed)
if (!ad.device.supportsFeature(DeviceFeature.SET_SPEED)) { setSelected(playbackSpeedString);
return@subscribe
}
qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");
ad.device.changeSpeed(newPlaybackSpeed)
setSelected(playbackSpeedString);
} else {
val ad = StateCasting.instance.activeDevice ?: return@subscribe
if (!ad.canSetSpeed) {
return@subscribe
}
qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");
ad.changeSpeed(newPlaybackSpeed)
setSelected(playbackSpeedString);
}
} else { } else {
qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})"); qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");
_player.setPlaybackRate(playbackSpeedString.toFloat()); _player.setPlaybackRate(playbackSpeedString.toFloat());
@ -2599,14 +2561,8 @@ class VideoDetailView : ConstraintLayout {
//Handlers //Handlers
private fun handlePlay() { private fun handlePlay() {
Logger.i(TAG, "handlePlay") Logger.i(TAG, "handlePlay")
if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.resumeVideo()) {
if (!ExpStateCasting.instance.resumeVideo()) { _player.play()
_player.play()
}
} else {
if (!StateCasting.instance.resumeVideo()) {
_player.play();
}
} }
//TODO: This was needed because handleLowerVolume was done. //TODO: This was needed because handleLowerVolume was done.
@ -2621,60 +2577,31 @@ class VideoDetailView : ConstraintLayout {
private fun handlePause() { private fun handlePause() {
Logger.i(TAG, "handlePause") Logger.i(TAG, "handlePause")
if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.pauseVideo()) {
if (!ExpStateCasting.instance.pauseVideo()) { _player.pause()
_player.pause()
}
} else {
if (!StateCasting.instance.pauseVideo()) {
_player.pause()
}
} }
} }
private fun handleSeek(ms: Long) { private fun handleSeek(ms: Long) {
Logger.i(TAG, "handleSeek(ms=$ms)") Logger.i(TAG, "handleSeek(ms=$ms)")
if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.videoSeekTo(ms.toDouble() / 1000.0)) {
if (!ExpStateCasting.instance.videoSeekTo(ms.toDouble() / 1000.0)) { _player.seekTo(ms)
_player.seekTo(ms)
}
} else {
if (!StateCasting.instance.videoSeekTo(ms.toDouble() / 1000.0)) {
_player.seekTo(ms)
}
} }
} }
private fun handleStop() { private fun handleStop() {
Logger.i(TAG, "handleStop") Logger.i(TAG, "handleStop")
if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.stopVideo()) {
if (!ExpStateCasting.instance.stopVideo()) { _player.stop()
_player.stop()
}
} else {
if (!StateCasting.instance.stopVideo()) {
_player.stop()
}
} }
} }
private fun handlePlayChanged(playing: Boolean) { private fun handlePlayChanged(playing: Boolean) {
Logger.i(TAG, "handlePlayChanged(playing=$playing)") Logger.i(TAG, "handlePlayChanged(playing=$playing)")
if (Settings.instance.casting.experimentalCasting) { if (StateCastingDispatcher.isCasting()) {
val ad = ExpStateCasting.instance.activeDevice; _cast.setIsPlaying(playing);
if (ad != null) {
_cast.setIsPlaying(playing);
} else {
StatePlayer.instance.updateMediaSession( null);
StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0);
}
} else { } else {
val ad = StateCasting.instance.activeDevice; StatePlayer.instance.updateMediaSession( null);
if (ad != null) { StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0);
_cast.setIsPlaying(playing);
} else {
StatePlayer.instance.updateMediaSession( null);
StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0);
}
} }
if(playing) { if(playing) {
@ -2712,26 +2639,20 @@ class VideoDetailView : ConstraintLayout {
fragment.lifecycleScope.launch(Dispatchers.Main) { fragment.lifecycleScope.launch(Dispatchers.Main) {
try { try {
if (Settings.instance.casting.experimentalCasting) { if (StateCastingDispatcher.isConnected()) {
val d = ExpStateCasting.instance.activeDevice; val expectedCurrentTime = StateCastingDispatcher.getExpectedCurrentTime() ?: 0.0
if (d != null && d.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED) val speed = StateCastingDispatcher.getActiveDeviceSpeed() ?: 1.0
castIfAvailable( castIfAvailable(
context.contentResolver, context.contentResolver,
video, video,
videoSource, videoSource,
_lastAudioSource, _lastAudioSource,
_lastSubtitleSource, _lastSubtitleSource,
(d.expectedCurrentTime * 1000.0).toLong(), (expectedCurrentTime * 1000.0).toLong(),
d.speed speed
); )
else if(!_player.swapSources(videoSource, _lastAudioSource, true, true, true)) } else if(!_player.swapSources(videoSource, _lastAudioSource, true, true, true)) {
_player.hideControls(false); //TODO: Disable player? _player.hideControls(false); //TODO: Disable player?
} else {
val d = StateCasting.instance.activeDevice;
if (d != null && d.connectionState == CastConnectionState.CONNECTED)
castIfAvailable(context.contentResolver, video, videoSource, _lastAudioSource, _lastSubtitleSource, (d.expectedCurrentTime * 1000.0).toLong(), d.speed);
else if(!_player.swapSources(videoSource, _lastAudioSource, true, true, true))
_player.hideControls(false); //TODO: Disable player?
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "handleSelectVideoTrack failed", e) Logger.e(TAG, "handleSelectVideoTrack failed", e)
@ -2749,34 +2670,20 @@ class VideoDetailView : ConstraintLayout {
fragment.lifecycleScope.launch(Dispatchers.Main) { fragment.lifecycleScope.launch(Dispatchers.Main) {
try { try {
if (Settings.instance.casting.experimentalCasting) { if (StateCastingDispatcher.isConnected()) {
val d = ExpStateCasting.instance.activeDevice; val expectedCurrentTime = StateCastingDispatcher.getExpectedCurrentTime() ?: 0.0
if (d != null && d.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED) val speed = StateCastingDispatcher.getActiveDeviceSpeed() ?: 1.0
castIfAvailable( castIfAvailable(
context.contentResolver, context.contentResolver,
video, video,
_lastVideoSource, _lastVideoSource,
audioSource, audioSource,
_lastSubtitleSource, _lastSubtitleSource,
(d.expectedCurrentTime * 1000.0).toLong(), (expectedCurrentTime * 1000.0).toLong(),
d.speed speed
) )
else if (!_player.swapSources(_lastVideoSource, audioSource, true, true, true)) } else if (!_player.swapSources(_lastVideoSource, audioSource, true, true, true)) {
_player.hideControls(false); //TODO: Disable player? _player.hideControls(false); //TODO: Disable player?
} else {
val d = StateCasting.instance.activeDevice;
if (d != null && d.connectionState == CastConnectionState.CONNECTED)
castIfAvailable(
context.contentResolver,
video,
_lastVideoSource,
audioSource,
_lastSubtitleSource,
(d.expectedCurrentTime * 1000.0).toLong(),
d.speed
)
else if (!_player.swapSources(_lastVideoSource, audioSource, true, true, true))
_player.hideControls(false); //TODO: Disable player?
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "handleSelectAudioTrack failed", e) Logger.e(TAG, "handleSelectAudioTrack failed", e)
@ -2795,36 +2702,20 @@ class VideoDetailView : ConstraintLayout {
fragment.lifecycleScope.launch(Dispatchers.Main) { fragment.lifecycleScope.launch(Dispatchers.Main) {
try { try {
if (Settings.instance.casting.experimentalCasting) { if (StateCastingDispatcher.isConnected()) {
val d = ExpStateCasting.instance.activeDevice; val expectedCurrentTime = StateCastingDispatcher.getExpectedCurrentTime() ?: 0.0
if (d != null && d.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED) val speed = StateCastingDispatcher.getActiveDeviceSpeed() ?: 1.0
castIfAvailable( castIfAvailable(
context.contentResolver, context.contentResolver,
video, video,
_lastVideoSource, _lastVideoSource,
_lastAudioSource, _lastAudioSource,
toSet, toSet,
(d.expectedCurrentTime * 1000.0).toLong(), (expectedCurrentTime * 1000.0).toLong(),
d.speed speed
); )
else {
_player.swapSubtitles(toSet);
}
} else { } else {
val d = StateCasting.instance.activeDevice; _player.swapSubtitles(toSet);
if (d != null && d.connectionState == CastConnectionState.CONNECTED)
castIfAvailable(
context.contentResolver,
video,
_lastVideoSource,
_lastAudioSource,
toSet,
(d.expectedCurrentTime * 1000.0).toLong(),
d.speed
);
else {
_player.swapSubtitles(toSet);
}
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "handleSelectSubtitleTrack failed", e) Logger.e(TAG, "handleSelectSubtitleTrack failed", e)

View file

@ -28,6 +28,7 @@ import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.experimental_casting.ExpStateCasting import com.futo.platformplayer.experimental_casting.ExpStateCasting
import com.futo.platformplayer.experimental_casting.StateCastingDispatcher
import com.futo.platformplayer.formatDuration import com.futo.platformplayer.formatDuration
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateHistory import com.futo.platformplayer.states.StateHistory
@ -142,13 +143,8 @@ class CastView : ConstraintLayout {
} }
_gestureControlView.onSeek.subscribe { _gestureControlView.onSeek.subscribe {
if (Settings.instance.casting.experimentalCasting) { val expectedCurrentTime = StateCastingDispatcher.getExpectedCurrentTime() ?: return@subscribe
val d = ExpStateCasting.instance.activeDevice ?: return@subscribe; StateCastingDispatcher.videoSeekTo(expectedCurrentTime + it / 1000)
ExpStateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
} else {
val d = StateCasting.instance.activeDevice ?: return@subscribe;
StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
}
}; };
_buttonLoop.setOnClickListener { _buttonLoop.setOnClickListener {
@ -159,45 +155,25 @@ class CastView : ConstraintLayout {
_timeBar.addListener(object : TimeBar.OnScrubListener { _timeBar.addListener(object : TimeBar.OnScrubListener {
override fun onScrubStart(timeBar: TimeBar, position: Long) { override fun onScrubStart(timeBar: TimeBar, position: Long) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.videoSeekTo(position.toDouble())
ExpStateCasting.instance.videoSeekTo(position.toDouble());
} else {
StateCasting.instance.videoSeekTo(position.toDouble());
}
} }
override fun onScrubMove(timeBar: TimeBar, position: Long) { override fun onScrubMove(timeBar: TimeBar, position: Long) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.videoSeekTo(position.toDouble())
ExpStateCasting.instance.videoSeekTo(position.toDouble());
} else {
StateCasting.instance.videoSeekTo(position.toDouble());
}
} }
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.videoSeekTo(position.toDouble())
ExpStateCasting.instance.videoSeekTo(position.toDouble());
} else {
StateCasting.instance.videoSeekTo(position.toDouble());
}
} }
}); });
_buttonMinimize.setOnClickListener { onMinimizeClick.emit(); }; _buttonMinimize.setOnClickListener { onMinimizeClick.emit(); };
_buttonSettings.setOnClickListener { onSettingsClick.emit(); }; _buttonSettings.setOnClickListener { onSettingsClick.emit(); };
_buttonPlay.setOnClickListener { _buttonPlay.setOnClickListener {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.resumeVideo()
ExpStateCasting.instance.resumeVideo()
} else {
StateCasting.instance.resumeVideo()
}
} }
_buttonPause.setOnClickListener { _buttonPause.setOnClickListener {
if (Settings.instance.casting.experimentalCasting) { StateCastingDispatcher.pauseVideo()
ExpStateCasting.instance.pauseVideo()
} else {
StateCasting.instance.pauseVideo()
}
} }
if (!isInEditMode) { if (!isInEditMode) {
@ -311,11 +287,7 @@ class CastView : ConstraintLayout {
_buttonPlay.visibility = View.VISIBLE; _buttonPlay.visibility = View.VISIBLE;
} }
val position = if (Settings.instance.casting.experimentalCasting) { val position = StateCastingDispatcher.getExpectedCurrentTime()?.times(1000.0)?.toLong()
ExpStateCasting.instance.activeDevice?.expectedCurrentTime?.times(1000.0)?.toLong()
} else {
StateCasting.instance.activeDevice?.expectedCurrentTime?.times(1000.0)?.toLong();
}
if(StatePlayer.instance.hasMediaSession()) { if(StatePlayer.instance.hasMediaSession()) {
StatePlayer.instance.updateMediaSession(null); StatePlayer.instance.updateMediaSession(null);
StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), (position ?: 0)); StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), (position ?: 0));
@ -379,20 +351,12 @@ class CastView : ConstraintLayout {
} }
private fun getPlaybackStateCompat(): Int { private fun getPlaybackStateCompat(): Int {
if (Settings.instance.casting.experimentalCasting) { if (!StateCastingDispatcher.isConnected()) {
val d = ExpStateCasting.instance.activeDevice ?: return PlaybackState.STATE_NONE; return PlaybackState.STATE_NONE
}
return when(d.isPlaying) { return when(StateCastingDispatcher.isPlaying()) {
true -> PlaybackStateCompat.STATE_PLAYING; true -> PlaybackStateCompat.STATE_PLAYING;
else -> PlaybackStateCompat.STATE_PAUSED; else -> PlaybackStateCompat.STATE_PAUSED;
}
} else {
val d = StateCasting.instance.activeDevice ?: return PlaybackState.STATE_NONE;
return when(d.isPlaying) {
true -> PlaybackStateCompat.STATE_PLAYING;
else -> PlaybackStateCompat.STATE_PAUSED;
}
} }
} }