Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit speed at live edge #8134

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,13 @@ private void openPlaybackParameterDialog() {
if (player == null) {
return;
}
PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(),
player.getPlaybackSkipSilence(), this).show(getSupportFragmentManager(), TAG);
PlaybackParameterDialog.newInstance(
player.getPlaybackSpeed(),
player.getPlaybackPitch(),
player.getPlaybackSkipSilence(),
player.isLiveEdge(),
this
).show(getSupportFragmentManager(), TAG);
}

@Override
Expand Down
22 changes: 21 additions & 1 deletion app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -1675,10 +1675,26 @@ public PlaybackParameters getPlaybackParameters() {
*/
public void setPlaybackParameters(final float speed, final float pitch,
final boolean skipSilence) {
setPlaybackParameters(speed, pitch, skipSilence, true);
}

/**
* Sets the playback parameters of the player, and optionally saves them to shared preferences.
* Speed and pitch are rounded up to 2 decimal places before being used or saved.
*
* @param speed the playback speed, will be rounded to up to 2 decimal places
* @param pitch the playback pitch, will be rounded to up to 2 decimal places
* @param skipSilence skip silence during playback
* @param savePref should these settings be saved to preferences
*/
public void setPlaybackParameters(final float speed, final float pitch,
final boolean skipSilence, final boolean savePref) {
final float roundedSpeed = Math.round(speed * 100.0f) / 100.0f;
final float roundedPitch = Math.round(pitch * 100.0f) / 100.0f;

savePlaybackParametersToPrefs(this, roundedSpeed, roundedPitch, skipSilence);
if (savePref) {
savePlaybackParametersToPrefs(this, roundedSpeed, roundedPitch, skipSilence);
}
simpleExoPlayer.setPlaybackParameters(
new PlaybackParameters(roundedSpeed, roundedPitch));
simpleExoPlayer.setSkipSilenceEnabled(skipSilence);
Expand Down Expand Up @@ -1717,6 +1733,10 @@ private void onUpdateProgress(final int currentProgress,
}
binding.playbackLiveSync.setClickable(!isLiveEdge());

if (isLiveEdge() && getPlaybackSpeed() > 1.0f) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you notice that this is run constantly in a loop?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did realize this is a function that is executing very frequently, but I wanted the playback speed to reset the moment the live edge was reached and wasn't sure if there was a better place to put the logic.

This logic could be moved to onBuffering(), which would match Youtube's livestreaming behavior and avoid calling the check in a loop. The downside is that the speed reset won't occur immediately after hitting the live edge.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more resource-heavy part here is probably calculating isLiveEdge, seeing it is not a trivial function.

Comment on lines 1734 to +1736
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then probably we can just do this:

Suggested change
binding.playbackLiveSync.setClickable(!isLiveEdge());
if (isLiveEdge() && getPlaybackSpeed() > 1.0f) {
// if live edge of a livestream -> no need for sync button; playback speed must be <= 1.0
final boolean liveEdge = isLiveEdge();
binding.playbackLiveSync.setClickable(!liveEdge);
if (liveEdge && getPlaybackSpeed() > 1.0f) {

setPlaybackParameters(1.0f, getPlaybackPitch(), false, false);
}

notifyProgressUpdateToListeners(currentProgress, duration, bufferPercent);

if (areSegmentsVisible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class PlaybackParameterDialog extends DialogFragment {
// Minimum allowable range in ExoPlayer
private static final double MIN_PITCH_OR_SPEED = 0.10f;
private static final double MAX_PITCH_OR_SPEED = 3.00f;
private static final double MAX_LIVE_EDGE_SPEED = 1.00f;

private static final boolean PITCH_CTRL_MODE_PERCENT = false;
private static final boolean PITCH_CTRL_MODE_SEMITONE = true;
Expand All @@ -63,13 +64,20 @@ public class PlaybackParameterDialog extends DialogFragment {
private static final double DEFAULT_PITCH_PERCENT = 1.00f;
private static final double DEFAULT_STEP = STEP_25_PERCENT_VALUE;
private static final boolean DEFAULT_SKIP_SILENCE = false;
private static final boolean DEFAULT_LIVE_EDGE = false;

private static final SliderStrategy QUADRATIC_STRATEGY = new SliderStrategy.Quadratic(
MIN_PITCH_OR_SPEED,
MAX_PITCH_OR_SPEED,
1.00f,
10_000);

private static final SliderStrategy LIVE_EDGE_QUADRATIC_STRATEGY = new SliderStrategy.Quadratic(
MIN_PITCH_OR_SPEED,
MAX_LIVE_EDGE_SPEED,
0.50f,
10_000);

private static final SliderStrategy SEMITONE_STRATEGY = new SliderStrategy() {
@Override
public int progressOf(final double value) {
Expand Down Expand Up @@ -98,13 +106,16 @@ public double valueOf(final int progress) {
double pitchPercent = DEFAULT_PITCH_PERCENT;
@State
boolean skipSilence = DEFAULT_SKIP_SILENCE;
@State
boolean liveEdge = DEFAULT_LIVE_EDGE;

private DialogPlaybackParameterBinding binding;

public static PlaybackParameterDialog newInstance(
final double playbackTempo,
final double playbackPitch,
final boolean playbackSkipSilence,
final boolean playbackLiveEdge,
final Callback callback
) {
final PlaybackParameterDialog dialog = new PlaybackParameterDialog();
Expand All @@ -117,6 +128,7 @@ public static PlaybackParameterDialog newInstance(
dialog.tempo = dialog.initialTempo;
dialog.pitchPercent = dialog.initialPitchPercent;
dialog.skipSilence = dialog.initialSkipSilence;
dialog.liveEdge = playbackLiveEdge;

return dialog;
}
Expand Down Expand Up @@ -180,14 +192,16 @@ public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {

private void initUI() {
// Tempo
setText(binding.tempoMinimumText, PlayerHelper::formatSpeed, MIN_PITCH_OR_SPEED);
setText(binding.tempoMaximumText, PlayerHelper::formatSpeed, MAX_PITCH_OR_SPEED);
final double maxTempo = liveEdge ? MAX_LIVE_EDGE_SPEED : MAX_PITCH_OR_SPEED;
final SliderStrategy tempoStrategy = getTempoStrategy();
setText(binding.tempoMinimumText, PlayerHelper::formatSpeed, maxTempo);
setText(binding.tempoMaximumText, PlayerHelper::formatSpeed, maxTempo);

binding.tempoSeekbar.setMax(QUADRATIC_STRATEGY.progressOf(MAX_PITCH_OR_SPEED));
binding.tempoSeekbar.setMax(tempoStrategy.progressOf(maxTempo));
setAndUpdateTempo(tempo);
binding.tempoSeekbar.setOnSeekBarChangeListener(
getTempoOrPitchSeekbarChangeListener(
QUADRATIC_STRATEGY,
tempoStrategy,
this::onTempoSliderUpdated));

registerOnStepClickListener(
Expand Down Expand Up @@ -509,6 +523,14 @@ public void onProgressChanged(@Nonnull final SeekBar seekBar,
};
}

private SliderStrategy getTempoStrategy() {
if (liveEdge) {
return LIVE_EDGE_QUADRATIC_STRATEGY;
} else {
return QUADRATIC_STRATEGY;
}
}

private void onTempoSliderUpdated(final double newTempo) {
if (!binding.unhookCheckbox.isChecked()) {
setSliders(newTempo);
Expand All @@ -533,7 +555,7 @@ private void setSliders(final double newValue) {
private void setAndUpdateTempo(final double newTempo) {
this.tempo = calcValidTempo(newTempo);

binding.tempoSeekbar.setProgress(QUADRATIC_STRATEGY.progressOf(tempo));
binding.tempoSeekbar.setProgress(getTempoStrategy().progressOf(tempo));
setText(binding.tempoCurrentText, PlayerHelper::formatSpeed, tempo);
}

Expand All @@ -551,7 +573,8 @@ private void setAndUpdatePitch(final double newPitch) {
}

private double calcValidTempo(final double newTempo) {
return Math.max(MIN_PITCH_OR_SPEED, Math.min(MAX_PITCH_OR_SPEED, newTempo));
final double maxTempo = liveEdge ? MAX_LIVE_EDGE_SPEED : MAX_PITCH_OR_SPEED;
return Math.max(MIN_PITCH_OR_SPEED, Math.min(maxTempo, newTempo));
}

private double calcValidPitch(final double newPitch) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class PlaybackSpeedClickListener(
PlaybackParameterDialog.newInstance(
player.playbackSpeed.toDouble(),
player.playbackPitch.toDouble(),
player.playbackSkipSilence
player.playbackSkipSilence,
player.isLiveEdge
) { speed: Float, pitch: Float, skipSilence: Boolean ->
player.setPlaybackParameters(
speed,
Expand Down