Allow switching video quality.

This commit is contained in:
John Preston 2024-10-25 14:26:41 +04:00
parent 7ba78540ac
commit 3f2f3ebd51
9 changed files with 227 additions and 46 deletions

View file

@ -29,6 +29,7 @@ MediaPlayerButton {
MediaSpeedMenu { MediaSpeedMenu {
dropdown: DropdownMenu; dropdown: DropdownMenu;
qualityMenu: Menu;
activeCheck: icon; activeCheck: icon;
activeCheckSkip: pixels; activeCheckSkip: pixels;
sliderStyle: TextStyle; sliderStyle: TextStyle;
@ -165,16 +166,20 @@ mediaPlayerMenu: DropdownMenu(defaultDropdownMenu) {
} }
mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }}; mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }};
mediaPlayerSpeedMenuInner: Menu(menuWithIcons) {
separator: MenuSeparator(defaultMenuSeparator) {
padding: margins(0px, 4px, 0px, 4px);
width: 6px;
}
itemPadding: margins(54px, 7px, 54px, 9px);
itemFgDisabled: mediaPlayerActiveFg;
}
mediaPlayerSpeedMenu: MediaSpeedMenu { mediaPlayerSpeedMenu: MediaSpeedMenu {
dropdown: DropdownMenu(mediaPlayerMenu) { dropdown: DropdownMenu(mediaPlayerMenu) {
menu: Menu(menuWithIcons) { menu: mediaPlayerSpeedMenuInner;
separator: MenuSeparator(defaultMenuSeparator) { }
padding: margins(0px, 4px, 0px, 4px); qualityMenu: Menu(mediaPlayerSpeedMenuInner) {
width: 6px; itemPadding: margins(17px, 7px, 54px, 9px);
}
itemPadding: margins(54px, 7px, 54px, 9px);
itemFgDisabled: mediaPlayerActiveFg;
}
} }
activeCheck: mediaPlayerMenuCheck; activeCheck: mediaPlayerMenuCheck;
activeCheckSkip: 8px; activeCheckSkip: 8px;

View file

@ -177,7 +177,8 @@ void FillSpeedMenu(
not_null<Ui::Menu::Menu*> menu, not_null<Ui::Menu::Menu*> menu,
const style::MediaSpeedMenu &st, const style::MediaSpeedMenu &st,
rpl::producer<float64> value, rpl::producer<float64> value,
Fn<void(float64)> callback) { Fn<void(float64)> callback,
bool onlySlider) {
auto slider = base::make_unique_q<SpeedSliderItem>( auto slider = base::make_unique_q<SpeedSliderItem>(
menu, menu,
st, st,
@ -198,6 +199,11 @@ void FillSpeedMenu(
)); ));
menu->addAction(std::move(slider)); menu->addAction(std::move(slider));
if (onlySlider) {
return;
}
menu->addSeparator(&st.dropdown.menu.separator); menu->addSeparator(&st.dropdown.menu.separator);
struct SpeedPoint { struct SpeedPoint {
@ -693,7 +699,10 @@ SpeedController::SpeedController(
not_null<QWidget*> menuParent, not_null<QWidget*> menuParent,
Fn<void(bool)> menuOverCallback, Fn<void(bool)> menuOverCallback,
Fn<float64(bool lastNonDefault)> value, Fn<float64(bool lastNonDefault)> value,
Fn<void(float64)> change) Fn<void(float64)> change,
std::vector<int> qualities,
Fn<int()> quality,
Fn<void(int)> changeQuality)
: WithDropdownController( : WithDropdownController(
button, button,
menuParent, menuParent,
@ -702,7 +711,12 @@ SpeedController::SpeedController(
std::move(menuOverCallback)) std::move(menuOverCallback))
, _st(button->st()) , _st(button->st())
, _lookup(std::move(value)) , _lookup(std::move(value))
, _change(std::move(change)) { , _change(std::move(change))
, _qualities(std::move(qualities))
, _lookupQuality(std::move(quality))
, _changeQuality(std::move(changeQuality)) {
Expects(_qualities.empty() || (_lookupQuality && _changeQuality));
button->setClickedCallback([=] { button->setClickedCallback([=] {
toggleDefault(); toggleDefault();
save(); save();
@ -756,12 +770,63 @@ void SpeedController::save() {
_saved.fire({}); _saved.fire({});
} }
void SpeedController::setQuality(int quality) {
_quality = quality;
_changeQuality(quality);
}
void SpeedController::fillMenu(not_null<Ui::DropdownMenu*> menu) { void SpeedController::fillMenu(not_null<Ui::DropdownMenu*> menu) {
FillSpeedMenu( FillSpeedMenu(
menu->menu(), menu->menu(),
_st.menu, _st.menu,
_speedChanged.events_starting_with(speed()), _speedChanged.events_starting_with(speed()),
[=](float64 speed) { setSpeed(speed); save(); }); [=](float64 speed) { setSpeed(speed); save(); },
!_qualities.empty());
if (_qualities.empty()) {
return;
}
_quality = _lookupQuality();
const auto raw = menu->menu();
const auto &st = _st.menu;
raw->addSeparator(&st.dropdown.menu.separator);
const auto add = [&](int quality) {
const auto text = quality ? u"%1p"_q.arg(quality) : u"Original"_q;
auto action = base::make_unique_q<Ui::Menu::Action>(
raw,
st.qualityMenu,
Ui::Menu::CreateAction(raw, text, [=] { _changeQuality(quality); }),
nullptr,
nullptr);
const auto raw = action.get();
const auto check = Ui::CreateChild<Ui::RpWidget>(raw);
check->resize(st.activeCheck.size());
check->paintRequest(
) | rpl::start_with_next([check, icon = &st.activeCheck] {
auto p = QPainter(check);
icon->paint(p, 0, 0, check->width());
}, check->lifetime());
raw->sizeValue(
) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) {
check->moveToRight(
skip,
(size.height() - check->height()) / 2,
size.width());
}, check->lifetime());
check->setAttribute(Qt::WA_TransparentForMouseEvents);
_quality.value(
) | rpl::start_with_next([=](int now) {
const auto chosen = (now == quality);
raw->action()->setEnabled(!chosen);
check->setVisible(chosen);
}, raw->lifetime());
menu->addAction(std::move(action));
};
add(0);
for (const auto quality : _qualities) {
add(quality);
}
} }
} // namespace Media::Player } // namespace Media::Player

View file

@ -129,7 +129,10 @@ public:
not_null<QWidget*> menuParent, not_null<QWidget*> menuParent,
Fn<void(bool)> menuOverCallback, Fn<void(bool)> menuOverCallback,
Fn<float64(bool lastNonDefault)> value, Fn<float64(bool lastNonDefault)> value,
Fn<void(float64)> change); Fn<void(float64)> change,
std::vector<int> qualities = {},
Fn<int()> quality = nullptr,
Fn<void(int)> changeQuality = nullptr);
[[nodiscard]] rpl::producer<> saved() const; [[nodiscard]] rpl::producer<> saved() const;
@ -141,6 +144,7 @@ private:
[[nodiscard]] float64 lastNonDefaultSpeed() const; [[nodiscard]] float64 lastNonDefaultSpeed() const;
void toggleDefault(); void toggleDefault();
void setSpeed(float64 newSpeed); void setSpeed(float64 newSpeed);
void setQuality(int quality);
void save(); void save();
const style::MediaSpeedButton &_st; const style::MediaSpeedButton &_st;
@ -151,6 +155,11 @@ private:
rpl::event_stream<float64> _speedChanged; rpl::event_stream<float64> _speedChanged;
rpl::event_stream<> _saved; rpl::event_stream<> _saved;
std::vector<int> _qualities;
Fn<int()> _lookupQuality;
Fn<void(int)> _changeQuality;
rpl::variable<int> _quality;
}; };
} // namespace Media::Player } // namespace Media::Player

View file

@ -17,6 +17,9 @@ inline constexpr auto kTimeUnknown = std::numeric_limits<crl::time>::min();
inline constexpr auto kDurationMax = crl::time(std::numeric_limits<int>::max()); inline constexpr auto kDurationMax = crl::time(std::numeric_limits<int>::max());
inline constexpr auto kDurationUnavailable = std::numeric_limits<crl::time>::max(); inline constexpr auto kDurationUnavailable = std::numeric_limits<crl::time>::max();
inline constexpr auto kOriginalQuality = 0;
inline constexpr auto kAutoQuality = -1;
namespace Audio { namespace Audio {
bool SupportsSpeedControl(); bool SupportsSpeedControl();
} // namespace Audio } // namespace Audio

View file

@ -307,17 +307,21 @@ mediaviewTitleMaximizeMacPadding: margins(0px, 4px, 8px, 4px);
mediaviewShadowTop: icon{{ "mediaview/shadow_top", windowShadowFg }}; mediaviewShadowTop: icon{{ "mediaview/shadow_top", windowShadowFg }};
mediaviewShadowBottom: icon{{ "mediaview/shadow_bottom", windowShadowFg }}; mediaviewShadowBottom: icon{{ "mediaview/shadow_bottom", windowShadowFg }};
mediaviewSpeedMenuInner: Menu(mediaviewMenu) {
separator: MenuSeparator(mediaviewMenuSeparator) {
fg: groupCallMenuBgOver;
padding: margins(0px, 4px, 0px, 4px);
width: 6px;
}
itemPadding: margins(54px, 7px, 54px, 9px);
itemFgDisabled: mediaviewTextLinkFg;
}
mediaviewSpeedMenu: MediaSpeedMenu(mediaPlayerSpeedMenu) { mediaviewSpeedMenu: MediaSpeedMenu(mediaPlayerSpeedMenu) {
dropdown: DropdownMenu(mediaviewDropdownMenu) { dropdown: DropdownMenu(mediaviewDropdownMenu) {
menu: Menu(mediaviewMenu) { menu: mediaviewSpeedMenuInner;
separator: MenuSeparator(mediaviewMenuSeparator) { }
fg: groupCallMenuBgOver; qualityMenu: Menu(mediaviewSpeedMenuInner) {
padding: margins(0px, 4px, 0px, 4px); itemPadding: margins(17px, 7px, 54px, 9px);
width: 6px;
}
itemPadding: margins(54px, 7px, 54px, 9px);
itemFgDisabled: mediaviewTextLinkFg;
}
} }
activeCheck: icon {{ "player/player_check", mediaviewTextLinkFg }}; activeCheck: icon {{ "player/player_check", mediaviewTextLinkFg }};
slider: MediaSlider(defaultContinuousSlider) { slider: MediaSlider(defaultContinuousSlider) {

View file

@ -1112,7 +1112,10 @@ bool OverlayWidget::videoShown() const {
QSize OverlayWidget::videoSize() const { QSize OverlayWidget::videoSize() const {
Expects(videoShown()); Expects(videoShown());
return flipSizeByRotation(_streamed->instance.info().video.size); const auto use = (_document && _chosenQuality != _document)
? _document->dimensions
: _streamed->instance.info().video.size;
return flipSizeByRotation(use);
} }
bool OverlayWidget::streamingRequiresControls() const { bool OverlayWidget::streamingRequiresControls() const {
@ -2257,16 +2260,37 @@ OverlayWidget::~OverlayWidget() {
_dropdown.destroy(); _dropdown.destroy();
} }
not_null<DocumentData*> OverlayWidget::chooseQuality() const {
Expects(_document != nullptr);
const auto video = _document->video();
if (!video || video->qualities.empty() || _quality == kOriginalQuality) {
return _document;
}
auto closest = _document;
auto closestAbs = std::abs(_quality - _document->resolveVideoQuality());
for (const auto &quality : video->qualities) {
const auto abs = std::abs(_quality - quality->resolveVideoQuality());
if (abs < closestAbs) {
closestAbs = abs;
closest = quality;
}
}
return closest;
}
void OverlayWidget::assignMediaPointer(DocumentData *document) { void OverlayWidget::assignMediaPointer(DocumentData *document) {
_savePhotoVideoWhenLoaded = SavePhotoVideo::None; _savePhotoVideoWhenLoaded = SavePhotoVideo::None;
_photo = nullptr; _photo = nullptr;
_photoMedia = nullptr; _photoMedia = nullptr;
if (_document != document) { if (_document != document) {
if ((_document = document)) { if ((_document = document)) {
_chosenQuality = chooseQuality();
_documentMedia = _document->createMediaView(); _documentMedia = _document->createMediaView();
_documentMedia->goodThumbnailWanted(); _documentMedia->goodThumbnailWanted();
_documentMedia->thumbnailWanted(fileOrigin()); _documentMedia->thumbnailWanted(fileOrigin());
} else { } else {
_chosenQuality = nullptr;
_documentMedia = nullptr; _documentMedia = nullptr;
} }
_documentLoadingTo = QString(); _documentLoadingTo = QString();
@ -2275,6 +2299,7 @@ void OverlayWidget::assignMediaPointer(DocumentData *document) {
void OverlayWidget::assignMediaPointer(not_null<PhotoData*> photo) { void OverlayWidget::assignMediaPointer(not_null<PhotoData*> photo) {
_savePhotoVideoWhenLoaded = SavePhotoVideo::None; _savePhotoVideoWhenLoaded = SavePhotoVideo::None;
_chosenQuality = nullptr;
_document = nullptr; _document = nullptr;
_documentMedia = nullptr; _documentMedia = nullptr;
_documentLoadingTo = QString(); _documentLoadingTo = QString();
@ -3848,12 +3873,12 @@ void OverlayWidget::startStreamingPlayer(
return; return;
} }
const auto position = _document _streamedPosition = _document
? startStreaming.startTime ? startStreaming.startTime
: _photo : _photo
? _photo->videoStartPosition() ? _photo->videoStartPosition()
: 0; : 0;
restartAtSeekPosition(position); restartAtSeekPosition(_streamedPosition);
} }
void OverlayWidget::initStreamingThumbnail() { void OverlayWidget::initStreamingThumbnail() {
@ -3892,9 +3917,15 @@ void OverlayWidget::initStreamingThumbnail() {
: good : good
? good->size() ? good->size()
: _document->dimensions; : _document->dimensions;
if (!good && !thumbnail && !blurred) { if (size.isEmpty()) {
return; return;
} else if (size.isEmpty()) { } else if (!_streamedQualityChangeFrame.isNull()) {
setStaticContent(_streamedQualityChangeFrame.scaled(
size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
return;
} else if (!good && !thumbnail && !blurred) {
return; return;
} }
const auto options = VideoThumbOptions(_document); const auto options = VideoThumbOptions(_document);
@ -3917,6 +3948,7 @@ void OverlayWidget::initStreamingThumbnail() {
void OverlayWidget::streamingReady(Streaming::Information &&info) { void OverlayWidget::streamingReady(Streaming::Information &&info) {
if (videoShown()) { if (videoShown()) {
applyVideoSize(); applyVideoSize();
_streamedQualityChangeFrame = QImage();
} else { } else {
updateContentRect(); updateContentRect();
} }
@ -3938,8 +3970,9 @@ bool OverlayWidget::createStreamingObjects() {
const auto origin = fileOrigin(); const auto origin = fileOrigin();
const auto callback = [=] { waitingAnimationCallback(); }; const auto callback = [=] { waitingAnimationCallback(); };
if (_document) { const auto video = _chosenQuality ? _chosenQuality : _document;
_streamed = std::make_unique<Streamed>(_document, origin, callback); if (video) {
_streamed = std::make_unique<Streamed>(video, origin, callback);
} else { } else {
_streamed = std::make_unique<Streamed>(_photo, origin, callback); _streamed = std::make_unique<Streamed>(_photo, origin, callback);
} }
@ -3950,8 +3983,8 @@ bool OverlayWidget::createStreamingObjects() {
++_streamedCreated; ++_streamedCreated;
_streamed->instance.setPriority(kOverlayLoaderPriority); _streamed->instance.setPriority(kOverlayLoaderPriority);
_streamed->instance.lockPlayer(); _streamed->instance.lockPlayer();
_streamed->withSound = _document _streamed->withSound = video
&& !_document->isSilentVideo() && !video->isSilentVideo()
&& (_document->isAudioFile() && (_document->isAudioFile()
|| _document->isVideoFile() || _document->isVideoFile()
|| _document->isVoiceMessage() || _document->isVoiceMessage()
@ -4018,6 +4051,7 @@ void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) {
updateContentRect(); updateContentRect();
Core::App().updateNonIdle(); Core::App().updateNonIdle();
updatePlaybackState(); updatePlaybackState();
_streamedPosition = update.position;
}, [&](const PreloadedAudio &update) { }, [&](const PreloadedAudio &update) {
updatePlaybackState(); updatePlaybackState();
}, [&](const UpdateAudio &update) { }, [&](const UpdateAudio &update) {
@ -4381,6 +4415,45 @@ float64 OverlayWidget::playbackControlsCurrentSpeed(bool lastNonDefault) {
return Core::App().settings().videoPlaybackSpeed(lastNonDefault); return Core::App().settings().videoPlaybackSpeed(lastNonDefault);
} }
std::vector<int> OverlayWidget::playbackControlsQualities() {
const auto video = _document ? _document->video() : nullptr;
if (!video || video->qualities.empty()) {
return {};
}
auto result = std::vector<int>();
result.reserve(video->qualities.size());
for (const auto &quality : video->qualities) {
result.push_back(quality->resolveVideoQuality());
}
return result;
}
int OverlayWidget::playbackControlsCurrentQuality() {
return _quality;
}
void OverlayWidget::playbackControlsQualityChanged(int quality) {
const auto now = _chosenQuality;
if (_quality != quality) {
_quality = quality;
if (_document) {
_chosenQuality = chooseQuality();
if (_chosenQuality != now) {
if (_streamed && _streamed->instance.ready()) {
_streamedQualityChangeFrame = currentVideoFrameImage();
}
clearStreaming();
_streamingStartPaused = false;
const auto time = _streamedPosition;
const auto startStreaming = StartStreaming(false, time);
if (!canInitStreaming() || !initStreaming(startStreaming)) {
redisplayContent();
}
}
}
}
}
void OverlayWidget::switchToPip() { void OverlayWidget::switchToPip() {
Expects(_streamed != nullptr); Expects(_streamed != nullptr);
Expects(_document != nullptr); Expects(_document != nullptr);
@ -4628,6 +4701,7 @@ void OverlayWidget::updatePlaybackState() {
} }
const auto state = _streamed->instance.player().prepareLegacyState(); const auto state = _streamed->instance.player().prepareLegacyState();
if (state.position != kTimeUnknown && state.length != kTimeUnknown) { if (state.position != kTimeUnknown && state.length != kTimeUnknown) {
_streamedPosition = state.position;
if (_streamed->controls) { if (_streamed->controls) {
_streamed->controls->updatePlayback(state); _streamed->controls->updatePlayback(state);
_touchbarTrackState.fire_copy(state); _touchbarTrackState.fire_copy(state);

View file

@ -236,6 +236,9 @@ private:
void playbackControlsVolumeChangeFinished() override; void playbackControlsVolumeChangeFinished() override;
void playbackControlsSpeedChanged(float64 speed) override; void playbackControlsSpeedChanged(float64 speed) override;
float64 playbackControlsCurrentSpeed(bool lastNonDefault) override; float64 playbackControlsCurrentSpeed(bool lastNonDefault) override;
std::vector<int> playbackControlsQualities() override;
int playbackControlsCurrentQuality() override;
void playbackControlsQualityChanged(int quality) override;
void playbackControlsToFullScreen() override; void playbackControlsToFullScreen() override;
void playbackControlsFromFullScreen() override; void playbackControlsFromFullScreen() override;
void playbackControlsToPictureInPicture() override; void playbackControlsToPictureInPicture() override;
@ -315,11 +318,11 @@ private:
void checkForSaveLoaded(); void checkForSaveLoaded();
void showPremiumDownloadPromo(); void showPremiumDownloadPromo();
Entity entityForUserPhotos(int index) const; [[nodiscard]] Entity entityForUserPhotos(int index) const;
Entity entityForSharedMedia(int index) const; [[nodiscard]] Entity entityForSharedMedia(int index) const;
Entity entityForCollage(int index) const; [[nodiscard]] Entity entityForCollage(int index) const;
Entity entityByIndex(int index) const; [[nodiscard]] Entity entityByIndex(int index) const;
Entity entityForItemId(const FullMsgId &itemId) const; [[nodiscard]] Entity entityForItemId(const FullMsgId &itemId) const;
bool moveToEntity(const Entity &entity, int preloadDelta = 0); bool moveToEntity(const Entity &entity, int preloadDelta = 0);
void setContext(std::variant< void setContext(std::variant<
@ -335,23 +338,23 @@ private:
struct SharedMedia; struct SharedMedia;
using SharedMediaType = SharedMediaWithLastSlice::Type; using SharedMediaType = SharedMediaWithLastSlice::Type;
using SharedMediaKey = SharedMediaWithLastSlice::Key; using SharedMediaKey = SharedMediaWithLastSlice::Key;
std::optional<SharedMediaType> sharedMediaType() const; [[nodiscard]] std::optional<SharedMediaType> sharedMediaType() const;
std::optional<SharedMediaKey> sharedMediaKey() const; [[nodiscard]] std::optional<SharedMediaKey> sharedMediaKey() const;
std::optional<SharedMediaType> computeOverviewType() const; [[nodiscard]] std::optional<SharedMediaType> computeOverviewType() const;
bool validSharedMedia() const; bool validSharedMedia() const;
void validateSharedMedia(); void validateSharedMedia();
void handleSharedMediaUpdate(SharedMediaWithLastSlice &&update); void handleSharedMediaUpdate(SharedMediaWithLastSlice &&update);
struct UserPhotos; struct UserPhotos;
using UserPhotosKey = UserPhotosSlice::Key; using UserPhotosKey = UserPhotosSlice::Key;
std::optional<UserPhotosKey> userPhotosKey() const; [[nodiscard]] std::optional<UserPhotosKey> userPhotosKey() const;
bool validUserPhotos() const; bool validUserPhotos() const;
void validateUserPhotos(); void validateUserPhotos();
void handleUserPhotosUpdate(UserPhotosSlice &&update); void handleUserPhotosUpdate(UserPhotosSlice &&update);
struct Collage; struct Collage;
using CollageKey = WebPageCollage::Item; using CollageKey = WebPageCollage::Item;
std::optional<CollageKey> collageKey() const; [[nodiscard]] std::optional<CollageKey> collageKey() const;
bool validCollage() const; bool validCollage() const;
void validateCollage(); void validateCollage();
@ -430,11 +433,11 @@ private:
void contentSizeChanged(); void contentSizeChanged();
// Radial animation interface. // Radial animation interface.
float64 radialProgress() const; [[nodiscard]] float64 radialProgress() const;
bool radialLoading() const; [[nodiscard]] bool radialLoading() const;
QRect radialRect() const; [[nodiscard]] QRect radialRect() const;
void radialStart(); void radialStart();
crl::time radialTimeShift() const; [[nodiscard]] crl::time radialTimeShift() const;
void updateHeader(); void updateHeader();
void snapXY(); void snapXY();
@ -524,6 +527,7 @@ private:
void clearStreaming(bool savePosition = true); void clearStreaming(bool savePosition = true);
[[nodiscard]] bool canInitStreaming() const; [[nodiscard]] bool canInitStreaming() const;
[[nodiscard]] bool saveControlLocked() const; [[nodiscard]] bool saveControlLocked() const;
[[nodiscard]] not_null<DocumentData*> chooseQuality() const;
[[nodiscard]] bool topShadowOnTheRight() const; [[nodiscard]] bool topShadowOnTheRight() const;
void applyHideWindowWorkaround(); void applyHideWindowWorkaround();
@ -551,6 +555,8 @@ private:
rpl::lifetime _sessionLifetime; rpl::lifetime _sessionLifetime;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
DocumentData *_document = nullptr; DocumentData *_document = nullptr;
DocumentData *_chosenQuality = nullptr;
int _quality = {};
QString _documentLoadingTo; QString _documentLoadingTo;
std::shared_ptr<Data::PhotoMedia> _photoMedia; std::shared_ptr<Data::PhotoMedia> _photoMedia;
std::shared_ptr<Data::DocumentMedia> _documentMedia; std::shared_ptr<Data::DocumentMedia> _documentMedia;
@ -625,6 +631,8 @@ private:
std::unique_ptr<Streamed> _streamed; std::unique_ptr<Streamed> _streamed;
std::unique_ptr<PipWrap> _pip; std::unique_ptr<PipWrap> _pip;
QImage _streamedQualityChangeFrame;
crl::time _streamedPosition = 0;
int _streamedCreated = 0; int _streamedCreated = 0;
bool _showAsPip = false; bool _showAsPip = false;

View file

@ -47,7 +47,10 @@ PlaybackControls::PlaybackControls(
parent, parent,
[=](bool) {}, [=](bool) {},
[=](bool lastNonDefault) { return speedLookup(lastNonDefault); }, [=](bool lastNonDefault) { return speedLookup(lastNonDefault); },
[=](float64 speed) { saveSpeed(speed); }) [=](float64 speed) { saveSpeed(speed); },
_delegate->playbackControlsQualities(),
[=] { return _delegate->playbackControlsCurrentQuality(); },
[=](int quality) { saveQuality(quality); })
: nullptr) : nullptr)
, _fadeAnimation(std::make_unique<Ui::FadeAnimation>(this)) { , _fadeAnimation(std::make_unique<Ui::FadeAnimation>(this)) {
_fadeAnimation->show(); _fadeAnimation->show();
@ -192,6 +195,10 @@ void PlaybackControls::saveSpeed(float64 speed) {
_delegate->playbackControlsSpeedChanged(speed); _delegate->playbackControlsSpeedChanged(speed);
} }
void PlaybackControls::saveQuality(int quality) {
_delegate->playbackControlsQualityChanged(quality);
}
void PlaybackControls::updatePlaybackSpeed(float64 speed) { void PlaybackControls::updatePlaybackSpeed(float64 speed) {
DEBUG_LOG(("Media playback speed: update to %1.").arg(speed)); DEBUG_LOG(("Media playback speed: update to %1.").arg(speed));
_delegate->playbackControlsSpeedChanged(speed); _delegate->playbackControlsSpeedChanged(speed);

View file

@ -44,6 +44,10 @@ public:
virtual void playbackControlsSpeedChanged(float64 speed) = 0; virtual void playbackControlsSpeedChanged(float64 speed) = 0;
[[nodiscard]] virtual float64 playbackControlsCurrentSpeed( [[nodiscard]] virtual float64 playbackControlsCurrentSpeed(
bool lastNonDefault) = 0; bool lastNonDefault) = 0;
[[nodiscard]] virtual auto playbackControlsQualities()
-> std::vector<int> = 0;
[[nodiscard]] virtual int playbackControlsCurrentQuality() = 0;
virtual void playbackControlsQualityChanged(int quality) = 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 +94,8 @@ private:
[[nodiscard]] float64 speedLookup(bool lastNonDefault) const; [[nodiscard]] float64 speedLookup(bool lastNonDefault) const;
void saveSpeed(float64 speed); void saveSpeed(float64 speed);
void saveQuality(int quality);
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;
bool _inFullScreen = false; bool _inFullScreen = false;