Add video speed control slider.

This commit is contained in:
John Preston 2020-01-28 15:41:08 +03:00
parent b88219902f
commit 87cc18aff8
8 changed files with 74 additions and 5 deletions

View File

@ -23,6 +23,14 @@ constexpr auto kVersionTag = -1;
constexpr auto kVersion = 1; constexpr auto kVersion = 1;
constexpr auto kMaxSavedPlaybackPositions = 16; constexpr auto kMaxSavedPlaybackPositions = 16;
[[nodiscard]] qint32 SerializePlaybackSpeed(float64 speed) {
return int(std::round(std::clamp(speed * 4., 2., 8.))) - 2;
}
float64 DeserializePlaybackSpeed(qint32 speed) {
return (std::clamp(speed, 0, 6) + 2) / 4.;
}
} // namespace } // namespace
Settings::Variables::Variables() Settings::Variables::Variables()
@ -98,6 +106,7 @@ QByteArray Settings::serialize() const {
for (const auto &[id, time] : _variables.mediaLastPlaybackPosition) { for (const auto &[id, time] : _variables.mediaLastPlaybackPosition) {
stream << quint64(id) << qint64(time); stream << quint64(id) << qint64(time);
} }
stream << qint32(SerializePlaybackSpeed(_variables.videoPlaybackSpeed.current()));
} }
return result; return result;
} }
@ -148,6 +157,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) {
qint32 suggestStickersByEmoji = _variables.suggestStickersByEmoji ? 1 : 0; qint32 suggestStickersByEmoji = _variables.suggestStickersByEmoji ? 1 : 0;
qint32 spellcheckerEnabled = _variables.spellcheckerEnabled.current() ? 1 : 0; qint32 spellcheckerEnabled = _variables.spellcheckerEnabled.current() ? 1 : 0;
std::vector<std::pair<DocumentId, crl::time>> mediaLastPlaybackPosition; std::vector<std::pair<DocumentId, crl::time>> mediaLastPlaybackPosition;
qint32 videoPlaybackSpeed = SerializePlaybackSpeed(_variables.videoPlaybackSpeed.current());
stream >> versionTag; stream >> versionTag;
if (versionTag == kVersionTag) { if (versionTag == kVersionTag) {
@ -268,6 +278,9 @@ void Settings::constructFromSerialized(const QByteArray &serialized) {
} }
} }
} }
if (!stream.atEnd()) {
stream >> videoPlaybackSpeed;
}
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
LOG(("App Error: " LOG(("App Error: "
"Bad data for Main::Settings::constructFromSerialized()")); "Bad data for Main::Settings::constructFromSerialized()"));
@ -355,6 +368,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) {
_variables.suggestStickersByEmoji = (suggestStickersByEmoji == 1); _variables.suggestStickersByEmoji = (suggestStickersByEmoji == 1);
_variables.spellcheckerEnabled = (spellcheckerEnabled == 1); _variables.spellcheckerEnabled = (spellcheckerEnabled == 1);
_variables.mediaLastPlaybackPosition = std::move(mediaLastPlaybackPosition); _variables.mediaLastPlaybackPosition = std::move(mediaLastPlaybackPosition);
_variables.videoPlaybackSpeed = DeserializePlaybackSpeed(videoPlaybackSpeed);
} }
void Settings::setSupportChatsTimeSlice(int slice) { void Settings::setSupportChatsTimeSlice(int slice) {

View File

@ -241,6 +241,13 @@ public:
return _variables.spellcheckerEnabled.changes(); return _variables.spellcheckerEnabled.changes();
} }
[[nodiscard]] float64 videoPlaybackSpeed() const {
return _variables.videoPlaybackSpeed.current();
}
void setVideoPlaybackSpeed(float64 speed) {
_variables.videoPlaybackSpeed = speed;
}
private: private:
struct Variables { struct Variables {
Variables(); Variables();
@ -281,6 +288,7 @@ private:
bool suggestStickersByEmoji = true; bool suggestStickersByEmoji = true;
rpl::variable<bool> spellcheckerEnabled = true; rpl::variable<bool> spellcheckerEnabled = true;
std::vector<std::pair<DocumentId, crl::time>> mediaLastPlaybackPosition; std::vector<std::pair<DocumentId, crl::time>> mediaLastPlaybackPosition;
rpl::variable<float64> videoPlaybackSpeed = 1.;
static constexpr auto kDefaultSupportChatsLimitSlice static constexpr auto kDefaultSupportChatsLimitSlice
= 7 * 24 * 60 * 60; = 7 * 24 * 60 * 60;

View File

@ -958,6 +958,7 @@ void Player::unlock() {
--_locks; --_locks;
if (!_locks) { if (!_locks) {
stopAudio(); stopAudio();
setSpeed(1.);
setWaitForMarkAsShown(true); setWaitForMarkAsShown(true);
} }
} }

View File

@ -2361,8 +2361,11 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
if (!_streamed->withSound) { if (!_streamed->withSound) {
options.mode = Streaming::Mode::Video; options.mode = Streaming::Mode::Video;
options.loop = true; options.loop = true;
} else if (_pip) { } else {
_pip = nullptr; options.speed = _doc->session().settings().videoPlaybackSpeed();
if (_pip) {
_pip = nullptr;
}
} }
_streamed->instance.play(options); _streamed->instance.play(options);
if (_streamingStartPaused) { if (_streamingStartPaused) {
@ -2397,13 +2400,29 @@ void OverlayWidget::playbackControlsVolumeChanged(float64 volume) {
Global::SetVideoVolume(volume); Global::SetVideoVolume(volume);
updateMixerVideoVolume(); updateMixerVideoVolume();
Global::RefVideoVolumeChanged().notify(); Global::RefVideoVolumeChanged().notify();
Auth().saveSettingsDelayed(); if (_doc) {
_doc->session().saveSettingsDelayed();
}
} }
float64 OverlayWidget::playbackControlsCurrentVolume() { float64 OverlayWidget::playbackControlsCurrentVolume() {
return Global::VideoVolume(); return Global::VideoVolume();
} }
void OverlayWidget::playbackControlsSpeedChanged(float64 speed) {
if (_doc) {
_doc->session().settings().setVideoPlaybackSpeed(speed);
_doc->session().saveSettingsDelayed();
}
if (_streamed && !videoIsGifv()) {
_streamed->instance.setSpeed(speed);
}
}
float64 OverlayWidget::playbackControlsCurrentSpeed() {
return _doc ? _doc->session().settings().videoPlaybackSpeed() : 1.;
}
void OverlayWidget::switchToPip() { void OverlayWidget::switchToPip() {
const auto document = _doc; const auto document = _doc;
const auto msgId = _msgid; const auto msgId = _msgid;

View File

@ -172,6 +172,8 @@ private:
void playbackControlsSeekFinished(crl::time position) override; void playbackControlsSeekFinished(crl::time position) override;
void playbackControlsVolumeChanged(float64 volume) override; void playbackControlsVolumeChanged(float64 volume) override;
float64 playbackControlsCurrentVolume() override; float64 playbackControlsCurrentVolume() override;
void playbackControlsSpeedChanged(float64 speed);
float64 playbackControlsCurrentSpeed() override;
void playbackControlsToFullScreen() override; void playbackControlsToFullScreen() override;
void playbackControlsFromFullScreen() override; void playbackControlsFromFullScreen() override;
void playbackControlsToPictureInPicture() override; void playbackControlsToPictureInPicture() override;

View File

@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/streaming/media_streaming_document.h" #include "media/streaming/media_streaming_document.h"
#include "media/streaming/media_streaming_utility.h" #include "media/streaming/media_streaming_utility.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "main/main_session.h"
#include "main/main_settings.h"
#include "data/data_document.h"
#include "core/application.h" #include "core/application.h"
#include "ui/platform/ui_platform_utility.h" #include "ui/platform/ui_platform_utility.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
@ -721,6 +724,9 @@ void Pip::restartAtSeekPosition(crl::time position) {
auto options = Streaming::PlaybackOptions(); auto options = Streaming::PlaybackOptions();
options.position = position; options.position = position;
options.audioId = _instance.player().prepareLegacyState().id; options.audioId = _instance.player().prepareLegacyState().id;
options.speed = options.audioId.audio()
? options.audioId.audio()->session().settings().videoPlaybackSpeed()
: 1.;
_instance.play(options); _instance.play(options);
updatePlaybackState(); updatePlaybackState();
} }

View File

@ -30,6 +30,7 @@ PlaybackControls::PlaybackControls(
, _playbackSlider(this, st::mediaviewPlayback) , _playbackSlider(this, st::mediaviewPlayback)
, _playbackProgress(std::make_unique<PlaybackProgress>()) , _playbackProgress(std::make_unique<PlaybackProgress>())
, _volumeController(this, st::mediaviewPlayback) , _volumeController(this, st::mediaviewPlayback)
, _speedController(this, st::mediaviewPlayback)
, _fullScreenToggle(this, st::mediaviewFullScreenButton) , _fullScreenToggle(this, st::mediaviewFullScreenButton)
, _pictureInPicture(this, st::mediaviewFullScreenButton) , _pictureInPicture(this, st::mediaviewFullScreenButton)
, _playedAlready(this, st::mediaviewPlayProgressLabel) , _playedAlready(this, st::mediaviewPlayProgressLabel)
@ -54,6 +55,12 @@ PlaybackControls::PlaybackControls(
_volumeController->setChangeProgressCallback([=](float64 value) { _volumeController->setChangeProgressCallback([=](float64 value) {
_delegate->playbackControlsVolumeChanged(value); _delegate->playbackControlsVolumeChanged(value);
}); });
_speedController->setPseudoDiscrete(
7,
[=](int index) { return (index + 2) / 4.; },
_delegate->playbackControlsCurrentSpeed(),
[=](float64 speed) { _delegate->playbackControlsSpeedChanged(speed); });
_speedController->setAlwaysDisplayMarker(false);
_playPauseResume->addClickHandler([=] { _playPauseResume->addClickHandler([=] {
if (_showPause) { if (_showPause) {
@ -121,6 +128,7 @@ void PlaybackControls::startFading(Callback start) {
showChildren(); showChildren();
_playbackSlider->disablePaint(true); _playbackSlider->disablePaint(true);
_volumeController->disablePaint(true); _volumeController->disablePaint(true);
_speedController->disablePaint(true);
_childrenHidden = false; _childrenHidden = false;
} }
start(); start();
@ -128,7 +136,8 @@ void PlaybackControls::startFading(Callback start) {
for (const auto child : children()) { for (const auto child : children()) {
if (child->isWidgetType() if (child->isWidgetType()
&& child != _playbackSlider && child != _playbackSlider
&& child != _volumeController) { && child != _volumeController
&& child != _speedController) {
static_cast<QWidget*>(child)->hide(); static_cast<QWidget*>(child)->hide();
} }
} }
@ -138,6 +147,7 @@ void PlaybackControls::startFading(Callback start) {
} }
_playbackSlider->disablePaint(false); _playbackSlider->disablePaint(false);
_volumeController->disablePaint(false); _volumeController->disablePaint(false);
_speedController->disablePaint(false);
} }
void PlaybackControls::showAnimated() { void PlaybackControls::showAnimated() {
@ -159,6 +169,7 @@ void PlaybackControls::fadeFinished() {
void PlaybackControls::fadeUpdated(float64 opacity) { void PlaybackControls::fadeUpdated(float64 opacity) {
_playbackSlider->setFadeOpacity(opacity); _playbackSlider->setFadeOpacity(opacity);
_volumeController->setFadeOpacity(opacity); _volumeController->setFadeOpacity(opacity);
_speedController->setFadeOpacity(opacity);
} }
void PlaybackControls::updatePlayback(const Player::TrackState &state) { void PlaybackControls::updatePlayback(const Player::TrackState &state) {
@ -306,8 +317,12 @@ void PlaybackControls::resizeEvent(QResizeEvent *e) {
_pictureInPicture->moveToLeft(left, playTop); _pictureInPicture->moveToLeft(left, playTop);
const auto volumeTop = playTop + (_fullScreenToggle->height() - _volumeController->height()) / 2;
_volumeController->resize(st::mediaviewVolumeWidth, st::mediaviewPlayback.seekSize.height()); _volumeController->resize(st::mediaviewVolumeWidth, st::mediaviewPlayback.seekSize.height());
_volumeController->moveToRight(skip, playTop + (_fullScreenToggle->height() - _volumeController->height()) / 2); _volumeController->moveToRight(skip, volumeTop);
_speedController->resize(st::mediaviewVolumeWidth, st::mediaviewPlayback.seekSize.height());
_speedController->moveToRight(skip + _volumeController->width() + skip, volumeTop);
} }
void PlaybackControls::paintEvent(QPaintEvent *e) { void PlaybackControls::paintEvent(QPaintEvent *e) {
@ -320,6 +335,7 @@ void PlaybackControls::paintEvent(QPaintEvent *e) {
showChildren(); showChildren();
_playbackSlider->setFadeOpacity(1.); _playbackSlider->setFadeOpacity(1.);
_volumeController->setFadeOpacity(1.); _volumeController->setFadeOpacity(1.);
_speedController->setFadeOpacity(1.);
_childrenHidden = false; _childrenHidden = false;
} }
App::roundRect(p, rect(), st::mediaviewSaveMsgBg, MediaviewSaveCorners); App::roundRect(p, rect(), st::mediaviewSaveMsgBg, MediaviewSaveCorners);

View File

@ -36,6 +36,8 @@ public:
virtual void playbackControlsSeekFinished(crl::time position) = 0; virtual void playbackControlsSeekFinished(crl::time position) = 0;
virtual void playbackControlsVolumeChanged(float64 volume) = 0; virtual void playbackControlsVolumeChanged(float64 volume) = 0;
[[nodiscard]] virtual float64 playbackControlsCurrentVolume() = 0; [[nodiscard]] virtual float64 playbackControlsCurrentVolume() = 0;
virtual void playbackControlsSpeedChanged(float64 speed) = 0;
[[nodiscard]] virtual float64 playbackControlsCurrentSpeed() = 0;
virtual void playbackControlsToFullScreen() = 0; virtual void playbackControlsToFullScreen() = 0;
virtual void playbackControlsFromFullScreen() = 0; virtual void playbackControlsFromFullScreen() = 0;
virtual void playbackControlsToPictureInPicture() = 0; virtual void playbackControlsToPictureInPicture() = 0;
@ -90,6 +92,7 @@ private:
std::unique_ptr<PlaybackProgress> _playbackProgress; std::unique_ptr<PlaybackProgress> _playbackProgress;
std::unique_ptr<PlaybackProgress> _receivedTillProgress; std::unique_ptr<PlaybackProgress> _receivedTillProgress;
object_ptr<Ui::MediaSlider> _volumeController; object_ptr<Ui::MediaSlider> _volumeController;
object_ptr<Ui::MediaSlider> _speedController;
object_ptr<Ui::IconButton> _fullScreenToggle; object_ptr<Ui::IconButton> _fullScreenToggle;
object_ptr<Ui::IconButton> _pictureInPicture; object_ptr<Ui::IconButton> _pictureInPicture;
object_ptr<Ui::LabelSimple> _playedAlready; object_ptr<Ui::LabelSimple> _playedAlready;