Use shared player for each document.

This commit is contained in:
John Preston 2019-12-11 13:15:48 +03:00
parent f91f77ff2e
commit bfa5accc29
9 changed files with 191 additions and 135 deletions

View File

@ -1167,9 +1167,13 @@ std::shared_ptr<::Media::Streaming::Document> Session::documentStreamer(
return result; return result;
} }
} }
auto reader = documentStreamedReader(document, origin);
if (!reader) {
return nullptr;
}
auto result = std::make_shared<::Media::Streaming::Document>( auto result = std::make_shared<::Media::Streaming::Document>(
document, document,
origin); std::move(reader));
if (!PruneDestroyedAndSet(_streamedDocuments, document, result)) { if (!PruneDestroyedAndSet(_streamedDocuments, document, result)) {
_streamedDocuments.emplace_or_assign(document, result); _streamedDocuments.emplace_or_assign(document, result);
} }

View File

@ -45,9 +45,8 @@ int gifMaxStatusWidth(DocumentData *document) {
} // namespace } // namespace
struct Gif::Streamed { struct Gif::Streamed {
Streamed( explicit Streamed(
not_null<DocumentData*> document, std::shared_ptr<::Media::Streaming::Document> document);
Data::FileOrigin origin);
~Streamed(); ~Streamed();
std::shared_ptr<::Media::Streaming::Document> shared; std::shared_ptr<::Media::Streaming::Document> shared;
@ -56,9 +55,8 @@ struct Gif::Streamed {
}; };
Gif::Streamed::Streamed( Gif::Streamed::Streamed(
not_null<DocumentData*> document, std::shared_ptr<::Media::Streaming::Document> document)
Data::FileOrigin origin) : shared(std::move(document))
: shared(document->owner().documentStreamer(document, origin))
, instance(shared->addInstance()) { , instance(shared->addInstance()) {
} }
@ -116,7 +114,7 @@ QSize Gif::countOptimalSize() {
auto maxWidth = qMax(tw, st::minPhotoSize); auto maxWidth = qMax(tw, st::minPhotoSize);
auto minHeight = qMax(th, st::minPhotoSize); auto minHeight = qMax(th, st::minPhotoSize);
accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
if (!activeCurrentPlayer()) { if (!activeCurrentStreamed()) {
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
} }
if (_parent->hasBubble()) { if (_parent->hasBubble()) {
@ -171,7 +169,7 @@ QSize Gif::countCurrentSize(int newWidth) {
newWidth = qMax(tw, st::minPhotoSize); newWidth = qMax(tw, st::minPhotoSize);
auto newHeight = qMax(th, st::minPhotoSize); auto newHeight = qMax(th, st::minPhotoSize);
accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x()); accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x());
if (!activeCurrentPlayer()) { if (!activeCurrentStreamed()) {
//const auto own = (reader->mode() == ::Media::Clip::Reader::Mode::Gif); //const auto own = (reader->mode() == ::Media::Clip::Reader::Mode::Gif);
//if (own && !reader->started()) { //if (own && !reader->started()) {
// auto isRound = _data->isVideoMessage(); // auto isRound = _data->isVideoMessage();
@ -232,8 +230,8 @@ QSize Gif::countCurrentSize(int newWidth) {
} }
QSize Gif::videoSize() const { QSize Gif::videoSize() const {
if (const auto player = activeCurrentPlayer()) { if (const auto streamed = activeCurrentStreamed()) {
return player->videoSize(); return streamed->player().videoSize();
} else if (!_data->dimensions.isEmpty()) { } else if (!_data->dimensions.isEmpty()) {
return _data->dimensions; return _data->dimensions;
} else if (const auto thumbnail = _data->thumbnail()) { } else if (const auto thumbnail = _data->thumbnail()) {
@ -256,11 +254,11 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
//auto loaded = _data->loaded(); //auto loaded = _data->loaded();
auto displayLoading = (item->id < 0) || _data->displayLoading(); auto displayLoading = (item->id < 0) || _data->displayLoading();
auto selected = (selection == FullSelection); auto selected = (selection == FullSelection);
const auto startPlayAsync = autoplayEnabled()
if (autoplayEnabled()
&& !_streamed && !_streamed
&& _data->canBePlayed() && _data->canBePlayed()
&& !activeRoundPlayer()) { && !activeRoundStreamed();
if (startPlayAsync) {
_parent->delegate()->elementAnimationAutoplayAsync(_parent); _parent->delegate()->elementAnimationAutoplayAsync(_parent);
} }
@ -273,16 +271,18 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
const auto isRound = _data->isVideoMessage(); const auto isRound = _data->isVideoMessage();
auto displayMute = false; auto displayMute = false;
const auto player = activeCurrentPlayer(); const auto streamed = activeCurrentStreamed();
const auto player = streamed ? &streamed->player() : nullptr;
if ((!player || item->id < 0) && displayLoading) { if ((!streamed || item->id < 0) && displayLoading) {
ensureAnimation(); ensureAnimation();
if (!_animation->radial.animating()) { if (!_animation->radial.animating()) {
_animation->radial.start(dataProgress()); _animation->radial.start(dataProgress());
} }
} }
updateStatusText(); updateStatusText();
const auto radial = isRadialAnimation(); const auto radial = isRadialAnimation()
|| (streamed && streamed->waitingShown());
if (bubble) { if (bubble) {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -313,10 +313,10 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
if (player) { if (streamed) {
auto paused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); auto paused = App::wnd()->sessionController()->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
if (isRound) { if (isRound) {
if (activeRoundPlayer()) { if (activeRoundStreamed()) {
paused = false; paused = false;
} else { } else {
displayMute = true; displayMute = true;
@ -385,8 +385,17 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
} }
if (radial || (!player && ((_streamed && _streamed->shared->player().failed()) || (!_data->loaded() && !_data->loading()) || !autoplayEnabled()))) { if (radial
auto radialOpacity = (radial && _data->loaded() && item->id > 0) ? _animation->radial.opacity() : 1.; || (!player
&& !startPlayAsync
&& ((_streamed && _streamed->shared->player().failed())
|| (!_data->loaded() && !_data->loading())
|| !autoplayEnabled()))) {
auto radialOpacity = (radial && _data->loaded() && item->id > 0)
? _animation->radial.opacity()
: streamed
? streamed->waitingOpacity()
: 1.;
auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
if (selected) { if (selected) {
@ -423,7 +432,25 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
if (radial) { if (radial) {
p.setOpacity(1); p.setOpacity(1);
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); const auto fg = selected
? st::historyFileThumbRadialFgSelected
: st::historyFileThumbRadialFg;
if (streamed) {
Ui::InfiniteRadialAnimation::Draw(
p,
streamed->waitingState(),
rinner.topLeft(),
rinner.size(),
width(),
fg,
st::msgFileRadialLine);
} else {
_animation->radial.draw(
p,
rinner,
st::msgFileRadialLine,
fg);
}
} }
if (!isRound && (!player || item->id < 0)) { if (!isRound && (!player || item->id < 0)) {
@ -763,8 +790,8 @@ void Gif::updateStatusText() const {
statusSize = _data->loadOffset(); statusSize = _data->loadOffset();
} else if (_data->loaded()) { } else if (_data->loaded()) {
statusSize = FileStatusSizeLoaded; statusSize = FileStatusSizeLoaded;
if (const auto video = activeRoundPlayer()) { if (const auto streamed = activeRoundStreamed()) {
const auto state = video->prepareLegacyState(); const auto state = streamed->player().prepareLegacyState();
if (state.length) { if (state.length) {
auto position = int64(0); auto position = int64(0);
if (::Media::Player::IsStoppedAtEnd(state.state)) { if (::Media::Player::IsStoppedAtEnd(state.state)) {
@ -817,23 +844,23 @@ int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply
return result; return result;
} }
::Media::Streaming::Player *Gif::activeRoundPlayer() const { ::Media::Streaming::Document *Gif::activeRoundStreamed() const {
return ::Media::Player::instance()->roundVideoPlayer(_parent->data()); return ::Media::Player::instance()->roundVideoStreamed(_parent->data());
} }
const ::Media::Streaming::Player *Gif::activeOwnPlayer() const { const ::Media::Streaming::Document *Gif::activeOwnStreamed() const {
return (_streamed return (_streamed
&& _streamed->shared->player().ready() && _streamed->shared->player().ready()
&& !_streamed->shared->player().videoSize().isEmpty()) && !_streamed->shared->player().videoSize().isEmpty())
? &_streamed->shared->player() ? _streamed->shared.get()
: nullptr; : nullptr;
} }
const ::Media::Streaming::Player *Gif::activeCurrentPlayer() const { const ::Media::Streaming::Document *Gif::activeCurrentStreamed() const {
if (const auto player = activeRoundPlayer()) { if (const auto player = activeRoundStreamed()) {
return player; return player;
} }
return activeOwnPlayer(); return activeOwnStreamed();
} }
::Media::View::PlaybackProgress *Gif::videoPlayback() const { ::Media::View::PlaybackProgress *Gif::videoPlayback() const {
@ -890,7 +917,9 @@ void Gif::playAnimation(bool autoplay) {
if (!autoplayEnabled()) { if (!autoplayEnabled()) {
history()->owner().checkPlayingVideoFiles(); history()->owner().checkPlayingVideoFiles();
} }
createStreamedPlayer(); if (!createStreamedPlayer()) {
return;
}
auto options = ::Media::Streaming::PlaybackOptions(); auto options = ::Media::Streaming::PlaybackOptions();
options.audioId = AudioMsgId(_data, _realParent->fullId()); options.audioId = AudioMsgId(_data, _realParent->fullId());
//if (!_streamed->withSound) { //if (!_streamed->withSound) {
@ -901,8 +930,14 @@ void Gif::playAnimation(bool autoplay) {
} }
} }
void Gif::createStreamedPlayer() { bool Gif::createStreamedPlayer() {
setStreamed(std::make_unique<Streamed>(_data, _realParent->fullId())); auto shared = _data->owner().documentStreamer(
_data,
_realParent->fullId());
if (!shared) {
return false;
}
setStreamed(std::make_unique<Streamed>(std::move(shared)));
_streamed->shared->player().updates( _streamed->shared->player().updates(
) | rpl::start_with_next_error([=](::Media::Streaming::Update &&update) { ) | rpl::start_with_next_error([=](::Media::Streaming::Update &&update) {
@ -911,10 +946,10 @@ void Gif::createStreamedPlayer() {
handleStreamingError(std::move(error)); handleStreamingError(std::move(error));
}, _streamed->lifetime); }, _streamed->lifetime);
_streamed->shared->player().fullInCache( if (_streamed->shared->player().ready()) {
) | rpl::start_with_next([=](bool fullInCache) { streamingReady(base::duplicate(_streamed->shared->info()));
_data->setLoadedInMediaCache(fullInCache); }
}, _streamed->lifetime); return true;
} }
void Gif::setStreamed(std::unique_ptr<Streamed> value) { void Gif::setStreamed(std::unique_ptr<Streamed> value) {
@ -925,6 +960,9 @@ void Gif::setStreamed(std::unique_ptr<Streamed> value) {
} }
_streamed = std::move(value); _streamed = std::move(value);
if (set) { if (set) {
_streamed->instance->setWaitingCallback([=] {
history()->owner().requestViewRepaint(_parent);
});
history()->owner().registerPlayingVideoFile(_parent); history()->owner().registerPlayingVideoFile(_parent);
} }
} }
@ -938,7 +976,6 @@ void Gif::handleStreamingUpdate(::Media::Streaming::Update &&update) {
//updatePlaybackState(); //updatePlaybackState();
}, [&](const UpdateVideo &update) { }, [&](const UpdateVideo &update) {
history()->owner().requestViewRepaint(_parent); history()->owner().requestViewRepaint(_parent);
Core::App().updateNonIdle();
//updatePlaybackState(); //updatePlaybackState();
}, [&](const PreloadedAudio &update) { }, [&](const PreloadedAudio &update) {
//_streamed->info.audio.state.receivedTill = update.till; //_streamed->info.audio.state.receivedTill = update.till;
@ -955,12 +992,6 @@ void Gif::handleStreamingUpdate(::Media::Streaming::Update &&update) {
} }
void Gif::handleStreamingError(::Media::Streaming::Error &&error) { void Gif::handleStreamingError(::Media::Streaming::Error &&error) {
using namespace ::Media::Streaming;
if (error == Error::NotStreamable) {
_data->setNotSupportsStreaming();
} else if (error == Error::OpenFailed) {
_data->setInappPlaybackFailed();
}
//if (!_data->canBePlayed()) { //if (!_data->canBePlayed()) {
// redisplayContent(); // redisplayContent();
//} else { //} else {

View File

@ -22,7 +22,7 @@ class PlaybackProgress;
namespace Media { namespace Media {
namespace Streaming { namespace Streaming {
class Player; class Document;
struct Update; struct Update;
struct Information; struct Information;
enum class Error; enum class Error;
@ -93,12 +93,12 @@ private:
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
QSize videoSize() const; QSize videoSize() const;
::Media::Streaming::Player *activeRoundPlayer() const; ::Media::Streaming::Document *activeRoundStreamed() const;
const ::Media::Streaming::Player *activeOwnPlayer() const; const ::Media::Streaming::Document *activeOwnStreamed() const;
const ::Media::Streaming::Player *activeCurrentPlayer() const; const ::Media::Streaming::Document *activeCurrentStreamed() const;
::Media::View::PlaybackProgress *videoPlayback() const; ::Media::View::PlaybackProgress *videoPlayback() const;
void createStreamedPlayer(); bool createStreamedPlayer();
void setStreamed(std::unique_ptr<Streamed> value); void setStreamed(std::unique_ptr<Streamed> value);
void handleStreamingUpdate(::Media::Streaming::Update &&update); void handleStreamingUpdate(::Media::Streaming::Update &&update);
void handleStreamingError(::Media::Streaming::Error &&error); void handleStreamingError(::Media::Streaming::Error &&error);

View File

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h" #include "history/history_item.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_document.h"
#include "media/view/media_view_playback_progress.h" #include "media/view/media_view_playback_progress.h"
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
@ -132,11 +132,11 @@ void Float::mouseDoubleClickEvent(QMouseEvent *e) {
} }
void Float::pauseResume() { void Float::pauseResume() {
if (const auto player = instance()->roundVideoPlayer(_item)) { if (const auto streamed = getStreamed()) {
if (player->paused()) { if (streamed->paused()) {
player->resume(); streamed->resume();
} else { } else {
player->pause(); streamed->pause();
} }
} }
} }
@ -206,8 +206,13 @@ void Float::paintEvent(QPaintEvent *e) {
} }
} }
Streaming::Player *Float::getPlayer() const { Streaming::Document *Float::getStreamed() const {
return instance()->roundVideoPlayer(_item); return instance()->roundVideoStreamed(_item);
}
const Streaming::Player *Float::getPlayer() const {
const auto streamed = getStreamed();
return streamed ? &streamed->player() : nullptr;
} }
View::PlaybackProgress *Float::getPlayback() const { View::PlaybackProgress *Float::getPlayback() const {

View File

@ -26,6 +26,7 @@ class PlaybackProgress;
namespace Media { namespace Media {
namespace Streaming { namespace Streaming {
class Document;
class Player; class Player;
} // namespace Streaming } // namespace Streaming
} // namespace Media } // namespace Media
@ -42,7 +43,7 @@ public:
Fn<void(bool visible)> toggleCallback, Fn<void(bool visible)> toggleCallback,
Fn<void(bool closed)> draggedCallback); Fn<void(bool closed)> draggedCallback);
HistoryItem *item() const { [[nodiscard]] HistoryItem *item() const {
return _item; return _item;
} }
void setOpacity(float64 opacity) { void setOpacity(float64 opacity) {
@ -51,17 +52,17 @@ public:
update(); update();
} }
} }
float64 countOpacityByParent() const { [[nodiscard]] float64 countOpacityByParent() const {
return outRatio(); return outRatio();
} }
bool isReady() const { [[nodiscard]] bool isReady() const {
return (getPlayer() != nullptr); return (getPlayer() != nullptr);
} }
void detach(); void detach();
bool detached() const { [[nodiscard]] bool detached() const {
return !_item; return !_item;
} }
bool dragged() const { [[nodiscard]] bool dragged() const {
return _drag; return _drag;
} }
void resetMouseState() { void resetMouseState() {
@ -79,14 +80,15 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override;
private: private:
float64 outRatio() const; [[nodiscard]] float64 outRatio() const;
Streaming::Player *getPlayer() const; [[nodiscard]] Streaming::Document *getStreamed() const;
View::PlaybackProgress *getPlayback() const; [[nodiscard]] const Streaming::Player *getPlayer() const;
[[nodiscard]] View::PlaybackProgress *getPlayback() const;
void repaintItem(); void repaintItem();
void prepareShadow(); void prepareShadow();
bool hasFrame() const; bool hasFrame() const;
bool fillFrame(); bool fillFrame();
QRect getInnerRect() const; [[nodiscard]] QRect getInnerRect() const;
void finishDrag(bool closed); void finishDrag(bool closed);
void pauseResume(); void pauseResume();

View File

@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "media/audio/media_audio_capture.h" #include "media/audio/media_audio_capture.h"
#include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_document.h"
#include "media/streaming/media_streaming_reader.h" #include "media/streaming/media_streaming_reader.h"
#include "media/view/media_view_playback_progress.h" #include "media/view/media_view_playback_progress.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
@ -60,22 +60,20 @@ void finish(not_null<Audio::Instance*> instance) {
struct Instance::Streamed { struct Instance::Streamed {
Streamed( Streamed(
AudioMsgId id, AudioMsgId id,
not_null<::Data::Session*> owner, std::shared_ptr<Streaming::Document> document);
std::shared_ptr<Streaming::Reader> reader);
AudioMsgId id; AudioMsgId id;
Streaming::Player player; std::shared_ptr<Streaming::Document> shared;
Streaming::Information info;
View::PlaybackProgress progress; View::PlaybackProgress progress;
bool clearing = false; bool clearing = false;
rpl::lifetime lifetime;
}; };
Instance::Streamed::Streamed( Instance::Streamed::Streamed(
AudioMsgId id, AudioMsgId id,
not_null<::Data::Session*> owner, std::shared_ptr<Streaming::Document> document)
std::shared_ptr<Streaming::Reader> reader)
: id(id) : id(id)
, player(owner, std::move(reader)) { , shared(std::move(document)) {
} }
Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview) Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview)
@ -175,7 +173,7 @@ void Instance::clearStreamed(not_null<Data*> data) {
return; return;
} }
data->streamed->clearing = true; data->streamed->clearing = true;
data->streamed->player.stop(); data->streamed->shared->stop();
data->isPlaying = false; data->isPlaying = false;
requestRoundVideoResize(); requestRoundVideoResize();
emitUpdate(data->type); emitUpdate(data->type);
@ -344,8 +342,8 @@ void Instance::play(AudioMsgId::Type type) {
if (!data->streamed || IsStopped(getState(type).state)) { if (!data->streamed || IsStopped(getState(type).state)) {
play(data->current); play(data->current);
} else { } else {
if (data->streamed->player.active()) { if (data->streamed->shared->active()) {
data->streamed->player.resume(); data->streamed->shared->resume();
} }
emitUpdate(type); emitUpdate(type);
} }
@ -361,13 +359,13 @@ void Instance::play(const AudioMsgId &audioId) {
if (document->isAudioFile() if (document->isAudioFile()
|| document->isVoiceMessage() || document->isVoiceMessage()
|| document->isVideoMessage()) { || document->isVideoMessage()) {
auto reader = document->owner().documentStreamedReader( auto shared = document->owner().documentStreamer(
document, document,
audioId.contextId()); audioId.contextId());
if (!reader) { if (!shared) {
return; return;
} }
playStreamed(audioId, std::move(reader)); playStreamed(audioId, std::move(shared));
} }
if (document->isVoiceMessage() || document->isVideoMessage()) { if (document->isVoiceMessage() || document->isVideoMessage()) {
document->owner().markMediaRead(document); document->owner().markMediaRead(document);
@ -386,7 +384,7 @@ void Instance::playPause(const AudioMsgId &audioId) {
void Instance::playStreamed( void Instance::playStreamed(
const AudioMsgId &audioId, const AudioMsgId &audioId,
std::shared_ptr<Streaming::Reader> reader) { std::shared_ptr<Streaming::Document> shared) {
Expects(audioId.audio() != nullptr); Expects(audioId.audio() != nullptr);
const auto data = getData(audioId.type()); const auto data = getData(audioId.type());
@ -395,23 +393,16 @@ void Instance::playStreamed(
clearStreamed(data); clearStreamed(data);
data->streamed = std::make_unique<Streamed>( data->streamed = std::make_unique<Streamed>(
audioId, audioId,
&audioId.audio()->owner(), std::move(shared));
std::move(reader));
data->streamed->player.updates( data->streamed->shared->player().updates(
) | rpl::start_with_next_error([=](Streaming::Update &&update) { ) | rpl::start_with_next_error([=](Streaming::Update &&update) {
handleStreamingUpdate(data, std::move(update)); handleStreamingUpdate(data, std::move(update));
}, [=](Streaming::Error &&error) { }, [=](Streaming::Error &&error) {
handleStreamingError(data, std::move(error)); handleStreamingError(data, std::move(error));
}, data->streamed->player.lifetime()); }, data->streamed->lifetime);
data->streamed->player.fullInCache( data->streamed->shared->play(streamingOptions(audioId));
) | rpl::start_with_next([=](bool fullInCache) {
const auto document = data->streamed->id.audio();
document->setLoadedInMediaCache(fullInCache);
}, data->streamed->player.lifetime());
data->streamed->player.play(streamingOptions(audioId));
emitUpdate(audioId.type()); emitUpdate(audioId.type());
} }
@ -437,8 +428,8 @@ Streaming::PlaybackOptions Instance::streamingOptions(
void Instance::pause(AudioMsgId::Type type) { void Instance::pause(AudioMsgId::Type type) {
if (const auto data = getData(type)) { if (const auto data = getData(type)) {
if (data->streamed) { if (data->streamed) {
if (data->streamed->player.active()) { if (data->streamed->shared->active()) {
data->streamed->player.pause(); data->streamed->shared->pause();
} }
emitUpdate(type); emitUpdate(type);
} }
@ -459,13 +450,13 @@ void Instance::playPause(AudioMsgId::Type type) {
if (!data->streamed) { if (!data->streamed) {
play(data->current); play(data->current);
} else { } else {
if (!data->streamed->player.active()) { const auto shared = data->streamed->shared.get();
data->streamed->player.play( if (!shared->active()) {
streamingOptions(data->streamed->id)); shared->play(streamingOptions(data->streamed->id));
} else if (data->streamed->player.paused()) { } else if (shared->paused()) {
data->streamed->player.resume(); shared->resume();
} else { } else {
data->streamed->player.pause(); shared->pause();
} }
emitUpdate(type); emitUpdate(type);
} }
@ -542,13 +533,14 @@ void Instance::startSeeking(AudioMsgId::Type type) {
void Instance::finishSeeking(AudioMsgId::Type type, float64 progress) { void Instance::finishSeeking(AudioMsgId::Type type, float64 progress) {
if (const auto data = getData(type)) { if (const auto data = getData(type)) {
if (data->streamed) { if (const auto streamed = data->streamed.get()) {
const auto duration = data->streamed->info.audio.state.duration; const auto &info = streamed->shared->info();
const auto duration = info.audio.state.duration;
if (duration != kTimeUnknown) { if (duration != kTimeUnknown) {
const auto position = crl::time(std::round( const auto position = crl::time(std::round(
std::clamp(progress, 0., 1.) * duration)); std::clamp(progress, 0., 1.) * duration));
data->streamed->player.play(streamingOptions( streamed->shared->play(streamingOptions(
data->streamed->id, streamed->id,
position)); position));
emitUpdate(type); emitUpdate(type);
} }
@ -567,7 +559,7 @@ void Instance::cancelSeeking(AudioMsgId::Type type) {
void Instance::updateVoicePlaybackSpeed() { void Instance::updateVoicePlaybackSpeed() {
if (const auto data = getData(AudioMsgId::Type::Voice)) { if (const auto data = getData(AudioMsgId::Type::Voice)) {
if (const auto streamed = data->streamed.get()) { if (const auto streamed = data->streamed.get()) {
streamed->player.setSpeed(Global::VoiceMsgPlaybackDoubled() streamed->shared->setSpeed(Global::VoiceMsgPlaybackDoubled()
? kVoicePlaybackSpeedMultiplier ? kVoicePlaybackSpeedMultiplier
: 1.); : 1.);
} }
@ -590,21 +582,21 @@ void Instance::emitUpdate(AudioMsgId::Type type) {
TrackState Instance::getState(AudioMsgId::Type type) const { TrackState Instance::getState(AudioMsgId::Type type) const {
if (const auto data = getData(type)) { if (const auto data = getData(type)) {
if (data->streamed) { if (data->streamed) {
return data->streamed->player.prepareLegacyState(); return data->streamed->shared->player().prepareLegacyState();
} }
} }
return TrackState(); return TrackState();
} }
Streaming::Player *Instance::roundVideoPlayer(HistoryItem *item) const { Streaming::Document *Instance::roundVideoStreamed(HistoryItem *item) const {
if (!item) { if (!item) {
return nullptr; return nullptr;
} else if (const auto data = getData(AudioMsgId::Type::Voice)) { } else if (const auto data = getData(AudioMsgId::Type::Voice)) {
if (const auto streamed = data->streamed.get()) { if (const auto streamed = data->streamed.get()) {
if (streamed->id.contextId() == item->fullId()) { if (streamed->id.contextId() == item->fullId()) {
const auto player = &streamed->player; const auto player = &streamed->shared->player();
if (player->ready() && !player->videoSize().isEmpty()) { if (player->ready() && !player->videoSize().isEmpty()) {
return player; return streamed->shared.get();
} }
} }
} }
@ -614,7 +606,7 @@ Streaming::Player *Instance::roundVideoPlayer(HistoryItem *item) const {
View::PlaybackProgress *Instance::roundVideoPlayback( View::PlaybackProgress *Instance::roundVideoPlayback(
HistoryItem *item) const { HistoryItem *item) const {
return roundVideoPlayer(item) return roundVideoStreamed(item)
? &getData(AudioMsgId::Type::Voice)->streamed->progress ? &getData(AudioMsgId::Type::Voice)->streamed->progress
: nullptr; : nullptr;
} }
@ -627,8 +619,10 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) {
return; return;
} }
setCurrent(state.id); setCurrent(state.id);
if (data->streamed && !data->streamed->info.video.size.isEmpty()) { if (const auto streamed = data->streamed.get()) {
data->streamed->progress.updateState(state); if (!streamed->shared->info().video.size.isEmpty()) {
streamed->progress.updateState(state);
}
} }
_updatedNotifier.fire_copy({state}); _updatedNotifier.fire_copy({state});
if (data->isPlaying && state.state == State::StoppedAtEnd) { if (data->isPlaying && state.state == State::StoppedAtEnd) {
@ -679,8 +673,7 @@ void Instance::handleStreamingUpdate(
using namespace Streaming; using namespace Streaming;
update.data.match([&](Information &update) { update.data.match([&](Information &update) {
data->streamed->info = std::move(update); if (!update.video.size.isEmpty()) {
if (!data->streamed->info.video.size.isEmpty()) {
data->streamed->progress.setValueChangedCallback([=]( data->streamed->progress.setValueChangedCallback([=](
float64, float64,
float64) { float64) {
@ -692,26 +685,18 @@ void Instance::handleStreamingUpdate(
} }
emitUpdate(data->type); emitUpdate(data->type);
}, [&](PreloadedVideo &update) { }, [&](PreloadedVideo &update) {
data->streamed->info.video.state.receivedTill = update.till;
//emitUpdate(data->type, [](AudioMsgId) { return true; }); //emitUpdate(data->type, [](AudioMsgId) { return true; });
}, [&](UpdateVideo &update) { }, [&](UpdateVideo &update) {
data->streamed->info.video.state.position = update.position;
emitUpdate(data->type); emitUpdate(data->type);
}, [&](PreloadedAudio &update) { }, [&](PreloadedAudio &update) {
data->streamed->info.audio.state.receivedTill = update.till;
//emitUpdate(data->type, [](AudioMsgId) { return true; }); //emitUpdate(data->type, [](AudioMsgId) { return true; });
}, [&](UpdateAudio &update) { }, [&](UpdateAudio &update) {
data->streamed->info.audio.state.position = update.position;
emitUpdate(data->type); emitUpdate(data->type);
}, [&](WaitingForData) { }, [&](WaitingForData) {
}, [&](MutedByOther) { }, [&](MutedByOther) {
}, [&](Finished) { }, [&](Finished) {
const auto finishTrack = [](Media::Streaming::TrackState &state) {
state.position = state.receivedTill = state.duration;
};
finishTrack(data->streamed->info.audio.state);
emitUpdate(data->type); emitUpdate(data->type);
if (data->streamed && data->streamed->player.finished()) { if (data->streamed && data->streamed->shared->player().finished()) {
clearStreamed(data); clearStreamed(data);
} }
}); });
@ -720,7 +705,7 @@ void Instance::handleStreamingUpdate(
HistoryItem *Instance::roundVideoItem() const { HistoryItem *Instance::roundVideoItem() const {
const auto data = getData(AudioMsgId::Type::Voice); const auto data = getData(AudioMsgId::Type::Voice);
return (data->streamed return (data->streamed
&& !data->streamed->info.video.size.isEmpty()) && !data->streamed->shared->info().video.size.isEmpty())
? Auth().data().message(data->streamed->id.contextId()) ? Auth().data().message(data->streamed->id.contextId())
: nullptr; : nullptr;
} }
@ -745,19 +730,17 @@ void Instance::handleStreamingError(
const auto document = data->streamed->id.audio(); const auto document = data->streamed->id.audio();
const auto contextId = data->streamed->id.contextId(); const auto contextId = data->streamed->id.contextId();
if (error == Streaming::Error::NotStreamable) { if (error == Streaming::Error::NotStreamable) {
document->setNotSupportsStreaming();
DocumentSaveClickHandler::Save( DocumentSaveClickHandler::Save(
(contextId ? contextId : ::Data::FileOrigin()), (contextId ? contextId : ::Data::FileOrigin()),
document); document);
} else if (error == Streaming::Error::OpenFailed) { } else if (error == Streaming::Error::OpenFailed) {
document->setInappPlaybackFailed();
DocumentSaveClickHandler::Save( DocumentSaveClickHandler::Save(
(contextId ? contextId : ::Data::FileOrigin()), (contextId ? contextId : ::Data::FileOrigin()),
document, document,
DocumentSaveClickHandler::Mode::ToFile); DocumentSaveClickHandler::Mode::ToFile);
} }
emitUpdate(data->type); emitUpdate(data->type);
if (data->streamed && data->streamed->player.failed()) { if (data->streamed && data->streamed->shared->player().failed()) {
clearStreamed(data); clearStreamed(data);
} }
} }

View File

@ -25,7 +25,7 @@ class PlaybackProgress;
namespace Media { namespace Media {
namespace Streaming { namespace Streaming {
class Player; class Document;
class Reader; class Reader;
struct PlaybackOptions; struct PlaybackOptions;
struct Update; struct Update;
@ -80,7 +80,7 @@ public:
void playPause(const AudioMsgId &audioId); void playPause(const AudioMsgId &audioId);
[[nodiscard]] TrackState getState(AudioMsgId::Type type) const; [[nodiscard]] TrackState getState(AudioMsgId::Type type) const;
[[nodiscard]] Streaming::Player *roundVideoPlayer( [[nodiscard]] Streaming::Document *roundVideoStreamed(
HistoryItem *item) const; HistoryItem *item) const;
[[nodiscard]] View::PlaybackProgress *roundVideoPlayback( [[nodiscard]] View::PlaybackProgress *roundVideoPlayback(
HistoryItem *item) const; HistoryItem *item) const;
@ -193,7 +193,7 @@ private:
void setupShortcuts(); void setupShortcuts();
void playStreamed( void playStreamed(
const AudioMsgId &audioId, const AudioMsgId &audioId,
std::shared_ptr<Streaming::Reader> reader); std::shared_ptr<Streaming::Document> shared);
Streaming::PlaybackOptions streamingOptions( Streaming::PlaybackOptions streamingOptions(
const AudioMsgId &audioId, const AudioMsgId &audioId,
crl::time position = 0); crl::time position = 0);

View File

@ -38,10 +38,8 @@ void Instance::callWaitingCallback() {
Document::Document( Document::Document(
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin) std::shared_ptr<Reader> reader)
: _player( : _player(&document->owner(), reader)
&document->owner(),
document->owner().documentStreamedReader(document, origin))
, _radial( , _radial(
[=] { waitingCallback(); }, [=] { waitingCallback(); },
st::defaultInfiniteRadialAnimation) st::defaultInfiniteRadialAnimation)
@ -83,6 +81,10 @@ void Document::resume() {
_player.resume(); _player.resume();
} }
void Document::stop() {
_player.stop();
}
void Document::saveFrameToCover() { void Document::saveFrameToCover() {
auto request = Streaming::FrameRequest(); auto request = Streaming::FrameRequest();
//request.radius = (_doc && _doc->isVideoMessage()) //request.radius = (_doc && _doc->isVideoMessage())
@ -93,6 +95,26 @@ void Document::saveFrameToCover() {
: _info.video.cover; : _info.video.cover;
} }
bool Document::active() const {
return _player.active();
}
bool Document::ready() const {
return _player.ready();
}
bool Document::paused() const {
return _player.paused();
}
float64 Document::speed() const {
return _player.speed();
}
void Document::setSpeed(float64 speed) {
_player.setSpeed(speed);
}
not_null<Instance*> Document::addInstance() { not_null<Instance*> Document::addInstance() {
return _instances.emplace(std::make_unique<Instance>()).first->get(); return _instances.emplace(std::make_unique<Instance>()).first->get();
} }

View File

@ -36,7 +36,7 @@ class Document {
public: public:
Document( Document(
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin); std::shared_ptr<Reader> reader);
[[nodiscard]] const Player &player() const; [[nodiscard]] const Player &player() const;
[[nodiscard]] const Information &info() const; [[nodiscard]] const Information &info() const;
@ -44,8 +44,17 @@ public:
void play(const PlaybackOptions &options); void play(const PlaybackOptions &options);
void pause(); void pause();
void resume(); void resume();
void stop();
void saveFrameToCover(); void saveFrameToCover();
[[nodiscard]] bool active() const;
[[nodiscard]] bool ready() const;
[[nodiscard]] bool paused() const;
[[nodiscard]] float64 speed() const;
void setSpeed(float64 speed); // 0.5 <= speed <= 2.
[[nodiscard]] not_null<Instance*> addInstance(); [[nodiscard]] not_null<Instance*> addInstance();
void removeInstance(not_null<Instance*> instance); void removeInstance(not_null<Instance*> instance);