2016-06-24 10:37:29 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-06-24 10:37:29 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-06-24 10:37:29 +00:00
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
|
2017-03-10 14:14:10 +00:00
|
|
|
#include "storage/localimageloader.h"
|
2018-10-23 09:44:42 +00:00
|
|
|
#include "ui/image/image_prepare.h"
|
2017-03-10 14:14:10 +00:00
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
class FileLocation;
|
|
|
|
|
|
|
|
namespace Media {
|
|
|
|
namespace Clip {
|
|
|
|
|
|
|
|
enum class State {
|
|
|
|
Reading,
|
|
|
|
Error,
|
2016-07-12 18:04:34 +00:00
|
|
|
Finished,
|
2016-06-24 10:37:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FrameRequest {
|
|
|
|
bool valid() const {
|
|
|
|
return factor > 0;
|
|
|
|
}
|
|
|
|
int factor = 0;
|
|
|
|
int framew = 0;
|
|
|
|
int frameh = 0;
|
|
|
|
int outerw = 0;
|
|
|
|
int outerh = 0;
|
2016-07-19 10:54:43 +00:00
|
|
|
ImageRoundRadius radius = ImageRoundRadius::None;
|
2017-12-18 17:50:25 +00:00
|
|
|
RectParts corners = RectPart::AllCorners;
|
2016-06-24 10:37:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum ReaderSteps {
|
|
|
|
WaitingForDimensionsStep = -3, // before ReaderPrivate read the first image and got the original frame size
|
|
|
|
WaitingForRequestStep = -2, // before Reader got the original frame size and prepared the frame request
|
|
|
|
WaitingForFirstFrameStep = -1, // before ReaderPrivate got the frame request and started waiting for the 1-2 delay
|
|
|
|
};
|
|
|
|
|
|
|
|
class ReaderPrivate;
|
|
|
|
class Reader {
|
|
|
|
public:
|
2018-06-04 15:35:11 +00:00
|
|
|
using Callback = Fn<void(Notification)>;
|
2016-06-24 10:37:29 +00:00
|
|
|
enum class Mode {
|
|
|
|
Gif,
|
|
|
|
Video,
|
|
|
|
};
|
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
Reader(const QString &filepath, Callback &&callback, Mode mode = Mode::Gif, TimeMs seekMs = 0);
|
2017-08-17 08:31:24 +00:00
|
|
|
Reader(not_null<DocumentData*> document, FullMsgId msgId, Callback &&callback, Mode mode = Mode::Gif, TimeMs seekMs = 0);
|
2017-05-18 20:18:59 +00:00
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
static void callback(Reader *reader, int threadIndex, Notification notification); // reader can be deleted
|
|
|
|
|
|
|
|
void setAutoplay() {
|
|
|
|
_autoplay = true;
|
|
|
|
}
|
|
|
|
bool autoplay() const {
|
|
|
|
return _autoplay;
|
|
|
|
}
|
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
AudioMsgId audioMsgId() const {
|
|
|
|
return _audioMsgId;
|
2016-07-12 11:38:16 +00:00
|
|
|
}
|
2016-12-01 19:20:33 +00:00
|
|
|
TimeMs seekPositionMs() const {
|
2016-07-13 17:34:57 +00:00
|
|
|
return _seekPositionMs;
|
|
|
|
}
|
2016-07-12 11:38:16 +00:00
|
|
|
|
2017-12-18 17:50:25 +00:00
|
|
|
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, TimeMs ms);
|
2017-05-22 15:25:49 +00:00
|
|
|
QPixmap current();
|
2016-06-24 10:37:29 +00:00
|
|
|
QPixmap frameOriginal() const {
|
2016-11-15 11:56:49 +00:00
|
|
|
if (auto frame = frameToShow()) {
|
|
|
|
auto result = QPixmap::fromImage(frame->original);
|
|
|
|
result.detach();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return QPixmap();
|
2016-06-24 10:37:29 +00:00
|
|
|
}
|
|
|
|
bool currentDisplayed() const {
|
2017-05-22 15:25:49 +00:00
|
|
|
auto frame = frameToShow();
|
2016-06-24 10:37:29 +00:00
|
|
|
return frame ? (frame->displayed.loadAcquire() != 0) : true;
|
|
|
|
}
|
2016-07-13 11:24:31 +00:00
|
|
|
bool autoPausedGif() const {
|
|
|
|
return _autoPausedGif.loadAcquire();
|
2016-06-24 10:37:29 +00:00
|
|
|
}
|
2016-07-13 11:24:31 +00:00
|
|
|
bool videoPaused() const;
|
2016-06-24 10:37:29 +00:00
|
|
|
int threadIndex() const {
|
|
|
|
return _threadIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const;
|
|
|
|
int height() const;
|
|
|
|
|
|
|
|
State state() const;
|
|
|
|
bool started() const {
|
2017-05-22 15:25:49 +00:00
|
|
|
auto step = _step.loadAcquire();
|
2016-06-24 10:37:29 +00:00
|
|
|
return (step == WaitingForFirstFrameStep) || (step >= 0);
|
|
|
|
}
|
|
|
|
bool ready() const;
|
|
|
|
|
2016-07-12 12:28:07 +00:00
|
|
|
bool hasAudio() const;
|
2016-12-01 19:20:33 +00:00
|
|
|
TimeMs getPositionMs() const;
|
|
|
|
TimeMs getDurationMs() const;
|
2016-07-13 11:24:31 +00:00
|
|
|
void pauseResumeVideo();
|
2016-07-12 12:28:07 +00:00
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
void stop();
|
|
|
|
void error();
|
2016-07-12 18:04:34 +00:00
|
|
|
void finished();
|
2016-06-24 10:37:29 +00:00
|
|
|
|
2016-07-05 17:43:30 +00:00
|
|
|
Mode mode() const {
|
|
|
|
return _mode;
|
|
|
|
}
|
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
~Reader();
|
|
|
|
|
|
|
|
private:
|
2017-05-18 20:18:59 +00:00
|
|
|
void init(const FileLocation &location, const QByteArray &data);
|
2016-06-24 10:37:29 +00:00
|
|
|
|
|
|
|
Callback _callback;
|
|
|
|
Mode _mode;
|
|
|
|
|
|
|
|
State _state = State::Reading;
|
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
AudioMsgId _audioMsgId;
|
2016-07-12 12:28:07 +00:00
|
|
|
bool _hasAudio = false;
|
2016-12-01 19:20:33 +00:00
|
|
|
TimeMs _durationMs = 0;
|
|
|
|
TimeMs _seekPositionMs = 0;
|
2016-07-12 11:38:16 +00:00
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
mutable int _width = 0;
|
|
|
|
mutable int _height = 0;
|
|
|
|
|
|
|
|
// -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
|
|
|
|
mutable QAtomicInt _step = WaitingForDimensionsStep;
|
|
|
|
struct Frame {
|
|
|
|
void clear() {
|
|
|
|
pix = QPixmap();
|
|
|
|
original = QImage();
|
|
|
|
}
|
|
|
|
QPixmap pix;
|
|
|
|
QImage original;
|
|
|
|
FrameRequest request;
|
2016-07-12 12:28:07 +00:00
|
|
|
QAtomicInt displayed = 0;
|
|
|
|
|
|
|
|
// Should be counted from the end,
|
|
|
|
// so that positionMs <= _durationMs.
|
2016-12-01 19:20:33 +00:00
|
|
|
TimeMs positionMs = 0;
|
2016-06-24 10:37:29 +00:00
|
|
|
};
|
|
|
|
mutable Frame _frames[3];
|
2017-05-18 20:18:59 +00:00
|
|
|
Frame *frameToShow(int *index = nullptr) const; // 0 means not ready
|
|
|
|
Frame *frameToWrite(int *index = nullptr) const; // 0 means not ready
|
|
|
|
Frame *frameToWriteNext(bool check, int *index = nullptr) const;
|
2016-06-24 10:37:29 +00:00
|
|
|
void moveToNextShow() const;
|
|
|
|
void moveToNextWrite() const;
|
|
|
|
|
2016-07-13 11:24:31 +00:00
|
|
|
QAtomicInt _autoPausedGif = 0;
|
|
|
|
QAtomicInt _videoPauseRequest = 0;
|
2016-06-24 10:37:29 +00:00
|
|
|
int32 _threadIndex;
|
|
|
|
|
|
|
|
bool _autoplay = false;
|
|
|
|
|
|
|
|
friend class Manager;
|
|
|
|
|
|
|
|
ReaderPrivate *_private = nullptr;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-09-28 21:33:05 +00:00
|
|
|
template <typename ...Args>
|
|
|
|
inline ReaderPointer MakeReader(Args&&... args) {
|
2017-02-21 13:45:56 +00:00
|
|
|
return ReaderPointer(new Reader(std::forward<Args>(args)...));
|
2016-09-28 21:33:05 +00:00
|
|
|
}
|
|
|
|
|
2016-06-24 10:37:29 +00:00
|
|
|
enum class ProcessResult {
|
|
|
|
Error,
|
|
|
|
Started,
|
2016-07-12 18:04:34 +00:00
|
|
|
Finished,
|
2016-06-24 10:37:29 +00:00
|
|
|
Paused,
|
|
|
|
Repaint,
|
|
|
|
CopyFrame,
|
|
|
|
Wait,
|
|
|
|
};
|
|
|
|
|
|
|
|
class Manager : public QObject {
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Manager(QThread *thread);
|
|
|
|
int32 loadLevel() const {
|
|
|
|
return _loadLevel.load();
|
|
|
|
}
|
|
|
|
void append(Reader *reader, const FileLocation &location, const QByteArray &data);
|
|
|
|
void start(Reader *reader);
|
|
|
|
void update(Reader *reader);
|
|
|
|
void stop(Reader *reader);
|
|
|
|
bool carries(Reader *reader) const;
|
|
|
|
~Manager();
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void processDelayed();
|
|
|
|
|
|
|
|
void callback(Media::Clip::Reader *reader, qint32 threadIndex, qint32 notification);
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void process();
|
|
|
|
void finish();
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
void clear();
|
|
|
|
|
|
|
|
QAtomicInt _loadLevel;
|
2016-09-25 18:05:47 +00:00
|
|
|
using ReaderPointers = QMap<Reader*, QAtomicInt>;
|
2016-06-24 10:37:29 +00:00
|
|
|
ReaderPointers _readerPointers;
|
2016-09-25 18:05:47 +00:00
|
|
|
mutable QMutex _readerPointersMutex;
|
2016-06-24 10:37:29 +00:00
|
|
|
|
|
|
|
ReaderPointers::const_iterator constUnsafeFindReaderPointer(ReaderPrivate *reader) const;
|
|
|
|
ReaderPointers::iterator unsafeFindReaderPointer(ReaderPrivate *reader);
|
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
bool handleProcessResult(ReaderPrivate *reader, ProcessResult result, TimeMs ms);
|
2016-06-24 10:37:29 +00:00
|
|
|
|
|
|
|
enum ResultHandleState {
|
|
|
|
ResultHandleRemove,
|
|
|
|
ResultHandleStop,
|
|
|
|
ResultHandleContinue,
|
|
|
|
};
|
2016-12-01 19:20:33 +00:00
|
|
|
ResultHandleState handleResult(ReaderPrivate *reader, ProcessResult result, TimeMs ms);
|
2016-06-24 10:37:29 +00:00
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
typedef QMap<ReaderPrivate*, TimeMs> Readers;
|
2016-06-24 10:37:29 +00:00
|
|
|
Readers _readers;
|
|
|
|
|
|
|
|
QTimer _timer;
|
|
|
|
QThread *_processingInThread;
|
|
|
|
bool _needReProcess;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-12-19 16:57:42 +00:00
|
|
|
FileMediaInformation::Video PrepareForSending(const QString &fname, const QByteArray &data);
|
2016-06-24 10:37:29 +00:00
|
|
|
|
|
|
|
void Finish();
|
|
|
|
|
|
|
|
} // namespace Clip
|
|
|
|
} // namespace Media
|