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.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/*
|
||||
class SyncServerTests {
|
||||
|
||||
//private val relayHost = "relay.grayjay.app"
|
||||
@@ -335,4 +335,4 @@ class SyncServerTests {
|
||||
|
||||
class AlwaysAuthorized : IAuthorizable {
|
||||
override val isAuthorized: Boolean get() = true
|
||||
}
|
||||
}*/
|
||||
@@ -13,7 +13,7 @@ import kotlin.random.Random
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/*
|
||||
data class PipeStreams(
|
||||
val initiatorInput: LittleEndianDataInputStream,
|
||||
val initiatorOutput: LittleEndianDataOutputStream,
|
||||
@@ -509,4 +509,4 @@ class Authorized : IAuthorizable {
|
||||
|
||||
class Unauthorized : IAuthorizable {
|
||||
override val isAuthorized: Boolean = false
|
||||
}
|
||||
}*/
|
||||
@@ -584,6 +584,23 @@ class Settings : FragmentedStorageFileJson() {
|
||||
playbackSpeeds.sort();
|
||||
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)
|
||||
|
||||
@@ -35,7 +35,7 @@ class ChromecastCastingDevice : CastingDevice {
|
||||
override var usedRemoteAddress: InetAddress? = null;
|
||||
override var localAddress: InetAddress? = null;
|
||||
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 port: Int = 0;
|
||||
@@ -144,6 +144,23 @@ class ChromecastCastingDevice : CastingDevice {
|
||||
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) {
|
||||
if (invokeInIOScopeIfRequired({ changeVolume(volume) })) {
|
||||
return;
|
||||
|
||||
@@ -39,6 +39,9 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class GestureControlView : LinearLayout {
|
||||
@@ -79,6 +82,9 @@ class GestureControlView : LinearLayout {
|
||||
private var _adjustingFullscreenDown: Boolean = false;
|
||||
private var _fullScreenFactorUp = 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 _scaleFactor = 1.0f
|
||||
@@ -94,6 +100,10 @@ class GestureControlView : LinearLayout {
|
||||
private var _layoutIndicatorFit: FrameLayout;
|
||||
private var _speedHolding = false
|
||||
|
||||
private val _speedFormatter = DecimalFormat("#.##", DecimalFormatSymbols(Locale.US)).apply {
|
||||
roundingMode = java.math.RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
private val _gestureController: GestureDetectorCompat;
|
||||
|
||||
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);
|
||||
_layoutIndicatorFill = findViewById(R.id.layout_indicator_fill);
|
||||
_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() {
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
@@ -229,6 +242,7 @@ class GestureControlView : LinearLayout {
|
||||
&& !_isPanning
|
||||
&& !_isZooming) {
|
||||
_speedHolding = true
|
||||
showHoldSpeedControls()
|
||||
onSpeedHoldStart.emit()
|
||||
}
|
||||
}
|
||||
@@ -316,6 +330,17 @@ class GestureControlView : LinearLayout {
|
||||
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) {
|
||||
_layoutControls = layoutControls;
|
||||
_background = background;
|
||||
@@ -326,6 +351,7 @@ class GestureControlView : LinearLayout {
|
||||
|
||||
if (ev.action == MotionEvent.ACTION_UP && _speedHolding) {
|
||||
_speedHolding = false
|
||||
hideHoldSpeedControls()
|
||||
onSpeedHoldEnd.emit()
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.media3.ui.DefaultTimeBar
|
||||
import androidx.media3.ui.TimeBar
|
||||
import com.bumptech.glide.Glide
|
||||
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.video.IPlatformVideoDetails
|
||||
import com.futo.platformplayer.casting.AirPlayCastingDevice
|
||||
@@ -58,6 +59,8 @@ class CastView : ConstraintLayout {
|
||||
private var _inPictureInPicture: Boolean = false;
|
||||
private var _chapters: List<IChapter>? = null;
|
||||
private var _currentChapter: IChapter? = null;
|
||||
private var _speedHoldPrevRate = 1.0
|
||||
private var _speedHoldWasPlaying = false
|
||||
|
||||
val onChapterChanged = Event2<IChapter?, Boolean>();
|
||||
val onMinimizeClick = Event0();
|
||||
@@ -87,6 +90,20 @@ class CastView : ConstraintLayout {
|
||||
_gestureControlView = findViewById(R.id.gesture_control);
|
||||
_gestureControlView.fullScreenGestureEnabled = false
|
||||
_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 {
|
||||
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
||||
StateCasting.instance.videoSeekTo(d.expectedCurrentTime + it / 1000);
|
||||
|
||||
@@ -261,7 +261,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
exoPlayer?.player?.let { player ->
|
||||
_speedHoldWasPlaying = player.isPlaying
|
||||
_speedHoldPrevRate = getPlaybackRate()
|
||||
setPlaybackRate(2f)
|
||||
setPlaybackRate(Settings.instance.playback.getHoldPlaybackSpeed().toFloat())
|
||||
player.play()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,4 +195,39 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
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>
|
||||
@@ -433,6 +433,8 @@
|
||||
<string name="min_playback_speed_description">Minimum Available Speed</string>
|
||||
<string name="max_playback_speed">Maximum Playback 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_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>
|
||||
@@ -1106,6 +1108,16 @@
|
||||
<item>4.0</item>
|
||||
<item>5.0</item>
|
||||
</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">
|
||||
<item>0.25</item>
|
||||
<item>0.5</item>
|
||||
|
||||
Reference in New Issue
Block a user