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