diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0147c99bb3..cf59430033 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -5608,6 +5608,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_playback_speed" = "Playback speed: {speed}"; "lng_mediaview_rotate_video" = "Rotate video"; "lng_mediaview_quality_auto" = "Auto"; +"lng_mediaview_quality_original" = "Original ({quality}p)"; "lng_theme_preview_title" = "Theme Preview"; "lng_theme_preview_generating" = "Generating color theme preview..."; diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 6ce4b5b745..3c156814f2 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -115,13 +115,7 @@ void LogPosition(const WindowPosition &position, const QString &name) { auto result = Media::VideoQuality(); const auto data = static_cast(&result); memcpy(data, &value, sizeof(result)); - - const auto height = result.height; - const auto offset = Media::kVideoQualityOriginalOffset; - const auto max = 4320; - return (height <= max || (height >= offset && height <= offset + max)) - ? result - : Media::VideoQuality(); + return (result.height <= 4320) ? result : Media::VideoQuality(); } } // namespace diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 7522dc1bc0..d36b6d94a0 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -88,6 +88,16 @@ void UpdateStickerSetIdentifier( }); } +[[nodiscard]] int ResolveAttributeVsTranscodeQuality( + int attributesQuality, + int transcodeMax) { + return (transcodeMax > 0 + && (attributesQuality < transcodeMax + || attributesQuality > transcodeMax * 1.5)) + ? transcodeMax + : attributesQuality; +} + } // namespace QString FileNameUnsafe( @@ -544,8 +554,6 @@ void DocumentData::setVideoQualities( return; } const auto good = [&](not_null document) { - // Transcodes in alt_documents are always streamable, - // even if the supports_streaming flag is missing return document->isVideoFile() && !document->dimensions.isEmpty() && !document->inappPlaybackFailed() @@ -590,14 +598,9 @@ void DocumentData::setVideoQualities( const auto attributesQuality = attributesSize.isEmpty() ? 0 : std::min(attributesSize.width(), attributesSize.height()); - - // Heuristic: Trust attributes resolution unless it blatantly - // contradicts server-side transcodes (>1.5x delta) - auto mine = (transcodeMax > 0 - && (attributesQuality < transcodeMax - || attributesQuality > transcodeMax * 1.5)) - ? transcodeMax - : attributesQuality; + auto mine = ResolveAttributeVsTranscodeQuality( + attributesQuality, + transcodeMax); if (mine) { qualities.insert(begin(qualities), this); } @@ -608,7 +611,6 @@ void DocumentData::setVideoQualities( int DocumentData::resolveVideoQuality() const { if (const auto data = video()) { if (!data->realVideoSize.isEmpty()) { - // Always trust FFmpeg-parsed physical resolution const auto size = data->realVideoSize; return std::min(size.width(), size.height()); } @@ -627,12 +629,9 @@ int DocumentData::resolveVideoQuality() const { } } if (transcodeMax > 0) { - // Trust transcodes if attributes appear fake - if (attributesQuality < transcodeMax - || attributesQuality > transcodeMax * 1.5) { - return transcodeMax; - } - return attributesQuality; + return ResolveAttributeVsTranscodeQuality( + attributesQuality, + transcodeMax); } } } @@ -661,7 +660,7 @@ not_null DocumentData::chooseQuality( return this; } const auto height = int(request.height); - if (height >= Media::kVideoQualityOriginalOffset) { + if (request.original) { return this; } @@ -672,8 +671,6 @@ not_null DocumentData::chooseQuality( for (const auto &quality : list) { const auto qres = quality->resolveVideoQuality(); const auto abs = std::abs(height - qres); - // Prefer Original if it fits target resolution best, - // falling back to transcode only on exact matches if (!closest || abs < closestAbs || (abs == closestAbs && (quality->size < closestSize diff --git a/Telegram/SourceFiles/media/media_common.h b/Telegram/SourceFiles/media/media_common.h index 42d6e4e4d8..b3049900d7 100644 --- a/Telegram/SourceFiles/media/media_common.h +++ b/Telegram/SourceFiles/media/media_common.h @@ -27,7 +27,8 @@ enum class OrderMode { struct VideoQuality { uint32 manual : 1 = 0; - uint32 height : 31 = 0; + uint32 height : 30 = 0; + uint32 original : 1 = 0; friend inline constexpr auto operator<=>( VideoQuality, @@ -41,8 +42,6 @@ inline constexpr auto kSpeedMin = 0.5; inline constexpr auto kSpeedMax = 2.5; inline constexpr auto kSpedUpDefault = 1.7; -inline constexpr auto kVideoQualityOriginalOffset = 1000000; - [[nodiscard]] inline bool EqualSpeeds(float64 a, float64 b) { return int(base::SafeRound(a * 10.)) == int(base::SafeRound(b * 10.)); } diff --git a/Telegram/SourceFiles/media/player/media_player_button.cpp b/Telegram/SourceFiles/media/player/media_player_button.cpp index 8e043be964..59fd6c8743 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.cpp +++ b/Telegram/SourceFiles/media/player/media_player_button.cpp @@ -355,7 +355,7 @@ void SettingsButton::setSpeed(float64 speed) { } } -void SettingsButton::setQuality(int quality) { +void SettingsButton::setQuality(Media::VideoQuality quality) { if (_quality != quality) { _quality = quality; update(); @@ -437,16 +437,14 @@ void SettingsButton::prepareFrame() { : u"%1X"_q.arg(rounded / 10); paintBadge(p, text, RectPart::TopLeft, color); } - const auto displayQuality = (_quality >= Media::kVideoQualityOriginalOffset) - ? (_quality - Media::kVideoQualityOriginalOffset) - : _quality; - const auto text = (!displayQuality) + const auto height = _quality.height; + const auto text = !height ? QString() - : (displayQuality > 2000) + : (height > 2000) ? u"4K"_q - : (displayQuality > 1000) + : (height > 1000) ? u"FHD"_q - : (displayQuality > 700) + : (height > 700) ? u"HD"_q : u"SD"_q; if (!text.isEmpty()) { diff --git a/Telegram/SourceFiles/media/player/media_player_button.h b/Telegram/SourceFiles/media/player/media_player_button.h index 0c667b099f..998e23452a 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.h +++ b/Telegram/SourceFiles/media/player/media_player_button.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "media/media_common.h" #include "ui/effects/animations.h" #include "ui/widgets/buttons.h" #include "ui/rect_part.h" @@ -111,7 +112,7 @@ public: } void setSpeed(float64 speed); - void setQuality(int quality); + void setQuality(Media::VideoQuality quality); void setActive(bool active); private: @@ -134,7 +135,7 @@ private: Ui::Animations::Simple _overAnimation; QImage _frameCache; float _speed = 1.; - int _quality = 0; + Media::VideoQuality _quality; bool _isDefaultSpeed = false; bool _active = false; diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp index 00950631e2..8ac53b8fbc 100644 --- a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp @@ -719,9 +719,9 @@ SpeedController::SpeedController( Fn menuOverCallback, Fn value, Fn change, - std::vector qualities, + std::vector qualities, Fn quality, - Fn changeQuality) + Fn changeQuality) : WithDropdownController( button, menuParent, @@ -802,7 +802,7 @@ void SpeedController::save() { void SpeedController::setQuality(VideoQuality quality) { _quality = quality; - _changeQuality(quality.manual ? quality.height : 0); + _changeQuality(quality); } void SpeedController::fillMenu(not_null menu) { @@ -824,15 +824,16 @@ void SpeedController::fillMenu(not_null menu) { raw->addSeparator(&st.dropdown.menu.separator); } - const auto add = [&](int quality) { + const auto add = [&](VideoQuality quality) { const auto automatic = tr::lng_mediaview_quality_auto(tr::now); - const auto offset = Media::kVideoQualityOriginalOffset; - // Quality is height-based, except for Original which uses offset - const auto text = !quality + const auto text = (!quality.height && !quality.original) ? automatic - : (quality >= offset) - ? u"Original (%1p)"_q.arg(std::clamp(quality - offset, 0, 4320)) - : u"%1p"_q.arg(quality); + : quality.original + ? tr::lng_mediaview_quality_original( + tr::now, + lt_quality, + QString::number(quality.height)) + : u"%1p"_q.arg(quality.height); auto action = base::make_unique_q( raw, st.qualityMenu, @@ -861,17 +862,13 @@ void SpeedController::fillMenu(not_null menu) { _quality.value( ) | rpl::on_next([=](VideoQuality now) { const auto chosen = now.manual - ? (now.height == quality) - : !quality; + ? (now == quality) + : (!quality.height && !quality.original); rawAction->action()->setEnabled(!chosen); - if (!quality) { - const auto offset = Media::kVideoQualityOriginalOffset; - const auto displayHeight = (now.height >= offset) - ? std::clamp(int(now.height - offset), 0, 4320) - : now.height; + if (!quality.height && !quality.original) { const auto suffix = now.manual ? QString() - : u"\t%1p"_q.arg(displayHeight); + : u"\t%1p"_q.arg(now.height); rawAction->action()->setText(automatic + suffix); } check->setVisible(chosen); @@ -879,8 +876,8 @@ void SpeedController::fillMenu(not_null menu) { menu->addAction(std::move(action)); }; - add(0); - for (const auto quality : _qualities) { + add(VideoQuality()); + for (const auto &quality : _qualities) { add(quality); } } diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.h b/Telegram/SourceFiles/media/player/media_player_dropdown.h index 6e047eb73e..45851da7a2 100644 --- a/Telegram/SourceFiles/media/player/media_player_dropdown.h +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.h @@ -133,9 +133,9 @@ public: Fn menuOverCallback, Fn value, Fn change, - std::vector qualities = {}, + std::vector qualities = {}, Fn quality = nullptr, - Fn changeQuality = nullptr); + Fn changeQuality = nullptr); [[nodiscard]] rpl::producer<> saved() const; [[nodiscard]] rpl::producer realtimeValue() const; @@ -160,9 +160,9 @@ private: rpl::event_stream _speedChanged; rpl::event_stream<> _saved; - std::vector _qualities; + std::vector _qualities; Fn _lookupQuality; - Fn _changeQuality; + Fn _changeQuality; rpl::variable _quality; }; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 7a61e74aaf..950c0e2f1d 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -62,7 +62,6 @@ void SaveValidVideoInformation( SaveValidStateInformation(to.state, std::move(from.state)); to.size = from.size; - // Propagate physical resolution parsed from the stream to.realSize = from.realSize; to.cover = std::move(from.cover); to.rotation = from.rotation; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp index 7c554a7a1c..f352f00484 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp @@ -736,7 +736,6 @@ void VideoTrackObject::callReady() { .size = FFmpeg::TransposeSizeByRotation( FFmpeg::CorrectByAspect(frameSize, _stream.aspect), _stream.rotation), - // realSize captures physical resolution before SAR correction .realSize = FFmpeg::TransposeSizeByRotation( frameSize, _stream.rotation), diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 1c18cbfb37..83b2430ca6 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1246,7 +1246,6 @@ QSize OverlayWidget::videoSize() const { Expects(videoShown()); const auto use = (_document && _chosenQuality != _document) - // Use chosen quality dimensions instead of original ? _chosenQuality->dimensions : _streamed->instance.info().video.size; return flipSizeByRotation(use); @@ -4715,9 +4714,11 @@ void OverlayWidget::initStreamingThumbnail() { void OverlayWidget::streamingReady(Streaming::Information &&info) { markStreamedReady(); if (videoShown()) { - if (_document && _streamed && _streamed->ready) { - const auto targetDocument = _chosenQuality ? _chosenQuality : _document; - if (const auto video = targetDocument->video()) { + if (_document + && _streamed + && _streamed->ready + && (!_chosenQuality || _chosenQuality == _document)) { + if (const auto video = _document->video()) { video->realVideoSize = info.video.realSize; } } @@ -5239,7 +5240,7 @@ float64 OverlayWidget::playbackControlsCurrentSpeed(bool lastNonDefault) { return Core::App().settings().videoPlaybackSpeed(lastNonDefault); } -std::vector OverlayWidget::playbackControlsQualities() { +std::vector OverlayWidget::playbackControlsQualities() { if (!_document) { return {}; } @@ -5247,17 +5248,16 @@ std::vector OverlayWidget::playbackControlsQualities() { if (list.empty()) { return {}; } - auto result = std::vector(); + auto result = std::vector(); result.reserve(list.size()); - auto seen = std::vector(); for (const auto &quality : list) { - const auto res = quality->resolveVideoQuality(); - const auto value = (quality == _document) - ? (res + Media::kVideoQualityOriginalOffset) - : res; - if (!ranges::contains(seen, value)) { + const auto value = VideoQuality{ + .manual = 1u, + .height = uint32(quality->resolveVideoQuality()), + .original = (quality == _document) ? 1u : 0u, + }; + if (!ranges::contains(result, value)) { result.push_back(value); - seen.push_back(value); } } return result; @@ -5267,16 +5267,18 @@ VideoQuality OverlayWidget::playbackControlsCurrentQuality() { if (!_chosenQuality) { return _quality; } - auto height = uint32(_chosenQuality->resolveVideoQuality()); - if (_chosenQuality == _document) { - height += Media::kVideoQualityOriginalOffset; - } - return { .manual = _quality.manual, .height = height }; + return { + .manual = _quality.manual, + .height = uint32(_chosenQuality->resolveVideoQuality()), + .original = (_chosenQuality == _document) ? 1u : 0u, + }; } -void OverlayWidget::playbackControlsQualityChanged(int quality) { + +void OverlayWidget::playbackControlsQualityChanged(VideoQuality quality) { applyVideoQuality({ - .manual = (quality > 0), - .height = quality ? uint32(quality) : _quality.height, + .manual = uint32(quality.height ? 1 : 0), + .height = quality.height, + .original = quality.original, }); } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 6b55cba4e9..5f1fb6f7a5 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -241,9 +241,9 @@ private: void playbackControlsVolumeChangeFinished() override; void playbackControlsSpeedChanged(float64 speed) override; float64 playbackControlsCurrentSpeed(bool lastNonDefault) override; - std::vector playbackControlsQualities() override; + std::vector playbackControlsQualities() override; VideoQuality playbackControlsCurrentQuality() override; - void playbackControlsQualityChanged(int quality) override; + void playbackControlsQualityChanged(VideoQuality quality) override; void playbackControlsToFullScreen() override; void playbackControlsFromFullScreen() override; void playbackControlsToPictureInPicture() override; diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 6c2e83bd23..7cd01b2ea6 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -65,7 +65,7 @@ PlaybackControls::PlaybackControls( : Fn()), _qualitiesList, [=] { return _delegate->playbackControlsCurrentQuality(); }, - [=](int quality) { saveQuality(quality); }) + [=](Media::VideoQuality quality) { saveQuality(quality); }) : nullptr) , _fadeAnimation(std::make_unique(this)) { _fadeAnimation->show(); @@ -214,7 +214,6 @@ void PlaybackControls::fadeUpdated(float64 opacity) { _volumeController->setFadeOpacity(opacity); } - float64 PlaybackControls::speedLookup(bool lastNonDefault) const { return _delegate->playbackControlsCurrentSpeed(lastNonDefault); } @@ -224,14 +223,13 @@ void PlaybackControls::saveSpeed(float64 speed) { _delegate->playbackControlsSpeedChanged(speed); } -void PlaybackControls::saveQuality(int quality) { +void PlaybackControls::saveQuality(Media::VideoQuality quality) { _speedToggle->setQuality(quality); _delegate->playbackControlsQualityChanged(quality); } void PlaybackControls::updateSpeedToggleQuality() { - const auto quality = _delegate->playbackControlsCurrentQuality(); - _speedToggle->setQuality(quality.height); + _speedToggle->setQuality(_delegate->playbackControlsCurrentQuality()); } void PlaybackControls::updatePlaybackSpeed(float64 speed) { diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index 0cd4f3b551..1c06dfb6b6 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -46,10 +46,11 @@ public: [[nodiscard]] virtual float64 playbackControlsCurrentSpeed( bool lastNonDefault) = 0; [[nodiscard]] virtual auto playbackControlsQualities() - -> std::vector = 0; + -> std::vector = 0; [[nodiscard]] virtual auto playbackControlsCurrentQuality() -> VideoQuality = 0; - virtual void playbackControlsQualityChanged(int quality) = 0; + virtual void playbackControlsQualityChanged( + Media::VideoQuality quality) = 0; virtual void playbackControlsToFullScreen() = 0; virtual void playbackControlsFromFullScreen() = 0; virtual void playbackControlsToPictureInPicture() = 0; @@ -108,13 +109,13 @@ private: [[nodiscard]] float64 speedLookup(bool lastNonDefault) const; void saveSpeed(float64 speed); - void saveQuality(int quality); + void saveQuality(Media::VideoQuality quality); void updateTimestampLabel(); const not_null _delegate; bool _speedControllable = false; - std::vector _qualitiesList; + std::vector _qualitiesList; bool _inFullScreen = false; bool _showPause = false;