1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9
10 #include "ui/chat/attach/attach_prepare.h"
11 #include "ui/image/image_prepare.h"
12
13 #include <QtCore/QTimer>
14 #include <QtCore/QMutex>
15
16 namespace Core {
17 class FileLocation;
18 } // namespace Core
19
20 namespace Media {
21 namespace Clip {
22
23 enum class State {
24 Reading,
25 Error,
26 Finished,
27 };
28
29 struct FrameRequest {
validFrameRequest30 bool valid() const {
31 return factor > 0;
32 }
33 int factor = 0;
34 int framew = 0;
35 int frameh = 0;
36 int outerw = 0;
37 int outerh = 0;
38 ImageRoundRadius radius = ImageRoundRadius::None;
39 RectParts corners = RectPart::AllCorners;
40 };
41
42 enum ReaderSteps : int {
43 WaitingForDimensionsStep = -3, // before ReaderPrivate read the first image and got the original frame size
44 WaitingForRequestStep = -2, // before Reader got the original frame size and prepared the frame request
45 WaitingForFirstFrameStep = -1, // before ReaderPrivate got the frame request and started waiting for the 1-2 delay
46 };
47
48 enum Notification : int {
49 NotificationReinit,
50 NotificationRepaint,
51 };
52
53 class ReaderPrivate;
54 class Reader {
55 public:
56 using Callback = Fn<void(Notification)>;
57 enum class Mode {
58 Gif,
59 Video,
60 };
61
62 Reader(const Core::FileLocation &location, const QByteArray &data, Callback &&callback);
63 Reader(const QString &filepath, Callback &&callback);
64 Reader(const QByteArray &data, Callback &&callback);
65
66 // Reader can be already deleted.
67 static void callback(Reader *reader, qint32 threadIndex, qint32 notification);
68
69 void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners);
70 QPixmap current(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners, crl::time ms);
frameOriginal()71 QPixmap frameOriginal() const {
72 if (auto frame = frameToShow()) {
73 auto result = QPixmap::fromImage(frame->original);
74 result.detach();
75 return result;
76 }
77 return QPixmap();
78 }
currentDisplayed()79 bool currentDisplayed() const {
80 auto frame = frameToShow();
81 return frame ? (frame->displayed.loadAcquire() != 0) : true;
82 }
autoPausedGif()83 bool autoPausedGif() const {
84 return _autoPausedGif.loadAcquire();
85 }
86 bool videoPaused() const;
threadIndex()87 int threadIndex() const {
88 return _threadIndex;
89 }
90
91 int width() const;
92 int height() const;
93
94 State state() const;
started()95 bool started() const {
96 auto step = _step.loadAcquire();
97 return (step == WaitingForFirstFrameStep) || (step >= 0);
98 }
99 bool ready() const;
100
101 crl::time getPositionMs() const;
102 crl::time getDurationMs() const;
103 void pauseResumeVideo();
104
105 void stop();
106 void error();
107 void finished();
108
109 ~Reader();
110
111 private:
112 void init(const Core::FileLocation &location, const QByteArray &data);
113
114 Callback _callback;
115 State _state = State::Reading;
116
117 crl::time _durationMs = 0;
118
119 mutable int _width = 0;
120 mutable int _height = 0;
121
122 // -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
123 mutable QAtomicInt _step = WaitingForDimensionsStep;
124 struct Frame {
clearFrame125 void clear() {
126 pix = QPixmap();
127 original = QImage();
128 }
129 QPixmap pix;
130 QImage original;
131 FrameRequest request;
132 QAtomicInt displayed = 0;
133
134 // Should be counted from the end,
135 // so that positionMs <= _durationMs.
136 crl::time positionMs = 0;
137 };
138 mutable Frame _frames[3];
139 Frame *frameToShow(int *index = nullptr) const; // 0 means not ready
140 Frame *frameToWrite(int *index = nullptr) const; // 0 means not ready
141 Frame *frameToWriteNext(bool check, int *index = nullptr) const;
142 void moveToNextShow() const;
143 void moveToNextWrite() const;
144
145 QAtomicInt _autoPausedGif = 0;
146 QAtomicInt _videoPauseRequest = 0;
147 int32 _threadIndex;
148
149 friend class Manager;
150
151 ReaderPrivate *_private = nullptr;
152
153 };
154
155 class ReaderPointer {
156 public:
157 ReaderPointer(std::nullptr_t = nullptr) {
158 }
ReaderPointer(Reader * pointer)159 explicit ReaderPointer(Reader *pointer) : _pointer(pointer) {
160 }
161 ReaderPointer(const ReaderPointer &other) = delete;
162 ReaderPointer &operator=(const ReaderPointer &other) = delete;
ReaderPointer(ReaderPointer && other)163 ReaderPointer(ReaderPointer &&other) : _pointer(base::take(other._pointer)) {
164 }
165 ReaderPointer &operator=(ReaderPointer &&other) {
166 swap(other);
167 return *this;
168 }
swap(ReaderPointer & other)169 void swap(ReaderPointer &other) {
170 qSwap(_pointer, other._pointer);
171 }
get()172 Reader *get() const {
173 return valid() ? _pointer : nullptr;
174 }
175 Reader *operator->() const {
176 return get();
177 }
setBad()178 void setBad() {
179 reset();
180 _pointer = BadPointer;
181 }
reset()182 void reset() {
183 ReaderPointer temp;
184 swap(temp);
185 }
isBad()186 bool isBad() const {
187 return (_pointer == BadPointer);
188 }
valid()189 bool valid() const {
190 return _pointer && !isBad();
191 }
192 explicit operator bool() const {
193 return valid();
194 }
Bad()195 static inline ReaderPointer Bad() {
196 ReaderPointer result;
197 result.setBad();
198 return result;
199 }
200 ~ReaderPointer();
201
202 private:
203 Reader *_pointer = nullptr;
204 static Reader *const BadPointer;
205
206 };
207
208 template <typename ...Args>
MakeReader(Args &&...args)209 inline ReaderPointer MakeReader(Args&&... args) {
210 return ReaderPointer(new Reader(std::forward<Args>(args)...));
211 }
212
213 enum class ProcessResult {
214 Error,
215 Started,
216 Finished,
217 Paused,
218 Repaint,
219 CopyFrame,
220 Wait,
221 };
222
223 class Manager : public QObject {
224 public:
225 explicit Manager(QThread *thread);
226 ~Manager();
227
loadLevel()228 int loadLevel() const {
229 return _loadLevel;
230 }
231 void append(Reader *reader, const Core::FileLocation &location, const QByteArray &data);
232 void start(Reader *reader);
233 void update(Reader *reader);
234 void stop(Reader *reader);
235 bool carries(Reader *reader) const;
236
237 private:
238 void process();
239 void finish();
240 void callback(Reader *reader, Notification notification);
241 void clear();
242
243 QAtomicInt _loadLevel;
244 using ReaderPointers = QMap<Reader*, QAtomicInt>;
245 ReaderPointers _readerPointers;
246 mutable QMutex _readerPointersMutex;
247
248 ReaderPointers::const_iterator constUnsafeFindReaderPointer(ReaderPrivate *reader) const;
249 ReaderPointers::iterator unsafeFindReaderPointer(ReaderPrivate *reader);
250
251 bool handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms);
252
253 enum ResultHandleState {
254 ResultHandleRemove,
255 ResultHandleStop,
256 ResultHandleContinue,
257 };
258 ResultHandleState handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms);
259
260 using Readers = QMap<ReaderPrivate*, crl::time>;
261 Readers _readers;
262
263 QTimer _timer;
264 QThread *_processingInThread = nullptr;
265 bool _needReProcess = false;
266
267 };
268
269 [[nodiscard]] Ui::PreparedFileInformation::Video PrepareForSending(
270 const QString &fname,
271 const QByteArray &data);
272
273 void Finish();
274
275 } // namespace Clip
276 } // namespace Media
277