mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Added setting for hold playback speed increase. Implemented chromecast playback rate adjustment in range [1, 2]. Implemented hold playback speed increase pill.
This commit is contained in:
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer
|
|||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
/*
|
||||||
class SyncServerTests {
|
class SyncServerTests {
|
||||||
|
|
||||||
//private val relayHost = "relay.grayjay.app"
|
//private val relayHost = "relay.grayjay.app"
|
||||||
@@ -335,4 +335,4 @@ class SyncServerTests {
|
|||||||
|
|
||||||
class AlwaysAuthorized : IAuthorizable {
|
class AlwaysAuthorized : IAuthorizable {
|
||||||
override val isAuthorized: Boolean get() = true
|
override val isAuthorized: Boolean get() = true
|
||||||
}
|
}*/
|
||||||
@@ -13,7 +13,7 @@ import kotlin.random.Random
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
/*
|
||||||
data class PipeStreams(
|
data class PipeStreams(
|
||||||
val initiatorInput: LittleEndianDataInputStream,
|
val initiatorInput: LittleEndianDataInputStream,
|
||||||
val initiatorOutput: LittleEndianDataOutputStream,
|
val initiatorOutput: LittleEndianDataOutputStream,
|
||||||
@@ -509,4 +509,4 @@ class Authorized : IAuthorizable {
|
|||||||
|
|
||||||
class Unauthorized : IAuthorizable {
|
class Unauthorized : IAuthorizable {
|
||||||
override val isAuthorized: Boolean = false
|
override val isAuthorized: Boolean = false
|
||||||
}
|
}*/
|
||||||
@@ -584,6 +584,23 @@ class Settings : FragmentedStorageFileJson() {
|
|||||||
playbackSpeeds.sort();
|
playbackSpeeds.sort();
|
||||||
return playbackSpeeds;
|
return playbackSpeeds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FormField(R.string.hold_playback_speed, FieldForm.DROPDOWN, R.string.hold_playback_speed_description, 27)
|
||||||
|
@DropdownFieldOptionsId(R.array.hold_playback_speeds)
|
||||||
|
var holdPlaybackSpeed: Int = 3;
|
||||||
|
|
||||||
|
fun getHoldPlaybackSpeed(): Double {
|
||||||
|
return when(holdPlaybackSpeed) {
|
||||||
|
0 -> 1.25
|
||||||
|
1 -> 1.5
|
||||||
|
2 -> 1.75
|
||||||
|
3 -> 2.0
|
||||||
|
4 -> 2.25
|
||||||
|
5 -> 2.5
|
||||||
|
6 -> 2.75
|
||||||
|
else -> 3.0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FormField(R.string.comments, "group", R.string.comments_description, 6)
|
@FormField(R.string.comments, "group", R.string.comments_description, 6)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ChromecastCastingDevice : CastingDevice {
|
|||||||
override var usedRemoteAddress: InetAddress? = null;
|
override var usedRemoteAddress: InetAddress? = null;
|
||||||
override var localAddress: InetAddress? = null;
|
override var localAddress: InetAddress? = null;
|
||||||
override val canSetVolume: Boolean get() = true;
|
override val canSetVolume: Boolean get() = true;
|
||||||
override val canSetSpeed: Boolean get() = false; //TODO: Implement
|
override val canSetSpeed: Boolean get() = true;
|
||||||
|
|
||||||
var addresses: Array<InetAddress>? = null;
|
var addresses: Array<InetAddress>? = null;
|
||||||
var port: Int = 0;
|
var port: Int = 0;
|
||||||
@@ -144,6 +144,23 @@ class ChromecastCastingDevice : CastingDevice {
|
|||||||
sendChannelMessage("sender-0", transportId, "urn:x-cast:com.google.cast.media", json);
|
sendChannelMessage("sender-0", transportId, "urn:x-cast:com.google.cast.media", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun changeSpeed(speed: Double) {
|
||||||
|
if (invokeInIOScopeIfRequired { changeSpeed(speed) }) return
|
||||||
|
|
||||||
|
val speedClamped = speed.coerceAtLeast(1.0).coerceAtLeast(1.0).coerceAtMost(2.0)
|
||||||
|
setSpeed(speedClamped)
|
||||||
|
val mediaSessionId = _mediaSessionId ?: return
|
||||||
|
val transportId = _transportId ?: return
|
||||||
|
val setSpeedObject = JSONObject().apply {
|
||||||
|
put("type", "SET_PLAYBACK_RATE")
|
||||||
|
put("mediaSessionId", mediaSessionId)
|
||||||
|
put("playbackRate", speedClamped)
|
||||||
|
put("requestId", _requestId++)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChannelMessage(sourceId = "sender-0", destinationId = transportId, namespace = "urn:x-cast:com.google.cast.media", json = setSpeedObject.toString())
|
||||||
|
}
|
||||||
|
|
||||||
override fun changeVolume(volume: Double) {
|
override fun changeVolume(volume: Double) {
|
||||||
if (invokeInIOScopeIfRequired({ changeVolume(volume) })) {
|
if (invokeInIOScopeIfRequired({ changeVolume(volume) })) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
class GestureControlView : LinearLayout {
|
class GestureControlView : LinearLayout {
|
||||||
@@ -79,6 +82,9 @@ class GestureControlView : LinearLayout {
|
|||||||
private var _adjustingFullscreenDown: Boolean = false;
|
private var _adjustingFullscreenDown: Boolean = false;
|
||||||
private var _fullScreenFactorUp = 1.0f;
|
private var _fullScreenFactorUp = 1.0f;
|
||||||
private var _fullScreenFactorDown = 1.0f;
|
private var _fullScreenFactorDown = 1.0f;
|
||||||
|
private val _layoutHoldSpeed: LinearLayout
|
||||||
|
private val _textHoldFastForward: TextView
|
||||||
|
private val _imageHoldFastForward: ImageView
|
||||||
|
|
||||||
private var _scaleGestureDetector: ScaleGestureDetector
|
private var _scaleGestureDetector: ScaleGestureDetector
|
||||||
private var _scaleFactor = 1.0f
|
private var _scaleFactor = 1.0f
|
||||||
@@ -94,6 +100,10 @@ class GestureControlView : LinearLayout {
|
|||||||
private var _layoutIndicatorFit: FrameLayout;
|
private var _layoutIndicatorFit: FrameLayout;
|
||||||
private var _speedHolding = false
|
private var _speedHolding = false
|
||||||
|
|
||||||
|
private val _speedFormatter = DecimalFormat("#.##", DecimalFormatSymbols(Locale.US)).apply {
|
||||||
|
roundingMode = java.math.RoundingMode.HALF_UP
|
||||||
|
}
|
||||||
|
|
||||||
private val _gestureController: GestureDetectorCompat;
|
private val _gestureController: GestureDetectorCompat;
|
||||||
|
|
||||||
val isUserGesturing get() = _rewinding || _skipping || _adjustingBrightness || _adjustingSound || _adjustingFullscreenUp || _adjustingFullscreenDown || _isPanning || _isZooming;
|
val isUserGesturing get() = _rewinding || _skipping || _adjustingBrightness || _adjustingSound || _adjustingFullscreenUp || _adjustingFullscreenDown || _isPanning || _isZooming;
|
||||||
@@ -127,6 +137,9 @@ class GestureControlView : LinearLayout {
|
|||||||
_layoutControlsFullscreen = findViewById(R.id.layout_controls_fullscreen);
|
_layoutControlsFullscreen = findViewById(R.id.layout_controls_fullscreen);
|
||||||
_layoutIndicatorFill = findViewById(R.id.layout_indicator_fill);
|
_layoutIndicatorFill = findViewById(R.id.layout_indicator_fill);
|
||||||
_layoutIndicatorFit = findViewById(R.id.layout_indicator_fit);
|
_layoutIndicatorFit = findViewById(R.id.layout_indicator_fit);
|
||||||
|
_layoutHoldSpeed = findViewById(R.id.layout_controls_increased_speed)
|
||||||
|
_textHoldFastForward = findViewById(R.id.text_holdFastForward)
|
||||||
|
_imageHoldFastForward = findViewById(R.id.image_holdFastForward)
|
||||||
|
|
||||||
_scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
_scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||||
@@ -229,6 +242,7 @@ class GestureControlView : LinearLayout {
|
|||||||
&& !_isPanning
|
&& !_isPanning
|
||||||
&& !_isZooming) {
|
&& !_isZooming) {
|
||||||
_speedHolding = true
|
_speedHolding = true
|
||||||
|
showHoldSpeedControls()
|
||||||
onSpeedHoldStart.emit()
|
onSpeedHoldStart.emit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,6 +330,17 @@ class GestureControlView : LinearLayout {
|
|||||||
onPan.emit(_translationX, _translationY)
|
onPan.emit(_translationX, _translationY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showHoldSpeedControls() {
|
||||||
|
_layoutHoldSpeed.visibility = View.VISIBLE
|
||||||
|
_textHoldFastForward.text = _speedFormatter.format(Settings.instance.playback.getHoldPlaybackSpeed()) + "x"
|
||||||
|
(_imageHoldFastForward.drawable as? Animatable)?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideHoldSpeedControls() {
|
||||||
|
_layoutHoldSpeed.visibility = View.GONE
|
||||||
|
(_imageHoldFastForward.drawable as? Animatable)?.stop()
|
||||||
|
}
|
||||||
|
|
||||||
fun setupTouchArea(layoutControls: ViewGroup? = null, background: View? = null) {
|
fun setupTouchArea(layoutControls: ViewGroup? = null, background: View? = null) {
|
||||||
_layoutControls = layoutControls;
|
_layoutControls = layoutControls;
|
||||||
_background = background;
|
_background = background;
|
||||||
@@ -326,6 +351,7 @@ class GestureControlView : LinearLayout {
|
|||||||
|
|
||||||
if (ev.action == MotionEvent.ACTION_UP && _speedHolding) {
|
if (ev.action == MotionEvent.ACTION_UP && _speedHolding) {
|
||||||
_speedHolding = false
|
_speedHolding = false
|
||||||
|
hideHoldSpeedControls()
|
||||||
onSpeedHoldEnd.emit()
|
onSpeedHoldEnd.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import androidx.media3.ui.DefaultTimeBar
|
|||||||
import androidx.media3.ui.TimeBar
|
import androidx.media3.ui.TimeBar
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.api.media.models.chapters.IChapter
|
import com.futo.platformplayer.api.media.models.chapters.IChapter
|
||||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
import com.futo.platformplayer.casting.AirPlayCastingDevice
|
import com.futo.platformplayer.casting.AirPlayCastingDevice
|
||||||
@@ -58,6 +59,8 @@ class CastView : ConstraintLayout {
|
|||||||
private var _inPictureInPicture: Boolean = false;
|
private var _inPictureInPicture: Boolean = false;
|
||||||
private var _chapters: List<IChapter>? = null;
|
private var _chapters: List<IChapter>? = null;
|
||||||
private var _currentChapter: IChapter? = null;
|
private var _currentChapter: IChapter? = null;
|
||||||
|
private var _speedHoldPrevRate = 1.0
|
||||||
|
private var _speedHoldWasPlaying = false
|
||||||
|
|
||||||
val onChapterChanged = Event2<IChapter?, Boolean>();
|
val onChapterChanged = Event2<IChapter?, Boolean>();
|
||||||
val onMinimizeClick = Event0();
|
val onMinimizeClick = Event0();
|
||||||
@@ -87,6 +90,20 @@ class CastView : ConstraintLayout {
|
|||||||
_gestureControlView = findViewById(R.id.gesture_control);
|
_gestureControlView = findViewById(R.id.gesture_control);
|
||||||
_gestureControlView.fullScreenGestureEnabled = false
|
_gestureControlView.fullScreenGestureEnabled = false
|
||||||
_gestureControlView.setupTouchArea();
|
_gestureControlView.setupTouchArea();
|
||||||
|
_gestureControlView.onSpeedHoldStart.subscribe {
|
||||||
|
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
||||||
|
_speedHoldWasPlaying = d.isPlaying
|
||||||
|
_speedHoldPrevRate = d.speed
|
||||||
|
if (d.canSetSpeed)
|
||||||
|
d.changeSpeed(Settings.instance.playback.getHoldPlaybackSpeed())
|
||||||
|
d.resumeVideo()
|
||||||
|
}
|
||||||
|
_gestureControlView.onSpeedHoldEnd.subscribe {
|
||||||
|
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
||||||
|
if (!_speedHoldWasPlaying) d.pauseVideo()
|
||||||
|
d.changeSpeed(_speedHoldPrevRate)
|
||||||
|
}
|
||||||
|
|
||||||
_gestureControlView.onSeek.subscribe {
|
_gestureControlView.onSeek.subscribe {
|
||||||
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
||||||
StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
|
StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
|||||||
exoPlayer?.player?.let { player ->
|
exoPlayer?.player?.let { player ->
|
||||||
_speedHoldWasPlaying = player.isPlaying
|
_speedHoldWasPlaying = player.isPlaying
|
||||||
_speedHoldPrevRate = getPlaybackRate()
|
_speedHoldPrevRate = getPlaybackRate()
|
||||||
setPlaybackRate(2f)
|
setPlaybackRate(Settings.instance.playback.getHoldPlaybackSpeed().toFloat())
|
||||||
player.play()
|
player.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,4 +195,39 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_controls_increased_speed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@drawable/background_pill_black"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_holdFastForward"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="2x"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_regular" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_holdFastForward"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="8dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
app:srcCompat="@drawable/ic_fastforward_animated"
|
||||||
|
android:layout_marginStart="4dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -433,6 +433,8 @@
|
|||||||
<string name="min_playback_speed_description">Minimum Available Speed</string>
|
<string name="min_playback_speed_description">Minimum Available Speed</string>
|
||||||
<string name="max_playback_speed">Maximum Playback Speed</string>
|
<string name="max_playback_speed">Maximum Playback Speed</string>
|
||||||
<string name="max_playback_speed_description">Maximum Available Speed</string>
|
<string name="max_playback_speed_description">Maximum Available Speed</string>
|
||||||
|
<string name="hold_playback_speed">Hold playback speed</string>
|
||||||
|
<string name="hold_playback_speed_description">Playback speed when pressing down on the video</string>
|
||||||
<string name="step_playback_speed">Playback Speed Step Size</string>
|
<string name="step_playback_speed">Playback Speed Step Size</string>
|
||||||
<string name="step_playback_speed_description">The step size of playback speeds, may not affect higher playback speeds.</string>
|
<string name="step_playback_speed_description">The step size of playback speeds, may not affect higher playback speeds.</string>
|
||||||
<string name="seek_offset_description">Fast-Forward / Fast-Rewind duration</string>
|
<string name="seek_offset_description">Fast-Forward / Fast-Rewind duration</string>
|
||||||
@@ -1106,6 +1108,16 @@
|
|||||||
<item>4.0</item>
|
<item>4.0</item>
|
||||||
<item>5.0</item>
|
<item>5.0</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="hold_playback_speeds">
|
||||||
|
<item>1.25</item>
|
||||||
|
<item>1.5</item>
|
||||||
|
<item>1.75</item>
|
||||||
|
<item>2.0</item>
|
||||||
|
<item>2.25</item>
|
||||||
|
<item>2.5</item>
|
||||||
|
<item>2.75</item>
|
||||||
|
<item>3.0</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="min_playback_speed">
|
<string-array name="min_playback_speed">
|
||||||
<item>0.25</item>
|
<item>0.25</item>
|
||||||
<item>0.5</item>
|
<item>0.5</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user