Move Media::Clip::Reader and FileLocation to td_ui.
This commit is contained in:
parent
05eb549a3d
commit
8b96f4c214
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
121
Telegram/SourceFiles/core/file_location.cpp
Normal file
121
Telegram/SourceFiles/core/file_location.cpp
Normal 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
|
72
Telegram/SourceFiles/core/file_location.h
Normal file
72
Telegram/SourceFiles/core/file_location.h
Normal 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
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
33
Telegram/SourceFiles/platform/mac/file_bookmark_mac.h
Normal file
33
Telegram/SourceFiles/platform/mac/file_bookmark_mac.h
Normal 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
|
125
Telegram/SourceFiles/platform/mac/file_bookmark_mac.mm
Normal file
125
Telegram/SourceFiles/platform/mac/file_bookmark_mac.mm
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
43
Telegram/SourceFiles/platform/platform_file_bookmark.h
Normal file
43
Telegram/SourceFiles/platform/platform_file_bookmark.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user