From 87cc18aff8cabe5e01062771632cc19af036c770 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 28 Jan 2020 15:41:08 +0300 Subject: [PATCH] Add video speed control slider. --- Telegram/SourceFiles/main/main_settings.cpp | 14 +++++++++++ Telegram/SourceFiles/main/main_settings.h | 8 ++++++ .../streaming/media_streaming_player.cpp | 1 + .../media/view/media_view_overlay_widget.cpp | 25 ++++++++++++++++--- .../media/view/media_view_overlay_widget.h | 2 ++ .../SourceFiles/media/view/media_view_pip.cpp | 6 +++++ .../view/media_view_playback_controls.cpp | 20 +++++++++++++-- .../media/view/media_view_playback_controls.h | 3 +++ 8 files changed, 74 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/main/main_settings.cpp b/Telegram/SourceFiles/main/main_settings.cpp index acb3c1d04..40c7d9eb7 100644 --- a/Telegram/SourceFiles/main/main_settings.cpp +++ b/Telegram/SourceFiles/main/main_settings.cpp @@ -23,6 +23,14 @@ constexpr auto kVersionTag = -1; constexpr auto kVersion = 1; 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 Settings::Variables::Variables() @@ -98,6 +106,7 @@ QByteArray Settings::serialize() const { for (const auto &[id, time] : _variables.mediaLastPlaybackPosition) { stream << quint64(id) << qint64(time); } + stream << qint32(SerializePlaybackSpeed(_variables.videoPlaybackSpeed.current())); } return result; } @@ -148,6 +157,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { qint32 suggestStickersByEmoji = _variables.suggestStickersByEmoji ? 1 : 0; qint32 spellcheckerEnabled = _variables.spellcheckerEnabled.current() ? 1 : 0; std::vector> mediaLastPlaybackPosition; + qint32 videoPlaybackSpeed = SerializePlaybackSpeed(_variables.videoPlaybackSpeed.current()); stream >> versionTag; if (versionTag == kVersionTag) { @@ -268,6 +278,9 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { } } } + if (!stream.atEnd()) { + stream >> videoPlaybackSpeed; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Main::Settings::constructFromSerialized()")); @@ -355,6 +368,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { _variables.suggestStickersByEmoji = (suggestStickersByEmoji == 1); _variables.spellcheckerEnabled = (spellcheckerEnabled == 1); _variables.mediaLastPlaybackPosition = std::move(mediaLastPlaybackPosition); + _variables.videoPlaybackSpeed = DeserializePlaybackSpeed(videoPlaybackSpeed); } void Settings::setSupportChatsTimeSlice(int slice) { diff --git a/Telegram/SourceFiles/main/main_settings.h b/Telegram/SourceFiles/main/main_settings.h index 4cf55aa8c..ee922a8f4 100644 --- a/Telegram/SourceFiles/main/main_settings.h +++ b/Telegram/SourceFiles/main/main_settings.h @@ -241,6 +241,13 @@ public: return _variables.spellcheckerEnabled.changes(); } + [[nodiscard]] float64 videoPlaybackSpeed() const { + return _variables.videoPlaybackSpeed.current(); + } + void setVideoPlaybackSpeed(float64 speed) { + _variables.videoPlaybackSpeed = speed; + } + private: struct Variables { Variables(); @@ -281,6 +288,7 @@ private: bool suggestStickersByEmoji = true; rpl::variable spellcheckerEnabled = true; std::vector> mediaLastPlaybackPosition; + rpl::variable videoPlaybackSpeed = 1.; static constexpr auto kDefaultSupportChatsLimitSlice = 7 * 24 * 60 * 60; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 92652d82a..144cdf4a9 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -958,6 +958,7 @@ void Player::unlock() { --_locks; if (!_locks) { stopAudio(); + setSpeed(1.); setWaitForMarkAsShown(true); } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index a0137e22f..53d8b5967 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2361,8 +2361,11 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { if (!_streamed->withSound) { options.mode = Streaming::Mode::Video; options.loop = true; - } else if (_pip) { - _pip = nullptr; + } else { + options.speed = _doc->session().settings().videoPlaybackSpeed(); + if (_pip) { + _pip = nullptr; + } } _streamed->instance.play(options); if (_streamingStartPaused) { @@ -2397,13 +2400,29 @@ void OverlayWidget::playbackControlsVolumeChanged(float64 volume) { Global::SetVideoVolume(volume); updateMixerVideoVolume(); Global::RefVideoVolumeChanged().notify(); - Auth().saveSettingsDelayed(); + if (_doc) { + _doc->session().saveSettingsDelayed(); + } } float64 OverlayWidget::playbackControlsCurrentVolume() { 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() { const auto document = _doc; const auto msgId = _msgid; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index ab13d5c81..a72dcf936 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -172,6 +172,8 @@ private: void playbackControlsSeekFinished(crl::time position) override; void playbackControlsVolumeChanged(float64 volume) override; float64 playbackControlsCurrentVolume() override; + void playbackControlsSpeedChanged(float64 speed); + float64 playbackControlsCurrentSpeed() override; void playbackControlsToFullScreen() override; void playbackControlsFromFullScreen() override; void playbackControlsToPictureInPicture() override; diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index b3c722d78..73f6e3b3c 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_document.h" #include "media/streaming/media_streaming_utility.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 "ui/platform/ui_platform_utility.h" #include "ui/widgets/buttons.h" @@ -721,6 +724,9 @@ void Pip::restartAtSeekPosition(crl::time position) { auto options = Streaming::PlaybackOptions(); options.position = position; options.audioId = _instance.player().prepareLegacyState().id; + options.speed = options.audioId.audio() + ? options.audioId.audio()->session().settings().videoPlaybackSpeed() + : 1.; _instance.play(options); updatePlaybackState(); } diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 1c1f3cc40..f67fff407 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -30,6 +30,7 @@ PlaybackControls::PlaybackControls( , _playbackSlider(this, st::mediaviewPlayback) , _playbackProgress(std::make_unique()) , _volumeController(this, st::mediaviewPlayback) +, _speedController(this, st::mediaviewPlayback) , _fullScreenToggle(this, st::mediaviewFullScreenButton) , _pictureInPicture(this, st::mediaviewFullScreenButton) , _playedAlready(this, st::mediaviewPlayProgressLabel) @@ -54,6 +55,12 @@ PlaybackControls::PlaybackControls( _volumeController->setChangeProgressCallback([=](float64 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([=] { if (_showPause) { @@ -121,6 +128,7 @@ void PlaybackControls::startFading(Callback start) { showChildren(); _playbackSlider->disablePaint(true); _volumeController->disablePaint(true); + _speedController->disablePaint(true); _childrenHidden = false; } start(); @@ -128,7 +136,8 @@ void PlaybackControls::startFading(Callback start) { for (const auto child : children()) { if (child->isWidgetType() && child != _playbackSlider - && child != _volumeController) { + && child != _volumeController + && child != _speedController) { static_cast(child)->hide(); } } @@ -138,6 +147,7 @@ void PlaybackControls::startFading(Callback start) { } _playbackSlider->disablePaint(false); _volumeController->disablePaint(false); + _speedController->disablePaint(false); } void PlaybackControls::showAnimated() { @@ -159,6 +169,7 @@ void PlaybackControls::fadeFinished() { void PlaybackControls::fadeUpdated(float64 opacity) { _playbackSlider->setFadeOpacity(opacity); _volumeController->setFadeOpacity(opacity); + _speedController->setFadeOpacity(opacity); } void PlaybackControls::updatePlayback(const Player::TrackState &state) { @@ -306,8 +317,12 @@ void PlaybackControls::resizeEvent(QResizeEvent *e) { _pictureInPicture->moveToLeft(left, playTop); + const auto volumeTop = playTop + (_fullScreenToggle->height() - _volumeController->height()) / 2; _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) { @@ -320,6 +335,7 @@ void PlaybackControls::paintEvent(QPaintEvent *e) { showChildren(); _playbackSlider->setFadeOpacity(1.); _volumeController->setFadeOpacity(1.); + _speedController->setFadeOpacity(1.); _childrenHidden = false; } App::roundRect(p, rect(), st::mediaviewSaveMsgBg, MediaviewSaveCorners); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index f2addb92f..edff22b15 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -36,6 +36,8 @@ public: virtual void playbackControlsSeekFinished(crl::time position) = 0; virtual void playbackControlsVolumeChanged(float64 volume) = 0; [[nodiscard]] virtual float64 playbackControlsCurrentVolume() = 0; + virtual void playbackControlsSpeedChanged(float64 speed) = 0; + [[nodiscard]] virtual float64 playbackControlsCurrentSpeed() = 0; virtual void playbackControlsToFullScreen() = 0; virtual void playbackControlsFromFullScreen() = 0; virtual void playbackControlsToPictureInPicture() = 0; @@ -90,6 +92,7 @@ private: std::unique_ptr _playbackProgress; std::unique_ptr _receivedTillProgress; object_ptr _volumeController; + object_ptr _speedController; object_ptr _fullScreenToggle; object_ptr _pictureInPicture; object_ptr _playedAlready;