mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Support quality change in PiP.
This commit is contained in:
parent
3cfa963f69
commit
0971485367
5 changed files with 182 additions and 42 deletions
|
@ -17,6 +17,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Media {
|
||||
namespace Streaming {
|
||||
|
||||
Instance::Instance(const Instance &other)
|
||||
: _shared(other._shared)
|
||||
, _waitingCallback(other._waitingCallback)
|
||||
, _priority(other._priority)
|
||||
, _playerLocked(other._playerLocked) {
|
||||
if (_shared) {
|
||||
_shared->registerInstance(this);
|
||||
if (_playerLocked) {
|
||||
_shared->player().lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instance::Instance(
|
||||
std::shared_ptr<Document> shared,
|
||||
Fn<void()> waitingCallback)
|
||||
|
|
|
@ -27,6 +27,7 @@ class Player;
|
|||
|
||||
class Instance {
|
||||
public:
|
||||
Instance(const Instance &other);
|
||||
Instance(
|
||||
std::shared_ptr<Document> shared,
|
||||
Fn<void()> waitingCallback);
|
||||
|
|
|
@ -345,6 +345,10 @@ struct OverlayWidget::PipWrap {
|
|||
PipWrap(
|
||||
QWidget *parent,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
not_null<DocumentData*> chosenQuality,
|
||||
HistoryItem *context,
|
||||
VideoQuality quality,
|
||||
std::shared_ptr<Streaming::Document> shared,
|
||||
FnMut<void()> closeAndContinue,
|
||||
FnMut<void()> destroy);
|
||||
|
@ -470,6 +474,10 @@ OverlayWidget::Streamed::Streamed(
|
|||
OverlayWidget::PipWrap::PipWrap(
|
||||
QWidget *parent,
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
not_null<DocumentData*> chosenQuality,
|
||||
HistoryItem *context,
|
||||
VideoQuality quality,
|
||||
std::shared_ptr<Streaming::Document> shared,
|
||||
FnMut<void()> closeAndContinue,
|
||||
FnMut<void()> destroy)
|
||||
|
@ -477,6 +485,10 @@ OverlayWidget::PipWrap::PipWrap(
|
|||
, wrapped(
|
||||
&delegate,
|
||||
document,
|
||||
origin,
|
||||
chosenQuality,
|
||||
context,
|
||||
quality,
|
||||
std::move(shared),
|
||||
std::move(closeAndContinue),
|
||||
std::move(destroy)) {
|
||||
|
@ -3849,10 +3861,14 @@ bool OverlayWidget::initStreaming(const StartStreaming &startStreaming) {
|
|||
});
|
||||
}, _streamed->instance.lifetime());
|
||||
|
||||
const auto continuing = startStreaming.continueStreaming
|
||||
&& _pip
|
||||
&& (_pip->wrapped.shared().get()
|
||||
== _streamed->instance.shared().get());
|
||||
if (startStreaming.continueStreaming) {
|
||||
_pip = nullptr;
|
||||
}
|
||||
if (!startStreaming.continueStreaming
|
||||
if (!continuing
|
||||
|| (!_streamed->instance.player().active()
|
||||
&& !_streamed->instance.player().finished())) {
|
||||
startStreamingPlayer(startStreaming);
|
||||
|
@ -4517,6 +4533,10 @@ void OverlayWidget::switchToPip() {
|
|||
_pip = std::make_unique<PipWrap>(
|
||||
_window,
|
||||
document,
|
||||
fileOrigin(),
|
||||
_chosenQuality ? _chosenQuality : document,
|
||||
_message,
|
||||
_quality,
|
||||
_streamed->instance.shared(),
|
||||
closeAndContinue,
|
||||
[=] { _pip = nullptr; });
|
||||
|
|
|
@ -885,18 +885,30 @@ void PipPanel::updateDecorations() {
|
|||
Pip::Pip(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<DocumentData*> data,
|
||||
Data::FileOrigin origin,
|
||||
not_null<DocumentData*> chosenQuality,
|
||||
HistoryItem *context,
|
||||
VideoQuality quality,
|
||||
std::shared_ptr<Streaming::Document> shared,
|
||||
FnMut<void()> closeAndContinue,
|
||||
FnMut<void()> destroy)
|
||||
: _delegate(delegate)
|
||||
, _data(data)
|
||||
, _instance(std::move(shared), [=] { waitingAnimationCallback(); })
|
||||
, _origin(origin)
|
||||
, _chosenQuality(chosenQuality)
|
||||
, _context(context)
|
||||
, _quality(quality)
|
||||
, _instance(
|
||||
std::in_place,
|
||||
std::move(shared),
|
||||
[=] { waitingAnimationCallback(); })
|
||||
, _panel(
|
||||
_delegate->pipParentWidget(),
|
||||
[=](Ui::GL::Capabilities capabilities) {
|
||||
return chooseRenderer(capabilities);
|
||||
})
|
||||
, _playbackProgress(std::make_unique<PlaybackProgress>())
|
||||
, _dataMedia(_data->createMediaView())
|
||||
, _rotation(data->owner().mediaRotation().get(data))
|
||||
, _lastPositiveVolume((Core::App().settings().videoVolume() > 0.)
|
||||
? Core::App().settings().videoVolume()
|
||||
|
@ -911,15 +923,28 @@ Pip::Pip(
|
|||
) | rpl::start_with_next([=] {
|
||||
_destroy();
|
||||
}, _panel.rp()->lifetime());
|
||||
|
||||
if (_context) {
|
||||
_data->owner().itemRemoved(
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> data) {
|
||||
if (_context != data) {
|
||||
_context = nullptr;
|
||||
}
|
||||
}, _panel.rp()->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
Pip::~Pip() = default;
|
||||
|
||||
std::shared_ptr<Streaming::Document> Pip::shared() const {
|
||||
return _instance->shared();
|
||||
}
|
||||
|
||||
void Pip::setupPanel() {
|
||||
_panel.init();
|
||||
const auto size = [&] {
|
||||
if (!_instance.info().video.size.isEmpty()) {
|
||||
return _instance.info().video.size;
|
||||
if (!_instance->info().video.size.isEmpty()) {
|
||||
return _instance->info().video.size;
|
||||
}
|
||||
const auto media = _data->activeMediaView();
|
||||
if (media) {
|
||||
|
@ -1138,8 +1163,8 @@ void Pip::seekProgress(float64 value) {
|
|||
_lastDurationMs);
|
||||
if (_seekPositionMs != positionMs) {
|
||||
_seekPositionMs = positionMs;
|
||||
if (!_instance.player().paused()
|
||||
&& !_instance.player().finished()) {
|
||||
if (!_instance->player().paused()
|
||||
&& !_instance->player().finished()) {
|
||||
_pausedBySeek = true;
|
||||
playbackPauseResume();
|
||||
}
|
||||
|
@ -1157,7 +1182,7 @@ void Pip::seekFinish(float64 value) {
|
|||
crl::time(0),
|
||||
_lastDurationMs);
|
||||
_seekPositionMs = -1;
|
||||
_startPaused = !_pausedBySeek && !_instance.player().finished();
|
||||
_startPaused = !_pausedBySeek && !_instance->player().finished();
|
||||
restartAtSeekPosition(positionMs);
|
||||
}
|
||||
|
||||
|
@ -1294,18 +1319,71 @@ void Pip::updatePlayPauseResumeState(const Player::TrackState &state) {
|
|||
}
|
||||
|
||||
void Pip::setupStreaming() {
|
||||
_instance.setPriority(kPipLoaderPriority);
|
||||
_instance.lockPlayer();
|
||||
_instance->setPriority(kPipLoaderPriority);
|
||||
_instance->lockPlayer();
|
||||
|
||||
_instance.player().updates(
|
||||
_instance->switchQualityRequests(
|
||||
) | rpl::filter([=](int quality) {
|
||||
return !_quality.manual && _quality.height != quality;
|
||||
}) | rpl::start_with_next([=](int quality) {
|
||||
applyVideoQuality({
|
||||
.manual = 0,
|
||||
.height = uint32(quality),
|
||||
});
|
||||
}, _instance->lifetime());
|
||||
|
||||
_instance->player().updates(
|
||||
) | rpl::start_with_next_error([=](Streaming::Update &&update) {
|
||||
handleStreamingUpdate(std::move(update));
|
||||
}, [=](Streaming::Error &&error) {
|
||||
handleStreamingError(std::move(error));
|
||||
}, _instance.lifetime());
|
||||
}, _instance->lifetime());
|
||||
updatePlaybackState();
|
||||
}
|
||||
|
||||
void Pip::applyVideoQuality(VideoQuality value) {
|
||||
if (_quality == value
|
||||
|| !_dataMedia->canBePlayed(_context)) {
|
||||
return;
|
||||
}
|
||||
const auto resolved = _data->chooseQuality(_context, value);
|
||||
if (_chosenQuality == resolved) {
|
||||
return;
|
||||
}
|
||||
auto instance = Streaming::Instance(
|
||||
resolved,
|
||||
_data,
|
||||
_context,
|
||||
_origin,
|
||||
[=] { waitingAnimationCallback(); });
|
||||
if (!instance.valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_instance->ready()) {
|
||||
_qualityChangeFrame = currentVideoFrameImage();
|
||||
}
|
||||
if (!_instance->player().active()
|
||||
|| _instance->player().finished()) {
|
||||
_qualityChangeFinished = true;
|
||||
}
|
||||
_startPaused = _qualityChangeFinished || _instance->player().paused();
|
||||
|
||||
_quality = value;
|
||||
Core::App().settings().setVideoQuality(value);
|
||||
Core::App().saveSettingsDelayed();
|
||||
_chosenQuality = resolved;
|
||||
_instance.emplace(std::move(instance));
|
||||
setupStreaming();
|
||||
restartAtSeekPosition(_lastUpdatePosition);
|
||||
}
|
||||
|
||||
QImage Pip::currentVideoFrameImage() const {
|
||||
return _instance->player().ready()
|
||||
? _instance->player().currentFrameImage()
|
||||
: _instance->info().video.cover;
|
||||
}
|
||||
|
||||
Ui::GL::ChosenRenderer Pip::chooseRenderer(
|
||||
Ui::GL::Capabilities capabilities) {
|
||||
const auto use = Platform::IsMac()
|
||||
|
@ -1336,12 +1414,12 @@ void Pip::paint(not_null<Renderer*> renderer) const {
|
|||
.fade = controlsShown,
|
||||
.outer = _panel.widget()->size(),
|
||||
.rotation = _rotation,
|
||||
.videoRotation = _instance.info().video.rotation,
|
||||
.videoRotation = _instance->info().video.rotation,
|
||||
.useTransparency = _panel.useTransparency(),
|
||||
};
|
||||
if (canUseVideoFrame()) {
|
||||
renderer->paintTransformedVideoFrame(geometry);
|
||||
_instance.markFrameShown();
|
||||
_instance->markFrameShown();
|
||||
} else {
|
||||
const auto content = staticContent();
|
||||
if (_preparedCoverState == ThumbState::Cover) {
|
||||
|
@ -1349,7 +1427,7 @@ void Pip::paint(not_null<Renderer*> renderer) const {
|
|||
}
|
||||
renderer->paintTransformedStaticContent(content, geometry);
|
||||
}
|
||||
if (_instance.waitingShown()) {
|
||||
if (_instance->waitingShown()) {
|
||||
renderer->paintRadialLoading(countRadialRect(), controlsShown);
|
||||
}
|
||||
if (controlsShown > 0) {
|
||||
|
@ -1535,12 +1613,14 @@ void Pip::handleStreamingUpdate(Streaming::Update &&update) {
|
|||
v::match(update.data, [&](const Information &update) {
|
||||
_panel.setAspectRatio(
|
||||
FlipSizeByRotation(update.video.size, _rotation));
|
||||
_qualityChangeFrame = QImage();
|
||||
}, [&](PreloadedVideo) {
|
||||
updatePlaybackState();
|
||||
}, [&](UpdateVideo) {
|
||||
}, [&](UpdateVideo update) {
|
||||
_panel.update();
|
||||
Core::App().updateNonIdle();
|
||||
updatePlaybackState();
|
||||
_lastUpdatePosition = update.position;
|
||||
}, [&](PreloadedAudio) {
|
||||
updatePlaybackState();
|
||||
}, [&](UpdateAudio) {
|
||||
|
@ -1554,7 +1634,7 @@ void Pip::handleStreamingUpdate(Streaming::Update &&update) {
|
|||
}
|
||||
|
||||
void Pip::updatePlaybackState() {
|
||||
const auto state = _instance.player().prepareLegacyState();
|
||||
const auto state = _instance->player().prepareLegacyState();
|
||||
updatePlayPauseResumeState(state);
|
||||
if (state.position == kTimeUnknown
|
||||
|| state.length == kTimeUnknown
|
||||
|
@ -1619,61 +1699,65 @@ void Pip::handleStreamingError(Streaming::Error &&error) {
|
|||
}
|
||||
|
||||
void Pip::playbackPauseResume() {
|
||||
if (_instance.player().failed()) {
|
||||
if (_instance->player().failed()) {
|
||||
_panel.widget()->close();
|
||||
} else if (_instance.player().finished()
|
||||
|| !_instance.player().active()) {
|
||||
} else if (_instance->player().finished()
|
||||
|| !_instance->player().active()) {
|
||||
_startPaused = false;
|
||||
restartAtSeekPosition(0);
|
||||
} else if (_instance.player().paused()) {
|
||||
_instance.resume();
|
||||
} else if (_instance->player().paused()) {
|
||||
_instance->resume();
|
||||
updatePlaybackState();
|
||||
} else {
|
||||
_instance.pause();
|
||||
_instance->pause();
|
||||
updatePlaybackState();
|
||||
}
|
||||
}
|
||||
|
||||
void Pip::restartAtSeekPosition(crl::time position) {
|
||||
if (!_instance.info().video.cover.isNull()) {
|
||||
_lastUpdatePosition = position;
|
||||
|
||||
if (!_instance->info().video.cover.isNull()) {
|
||||
_preparedCoverStorage = QImage();
|
||||
_preparedCoverState = ThumbState::Empty;
|
||||
_instance.saveFrameToCover();
|
||||
_instance->saveFrameToCover();
|
||||
}
|
||||
|
||||
auto options = Streaming::PlaybackOptions();
|
||||
options.position = position;
|
||||
options.hwAllowed = Core::App().settings().hardwareAcceleratedVideo();
|
||||
options.audioId = _instance.player().prepareLegacyState().id;
|
||||
options.audioId = _instance->player().prepareLegacyState().id;
|
||||
options.speed = _delegate->pipPlaybackSpeed();
|
||||
|
||||
_instance.play(options);
|
||||
_instance->play(options);
|
||||
if (_startPaused) {
|
||||
_instance.pause();
|
||||
_instance->pause();
|
||||
}
|
||||
_pausedBySeek = false;
|
||||
updatePlaybackState();
|
||||
}
|
||||
|
||||
bool Pip::canUseVideoFrame() const {
|
||||
return _instance.player().ready()
|
||||
&& !_instance.info().video.cover.isNull();
|
||||
return _instance->player().ready()
|
||||
&& !_instance->info().video.cover.isNull();
|
||||
}
|
||||
|
||||
QImage Pip::videoFrame(const FrameRequest &request) const {
|
||||
Expects(canUseVideoFrame());
|
||||
|
||||
return _instance.frame(request);
|
||||
return _instance->frame(request);
|
||||
}
|
||||
|
||||
Streaming::FrameWithInfo Pip::videoFrameWithInfo() const {
|
||||
Expects(canUseVideoFrame());
|
||||
|
||||
return _instance.frameWithInfo();
|
||||
return _instance->frameWithInfo();
|
||||
}
|
||||
|
||||
QImage Pip::staticContent() const {
|
||||
const auto &cover = _instance.info().video.cover;
|
||||
const auto &cover = !_qualityChangeFrame.isNull()
|
||||
? _qualityChangeFrame
|
||||
: _instance->info().video.cover;
|
||||
const auto media = _data->activeMediaView();
|
||||
const auto use = media
|
||||
? media
|
||||
|
@ -1701,7 +1785,7 @@ QImage Pip::staticContent() const {
|
|||
}
|
||||
_preparedCoverState = state;
|
||||
if (state == ThumbState::Cover) {
|
||||
_preparedCoverStorage = _instance.info().video.cover;
|
||||
_preparedCoverStorage = cover;
|
||||
} else {
|
||||
_preparedCoverStorage = (good
|
||||
? good
|
||||
|
@ -1727,7 +1811,7 @@ void Pip::paintRadialLoadingContent(
|
|||
st::radialLine,
|
||||
st::radialLine,
|
||||
st::radialLine));
|
||||
p.setOpacity(_instance.waitingOpacity());
|
||||
p.setOpacity(_instance->waitingOpacity());
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::radialBg);
|
||||
{
|
||||
|
@ -1737,7 +1821,7 @@ void Pip::paintRadialLoadingContent(
|
|||
p.setOpacity(1.);
|
||||
Ui::InfiniteRadialAnimation::Draw(
|
||||
p,
|
||||
_instance.waitingState(),
|
||||
_instance->waitingState(),
|
||||
arc.topLeft(),
|
||||
arc.size(),
|
||||
_panel.widget()->width(),
|
||||
|
|
|
@ -7,13 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_file_origin.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/media_common.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/round_rect.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
namespace base {
|
||||
class PowerSaveBlocker;
|
||||
} // namespace base
|
||||
|
@ -32,12 +36,15 @@ struct Capabilities;
|
|||
} // namespace GL
|
||||
} // namespace Ui
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
namespace Media::Player {
|
||||
struct TrackState;
|
||||
} // namespace Player
|
||||
} // namespace Media::Player
|
||||
|
||||
namespace View {
|
||||
namespace Media::Streaming {
|
||||
class Document;
|
||||
} // namespace Media::Streaming
|
||||
|
||||
namespace Media::View {
|
||||
|
||||
class PlaybackProgress;
|
||||
|
||||
|
@ -134,11 +141,17 @@ public:
|
|||
Pip(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<DocumentData*> data,
|
||||
Data::FileOrigin origin,
|
||||
not_null<DocumentData*> chosenQuality,
|
||||
HistoryItem *context,
|
||||
VideoQuality quality,
|
||||
std::shared_ptr<Streaming::Document> shared,
|
||||
FnMut<void()> closeAndContinue,
|
||||
FnMut<void()> destroy);
|
||||
~Pip();
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Streaming::Document> shared() const;
|
||||
|
||||
private:
|
||||
enum class OverState {
|
||||
None,
|
||||
|
@ -245,6 +258,8 @@ private:
|
|||
QRect outer,
|
||||
float64 shown) const;
|
||||
[[nodiscard]] QRect countRadialRect() const;
|
||||
void applyVideoQuality(VideoQuality value);
|
||||
[[nodiscard]] QImage currentVideoFrameImage() const;
|
||||
|
||||
void seekUpdate(QPoint position);
|
||||
void seekProgress(float64 value);
|
||||
|
@ -252,7 +267,11 @@ private:
|
|||
|
||||
const not_null<Delegate*> _delegate;
|
||||
const not_null<DocumentData*> _data;
|
||||
Streaming::Instance _instance;
|
||||
const Data::FileOrigin _origin;
|
||||
DocumentData *_chosenQuality = nullptr;
|
||||
HistoryItem *_context = nullptr;
|
||||
Media::VideoQuality _quality;
|
||||
std::optional<Streaming::Instance> _instance;
|
||||
bool _opengl = false;
|
||||
PipPanel _panel;
|
||||
QSize _size;
|
||||
|
@ -260,6 +279,10 @@ private:
|
|||
std::unique_ptr<PlaybackProgress> _playbackProgress;
|
||||
std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
|
||||
QImage _qualityChangeFrame;
|
||||
bool _qualityChangeFinished = false;
|
||||
crl::time _lastUpdatePosition = 0;
|
||||
|
||||
bool _showPause = false;
|
||||
bool _startPaused = false;
|
||||
bool _pausedBySeek = false;
|
||||
|
@ -288,5 +311,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
} // namespace View
|
||||
} // namespace Media
|
||||
} // namespace Media::View
|
||||
|
|
Loading…
Add table
Reference in a new issue