inline gifs playback / stopping done

This commit is contained in:
John Preston 2015-12-23 19:48:44 +03:00
parent 9a72293e0a
commit 6170144fe9
17 changed files with 143 additions and 42 deletions

View File

@ -2407,7 +2407,6 @@ namespace App {
}
void regGifItem(ClipReader *reader, HistoryItem *item) {
stopGifItems();
::gifItems.insert(reader, item);
}

View File

@ -116,10 +116,10 @@ enum {
AudioVoiceMsgInMemory = 2 * 1024 * 1024, // 2 Mb audio is hold in memory and auto loaded
AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over
StickerInMemory = 2 * 1024 * 1024, // 1 Mb stickers hold in memory, auto loaded and displayed inline
StickerInMemory = 2 * 1024 * 1024, // 2 Mb stickers hold in memory, auto loaded and displayed inline
StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker
AnimationInMemory = 2 * 1024 * 1024, // 2 Mb gif and mp4 animations held in memory while playing
AnimationInMemory = 10 * 1024 * 1024, // 10 Mb gif and mp4 animations held in memory while playing
MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app
MaxZoomLevel = 7, // x8

View File

@ -147,9 +147,28 @@ namespace Notify {
if (it != items.cend()) {
HistoryItem *item = it.value();
item->initDimensions(); // can delete reader and items entry it
Notify::historyItemResized(item, true);
Notify::historyItemLayoutChanged(item);
bool stopped = false;
if (reader->paused()) {
if (MainWidget *m = App::main()) {
if (!m->isItemVisible(item)) { // stop animation if it is not visible
if (HistoryMedia *media = item->getMedia()) {
media->stopInline(item);
if (DocumentData *document = media->getDocument()) { // forget data from memory
if (!document->data.isEmpty()) {
document->data.clear();
document->prepareAutoLoader();
}
}
stopped = true;
}
}
}
}
if (!stopped) {
item->initDimensions(); // can delete reader and items entry it
Notify::historyItemResized(item);
Notify::historyItemLayoutChanged(item);
}
}
if (Window *w = App::wnd()) w->notify_clipReinit(reader);
}

View File

@ -783,6 +783,7 @@ public:
stop();
setBadPointer(_location);
setBadPointer(_implementation);
_data.clear();
}
private:
@ -870,6 +871,8 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause >= qMax(reader->_previousMs, ms)) {
it.key()->_paused.set(false);
reader->_paused = false;
} else {
result = ClipProcessReinit;
}
}
}

View File

@ -474,6 +474,9 @@ public:
bool currentDisplayed() const {
return _currentDisplayed.get();
}
bool paused() const {
return _paused.get();
}
int32 width() const;
int32 height() const;

View File

@ -3772,13 +3772,12 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, b
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty();
if (!_data->loader && _data->status == FileReady && !already && !hasdata && _data->size < AudioVoiceMsgInMemory) {
bool loaded = _data->loaded();
if (!loaded && _data->status == FileReady && _data->loader && !_data->loader->loading() && !_data->loader->paused()) {
_data->save(QString());
}
if (_data->loader) {
if (_data->loader && (_data->loader->loading() || _data->loader->paused())) {
ensureAnimation(parent);
if (!_animation->radial.animating()) {
_animation->radial.start(_data->progress());
@ -4394,10 +4393,18 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
bool bubble = parent->hasBubble();
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
bool loaded = _data->loaded(_gif ? false : true);
if (!loaded && _data->status == FileReady && _data->loader && !_data->loader->loading() && !_data->loader->paused()) {
_data->save(QString());
}
if (loaded && !_gif) {
const_cast<HistoryGif*>(this)->playInline(const_cast<HistoryItem*>(parent));
}
bool animating = (_gif && _gif->started());
if (!animating) {
if (_data->loader) {
if (_data->loader && !_data->loader->tryingLocal()) {
ensureAnimation(parent);
if (!_animation->radial.animating()) {
_animation->radial.start(_data->progress());
@ -4429,7 +4436,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
if (!animating || radial) {
if (radial || (!animating && !_gif && (!_data->loader || !_data->loader->tryingLocal() || _data->size > AnimationInMemory))) {
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
p.setPen(Qt::NoPen);
if (selected) {
@ -4453,7 +4460,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
}
style::sprite icon;
if (!_data->already().isEmpty() && !radial) {
if (_data->loaded() && !radial) {
icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
} else if (_data->loader || radial) {
icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
@ -4497,9 +4504,8 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x,
}
if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) {
if (_gif && _gif->started()) {
lnk = _savel;
} else {
lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _savel;
lnk = _data->loaded() ? _savel : (_data->loader ? _cancell : _savel);
}
if (parent->getMedia() == this) {
@ -4514,11 +4520,11 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x,
}
const QString HistoryGif::inDialogsText() const {
return _data->name.isEmpty() ? lang(lng_in_dlg_file) : _data->name;
return qsl("GIF");
}
const QString HistoryGif::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_file) + (_data->name.isEmpty() ? QString() : (qsl(" : ") + _data->name)) + qsl(" ]");
return qsl("[ GIF ]");
}
void HistoryGif::setStatusSize(int32 newSize) const {
@ -4534,7 +4540,7 @@ void HistoryGif::updateStatusText(const HistoryItem *parent) const {
statusSize = _data->uploadOffset;
} else if (_data->loader) {
statusSize = _data->loader->currentOffset();
} else if (!_data->already().isEmpty()) {
} else if (_data->loaded()) {
statusSize = FileStatusSizeLoaded;
} else {
statusSize = FileStatusSizeReady;
@ -4629,7 +4635,10 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r,
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty();
bool loaded = _data->loaded();
if (!loaded && _data->status == FileReady && _data->loader && !_data->loader->loading() && !_data->loader->paused()) {
_data->save(QString());
}
int32 usew = _maxw, usex = 0;
const HistoryReply *reply = toHistoryReply(parent);
@ -4642,11 +4651,8 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r,
}
if (rtl()) usex = _width - usex - usew;
if (!already && !hasdata && !_data->loader && _data->status == FileReady) {
_data->save(QString());
}
if (_data->sticker()->img->isNull() && (already || hasdata)) {
if (already) {
if (_data->sticker()->img->isNull() && loaded) {
if (_data->data.isEmpty()) {
_data->sticker()->img = ImagePtr(_data->already());
} else {
_data->sticker()->img = ImagePtr(_data->data);

View File

@ -1467,7 +1467,7 @@ protected:
return !_data->loader;
}
bool dataLoaded() const {
return !_data->already().isEmpty() || !_data->data.isEmpty();
return _data->loaded();
}
private:
@ -1542,7 +1542,7 @@ protected:
return !_data->loader;
}
bool dataLoaded() const {
return !_data->already().isEmpty() || !_data->data.isEmpty();
return _data->loaded();
}
private:
@ -1628,7 +1628,7 @@ protected:
return !_data->loader;
}
bool dataLoaded() const {
return !_data->already().isEmpty() && !_data->data.isEmpty();
return _data->loaded();
}
private:

View File

@ -5576,6 +5576,17 @@ void HistoryWidget::peerMessagesUpdated() {
if (_list) peerMessagesUpdated(_peer->id);
}
bool HistoryWidget::isItemVisible(HistoryItem *item) {
if (isHidden() || _a_show.animating() || !_list) {
return false;
}
int32 top = _list->itemTop(item), st = _scroll.scrollTop();
if (top < 0 || top + item->height() <= st || top >= st + _scroll.height()) {
return false;
}
return true;
}
void HistoryWidget::ui_redrawHistoryItem(const HistoryItem *item) {
if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) {
_list->redrawItem(item);

View File

@ -554,6 +554,8 @@ public:
resizeEvent(0);
}
bool isItemVisible(HistoryItem *item);
void ui_redrawHistoryItem(const HistoryItem *item);
void notify_historyItemLayoutChanged(const HistoryItem *item);

View File

@ -2357,6 +2357,10 @@ namespace Local {
}
virtual void readFromStream(QDataStream &stream, quint64 &first, quint64 &second, quint32 &type, QByteArray &data) = 0;
virtual void clearInMap() = 0;
virtual ~AbstractCachedLoadTask() {
delete _result;
setBadPointer(_result);
}
protected:
FileKey _key;

View File

@ -723,6 +723,13 @@ QPixmap MainWidget::grabInner() {
}
}
bool MainWidget::isItemVisible(HistoryItem *item) {
if (isHidden() || _a_show.animating()) {
return false;
}
return history.isItemVisible(item);
}
QPixmap MainWidget::grabTopBar() {
if (!_topBar.isHidden()) {
return myGrab(&_topBar);

View File

@ -405,6 +405,8 @@ public:
QPixmap grabTopBar();
QPixmap grabInner();
bool isItemVisible(HistoryItem *item);
void ui_showStickerPreview(DocumentData *sticker);
void ui_hideStickerPreview();
void ui_redrawHistoryItem(const HistoryItem *item);

View File

@ -1000,7 +1000,11 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
int32 maxw = st::mvDocSize.width() - st::mvDocIconSize - st::mvDocPadding * 3;
_docName = (!_doc || _doc->name.isEmpty()) ? lang(_doc ? (_doc->type == StickerDocument ? lng_in_dlg_sticker : lng_mediaview_doc_image) : lng_message_empty) : _doc->name;
if (_doc) {
_docName = (_doc->type == StickerDocument) ? lang(lng_in_dlg_sticker) : (_doc->type == AnimatedDocument ? qsl("GIF") : (_doc->name.isEmpty() ? lang(lng_mediaview_doc_image) : _doc->name));
} else {
_docName = lang(lng_message_empty);
}
_docNameWidth = st::mvDocNameFont->width(_docName);
if (_docNameWidth > maxw) {
_docName = st::mvDocNameFont->elided(_docName, maxw, Qt::ElideMiddle);

View File

@ -48,7 +48,7 @@ namespace {
}
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size) : prev(0), next(0),
priority(0), inQueue(false), complete(false),
priority(0), _paused(false), inQueue(false), complete(false),
_localStatus(LocalNotTried), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), _locationType(UnknownFileLocation), volume(volume), local(local), secret(secret),
id(0), access(0), fileIsOpen(false), size(size), type(mtpc_storage_fileUnknown), _localTaskId(0) {
@ -60,7 +60,7 @@ id(0), access(0), fileIsOpen(false), size(size), type(mtpc_storage_fileUnknown),
}
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &to, int32 size, bool todata) : prev(0), next(0),
priority(0), inQueue(false), complete(false),
priority(0), _paused(false), inQueue(false), complete(false),
_localStatus(LocalNotTried), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), _locationType(type), volume(0), local(0), secret(0),
id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(todata), size(size), type(mtpc_storage_fileUnknown), _localTaskId(0) {
@ -114,7 +114,7 @@ int32 mtpFileLoader::fullSize() const {
}
void mtpFileLoader::setFileName(const QString &fileName) {
if (duplicateInData && fname.isEmpty()) {
if (duplicateInData && fname.isEmpty() && !fileName.isEmpty()) {
file.setFileName(fname = fileName);
}
}
@ -318,6 +318,7 @@ void mtpFileLoader::removeFromQueue() {
void mtpFileLoader::pause() {
removeFromQueue();
_paused = true;
}
bool mtpFileLoader::tryLoadLocal() {
@ -330,10 +331,6 @@ bool mtpFileLoader::tryLoadLocal() {
if (_locationType == UnknownFileLocation) {
_localTaskId = Local::startImageLoad(storageKey(dc, volume, local), this);
if (_localTaskId) {
_localStatus = LocalLoading;
return true;
}
} else {
if (duplicateInData) {
MediaKey mkey = mediaKey(_locationType, dc, id);
@ -346,6 +343,10 @@ bool mtpFileLoader::tryLoadLocal() {
}
if (data.isEmpty()) {
if (_localTaskId) {
_localStatus = LocalLoading;
return true;
}
_localStatus = LocalNotFound;
return false;
}
@ -410,7 +411,14 @@ void mtpFileLoader::localLoaded(const StorageImageSaved &result, const QByteArra
loadNext();
}
bool mtpFileLoader::tryingLocal() const {
return (_localStatus == LocalLoading);
}
void mtpFileLoader::start(bool loadFirst, bool prior) {
if (_paused) {
_paused = false;
}
if (complete || tryLoadLocal()) return;
if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) {
@ -545,6 +553,10 @@ bool mtpFileLoader::loading() const {
return inQueue;
}
bool mtpFileLoader::paused() const {
return _paused;
}
void mtpFileLoader::started(bool loadFirst, bool prior) {
if ((queue->queries >= MaxFileQueries && (!loadFirst || !prior)) || complete) return;
loadPart();

View File

@ -140,6 +140,8 @@ public:
void start(bool loadFirst = false, bool prior = true);
void cancel();
bool loading() const;
bool paused() const;
bool tryingLocal() const;
uint64 objId() const;
@ -158,7 +160,7 @@ signals:
private:
mtpFileLoaderQueue *queue;
bool inQueue, complete;
bool _paused, inQueue, complete;
LocalLoadStatus _localStatus;
bool tryLoadLocal();

View File

@ -844,11 +844,18 @@ bool StickerData::setInstalled() const {
AudioData::AudioData(const AudioId &id, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) :
id(id), access(access), date(date), mime(mime), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) {
_location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id));
if (!loaded() && size < AudioVoiceMsgInMemory) {
loader = new mtpFileLoader(dc, id, access, AudioFileLocation, QString(), size, true);
}
}
void AudioData::save(const QString &toFile) {
cancel(true);
loader = new mtpFileLoader(dc, id, access, AudioFileLocation, toFile, size, (size < AudioVoiceMsgInMemory));
if (loader && loader->fileName().isEmpty()) {
loader->setFileName(toFile);
} else {
cancel(true);
loader = new mtpFileLoader(dc, id, access, AudioFileLocation, toFile, size, (size < AudioVoiceMsgInMemory));
}
loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(audioLoadProgress(mtpFileLoader*)));
loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(audioLoadFailed(mtpFileLoader*, bool)));
loader->start();
@ -1025,8 +1032,8 @@ DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 dat
, loader(0)
, _additional(0)
, _isImage(false) {
setattributes(attributes);
_location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id));
setattributes(attributes);
}
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@ -1071,19 +1078,32 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
case mtpc_documentAttributeFilename: name = qs(attributes[i].c_documentAttributeFilename().vfile_name); break;
}
}
prepareAutoLoader();
}
void DocumentData::prepareAutoLoader() {
if (type == StickerDocument) {
if (dimensions.width() <= 0 || dimensions.height() <= 0 || dimensions.width() > StickerMaxSize || dimensions.height() > StickerMaxSize || size > StickerInMemory) {
type = FileDocument;
delete _additional;
_additional = 0;
} else if (!loader && !loaded(true)) {
loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, QString(), size, true);
}
} else if (isAnimation()) {
if (size <= AnimationInMemory && !loader && !loaded(true)) {
loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, QString(), size, true);
}
}
}
void DocumentData::save(const QString &toFile) {
cancel(true);
bool isSticker = (type == StickerDocument) && (dimensions.width() > 0) && (dimensions.height() > 0) && (size < StickerInMemory);
loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, toFile, size, isSticker);
if (loader && loader->fileName().isEmpty()) {
loader->setFileName(toFile);
} else {
cancel(true);
loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, toFile, size, (type == StickerDocument));
}
loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(documentLoadProgress(mtpFileLoader*)));
loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(documentLoadFailed(mtpFileLoader*, bool)));
loader->start();

View File

@ -942,6 +942,9 @@ struct AudioData {
_location = loc;
}
}
bool loaded(bool check = false) {
return !data.isEmpty() || !already(check).isEmpty();
}
float64 progress() const {
return loader ? loader->currentProgress() : ((status == FileDownloadFailed || (_location.name().isEmpty() && data.isEmpty())) ? 0 : 1);
@ -1135,6 +1138,10 @@ struct DocumentData {
_location = loc;
}
}
bool loaded(bool check = false) {
return !data.isEmpty() || !already(check).isEmpty();
}
void prepareAutoLoader();
StickerData *sticker() {
return (type == StickerDocument) ? static_cast<StickerData*>(_additional) : 0;
}