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 #include "media/clip/media_clip_reader.h"
9
10 #include "media/clip/media_clip_ffmpeg.h"
11 #include "media/clip/media_clip_check_streaming.h"
12 #include "core/file_location.h"
13 #include "base/random.h"
14 #include "base/invoke_queued.h"
15 #include "logs.h"
16
17 #include <QtCore/QBuffer>
18 #include <QtCore/QAbstractEventDispatcher>
19 #include <QtCore/QCoreApplication>
20 #include <QtCore/QThread>
21 #include <QtCore/QFileInfo>
22
23 extern "C" {
24 #include <libavcodec/avcodec.h>
25 #include <libavformat/avformat.h>
26 #include <libavutil/opt.h>
27 #include <libswscale/swscale.h>
28 } // extern "C"
29
30 namespace Media {
31 namespace Clip {
32 namespace {
33
34 constexpr auto kClipThreadsCount = 8;
35 constexpr auto kAverageGifSize = 320 * 240;
36 constexpr auto kWaitBeforeGifPause = crl::time(200);
37
38 QVector<QThread*> threads;
39 QVector<Manager*> managers;
40
PrepareFrameImage(const FrameRequest & request,const QImage & original,bool hasAlpha,QImage & cache)41 QImage PrepareFrameImage(const FrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) {
42 auto needResize = (original.width() != request.framew) || (original.height() != request.frameh);
43 auto needOuterFill = (request.outerw != request.framew) || (request.outerh != request.frameh);
44 auto needRounding = (request.radius != ImageRoundRadius::None);
45 if (!needResize && !needOuterFill && !hasAlpha && !needRounding) {
46 return original;
47 }
48
49 auto factor = request.factor;
50 auto needNewCache = (cache.width() != request.outerw || cache.height() != request.outerh);
51 if (needNewCache) {
52 cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied);
53 cache.setDevicePixelRatio(factor);
54 }
55 {
56 Painter p(&cache);
57 if (needNewCache) {
58 if (request.framew < request.outerw) {
59 p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::imageBg);
60 p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::imageBg);
61 }
62 if (request.frameh < request.outerh) {
63 p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::imageBg);
64 p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::imageBg);
65 }
66 }
67 if (hasAlpha) {
68 p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::imageBgTransparent);
69 }
70 auto position = QPoint((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor));
71 if (needResize) {
72 PainterHighQualityEnabler hq(p);
73
74 auto dst = QRect(position, QSize(request.framew / factor, request.frameh / factor));
75 auto src = QRect(0, 0, original.width(), original.height());
76 p.drawImage(dst, original, src, Qt::ColorOnly);
77 } else {
78 p.drawImage(position, original);
79 }
80 }
81 if (needRounding) {
82 Images::prepareRound(cache, request.radius, request.corners);
83 }
84 return cache;
85 }
86
PrepareFrame(const FrameRequest & request,const QImage & original,bool hasAlpha,QImage & cache)87 QPixmap PrepareFrame(const FrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) {
88 return QPixmap::fromImage(PrepareFrameImage(request, original, hasAlpha, cache), Qt::ColorOnly);
89 }
90
91 } // namespace
92
Reader(const Core::FileLocation & location,const QByteArray & data,Callback && callback)93 Reader::Reader(
94 const Core::FileLocation &location,
95 const QByteArray &data,
96 Callback &&callback)
97 : _callback(std::move(callback)) {
98 init(location, data);
99 }
100
Reader(const QString & filepath,Callback && callback)101 Reader::Reader(const QString &filepath, Callback &&callback)
102 : _callback(std::move(callback)) {
103 init(Core::FileLocation(filepath), QByteArray());
104 }
105
Reader(const QByteArray & data,Callback && callback)106 Reader::Reader(const QByteArray &data, Callback &&callback)
107 : _callback(std::move(callback)) {
108 init(Core::FileLocation(QString()), data);
109 }
110
init(const Core::FileLocation & location,const QByteArray & data)111 void Reader::init(const Core::FileLocation &location, const QByteArray &data) {
112 if (threads.size() < kClipThreadsCount) {
113 _threadIndex = threads.size();
114 threads.push_back(new QThread());
115 managers.push_back(new Manager(threads.back()));
116 threads.back()->start();
117 } else {
118 _threadIndex = int32(base::RandomValue<uint32>() % threads.size());
119 int32 loadLevel = 0x7FFFFFFF;
120 for (int32 i = 0, l = threads.size(); i < l; ++i) {
121 int32 level = managers.at(i)->loadLevel();
122 if (level < loadLevel) {
123 _threadIndex = i;
124 loadLevel = level;
125 }
126 }
127 }
128 managers.at(_threadIndex)->append(this, location, data);
129 }
130
frameToShow(int32 * index) const131 Reader::Frame *Reader::frameToShow(int32 *index) const { // 0 means not ready
132 int step = _step.loadAcquire(), i;
133 if (step == WaitingForDimensionsStep) {
134 if (index) *index = 0;
135 return nullptr;
136 } else if (step == WaitingForRequestStep) {
137 i = 0;
138 } else if (step == WaitingForFirstFrameStep) {
139 i = 0;
140 } else {
141 i = (step / 2) % 3;
142 }
143 if (index) *index = i;
144 return _frames + i;
145 }
146
frameToWrite(int32 * index) const147 Reader::Frame *Reader::frameToWrite(int32 *index) const { // 0 means not ready
148 int32 step = _step.loadAcquire(), i;
149 if (step == WaitingForDimensionsStep) {
150 i = 0;
151 } else if (step == WaitingForRequestStep) {
152 if (index) *index = 0;
153 return nullptr;
154 } else if (step == WaitingForFirstFrameStep) {
155 i = 0;
156 } else {
157 i = ((step + 2) / 2) % 3;
158 }
159 if (index) *index = i;
160 return _frames + i;
161 }
162
frameToWriteNext(bool checkNotWriting,int32 * index) const163 Reader::Frame *Reader::frameToWriteNext(bool checkNotWriting, int32 *index) const {
164 int32 step = _step.loadAcquire(), i;
165 if (step == WaitingForDimensionsStep || step == WaitingForRequestStep || (checkNotWriting && (step % 2))) {
166 if (index) *index = 0;
167 return nullptr;
168 }
169 i = ((step + 4) / 2) % 3;
170 if (index) *index = i;
171 return _frames + i;
172 }
173
moveToNextShow() const174 void Reader::moveToNextShow() const {
175 int32 step = _step.loadAcquire();
176 if (step == WaitingForDimensionsStep) {
177 } else if (step == WaitingForRequestStep) {
178 _step.storeRelease(WaitingForFirstFrameStep);
179 } else if (step == WaitingForFirstFrameStep) {
180 } else if (!(step % 2)) {
181 _step.storeRelease(step + 1);
182 }
183 }
184
moveToNextWrite() const185 void Reader::moveToNextWrite() const {
186 int32 step = _step.loadAcquire();
187 if (step == WaitingForDimensionsStep) {
188 _step.storeRelease(WaitingForRequestStep);
189 } else if (step == WaitingForRequestStep) {
190 } else if (step == WaitingForFirstFrameStep) {
191 _step.storeRelease(0);
192
193 // Force paint the first frame so moveToNextShow() is called.
194 _frames[0].displayed.storeRelease(0);
195 } else if (step % 2) {
196 _step.storeRelease((step + 1) % 6);
197 }
198 }
199
callback(Reader * reader,qint32 threadIndex,qint32 notification)200 void Reader::callback(Reader *reader, qint32 threadIndex, qint32 notification) {
201 // Check if reader is not deleted already
202 if (managers.size() > threadIndex && managers.at(threadIndex)->carries(reader) && reader->_callback) {
203 reader->_callback(Notification(notification));
204 }
205 }
206
start(int32 framew,int32 frameh,int32 outerw,int32 outerh,ImageRoundRadius radius,RectParts corners)207 void Reader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, ImageRoundRadius radius, RectParts corners) {
208 if (managers.size() <= _threadIndex) error();
209 if (_state == State::Error) return;
210
211 if (_step.loadAcquire() == WaitingForRequestStep) {
212 int factor = style::DevicePixelRatio();
213 FrameRequest request;
214 request.factor = factor;
215 request.framew = framew * factor;
216 request.frameh = frameh * factor;
217 request.outerw = outerw * factor;
218 request.outerh = outerh * factor;
219 request.radius = radius;
220 request.corners = corners;
221 _frames[0].request = _frames[1].request = _frames[2].request = request;
222 moveToNextShow();
223 managers.at(_threadIndex)->start(this);
224 }
225 }
226
current(int32 framew,int32 frameh,int32 outerw,int32 outerh,ImageRoundRadius radius,RectParts corners,crl::time ms)227 QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, ImageRoundRadius radius, RectParts corners, crl::time ms) {
228 Expects(outerw > 0);
229 Expects(outerh > 0);
230
231 auto frame = frameToShow();
232 Assert(frame != nullptr);
233
234 auto shouldBePaused = !ms;
235 if (!shouldBePaused) {
236 frame->displayed.storeRelease(1);
237 if (_autoPausedGif.loadAcquire()) {
238 _autoPausedGif.storeRelease(0);
239 if (managers.size() <= _threadIndex) error();
240 if (_state != State::Error) {
241 managers.at(_threadIndex)->update(this);
242 }
243 }
244 } else {
245 frame->displayed.storeRelease(-1);
246 }
247
248 auto factor = style::DevicePixelRatio();
249 if (frame->pix.width() == outerw * factor
250 && frame->pix.height() == outerh * factor
251 && frame->request.radius == radius
252 && frame->request.corners == corners) {
253 moveToNextShow();
254 return frame->pix;
255 }
256
257 frame->request.framew = framew * factor;
258 frame->request.frameh = frameh * factor;
259 frame->request.outerw = outerw * factor;
260 frame->request.outerh = outerh * factor;
261
262 QImage cacheForResize;
263 frame->original.setDevicePixelRatio(factor);
264 frame->pix = QPixmap();
265 frame->pix = PrepareFrame(frame->request, frame->original, true, cacheForResize);
266
267 auto other = frameToWriteNext(true);
268 if (other) other->request = frame->request;
269
270 moveToNextShow();
271
272 if (managers.size() <= _threadIndex) error();
273 if (_state != State::Error) {
274 managers.at(_threadIndex)->update(this);
275 }
276
277 return frame->pix;
278 }
279
ready() const280 bool Reader::ready() const {
281 if (_width && _height) return true;
282
283 auto frame = frameToShow();
284 if (frame) {
285 _width = frame->original.width();
286 _height = frame->original.height();
287 return true;
288 }
289 return false;
290 }
291
getPositionMs() const292 crl::time Reader::getPositionMs() const {
293 if (auto frame = frameToShow()) {
294 return frame->positionMs;
295 }
296 return 0;
297 }
298
getDurationMs() const299 crl::time Reader::getDurationMs() const {
300 return ready() ? _durationMs : 0;
301 }
302
pauseResumeVideo()303 void Reader::pauseResumeVideo() {
304 if (managers.size() <= _threadIndex) error();
305 if (_state == State::Error) return;
306
307 _videoPauseRequest.storeRelease(1 - _videoPauseRequest.loadAcquire());
308 managers.at(_threadIndex)->start(this);
309 }
310
videoPaused() const311 bool Reader::videoPaused() const {
312 return _videoPauseRequest.loadAcquire() != 0;
313 }
314
width() const315 int32 Reader::width() const {
316 return _width;
317 }
318
height() const319 int32 Reader::height() const {
320 return _height;
321 }
322
state() const323 State Reader::state() const {
324 return _state;
325 }
326
stop()327 void Reader::stop() {
328 if (managers.size() <= _threadIndex) error();
329 if (_state != State::Error) {
330 managers.at(_threadIndex)->stop(this);
331 _width = _height = 0;
332 }
333 }
334
error()335 void Reader::error() {
336 _state = State::Error;
337 _private = nullptr;
338 }
339
finished()340 void Reader::finished() {
341 _state = State::Finished;
342 _private = nullptr;
343 }
344
~Reader()345 Reader::~Reader() {
346 stop();
347 }
348
349 class ReaderPrivate {
350 public:
ReaderPrivate(Reader * reader,const Core::FileLocation & location,const QByteArray & data)351 ReaderPrivate(Reader *reader, const Core::FileLocation &location, const QByteArray &data)
352 : _interface(reader)
353 , _data(data) {
354 if (_data.isEmpty()) {
355 _location = std::make_unique<Core::FileLocation>(location);
356 if (!_location->accessEnable()) {
357 error();
358 return;
359 }
360 }
361 _accessed = true;
362 }
363
start(crl::time ms)364 ProcessResult start(crl::time ms) {
365 if (!_implementation && !init()) {
366 return error();
367 }
368 if (frame()->original.isNull()) {
369 auto readResult = _implementation->readFramesTill(-1, ms);
370 if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile && _seekPositionMs > 0) {
371 // If seek was done to the end: try to read the first frame,
372 // get the frame size and return a black frame with that size.
373
374 auto firstFramePositionMs = crl::time(0);
375 auto reader = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data);
376 if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) {
377 auto firstFrameReadResult = reader->readFramesTill(-1, ms);
378 if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) {
379 if (reader->renderFrame(frame()->original, frame()->alpha, QSize())) {
380 frame()->original.fill(QColor(0, 0, 0));
381
382 frame()->positionMs = _seekPositionMs;
383
384 _width = frame()->original.width();
385 _height = frame()->original.height();
386 _durationMs = _implementation->durationMs();
387 return ProcessResult::Started;
388 }
389 }
390 }
391
392 return error();
393 } else if (readResult != internal::ReaderImplementation::ReadResult::Success) { // Read the first frame.
394 return error();
395 }
396 if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize())) {
397 return error();
398 }
399 frame()->positionMs = _implementation->frameRealTime();
400
401 _width = frame()->original.width();
402 _height = frame()->original.height();
403 _durationMs = _implementation->durationMs();
404 return ProcessResult::Started;
405 }
406 return ProcessResult::Wait;
407 }
408
process(crl::time ms)409 ProcessResult process(crl::time ms) { // -1 - do nothing, 0 - update, 1 - reinit
410 if (_state == State::Error) {
411 return ProcessResult::Error;
412 } else if (_state == State::Finished) {
413 return ProcessResult::Finished;
414 }
415
416 if (!_request.valid()) {
417 return start(ms);
418 }
419 if (!_started) {
420 _started = true;
421 }
422
423 if (!_autoPausedGif && !_videoPausedAtMs && ms >= _nextFrameWhen) {
424 return ProcessResult::Repaint;
425 }
426 return ProcessResult::Wait;
427 }
428
finishProcess(crl::time ms)429 ProcessResult finishProcess(crl::time ms) {
430 auto frameMs = _seekPositionMs + ms - _animationStarted;
431 auto readResult = _implementation->readFramesTill(frameMs, ms);
432 if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile) {
433 stop();
434 _state = State::Finished;
435 return ProcessResult::Finished;
436 } else if (readResult == internal::ReaderImplementation::ReadResult::Error) {
437 return error();
438 }
439 _nextFramePositionMs = _implementation->frameRealTime();
440 _nextFrameWhen = _animationStarted + _implementation->framePresentationTime();
441 if (_nextFrameWhen > _seekPositionMs) {
442 _nextFrameWhen -= _seekPositionMs;
443 } else {
444 _nextFrameWhen = 1;
445 }
446
447 if (!renderFrame()) {
448 return error();
449 }
450 return ProcessResult::CopyFrame;
451 }
452
renderFrame()453 bool renderFrame() {
454 Expects(_request.valid());
455
456 if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) {
457 return false;
458 }
459 frame()->original.setDevicePixelRatio(_request.factor);
460 frame()->pix = QPixmap();
461 frame()->pix = PrepareFrame(_request, frame()->original, frame()->alpha, frame()->cache);
462 frame()->when = _nextFrameWhen;
463 frame()->positionMs = _nextFramePositionMs;
464 return true;
465 }
466
init()467 bool init() {
468 if (_data.isEmpty() && QFileInfo(_location->name()).size() <= internal::kMaxInMemory) {
469 QFile f(_location->name());
470 if (f.open(QIODevice::ReadOnly)) {
471 _data = f.readAll();
472 if (f.error() != QFile::NoError) {
473 _data = QByteArray();
474 }
475 }
476 }
477
478 _implementation = std::make_unique<internal::FFMpegReaderImplementation>(_location.get(), &_data);
479
480 return _implementation->start(internal::ReaderImplementation::Mode::Silent, _seekPositionMs);
481 }
482
startedAt(crl::time ms)483 void startedAt(crl::time ms) {
484 _animationStarted = _nextFrameWhen = ms;
485 }
486
pauseVideo(crl::time ms)487 void pauseVideo(crl::time ms) {
488 if (_videoPausedAtMs) return; // Paused already.
489
490 _videoPausedAtMs = ms;
491 }
492
resumeVideo(crl::time ms)493 void resumeVideo(crl::time ms) {
494 if (!_videoPausedAtMs) return; // Not paused.
495
496 auto delta = ms - _videoPausedAtMs;
497 _animationStarted += delta;
498 _nextFrameWhen += delta;
499
500 _videoPausedAtMs = 0;
501 }
502
error()503 ProcessResult error() {
504 stop();
505 _state = State::Error;
506 return ProcessResult::Error;
507 }
508
stop()509 void stop() {
510 _implementation = nullptr;
511 if (_location) {
512 if (_accessed) {
513 _location->accessDisable();
514 }
515 _location = nullptr;
516 }
517 _accessed = false;
518 }
519
~ReaderPrivate()520 ~ReaderPrivate() {
521 stop();
522 _data.clear();
523 }
524
525 private:
526 Reader *_interface;
527 State _state = State::Reading;
528 crl::time _seekPositionMs = 0;
529
530 QByteArray _data;
531 std::unique_ptr<Core::FileLocation> _location;
532 bool _accessed = false;
533
534 QBuffer _buffer;
535 std::unique_ptr<internal::ReaderImplementation> _implementation;
536
537 FrameRequest _request;
538 struct Frame {
539 QPixmap pix;
540 QImage original, cache;
541 bool alpha = true;
542 crl::time when = 0;
543
544 // Counted from the end, so that positionMs <= durationMs despite keep up delays.
545 crl::time positionMs = 0;
546 };
547 Frame _frames[3];
548 int _frame = 0;
frame()549 not_null<Frame*> frame() {
550 return _frames + _frame;
551 }
552
553 int _width = 0;
554 int _height = 0;
555
556 crl::time _durationMs = 0;
557 crl::time _animationStarted = 0;
558 crl::time _nextFrameWhen = 0;
559 crl::time _nextFramePositionMs = 0;
560
561 bool _autoPausedGif = false;
562 bool _started = false;
563 crl::time _videoPausedAtMs = 0;
564
565 friend class Manager;
566
567 };
568
Manager(QThread * thread)569 Manager::Manager(QThread *thread) {
570 moveToThread(thread);
571 connect(thread, &QThread::started, this, [=] { process(); });
572 connect(thread, &QThread::finished, this, [=] { finish(); });
573
574 _timer.setSingleShot(true);
575 _timer.moveToThread(thread);
576 connect(&_timer, &QTimer::timeout, this, [=] { process(); });
577 }
578
append(Reader * reader,const Core::FileLocation & location,const QByteArray & data)579 void Manager::append(Reader *reader, const Core::FileLocation &location, const QByteArray &data) {
580 reader->_private = new ReaderPrivate(reader, location, data);
581 _loadLevel.fetchAndAddRelaxed(kAverageGifSize);
582 update(reader);
583 }
584
start(Reader * reader)585 void Manager::start(Reader *reader) {
586 update(reader);
587 }
588
update(Reader * reader)589 void Manager::update(Reader *reader) {
590 QMutexLocker lock(&_readerPointersMutex);
591 auto i = _readerPointers.find(reader);
592 if (i == _readerPointers.cend()) {
593 _readerPointers.insert(reader, QAtomicInt(1));
594 } else {
595 i->storeRelease(1);
596 }
597 InvokeQueued(this, [=] { process(); });
598 }
599
stop(Reader * reader)600 void Manager::stop(Reader *reader) {
601 if (!carries(reader)) return;
602
603 QMutexLocker lock(&_readerPointersMutex);
604 _readerPointers.remove(reader);
605 InvokeQueued(this, [=] { process(); });
606 }
607
carries(Reader * reader) const608 bool Manager::carries(Reader *reader) const {
609 QMutexLocker lock(&_readerPointersMutex);
610 return _readerPointers.contains(reader);
611 }
612
unsafeFindReaderPointer(ReaderPrivate * reader)613 Manager::ReaderPointers::iterator Manager::unsafeFindReaderPointer(ReaderPrivate *reader) {
614 ReaderPointers::iterator it = _readerPointers.find(reader->_interface);
615
616 // could be a new reader which was realloced in the same address
617 return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.end();
618 }
619
constUnsafeFindReaderPointer(ReaderPrivate * reader) const620 Manager::ReaderPointers::const_iterator Manager::constUnsafeFindReaderPointer(ReaderPrivate *reader) const {
621 ReaderPointers::const_iterator it = _readerPointers.constFind(reader->_interface);
622
623 // could be a new reader which was realloced in the same address
624 return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.cend();
625 }
626
callback(Reader * reader,Notification notification)627 void Manager::callback(Reader *reader, Notification notification) {
628 crl::on_main([=, threadIndex = reader->threadIndex()] {
629 Reader::callback(reader, threadIndex, notification);
630 });
631 }
632
handleProcessResult(ReaderPrivate * reader,ProcessResult result,crl::time ms)633 bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) {
634 QMutexLocker lock(&_readerPointersMutex);
635 auto it = unsafeFindReaderPointer(reader);
636 if (result == ProcessResult::Error) {
637 if (it != _readerPointers.cend()) {
638 it.key()->error();
639 callback(it.key(), NotificationReinit);
640 _readerPointers.erase(it);
641 }
642 return false;
643 } else if (result == ProcessResult::Finished) {
644 if (it != _readerPointers.cend()) {
645 it.key()->finished();
646 callback(it.key(), NotificationReinit);
647 }
648 return false;
649 }
650 if (it == _readerPointers.cend()) {
651 return false;
652 }
653
654 if (result == ProcessResult::Started) {
655 _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - kAverageGifSize);
656 it.key()->_durationMs = reader->_durationMs;
657 }
658 // See if we need to pause GIF because it is not displayed right now.
659 if (!reader->_autoPausedGif && result == ProcessResult::Repaint) {
660 int32 ishowing, iprevious;
661 auto showing = it.key()->frameToShow(&ishowing), previous = it.key()->frameToWriteNext(false, &iprevious);
662 Assert(previous != nullptr && showing != nullptr && ishowing >= 0 && iprevious >= 0);
663 if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown
664 if (reader->_frames[ishowing].when + kWaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) {
665 reader->_autoPausedGif = true;
666 it.key()->_autoPausedGif.storeRelease(1);
667 result = ProcessResult::Paused;
668 }
669 }
670 }
671 if (result == ProcessResult::Started || result == ProcessResult::CopyFrame) {
672 Assert(reader->_frame >= 0);
673 auto frame = it.key()->_frames + reader->_frame;
674 frame->clear();
675 frame->pix = reader->frame()->pix;
676 frame->original = reader->frame()->original;
677 frame->displayed.storeRelease(0);
678 frame->positionMs = reader->frame()->positionMs;
679 if (result == ProcessResult::Started) {
680 reader->startedAt(ms);
681 it.key()->moveToNextWrite();
682 callback(it.key(), NotificationReinit);
683 }
684 } else if (result == ProcessResult::Paused) {
685 it.key()->moveToNextWrite();
686 callback(it.key(), NotificationReinit);
687 } else if (result == ProcessResult::Repaint) {
688 it.key()->moveToNextWrite();
689 callback(it.key(), NotificationRepaint);
690 }
691 return true;
692 }
693
handleResult(ReaderPrivate * reader,ProcessResult result,crl::time ms)694 Manager::ResultHandleState Manager::handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms) {
695 if (!handleProcessResult(reader, result, ms)) {
696 _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : kAverageGifSize));
697 delete reader;
698 return ResultHandleRemove;
699 }
700
701 _processingInThread->eventDispatcher()->processEvents(QEventLoop::AllEvents);
702 if (_processingInThread->isInterruptionRequested()) {
703 return ResultHandleStop;
704 }
705
706 if (result == ProcessResult::Repaint) {
707 {
708 QMutexLocker lock(&_readerPointersMutex);
709 auto it = constUnsafeFindReaderPointer(reader);
710 if (it != _readerPointers.cend()) {
711 int32 index = 0;
712 Reader::Frame *frame = it.key()->frameToWrite(&index);
713 if (frame) {
714 frame->clear();
715 } else {
716 Assert(!reader->_request.valid());
717 }
718 reader->_frame = index;
719 }
720 }
721 return handleResult(reader, reader->finishProcess(ms), ms);
722 }
723
724 return ResultHandleContinue;
725 }
726
process()727 void Manager::process() {
728 if (_processingInThread) {
729 _needReProcess = true;
730 return;
731 }
732
733 _timer.stop();
734 _processingInThread = thread();
735
736 bool checkAllReaders = false;
737 auto ms = crl::now(), minms = ms + 86400 * crl::time(1000);
738 {
739 QMutexLocker lock(&_readerPointersMutex);
740 for (auto it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
741 if (it->loadAcquire() && it.key()->_private != nullptr) {
742 auto i = _readers.find(it.key()->_private);
743 if (i == _readers.cend()) {
744 _readers.insert(it.key()->_private, 0);
745 } else {
746 i.value() = ms;
747 if (i.key()->_autoPausedGif && !it.key()->_autoPausedGif.loadAcquire()) {
748 i.key()->_autoPausedGif = false;
749 }
750 if (it.key()->_videoPauseRequest.loadAcquire()) {
751 i.key()->pauseVideo(ms);
752 } else {
753 i.key()->resumeVideo(ms);
754 }
755 }
756 auto frame = it.key()->frameToWrite();
757 if (frame) it.key()->_private->_request = frame->request;
758 it->storeRelease(0);
759 }
760 }
761 checkAllReaders = (_readers.size() > _readerPointers.size());
762 }
763
764 for (auto i = _readers.begin(), e = _readers.end(); i != e;) {
765 ReaderPrivate *reader = i.key();
766 if (i.value() <= ms) {
767 ResultHandleState state = handleResult(reader, reader->process(ms), ms);
768 if (state == ResultHandleRemove) {
769 i = _readers.erase(i);
770 continue;
771 } else if (state == ResultHandleStop) {
772 _processingInThread = nullptr;
773 return;
774 }
775 ms = crl::now();
776 if (reader->_videoPausedAtMs) {
777 i.value() = ms + 86400 * 1000ULL;
778 } else if (reader->_nextFrameWhen && reader->_started) {
779 i.value() = reader->_nextFrameWhen;
780 } else {
781 i.value() = (ms + 86400 * 1000ULL);
782 }
783 } else if (checkAllReaders) {
784 QMutexLocker lock(&_readerPointersMutex);
785 auto it = constUnsafeFindReaderPointer(reader);
786 if (it == _readerPointers.cend()) {
787 _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : kAverageGifSize));
788 delete reader;
789 i = _readers.erase(i);
790 continue;
791 }
792 }
793 if (!reader->_autoPausedGif && i.value() < minms) {
794 minms = i.value();
795 }
796 ++i;
797 }
798
799 ms = crl::now();
800 if (_needReProcess || minms <= ms) {
801 _needReProcess = false;
802 _timer.start(1);
803 } else {
804 _timer.start(minms - ms);
805 }
806
807 _processingInThread = nullptr;
808 }
809
finish()810 void Manager::finish() {
811 _timer.stop();
812 clear();
813 }
814
clear()815 void Manager::clear() {
816 {
817 QMutexLocker lock(&_readerPointersMutex);
818 for (auto it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
819 it.key()->_private = nullptr;
820 }
821 _readerPointers.clear();
822 }
823
824 for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e; ++i) {
825 delete i.key();
826 }
827 _readers.clear();
828 }
829
~Manager()830 Manager::~Manager() {
831 clear();
832 }
833
PrepareForSending(const QString & fname,const QByteArray & data)834 Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const QByteArray &data) {
835 auto result = Ui::PreparedFileInformation::Video();
836 auto localLocation = Core::FileLocation(fname);
837 auto localData = QByteArray(data);
838
839 auto seekPositionMs = crl::time(0);
840 auto reader = std::make_unique<internal::FFMpegReaderImplementation>(&localLocation, &localData);
841 if (reader->start(internal::ReaderImplementation::Mode::Inspecting, seekPositionMs)) {
842 auto durationMs = reader->durationMs();
843 if (durationMs > 0) {
844 result.isGifv = reader->isGifv();
845 // Use first video frame as a thumbnail.
846 // All other apps and server do that way.
847 //if (!result.isGifv) {
848 // auto middleMs = durationMs / 2;
849 // if (!reader->inspectAt(middleMs)) {
850 // return result;
851 // }
852 //}
853 auto hasAlpha = false;
854 auto readResult = reader->readFramesTill(-1, crl::now());
855 auto readFrame = (readResult == internal::ReaderImplementation::ReadResult::Success);
856 if (readFrame && reader->renderFrame(result.thumbnail, hasAlpha, QSize())) {
857 if (hasAlpha) {
858 auto cacheForResize = QImage();
859 auto request = FrameRequest();
860 request.framew = request.outerw = result.thumbnail.width();
861 request.frameh = request.outerh = result.thumbnail.height();
862 request.factor = 1;
863 result.thumbnail = PrepareFrameImage(request, result.thumbnail, hasAlpha, cacheForResize);
864 }
865 result.duration = static_cast<int>(durationMs / 1000);
866 }
867
868 result.supportsStreaming = CheckStreamingSupport(
869 localLocation,
870 localData);
871 }
872 }
873 return result;
874 }
875
Finish()876 void Finish() {
877 if (!threads.isEmpty()) {
878 for (int32 i = 0, l = threads.size(); i < l; ++i) {
879 threads.at(i)->quit();
880 DEBUG_LOG(("Waiting for clipThread to finish: %1").arg(i));
881 threads.at(i)->wait();
882 delete managers.at(i);
883 delete threads.at(i);
884 }
885 threads.clear();
886 managers.clear();
887 }
888 }
889
890 Reader *const ReaderPointer::BadPointer = reinterpret_cast<Reader*>(1);
891
~ReaderPointer()892 ReaderPointer::~ReaderPointer() {
893 if (valid()) {
894 delete _pointer;
895 }
896 _pointer = nullptr;
897 }
898
899 } // namespace Clip
900 } // namespace Media
901