diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 8fb194ed4..15f4db408 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_layout_item.h" #include "storage/localstorage.h" #include "storage/storage_encrypted_file.h" +#include "media/player/media_player_instance.h" // for instance()->play(). #include "boxes/abstract_box.h" #include "passport/passport_form_controller.h" #include "window/themes/window_theme.h" @@ -1099,6 +1100,15 @@ void Session::requestItemTextRefresh(not_null item) { void Session::requestAnimationPlayInline(not_null item) { _animationPlayInlineRequest.fire_copy(item); + + if (const auto media = item->media()) { + if (const auto data = media->document()) { + if (data && data->isVideoMessage()) { + const auto msgId = item->fullId(); + ::Media::Player::instance()->playPause({ data, msgId }); + } + } + } } rpl::producer> Session::animationPlayInlineRequest() const { diff --git a/Telegram/SourceFiles/history/media/history_media_gif.cpp b/Telegram/SourceFiles/history/media/history_media_gif.cpp index f16afae6f..fa0d88424 100644 --- a/Telegram/SourceFiles/history/media/history_media_gif.cpp +++ b/Telegram/SourceFiles/history/media/history_media_gif.cpp @@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "media/audio/media_audio.h" #include "media/clip/media_clip_reader.h" -#include "media/player/media_player_round_controller.h" #include "media/player/media_player_instance.h" +#include "media/streaming/media_streaming_player.h" #include "media/view/media_view_playback_progress.h" #include "boxes/confirm_box.h" #include "history/history_item_components.h" @@ -69,8 +69,6 @@ QSize HistoryGif::countOptimalSize() { _parent->skipBlockWidth(), _parent->skipBlockHeight()); } - auto tw = 0; - auto th = 0; if (_gif && _gif->state() == Media::Clip::State::Error) { if (!_gif->autoplay()) { Ui::show(Box(lang(lng_gif_error))); @@ -78,20 +76,12 @@ QSize HistoryGif::countOptimalSize() { setClipReader(Media::Clip::ReaderPointer::Bad()); } - const auto reader = currentReader(); - if (reader) { - tw = ConvertScale(reader->width()); - th = ConvertScale(reader->height()); - } else { - tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); - if (!tw || !th) { - tw = ConvertScale(_data->thumbnail()->width()); - th = ConvertScale(_data->thumbnail()->height()); - } - } const auto maxSize = _data->isVideoMessage() ? st::maxVideoMessageSize : st::maxGifSize; + const auto size = ConvertScale(videoSize()); + auto tw = size.width(); + auto th = size.height(); if (tw > maxSize) { th = (maxSize * th) / tw; tw = maxSize; @@ -108,7 +98,7 @@ QSize HistoryGif::countOptimalSize() { auto maxWidth = qMax(tw, st::minPhotoSize); auto minHeight = qMax(th, st::minPhotoSize); accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - if (!reader) { + if (!currentReader() && !activeRoundPlayer()) { accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { @@ -135,21 +125,12 @@ QSize HistoryGif::countOptimalSize() { QSize HistoryGif::countCurrentSize(int newWidth) { auto availableWidth = newWidth; - int tw = 0, th = 0; - const auto reader = currentReader(); - if (reader) { - tw = ConvertScale(reader->width()); - th = ConvertScale(reader->height()); - } else { - tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); - if (!tw || !th) { - tw = ConvertScale(_data->thumbnail()->width()); - th = ConvertScale(_data->thumbnail()->height()); - } - } const auto maxSize = _data->isVideoMessage() ? st::maxVideoMessageSize : st::maxGifSize; + const auto size = ConvertScale(videoSize()); + auto tw = size.width(); + auto th = size.height(); if (tw > maxSize) { th = (maxSize * th) / tw; tw = maxSize; @@ -172,6 +153,7 @@ QSize HistoryGif::countCurrentSize(int newWidth) { newWidth = qMax(tw, st::minPhotoSize); auto newHeight = qMax(th, st::minPhotoSize); accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x()); + const auto reader = activeRoundPlayer() ? nullptr : currentReader(); if (reader) { const auto own = (reader->mode() == Media::Clip::Reader::Mode::Gif); if (own && !reader->started()) { @@ -232,6 +214,20 @@ QSize HistoryGif::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } +QSize HistoryGif::videoSize() const { + if (const auto player = activeRoundPlayer()) { + return player->videoSize(); + } else if (const auto reader = currentReader()) { + return QSize(reader->width(), reader->height()); + } else if (!_data->dimensions.isEmpty()) { + return _data->dimensions; + } else if (const auto thumbnail = _data->thumbnail()) { + return thumbnail->size(); + } else { + return QSize(1, 1); + } +} + void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; @@ -245,7 +241,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, crl:: && cAutoPlayGif() && !_gif && !_gif.isBad() - && !activeRoundVideo()) { + && !activeRoundPlayer()) { _parent->delegate()->elementAnimationAutoplayAsync(_parent); } @@ -258,11 +254,9 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, crl:: auto isRound = _data->isVideoMessage(); auto displayMute = false; - const auto reader = currentReader(); - const auto playingVideo = reader - ? (reader->mode() == Media::Clip::Reader::Mode::Video) - : false; - const auto animating = reader && reader->started(); + const auto player = activeRoundPlayer(); + const auto reader = player ? nullptr : currentReader(); + const auto animating = player || (reader && reader->started()); if ((!animating || item->id < 0) && displayLoading) { ensureAnimation(); @@ -305,13 +299,22 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, crl:: if (animating) { auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); if (isRound) { - if (playingVideo) { + if (player) { paused = false; } else { displayMute = true; } } - p.drawPixmap(rthumb.topLeft(), reader->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms)); + if (player) { + auto request = Media::Streaming::FrameRequest(); + request.outer = QSize(usew, painth) * cIntRetinaFactor(); + request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor(); + request.corners = roundCorners; + request.radius = roundRadius; + p.drawImage(rthumb, player->frame(request)); + } else { + p.drawPixmap(rthumb.topLeft(), reader->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms)); + } if (const auto playback = videoPlayback()) { const auto value = playback->value(); @@ -746,18 +749,17 @@ void HistoryGif::updateStatusText() const { } else if (_data->loaded()) { statusSize = FileStatusSizeLoaded; if (const auto video = activeRoundPlayer()) { - statusSize = -1 - _data->getDuration(); - - const auto type = AudioMsgId::Type::Voice; - const auto state = Media::Player::instance()->getState(type); - if (state.id == video->audioMsgId() && state.length) { + const auto state = video->prepareLegacyState(); + if (state.length) { auto position = int64(0); if (Media::Player::IsStoppedAtEnd(state.state)) { position = state.length; } else if (!Media::Player::IsStoppedOrStopping(state.state)) { position = state.position; } - accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1)); + statusSize = -1 - int((state.length - position) / state.frequency + 1); + } else { + statusSize = -1 - _data->getDuration(); } } } else { @@ -800,33 +802,16 @@ int HistoryGif::additionalWidth(const HistoryMessageVia *via, const HistoryMessa return result; } -Media::Player::RoundController *HistoryGif::activeRoundVideo() const { - return App::wnd()->controller()->roundVideo(_parent->data()); -} - -Media::Clip::Reader *HistoryGif::activeRoundPlayer() const { - if (const auto video = activeRoundVideo()) { - if (const auto result = video->reader()) { - if (result->ready()) { - return result; - } - } - } - return nullptr; +Media::Streaming::Player *HistoryGif::activeRoundPlayer() const { + return Media::Player::instance()->roundVideoPlayer(_parent->data()); } Media::Clip::Reader *HistoryGif::currentReader() const { - if (const auto result = activeRoundPlayer()) { - return result; - } return (_gif && _gif->ready()) ? _gif.get() : nullptr; } Media::View::PlaybackProgress *HistoryGif::videoPlayback() const { - if (const auto video = activeRoundVideo()) { - return video->playback(); - } - return nullptr; + return Media::Player::instance()->roundVideoPlayback(_parent->data()); } void HistoryGif::clipCallback(Media::Clip::Notification notification) { diff --git a/Telegram/SourceFiles/history/media/history_media_gif.h b/Telegram/SourceFiles/history/media/history_media_gif.h index 766324320..453859493 100644 --- a/Telegram/SourceFiles/history/media/history_media_gif.h +++ b/Telegram/SourceFiles/history/media/history_media_gif.h @@ -17,10 +17,12 @@ namespace Media { namespace View { class PlaybackProgress; } // namespace View +} // namespace Media -namespace Player { -class RoundController; -} // namespace Player +namespace Media { +namespace Streaming { +class Player; +} // namespace Streaming } // namespace Media class HistoryGif : public HistoryFileMedia { @@ -86,8 +88,8 @@ private: void playAnimation(bool autoplay) override; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; - Media::Player::RoundController *activeRoundVideo() const; - Media::Clip::Reader *activeRoundPlayer() const; + QSize videoSize() const; + Media::Streaming::Player *activeRoundPlayer() const; Media::Clip::Reader *currentReader() const; Media::View::PlaybackProgress *videoPlayback() const; void clipCallback(Media::Clip::Notification notification); diff --git a/Telegram/SourceFiles/media/player/media_player_float.cpp b/Telegram/SourceFiles/media/player/media_player_float.cpp index 23d62dd7f..dee65dd14 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.cpp +++ b/Telegram/SourceFiles/media/player/media_player_float.cpp @@ -15,10 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/view/history_view_element.h" #include "media/audio/media_audio.h" -#include "media/clip/media_clip_reader.h" +#include "media/streaming/media_streaming_player.h" #include "media/view/media_view_playback_progress.h" #include "media/player/media_player_instance.h" -#include "media/player/media_player_round_controller.h" #include "window/window_controller.h" #include "window/section_widget.h" #include "auth_session.h" @@ -105,9 +104,7 @@ float64 Float::outRatio() const { void Float::mouseReleaseEvent(QMouseEvent *e) { if (base::take(_down) && _item) { - if (const auto controller = _controller->roundVideo(_item)) { - controller->pauseResume(); - } + pauseResume(); } if (_drag) { finishDrag(outRatio() < 0.5); @@ -124,13 +121,21 @@ void Float::finishDrag(bool closed) { void Float::mouseDoubleClickEvent(QMouseEvent *e) { if (_item) { // Handle second click. - if (const auto controller = _controller->roundVideo(_item)) { - controller->pauseResume(); - } + pauseResume(); Ui::showPeerHistoryAtItem(_item); } } +void Float::pauseResume() { + if (const auto player = instance()->roundVideoPlayer(_item)) { + if (player->paused()) { + player->resume(); + } else { + player->pause(); + } + } +} + void Float::detach() { if (_item) { _item = nullptr; @@ -196,35 +201,16 @@ void Float::paintEvent(QPaintEvent *e) { } } -Clip::Reader *Float::getReader() const { - if (detached()) { - return nullptr; - } - if (const auto controller = _controller->roundVideo(_item)) { - if (const auto reader = controller->reader()) { - if (reader->started()) { - return reader; - } - } - } - return nullptr; +Streaming::Player *Float::getPlayer() const { + return instance()->roundVideoPlayer(_item); } View::PlaybackProgress *Float::getPlayback() const { - if (detached()) { - return nullptr; - } - if (const auto controller = _controller->roundVideo(_item)) { - return controller->playback(); - } - return nullptr; + return instance()->roundVideoPlayback(_item); } bool Float::hasFrame() const { - if (const auto reader = getReader()) { - return !reader->current().isNull(); - } - return false; + return (getPlayer() != nullptr); } bool Float::fillFrame() { @@ -238,14 +224,17 @@ bool Float::fillFrame() { auto frameInner = [&] { return QRect(QPoint(), _frame.size() / cIntRetinaFactor()); }; - if (const auto reader = getReader()) { - auto frame = reader->current(); + if (const auto player = getPlayer()) { + auto request = Streaming::FrameRequest::NonStrict(); + request.outer = request.resize = _frame.size(); + request.radius = ImageRoundRadius::Ellipse; + auto frame = player->frame(request); if (!frame.isNull()) { _frame.fill(Qt::transparent); Painter p(&_frame); PainterHighQualityEnabler hq(p); - p.drawPixmap(frameInner(), frame); + p.drawImage(frameInner(), frame); return true; } } diff --git a/Telegram/SourceFiles/media/player/media_player_float.h b/Telegram/SourceFiles/media/player/media_player_float.h index 8637bd510..d03a8c22f 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.h +++ b/Telegram/SourceFiles/media/player/media_player_float.h @@ -19,7 +19,15 @@ namespace Media { namespace View { class PlaybackProgress; } // namespace View +} // namespace Media +namespace Media { +namespace Streaming { +class Player; +} // namespace Streaming +} // namespace Media + +namespace Media { namespace Player { class Float : public Ui::RpWidget, private base::Subscriber { @@ -44,7 +52,7 @@ public: return outRatio(); } bool isReady() const { - return (getReader() != nullptr); + return (getPlayer() != nullptr); } void detach(); bool detached() const { @@ -69,7 +77,7 @@ protected: private: float64 outRatio() const; - Clip::Reader *getReader() const; + Streaming::Player *getPlayer() const; View::PlaybackProgress *getPlayback() const; void repaintItem(); void prepareShadow(); @@ -77,6 +85,7 @@ private: bool fillFrame(); QRect getInnerRect() const; void finishDrag(bool closed); + void pauseResume(); not_null _controller; HistoryItem *_item = nullptr; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 968b9b4f3..4a068515e 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio_capture.h" #include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_loader.h" +#include "media/view/media_view_playback_progress.h" #include "calls/calls_instance.h" #include "history/history.h" #include "history/history_item.h" @@ -61,6 +62,7 @@ struct Instance::Streamed { AudioMsgId id; Streaming::Player player; Streaming::Information info; + View::PlaybackProgress progress; }; Instance::Streamed::Streamed( @@ -112,18 +114,19 @@ Instance::Instance() Instance::~Instance() = default; AudioMsgId::Type Instance::getActiveType() const { - const auto voiceData = getData(AudioMsgId::Type::Voice); - if (voiceData->current) { - const auto state = getState(voiceData->type); - if (!IsStoppedOrStopping(state.state)) { - return voiceData->type; + if (const auto data = getData(AudioMsgId::Type::Voice)) { + if (data->current) { + const auto state = getState(data->type); + if (!IsStoppedOrStopping(state.state)) { + return data->type; + } } } return AudioMsgId::Type::Song; } void Instance::handleSongUpdate(const AudioMsgId &audioId) { - emitUpdate(audioId.type(), [&audioId](const AudioMsgId &playing) { + emitUpdate(audioId.type(), [&](const AudioMsgId &playing) { return (audioId == playing); }); } @@ -166,8 +169,11 @@ void Instance::clearStreamed(not_null data) { } data->streamed->player.stop(); data->isPlaying = false; + requestRoundVideoResize(); emitUpdate(data->type); data->streamed = nullptr; + App::wnd()->controller()->disableGifPauseReason( + Window::GifPauseReason::RoundPlaying); } void Instance::refreshPlaylist(not_null data) { @@ -327,20 +333,13 @@ Instance *instance() { void Instance::play(AudioMsgId::Type type) { if (const auto data = getData(type)) { - const auto state = getState(type); - if (state.id) { - if (IsStopped(state.state)) { - play(state.id); - } else if (data->streamed) { - if (data->streamed->player.active()) { - data->streamed->player.resume(); - } - emitUpdate(type); - } else { - mixer()->resume(state.id); - } - } else { + if (!data->streamed || IsStopped(getState(type).state)) { play(data->current); + } else { + if (data->streamed->player.active()) { + data->streamed->player.resume(); + } + emitUpdate(type); } data->resumeOnCallEnd = false; } @@ -351,17 +350,14 @@ void Instance::play(const AudioMsgId &audioId) { if (!document) { return; } - if (document->isAudioFile() || document->isVoiceMessage()) { + if (document->isAudioFile() + || document->isVoiceMessage() + || document->isVideoMessage()) { auto loader = document->createStreamingLoader(audioId.contextId()); if (!loader) { return; } playStreamed(audioId, std::move(loader)); - } else if (document->isVideoMessage()) { - if (const auto item = App::histItemById(audioId.contextId())) { - setCurrent(audioId); - App::wnd()->controller()->startRoundVideo(item); - } } if (document->isVoiceMessage() || document->isVideoMessage()) { document->owner().markMediaRead(document); @@ -385,9 +381,8 @@ void Instance::playStreamed( const auto data = getData(audioId.type()); Assert(data != nullptr); - if (data->streamed) { - clearStreamed(data); - } + + clearStreamed(data); data->streamed = std::make_unique( audioId, &audioId.audio()->owner(), @@ -408,8 +403,11 @@ void Instance::playStreamed( Streaming::PlaybackOptions Instance::streamingOptions( const AudioMsgId &audioId, crl::time position) { + const auto document = audioId.audio(); auto result = Streaming::PlaybackOptions(); - result.mode = Streaming::Mode::Audio; + result.mode = (document && document->isVideoMessage()) + ? Streaming::Mode::Both + : Streaming::Mode::Audio; result.audioId = audioId; result.position = position; return result; @@ -422,11 +420,6 @@ void Instance::pause(AudioMsgId::Type type) { data->streamed->player.pause(); } emitUpdate(type); - } else { - const auto state = getState(type); - if (state.id) { - mixer()->pause(state.id); - } } } } @@ -434,12 +427,7 @@ void Instance::pause(AudioMsgId::Type type) { void Instance::stop(AudioMsgId::Type type) { if (const auto data = getData(type)) { if (data->streamed) { - data->streamed = nullptr; - } else { - const auto state = getState(type); - if (state.id) { - mixer()->stop(state.id); - } + clearStreamed(data); } data->resumeOnCallEnd = false; } @@ -447,7 +435,9 @@ void Instance::stop(AudioMsgId::Type type) { void Instance::playPause(AudioMsgId::Type type) { if (const auto data = getData(type)) { - if (data->streamed) { + if (!data->streamed) { + play(data->current); + } else { if (!data->streamed->player.active()) { data->streamed->player.play( streamingOptions(data->streamed->id)); @@ -457,21 +447,6 @@ void Instance::playPause(AudioMsgId::Type type) { data->streamed->player.pause(); } emitUpdate(type); - } else { - const auto state = getState(type); - if (state.id - && state.id.audio() == data->current.audio() - && state.id.contextId() == data->current.contextId()) { - if (IsStopped(state.state)) { - play(state.id); - } else if (IsPaused(state.state) || state.state == State::Pausing) { - mixer()->resume(state.id); - } else { - mixer()->pause(state.id); - } - } else { - play(data->current); - } } data->resumeOnCallEnd = false; } @@ -556,13 +531,6 @@ void Instance::finishSeeking(AudioMsgId::Type type, float64 progress) { position)); emitUpdate(type); } - // - // Right now all music is played in streaming player. - //} else { - // const auto state = getState(type); - // if (state.id && state.length && state.frequency) { - // mixer()->seek(type, qRound(progress * state.length * 1000. / state.frequency)); - // } } } cancelSeeking(type); @@ -594,7 +562,30 @@ TrackState Instance::getState(AudioMsgId::Type type) const { return data->streamed->player.prepareLegacyState(); } } - return mixer()->currentState(type); + return TrackState(); +} + +Streaming::Player *Instance::roundVideoPlayer(HistoryItem *item) const { + if (!item) { + return nullptr; + } else if (const auto data = getData(AudioMsgId::Type::Voice)) { + if (const auto streamed = data->streamed.get()) { + if (streamed->id.contextId() == item->fullId()) { + const auto player = &streamed->player; + if (player->ready() && !player->videoSize().isEmpty()) { + return player; + } + } + } + } + return nullptr; +} + +View::PlaybackProgress *Instance::roundVideoPlayback( + HistoryItem *item) const { + return roundVideoPlayer(item) + ? &getData(AudioMsgId::Type::Voice)->streamed->progress + : nullptr; } template @@ -605,6 +596,10 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) { return; } setCurrent(state.id); + if (data->streamed && !data->streamed->info.video.size.isEmpty()) { + LOG(("ID: %1, PROGRESS: %1 / %2").arg(state.id.audio()->id).arg(state.position).arg(state.length)); + data->streamed->progress.updateState(state); + } _updatedNotifier.notify(state, true); if (data->isPlaying && state.state == State::StoppedAtEnd) { if (data->repeatEnabled) { @@ -662,12 +657,26 @@ void Instance::handleStreamingUpdate( Streaming::Update &&update) { using namespace Streaming; - update.data.match([&](Information & update) { + update.data.match([&](Information &update) { data->streamed->info = std::move(update); + if (!data->streamed->info.video.size.isEmpty()) { + data->streamed->progress.setValueChangedCallback([=]( + float64, + float64) { + requestRoundVideoRepaint(); + }); + App::wnd()->controller()->enableGifPauseReason( + Window::GifPauseReason::RoundPlaying); + requestRoundVideoResize(); + } emitUpdate(data->type); }, [&](PreloadedVideo &update) { + data->streamed->info.video.state.receivedTill = update.till; + //emitUpdate(data->type, [](AudioMsgId) { return true; }); }, [&](UpdateVideo &update) { - }, [&](PreloadedAudio & update) { + data->streamed->info.video.state.position = update.position; + emitUpdate(data->type); + }, [&](PreloadedAudio &update) { data->streamed->info.audio.state.receivedTill = update.till; //emitUpdate(data->type, [](AudioMsgId) { return true; }); }, [&](UpdateAudio &update) { @@ -682,11 +691,32 @@ void Instance::handleStreamingUpdate( finishTrack(data->streamed->info.audio.state); emitUpdate(data->type); if (data->streamed && data->streamed->player.finished()) { - data->streamed = nullptr; + clearStreamed(data); } }); } +HistoryItem *Instance::roundVideoItem() const { + const auto data = getData(AudioMsgId::Type::Voice); + return (data->streamed + && !data->streamed->info.video.size.isEmpty()) + ? App::histItemById(data->streamed->id.contextId()) + : nullptr; + +} + +void Instance::requestRoundVideoResize() const { + if (const auto item = roundVideoItem()) { + Auth().data().requestItemResize(item); + } +} + +void Instance::requestRoundVideoRepaint() const { + if (const auto item = roundVideoItem()) { + Auth().data().requestItemRepaint(item); + } +} + void Instance::handleStreamingError( not_null data, Streaming::Error &&error) { @@ -708,7 +738,7 @@ void Instance::handleStreamingError( } emitUpdate(data->type); if (data->streamed && data->streamed->player.failed()) { - data->streamed = nullptr; + clearStreamed(data); } } diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 5b58da81c..399ade703 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -15,7 +15,17 @@ namespace Media { namespace Audio { class Instance; } // namespace Audio +} // namespace Media + +namespace Media { +namespace View { +class PlaybackProgress; +} // namespace View +} // namespace Media + +namespace Media { namespace Streaming { +class Player; class Loader; struct PlaybackOptions; struct Update; @@ -68,30 +78,35 @@ public: void play(const AudioMsgId &audioId); void playPause(const AudioMsgId &audioId); - TrackState getState(AudioMsgId::Type type) const; + [[nodiscard]] TrackState getState(AudioMsgId::Type type) const; - AudioMsgId current(AudioMsgId::Type type) const { - if (auto data = getData(type)) { + [[nodiscard]] Streaming::Player *roundVideoPlayer( + HistoryItem *item) const; + [[nodiscard]] View::PlaybackProgress *roundVideoPlayback( + HistoryItem *item) const; + + [[nodiscard]] AudioMsgId current(AudioMsgId::Type type) const { + if (const auto data = getData(type)) { return data->current; } return AudioMsgId(); } - bool repeatEnabled(AudioMsgId::Type type) const { - if (auto data = getData(type)) { + [[nodiscard]] bool repeatEnabled(AudioMsgId::Type type) const { + if (const auto data = getData(type)) { return data->repeatEnabled; } return false; } void toggleRepeat(AudioMsgId::Type type) { - if (auto data = getData(type)) { + if (const auto data = getData(type)) { data->repeatEnabled = !data->repeatEnabled; _repeatChangedNotifier.notify(type); } } - bool isSeeking(AudioMsgId::Type type) const { - if (auto data = getData(type)) { + [[nodiscard]] bool isSeeking(AudioMsgId::Type type) const { + if (const auto data = getData(type)) { return (data->seeking == data->current); } return false; @@ -100,8 +115,8 @@ public: void finishSeeking(AudioMsgId::Type type, float64 progress); void cancelSeeking(AudioMsgId::Type type); - bool nextAvailable(AudioMsgId::Type type) const; - bool previousAvailable(AudioMsgId::Type type) const; + [[nodiscard]] bool nextAvailable(AudioMsgId::Type type) const; + [[nodiscard]] bool previousAvailable(AudioMsgId::Type type) const; struct Switch { AudioMsgId from; @@ -220,6 +235,10 @@ private: return nullptr; } + HistoryItem *roundVideoItem() const; + void requestRoundVideoResize() const; + void requestRoundVideoRepaint() const; + Data _songData; Data _voiceData; diff --git a/Telegram/SourceFiles/media/player/media_player_round_controller.cpp b/Telegram/SourceFiles/media/player/media_player_round_controller.cpp deleted file mode 100644 index 792a4c180..000000000 --- a/Telegram/SourceFiles/media/player/media_player_round_controller.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "media/player/media_player_round_controller.h" - -#include "media/audio/media_audio.h" -#include "media/clip/media_clip_reader.h" -#include "media/player/media_player_instance.h" -#include "media/view/media_view_playback_progress.h" -#include "history/history_item.h" -#include "window/window_controller.h" -#include "data/data_media_types.h" -#include "data/data_document.h" -#include "data/data_session.h" -#include "auth_session.h" - -namespace Media { -namespace Player { - -struct RoundController::CreateTag { -}; - -std::unique_ptr RoundController::TryStart( - not_null parent, - not_null item) { - const auto media = item->media(); - if (!media) { - return nullptr; - } - const auto document = media->document(); - if (!document || !document->isVideoMessage()) { - return nullptr; - } - return std::make_unique(CreateTag(), parent, item); -} - -RoundController::RoundController( - CreateTag&&, - not_null parent, - not_null item) -: _parent(parent) -, _data(item->media()->document()) -, _context(item) { - Expects(_data->isVideoMessage()); - - subscribe(instance()->updatedNotifier(), [this](const TrackState &state) { - handleAudioUpdate(state); - }); - - _reader = Clip::MakeReader( - _data, - _context->fullId(), - [=](Clip::Notification notification) { callback(notification); }, - Clip::Reader::Mode::Video); - _playbackProgress = std::make_unique(); - _playbackProgress->setValueChangedCallback([=](float64, float64) { - Auth().data().requestItemRepaint(_context); - }); - Auth().data().markMediaRead(_data); - Auth().data().itemRemoved( - ) | rpl::start_with_next([=](not_null item) { - if (item == _context) { - stop(State::Stopped); - } - }, lifetime()); - Auth().data().itemRepaintRequest( - ) | rpl::start_with_next([=](not_null item) { - if (item == _context) { - crl::on_main(this, [=] { - checkReaderState(); - }); - } - }, lifetime()); -} - -rpl::lifetime &RoundController::lifetime() { - return _lifetime; -} - -FullMsgId RoundController::contextId() const { - return _context->fullId(); -} - -void RoundController::pauseResume() { - if (checkReaderState()) { - _reader->pauseResumeVideo(); - } -} - -Clip::Reader *RoundController::reader() const { - return _reader ? _reader.get() : nullptr; -} - -View::PlaybackProgress *RoundController::playback() const { - return _playbackProgress.get(); -} - -void RoundController::handleAudioUpdate(const TrackState &state) { - if (state.id.type() != AudioMsgId::Type::Voice) { - return; - } - const auto audio = _reader->audioMsgId(); - const auto another = (state.id != _reader->audioMsgId()); - const auto stopped = IsStoppedOrStopping(state.state); - if ((another && !stopped) || (!another && stopped)) { - stop(State::Stopped); - return; - } else if (another) { - return; - } - if (_playbackProgress) { - _playbackProgress->updateState(state); - } - if (IsPaused(state.state) || state.state == State::Pausing) { - if (!_reader->videoPaused()) { - _reader->pauseResumeVideo(); - } - } else { - if (_reader->videoPaused()) { - _reader->pauseResumeVideo(); - } - } -} - -void RoundController::callback(Clip::Notification notification) { - if (!_reader) { - return; - } - switch (notification) { - case Clip::NotificationReinit: { - if (checkReaderState()) { - Auth().data().requestItemResize(_context); - } - } break; - - case Clip::NotificationRepaint: { - Auth().data().requestItemRepaint(_context); - } break; - } -} - -bool RoundController::checkReaderState() { - if (!_reader) { - return false; - } - const auto state = _reader->state(); - if (state == Media::Clip::State::Error) { - stop(State::StoppedAtError); - return false; - } else if (state == Media::Clip::State::Finished) { - stop(State::StoppedAtEnd); - return false; - } else if (_reader->ready() && !_reader->started()) { - const auto size = QSize(_reader->width(), _reader->height()) - / cIntRetinaFactor(); - _reader->start( - size.width(), - size.height(), - size.width(), - size.height(), - ImageRoundRadius::Ellipse, - RectPart::AllCorners); - } - return true; -} - -void RoundController::stop(State state) { - if (const auto audioId = _reader->audioMsgId()) { - mixer()->stop(audioId, state); - } - _parent->roundVideoFinished(this); -} - -RoundController::~RoundController() = default; - -} // namespace Player -} // namespace Media diff --git a/Telegram/SourceFiles/media/player/media_player_round_controller.h b/Telegram/SourceFiles/media/player/media_player_round_controller.h deleted file mode 100644 index 3a0ebd860..000000000 --- a/Telegram/SourceFiles/media/player/media_player_round_controller.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -class HistoryItem; -class AudioMsgId; - -namespace Window { -class Controller; -} // namespace Window - -namespace Media { -namespace View { -class PlaybackProgress; -} // namespace View -} // namespace Media - -namespace Media { -namespace Player { - -struct TrackState; -enum class State; - -class RoundController - : public base::has_weak_ptr - , private base::Subscriber { - struct CreateTag; - -public: - static std::unique_ptr TryStart( - not_null parent, - not_null item); - - FullMsgId contextId() const; - void pauseResume(); - Clip::Reader *reader() const; - View::PlaybackProgress *playback() const; - - rpl::lifetime &lifetime(); - - RoundController( - CreateTag&&, - not_null parent, - not_null item); - ~RoundController(); - -private: - void stop(State state); - bool checkReaderState(); - void callback(Clip::Notification notification); - void handleAudioUpdate(const TrackState &audioId); - - not_null _parent; - not_null _data; - not_null _context; - Clip::ReaderPointer _reader; - std::unique_ptr _playbackProgress; - - rpl::lifetime _lifetime; - -}; - -} // namespace Player -} // namespace Media diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 606b3c563..2e0df756c 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -94,27 +94,27 @@ Widget::Widget(QWidget *parent) : RpWidget(parent) _nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents); _timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents); - _playbackProgress->setInLoadingStateChangedCallback([this](bool loading) { + _playbackProgress->setInLoadingStateChangedCallback([=](bool loading) { _playbackSlider->setDisabled(loading); }); - _playbackProgress->setValueChangedCallback([this](float64 value, float64) { + _playbackProgress->setValueChangedCallback([=](float64 value, float64) { _playbackSlider->setValue(value); }); - _playbackSlider->setChangeProgressCallback([this](float64 value) { + _playbackSlider->setChangeProgressCallback([=](float64 value) { if (_type != AudioMsgId::Type::Song) { return; // Round video seek is not supported for now :( } _playbackProgress->setValue(value, false); handleSeekProgress(value); }); - _playbackSlider->setChangeFinishedCallback([this](float64 value) { + _playbackSlider->setChangeFinishedCallback([=](float64 value) { if (_type != AudioMsgId::Type::Song) { return; // Round video seek is not supported for now :( } _playbackProgress->setValue(value, false); handleSeekFinished(value); }); - _playPause->setClickedCallback([this] { + _playPause->setClickedCallback([=] { instance()->playPauseCancelClicked(_type); }); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_common.h b/Telegram/SourceFiles/media/streaming/media_streaming_common.h index 210c8bb89..de70c8262 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_common.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_common.h @@ -114,6 +114,13 @@ struct FrameRequest { QSize outer; ImageRoundRadius radius = ImageRoundRadius(); RectParts corners = RectPart::AllCorners; + bool strict = true; + + static FrameRequest NonStrict() { + auto result = FrameRequest(); + result.strict = false; + return result; + } bool empty() const { return resize.isEmpty(); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 9a52a7efb..bc1da6a70 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -750,13 +750,18 @@ bool Player::active() const { } bool Player::ready() const { - return (_stage != Stage::Uninitialized) && (_stage != Stage::Initializing); + return (_stage != Stage::Uninitialized) + && (_stage != Stage::Initializing); } rpl::producer Player::updates() const { return _updates.events(); } +QSize Player::videoSize() const { + return _information.video.size; +} + QImage Player::frame(const FrameRequest &request) const { Expects(_video != nullptr); @@ -775,6 +780,8 @@ Media::Player::TrackState Player::prepareLegacyState() const { ? State::StoppedAtError : finished() ? State::StoppedAtEnd + : (_stage == Stage::Uninitialized) + ? State::Stopped : paused() ? State::Paused : State::Playing; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.h b/Telegram/SourceFiles/media/streaming/media_streaming_player.h index 40d5f0732..e75985c38 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.h @@ -58,8 +58,8 @@ public: [[nodiscard]] rpl::producer updates() const; + [[nodiscard]] QSize videoSize() const; [[nodiscard]] QImage frame(const FrameRequest &request) const; - //[[nodiscard]] int videoRotation() const; [[nodiscard]] Media::Player::TrackState prepareLegacyState() const; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp index 4dbf5f41b..4d091a633 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp @@ -506,6 +506,10 @@ QImage PrepareByRequest( PainterHighQualityEnabler hq(p); p.drawImage(QRect(QPoint(), request.outer), original); } + if ((request.corners & RectPart::AllCorners) + && (request.radius != ImageRoundRadius::None)) { + Images::prepareRound(storage, request.radius, request.corners); + } // #TODO streaming later full prepare support. return storage; } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp index 1c2081e1d..b13f162c1 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp @@ -97,7 +97,7 @@ private: crl::time _loopingShift = 0; rpl::event_stream<> _checkNextFrame; rpl::event_stream<> _waitingForData; - FrameRequest _request; + FrameRequest _request = FrameRequest::NonStrict(); bool _queued = false; base::ConcurrentTimer _readFramesTimer; @@ -801,7 +801,8 @@ crl::time VideoTrack::markFrameDisplayed(crl::time now) { QImage VideoTrack::frame(const FrameRequest &request) { const auto frame = _shared->frameForPaint(); - const auto changed = (frame->request != request); + const auto changed = (frame->request != request) + && (request.strict || !frame->request.strict); if (changed) { frame->request = request; _wrapped.with([=](Implementation &unwrapped) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h index 20c63cf9c..8c0a91626 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h @@ -65,7 +65,7 @@ private: crl::time displayed = kTimeUnknown; crl::time display = kTimeUnknown; - FrameRequest request; + FrameRequest request = FrameRequest::NonStrict(); QImage prepared; }; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 3a947e4a4..212e94b21 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -165,7 +165,6 @@ struct OverlayWidget::Streamed { QImage frameForDirectPaint; bool resumeOnCallEnd = false; - std::optional lastError; }; OverlayWidget::Streamed::Streamed( @@ -2097,7 +2096,6 @@ void OverlayWidget::handleStreamingError(Streaming::Error &&error) { if (!_doc->canBePlayed()) { redisplayContent(); } else { - _streamed->lastError = std::move(error); playbackWaitingChange(false); updatePlaybackState(); } @@ -2230,7 +2228,6 @@ void OverlayWidget::playbackPauseResume() { Expects(_streamed != nullptr); _streamed->resumeOnCallEnd = false; - _streamed->lastError = std::nullopt; if (const auto item = App::histItemById(_msgid)) { if (_streamed->player.failed()) { clearStreaming(); diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 74ecc4e1c..99fec632f 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/view/history_view_element.h" #include "history/feed/history_feed_section.h" -#include "media/player/media_player_round_controller.h" +#include "data/data_media_types.h" #include "data/data_session.h" #include "data/data_feed.h" #include "data/data_channel.h" @@ -116,15 +116,6 @@ void Controller::showEditPeerBox(PeerData *peer) { } void Controller::init() { - session().data().animationPlayInlineRequest( - ) | rpl::start_with_next([=](auto item) { - if (const auto video = roundVideo(item)) { - video->pauseResume(); - } else { - startRoundVideo(item); - } - }, lifetime()); - if (session().supportMode()) { initSupportMode(); } @@ -616,40 +607,6 @@ not_null Controller::chats() const { return App::wnd()->chatsWidget(); } -bool Controller::startRoundVideo(not_null context) { - if (auto video = RoundController::TryStart(this, context)) { - enableGifPauseReason(Window::GifPauseReason::RoundPlaying); - _roundVideo = std::move(video); - return true; - } - return false; -} - -auto Controller::currentRoundVideo() const -> RoundController* { - return _roundVideo.get(); -} - -auto Controller::roundVideo(not_null context) const --> RoundController* { - return roundVideo(context->fullId()); -} - -auto Controller::roundVideo(FullMsgId contextId) const -> RoundController* { - if (const auto result = currentRoundVideo()) { - if (result->contextId() == contextId) { - return result; - } - } - return nullptr; -} - -void Controller::roundVideoFinished(not_null video) { - if (video == _roundVideo.get()) { - _roundVideo = nullptr; - disableGifPauseReason(Window::GifPauseReason::RoundPlaying); - } -} - void Controller::setDefaultFloatPlayerDelegate( not_null delegate) { Expects(_defaultFloatPlayerDelegate == nullptr); diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index abc00adac..14c3ad754 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -23,7 +23,6 @@ enum class Type; namespace Media { namespace Player { -class RoundController; class FloatController; class FloatDelegate; } // namespace Player @@ -138,7 +137,7 @@ private: }; -class Controller +class Controller : public Navigation , private base::Subscriber { public: @@ -252,13 +251,6 @@ public: return this; } - using RoundController = Media::Player::RoundController; - bool startRoundVideo(not_null context); - RoundController *currentRoundVideo() const; - RoundController *roundVideo(not_null context) const; - RoundController *roundVideo(FullMsgId contextId) const; - void roundVideoFinished(not_null video); - void setDefaultFloatPlayerDelegate( not_null delegate); void replaceFloatPlayerDelegate( @@ -307,7 +299,6 @@ private: std::deque _chatEntryHistory; int _chatEntryHistoryPosition = -1; - std::unique_ptr _roundVideo; std::unique_ptr _floatPlayers; Media::Player::FloatDelegate *_defaultFloatPlayerDelegate = nullptr; Media::Player::FloatDelegate *_replacementFloatPlayerDelegate = nullptr; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 1632a0c49..cbcd81e74 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -448,8 +448,6 @@ <(src_loc)/media/player/media_player_instance.h <(src_loc)/media/player/media_player_panel.cpp <(src_loc)/media/player/media_player_panel.h -<(src_loc)/media/player/media_player_round_controller.cpp -<(src_loc)/media/player/media_player_round_controller.h <(src_loc)/media/player/media_player_volume_controller.cpp <(src_loc)/media/player/media_player_volume_controller.h <(src_loc)/media/player/media_player_widget.cpp