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_openal_functions.cpp
media/audio/media_openal_functions.h
media/clip/media_clip_check_streaming.cpp
media/clip/media_clip_check_streaming.h
media/clip/media_clip_ffmpeg.cpp
media/clip/media_clip_ffmpeg.h
media/clip/media_clip_implementation.cpp
media/clip/media_clip_implementation.h
media/clip/media_clip_reader.cpp
media/clip/media_clip_reader.h
media/player/media_player_button.cpp
media/player/media_player_button.h
media/player/media_player_float.cpp

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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_ffmpeg_loader.h"
#include "ffmpeg/ffmpeg_utility.h"
#include "base/timer.h"
#include <al.h>
@ -247,9 +248,9 @@ void Instance::Inner::start(Fn<void(Update)> updated, Fn<void()> error) {
// Create encoding context
d->ioBuffer = (uchar*)av_malloc(AVBlockSize);
d->ioBuffer = (uchar*)av_malloc(FFmpeg::kAVBlockSize);
d->ioContext = avio_alloc_context(d->ioBuffer, AVBlockSize, 1, static_cast<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;
char err[AV_ERROR_MAX_STRING_SIZE] = { 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 "core/file_location.h"
#include "ffmpeg/ffmpeg_utility.h"
#include "base/bytes.h"
namespace Media {
@ -34,13 +36,13 @@ bool AbstractFFMpegLoader::open(crl::time positionMs) {
int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
ioBuffer = (uchar *)av_malloc(AVBlockSize);
ioBuffer = (uchar *)av_malloc(FFmpeg::kAVBlockSize);
if (!_data.isEmpty()) {
ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<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()) {
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 {
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();
if (!fmtContext) {
@ -187,7 +189,7 @@ int64_t AbstractFFMpegLoader::_seek_file(void *opaque, int64_t offset, int whenc
}
AbstractAudioFFMpegLoader::AbstractAudioFFMpegLoader(
const FileLocation &file,
const Core::FileLocation &file,
const QByteArray &data,
bytes::vector &&buffer)
: AbstractFFMpegLoader(file, data, std::move(buffer))
@ -389,7 +391,7 @@ bool AbstractAudioFFMpegLoader::ensureResampleSpaceAvailable(int samples) {
return true;
}
const auto allocate = std::max(samples, int(av_rescale_rnd(
AVBlockSize / _outputSampleSize,
FFmpeg::kAVBlockSize / _outputSampleSize,
_swrDstRate,
_swrSrcRate,
AV_ROUND_UP)));
@ -501,7 +503,7 @@ AbstractAudioFFMpegLoader::~AbstractAudioFFMpegLoader() {
}
FFMpegLoader::FFMpegLoader(
const FileLocation & file,
const Core::FileLocation & file,
const QByteArray & data,
bytes::vector && buffer)
: AbstractAudioFFMpegLoader(file, data, std::move(buffer)) {

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ public:
bool open(crl::time positionMs) override;
bool check(const FileLocation &file, const QByteArray &data) override {
bool check(const Core::FileLocation &file, const QByteArray &data) override {
return true;
}

View File

@ -7,8 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#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/QBuffer>
namespace Media {
namespace Clip {
@ -33,7 +37,7 @@ bool IsAtom(bytes::const_span header, const char (&atom)[5]) {
} // namespace
bool CheckStreamingSupport(
const FileLocation &location,
const Core::FileLocation &location,
QByteArray data) {
QBuffer buffer;
QFile file;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -73,33 +73,9 @@ void psNewVersion();
inline QByteArray psDownloadPathBookmark(const QString &path) {
return QByteArray();
}
inline QByteArray psPathBookmark(const QString &path) {
return QByteArray();
}
inline void psDownloadPathEnableAccess() {
}
class PsFileBookmark {
public:
PsFileBookmark(const QByteArray &bookmark) {
}
bool check() const {
return true;
}
bool enable() const {
return true;
}
void disable() const {
}
const QString &name(const QString &original) const {
return original;
}
QByteArray bookmark() const {
return QByteArray();
}
};
bool linuxMoveFile(const char *from, const char *to);
bool psLaunchMaps(const Data::LocationPoint &point);

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 psPathBookmark(const QString &path);
class PsFileBookmark {
public:
PsFileBookmark(const QByteArray &bookmark) : _inner(bookmark) {
}
bool check() const {
return _inner.valid();
}
bool enable() const {
return _inner.enable();
}
void disable() const {
return _inner.disable();
}
const QString &name(const QString &original) const {
return _inner.name(original);
}
QByteArray bookmark() const {
return _inner.bookmark();
}
private:
objc_FileBookmark _inner;
};
QString strNotificationAboutThemeChange();
QString strNotificationAboutScreenLocked();
QString strNotificationAboutScreenUnlocked();

View File

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

View File

@ -25,25 +25,4 @@ double objc_appkitVersion();
QString objc_documentsPath();
QString objc_appDataPath();
QByteArray objc_downloadPathBookmark(const QString &path);
QByteArray objc_pathBookmark(const QString &path);
void objc_downloadPathEnableAccess(const QByteArray &bookmark);
class objc_FileBookmark {
public:
objc_FileBookmark(const QByteArray &bookmark);
bool valid() const;
bool enable() const;
void disable() const;
const QString &name(const QString &original) const;
QByteArray bookmark() const;
~objc_FileBookmark();
private:
#ifdef OS_MAC_STORE
class objc_FileBookmarkData;
objc_FileBookmarkData *data = nullptr;
#endif // OS_MAC_STORE
};

View File

@ -373,19 +373,6 @@ QByteArray objc_downloadPathBookmark(const QString &path) {
#endif // OS_MAC_STORE
}
QByteArray objc_pathBookmark(const QString &path) {
#ifndef OS_MAC_STORE
return QByteArray();
#else // OS_MAC_STORE
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.toUtf8().constData()]];
if (!url) return QByteArray();
NSError *error = nil;
NSData *data = [url bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
return data ? QByteArray::fromNSData(data) : QByteArray();
#endif // OS_MAC_STORE
}
void objc_downloadPathEnableAccess(const QByteArray &bookmark) {
#ifdef OS_MAC_STORE
if (bookmark.isEmpty()) return;
@ -412,101 +399,3 @@ void objc_downloadPathEnableAccess(const QByteArray &bookmark) {
}
#endif // OS_MAC_STORE
}
#ifdef OS_MAC_STORE
namespace {
QMutex _bookmarksMutex;
}
class objc_FileBookmark::objc_FileBookmarkData {
public:
~objc_FileBookmarkData() {
if (url) [url release];
}
NSURL *url = nil;
QString name;
QByteArray bookmark;
int counter = 0;
};
#endif // OS_MAC_STORE
objc_FileBookmark::objc_FileBookmark(const QByteArray &bookmark) {
#ifdef OS_MAC_STORE
if (bookmark.isEmpty()) return;
BOOL isStale = NO;
NSError *error = nil;
NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
if (!url) return;
if ([url startAccessingSecurityScopedResource]) {
data = new objc_FileBookmarkData();
data->url = [url retain];
data->name = NS2QString([url path]);
data->bookmark = bookmark;
[url stopAccessingSecurityScopedResource];
}
#endif // OS_MAC_STORE
}
bool objc_FileBookmark::valid() const {
if (enable()) {
disable();
return true;
}
return false;
}
bool objc_FileBookmark::enable() const {
#ifndef OS_MAC_STORE
return true;
#else // OS_MAC_STORE
if (!data) return false;
QMutexLocker lock(&_bookmarksMutex);
if (data->counter > 0 || [data->url startAccessingSecurityScopedResource] == YES) {
++data->counter;
return true;
}
return false;
#endif // OS_MAC_STORE
}
void objc_FileBookmark::disable() const {
#ifdef OS_MAC_STORE
if (!data) return;
QMutexLocker lock(&_bookmarksMutex);
if (data->counter > 0) {
--data->counter;
if (!data->counter) {
[data->url stopAccessingSecurityScopedResource];
}
}
#endif // OS_MAC_STORE
}
const QString &objc_FileBookmark::name(const QString &original) const {
#ifndef OS_MAC_STORE
return original;
#else // OS_MAC_STORE
return (data && !data->name.isEmpty()) ? data->name : original;
#endif // OS_MAC_STORE
}
QByteArray objc_FileBookmark::bookmark() const {
#ifndef OS_MAC_STORE
return QByteArray();
#else // OS_MAC_STORE
return data ? data->bookmark : QByteArray();
#endif // OS_MAC_STORE
}
objc_FileBookmark::~objc_FileBookmark() {
#ifdef OS_MAC_STORE
if (data && data->counter > 0) {
LOG(("Did not disable() bookmark, counter: %1").arg(data->counter));
[data->url stopAccessingSecurityScopedResource];
}
#endif // OS_MAC_STORE
}

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) {
return QByteArray();
}
inline QByteArray psPathBookmark(const QString &path) {
return QByteArray();
}
inline void psDownloadPathEnableAccess() {
}
class PsFileBookmark {
public:
PsFileBookmark(const QByteArray &bookmark) {
}
bool check() const {
return true;
}
bool enable() const {
return true;
}
void disable() const {
}
const QString &name(const QString &original) const {
return original;
}
QByteArray bookmark() const {
return QByteArray();
}
};
bool psLaunchMaps(const Data::LocationPoint &point);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,6 @@ constexpr auto kPhotoBaseCacheTag = 0x0000000000020000ULL;
constexpr auto kPhotoBaseCacheMask = 0x000000000000FF00ULL;
constexpr auto kSerializeTypeShift = quint8(0x08);
constexpr auto kNonStorageLocationToken = quint8(0x10);
const auto kInMediaCacheLocation = QString("*media_cache*");
enum class NonStorageLocationType : quint8 {
Web,
@ -939,102 +938,3 @@ std::optional<ImageLocation> ImageLocation::FromSerialized(
}
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));
}
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) {
_gif = Media::Clip::MakeReader(
_documentMedia.get(),
FullMsgId(),
_documentMedia->owner()->location(),
_documentMedia->bytes(),
std::move(callback));
} else {
_gifThumbnail = Media::Clip::MakeReader(

View File

@ -46,9 +46,24 @@ nice_target_sources(td_ui ${src_loc}
PRIVATE
${style_files}
core/file_location.cpp
core/file_location.h
core/mime_type.cpp
core/mime_type.h
media/clip/media_clip_check_streaming.cpp
media/clip/media_clip_check_streaming.h
media/clip/media_clip_ffmpeg.cpp
media/clip/media_clip_ffmpeg.h
media/clip/media_clip_implementation.cpp
media/clip/media_clip_implementation.h
media/clip/media_clip_reader.cpp
media/clip/media_clip_reader.h
platform/mac/file_bookmark_mac.h
platform/mac/file_bookmark_mac.mm
platform/platform_file_bookmark.h
ui/chat/attach/attach_album_thumbnail.cpp
ui/chat/attach/attach_album_thumbnail.h
ui/chat/attach/attach_album_preview.cpp
@ -91,4 +106,5 @@ target_link_libraries(td_ui
PUBLIC
tdesktop::td_lang
desktop-app::lib_ui
desktop-app::lib_ffmpeg
)