diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d6fdb2a4db..94741dda35 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -692,14 +692,6 @@ PRIVATE media/audio/media_child_ffmpeg_loader.h media/audio/media_openal_functions.cpp media/audio/media_openal_functions.h - media/clip/media_clip_check_streaming.cpp - media/clip/media_clip_check_streaming.h - media/clip/media_clip_ffmpeg.cpp - media/clip/media_clip_ffmpeg.h - media/clip/media_clip_implementation.cpp - media/clip/media_clip_implementation.h - media/clip/media_clip_reader.cpp - media/clip/media_clip_reader.h media/player/media_player_button.cpp media/player/media_player_button.h media/player/media_player_float.cpp diff --git a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp index baca58e7bc..083778c9e3 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp @@ -110,7 +110,7 @@ void DicePack::tryGenerateLocalZero() { const auto document = _session->data().processDocument( result->document, Images::FromImageInMemory(result->thumb, "PNG")); - document->setLocation(FileLocation(path)); + document->setLocation(Core::FileLocation(path)); _map.emplace(0, document); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 5bc8f9c47b..f9b9f496de 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -21,13 +21,8 @@ enum { LocalEncryptSaltSize = 32, // 256 bit AnimationTimerDelta = 7, - ClipThreadsCount = 8, - AverageGifSize = 320 * 240, - WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it RecentInlineBotsLimit = 10, - AVBlockSize = 4096, // 4Kb for ffmpeg blocksize - AutoSearchTimeout = 900, // 0.9 secs SearchPerPage = 50, SearchManyPerPage = 100, diff --git a/Telegram/SourceFiles/core/file_location.cpp b/Telegram/SourceFiles/core/file_location.cpp new file mode 100644 index 0000000000..641bf0ea0c --- /dev/null +++ b/Telegram/SourceFiles/core/file_location.cpp @@ -0,0 +1,121 @@ +/* +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 "core/file_location.h" + +#include "platform/platform_file_bookmark.h" +#include "logs.h" + +#include + +namespace Core { +namespace { + +const auto kInMediaCacheLocation = u"*media_cache*"_q; + +} // namespace + +ReadAccessEnabler::ReadAccessEnabler(const Platform::FileBookmark *bookmark) +: _bookmark(bookmark) +, _failed(_bookmark ? !_bookmark->enable() : false) { +} + +ReadAccessEnabler::ReadAccessEnabler( + const std::shared_ptr &bookmark) +: _bookmark(bookmark.get()) +, _failed(_bookmark ? !_bookmark->enable() : false) { +} + +ReadAccessEnabler::~ReadAccessEnabler() { + if (_bookmark && !_failed) _bookmark->disable(); +} + +FileLocation::FileLocation(const QString &name) : fname(name) { + if (fname.isEmpty() || fname == kInMediaCacheLocation) { + size = 0; + } else { + setBookmark(Platform::PathBookmark(name)); + + QFileInfo f(name); + if (f.exists()) { + qint64 s = f.size(); + if (s > INT_MAX) { + fname = QString(); + _bookmark = nullptr; + size = 0; + } else { + modified = f.lastModified(); + size = qint32(s); + } + } else { + fname = QString(); + _bookmark = nullptr; + size = 0; + } + } +} + +FileLocation FileLocation::InMediaCacheLocation() { + return FileLocation(kInMediaCacheLocation); +} + +bool FileLocation::check() const { + if (fname.isEmpty() || fname == kInMediaCacheLocation) { + return false; + } + + ReadAccessEnabler enabler(_bookmark); + if (enabler.failed()) { + const_cast(this)->_bookmark = nullptr; + } + + QFileInfo f(name()); + if (!f.isReadable()) return false; + + quint64 s = f.size(); + if (s > INT_MAX) { + DEBUG_LOG(("File location check: Wrong size %1").arg(s)); + return false; + } + + if (qint32(s) != size) { + DEBUG_LOG(("File location check: Wrong size %1 when should be %2").arg(s).arg(size)); + return false; + } + auto realModified = f.lastModified(); + if (realModified != modified) { + DEBUG_LOG(("File location check: Wrong last modified time %1 when should be %2").arg(realModified.toMSecsSinceEpoch()).arg(modified.toMSecsSinceEpoch())); + return false; + } + return true; +} + +const QString &FileLocation::name() const { + return _bookmark ? _bookmark->name(fname) : fname; +} + +QByteArray FileLocation::bookmark() const { + return _bookmark ? _bookmark->bookmark() : QByteArray(); +} + +bool FileLocation::inMediaCache() const { + return (fname == kInMediaCacheLocation); +} + +void FileLocation::setBookmark(const QByteArray &bm) { + _bookmark.reset(bm.isEmpty() ? nullptr : new Platform::FileBookmark(bm)); +} + +bool FileLocation::accessEnable() const { + return isEmpty() ? false : (_bookmark ? _bookmark->enable() : true); +} + +void FileLocation::accessDisable() const { + return _bookmark ? _bookmark->disable() : (void)0; +} + +} // namespace Core diff --git a/Telegram/SourceFiles/core/file_location.h b/Telegram/SourceFiles/core/file_location.h new file mode 100644 index 0000000000..16db6fce3e --- /dev/null +++ b/Telegram/SourceFiles/core/file_location.h @@ -0,0 +1,72 @@ +/* +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 + +#include + +namespace Platform { +class FileBookmark; +} // namespace Platform + +namespace Core { + +class ReadAccessEnabler { +public: + ReadAccessEnabler(const Platform::FileBookmark *bookmark); + ReadAccessEnabler( + const std::shared_ptr &bookmark); + bool failed() const { + return _failed; + } + ~ReadAccessEnabler(); + +private: + const Platform::FileBookmark *_bookmark = nullptr; + bool _failed; + +}; + +class FileLocation { +public: + FileLocation() = default; + explicit FileLocation(const QString &name); + + static FileLocation InMediaCacheLocation(); + + [[nodiscard]] bool check() const; + [[nodiscard]] const QString &name() const; + void setBookmark(const QByteArray &bookmark); + QByteArray bookmark() const; + [[nodiscard]] bool isEmpty() const { + return name().isEmpty(); + } + [[nodiscard]] bool inMediaCache() const; + + bool accessEnable() const; + void accessDisable() const; + + QString fname; + QDateTime modified; + qint32 size; + +private: + std::shared_ptr _bookmark; + +}; + +inline bool operator==(const FileLocation &a, const FileLocation &b) { + return (a.name() == b.name()) + && (a.modified == b.modified) + && (a.size == b.size); +} + +inline bool operator!=(const FileLocation &a, const FileLocation &b) { + return !(a == b); +} + +} // namespace Core diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 9d9dbf3e15..a4784561b2 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -849,7 +849,7 @@ void DocumentData::finishLoad() { _flags |= Flag::DownloadCancelled; return; } - setLocation(FileLocation(_loader->fileName())); + setLocation(Core::FileLocation(_loader->fileName())); setGoodThumbnailDataReady(); if (const auto media = activeMediaView()) { media->setBytes(_loader->bytes()); @@ -917,7 +917,7 @@ void DocumentData::setLoadedInMediaCache(bool loaded) { if (loadedInMediaCache()) { session().local().writeFileLocation( mediaKey(), - FileLocation::InMediaCacheLocation()); + Core::FileLocation::InMediaCacheLocation()); } else { session().local().removeFileLocation(mediaKey()); } @@ -926,7 +926,7 @@ void DocumentData::setLoadedInMediaCache(bool loaded) { } void DocumentData::setLoadedInMediaCacheLocation() { - _location = FileLocation(); + _location = Core::FileLocation(); _flags |= Flag::LoadedInMediaCache; } @@ -954,10 +954,10 @@ void DocumentData::save( f.write(media->bytes()); f.close(); - setLocation(FileLocation(toFile)); + setLocation(Core::FileLocation(toFile)); session().local().writeFileLocation( mediaKey(), - FileLocation(toFile)); + Core::FileLocation(toFile)); } else if (l.accessEnable()) { const auto &alreadyName = l.name(); if (alreadyName != toFile) { @@ -1151,7 +1151,7 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) { return result; } -const FileLocation &DocumentData::location(bool check) const { +const Core::FileLocation &DocumentData::location(bool check) const { if (check && !_location.check()) { const auto location = session().local().readFileLocation(mediaKey()); const auto that = const_cast(this); @@ -1164,7 +1164,7 @@ const FileLocation &DocumentData::location(bool check) const { return _location; } -void DocumentData::setLocation(const FileLocation &loc) { +void DocumentData::setLocation(const Core::FileLocation &loc) { if (loc.inMediaCache()) { setLoadedInMediaCacheLocation(); } else if (loc.check()) { @@ -1207,7 +1207,7 @@ bool DocumentData::saveFromDataChecked() { return false; } file.close(); - _location = FileLocation(path); + _location = Core::FileLocation(path); session().local().writeFileLocation(mediaKey(), _location); return true; } @@ -1585,7 +1585,7 @@ void DocumentData::setRemoteLocation( } else if (_location.isEmpty() && loadedInMediaCache()) { session().local().writeFileLocation( mediaKey(), - FileLocation::InMediaCacheLocation()); + Core::FileLocation::InMediaCacheLocation()); } } } diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index a31dd63701..35b998d12d 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/binary_guard.h" #include "data/data_types.h" #include "data/data_cloud_file.h" +#include "core/file_location.h" #include "ui/image/image.h" class mtpFileLoader; @@ -114,8 +115,8 @@ public: void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; - [[nodiscard]] const FileLocation &location(bool check = false) const; - void setLocation(const FileLocation &loc); + [[nodiscard]] const Core::FileLocation &location(bool check = false) const; + void setLocation(const Core::FileLocation &loc); bool saveFromData(); bool saveFromDataSilent(); @@ -315,7 +316,7 @@ private: std::weak_ptr _media; PhotoData *_goodThumbnailPhoto = nullptr; - FileLocation _location; + Core::FileLocation _location; std::unique_ptr _additional; int32 _duration = -1; mutable Flags _flags = kStreamingSupportedUnknown; diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp index 28916e511f..4a1267e6ec 100644 --- a/Telegram/SourceFiles/data/data_document_media.cpp +++ b/Telegram/SourceFiles/data/data_document_media.cpp @@ -111,8 +111,8 @@ void VideoPreviewState::automaticLoad(Data::FileOrigin origin) const { _media->videoThumbnailContent(), std::move(callback)) : ::Media::Clip::MakeReader( - _media, - FullMsgId(), + _media->owner()->location(), + _media->bytes(), std::move(callback)); } @@ -386,7 +386,7 @@ void DocumentMedia::GenerateGoodThumbnail( : FileType::Video; auto location = document->location().isEmpty() ? nullptr - : std::make_unique(document->location()); + : std::make_unique(document->location()); if (data.isEmpty() && !location) { document->setGoodThumbnailChecked(false); return; diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h index de7db7a219..4be81913c5 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h @@ -25,6 +25,7 @@ class QImage; namespace FFmpeg { inline constexpr auto kPixelBytesSize = 4; +inline constexpr auto kAVBlockSize = 4096; // 4Kb for ffmpeg blocksize constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE }; constexpr auto kNormalAspect = AVRational{ 1, 1 }; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 4810cbc4aa..c36ac5937f 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -1373,9 +1373,10 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con bool loaded = _documentMedia->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); if (loaded && !_gif && !_gif.isBad()) { auto that = const_cast(this); - that->_gif = Media::Clip::MakeReader(_documentMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) { - that->clipCallback(notification); - }); + that->_gif = Media::Clip::MakeReader( + _documentMedia->owner()->location(), + _documentMedia->bytes(), + [=](Media::Clip::Notification notification) { that->clipCallback(notification); }); } bool animating = (_gif && _gif->started()); diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp index e429ee774d..02dcffd79f 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio.cpp @@ -430,7 +430,7 @@ void Mixer::Track::clear() { detach(); state = TrackState(); - file = FileLocation(); + file = Core::FileLocation(); data = QByteArray(); bufferedPosition = 0; bufferedLength = 0; @@ -1519,7 +1519,7 @@ void DetachFromDevice(not_null instance) { class FFMpegAttributesReader : public AbstractFFMpegLoader { public: - FFMpegAttributesReader(const FileLocation &file, const QByteArray &data) + FFMpegAttributesReader(const Core::FileLocation &file, const QByteArray &data) : AbstractFFMpegLoader(file, data, bytes::vector()) { } @@ -1632,7 +1632,7 @@ namespace Player { Ui::PreparedFileInformation::Song PrepareForSending(const QString &fname, const QByteArray &data) { auto result = Ui::PreparedFileInformation::Song(); - FFMpegAttributesReader reader(FileLocation(fname), data); + FFMpegAttributesReader reader(Core::FileLocation(fname), data); const auto positionMs = crl::time(0); if (reader.open(positionMs) && reader.samplesCount() > 0) { result.duration = reader.samplesCount() / reader.samplesFrequency(); @@ -1647,7 +1647,7 @@ Ui::PreparedFileInformation::Song PrepareForSending(const QString &fname, const class FFMpegWaveformCounter : public FFMpegLoader { public: - FFMpegWaveformCounter(const FileLocation &file, const QByteArray &data) : FFMpegLoader(file, data, bytes::vector()) { + FFMpegWaveformCounter(const Core::FileLocation &file, const QByteArray &data) : FFMpegLoader(file, data, bytes::vector()) { } bool open(crl::time positionMs) override { @@ -1732,7 +1732,7 @@ private: } // namespace Media VoiceWaveform audioCountWaveform( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data) { Media::FFMpegWaveformCounter counter(file, data); const auto positionMs = crl::time(0); diff --git a/Telegram/SourceFiles/media/audio/media_audio.h b/Telegram/SourceFiles/media/audio/media_audio.h index 962017073b..890570cdb6 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.h +++ b/Telegram/SourceFiles/media/audio/media_audio.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animation_value.h" #include "ui/chat/attach/attach_prepare.h" +#include "core/file_location.h" #include "base/bytes.h" #include @@ -221,7 +222,7 @@ private: TrackState state; - FileLocation file; + Core::FileLocation file; QByteArray data; int64 bufferedPosition = 0; int64 bufferedLength = 0; @@ -368,7 +369,7 @@ bool audioCheckError(); } // namespace Player } // namespace Media -VoiceWaveform audioCountWaveform(const FileLocation &file, const QByteArray &data); +VoiceWaveform audioCountWaveform(const Core::FileLocation &file, const QByteArray &data); namespace Media { namespace Audio { diff --git a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp index 224bd7ffb6..580a8f8bc9 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio_capture.h" #include "media/audio/media_audio_ffmpeg_loader.h" +#include "ffmpeg/ffmpeg_utility.h" #include "base/timer.h" #include @@ -247,9 +248,9 @@ void Instance::Inner::start(Fn updated, Fn error) { // Create encoding context - d->ioBuffer = (uchar*)av_malloc(AVBlockSize); + d->ioBuffer = (uchar*)av_malloc(FFmpeg::kAVBlockSize); - d->ioContext = avio_alloc_context(d->ioBuffer, AVBlockSize, 1, static_cast(d.get()), &Private::_read_data, &Private::_write_data, &Private::_seek_data); + d->ioContext = avio_alloc_context(d->ioBuffer, FFmpeg::kAVBlockSize, 1, static_cast(d.get()), &Private::_read_data, &Private::_write_data, &Private::_seek_data); int res = 0; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; AVOutputFormat *fmt = 0; diff --git a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp index 258e46a415..24c8bac2a6 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/audio/media_audio_ffmpeg_loader.h" +#include "core/file_location.h" +#include "ffmpeg/ffmpeg_utility.h" #include "base/bytes.h" namespace Media { @@ -34,13 +36,13 @@ bool AbstractFFMpegLoader::open(crl::time positionMs) { int res = 0; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - ioBuffer = (uchar *)av_malloc(AVBlockSize); + ioBuffer = (uchar *)av_malloc(FFmpeg::kAVBlockSize); if (!_data.isEmpty()) { - ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data); + ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data); } else if (!_bytes.empty()) { - ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_bytes, 0, &AbstractFFMpegLoader::_seek_bytes); + ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_bytes, 0, &AbstractFFMpegLoader::_seek_bytes); } else { - ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file); + ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file); } fmtContext = avformat_alloc_context(); if (!fmtContext) { @@ -187,7 +189,7 @@ int64_t AbstractFFMpegLoader::_seek_file(void *opaque, int64_t offset, int whenc } AbstractAudioFFMpegLoader::AbstractAudioFFMpegLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer) : AbstractFFMpegLoader(file, data, std::move(buffer)) @@ -389,7 +391,7 @@ bool AbstractAudioFFMpegLoader::ensureResampleSpaceAvailable(int samples) { return true; } const auto allocate = std::max(samples, int(av_rescale_rnd( - AVBlockSize / _outputSampleSize, + FFmpeg::kAVBlockSize / _outputSampleSize, _swrDstRate, _swrSrcRate, AV_ROUND_UP))); @@ -501,7 +503,7 @@ AbstractAudioFFMpegLoader::~AbstractAudioFFMpegLoader() { } FFMpegLoader::FFMpegLoader( - const FileLocation & file, + const Core::FileLocation & file, const QByteArray & data, bytes::vector && buffer) : AbstractAudioFFMpegLoader(file, data, std::move(buffer)) { diff --git a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h index c0c5f32ca4..83de13bad2 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h @@ -20,12 +20,16 @@ extern "C" { #include +namespace Core { +class FileLocation; +} // namespace Core + namespace Media { class AbstractFFMpegLoader : public AudioPlayerLoader { public: AbstractFFMpegLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer) : AudioPlayerLoader(file, data, std::move(buffer)) { @@ -74,7 +78,7 @@ private: class AbstractAudioFFMpegLoader : public AbstractFFMpegLoader { public: AbstractAudioFFMpegLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer); @@ -149,7 +153,7 @@ private: class FFMpegLoader : public AbstractAudioFFMpegLoader { public: FFMpegLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer); diff --git a/Telegram/SourceFiles/media/audio/media_audio_loader.cpp b/Telegram/SourceFiles/media/audio/media_audio_loader.cpp index a71f668357..7871f71954 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_loader.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_loader.cpp @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { AudioPlayerLoader::AudioPlayerLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer) : _file(file) @@ -26,7 +26,7 @@ AudioPlayerLoader::~AudioPlayerLoader() { } bool AudioPlayerLoader::check( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data) { return (this->_file == file) && (this->_data.size() == data.size()); } diff --git a/Telegram/SourceFiles/media/audio/media_audio_loader.h b/Telegram/SourceFiles/media/audio/media_audio_loader.h index cdf51ca8dc..399d85f71c 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_loader.h +++ b/Telegram/SourceFiles/media/audio/media_audio_loader.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/bytes.h" +#include "core/file_location.h" #include "media/streaming/media_streaming_utility.h" namespace Media { @@ -15,12 +16,12 @@ namespace Media { class AudioPlayerLoader { public: AudioPlayerLoader( - const FileLocation &file, + const Core::FileLocation &file, const QByteArray &data, bytes::vector &&buffer); virtual ~AudioPlayerLoader(); - virtual bool check(const FileLocation &file, const QByteArray &data); + virtual bool check(const Core::FileLocation &file, const QByteArray &data); virtual bool open(crl::time positionMs) = 0; virtual int64 samplesCount() = 0; @@ -56,7 +57,7 @@ public: bool holdsSavedDecodedSamples() const; protected: - FileLocation _file; + Core::FileLocation _file; bool _access = false; QByteArray _data; bytes::vector _bytes; diff --git a/Telegram/SourceFiles/media/audio/media_audio_track.cpp b/Telegram/SourceFiles/media/audio/media_audio_track.cpp index 6e5e56c457..034aff4213 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_track.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_track.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio_ffmpeg_loader.h" #include "media/audio/media_audio.h" #include "core/application.h" +#include "core/file_location.h" #include #include @@ -49,7 +50,7 @@ void Track::samplePeakEach(crl::time peakDuration) { } void Track::fillFromData(bytes::vector &&data) { - FFMpegLoader loader(FileLocation(), QByteArray(), std::move(data)); + FFMpegLoader loader(Core::FileLocation(), QByteArray(), std::move(data)); auto position = qint64(0); if (!loader.open(position)) { @@ -110,7 +111,7 @@ void Track::fillFromData(bytes::vector &&data) { _lengthMs = (loader.samplesCount() * crl::time(1000)) / _sampleRate; } -void Track::fillFromFile(const FileLocation &location) { +void Track::fillFromFile(const Core::FileLocation &location) { if (location.accessEnable()) { fillFromFile(location.name()); location.accessDisable(); diff --git a/Telegram/SourceFiles/media/audio/media_audio_track.h b/Telegram/SourceFiles/media/audio/media_audio_track.h index 81656d1903..b24b50b285 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_track.h +++ b/Telegram/SourceFiles/media/audio/media_audio_track.h @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "base/bytes.h" +namespace Core { +class FileLocation; +} // namespace Core + namespace Media { namespace Audio { @@ -22,7 +26,7 @@ public: void samplePeakEach(crl::time peakDuration); void fillFromData(bytes::vector &&data); - void fillFromFile(const FileLocation &location); + void fillFromFile(const Core::FileLocation &location); void fillFromFile(const QString &filePath); void playOnce() { diff --git a/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp index e50ae0ad29..179a53589d 100644 --- a/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_child_ffmpeg_loader.h" #include "core/crash_reports.h" +#include "core/file_location.h" namespace Media { namespace { @@ -30,7 +31,7 @@ bool IsPlanarFormat(int format) { ChildFFMpegLoader::ChildFFMpegLoader( std::unique_ptr &&data) : AbstractAudioFFMpegLoader( - FileLocation(), + Core::FileLocation(), QByteArray(), bytes::vector()) , _parentData(std::move(data)) { diff --git a/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.h b/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.h index 95fb9a3207..a68dd5529e 100644 --- a/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.h @@ -31,7 +31,7 @@ public: bool open(crl::time positionMs) override; - bool check(const FileLocation &file, const QByteArray &data) override { + bool check(const Core::FileLocation &file, const QByteArray &data) override { return true; } diff --git a/Telegram/SourceFiles/media/clip/media_clip_check_streaming.cpp b/Telegram/SourceFiles/media/clip/media_clip_check_streaming.cpp index 38e659de50..3e37dc7cfa 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_check_streaming.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_check_streaming.cpp @@ -7,8 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/clip/media_clip_check_streaming.h" -#include +#include "core/file_location.h" +#include "base/bytes.h" +#include "logs.h" + #include +#include namespace Media { namespace Clip { @@ -33,7 +37,7 @@ bool IsAtom(bytes::const_span header, const char (&atom)[5]) { } // namespace bool CheckStreamingSupport( - const FileLocation &location, + const Core::FileLocation &location, QByteArray data) { QBuffer buffer; QFile file; diff --git a/Telegram/SourceFiles/media/clip/media_clip_check_streaming.h b/Telegram/SourceFiles/media/clip/media_clip_check_streaming.h index c2071e89f4..093a456cb4 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_check_streaming.h +++ b/Telegram/SourceFiles/media/clip/media_clip_check_streaming.h @@ -7,11 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Core { +class FileLocation; +} // namespace Core + namespace Media { namespace Clip { bool CheckStreamingSupport( - const FileLocation &location, + const Core::FileLocation &location, QByteArray data); } // namespace Clip diff --git a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp index 2053da32dd..15afec3ec3 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp @@ -7,9 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/clip/media_clip_ffmpeg.h" -#include "media/audio/media_audio.h" -#include "media/audio/media_child_ffmpeg_loader.h" -#include "storage/file_download.h" +#include "core/file_location.h" +#include "logs.h" namespace Media { namespace Clip { @@ -49,12 +48,10 @@ bool isAlignedImage(const QImage &image) { } // namespace FFMpegReaderImplementation::FFMpegReaderImplementation( - FileLocation *location, - QByteArray *data, - const AudioMsgId &audio) + Core::FileLocation *location, + QByteArray *data) : ReaderImplementation(location, data) -, _frame(FFmpeg::MakeFramePointer()) -, _audioMsgId(audio) { +, _frame(FFmpeg::MakeFramePointer()) { } ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { @@ -73,9 +70,6 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { if (res == AVERROR_EOF) { _packetQueue.clear(); - if (_mode == Mode::Normal) { - return ReadResult::EndOfFile; - } if (!_hadFrame) { LOG(("Gif Error: Got EOF before a single frame was read!")); return ReadResult::Error; @@ -171,44 +165,18 @@ void FFMpegReaderImplementation::processReadFrame() { } ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) { - if (_audioStreamId < 0) { // just keep up - if (_frameRead && _frameTime > frameMs) { - return ReadResult::Success; - } - auto readResult = readNextFrame(); - if (readResult != ReadResult::Success || _frameTime > frameMs) { - return readResult; - } - readResult = readNextFrame(); - if (_frameTime <= frameMs) { - _frameTime = frameMs + 5; // keep up - } + if (_frameRead && _frameTime > frameMs) { + return ReadResult::Success; + } + auto readResult = readNextFrame(); + if (readResult != ReadResult::Success || _frameTime > frameMs) { return readResult; } - - // sync by audio stream - auto correctMs = (frameMs >= 0) - ? Player::mixer()->getExternalCorrectedTime( - _audioMsgId, - frameMs, - systemMs) - : frameMs; - if (!_frameRead) { - auto readResult = readNextFrame(); - if (readResult != ReadResult::Success) { - return readResult; - } + readResult = readNextFrame(); + if (_frameTime <= frameMs) { + _frameTime = frameMs + 5; // keep up } - while (_frameTime <= correctMs) { - auto readResult = readNextFrame(); - if (readResult != ReadResult::Success) { - return readResult; - } - } - if (frameMs >= 0) { - _frameTimeCorrection = frameMs - correctMs; - } - return ReadResult::Success; + return readResult; } crl::time FFMpegReaderImplementation::frameRealTime() const { @@ -273,17 +241,6 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q to = to.transformed(rotationTransform); } - // Read some future packets for audio stream. - if (_audioStreamId >= 0) { - while (_frameMs + 5000 > _lastReadAudioMs - && _frameMs + 15000 > _lastReadVideoMs) { - auto packetResult = readAndProcessPacket(); - if (packetResult != PacketResult::Ok) { - break; - } - } - } - FFmpeg::ClearFrameMemory(_frame.get()); return true; @@ -306,8 +263,8 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) { LOG(("Gif Error: Unable to open device %1").arg(logData())); return false; } - _ioBuffer = (uchar*)av_malloc(AVBlockSize); - _ioContext = avio_alloc_context(_ioBuffer, AVBlockSize, 0, static_cast(this), &FFMpegReaderImplementation::_read, nullptr, &FFMpegReaderImplementation::_seek); + _ioBuffer = (uchar*)av_malloc(FFmpeg::kAVBlockSize); + _ioContext = avio_alloc_context(_ioBuffer, FFmpeg::kAVBlockSize, 0, static_cast(this), &FFMpegReaderImplementation::_read, nullptr, &FFMpegReaderImplementation::_seek); _fmtContext = avformat_alloc_context(); if (!_fmtContext) { LOG(("Gif Error: Unable to avformat_alloc_context %1").arg(logData())); @@ -360,12 +317,9 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) { const auto codec = avcodec_find_decoder(_codecContext->codec_id); - _audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (_mode == Mode::Inspecting) { - _hasAudioStream = (_audioStreamId >= 0); - _audioStreamId = -1; - } else if (_mode == Mode::Silent || !_audioMsgId.externalPlayId()) { - _audioStreamId = -1; + const auto audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); + _hasAudioStream = (audioStreamId >= 0); } if ((res = avcodec_open2(_codecContext, codec, nullptr)) < 0) { @@ -373,36 +327,6 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) { return false; } - std::unique_ptr soundData; - if (_audioStreamId >= 0) { - auto audioContext = avcodec_alloc_context3(nullptr); - if (!audioContext) { - LOG(("Audio Error: Unable to avcodec_alloc_context3 %1").arg(logData())); - return false; - } - if ((res = avcodec_parameters_to_context(audioContext, _fmtContext->streams[_audioStreamId]->codecpar)) < 0) { - LOG(("Audio Error: Unable to avcodec_parameters_to_context %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - return false; - } - av_codec_set_pkt_timebase(audioContext, _fmtContext->streams[_audioStreamId]->time_base); - av_opt_set_int(audioContext, "refcounted_frames", 1, 0); - - const auto audioCodec = avcodec_find_decoder(audioContext->codec_id); - if ((res = avcodec_open2(audioContext, audioCodec, 0)) < 0) { - avcodec_free_context(&audioContext); - LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - _audioStreamId = -1; - } else { - soundData = std::make_unique(); - soundData->codec = FFmpeg::CodecPointer(audioContext); - soundData->frequency = _fmtContext->streams[_audioStreamId]->codecpar->sample_rate; - if (_fmtContext->streams[_audioStreamId]->duration == AV_NOPTS_VALUE) { - soundData->length = (_fmtContext->duration * soundData->frequency) / AV_TIME_BASE; - } else { - soundData->length = (_fmtContext->streams[_audioStreamId]->duration * soundData->frequency * _fmtContext->streams[_audioStreamId]->time_base.num) / _fmtContext->streams[_audioStreamId]->time_base.den; - } - } - } if (positionMs > 0) { const auto timeBase = _fmtContext->streams[_streamId]->time_base; const auto timeStamp = (positionMs * timeBase.den) @@ -420,10 +344,6 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) { positionMs = countPacketMs(packet); } - if (hasAudio()) { - Player::mixer()->play(_audioMsgId, std::move(soundData), positionMs); - } - if (readResult == PacketResult::Ok) { processPacket(std::move(packet)); } @@ -462,7 +382,7 @@ bool FFMpegReaderImplementation::isGifv() const { if (_hasAudioStream) { return false; } - if (dataSize() > Storage::kMaxAnimationInMemory) { + if (dataSize() > kMaxInMemory) { return false; } if (_codecContext->codec_id != AV_CODEC_ID_H264) { @@ -472,7 +392,7 @@ bool FFMpegReaderImplementation::isGifv() const { } QString FFMpegReaderImplementation::logData() const { - return qsl("for file '%1', data size '%2'").arg(_location ? _location->name() : QString()).arg(_data->size()); + return u"for file '%1', data size '%2'"_q.arg(_location ? _location->name() : QString()).arg(_data->size()); } FFMpegReaderImplementation::~FFMpegReaderImplementation() { @@ -494,14 +414,6 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket( int res = 0; if ((res = av_read_frame(_fmtContext, &packet.fields())) < 0) { if (res == AVERROR_EOF) { - if (_audioStreamId >= 0) { - // queue terminating packet to audio player - auto empty = FFmpeg::Packet(); - Player::mixer()->feedFromExternal({ - _audioMsgId, - gsl::make_span(&empty, 1) - }); - } return PacketResult::EndOfFile; } char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; @@ -514,21 +426,9 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket( void FFMpegReaderImplementation::processPacket(FFmpeg::Packet &&packet) { const auto &native = packet.fields(); auto videoPacket = (native.stream_index == _streamId); - auto audioPacket = (_audioStreamId >= 0 && native.stream_index == _audioStreamId); - if (audioPacket || videoPacket) { - if (videoPacket) { - _lastReadVideoMs = countPacketMs(packet); - - _packetQueue.push_back(std::move(packet)); - } else if (audioPacket) { - _lastReadAudioMs = countPacketMs(packet); - - // queue packet to audio player - Player::mixer()->feedFromExternal({ - _audioMsgId, - gsl::make_span(&packet, 1) - }); - } + if (videoPacket) { + _lastReadVideoMs = countPacketMs(packet); + _packetQueue.push_back(std::move(packet)); } } diff --git a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.h b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.h index 6e1c7b11b3..03d96ff0e2 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.h +++ b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.h @@ -7,23 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -extern "C" { - -#include - -} // extern "C" - #include "media/clip/media_clip_implementation.h" -#include "media/audio/media_child_ffmpeg_loader.h" -#include "media/streaming/media_streaming_utility.h" +#include "ffmpeg/ffmpeg_utility.h" + +extern "C" { +#include +#include +} // extern "C" +#include + +//#include "media/streaming/media_streaming_utility.h" namespace Media { namespace Clip { namespace internal { +constexpr auto kMaxInMemory = 10 * 1024 * 1024; + class FFMpegReaderImplementation : public ReaderImplementation { public: - FFMpegReaderImplementation(FileLocation *location, QByteArray *data, const AudioMsgId &audio); + FFMpegReaderImplementation(Core::FileLocation *location, QByteArray *data); ReadResult readFramesTill(crl::time frameMs, crl::time systemMs) override; @@ -33,9 +36,6 @@ public: bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) override; crl::time durationMs() const override; - bool hasAudio() const override { - return (_audioStreamId >= 0); - } bool start(Mode mode, crl::time &positionMs) override; bool inspectAt(crl::time &positionMs); @@ -74,7 +74,7 @@ private: static int _read(void *opaque, uint8_t *buf, int buf_size); static int64_t _seek(void *opaque, int64_t offset, int whence); - Mode _mode = Mode::Normal; + Mode _mode = Mode::Silent; Rotation _rotation = Rotation::None; @@ -90,8 +90,6 @@ private: int _skippedInvalidDataPackets = 0; bool _hasAudioStream = false; - int _audioStreamId = -1; - AudioMsgId _audioMsgId; crl::time _lastReadVideoMs = 0; crl::time _lastReadAudioMs = 0; diff --git a/Telegram/SourceFiles/media/clip/media_clip_implementation.cpp b/Telegram/SourceFiles/media/clip/media_clip_implementation.cpp index d8999bfed8..e9b1f25b29 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_implementation.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_implementation.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/clip/media_clip_implementation.h" +#include "core/file_location.h" + namespace Media { namespace Clip { namespace internal { diff --git a/Telegram/SourceFiles/media/clip/media_clip_implementation.h b/Telegram/SourceFiles/media/clip/media_clip_implementation.h index 72d746cf75..7e709fe60c 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_implementation.h +++ b/Telegram/SourceFiles/media/clip/media_clip_implementation.h @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +namespace Core { class FileLocation; +} // namespace Core namespace Media { namespace Clip { @@ -17,13 +19,12 @@ namespace internal { class ReaderImplementation { public: - ReaderImplementation(FileLocation *location, QByteArray *data) - : _location(location) - , _data(data) { + ReaderImplementation(Core::FileLocation *location, QByteArray *data) + : _location(location) + , _data(data) { } enum class Mode { Silent, - Normal, Inspecting, // Not playing video, but reading data. }; @@ -43,7 +44,6 @@ public: virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; virtual crl::time durationMs() const = 0; - virtual bool hasAudio() const = 0; virtual bool start(Mode mode, crl::time &positionMs) = 0; @@ -54,8 +54,8 @@ public: } protected: - FileLocation *_location; - QByteArray *_data; + Core::FileLocation *_location = nullptr; + QByteArray *_data = nullptr; QFile _file; QBuffer _buffer; QIODevice *_device = nullptr; diff --git a/Telegram/SourceFiles/media/clip/media_clip_reader.cpp b/Telegram/SourceFiles/media/clip/media_clip_reader.cpp index 7cb0715293..8c8194e7ed 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_reader.cpp @@ -7,29 +7,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/clip/media_clip_reader.h" -#include "data/data_document.h" -#include "data/data_document_media.h" -#include "storage/file_download.h" #include "media/clip/media_clip_ffmpeg.h" #include "media/clip/media_clip_check_streaming.h" -#include "mainwidget.h" -#include "mainwindow.h" +#include "core/file_location.h" +#include "base/openssl_help.h" +#include "base/invoke_queued.h" +#include "logs.h" #include #include #include +#include +#include extern "C" { #include #include #include #include -} +} // extern "C" namespace Media { namespace Clip { namespace { +constexpr auto kClipThreadsCount = 8; +constexpr auto kAverageGifSize = 320 * 240; +constexpr auto kWaitBeforeGifPause = crl::time(200); + QVector threads; QVector managers; @@ -85,44 +90,32 @@ QPixmap PrepareFrame(const FrameRequest &request, const QImage &original, bool h } // namespace -Reader::Reader(const QString &filepath, Callback &&callback, Mode mode, crl::time seekMs) -: _callback(std::move(callback)) -, _mode(mode) -, _seekPositionMs(seekMs) { - init(FileLocation(filepath), QByteArray()); -} - Reader::Reader( - not_null media, - FullMsgId msgId, - Callback &&callback, - Mode mode, - crl::time seekMs) -: _callback(std::move(callback)) -, _mode(mode) -, _audioMsgId( - media->owner(), - msgId, - (mode == Mode::Video) ? AudioMsgId::CreateExternalPlayId() : 0) -, _seekPositionMs(seekMs) { - init(media->owner()->location(), media->bytes()); + const Core::FileLocation &location, + const QByteArray &data, + Callback &&callback) +: _callback(std::move(callback)) { + init(location, data); } -Reader::Reader(const QByteArray &data, Callback &&callback, Mode mode, crl::time seekMs) -: _callback(std::move(callback)) -, _mode(mode) -, _seekPositionMs(seekMs) { - init(FileLocation(QString()), data); +Reader::Reader(const QString &filepath, Callback &&callback) +: _callback(std::move(callback)) { + init(Core::FileLocation(filepath), QByteArray()); } -void Reader::init(const FileLocation &location, const QByteArray &data) { - if (threads.size() < ClipThreadsCount) { +Reader::Reader(const QByteArray &data, Callback &&callback) +: _callback(std::move(callback)) { + init(Core::FileLocation(QString()), data); +} + +void Reader::init(const Core::FileLocation &location, const QByteArray &data) { + if (threads.size() < kClipThreadsCount) { _threadIndex = threads.size(); threads.push_back(new QThread()); managers.push_back(new Manager(threads.back())); threads.back()->start(); } else { - _threadIndex = int32(rand_value() % threads.size()); + _threadIndex = int32(openssl::RandomValue() % threads.size()); int32 loadLevel = 0x7FFFFFFF; for (int32 i = 0, l = threads.size(); i < l; ++i) { int32 level = managers.at(i)->loadLevel(); @@ -216,7 +209,7 @@ void Reader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, Image if (_state == State::Error) return; if (_step.loadAcquire() == WaitingForRequestStep) { - int factor = cIntRetinaFactor(); + int factor = style::DevicePixelRatio(); FrameRequest request; request.factor = factor; request.framew = framew * factor; @@ -252,7 +245,7 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, frame->displayed.storeRelease(-1); } - auto factor = cIntRetinaFactor(); + auto factor = style::DevicePixelRatio(); if (frame->pix.width() == outerw * factor && frame->pix.height() == outerh * factor && frame->request.radius == radius @@ -284,17 +277,6 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, return frame->pix; } -QPixmap Reader::current() { - Expects(_mode == Mode::Video); - - auto frame = frameToShow(); - Assert(frame != nullptr); - - frame->displayed.storeRelease(1); - moveToNextShow(); - return frame->pix; -} - bool Reader::ready() const { if (_width && _height) return true; @@ -307,15 +289,11 @@ bool Reader::ready() const { return false; } -bool Reader::hasAudio() const { - return ready() ? _hasAudio : false; -} - crl::time Reader::getPositionMs() const { if (auto frame = frameToShow()) { return frame->positionMs; } - return _seekPositionMs; + return 0; } crl::time Reader::getDurationMs() const { @@ -370,13 +348,11 @@ Reader::~Reader() { class ReaderPrivate { public: - ReaderPrivate(Reader *reader, const FileLocation &location, const QByteArray &data) : _interface(reader) - , _mode(reader->mode()) - , _audioMsgId(reader->audioMsgId()) - , _seekPositionMs(reader->seekPositionMs()) + ReaderPrivate(Reader *reader, const Core::FileLocation &location, const QByteArray &data) + : _interface(reader) , _data(data) { if (_data.isEmpty()) { - _location = std::make_unique(location); + _location = std::make_unique(location); if (!_location->accessEnable()) { error(); return; @@ -396,8 +372,8 @@ public: // get the frame size and return a black frame with that size. auto firstFramePositionMs = crl::time(0); - auto reader = std::make_unique(_location.get(), &_data, AudioMsgId()); - if (reader->start(internal::ReaderImplementation::Mode::Normal, firstFramePositionMs)) { + auto reader = std::make_unique(_location.get(), &_data); + if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) { auto firstFrameReadResult = reader->readFramesTill(-1, ms); if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) { if (reader->renderFrame(frame()->original, frame()->alpha, QSize())) { @@ -408,7 +384,6 @@ public: _width = frame()->original.width(); _height = frame()->original.height(); _durationMs = _implementation->durationMs(); - _hasAudio = _implementation->hasAudio(); return ProcessResult::Started; } } @@ -426,7 +401,6 @@ public: _width = frame()->original.width(); _height = frame()->original.height(); _durationMs = _implementation->durationMs(); - _hasAudio = _implementation->hasAudio(); return ProcessResult::Started; } return ProcessResult::Wait; @@ -444,9 +418,6 @@ public: } if (!_started) { _started = true; - if (!_videoPausedAtMs && _hasAudio) { - Player::mixer()->resume(_audioMsgId, true); - } } if (!_autoPausedGif && !_videoPausedAtMs && ms >= _nextFrameWhen) { @@ -459,7 +430,7 @@ public: auto frameMs = _seekPositionMs + ms - _animationStarted; auto readResult = _implementation->readFramesTill(frameMs, ms); if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile) { - stop(Player::State::StoppedAtEnd); + stop(); _state = State::Finished; return ProcessResult::Finished; } else if (readResult == internal::ReaderImplementation::ReadResult::Error) { @@ -494,7 +465,7 @@ public: } bool init() { - if (_data.isEmpty() && QFileInfo(_location->name()).size() <= Storage::kMaxAnimationInMemory) { + if (_data.isEmpty() && QFileInfo(_location->name()).size() <= internal::kMaxInMemory) { QFile f(_location->name()); if (f.open(QIODevice::ReadOnly)) { _data = f.readAll(); @@ -504,16 +475,9 @@ public: } } - _implementation = std::make_unique(_location.get(), &_data, _audioMsgId); + _implementation = std::make_unique(_location.get(), &_data); - auto implementationMode = [this]() { - using ImplementationMode = internal::ReaderImplementation::Mode; - if (_mode == Reader::Mode::Gif) { - return ImplementationMode::Silent; - } - return ImplementationMode::Normal; - }; - return _implementation->start(implementationMode(), _seekPositionMs); + return _implementation->start(internal::ReaderImplementation::Mode::Silent, _seekPositionMs); } void startedAt(crl::time ms) { @@ -524,9 +488,6 @@ public: if (_videoPausedAtMs) return; // Paused already. _videoPausedAtMs = ms; - if (_hasAudio) { - Player::mixer()->pause(_audioMsgId, true); - } } void resumeVideo(crl::time ms) { @@ -537,23 +498,16 @@ public: _nextFrameWhen += delta; _videoPausedAtMs = 0; - if (_hasAudio) { - Player::mixer()->resume(_audioMsgId, true); - } } ProcessResult error() { - stop(Player::State::StoppedAtError); + stop(); _state = State::Error; return ProcessResult::Error; } - void stop(Player::State audioState) { + void stop() { _implementation = nullptr; - if (_hasAudio) { - Player::mixer()->stop(_audioMsgId, audioState); - } - if (_location) { if (_accessed) { _location->accessDisable(); @@ -564,19 +518,17 @@ public: } ~ReaderPrivate() { - stop(Player::State::Stopped); + stop(); _data.clear(); } private: Reader *_interface; State _state = State::Reading; - Reader::Mode _mode; - AudioMsgId _audioMsgId; crl::time _seekPositionMs = 0; QByteArray _data; - std::unique_ptr _location; + std::unique_ptr _location; bool _accessed = false; QBuffer _buffer; @@ -601,7 +553,6 @@ private: int _width = 0; int _height = 0; - bool _hasAudio = false; crl::time _durationMs = 0; crl::time _animationStarted = 0; crl::time _nextFrameWhen = 0; @@ -617,24 +568,17 @@ private: Manager::Manager(QThread *thread) { moveToThread(thread); - connect(thread, SIGNAL(started()), this, SLOT(process())); - connect(thread, SIGNAL(finished()), this, SLOT(finish())); - connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection); + connect(thread, &QThread::started, this, [=] { process(); }); + connect(thread, &QThread::finished, this, [=] { finish(); }); _timer.setSingleShot(true); _timer.moveToThread(thread); - connect(&_timer, SIGNAL(timeout()), this, SLOT(process())); - - connect( - this, - &Manager::callback, - QCoreApplication::instance(), - &Reader::callback); + connect(&_timer, &QTimer::timeout, this, [=] { process(); }); } -void Manager::append(Reader *reader, const FileLocation &location, const QByteArray &data) { +void Manager::append(Reader *reader, const Core::FileLocation &location, const QByteArray &data) { reader->_private = new ReaderPrivate(reader, location, data); - _loadLevel.fetchAndAddRelaxed(AverageGifSize); + _loadLevel.fetchAndAddRelaxed(kAverageGifSize); update(reader); } @@ -650,7 +594,7 @@ void Manager::update(Reader *reader) { } else { i->storeRelease(1); } - emit processDelayed(); + InvokeQueued(this, [=] { process(); }); } void Manager::stop(Reader *reader) { @@ -658,7 +602,7 @@ void Manager::stop(Reader *reader) { QMutexLocker lock(&_readerPointersMutex); _readerPointers.remove(reader); - emit processDelayed(); + InvokeQueued(this, [=] { process(); }); } bool Manager::carries(Reader *reader) const { @@ -680,20 +624,26 @@ Manager::ReaderPointers::const_iterator Manager::constUnsafeFindReaderPointer(Re return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.cend(); } +void Manager::callback(Reader *reader, Notification notification) { + crl::on_main([=, threadIndex = reader->threadIndex()] { + Reader::callback(reader, threadIndex, notification); + }); +} + bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) { QMutexLocker lock(&_readerPointersMutex); auto it = unsafeFindReaderPointer(reader); if (result == ProcessResult::Error) { if (it != _readerPointers.cend()) { it.key()->error(); - emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); + callback(it.key(), NotificationReinit); _readerPointers.erase(it); } return false; } else if (result == ProcessResult::Finished) { if (it != _readerPointers.cend()) { it.key()->finished(); - emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); + callback(it.key(), NotificationReinit); } return false; } @@ -702,17 +652,16 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c } if (result == ProcessResult::Started) { - _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize); + _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - kAverageGifSize); it.key()->_durationMs = reader->_durationMs; - it.key()->_hasAudio = reader->_hasAudio; } // See if we need to pause GIF because it is not displayed right now. - if (!reader->_autoPausedGif && reader->_mode == Reader::Mode::Gif && result == ProcessResult::Repaint) { + if (!reader->_autoPausedGif && result == ProcessResult::Repaint) { int32 ishowing, iprevious; auto showing = it.key()->frameToShow(&ishowing), previous = it.key()->frameToWriteNext(false, &iprevious); Assert(previous != nullptr && showing != nullptr && ishowing >= 0 && iprevious >= 0); if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown - if (reader->_frames[ishowing].when + WaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) { + if (reader->_frames[ishowing].when + kWaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) { reader->_autoPausedGif = true; it.key()->_autoPausedGif.storeRelease(1); result = ProcessResult::Paused; @@ -730,21 +679,21 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c if (result == ProcessResult::Started) { reader->startedAt(ms); it.key()->moveToNextWrite(); - emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); + callback(it.key(), NotificationReinit); } } else if (result == ProcessResult::Paused) { it.key()->moveToNextWrite(); - emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); + callback(it.key(), NotificationReinit); } else if (result == ProcessResult::Repaint) { it.key()->moveToNextWrite(); - emit callback(it.key(), it.key()->threadIndex(), NotificationRepaint); + callback(it.key(), NotificationRepaint); } return true; } Manager::ResultHandleState Manager::handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) { if (!handleProcessResult(reader, result, ms)) { - _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : AverageGifSize)); + _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : kAverageGifSize)); delete reader; return ResultHandleRemove; } @@ -836,7 +785,7 @@ void Manager::process() { QMutexLocker lock(&_readerPointersMutex); auto it = constUnsafeFindReaderPointer(reader); if (it == _readerPointers.cend()) { - _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : AverageGifSize)); + _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : kAverageGifSize)); delete reader; i = _readers.erase(i); continue; @@ -885,11 +834,11 @@ Manager::~Manager() { Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const QByteArray &data) { auto result = Ui::PreparedFileInformation::Video(); - auto localLocation = FileLocation(fname); + auto localLocation = Core::FileLocation(fname); auto localData = QByteArray(data); auto seekPositionMs = crl::time(0); - auto reader = std::make_unique(&localLocation, &localData, AudioMsgId()); + auto reader = std::make_unique(&localLocation, &localData); if (reader->start(internal::ReaderImplementation::Mode::Inspecting, seekPositionMs)) { auto durationMs = reader->durationMs(); if (durationMs > 0) { @@ -939,7 +888,7 @@ void Finish() { } } -Reader *const ReaderPointer::BadPointer = SharedMemoryLocation(); +Reader *const ReaderPointer::BadPointer = reinterpret_cast(1); ReaderPointer::~ReaderPointer() { if (valid()) { diff --git a/Telegram/SourceFiles/media/clip/media_clip_reader.h b/Telegram/SourceFiles/media/clip/media_clip_reader.h index cb84dffd99..9a86e68eb0 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_reader.h +++ b/Telegram/SourceFiles/media/clip/media_clip_reader.h @@ -11,12 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image_prepare.h" #include +#include +namespace Core { class FileLocation; - -namespace Data { -class DocumentMedia; -} // namespace Data +} // namespace Core namespace Media { namespace Clip { @@ -60,23 +59,15 @@ public: Video, }; - Reader(not_null media, FullMsgId msgId, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0); - Reader(const QString &filepath, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0); - Reader(const QByteArray &data, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0); + Reader(const Core::FileLocation &location, const QByteArray &data, Callback &&callback); + Reader(const QString &filepath, Callback &&callback); + Reader(const QByteArray &data, Callback &&callback); // Reader can be already deleted. static void callback(Reader *reader, qint32 threadIndex, qint32 notification); - AudioMsgId audioMsgId() const { - return _audioMsgId; - } - crl::time seekPositionMs() const { - return _seekPositionMs; - } - void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners); QPixmap current(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners, crl::time ms); - QPixmap current(); QPixmap frameOriginal() const { if (auto frame = frameToShow()) { auto result = QPixmap::fromImage(frame->original); @@ -107,7 +98,6 @@ public: } bool ready() const; - bool hasAudio() const; crl::time getPositionMs() const; crl::time getDurationMs() const; void pauseResumeVideo(); @@ -116,24 +106,15 @@ public: void error(); void finished(); - Mode mode() const { - return _mode; - } - ~Reader(); private: - void init(const FileLocation &location, const QByteArray &data); + void init(const Core::FileLocation &location, const QByteArray &data); Callback _callback; - Mode _mode; - State _state = State::Reading; - AudioMsgId _audioMsgId; - bool _hasAudio = false; crl::time _durationMs = 0; - crl::time _seekPositionMs = 0; mutable int _width = 0; mutable int _height = 0; @@ -240,32 +221,23 @@ enum class ProcessResult { }; class Manager : public QObject { - Q_OBJECT - public: + explicit Manager(QThread *thread); + ~Manager(); - Manager(QThread *thread); - int32 loadLevel() const { + int loadLevel() const { return _loadLevel.load(); } - void append(Reader *reader, const FileLocation &location, const QByteArray &data); + void append(Reader *reader, const Core::FileLocation &location, const QByteArray &data); void start(Reader *reader); void update(Reader *reader); void stop(Reader *reader); bool carries(Reader *reader) const; - ~Manager(); - -signals: - void processDelayed(); - - void callback(Media::Clip::Reader *reader, qint32 threadIndex, qint32 notification); - -public slots: - void process(); - void finish(); private: - + void process(); + void finish(); + void callback(Reader *reader, Notification notification); void clear(); QAtomicInt _loadLevel; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index bc71760f88..ff39fe802a 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -73,33 +73,9 @@ void psNewVersion(); inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } -inline QByteArray psPathBookmark(const QString &path) { - return QByteArray(); -} inline void psDownloadPathEnableAccess() { } -class PsFileBookmark { -public: - PsFileBookmark(const QByteArray &bookmark) { - } - bool check() const { - return true; - } - bool enable() const { - return true; - } - void disable() const { - } - const QString &name(const QString &original) const { - return original; - } - QByteArray bookmark() const { - return QByteArray(); - } - -}; - bool linuxMoveFile(const char *from, const char *to); bool psLaunchMaps(const Data::LocationPoint &point); diff --git a/Telegram/SourceFiles/platform/mac/file_bookmark_mac.h b/Telegram/SourceFiles/platform/mac/file_bookmark_mac.h new file mode 100644 index 0000000000..a60971184c --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/file_bookmark_mac.h @@ -0,0 +1,33 @@ +/* +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 + +namespace Platform { + +class FileBookmark final { +public: + FileBookmark(const QByteArray &bookmark); + ~FileBookmark(); + + [[nodiscard]] bool check() const; + bool enable() const; + void disable(); + [[nodiscard]] const QString &name(const QString &original) const; + [[nodiscard]] QByteArray bookmark() const; + +private: +#ifdef OS_MAC_STORE + struct Data; + Data *data = nullptr; +#endif // OS_MAC_STORE + +}; + +[[nodiscard]] QByteArray PathBookmark(const QString &path); + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/file_bookmark_mac.mm b/Telegram/SourceFiles/platform/mac/file_bookmark_mac.mm new file mode 100644 index 0000000000..27fd68f938 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/file_bookmark_mac.mm @@ -0,0 +1,125 @@ +/* +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 "platform/mac/file_bookmark_mac.h" + +namespace Platform { +namespace { + +#ifdef OS_MAC_STORE +QMutex BookmarksMutex; +#endif // OS_MAC_STORE + +} // namespace + +#ifdef OS_MAC_STORE +struct FileBookmark::Data { + ~Data() { + if (url) [url release]; + } + NSURL *url = nil; + QString name; + QByteArray bookmark; + int counter = 0; +}; +#endif // OS_MAC_STORE + +FileBookmark::FileBookmark(const QByteArray &bookmark) { +#ifdef OS_MAC_STORE + if (bookmark.isEmpty()) return; + + BOOL isStale = NO; + NSError *error = nil; + NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!url) return; + + if ([url startAccessingSecurityScopedResource]) { + data = new Data(); + data->url = [url retain]; + data->name = NS2QString([url path]); + data->bookmark = bookmark; + [url stopAccessingSecurityScopedResource]; + } +#endif // OS_MAC_STORE +} + +bool FileBookmark::check() const { + if (enable()) { + disable(); + return true; + } + return false; +} + +bool FileBookmark::enable() const { +#ifndef OS_MAC_STORE + return true; +#else // OS_MAC_STORE + if (!data) return false; + + QMutexLocker lock(&_bookmarksMutex); + if (data->counter > 0 || [data->url startAccessingSecurityScopedResource] == YES) { + ++data->counter; + return true; + } + return false; +#endif // OS_MAC_STORE +} + +void FileBookmark::disable() const { +#ifdef OS_MAC_STORE + if (!data) return; + + QMutexLocker lock(&_bookmarksMutex); + if (data->counter > 0) { + --data->counter; + if (!data->counter) { + [data->url stopAccessingSecurityScopedResource]; + } + } +#endif // OS_MAC_STORE +} + +const QString &FileBookmark::name(const QString &original) const { +#ifndef OS_MAC_STORE + return original; +#else // OS_MAC_STORE + return (data && !data->name.isEmpty()) ? data->name : original; +#endif // OS_MAC_STORE +} + +QByteArray FileBookmark::bookmark() const { +#ifndef OS_MAC_STORE + return QByteArray(); +#else // OS_MAC_STORE + return data ? data->bookmark : QByteArray(); +#endif // OS_MAC_STORE +} + +FileBookmark::~FileBookmark() { +#ifdef OS_MAC_STORE + if (data && data->counter > 0) { + LOG(("Did not disable() bookmark, counter: %1").arg(data->counter)); + [data->url stopAccessingSecurityScopedResource]; + } +#endif // OS_MAC_STORE +} + +QByteArray PathBookmark(const QString &path) { +#ifndef OS_MAC_STORE + return QByteArray(); +#else // OS_MAC_STORE + NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.toUtf8().constData()]]; + if (!url) return QByteArray(); + + NSError *error = nil; + NSData *data = [url bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + return data ? QByteArray::fromNSData(data) : QByteArray(); +#endif // OS_MAC_STORE +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index af04f13206..25c3bc9339 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -98,31 +98,6 @@ void psDownloadPathEnableAccess(); QByteArray psDownloadPathBookmark(const QString &path); QByteArray psPathBookmark(const QString &path); -class PsFileBookmark { -public: - PsFileBookmark(const QByteArray &bookmark) : _inner(bookmark) { - } - bool check() const { - return _inner.valid(); - } - bool enable() const { - return _inner.enable(); - } - void disable() const { - return _inner.disable(); - } - const QString &name(const QString &original) const { - return _inner.name(original); - } - QByteArray bookmark() const { - return _inner.bookmark(); - } - -private: - objc_FileBookmark _inner; - -}; - QString strNotificationAboutThemeChange(); QString strNotificationAboutScreenLocked(); QString strNotificationAboutScreenUnlocked(); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index 63d320087b..d2becc60da 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -253,10 +253,6 @@ QByteArray psDownloadPathBookmark(const QString &path) { return objc_downloadPathBookmark(path); } -QByteArray psPathBookmark(const QString &path) { - return objc_pathBookmark(path); -} - bool psLaunchMaps(const Data::LocationPoint &point) { return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString())); } diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.h b/Telegram/SourceFiles/platform/mac/specific_mac_p.h index 0a01209d33..35eb05d539 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.h @@ -25,25 +25,4 @@ double objc_appkitVersion(); QString objc_documentsPath(); QString objc_appDataPath(); QByteArray objc_downloadPathBookmark(const QString &path); -QByteArray objc_pathBookmark(const QString &path); void objc_downloadPathEnableAccess(const QByteArray &bookmark); - -class objc_FileBookmark { -public: - objc_FileBookmark(const QByteArray &bookmark); - bool valid() const; - bool enable() const; - void disable() const; - - const QString &name(const QString &original) const; - QByteArray bookmark() const; - - ~objc_FileBookmark(); - -private: -#ifdef OS_MAC_STORE - class objc_FileBookmarkData; - objc_FileBookmarkData *data = nullptr; -#endif // OS_MAC_STORE - -}; diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 59b9539ee4..6cba7ee3c9 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -373,19 +373,6 @@ QByteArray objc_downloadPathBookmark(const QString &path) { #endif // OS_MAC_STORE } -QByteArray objc_pathBookmark(const QString &path) { -#ifndef OS_MAC_STORE - return QByteArray(); -#else // OS_MAC_STORE - NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.toUtf8().constData()]]; - if (!url) return QByteArray(); - - NSError *error = nil; - NSData *data = [url bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; - return data ? QByteArray::fromNSData(data) : QByteArray(); -#endif // OS_MAC_STORE -} - void objc_downloadPathEnableAccess(const QByteArray &bookmark) { #ifdef OS_MAC_STORE if (bookmark.isEmpty()) return; @@ -412,101 +399,3 @@ void objc_downloadPathEnableAccess(const QByteArray &bookmark) { } #endif // OS_MAC_STORE } - -#ifdef OS_MAC_STORE -namespace { - QMutex _bookmarksMutex; -} - -class objc_FileBookmark::objc_FileBookmarkData { -public: - ~objc_FileBookmarkData() { - if (url) [url release]; - } - NSURL *url = nil; - QString name; - QByteArray bookmark; - int counter = 0; -}; -#endif // OS_MAC_STORE - -objc_FileBookmark::objc_FileBookmark(const QByteArray &bookmark) { -#ifdef OS_MAC_STORE - if (bookmark.isEmpty()) return; - - BOOL isStale = NO; - NSError *error = nil; - NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; - if (!url) return; - - if ([url startAccessingSecurityScopedResource]) { - data = new objc_FileBookmarkData(); - data->url = [url retain]; - data->name = NS2QString([url path]); - data->bookmark = bookmark; - [url stopAccessingSecurityScopedResource]; - } -#endif // OS_MAC_STORE -} - -bool objc_FileBookmark::valid() const { - if (enable()) { - disable(); - return true; - } - return false; -} - -bool objc_FileBookmark::enable() const { -#ifndef OS_MAC_STORE - return true; -#else // OS_MAC_STORE - if (!data) return false; - - QMutexLocker lock(&_bookmarksMutex); - if (data->counter > 0 || [data->url startAccessingSecurityScopedResource] == YES) { - ++data->counter; - return true; - } - return false; -#endif // OS_MAC_STORE -} - -void objc_FileBookmark::disable() const { -#ifdef OS_MAC_STORE - if (!data) return; - - QMutexLocker lock(&_bookmarksMutex); - if (data->counter > 0) { - --data->counter; - if (!data->counter) { - [data->url stopAccessingSecurityScopedResource]; - } - } -#endif // OS_MAC_STORE -} - -const QString &objc_FileBookmark::name(const QString &original) const { -#ifndef OS_MAC_STORE - return original; -#else // OS_MAC_STORE - return (data && !data->name.isEmpty()) ? data->name : original; -#endif // OS_MAC_STORE -} - -QByteArray objc_FileBookmark::bookmark() const { -#ifndef OS_MAC_STORE - return QByteArray(); -#else // OS_MAC_STORE - return data ? data->bookmark : QByteArray(); -#endif // OS_MAC_STORE -} - -objc_FileBookmark::~objc_FileBookmark() { -#ifdef OS_MAC_STORE - if (data && data->counter > 0) { - LOG(("Did not disable() bookmark, counter: %1").arg(data->counter)); - [data->url stopAccessingSecurityScopedResource]; - } -#endif // OS_MAC_STORE -} diff --git a/Telegram/SourceFiles/platform/platform_file_bookmark.h b/Telegram/SourceFiles/platform/platform_file_bookmark.h new file mode 100644 index 0000000000..dffc792b80 --- /dev/null +++ b/Telegram/SourceFiles/platform/platform_file_bookmark.h @@ -0,0 +1,43 @@ +/* +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 + +#ifdef Q_OS_MAC +#include "platform/mac/file_bookmark_mac.h" +#else // Q_OS_MAC + +namespace Platform { + +class FileBookmark { +public: + FileBookmark(const QByteArray &bookmark) { + } + bool check() const { + return true; + } + bool enable() const { + return true; + } + void disable() const { + } + const QString &name(const QString &original) const { + return original; + } + QByteArray bookmark() const { + return QByteArray(); + } + +}; + +[[nodiscard]] inline QByteArray PathBookmark(const QString &path) { + return QByteArray(); +} + +} // namespace Platform + +#endif // Q_OS_MAC diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 6e4ae4b542..e86df71bb8 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -89,31 +89,7 @@ void psNewVersion(); inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } -inline QByteArray psPathBookmark(const QString &path) { - return QByteArray(); -} inline void psDownloadPathEnableAccess() { } -class PsFileBookmark { -public: - PsFileBookmark(const QByteArray &bookmark) { - } - bool check() const { - return true; - } - bool enable() const { - return true; - } - void disable() const { - } - const QString &name(const QString &original) const { - return original; - } - QByteArray bookmark() const { - return QByteArray(); - } - -}; - bool psLaunchMaps(const Data::LocationPoint &point); diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index ba0fc2685d..629f2922bf 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "core/application.h" +#include "core/file_location.h" #include "storage/storage_account.h" #include "storage/file_download_mtproto.h" #include "storage/file_download_web.h" @@ -454,7 +455,7 @@ bool FileLoader::finalizeResult() { if (!_filename.isEmpty()) { _session->local().writeFileLocation( *key, - FileLocation(_filename)); + Core::FileLocation(_filename)); } } const auto key = cacheKey(); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 837f73a794..2fd2ce0b2d 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image_location_factory.h" #include "history/history_item.h" #include "history/history.h" +#include "core/file_location.h" #include "core/mime_type.h" #include "main/main_session.h" #include "apiwrap.h" @@ -320,7 +321,7 @@ void Uploader::uploadMedia( } } if (!media.file.isEmpty()) { - document->setLocation(FileLocation(media.file)); + document->setLocation(Core::FileLocation(media.file)); } } queue.emplace(msgId, File(media)); @@ -368,7 +369,7 @@ void Uploader::upload( document->setDataAndCache(file->content); } if (!file->filepath.isEmpty()) { - document->setLocation(FileLocation(file->filepath)); + document->setLocation(Core::FileLocation(file->filepath)); } if (file->type == SendMediaType::ThemeFile) { document->checkWallPaperProperties(); diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 4a869fc0c1..1d80e44c13 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -17,10 +17,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "ui/effects/animation_value.h" #include "core/update_checker.h" +#include "core/file_location.h" +#include "core/application.h" #include "media/audio/media_audio.h" #include "mtproto/mtproto_config.h" #include "mtproto/mtproto_dc_options.h" -#include "core/application.h" #include "main/main_domain.h" #include "main/main_account.h" #include "main/main_session.h" @@ -870,7 +871,7 @@ public: protected: DocumentData *_doc = nullptr; - FileLocation _loc; + Core::FileLocation _loc; QByteArray _data; VoiceWaveform _waveform; char _wavemax; diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 7a0ba37b12..0a541a0232 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_instance.h" #include "history/history.h" #include "core/application.h" +#include "core/file_location.h" #include "data/stickers/data_stickers.h" #include "data/data_session.h" #include "data/data_document.h" @@ -669,7 +670,7 @@ void Account::readLocations() { while (!locations.stream.atEnd()) { quint64 first, second; QByteArray bookmark; - FileLocation loc; + Core::FileLocation loc; quint32 legacyTypeField = 0; locations.stream >> first >> second >> legacyTypeField >> loc.fname; if (locations.version > 9013) { @@ -1164,7 +1165,7 @@ bool Account::hasDraft(const PeerId &peer) { return _draftsMap.contains(peer); } -void Account::writeFileLocation(MediaKey location, const FileLocation &local) { +void Account::writeFileLocation(MediaKey location, const Core::FileLocation &local) { if (local.fname.isEmpty()) { return; } @@ -1217,7 +1218,7 @@ void Account::removeFileLocation(MediaKey location) { writeLocationsQueued(); } -FileLocation Account::readFileLocation(MediaKey location) { +Core::FileLocation Account::readFileLocation(MediaKey location) { const auto aliasIt = _fileLocationAliases.constFind(location); if (aliasIt != _fileLocationAliases.cend()) { location = aliasIt.value(); @@ -1232,7 +1233,7 @@ FileLocation Account::readFileLocation(MediaKey location) { } return i.value(); } - return FileLocation(); + return Core::FileLocation(); } EncryptionKey Account::cacheKey() const { diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index c0a7a4a9b8..d40e4638be 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -12,7 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/stickers/data_stickers_set.h" class History; + +namespace Core { class FileLocation; +} // namespace Core namespace Export { struct Settings; @@ -86,8 +89,8 @@ public: [[nodiscard]] bool hasDraftCursors(const PeerId &peer); [[nodiscard]] bool hasDraft(const PeerId &peer); - void writeFileLocation(MediaKey location, const FileLocation &local); - [[nodiscard]] FileLocation readFileLocation(MediaKey location); + void writeFileLocation(MediaKey location, const Core::FileLocation &local); + [[nodiscard]] Core::FileLocation readFileLocation(MediaKey location); void removeFileLocation(MediaKey location); [[nodiscard]] EncryptionKey cacheKey() const; @@ -219,8 +222,8 @@ private: base::flat_map _draftCursorsMap; base::flat_map _draftsNotReadMap; - QMultiMap _fileLocations; - QMap> _fileLocationPairs; + QMultiMap _fileLocations; + QMap> _fileLocationPairs; QMap _fileLocationAliases; FileKey _locationsKey = 0; diff --git a/Telegram/SourceFiles/ui/image/image_location.cpp b/Telegram/SourceFiles/ui/image/image_location.cpp index dea8c72b5b..7b217e72ba 100644 --- a/Telegram/SourceFiles/ui/image/image_location.cpp +++ b/Telegram/SourceFiles/ui/image/image_location.cpp @@ -25,7 +25,6 @@ constexpr auto kPhotoBaseCacheTag = 0x0000000000020000ULL; constexpr auto kPhotoBaseCacheMask = 0x000000000000FF00ULL; constexpr auto kSerializeTypeShift = quint8(0x08); constexpr auto kNonStorageLocationToken = quint8(0x10); -const auto kInMediaCacheLocation = QString("*media_cache*"); enum class NonStorageLocationType : quint8 { Web, @@ -939,102 +938,3 @@ std::optional ImageLocation::FromSerialized( } return std::nullopt; } - -ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) -: _bookmark(bookmark) -, _failed(_bookmark ? !_bookmark->enable() : false) { -} - -ReadAccessEnabler::ReadAccessEnabler( - const std::shared_ptr &bookmark) -: _bookmark(bookmark.get()) -, _failed(_bookmark ? !_bookmark->enable() : false) { -} - -ReadAccessEnabler::~ReadAccessEnabler() { - if (_bookmark && !_failed) _bookmark->disable(); -} - -FileLocation::FileLocation(const QString &name) : fname(name) { - if (fname.isEmpty() || fname == kInMediaCacheLocation) { - size = 0; - } else { - setBookmark(psPathBookmark(name)); - - QFileInfo f(name); - if (f.exists()) { - qint64 s = f.size(); - if (s > INT_MAX) { - fname = QString(); - _bookmark = nullptr; - size = 0; - } else { - modified = f.lastModified(); - size = qint32(s); - } - } else { - fname = QString(); - _bookmark = nullptr; - size = 0; - } - } -} - -FileLocation FileLocation::InMediaCacheLocation() { - return FileLocation(kInMediaCacheLocation); -} - -bool FileLocation::check() const { - if (fname.isEmpty() || fname == kInMediaCacheLocation) { - return false; - } - - ReadAccessEnabler enabler(_bookmark); - if (enabler.failed()) { - const_cast(this)->_bookmark = nullptr; - } - - QFileInfo f(name()); - if (!f.isReadable()) return false; - - quint64 s = f.size(); - if (s > INT_MAX) { - DEBUG_LOG(("File location check: Wrong size %1").arg(s)); - return false; - } - - if (qint32(s) != size) { - DEBUG_LOG(("File location check: Wrong size %1 when should be %2").arg(s).arg(size)); - return false; - } - auto realModified = f.lastModified(); - if (realModified != modified) { - DEBUG_LOG(("File location check: Wrong last modified time %1 when should be %2").arg(realModified.toMSecsSinceEpoch()).arg(modified.toMSecsSinceEpoch())); - return false; - } - return true; -} - -const QString &FileLocation::name() const { - return _bookmark ? _bookmark->name(fname) : fname; -} - -QByteArray FileLocation::bookmark() const { - return _bookmark ? _bookmark->bookmark() : QByteArray(); -} - -bool FileLocation::inMediaCache() const { - return (fname == kInMediaCacheLocation); -} - -void FileLocation::setBookmark(const QByteArray &bm) { - _bookmark.reset(bm.isEmpty() ? nullptr : new PsFileBookmark(bm)); -} - -bool FileLocation::accessEnable() const { - return isEmpty() ? false : (_bookmark ? _bookmark->enable() : true); -} - -void FileLocation::accessDisable() const { - return _bookmark ? _bookmark->disable() : (void)0; -} diff --git a/Telegram/SourceFiles/ui/image/image_location.h b/Telegram/SourceFiles/ui/image/image_location.h index 2998122051..fb3fb72998 100644 --- a/Telegram/SourceFiles/ui/image/image_location.h +++ b/Telegram/SourceFiles/ui/image/image_location.h @@ -644,55 +644,3 @@ inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 } return QSize(qMax(w, 1), qMax(h, 1)); } - -class PsFileBookmark; -class ReadAccessEnabler { -public: - ReadAccessEnabler(const PsFileBookmark *bookmark); - ReadAccessEnabler(const std::shared_ptr &bookmark); - bool failed() const { - return _failed; - } - ~ReadAccessEnabler(); - -private: - const PsFileBookmark *_bookmark; - bool _failed; - -}; - -class FileLocation { -public: - FileLocation() = default; - explicit FileLocation(const QString &name); - - static FileLocation InMediaCacheLocation(); - - [[nodiscard]] bool check() const; - [[nodiscard]] const QString &name() const; - void setBookmark(const QByteArray &bookmark); - QByteArray bookmark() const; - [[nodiscard]] bool isEmpty() const { - return name().isEmpty(); - } - [[nodiscard]] bool inMediaCache() const; - - bool accessEnable() const; - void accessDisable() const; - - QString fname; - QDateTime modified; - qint32 size; - -private: - std::shared_ptr _bookmark; - -}; -inline bool operator==(const FileLocation &a, const FileLocation &b) { - return (a.name() == b.name()) - && (a.modified == b.modified) - && (a.size == b.size); -} -inline bool operator!=(const FileLocation &a, const FileLocation &b) { - return !(a == b); -} diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index ea24e9b5f2..bb2c97e1ff 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -375,8 +375,8 @@ void MediaPreviewWidget::validateGifAnimation() { }; if (contentLoaded) { _gif = Media::Clip::MakeReader( - _documentMedia.get(), - FullMsgId(), + _documentMedia->owner()->location(), + _documentMedia->bytes(), std::move(callback)); } else { _gifThumbnail = Media::Clip::MakeReader( diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 12d26d2da5..8a19ce621e 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -46,9 +46,24 @@ nice_target_sources(td_ui ${src_loc} PRIVATE ${style_files} + core/file_location.cpp + core/file_location.h core/mime_type.cpp core/mime_type.h + media/clip/media_clip_check_streaming.cpp + media/clip/media_clip_check_streaming.h + media/clip/media_clip_ffmpeg.cpp + media/clip/media_clip_ffmpeg.h + media/clip/media_clip_implementation.cpp + media/clip/media_clip_implementation.h + media/clip/media_clip_reader.cpp + media/clip/media_clip_reader.h + + platform/mac/file_bookmark_mac.h + platform/mac/file_bookmark_mac.mm + platform/platform_file_bookmark.h + ui/chat/attach/attach_album_thumbnail.cpp ui/chat/attach/attach_album_thumbnail.h ui/chat/attach/attach_album_preview.cpp @@ -91,4 +106,5 @@ target_link_libraries(td_ui PUBLIC tdesktop::td_lang desktop-app::lib_ui + desktop-app::lib_ffmpeg )