Move Media::Clip::Reader and FileLocation to td_ui.

This commit is contained in:
John Preston 2020-10-13 19:43:18 +03:00
parent 05eb549a3d
commit 8b96f4c214
47 changed files with 637 additions and 747 deletions

View file

@ -692,14 +692,6 @@ PRIVATE
media/audio/media_child_ffmpeg_loader.h media/audio/media_child_ffmpeg_loader.h
media/audio/media_openal_functions.cpp media/audio/media_openal_functions.cpp
media/audio/media_openal_functions.h 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.cpp
media/player/media_player_button.h media/player/media_player_button.h
media/player/media_player_float.cpp media/player/media_player_float.cpp

View file

@ -110,7 +110,7 @@ void DicePack::tryGenerateLocalZero() {
const auto document = _session->data().processDocument( const auto document = _session->data().processDocument(
result->document, result->document,
Images::FromImageInMemory(result->thumb, "PNG")); Images::FromImageInMemory(result->thumb, "PNG"));
document->setLocation(FileLocation(path)); document->setLocation(Core::FileLocation(path));
_map.emplace(0, document); _map.emplace(0, document);

View file

@ -21,13 +21,8 @@ enum {
LocalEncryptSaltSize = 32, // 256 bit LocalEncryptSaltSize = 32, // 256 bit
AnimationTimerDelta = 7, AnimationTimerDelta = 7,
ClipThreadsCount = 8,
AverageGifSize = 320 * 240,
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
RecentInlineBotsLimit = 10, RecentInlineBotsLimit = 10,
AVBlockSize = 4096, // 4Kb for ffmpeg blocksize
AutoSearchTimeout = 900, // 0.9 secs AutoSearchTimeout = 900, // 0.9 secs
SearchPerPage = 50, SearchPerPage = 50,
SearchManyPerPage = 100, SearchManyPerPage = 100,

View file

@ -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 <QtCore/QFileInfo>
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<Platform::FileBookmark> &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<FileLocation*>(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

View file

@ -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 <QtCore/QDateTime>
namespace Platform {
class FileBookmark;
} // namespace Platform
namespace Core {
class ReadAccessEnabler {
public:
ReadAccessEnabler(const Platform::FileBookmark *bookmark);
ReadAccessEnabler(
const std::shared_ptr<Platform::FileBookmark> &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<Platform::FileBookmark> _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

View file

@ -849,7 +849,7 @@ void DocumentData::finishLoad() {
_flags |= Flag::DownloadCancelled; _flags |= Flag::DownloadCancelled;
return; return;
} }
setLocation(FileLocation(_loader->fileName())); setLocation(Core::FileLocation(_loader->fileName()));
setGoodThumbnailDataReady(); setGoodThumbnailDataReady();
if (const auto media = activeMediaView()) { if (const auto media = activeMediaView()) {
media->setBytes(_loader->bytes()); media->setBytes(_loader->bytes());
@ -917,7 +917,7 @@ void DocumentData::setLoadedInMediaCache(bool loaded) {
if (loadedInMediaCache()) { if (loadedInMediaCache()) {
session().local().writeFileLocation( session().local().writeFileLocation(
mediaKey(), mediaKey(),
FileLocation::InMediaCacheLocation()); Core::FileLocation::InMediaCacheLocation());
} else { } else {
session().local().removeFileLocation(mediaKey()); session().local().removeFileLocation(mediaKey());
} }
@ -926,7 +926,7 @@ void DocumentData::setLoadedInMediaCache(bool loaded) {
} }
void DocumentData::setLoadedInMediaCacheLocation() { void DocumentData::setLoadedInMediaCacheLocation() {
_location = FileLocation(); _location = Core::FileLocation();
_flags |= Flag::LoadedInMediaCache; _flags |= Flag::LoadedInMediaCache;
} }
@ -954,10 +954,10 @@ void DocumentData::save(
f.write(media->bytes()); f.write(media->bytes());
f.close(); f.close();
setLocation(FileLocation(toFile)); setLocation(Core::FileLocation(toFile));
session().local().writeFileLocation( session().local().writeFileLocation(
mediaKey(), mediaKey(),
FileLocation(toFile)); Core::FileLocation(toFile));
} else if (l.accessEnable()) { } else if (l.accessEnable()) {
const auto &alreadyName = l.name(); const auto &alreadyName = l.name();
if (alreadyName != toFile) { if (alreadyName != toFile) {
@ -1151,7 +1151,7 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) {
return result; return result;
} }
const FileLocation &DocumentData::location(bool check) const { const Core::FileLocation &DocumentData::location(bool check) const {
if (check && !_location.check()) { if (check && !_location.check()) {
const auto location = session().local().readFileLocation(mediaKey()); const auto location = session().local().readFileLocation(mediaKey());
const auto that = const_cast<DocumentData*>(this); const auto that = const_cast<DocumentData*>(this);
@ -1164,7 +1164,7 @@ const FileLocation &DocumentData::location(bool check) const {
return _location; return _location;
} }
void DocumentData::setLocation(const FileLocation &loc) { void DocumentData::setLocation(const Core::FileLocation &loc) {
if (loc.inMediaCache()) { if (loc.inMediaCache()) {
setLoadedInMediaCacheLocation(); setLoadedInMediaCacheLocation();
} else if (loc.check()) { } else if (loc.check()) {
@ -1207,7 +1207,7 @@ bool DocumentData::saveFromDataChecked() {
return false; return false;
} }
file.close(); file.close();
_location = FileLocation(path); _location = Core::FileLocation(path);
session().local().writeFileLocation(mediaKey(), _location); session().local().writeFileLocation(mediaKey(), _location);
return true; return true;
} }
@ -1585,7 +1585,7 @@ void DocumentData::setRemoteLocation(
} else if (_location.isEmpty() && loadedInMediaCache()) { } else if (_location.isEmpty() && loadedInMediaCache()) {
session().local().writeFileLocation( session().local().writeFileLocation(
mediaKey(), mediaKey(),
FileLocation::InMediaCacheLocation()); Core::FileLocation::InMediaCacheLocation());
} }
} }
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/binary_guard.h" #include "base/binary_guard.h"
#include "data/data_types.h" #include "data/data_types.h"
#include "data/data_cloud_file.h" #include "data/data_cloud_file.h"
#include "core/file_location.h"
#include "ui/image/image.h" #include "ui/image/image.h"
class mtpFileLoader; class mtpFileLoader;
@ -114,8 +115,8 @@ public:
void setWaitingForAlbum(); void setWaitingForAlbum();
[[nodiscard]] bool waitingForAlbum() const; [[nodiscard]] bool waitingForAlbum() const;
[[nodiscard]] const FileLocation &location(bool check = false) const; [[nodiscard]] const Core::FileLocation &location(bool check = false) const;
void setLocation(const FileLocation &loc); void setLocation(const Core::FileLocation &loc);
bool saveFromData(); bool saveFromData();
bool saveFromDataSilent(); bool saveFromDataSilent();
@ -315,7 +316,7 @@ private:
std::weak_ptr<Data::DocumentMedia> _media; std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr; PhotoData *_goodThumbnailPhoto = nullptr;
FileLocation _location; Core::FileLocation _location;
std::unique_ptr<DocumentAdditionalData> _additional; std::unique_ptr<DocumentAdditionalData> _additional;
int32 _duration = -1; int32 _duration = -1;
mutable Flags _flags = kStreamingSupportedUnknown; mutable Flags _flags = kStreamingSupportedUnknown;

View file

@ -111,8 +111,8 @@ void VideoPreviewState::automaticLoad(Data::FileOrigin origin) const {
_media->videoThumbnailContent(), _media->videoThumbnailContent(),
std::move(callback)) std::move(callback))
: ::Media::Clip::MakeReader( : ::Media::Clip::MakeReader(
_media, _media->owner()->location(),
FullMsgId(), _media->bytes(),
std::move(callback)); std::move(callback));
} }
@ -386,7 +386,7 @@ void DocumentMedia::GenerateGoodThumbnail(
: FileType::Video; : FileType::Video;
auto location = document->location().isEmpty() auto location = document->location().isEmpty()
? nullptr ? nullptr
: std::make_unique<FileLocation>(document->location()); : std::make_unique<Core::FileLocation>(document->location());
if (data.isEmpty() && !location) { if (data.isEmpty() && !location) {
document->setGoodThumbnailChecked(false); document->setGoodThumbnailChecked(false);
return; return;

View file

@ -25,6 +25,7 @@ class QImage;
namespace FFmpeg { namespace FFmpeg {
inline constexpr auto kPixelBytesSize = 4; inline constexpr auto kPixelBytesSize = 4;
inline constexpr auto kAVBlockSize = 4096; // 4Kb for ffmpeg blocksize
constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE }; constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE };
constexpr auto kNormalAspect = AVRational{ 1, 1 }; constexpr auto kNormalAspect = AVRational{ 1, 1 };

View file

@ -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(); bool loaded = _documentMedia->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !_gif && !_gif.isBad()) { if (loaded && !_gif && !_gif.isBad()) {
auto that = const_cast<Game*>(this); auto that = const_cast<Game*>(this);
that->_gif = Media::Clip::MakeReader(_documentMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) { that->_gif = Media::Clip::MakeReader(
that->clipCallback(notification); _documentMedia->owner()->location(),
}); _documentMedia->bytes(),
[=](Media::Clip::Notification notification) { that->clipCallback(notification); });
} }
bool animating = (_gif && _gif->started()); bool animating = (_gif && _gif->started());

View file

@ -430,7 +430,7 @@ void Mixer::Track::clear() {
detach(); detach();
state = TrackState(); state = TrackState();
file = FileLocation(); file = Core::FileLocation();
data = QByteArray(); data = QByteArray();
bufferedPosition = 0; bufferedPosition = 0;
bufferedLength = 0; bufferedLength = 0;
@ -1519,7 +1519,7 @@ void DetachFromDevice(not_null<Audio::Instance*> instance) {
class FFMpegAttributesReader : public AbstractFFMpegLoader { class FFMpegAttributesReader : public AbstractFFMpegLoader {
public: public:
FFMpegAttributesReader(const FileLocation &file, const QByteArray &data) FFMpegAttributesReader(const Core::FileLocation &file, const QByteArray &data)
: AbstractFFMpegLoader(file, data, bytes::vector()) { : AbstractFFMpegLoader(file, data, bytes::vector()) {
} }
@ -1632,7 +1632,7 @@ namespace Player {
Ui::PreparedFileInformation::Song PrepareForSending(const QString &fname, const QByteArray &data) { Ui::PreparedFileInformation::Song PrepareForSending(const QString &fname, const QByteArray &data) {
auto result = Ui::PreparedFileInformation::Song(); auto result = Ui::PreparedFileInformation::Song();
FFMpegAttributesReader reader(FileLocation(fname), data); FFMpegAttributesReader reader(Core::FileLocation(fname), data);
const auto positionMs = crl::time(0); const auto positionMs = crl::time(0);
if (reader.open(positionMs) && reader.samplesCount() > 0) { if (reader.open(positionMs) && reader.samplesCount() > 0) {
result.duration = reader.samplesCount() / reader.samplesFrequency(); result.duration = reader.samplesCount() / reader.samplesFrequency();
@ -1647,7 +1647,7 @@ Ui::PreparedFileInformation::Song PrepareForSending(const QString &fname, const
class FFMpegWaveformCounter : public FFMpegLoader { class FFMpegWaveformCounter : public FFMpegLoader {
public: 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 { bool open(crl::time positionMs) override {
@ -1732,7 +1732,7 @@ private:
} // namespace Media } // namespace Media
VoiceWaveform audioCountWaveform( VoiceWaveform audioCountWaveform(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data) { const QByteArray &data) {
Media::FFMpegWaveformCounter counter(file, data); Media::FFMpegWaveformCounter counter(file, data);
const auto positionMs = crl::time(0); const auto positionMs = crl::time(0);

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animation_value.h" #include "ui/effects/animation_value.h"
#include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_prepare.h"
#include "core/file_location.h"
#include "base/bytes.h" #include "base/bytes.h"
#include <QtCore/QTimer> #include <QtCore/QTimer>
@ -221,7 +222,7 @@ private:
TrackState state; TrackState state;
FileLocation file; Core::FileLocation file;
QByteArray data; QByteArray data;
int64 bufferedPosition = 0; int64 bufferedPosition = 0;
int64 bufferedLength = 0; int64 bufferedLength = 0;
@ -368,7 +369,7 @@ bool audioCheckError();
} // namespace Player } // namespace Player
} // namespace Media } // namespace Media
VoiceWaveform audioCountWaveform(const FileLocation &file, const QByteArray &data); VoiceWaveform audioCountWaveform(const Core::FileLocation &file, const QByteArray &data);
namespace Media { namespace Media {
namespace Audio { namespace Audio {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_audio_capture.h" #include "media/audio/media_audio_capture.h"
#include "media/audio/media_audio_ffmpeg_loader.h" #include "media/audio/media_audio_ffmpeg_loader.h"
#include "ffmpeg/ffmpeg_utility.h"
#include "base/timer.h" #include "base/timer.h"
#include <al.h> #include <al.h>
@ -247,9 +248,9 @@ void Instance::Inner::start(Fn<void(Update)> updated, Fn<void()> error) {
// Create encoding context // 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<void*>(d.get()), &Private::_read_data, &Private::_write_data, &Private::_seek_data); d->ioContext = avio_alloc_context(d->ioBuffer, FFmpeg::kAVBlockSize, 1, static_cast<void*>(d.get()), &Private::_read_data, &Private::_write_data, &Private::_seek_data);
int res = 0; int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
AVOutputFormat *fmt = 0; AVOutputFormat *fmt = 0;

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/audio/media_audio_ffmpeg_loader.h" #include "media/audio/media_audio_ffmpeg_loader.h"
#include "core/file_location.h"
#include "ffmpeg/ffmpeg_utility.h"
#include "base/bytes.h" #include "base/bytes.h"
namespace Media { namespace Media {
@ -34,13 +36,13 @@ bool AbstractFFMpegLoader::open(crl::time positionMs) {
int res = 0; int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
ioBuffer = (uchar *)av_malloc(AVBlockSize); ioBuffer = (uchar *)av_malloc(FFmpeg::kAVBlockSize);
if (!_data.isEmpty()) { if (!_data.isEmpty()) {
ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data); ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data);
} else if (!_bytes.empty()) { } else if (!_bytes.empty()) {
ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_bytes, 0, &AbstractFFMpegLoader::_seek_bytes); ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_bytes, 0, &AbstractFFMpegLoader::_seek_bytes);
} else { } else {
ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file); ioContext = avio_alloc_context(ioBuffer, FFmpeg::kAVBlockSize, 0, reinterpret_cast<void *>(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file);
} }
fmtContext = avformat_alloc_context(); fmtContext = avformat_alloc_context();
if (!fmtContext) { if (!fmtContext) {
@ -187,7 +189,7 @@ int64_t AbstractFFMpegLoader::_seek_file(void *opaque, int64_t offset, int whenc
} }
AbstractAudioFFMpegLoader::AbstractAudioFFMpegLoader( AbstractAudioFFMpegLoader::AbstractAudioFFMpegLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer) bytes::vector &&buffer)
: AbstractFFMpegLoader(file, data, std::move(buffer)) : AbstractFFMpegLoader(file, data, std::move(buffer))
@ -389,7 +391,7 @@ bool AbstractAudioFFMpegLoader::ensureResampleSpaceAvailable(int samples) {
return true; return true;
} }
const auto allocate = std::max(samples, int(av_rescale_rnd( const auto allocate = std::max(samples, int(av_rescale_rnd(
AVBlockSize / _outputSampleSize, FFmpeg::kAVBlockSize / _outputSampleSize,
_swrDstRate, _swrDstRate,
_swrSrcRate, _swrSrcRate,
AV_ROUND_UP))); AV_ROUND_UP)));
@ -501,7 +503,7 @@ AbstractAudioFFMpegLoader::~AbstractAudioFFMpegLoader() {
} }
FFMpegLoader::FFMpegLoader( FFMpegLoader::FFMpegLoader(
const FileLocation & file, const Core::FileLocation & file,
const QByteArray & data, const QByteArray & data,
bytes::vector && buffer) bytes::vector && buffer)
: AbstractAudioFFMpegLoader(file, data, std::move(buffer)) { : AbstractAudioFFMpegLoader(file, data, std::move(buffer)) {

View file

@ -20,12 +20,16 @@ extern "C" {
#include <al.h> #include <al.h>
namespace Core {
class FileLocation;
} // namespace Core
namespace Media { namespace Media {
class AbstractFFMpegLoader : public AudioPlayerLoader { class AbstractFFMpegLoader : public AudioPlayerLoader {
public: public:
AbstractFFMpegLoader( AbstractFFMpegLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer) bytes::vector &&buffer)
: AudioPlayerLoader(file, data, std::move(buffer)) { : AudioPlayerLoader(file, data, std::move(buffer)) {
@ -74,7 +78,7 @@ private:
class AbstractAudioFFMpegLoader : public AbstractFFMpegLoader { class AbstractAudioFFMpegLoader : public AbstractFFMpegLoader {
public: public:
AbstractAudioFFMpegLoader( AbstractAudioFFMpegLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer); bytes::vector &&buffer);
@ -149,7 +153,7 @@ private:
class FFMpegLoader : public AbstractAudioFFMpegLoader { class FFMpegLoader : public AbstractAudioFFMpegLoader {
public: public:
FFMpegLoader( FFMpegLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer); bytes::vector &&buffer);

View file

@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Media { namespace Media {
AudioPlayerLoader::AudioPlayerLoader( AudioPlayerLoader::AudioPlayerLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer) bytes::vector &&buffer)
: _file(file) : _file(file)
@ -26,7 +26,7 @@ AudioPlayerLoader::~AudioPlayerLoader() {
} }
bool AudioPlayerLoader::check( bool AudioPlayerLoader::check(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data) { const QByteArray &data) {
return (this->_file == file) && (this->_data.size() == data.size()); return (this->_file == file) && (this->_data.size() == data.size());
} }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "base/bytes.h" #include "base/bytes.h"
#include "core/file_location.h"
#include "media/streaming/media_streaming_utility.h" #include "media/streaming/media_streaming_utility.h"
namespace Media { namespace Media {
@ -15,12 +16,12 @@ namespace Media {
class AudioPlayerLoader { class AudioPlayerLoader {
public: public:
AudioPlayerLoader( AudioPlayerLoader(
const FileLocation &file, const Core::FileLocation &file,
const QByteArray &data, const QByteArray &data,
bytes::vector &&buffer); bytes::vector &&buffer);
virtual ~AudioPlayerLoader(); 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 bool open(crl::time positionMs) = 0;
virtual int64 samplesCount() = 0; virtual int64 samplesCount() = 0;
@ -56,7 +57,7 @@ public:
bool holdsSavedDecodedSamples() const; bool holdsSavedDecodedSamples() const;
protected: protected:
FileLocation _file; Core::FileLocation _file;
bool _access = false; bool _access = false;
QByteArray _data; QByteArray _data;
bytes::vector _bytes; bytes::vector _bytes;

View file

@ -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_ffmpeg_loader.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "core/application.h" #include "core/application.h"
#include "core/file_location.h"
#include <al.h> #include <al.h>
#include <alc.h> #include <alc.h>
@ -49,7 +50,7 @@ void Track::samplePeakEach(crl::time peakDuration) {
} }
void Track::fillFromData(bytes::vector &&data) { 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); auto position = qint64(0);
if (!loader.open(position)) { if (!loader.open(position)) {
@ -110,7 +111,7 @@ void Track::fillFromData(bytes::vector &&data) {
_lengthMs = (loader.samplesCount() * crl::time(1000)) / _sampleRate; _lengthMs = (loader.samplesCount() * crl::time(1000)) / _sampleRate;
} }
void Track::fillFromFile(const FileLocation &location) { void Track::fillFromFile(const Core::FileLocation &location) {
if (location.accessEnable()) { if (location.accessEnable()) {
fillFromFile(location.name()); fillFromFile(location.name());
location.accessDisable(); location.accessDisable();

View file

@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h" #include "base/timer.h"
#include "base/bytes.h" #include "base/bytes.h"
namespace Core {
class FileLocation;
} // namespace Core
namespace Media { namespace Media {
namespace Audio { namespace Audio {
@ -22,7 +26,7 @@ public:
void samplePeakEach(crl::time peakDuration); void samplePeakEach(crl::time peakDuration);
void fillFromData(bytes::vector &&data); void fillFromData(bytes::vector &&data);
void fillFromFile(const FileLocation &location); void fillFromFile(const Core::FileLocation &location);
void fillFromFile(const QString &filePath); void fillFromFile(const QString &filePath);
void playOnce() { void playOnce() {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_child_ffmpeg_loader.h" #include "media/audio/media_child_ffmpeg_loader.h"
#include "core/crash_reports.h" #include "core/crash_reports.h"
#include "core/file_location.h"
namespace Media { namespace Media {
namespace { namespace {
@ -30,7 +31,7 @@ bool IsPlanarFormat(int format) {
ChildFFMpegLoader::ChildFFMpegLoader( ChildFFMpegLoader::ChildFFMpegLoader(
std::unique_ptr<ExternalSoundData> &&data) std::unique_ptr<ExternalSoundData> &&data)
: AbstractAudioFFMpegLoader( : AbstractAudioFFMpegLoader(
FileLocation(), Core::FileLocation(),
QByteArray(), QByteArray(),
bytes::vector()) bytes::vector())
, _parentData(std::move(data)) { , _parentData(std::move(data)) {

View file

@ -31,7 +31,7 @@ public:
bool open(crl::time positionMs) override; 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; return true;
} }

View file

@ -7,8 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/clip/media_clip_check_streaming.h" #include "media/clip/media_clip_check_streaming.h"
#include <QtCore/QBuffer> #include "core/file_location.h"
#include "base/bytes.h"
#include "logs.h"
#include <QtCore/QtEndian> #include <QtCore/QtEndian>
#include <QtCore/QBuffer>
namespace Media { namespace Media {
namespace Clip { namespace Clip {
@ -33,7 +37,7 @@ bool IsAtom(bytes::const_span header, const char (&atom)[5]) {
} // namespace } // namespace
bool CheckStreamingSupport( bool CheckStreamingSupport(
const FileLocation &location, const Core::FileLocation &location,
QByteArray data) { QByteArray data) {
QBuffer buffer; QBuffer buffer;
QFile file; QFile file;

View file

@ -7,11 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace Core {
class FileLocation;
} // namespace Core
namespace Media { namespace Media {
namespace Clip { namespace Clip {
bool CheckStreamingSupport( bool CheckStreamingSupport(
const FileLocation &location, const Core::FileLocation &location,
QByteArray data); QByteArray data);
} // namespace Clip } // namespace Clip

View file

@ -7,9 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/clip/media_clip_ffmpeg.h" #include "media/clip/media_clip_ffmpeg.h"
#include "media/audio/media_audio.h" #include "core/file_location.h"
#include "media/audio/media_child_ffmpeg_loader.h" #include "logs.h"
#include "storage/file_download.h"
namespace Media { namespace Media {
namespace Clip { namespace Clip {
@ -49,12 +48,10 @@ bool isAlignedImage(const QImage &image) {
} // namespace } // namespace
FFMpegReaderImplementation::FFMpegReaderImplementation( FFMpegReaderImplementation::FFMpegReaderImplementation(
FileLocation *location, Core::FileLocation *location,
QByteArray *data, QByteArray *data)
const AudioMsgId &audio)
: ReaderImplementation(location, data) : ReaderImplementation(location, data)
, _frame(FFmpeg::MakeFramePointer()) , _frame(FFmpeg::MakeFramePointer()) {
, _audioMsgId(audio) {
} }
ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() {
@ -73,9 +70,6 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() {
if (res == AVERROR_EOF) { if (res == AVERROR_EOF) {
_packetQueue.clear(); _packetQueue.clear();
if (_mode == Mode::Normal) {
return ReadResult::EndOfFile;
}
if (!_hadFrame) { if (!_hadFrame) {
LOG(("Gif Error: Got EOF before a single frame was read!")); LOG(("Gif Error: Got EOF before a single frame was read!"));
return ReadResult::Error; return ReadResult::Error;
@ -171,7 +165,6 @@ void FFMpegReaderImplementation::processReadFrame() {
} }
ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) { ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) {
if (_audioStreamId < 0) { // just keep up
if (_frameRead && _frameTime > frameMs) { if (_frameRead && _frameTime > frameMs) {
return ReadResult::Success; return ReadResult::Success;
} }
@ -184,31 +177,6 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl:
_frameTime = frameMs + 5; // keep up _frameTime = frameMs + 5; // keep up
} }
return readResult; 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;
}
}
while (_frameTime <= correctMs) {
auto readResult = readNextFrame();
if (readResult != ReadResult::Success) {
return readResult;
}
}
if (frameMs >= 0) {
_frameTimeCorrection = frameMs - correctMs;
}
return ReadResult::Success;
} }
crl::time FFMpegReaderImplementation::frameRealTime() const { crl::time FFMpegReaderImplementation::frameRealTime() const {
@ -273,17 +241,6 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
to = to.transformed(rotationTransform); 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()); FFmpeg::ClearFrameMemory(_frame.get());
return true; return true;
@ -306,8 +263,8 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) {
LOG(("Gif Error: Unable to open device %1").arg(logData())); LOG(("Gif Error: Unable to open device %1").arg(logData()));
return false; return false;
} }
_ioBuffer = (uchar*)av_malloc(AVBlockSize); _ioBuffer = (uchar*)av_malloc(FFmpeg::kAVBlockSize);
_ioContext = avio_alloc_context(_ioBuffer, AVBlockSize, 0, static_cast<void*>(this), &FFMpegReaderImplementation::_read, nullptr, &FFMpegReaderImplementation::_seek); _ioContext = avio_alloc_context(_ioBuffer, FFmpeg::kAVBlockSize, 0, static_cast<void*>(this), &FFMpegReaderImplementation::_read, nullptr, &FFMpegReaderImplementation::_seek);
_fmtContext = avformat_alloc_context(); _fmtContext = avformat_alloc_context();
if (!_fmtContext) { if (!_fmtContext) {
LOG(("Gif Error: Unable to avformat_alloc_context %1").arg(logData())); 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); 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) { if (_mode == Mode::Inspecting) {
_hasAudioStream = (_audioStreamId >= 0); const auto audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
_audioStreamId = -1; _hasAudioStream = (audioStreamId >= 0);
} else if (_mode == Mode::Silent || !_audioMsgId.externalPlayId()) {
_audioStreamId = -1;
} }
if ((res = avcodec_open2(_codecContext, codec, nullptr)) < 0) { if ((res = avcodec_open2(_codecContext, codec, nullptr)) < 0) {
@ -373,36 +327,6 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) {
return false; return false;
} }
std::unique_ptr<ExternalSoundData> 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<ExternalSoundData>();
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) { if (positionMs > 0) {
const auto timeBase = _fmtContext->streams[_streamId]->time_base; const auto timeBase = _fmtContext->streams[_streamId]->time_base;
const auto timeStamp = (positionMs * timeBase.den) const auto timeStamp = (positionMs * timeBase.den)
@ -420,10 +344,6 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) {
positionMs = countPacketMs(packet); positionMs = countPacketMs(packet);
} }
if (hasAudio()) {
Player::mixer()->play(_audioMsgId, std::move(soundData), positionMs);
}
if (readResult == PacketResult::Ok) { if (readResult == PacketResult::Ok) {
processPacket(std::move(packet)); processPacket(std::move(packet));
} }
@ -462,7 +382,7 @@ bool FFMpegReaderImplementation::isGifv() const {
if (_hasAudioStream) { if (_hasAudioStream) {
return false; return false;
} }
if (dataSize() > Storage::kMaxAnimationInMemory) { if (dataSize() > kMaxInMemory) {
return false; return false;
} }
if (_codecContext->codec_id != AV_CODEC_ID_H264) { if (_codecContext->codec_id != AV_CODEC_ID_H264) {
@ -472,7 +392,7 @@ bool FFMpegReaderImplementation::isGifv() const {
} }
QString FFMpegReaderImplementation::logData() 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() { FFMpegReaderImplementation::~FFMpegReaderImplementation() {
@ -494,14 +414,6 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
int res = 0; int res = 0;
if ((res = av_read_frame(_fmtContext, &packet.fields())) < 0) { if ((res = av_read_frame(_fmtContext, &packet.fields())) < 0) {
if (res == AVERROR_EOF) { 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; return PacketResult::EndOfFile;
} }
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
@ -514,21 +426,9 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
void FFMpegReaderImplementation::processPacket(FFmpeg::Packet &&packet) { void FFMpegReaderImplementation::processPacket(FFmpeg::Packet &&packet) {
const auto &native = packet.fields(); const auto &native = packet.fields();
auto videoPacket = (native.stream_index == _streamId); auto videoPacket = (native.stream_index == _streamId);
auto audioPacket = (_audioStreamId >= 0 && native.stream_index == _audioStreamId);
if (audioPacket || videoPacket) {
if (videoPacket) { if (videoPacket) {
_lastReadVideoMs = countPacketMs(packet); _lastReadVideoMs = countPacketMs(packet);
_packetQueue.push_back(std::move(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)
});
}
} }
} }

View file

@ -7,23 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
extern "C" {
#include <libswscale/swscale.h>
} // extern "C"
#include "media/clip/media_clip_implementation.h" #include "media/clip/media_clip_implementation.h"
#include "media/audio/media_child_ffmpeg_loader.h" #include "ffmpeg/ffmpeg_utility.h"
#include "media/streaming/media_streaming_utility.h"
extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
} // extern "C"
#include <deque>
//#include "media/streaming/media_streaming_utility.h"
namespace Media { namespace Media {
namespace Clip { namespace Clip {
namespace internal { namespace internal {
constexpr auto kMaxInMemory = 10 * 1024 * 1024;
class FFMpegReaderImplementation : public ReaderImplementation { class FFMpegReaderImplementation : public ReaderImplementation {
public: public:
FFMpegReaderImplementation(FileLocation *location, QByteArray *data, const AudioMsgId &audio); FFMpegReaderImplementation(Core::FileLocation *location, QByteArray *data);
ReadResult readFramesTill(crl::time frameMs, crl::time systemMs) override; ReadResult readFramesTill(crl::time frameMs, crl::time systemMs) override;
@ -33,9 +36,6 @@ public:
bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) override; bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) override;
crl::time durationMs() const override; crl::time durationMs() const override;
bool hasAudio() const override {
return (_audioStreamId >= 0);
}
bool start(Mode mode, crl::time &positionMs) override; bool start(Mode mode, crl::time &positionMs) override;
bool inspectAt(crl::time &positionMs); bool inspectAt(crl::time &positionMs);
@ -74,7 +74,7 @@ private:
static int _read(void *opaque, uint8_t *buf, int buf_size); static int _read(void *opaque, uint8_t *buf, int buf_size);
static int64_t _seek(void *opaque, int64_t offset, int whence); static int64_t _seek(void *opaque, int64_t offset, int whence);
Mode _mode = Mode::Normal; Mode _mode = Mode::Silent;
Rotation _rotation = Rotation::None; Rotation _rotation = Rotation::None;
@ -90,8 +90,6 @@ private:
int _skippedInvalidDataPackets = 0; int _skippedInvalidDataPackets = 0;
bool _hasAudioStream = false; bool _hasAudioStream = false;
int _audioStreamId = -1;
AudioMsgId _audioMsgId;
crl::time _lastReadVideoMs = 0; crl::time _lastReadVideoMs = 0;
crl::time _lastReadAudioMs = 0; crl::time _lastReadAudioMs = 0;

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/clip/media_clip_implementation.h" #include "media/clip/media_clip_implementation.h"
#include "core/file_location.h"
namespace Media { namespace Media {
namespace Clip { namespace Clip {
namespace internal { namespace internal {

View file

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
namespace Core {
class FileLocation; class FileLocation;
} // namespace Core
namespace Media { namespace Media {
namespace Clip { namespace Clip {
@ -17,13 +19,12 @@ namespace internal {
class ReaderImplementation { class ReaderImplementation {
public: public:
ReaderImplementation(FileLocation *location, QByteArray *data) ReaderImplementation(Core::FileLocation *location, QByteArray *data)
: _location(location) : _location(location)
, _data(data) { , _data(data) {
} }
enum class Mode { enum class Mode {
Silent, Silent,
Normal,
Inspecting, // Not playing video, but reading data. Inspecting, // Not playing video, but reading data.
}; };
@ -43,7 +44,6 @@ public:
virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0;
virtual crl::time durationMs() const = 0; virtual crl::time durationMs() const = 0;
virtual bool hasAudio() const = 0;
virtual bool start(Mode mode, crl::time &positionMs) = 0; virtual bool start(Mode mode, crl::time &positionMs) = 0;
@ -54,8 +54,8 @@ public:
} }
protected: protected:
FileLocation *_location; Core::FileLocation *_location = nullptr;
QByteArray *_data; QByteArray *_data = nullptr;
QFile _file; QFile _file;
QBuffer _buffer; QBuffer _buffer;
QIODevice *_device = nullptr; QIODevice *_device = nullptr;

View file

@ -7,29 +7,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/clip/media_clip_reader.h" #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_ffmpeg.h"
#include "media/clip/media_clip_check_streaming.h" #include "media/clip/media_clip_check_streaming.h"
#include "mainwidget.h" #include "core/file_location.h"
#include "mainwindow.h" #include "base/openssl_help.h"
#include "base/invoke_queued.h"
#include "logs.h"
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
#include <QtCore/QAbstractEventDispatcher> #include <QtCore/QAbstractEventDispatcher>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QFileInfo>
extern "C" { extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/opt.h> #include <libavutil/opt.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
} } // extern "C"
namespace Media { namespace Media {
namespace Clip { namespace Clip {
namespace { namespace {
constexpr auto kClipThreadsCount = 8;
constexpr auto kAverageGifSize = 320 * 240;
constexpr auto kWaitBeforeGifPause = crl::time(200);
QVector<QThread*> threads; QVector<QThread*> threads;
QVector<Manager*> managers; QVector<Manager*> managers;
@ -85,44 +90,32 @@ QPixmap PrepareFrame(const FrameRequest &request, const QImage &original, bool h
} // namespace } // 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( Reader::Reader(
not_null<Data::DocumentMedia*> media, const Core::FileLocation &location,
FullMsgId msgId, const QByteArray &data,
Callback &&callback, Callback &&callback)
Mode mode, : _callback(std::move(callback)) {
crl::time seekMs) init(location, data);
: _callback(std::move(callback))
, _mode(mode)
, _audioMsgId(
media->owner(),
msgId,
(mode == Mode::Video) ? AudioMsgId::CreateExternalPlayId() : 0)
, _seekPositionMs(seekMs) {
init(media->owner()->location(), media->bytes());
} }
Reader::Reader(const QByteArray &data, Callback &&callback, Mode mode, crl::time seekMs) Reader::Reader(const QString &filepath, Callback &&callback)
: _callback(std::move(callback)) : _callback(std::move(callback)) {
, _mode(mode) init(Core::FileLocation(filepath), QByteArray());
, _seekPositionMs(seekMs) {
init(FileLocation(QString()), data);
} }
void Reader::init(const FileLocation &location, const QByteArray &data) { Reader::Reader(const QByteArray &data, Callback &&callback)
if (threads.size() < ClipThreadsCount) { : _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(); _threadIndex = threads.size();
threads.push_back(new QThread()); threads.push_back(new QThread());
managers.push_back(new Manager(threads.back())); managers.push_back(new Manager(threads.back()));
threads.back()->start(); threads.back()->start();
} else { } else {
_threadIndex = int32(rand_value<uint32>() % threads.size()); _threadIndex = int32(openssl::RandomValue<uint32>() % threads.size());
int32 loadLevel = 0x7FFFFFFF; int32 loadLevel = 0x7FFFFFFF;
for (int32 i = 0, l = threads.size(); i < l; ++i) { for (int32 i = 0, l = threads.size(); i < l; ++i) {
int32 level = managers.at(i)->loadLevel(); 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 (_state == State::Error) return;
if (_step.loadAcquire() == WaitingForRequestStep) { if (_step.loadAcquire() == WaitingForRequestStep) {
int factor = cIntRetinaFactor(); int factor = style::DevicePixelRatio();
FrameRequest request; FrameRequest request;
request.factor = factor; request.factor = factor;
request.framew = framew * factor; request.framew = framew * factor;
@ -252,7 +245,7 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh,
frame->displayed.storeRelease(-1); frame->displayed.storeRelease(-1);
} }
auto factor = cIntRetinaFactor(); auto factor = style::DevicePixelRatio();
if (frame->pix.width() == outerw * factor if (frame->pix.width() == outerw * factor
&& frame->pix.height() == outerh * factor && frame->pix.height() == outerh * factor
&& frame->request.radius == radius && frame->request.radius == radius
@ -284,17 +277,6 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh,
return frame->pix; 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 { bool Reader::ready() const {
if (_width && _height) return true; if (_width && _height) return true;
@ -307,15 +289,11 @@ bool Reader::ready() const {
return false; return false;
} }
bool Reader::hasAudio() const {
return ready() ? _hasAudio : false;
}
crl::time Reader::getPositionMs() const { crl::time Reader::getPositionMs() const {
if (auto frame = frameToShow()) { if (auto frame = frameToShow()) {
return frame->positionMs; return frame->positionMs;
} }
return _seekPositionMs; return 0;
} }
crl::time Reader::getDurationMs() const { crl::time Reader::getDurationMs() const {
@ -370,13 +348,11 @@ Reader::~Reader() {
class ReaderPrivate { class ReaderPrivate {
public: public:
ReaderPrivate(Reader *reader, const FileLocation &location, const QByteArray &data) : _interface(reader) ReaderPrivate(Reader *reader, const Core::FileLocation &location, const QByteArray &data)
, _mode(reader->mode()) : _interface(reader)
, _audioMsgId(reader->audioMsgId())
, _seekPositionMs(reader->seekPositionMs())
, _data(data) { , _data(data) {
if (_data.isEmpty()) { if (_data.isEmpty()) {
_location = std::make_unique<FileLocation>(location); _location = std::make_unique<Core::FileLocation>(location);
if (!_location->accessEnable()) { if (!_location->accessEnable()) {
error(); error();
return; return;
@ -396,8 +372,8 @@ public:
// get the frame size and return a black frame with that size. // get the frame size and return a black frame with that size.
auto firstFramePositionMs = crl::time(0); auto firstFramePositionMs = crl::time(0);
auto reader = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data, AudioMsgId()); auto reader = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data);
if (reader->start(internal::ReaderImplementation::Mode::Normal, firstFramePositionMs)) { if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) {
auto firstFrameReadResult = reader->readFramesTill(-1, ms); auto firstFrameReadResult = reader->readFramesTill(-1, ms);
if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) { if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) {
if (reader->renderFrame(frame()->original, frame()->alpha, QSize())) { if (reader->renderFrame(frame()->original, frame()->alpha, QSize())) {
@ -408,7 +384,6 @@ public:
_width = frame()->original.width(); _width = frame()->original.width();
_height = frame()->original.height(); _height = frame()->original.height();
_durationMs = _implementation->durationMs(); _durationMs = _implementation->durationMs();
_hasAudio = _implementation->hasAudio();
return ProcessResult::Started; return ProcessResult::Started;
} }
} }
@ -426,7 +401,6 @@ public:
_width = frame()->original.width(); _width = frame()->original.width();
_height = frame()->original.height(); _height = frame()->original.height();
_durationMs = _implementation->durationMs(); _durationMs = _implementation->durationMs();
_hasAudio = _implementation->hasAudio();
return ProcessResult::Started; return ProcessResult::Started;
} }
return ProcessResult::Wait; return ProcessResult::Wait;
@ -444,9 +418,6 @@ public:
} }
if (!_started) { if (!_started) {
_started = true; _started = true;
if (!_videoPausedAtMs && _hasAudio) {
Player::mixer()->resume(_audioMsgId, true);
}
} }
if (!_autoPausedGif && !_videoPausedAtMs && ms >= _nextFrameWhen) { if (!_autoPausedGif && !_videoPausedAtMs && ms >= _nextFrameWhen) {
@ -459,7 +430,7 @@ public:
auto frameMs = _seekPositionMs + ms - _animationStarted; auto frameMs = _seekPositionMs + ms - _animationStarted;
auto readResult = _implementation->readFramesTill(frameMs, ms); auto readResult = _implementation->readFramesTill(frameMs, ms);
if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile) { if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile) {
stop(Player::State::StoppedAtEnd); stop();
_state = State::Finished; _state = State::Finished;
return ProcessResult::Finished; return ProcessResult::Finished;
} else if (readResult == internal::ReaderImplementation::ReadResult::Error) { } else if (readResult == internal::ReaderImplementation::ReadResult::Error) {
@ -494,7 +465,7 @@ public:
} }
bool init() { bool init() {
if (_data.isEmpty() && QFileInfo(_location->name()).size() <= Storage::kMaxAnimationInMemory) { if (_data.isEmpty() && QFileInfo(_location->name()).size() <= internal::kMaxInMemory) {
QFile f(_location->name()); QFile f(_location->name());
if (f.open(QIODevice::ReadOnly)) { if (f.open(QIODevice::ReadOnly)) {
_data = f.readAll(); _data = f.readAll();
@ -504,16 +475,9 @@ public:
} }
} }
_implementation = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data, _audioMsgId); _implementation = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data);
auto implementationMode = [this]() { return _implementation->start(internal::ReaderImplementation::Mode::Silent, _seekPositionMs);
using ImplementationMode = internal::ReaderImplementation::Mode;
if (_mode == Reader::Mode::Gif) {
return ImplementationMode::Silent;
}
return ImplementationMode::Normal;
};
return _implementation->start(implementationMode(), _seekPositionMs);
} }
void startedAt(crl::time ms) { void startedAt(crl::time ms) {
@ -524,9 +488,6 @@ public:
if (_videoPausedAtMs) return; // Paused already. if (_videoPausedAtMs) return; // Paused already.
_videoPausedAtMs = ms; _videoPausedAtMs = ms;
if (_hasAudio) {
Player::mixer()->pause(_audioMsgId, true);
}
} }
void resumeVideo(crl::time ms) { void resumeVideo(crl::time ms) {
@ -537,23 +498,16 @@ public:
_nextFrameWhen += delta; _nextFrameWhen += delta;
_videoPausedAtMs = 0; _videoPausedAtMs = 0;
if (_hasAudio) {
Player::mixer()->resume(_audioMsgId, true);
}
} }
ProcessResult error() { ProcessResult error() {
stop(Player::State::StoppedAtError); stop();
_state = State::Error; _state = State::Error;
return ProcessResult::Error; return ProcessResult::Error;
} }
void stop(Player::State audioState) { void stop() {
_implementation = nullptr; _implementation = nullptr;
if (_hasAudio) {
Player::mixer()->stop(_audioMsgId, audioState);
}
if (_location) { if (_location) {
if (_accessed) { if (_accessed) {
_location->accessDisable(); _location->accessDisable();
@ -564,19 +518,17 @@ public:
} }
~ReaderPrivate() { ~ReaderPrivate() {
stop(Player::State::Stopped); stop();
_data.clear(); _data.clear();
} }
private: private:
Reader *_interface; Reader *_interface;
State _state = State::Reading; State _state = State::Reading;
Reader::Mode _mode;
AudioMsgId _audioMsgId;
crl::time _seekPositionMs = 0; crl::time _seekPositionMs = 0;
QByteArray _data; QByteArray _data;
std::unique_ptr<FileLocation> _location; std::unique_ptr<Core::FileLocation> _location;
bool _accessed = false; bool _accessed = false;
QBuffer _buffer; QBuffer _buffer;
@ -601,7 +553,6 @@ private:
int _width = 0; int _width = 0;
int _height = 0; int _height = 0;
bool _hasAudio = false;
crl::time _durationMs = 0; crl::time _durationMs = 0;
crl::time _animationStarted = 0; crl::time _animationStarted = 0;
crl::time _nextFrameWhen = 0; crl::time _nextFrameWhen = 0;
@ -617,24 +568,17 @@ private:
Manager::Manager(QThread *thread) { Manager::Manager(QThread *thread) {
moveToThread(thread); moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(process())); connect(thread, &QThread::started, this, [=] { process(); });
connect(thread, SIGNAL(finished()), this, SLOT(finish())); connect(thread, &QThread::finished, this, [=] { finish(); });
connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection);
_timer.setSingleShot(true); _timer.setSingleShot(true);
_timer.moveToThread(thread); _timer.moveToThread(thread);
connect(&_timer, SIGNAL(timeout()), this, SLOT(process())); connect(&_timer, &QTimer::timeout, this, [=] { process(); });
connect(
this,
&Manager::callback,
QCoreApplication::instance(),
&Reader::callback);
} }
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); reader->_private = new ReaderPrivate(reader, location, data);
_loadLevel.fetchAndAddRelaxed(AverageGifSize); _loadLevel.fetchAndAddRelaxed(kAverageGifSize);
update(reader); update(reader);
} }
@ -650,7 +594,7 @@ void Manager::update(Reader *reader) {
} else { } else {
i->storeRelease(1); i->storeRelease(1);
} }
emit processDelayed(); InvokeQueued(this, [=] { process(); });
} }
void Manager::stop(Reader *reader) { void Manager::stop(Reader *reader) {
@ -658,7 +602,7 @@ void Manager::stop(Reader *reader) {
QMutexLocker lock(&_readerPointersMutex); QMutexLocker lock(&_readerPointersMutex);
_readerPointers.remove(reader); _readerPointers.remove(reader);
emit processDelayed(); InvokeQueued(this, [=] { process(); });
} }
bool Manager::carries(Reader *reader) const { 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(); 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) { bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) {
QMutexLocker lock(&_readerPointersMutex); QMutexLocker lock(&_readerPointersMutex);
auto it = unsafeFindReaderPointer(reader); auto it = unsafeFindReaderPointer(reader);
if (result == ProcessResult::Error) { if (result == ProcessResult::Error) {
if (it != _readerPointers.cend()) { if (it != _readerPointers.cend()) {
it.key()->error(); it.key()->error();
emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); callback(it.key(), NotificationReinit);
_readerPointers.erase(it); _readerPointers.erase(it);
} }
return false; return false;
} else if (result == ProcessResult::Finished) { } else if (result == ProcessResult::Finished) {
if (it != _readerPointers.cend()) { if (it != _readerPointers.cend()) {
it.key()->finished(); it.key()->finished();
emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); callback(it.key(), NotificationReinit);
} }
return false; return false;
} }
@ -702,17 +652,16 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
} }
if (result == ProcessResult::Started) { 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()->_durationMs = reader->_durationMs;
it.key()->_hasAudio = reader->_hasAudio;
} }
// See if we need to pause GIF because it is not displayed right now. // 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; int32 ishowing, iprevious;
auto showing = it.key()->frameToShow(&ishowing), previous = it.key()->frameToWriteNext(false, &iprevious); auto showing = it.key()->frameToShow(&ishowing), previous = it.key()->frameToWriteNext(false, &iprevious);
Assert(previous != nullptr && showing != nullptr && ishowing >= 0 && iprevious >= 0); 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 > 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; reader->_autoPausedGif = true;
it.key()->_autoPausedGif.storeRelease(1); it.key()->_autoPausedGif.storeRelease(1);
result = ProcessResult::Paused; result = ProcessResult::Paused;
@ -730,21 +679,21 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
if (result == ProcessResult::Started) { if (result == ProcessResult::Started) {
reader->startedAt(ms); reader->startedAt(ms);
it.key()->moveToNextWrite(); it.key()->moveToNextWrite();
emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); callback(it.key(), NotificationReinit);
} }
} else if (result == ProcessResult::Paused) { } else if (result == ProcessResult::Paused) {
it.key()->moveToNextWrite(); it.key()->moveToNextWrite();
emit callback(it.key(), it.key()->threadIndex(), NotificationReinit); callback(it.key(), NotificationReinit);
} else if (result == ProcessResult::Repaint) { } else if (result == ProcessResult::Repaint) {
it.key()->moveToNextWrite(); it.key()->moveToNextWrite();
emit callback(it.key(), it.key()->threadIndex(), NotificationRepaint); callback(it.key(), NotificationRepaint);
} }
return true; return true;
} }
Manager::ResultHandleState Manager::handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) { Manager::ResultHandleState Manager::handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) {
if (!handleProcessResult(reader, result, 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; delete reader;
return ResultHandleRemove; return ResultHandleRemove;
} }
@ -836,7 +785,7 @@ void Manager::process() {
QMutexLocker lock(&_readerPointersMutex); QMutexLocker lock(&_readerPointersMutex);
auto it = constUnsafeFindReaderPointer(reader); auto it = constUnsafeFindReaderPointer(reader);
if (it == _readerPointers.cend()) { 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; delete reader;
i = _readers.erase(i); i = _readers.erase(i);
continue; continue;
@ -885,11 +834,11 @@ Manager::~Manager() {
Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const QByteArray &data) { Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const QByteArray &data) {
auto result = Ui::PreparedFileInformation::Video(); auto result = Ui::PreparedFileInformation::Video();
auto localLocation = FileLocation(fname); auto localLocation = Core::FileLocation(fname);
auto localData = QByteArray(data); auto localData = QByteArray(data);
auto seekPositionMs = crl::time(0); auto seekPositionMs = crl::time(0);
auto reader = std::make_unique<internal::FFMpegReaderImplementation>(&localLocation, &localData, AudioMsgId()); auto reader = std::make_unique<internal::FFMpegReaderImplementation>(&localLocation, &localData);
if (reader->start(internal::ReaderImplementation::Mode::Inspecting, seekPositionMs)) { if (reader->start(internal::ReaderImplementation::Mode::Inspecting, seekPositionMs)) {
auto durationMs = reader->durationMs(); auto durationMs = reader->durationMs();
if (durationMs > 0) { if (durationMs > 0) {
@ -939,7 +888,7 @@ void Finish() {
} }
} }
Reader *const ReaderPointer::BadPointer = SharedMemoryLocation<Reader, 0>(); Reader *const ReaderPointer::BadPointer = reinterpret_cast<Reader*>(1);
ReaderPointer::~ReaderPointer() { ReaderPointer::~ReaderPointer() {
if (valid()) { if (valid()) {

View file

@ -11,12 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image_prepare.h" #include "ui/image/image_prepare.h"
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QMutex>
namespace Core {
class FileLocation; class FileLocation;
} // namespace Core
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Media { namespace Media {
namespace Clip { namespace Clip {
@ -60,23 +59,15 @@ public:
Video, Video,
}; };
Reader(not_null<Data::DocumentMedia*> media, FullMsgId msgId, 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, Mode mode = Mode::Gif, crl::time seekMs = 0); Reader(const QString &filepath, Callback &&callback);
Reader(const QByteArray &data, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0); Reader(const QByteArray &data, Callback &&callback);
// Reader can be already deleted. // Reader can be already deleted.
static void callback(Reader *reader, qint32 threadIndex, qint32 notification); 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); 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(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners, crl::time ms);
QPixmap current();
QPixmap frameOriginal() const { QPixmap frameOriginal() const {
if (auto frame = frameToShow()) { if (auto frame = frameToShow()) {
auto result = QPixmap::fromImage(frame->original); auto result = QPixmap::fromImage(frame->original);
@ -107,7 +98,6 @@ public:
} }
bool ready() const; bool ready() const;
bool hasAudio() const;
crl::time getPositionMs() const; crl::time getPositionMs() const;
crl::time getDurationMs() const; crl::time getDurationMs() const;
void pauseResumeVideo(); void pauseResumeVideo();
@ -116,24 +106,15 @@ public:
void error(); void error();
void finished(); void finished();
Mode mode() const {
return _mode;
}
~Reader(); ~Reader();
private: private:
void init(const FileLocation &location, const QByteArray &data); void init(const Core::FileLocation &location, const QByteArray &data);
Callback _callback; Callback _callback;
Mode _mode;
State _state = State::Reading; State _state = State::Reading;
AudioMsgId _audioMsgId;
bool _hasAudio = false;
crl::time _durationMs = 0; crl::time _durationMs = 0;
crl::time _seekPositionMs = 0;
mutable int _width = 0; mutable int _width = 0;
mutable int _height = 0; mutable int _height = 0;
@ -240,32 +221,23 @@ enum class ProcessResult {
}; };
class Manager : public QObject { class Manager : public QObject {
Q_OBJECT
public: public:
explicit Manager(QThread *thread);
~Manager();
Manager(QThread *thread); int loadLevel() const {
int32 loadLevel() const {
return _loadLevel.load(); 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 start(Reader *reader);
void update(Reader *reader); void update(Reader *reader);
void stop(Reader *reader); void stop(Reader *reader);
bool carries(Reader *reader) const; 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: private:
void process();
void finish();
void callback(Reader *reader, Notification notification);
void clear(); void clear();
QAtomicInt _loadLevel; QAtomicInt _loadLevel;

View file

@ -73,33 +73,9 @@ void psNewVersion();
inline QByteArray psDownloadPathBookmark(const QString &path) { inline QByteArray psDownloadPathBookmark(const QString &path) {
return QByteArray(); return QByteArray();
} }
inline QByteArray psPathBookmark(const QString &path) {
return QByteArray();
}
inline void psDownloadPathEnableAccess() { 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 linuxMoveFile(const char *from, const char *to);
bool psLaunchMaps(const Data::LocationPoint &point); bool psLaunchMaps(const Data::LocationPoint &point);

View file

@ -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

View file

@ -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

View file

@ -98,31 +98,6 @@ void psDownloadPathEnableAccess();
QByteArray psDownloadPathBookmark(const QString &path); QByteArray psDownloadPathBookmark(const QString &path);
QByteArray psPathBookmark(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 strNotificationAboutThemeChange();
QString strNotificationAboutScreenLocked(); QString strNotificationAboutScreenLocked();
QString strNotificationAboutScreenUnlocked(); QString strNotificationAboutScreenUnlocked();

View file

@ -253,10 +253,6 @@ QByteArray psDownloadPathBookmark(const QString &path) {
return objc_downloadPathBookmark(path); return objc_downloadPathBookmark(path);
} }
QByteArray psPathBookmark(const QString &path) {
return objc_pathBookmark(path);
}
bool psLaunchMaps(const Data::LocationPoint &point) { 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())); return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString()));
} }

View file

@ -25,25 +25,4 @@ double objc_appkitVersion();
QString objc_documentsPath(); QString objc_documentsPath();
QString objc_appDataPath(); QString objc_appDataPath();
QByteArray objc_downloadPathBookmark(const QString &path); QByteArray objc_downloadPathBookmark(const QString &path);
QByteArray objc_pathBookmark(const QString &path);
void objc_downloadPathEnableAccess(const QByteArray &bookmark); 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
};

View file

@ -373,19 +373,6 @@ QByteArray objc_downloadPathBookmark(const QString &path) {
#endif // OS_MAC_STORE #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) { void objc_downloadPathEnableAccess(const QByteArray &bookmark) {
#ifdef OS_MAC_STORE #ifdef OS_MAC_STORE
if (bookmark.isEmpty()) return; if (bookmark.isEmpty()) return;
@ -412,101 +399,3 @@ void objc_downloadPathEnableAccess(const QByteArray &bookmark) {
} }
#endif // OS_MAC_STORE #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
}

View file

@ -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

View file

@ -89,31 +89,7 @@ void psNewVersion();
inline QByteArray psDownloadPathBookmark(const QString &path) { inline QByteArray psDownloadPathBookmark(const QString &path) {
return QByteArray(); return QByteArray();
} }
inline QByteArray psPathBookmark(const QString &path) {
return QByteArray();
}
inline void psDownloadPathEnableAccess() { 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); bool psLaunchMaps(const Data::LocationPoint &point);

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "core/application.h" #include "core/application.h"
#include "core/file_location.h"
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "storage/file_download_mtproto.h" #include "storage/file_download_mtproto.h"
#include "storage/file_download_web.h" #include "storage/file_download_web.h"
@ -454,7 +455,7 @@ bool FileLoader::finalizeResult() {
if (!_filename.isEmpty()) { if (!_filename.isEmpty()) {
_session->local().writeFileLocation( _session->local().writeFileLocation(
*key, *key,
FileLocation(_filename)); Core::FileLocation(_filename));
} }
} }
const auto key = cacheKey(); const auto key = cacheKey();

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image_location_factory.h" #include "ui/image/image_location_factory.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history.h" #include "history/history.h"
#include "core/file_location.h"
#include "core/mime_type.h" #include "core/mime_type.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -320,7 +321,7 @@ void Uploader::uploadMedia(
} }
} }
if (!media.file.isEmpty()) { if (!media.file.isEmpty()) {
document->setLocation(FileLocation(media.file)); document->setLocation(Core::FileLocation(media.file));
} }
} }
queue.emplace(msgId, File(media)); queue.emplace(msgId, File(media));
@ -368,7 +369,7 @@ void Uploader::upload(
document->setDataAndCache(file->content); document->setDataAndCache(file->content);
} }
if (!file->filepath.isEmpty()) { if (!file->filepath.isEmpty()) {
document->setLocation(FileLocation(file->filepath)); document->setLocation(Core::FileLocation(file->filepath));
} }
if (file->type == SendMediaType::ThemeFile) { if (file->type == SendMediaType::ThemeFile) {
document->checkWallPaperProperties(); document->checkWallPaperProperties();

View file

@ -17,10 +17,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "ui/effects/animation_value.h" #include "ui/effects/animation_value.h"
#include "core/update_checker.h" #include "core/update_checker.h"
#include "core/file_location.h"
#include "core/application.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "mtproto/mtproto_config.h" #include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dc_options.h" #include "mtproto/mtproto_dc_options.h"
#include "core/application.h"
#include "main/main_domain.h" #include "main/main_domain.h"
#include "main/main_account.h" #include "main/main_account.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -870,7 +871,7 @@ public:
protected: protected:
DocumentData *_doc = nullptr; DocumentData *_doc = nullptr;
FileLocation _loc; Core::FileLocation _loc;
QByteArray _data; QByteArray _data;
VoiceWaveform _waveform; VoiceWaveform _waveform;
char _wavemax; char _wavemax;

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtp_instance.h" #include "mtproto/mtp_instance.h"
#include "history/history.h" #include "history/history.h"
#include "core/application.h" #include "core/application.h"
#include "core/file_location.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_document.h" #include "data/data_document.h"
@ -669,7 +670,7 @@ void Account::readLocations() {
while (!locations.stream.atEnd()) { while (!locations.stream.atEnd()) {
quint64 first, second; quint64 first, second;
QByteArray bookmark; QByteArray bookmark;
FileLocation loc; Core::FileLocation loc;
quint32 legacyTypeField = 0; quint32 legacyTypeField = 0;
locations.stream >> first >> second >> legacyTypeField >> loc.fname; locations.stream >> first >> second >> legacyTypeField >> loc.fname;
if (locations.version > 9013) { if (locations.version > 9013) {
@ -1164,7 +1165,7 @@ bool Account::hasDraft(const PeerId &peer) {
return _draftsMap.contains(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()) { if (local.fname.isEmpty()) {
return; return;
} }
@ -1217,7 +1218,7 @@ void Account::removeFileLocation(MediaKey location) {
writeLocationsQueued(); writeLocationsQueued();
} }
FileLocation Account::readFileLocation(MediaKey location) { Core::FileLocation Account::readFileLocation(MediaKey location) {
const auto aliasIt = _fileLocationAliases.constFind(location); const auto aliasIt = _fileLocationAliases.constFind(location);
if (aliasIt != _fileLocationAliases.cend()) { if (aliasIt != _fileLocationAliases.cend()) {
location = aliasIt.value(); location = aliasIt.value();
@ -1232,7 +1233,7 @@ FileLocation Account::readFileLocation(MediaKey location) {
} }
return i.value(); return i.value();
} }
return FileLocation(); return Core::FileLocation();
} }
EncryptionKey Account::cacheKey() const { EncryptionKey Account::cacheKey() const {

View file

@ -12,7 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/stickers/data_stickers_set.h" #include "data/stickers/data_stickers_set.h"
class History; class History;
namespace Core {
class FileLocation; class FileLocation;
} // namespace Core
namespace Export { namespace Export {
struct Settings; struct Settings;
@ -86,8 +89,8 @@ public:
[[nodiscard]] bool hasDraftCursors(const PeerId &peer); [[nodiscard]] bool hasDraftCursors(const PeerId &peer);
[[nodiscard]] bool hasDraft(const PeerId &peer); [[nodiscard]] bool hasDraft(const PeerId &peer);
void writeFileLocation(MediaKey location, const FileLocation &local); void writeFileLocation(MediaKey location, const Core::FileLocation &local);
[[nodiscard]] FileLocation readFileLocation(MediaKey location); [[nodiscard]] Core::FileLocation readFileLocation(MediaKey location);
void removeFileLocation(MediaKey location); void removeFileLocation(MediaKey location);
[[nodiscard]] EncryptionKey cacheKey() const; [[nodiscard]] EncryptionKey cacheKey() const;
@ -219,8 +222,8 @@ private:
base::flat_map<PeerId, FileKey> _draftCursorsMap; base::flat_map<PeerId, FileKey> _draftCursorsMap;
base::flat_map<PeerId, bool> _draftsNotReadMap; base::flat_map<PeerId, bool> _draftsNotReadMap;
QMultiMap<MediaKey, FileLocation> _fileLocations; QMultiMap<MediaKey, Core::FileLocation> _fileLocations;
QMap<QString, QPair<MediaKey, FileLocation>> _fileLocationPairs; QMap<QString, QPair<MediaKey, Core::FileLocation>> _fileLocationPairs;
QMap<MediaKey, MediaKey> _fileLocationAliases; QMap<MediaKey, MediaKey> _fileLocationAliases;
FileKey _locationsKey = 0; FileKey _locationsKey = 0;

View file

@ -25,7 +25,6 @@ constexpr auto kPhotoBaseCacheTag = 0x0000000000020000ULL;
constexpr auto kPhotoBaseCacheMask = 0x000000000000FF00ULL; constexpr auto kPhotoBaseCacheMask = 0x000000000000FF00ULL;
constexpr auto kSerializeTypeShift = quint8(0x08); constexpr auto kSerializeTypeShift = quint8(0x08);
constexpr auto kNonStorageLocationToken = quint8(0x10); constexpr auto kNonStorageLocationToken = quint8(0x10);
const auto kInMediaCacheLocation = QString("*media_cache*");
enum class NonStorageLocationType : quint8 { enum class NonStorageLocationType : quint8 {
Web, Web,
@ -939,102 +938,3 @@ std::optional<ImageLocation> ImageLocation::FromSerialized(
} }
return std::nullopt; return std::nullopt;
} }
ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark)
: _bookmark(bookmark)
, _failed(_bookmark ? !_bookmark->enable() : false) {
}
ReadAccessEnabler::ReadAccessEnabler(
const std::shared_ptr<PsFileBookmark> &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<FileLocation*>(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;
}

View file

@ -644,55 +644,3 @@ inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32
} }
return QSize(qMax(w, 1), qMax(h, 1)); return QSize(qMax(w, 1), qMax(h, 1));
} }
class PsFileBookmark;
class ReadAccessEnabler {
public:
ReadAccessEnabler(const PsFileBookmark *bookmark);
ReadAccessEnabler(const std::shared_ptr<PsFileBookmark> &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<PsFileBookmark> _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);
}

View file

@ -375,8 +375,8 @@ void MediaPreviewWidget::validateGifAnimation() {
}; };
if (contentLoaded) { if (contentLoaded) {
_gif = Media::Clip::MakeReader( _gif = Media::Clip::MakeReader(
_documentMedia.get(), _documentMedia->owner()->location(),
FullMsgId(), _documentMedia->bytes(),
std::move(callback)); std::move(callback));
} else { } else {
_gifThumbnail = Media::Clip::MakeReader( _gifThumbnail = Media::Clip::MakeReader(

View file

@ -46,9 +46,24 @@ nice_target_sources(td_ui ${src_loc}
PRIVATE PRIVATE
${style_files} ${style_files}
core/file_location.cpp
core/file_location.h
core/mime_type.cpp core/mime_type.cpp
core/mime_type.h 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.cpp
ui/chat/attach/attach_album_thumbnail.h ui/chat/attach/attach_album_thumbnail.h
ui/chat/attach/attach_album_preview.cpp ui/chat/attach/attach_album_preview.cpp
@ -91,4 +106,5 @@ target_link_libraries(td_ui
PUBLIC PUBLIC
tdesktop::td_lang tdesktop::td_lang
desktop-app::lib_ui desktop-app::lib_ui
desktop-app::lib_ffmpeg
) )