sending gifv done

This commit is contained in:
John Preston 2015-12-28 18:34:45 +03:00
parent d966eebb7c
commit 2e853f9338
11 changed files with 140 additions and 34 deletions

View File

@ -938,7 +938,7 @@ namespace App {
if (!item->toHistoryForwarded() && item->out()) {
if (HistoryMedia *media = item->getMedia()) {
if (DocumentData *doc = media->getDocument()) {
if (doc->type == AnimatedDocument && doc->mime.toLower() == qstr("video/mp4")) {
if (doc->isGifv()) {
addSavedGif(doc);
}
}
@ -1623,6 +1623,9 @@ namespace App {
}
convert->id = document;
convert->status = FileReady;
if (cSavedGifs().indexOf(convert) >= 0) { // id changed
Local::writeSavedGifs();
}
sentSticker = !!convert->sticker();
}
convert->access = access;

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
static const int32 AppVersion = 9015;
static const wchar_t *AppVersionStr = L"0.9.15";
static const bool DevVersion = false;
#define BETA_VERSION (9015004ULL) // just comment this line to build public version
#define BETA_VERSION (9015005ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";

View File

@ -358,13 +358,20 @@ ClipReader::~ClipReader() {
class ClipReaderImplementation {
public:
ClipReaderImplementation(FileLocation *location, QByteArray *data) : _location(location), _data(data), _device(0) {
ClipReaderImplementation(FileLocation *location, QByteArray *data)
: _location(location)
, _data(data)
, _device(0)
, _dataSize(0) {
}
virtual bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0;
virtual int32 nextFrameDelay() = 0;
virtual bool start() = 0;
virtual bool start(bool onlyGifv) = 0;
virtual ~ClipReaderImplementation() {
}
int64 dataSize() const {
return _dataSize;
}
protected:
FileLocation *_location;
@ -372,14 +379,17 @@ protected:
QFile _file;
QBuffer _buffer;
QIODevice *_device;
int64 _dataSize;
void initDevice() {
if (_data->isEmpty()) {
if (_file.isOpen()) _file.close();
_file.setFileName(_location->name());
_dataSize = _file.size();
} else {
if (_buffer.isOpen()) _buffer.close();
_buffer.setBuffer(_data);
_dataSize = _data->size();
}
_device = _data->isEmpty() ? static_cast<QIODevice*>(&_file) : static_cast<QIODevice*>(&_buffer);
}
@ -432,7 +442,8 @@ public:
return _frameDelay;
}
bool start() {
bool start(bool onlyGifv) {
if (onlyGifv) return false;
return jumpToStart();
}
@ -632,7 +643,7 @@ public:
return qsl("for file '%1', data size '%2'").arg(_location ? _location->name() : QString()).arg(_data->size());
}
bool start() {
bool start(bool onlyGifv) {
initDevice();
if (!_device->open(QIODevice::ReadOnly)) {
LOG(("Gif Error: Unable to open device %1").arg(logData()));
@ -671,6 +682,18 @@ public:
// Get a pointer to the codec context for the audio stream
_codecContext = _fmtContext->streams[_streamId]->codec;
_codec = avcodec_find_decoder(_codecContext->codec_id);
if (onlyGifv) {
if (av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0) >= 0) { // should be no audio stream
return false;
}
if (dataSize() > AnimationInMemory) {
return false;
}
if (_codecContext->codec_id != AV_CODEC_ID_H264) {
return false;
}
}
av_opt_set_int(_codecContext, "refcounted_frames", 1, 0);
if ((res = avcodec_open2(_codecContext, _codec, 0)) < 0) {
LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
@ -680,6 +703,11 @@ public:
return true;
}
int32 duration() const {
if (_fmtContext->streams[_streamId]->duration == AV_NOPTS_VALUE) return 0;
return (_fmtContext->streams[_streamId]->duration * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
}
~FFMpegReaderImplementation() {
if (_ioContext) av_free(_ioContext);
if (_codecContext) avcodec_close(_codecContext);
@ -864,7 +892,7 @@ public:
_implementation = new FFMpegReaderImplementation(_location, &_data);
// _implementation = new QtGifReaderImplementation(_location, &_data);
return _implementation->start();
return _implementation->start(false);
}
ClipProcessResult error() {
@ -1108,3 +1136,30 @@ void ClipReadManager::clear() {
ClipReadManager::~ClipReadManager() {
clear();
}
MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByteArray &data, QImage &cover) {
FileLocation localloc(StorageFilePartial, fname);
QByteArray localdata(data);
FFMpegReaderImplementation *reader = new FFMpegReaderImplementation(&localloc, &localdata);
if (reader->start(true)) {
bool hasAlpha = false;
if (reader->readNextFrame(cover, hasAlpha, QSize())) {
if (cover.width() > 0 && cover.height() > 0 && cover.width() < cover.height() * 10 && cover.height() < cover.width() * 10) {
if (hasAlpha) {
QImage cache;
ClipFrameRequest request;
request.framew = request.outerw = cover.width();
request.frameh = request.outerh = cover.height();
request.factor = 1;
cover = _prepareFrame(request, cover, cache, hasAlpha).toImage();
}
int32 duration = reader->duration();
delete reader;
return MTP_documentAttributeVideo(MTP_int(duration), MTP_int(cover.width()), MTP_int(cover.height()));
}
}
}
delete reader;
return MTP_documentAttributeFilename(MTP_string(fname));
}

View File

@ -646,3 +646,5 @@ private:
bool _needReProcess;
};
MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByteArray &data, QImage &cover);

View File

@ -944,7 +944,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (doc->loading()) {
_menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true);
} else {
if (doc->mime.toLower() == qstr("video/mp4") && doc->type == AnimatedDocument) {
if (doc->isGifv()) {
_menu->addAction(lang(lng_context_save_gif), this, SLOT(saveContextGif()))->setEnabled(true);
}
if (!doc->already(true).isEmpty()) {
@ -3181,10 +3181,8 @@ void HistoryWidget::savedGifsGot(const MTPmessages_SavedGifs &gifs) {
}
void HistoryWidget::saveGif(DocumentData *doc) {
if (doc->mime.toLower() == qstr("video/mp4") && doc->type == AnimatedDocument) {
if (cSavedGifs().indexOf(doc) != 0) {
MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_bool(false)), rpcDone(&HistoryWidget::saveGifDone, doc));
}
if (doc->isGifv() && cSavedGifs().indexOf(doc) != 0) {
MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_bool(false)), rpcDone(&HistoryWidget::saveGifDone, doc));
}
}
@ -5468,7 +5466,12 @@ namespace {
MTPVector<MTPDocumentAttribute> _composeDocumentAttributes(DocumentData *document) {
QVector<MTPDocumentAttribute> attributes(1, MTP_documentAttributeFilename(MTP_string(document->name)));
if (document->dimensions.width() > 0 && document->dimensions.height() > 0) {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
int32 duration = document->duration();
if (duration >= 0) {
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
} else {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
}
}
if (document->type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());

View File

@ -294,13 +294,12 @@ void FileLoadTask::process() {
MTPDocument document(MTP_documentEmpty(MTP_long(0)));
MTPAudio audio(MTP_audioEmpty(MTP_long(0)));
bool song = false;
bool song = false, gif = false;
if (_type != PrepareAudio) {
if (filemime == qstr("audio/mp3") || filemime == qstr("audio/m4a") || filemime == qstr("audio/aac") || filemime == qstr("audio/ogg") || filemime == qstr("audio/flac") ||
filename.endsWith(qstr(".mp3"), Qt::CaseInsensitive) || filename.endsWith(qstr(".m4a"), Qt::CaseInsensitive) ||
filename.endsWith(qstr(".aac"), Qt::CaseInsensitive) || filename.endsWith(qstr(".ogg"), Qt::CaseInsensitive) ||
filename.endsWith(qstr(".flac"), Qt::CaseInsensitive)) {
QImage cover;
QByteArray coverBytes, coverFormat;
MTPDocumentAttribute audioAttribute = audioReadSongAttributes(_filepath, _content, cover, coverBytes, coverFormat);
@ -327,9 +326,37 @@ void FileLoadTask::process() {
}
}
}
if (filemime == qstr("video/mp4") || filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive)) {
QImage cover;
MTPDocumentAttribute animatedAttribute = clipReadAnimatedAttributes(_filepath, _content, cover);
if (animatedAttribute.type() == mtpc_documentAttributeVideo) {
int32 cw = cover.width(), ch = cover.height();
if (cw < 20 * ch && ch < 20 * cw) {
attributes.push_back(MTP_documentAttributeAnimated());
attributes.push_back(animatedAttribute);
gif = true;
QPixmap full = (cw > 90 || ch > 90) ? QPixmap::fromImage(cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(cover, Qt::ColorOnly);
{
QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87;
QBuffer buffer(&thumbdata);
full.save(&buffer, thumbFormat, thumbQuality);
}
thumb = full;
thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0));
thumbId = MTP::nonce<uint64>();
filemime = qstr("video/mp4");
}
}
}
}
if (!fullimage.isNull() && fullimage.width() > 0 && !song) {
if (!fullimage.isNull() && fullimage.width() > 0 && !song && !gif) {
int32 w = fullimage.width(), h = fullimage.height();
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h)));

View File

@ -552,7 +552,8 @@ namespace {
lskStickers = 0x0b, // no data
lskSavedPeers = 0x0c, // no data
lskReportSpamStatuses = 0x0d, // no data
lskSavedGifs = 0x0e,
lskSavedGifsOld = 0x0e,
lskSavedGifs = 0x0f,
};
typedef QMap<PeerId, FileKey> DraftsMap;
@ -569,7 +570,7 @@ namespace {
FileLocationAliases _fileLocationAliases;
FileKey _locationsKey = 0, _reportSpamStatusesKey = 0;
FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey;
FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey = 0;
FileKey _backgroundKey = 0;
bool _backgroundWasRead = false;
@ -1720,6 +1721,10 @@ namespace {
case lskStickers: {
map.stream >> stickersKey;
} break;
case lskSavedGifsOld: {
quint64 key;
map.stream >> key;
} break;
case lskSavedGifs: {
map.stream >> savedGifsKey;
} break;
@ -2974,8 +2979,8 @@ namespace Local {
for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) {
DocumentData *doc = *i;
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32);
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + duration
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32);
// thumb
size += _storageImageLocationSize();
@ -2991,7 +2996,7 @@ namespace Local {
for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) {
DocumentData *doc = *i;
data.stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type);
data.stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << qint32(doc->duration());
_writeStorageImageLocation(data.stream, doc->thumb->location());
}
FileWriteDescriptor file(_savedGifsKey);
@ -3020,8 +3025,8 @@ namespace Local {
for (uint32 i = 0; i < cnt; ++i) {
quint64 id, access;
QString name, mime;
qint32 date, dc, size, width, height, type;
gifs.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type;
qint32 date, dc, size, width, height, type, duration;
gifs.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> duration;
StorageImageLocation thumb(_readStorageImageLocation(gifs));
@ -3034,7 +3039,11 @@ namespace Local {
attributes.push_back(MTP_documentAttributeAnimated());
}
if (width > 0 && height > 0) {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
if (duration >= 0) {
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(width), MTP_int(height)));
} else {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
}
}
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb);

View File

@ -1490,7 +1490,7 @@ DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 dat
, status(FileReady)
, uploadOffset(0)
, _additional(0)
, _isImage(false)
, _duration(-1)
, _actionOnLoad(ActionOnLoadNone)
, _loader(0) {
_location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id));
@ -1524,7 +1524,7 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
if (type == FileDocument) {
type = VideoDocument;
}
// duration = d.vduration.v;
_duration = d.vduration.v;
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAudio: {
@ -1812,7 +1812,8 @@ bool fileIsImage(const QString &name, const QString &mime) {
}
void DocumentData::recountIsImage() {
_isImage = fileIsImage(name, mime);
if (isAnimation() || type == VideoDocument) return;
_duration = fileIsImage(name, mime) ? 1 : -1; // hack
}
DocumentData::~DocumentData() {

View File

@ -1132,8 +1132,14 @@ public:
bool isAnimation() const {
return (type == AnimatedDocument) || !mime.compare(qstr("image/gif"), Qt::CaseInsensitive);
}
bool isGifv() const {
return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive);
}
int32 duration() const {
return (isAnimation() || type == VideoDocument) ? _duration : -1;
}
bool isImage() const {
return _isImage;
return !isAnimation() && (type != VideoDocument) && (_duration > 0);
}
void recountIsImage();
@ -1159,7 +1165,7 @@ private:
FileLocation _location;
QByteArray _data;
DocumentAdditionalData *_additional;
bool _isImage;
int32 _duration;
ActionOnLoad _actionOnLoad;
FullMsgId _actionOnLoadMsgId;

View File

@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,15,4
PRODUCTVERSION 0,9,15,4
FILEVERSION 0,9,15,5
PRODUCTVERSION 0,9,15,5
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.9.15.4"
VALUE "FileVersion", "0.9.15.5"
VALUE "LegalCopyright", "Copyright (C) 2013"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.15.4"
VALUE "ProductVersion", "0.9.15.5"
END
END
BLOCK "VarFileInfo"

View File

@ -3,4 +3,4 @@ AppVersionStrMajor 0.9
AppVersionStrSmall 0.9.15
AppVersionStr 0.9.15
DevChannel 0
BetaVersion 9015004
BetaVersion 9015005