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