1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "gifcreator.h"
#include "avconvutil.h"
#include "directoryscanner.h"
#include "filefilterscanner.h"
#include <QDir>
#include <QProcess>
#include <QDebug>
#include "config.h"
GifCreator::GifCreator(GifCreator::Config* config, const AvconvConfig& avconvConfig, QObject *parent) :
ShortClipCreator(config, avconvConfig, parent)
{
}
bool GifCreator::generate() {
return generate(this->config->resolution);
}
bool GifCreator::generate(std::pair<int,int> resolution) {
QString dirPath = QDir::temp().absoluteFilePath(QString().sprintf("gif_%p", this));
QDir dir(dirPath);
dir.removeRecursively();
if (!dir.mkpath(".")) {
qDebug() << "failed to create tmp dir" << dirPath;
return false;
}
float dif = config->endSec - config->startSec;
if (dif > 120.f) {
qDebug() << "gif longer than 120s canceling creation" << config->startSec << config->endSec;
return false;
}
qDebug() << avconfutil::time(config->startSec) << avconfutil::time(dif);
QStringList args = (QStringList() <<
// fast seek
"-ss" <<
avconfutil::time((int)config->startSec) <<
"-i" <<
config->videoPath <<
// accurate seek
"-ss" <<
avconfutil::time(config->startSec-(float)((int)config->startSec)) <<
// relative dif
"-t" <<
avconfutil::time(dif) <<
"-s" <<
avconfutil::resolution(resolution.first, resolution.second) <<
"-f" <<
"image2" <<
"%03d.png");
qDebug() << args;
QProcess process;
process.setWorkingDirectory(dirPath);
process.start(avconvConfig.command, args);
process.waitForFinished(-1);
const GifCreator::Config* gifConfig = dynamic_cast<const GifCreator::Config*>(config);
QList<QFileInfo> tmpFrames = QDir(dirPath).entryInfoList(QStringList() << "*.png", QDir::Files);
QStringList frames;
int iMod = 0;
foreach (QFileInfo fileinfo, tmpFrames) {
if (iMod == 0) {
frames << fileinfo.filePath();
}
++iMod;
if (iMod >= gifConfig->framesDropped+1) {
iMod = 0;
}
}
int fps = (24 / (gifConfig->framesDropped+1)) + ((gifConfig->framesDropped > 0) ? 1 : 0);
QString fpsString = QString("1x%1").arg(fps, 2, 10, QChar('0'));
QProcess imagemagick;
imagemagick.setWorkingDirectory(dirPath);
imagemagick.start("convert",
QStringList() <<
"+dither" <<
"-delay" <<
fpsString <<
//"-resize" <<
//"720x405" <<
frames <<
"-coalesce" <<
"-layers" <<
"OptimizeTransparency" <<
"out.gif"
);
imagemagick.waitForFinished(-1);
QString tmpFilePath = dir.absoluteFilePath("out.gif");
qint64 size = QFile(tmpFilePath).size();
qint64 maxSize = (1024*(1024*config->maxSizeMib));
if (config->maxSizeMib > 0 && size > maxSize) {
float scaleDownFactor = ((double)maxSize / (double)size) + 0.15;
scaleDownFactor = std::min(scaleDownFactor, 0.9f);
std::pair<int,int> lowerResolution = resolution;
lowerResolution.first *= scaleDownFactor;
lowerResolution.second *= scaleDownFactor;
if (lowerResolution.first < 50 || lowerResolution.second < 50) {
qDebug() << "gif is too long generation stopped. won't retry anymore.";
return false;
}
qDebug() << "gif was too big, retrying at resolution" << lowerResolution.first << "x" << lowerResolution.second;
return this->generate(lowerResolution);
}
QDir(QFileInfo(config->outputPath).dir()).mkpath(".");
if (QFile::rename(tmpFilePath, config->outputPath)) {
dir.removeRecursively();
return true;
}
return false;
}
GifCreator::Config::Config() :
ShortClipCreator::Config(),
framesDropped(2)
{
}
void GifCreator::Config::describe(nw::Describer& de) {
ShortClipCreator::Config::describe(de);
NwUtils::describe(de, "resolutionX", resolutionX);
NwUtils::describe(de, "resolutionY", resolutionY);
NwUtils::describe(de, "framesDropped", framesDropped);
}