1 /******************************************************************************
2     QtAV Player Demo:  this file is part of QtAV examples
3     Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV (from 2014)
6 
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 ******************************************************************************/
20 #include "Config.h"
21 #include <QtCore/QSettings>
22 #include <QtCore/QCoreApplication>
23 #include <QtCore/QDir>
24 #include <QtCore/QFile>
25 #include <QtCore/QMetaEnum>
26 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
27 #include <QtGui/QDesktopServices>
28 #else
29 #include <QtCore/QStandardPaths>
30 #endif
31 #include <QtSql/QSqlDatabase>
32 #include <QtSql/QSqlQuery>
33 #include <QtDebug>
34 #include "common.h"
35 
36 class Config::Data
37 {
38 public:
Data()39     Data() {
40         is_loading = false;
41         if (!Data::name.isEmpty())
42             file = appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini");
43         else
44             file = appDataDir() + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini");
45         if (!QDir(appDataDir()).exists()) {
46             if (!QDir().mkpath(appDataDir())) {
47                 qWarning() << "Failed to create appDataDir: " << appDataDir();
48             }
49         }
50         moveOldCfg();
51     }
52 
moveOldCfg()53     void moveOldCfg() {
54         // for old config data migration
55         QString dir_old = qApp->applicationDirPath() + QString::fromLatin1("/data");
56         if (!QDir(dir_old).exists()) {
57             dir_old = QDir::homePath() + QString::fromLatin1("/.QtAV");
58         }
59         if (QDir(dir_old).exists()) {
60             if (!QFile(file).exists()) {
61                 QString old = dir_old + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini");
62                 if (QFile(old).exists()) {
63                     QFile::copy(old, file);
64                     QFile::remove(old);
65                 }
66                 old = dir_old + QString::fromLatin1("/playlist.qds");
67                 if (QFile(old).exists()) {
68                     if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/playlist.qds")))
69                         qWarning("error to move old playlist data");
70                     QFile::remove(old);
71                 }
72                 old = dir_old + QString::fromLatin1("/history.qds");
73                 if (QFile(old).exists()) {
74                     if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/history.qds")))
75                         qWarning("error to move old history data");
76                     QFile::remove(old);
77                 }
78             }
79         }
80     }
81 
save()82     void save() {
83         if (is_loading)
84             return;
85         qDebug() << "sync config to " << file;
86         QSettings settings(file, QSettings::IniFormat);
87         // TODO: why crash on mac qt5.4 if call on aboutToQuit()
88         settings.setValue(QString::fromLatin1("log"), log);
89         settings.setValue(QString::fromLatin1("language"), lang);
90         settings.setValue(QString::fromLatin1("last_file"), last_file);
91         settings.setValue(QString::fromLatin1("timeout"), timeout);
92         settings.setValue(QString::fromLatin1("abort_timeout"), abort_timeout);
93         settings.setValue(QString::fromLatin1("force_fps"), force_fps);
94         settings.beginGroup(QString::fromLatin1("decoder"));
95         settings.beginGroup(QString::fromLatin1("video"));
96         settings.setValue(QString::fromLatin1("priority"), video_decoders.join(QString::fromLatin1(" ")));
97         settings.endGroup();
98         settings.endGroup();
99         settings.beginGroup(QString::fromLatin1("capture"));
100         settings.setValue(QString::fromLatin1("zeroCopy"), zero_copy);
101         settings.setValue(QString::fromLatin1("dir"), capture_dir);
102         settings.setValue(QString::fromLatin1("format"), capture_fmt);
103         settings.setValue(QString::fromLatin1("quality"), capture_quality);
104         settings.endGroup();
105         settings.beginGroup(QString::fromLatin1("subtitle"));
106         settings.setValue(QString::fromLatin1("enabled"), subtitle_enabled);
107         settings.setValue(QString::fromLatin1("autoLoad"), subtitle_autoload);
108         settings.setValue(QString::fromLatin1("engines"), subtitle_engines);
109         settings.setValue(QString::fromLatin1("delay"), subtitle_delay);
110         settings.setValue(QString::fromLatin1("font"), subtitle_font);
111         settings.setValue(QString::fromLatin1("color"), subtitle_color);
112         settings.setValue(QString::fromLatin1("outline_color"), subtitle_outline_color);
113         settings.setValue(QString::fromLatin1("outline"), subtitle_outline);
114         settings.setValue(QString::fromLatin1("bottom margin"), subtilte_bottom_margin);
115         settings.beginGroup(QString::fromLatin1("ass"));
116         settings.setValue(QString::fromLatin1("font_file"), ass_font_file);
117         settings.setValue(QString::fromLatin1("force_font_file"), ass_force_font_file);
118         settings.setValue(QString::fromLatin1("fonts_dir"), ass_fonts_dir);
119         settings.endGroup();
120         settings.endGroup();
121         settings.beginGroup(QString::fromLatin1("preview"));
122         settings.setValue(QString::fromLatin1("enabled"), preview_enabled);
123         settings.setValue(QString::fromLatin1("width"), preview_w);
124         settings.setValue(QString::fromLatin1("height"), preview_h);
125         settings.endGroup();
126         settings.beginGroup(QString::fromLatin1("avformat"));
127         settings.setValue(QString::fromLatin1("enable"), avformat_on);
128         settings.setValue(QString::fromLatin1("avioflags"), direct ? QString::fromLatin1("direct") : QString::fromLatin1("0"));
129         settings.setValue(QString::fromLatin1("probesize"), probe_size);
130         settings.setValue(QString::fromLatin1("analyzeduration"), analyze_duration);
131         settings.setValue(QString::fromLatin1("extra"), avformat_extra);
132         settings.endGroup();
133         settings.beginGroup(QString::fromLatin1("avfilterVideo"));
134         settings.setValue(QString::fromLatin1("enable"), avfilterVideo_on);
135         settings.setValue(QString::fromLatin1("options"), avfilterVideo);
136         settings.endGroup();
137         settings.beginGroup(QString::fromLatin1("avfilterAudio"));
138         settings.setValue(QString::fromLatin1("enable"), avfilterAudio_on);
139         settings.setValue(QString::fromLatin1("options"), avfilterAudio);
140         settings.endGroup();
141         settings.beginGroup(QString::fromLatin1("opengl"));
142         settings.setValue(QString::fromLatin1("egl"), egl);
143         const char* glname = Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).valueToKey(opengl);
144         settings.setValue(QString::fromLatin1("type"), QString::fromLatin1(glname));
145         settings.setValue(QString::fromLatin1("angle_platform"), angle_dx);
146         settings.endGroup();
147         settings.beginGroup(QString::fromLatin1("shader"));
148         settings.setValue(QString::fromLatin1("enable"), user_shader);
149         settings.setValue(QString::fromLatin1("fbo"), fbo);
150         settings.setValue(QString::fromLatin1("fragHeader"), frag_header);
151         settings.setValue(QString::fromLatin1("fragSample"), frag_sample);
152         settings.setValue(QString::fromLatin1("fragPostProcess"), frag_pp);
153         settings.endGroup();
154         settings.beginGroup(QString::fromLatin1("buffer"));
155         settings.setValue(QString::fromLatin1("value"), buffer_value);
156         settings.endGroup();
157         qDebug() << "sync end";
158     }
159 
160     QString file;
161     bool is_loading;
162 
163     qreal force_fps;
164     QStringList video_decoders;
165     bool zero_copy;
166 
167     QString last_file;
168 
169     QString capture_dir;
170     QString capture_fmt;
171     int capture_quality;
172 
173     bool avformat_on;
174     bool direct;
175     unsigned int probe_size;
176     int analyze_duration;
177     QString avformat_extra;
178     bool avfilterVideo_on;
179     QString avfilterVideo;
180     bool avfilterAudio_on;
181     QString avfilterAudio;
182 
183     QStringList subtitle_engines;
184     bool subtitle_autoload;
185     bool subtitle_enabled;
186     QFont subtitle_font;
187     QColor subtitle_color, subtitle_outline_color;
188     bool subtitle_outline;
189     int subtilte_bottom_margin;
190     qreal subtitle_delay;
191 
192     bool ass_force_font_file;
193     QString ass_font_file;
194     QString ass_fonts_dir;
195 
196     bool preview_enabled;
197     int preview_w, preview_h;
198 
199     bool egl;
200     Config::OpenGLType opengl;
201     QString angle_dx;
202     bool abort_timeout;
203     qreal timeout;
204     int buffer_value;
205     QString log;
206     QString lang;
207 
208     QVariantList history;
209 
210     bool user_shader;
211     bool fbo;
212     QString frag_header;
213     QString frag_sample;
214     QString frag_pp;
215 
216     static QString name;
217 };
218 
219 QString Config::Data::name;
220 
instance()221 Config& Config::instance()
222 {
223     static Config cfg;
224     return cfg;
225 }
226 
setName(const QString & name)227 void Config::setName(const QString &name)
228 {
229     Config::Data::name = name;
230 }
231 
getName()232 QString Config::getName()
233 {
234     return Config::Data::name;
235 }
236 
defaultConfigFile()237 QString Config::defaultConfigFile()
238 {
239     return appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini");;
240 }
241 
Config(QObject * parent)242 Config::Config(QObject *parent)
243     : QObject(parent)
244     , mpData(new Data())
245 {
246     // DO NOT call save() in dtor because it's a singleton and may be deleted later than qApp, QFont is not valid
247     connect(qApp, SIGNAL(aboutToQuit()), SLOT(save())); //FIXME: what if qapp not ready
248     reload();
249 }
250 
~Config()251 Config::~Config()
252 {
253     delete mpData;
254 }
255 
defaultDir()256 QString Config::defaultDir()
257 {
258     return appDataDir();
259 }
260 
reset()261 bool Config::reset()
262 {
263     QFile cf(mpData->file);
264     if (!cf.remove()) {
265         qWarning() << "Failed to remove config file: " << cf.errorString();
266         return false;
267     }
268     reload();
269     save();
270     return true;
271 }
272 
reload()273 void Config::reload()
274 {
275     QSqlDatabase db(QSqlDatabase::database());
276     if (!db.isOpen()) {
277         db = QSqlDatabase::addDatabase(QString::fromUtf8("QSQLITE"));
278         db.setDatabaseName(appDataDir().append(QString("/%1.db").arg(mpData->name)));
279         if (!db.open())
280             qWarning("error open db");
281         db.exec("CREATE TABLE IF NOT EXISTS history (url TEXT primary key, start BIGINT, duration BIGINT)");
282     }
283     QSqlQuery query(db.exec(QString::fromUtf8("SELECT * FROM history")));
284     while (query.next()) {
285         QVariantMap var;
286         var[QString::fromUtf8("url")] = query.value(0).toString();
287         var[QString::fromUtf8("start")] = query.value(1).toLongLong();
288         var[QString::fromUtf8("duration")] = query.value(2).toLongLong();
289         mpData->history.append(var);
290     }
291     mpData->is_loading = true;
292     QSettings settings(mpData->file, QSettings::IniFormat);
293     setLogLevel(settings.value(QString::fromLatin1("log"), QString()).toString());
294     setLanguage(settings.value(QString::fromLatin1("language"),
295 #if QT_VERSION == QT_VERSION_CHECK(5, 6, 0) && defined(Q_OS_WINPHONE) //qt bug
296                                QString::fromLatin1("en_US")
297 #else
298                                QString::fromLatin1("system")
299 #endif
300                                ).toString());
301     setLastFile(settings.value(QString::fromLatin1("last_file"), QString()).toString());
302     setTimeout(settings.value(QString::fromLatin1("timeout"), 30.0).toReal());
303     setAbortOnTimeout(settings.value(QString::fromLatin1("abort_timeout"), true).toBool());
304     setForceFrameRate(settings.value(QString::fromLatin1("force_fps"), 0.0).toReal());
305     settings.beginGroup(QString::fromLatin1("decoder"));
306     settings.beginGroup(QString::fromLatin1("video"));
307     QString decs_default(QString::fromLatin1("HW FFmpeg")); // HW is ignored
308     setDecoderPriorityNames(settings.value(QString::fromLatin1("priority"), decs_default).toString().split(QString::fromLatin1(" "), QString::SkipEmptyParts));
309     setZeroCopy(settings.value(QString::fromLatin1("zeroCopy"), true).toBool());
310     settings.endGroup(); //video
311     settings.endGroup(); //decoder
312 
313     settings.beginGroup(QString::fromLatin1("capture"));
314     setCaptureDir(settings.value(QString::fromLatin1("dir"), QString()).toString());
315     if (captureDir().isEmpty()) {
316 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
317         setCaptureDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
318 #else
319         setCaptureDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
320 #endif
321     }
322     setCaptureFormat(settings.value(QString::fromLatin1("format"), QString::fromLatin1("png")).toString());
323     setCaptureQuality(settings.value(QString::fromLatin1("quality"), 100).toInt());
324     settings.endGroup();
325     settings.beginGroup(QString::fromLatin1("subtitle"));
326     setSubtitleAutoLoad(settings.value(QString::fromLatin1("autoLoad"), true).toBool());
327     setSubtitleEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool());
328     setSubtitleEngines(settings.value(QString::fromLatin1("engines"), QStringList() << QString::fromLatin1("FFmpeg") << QString::fromLatin1("LibASS")).toStringList());
329     setSubtitleDelay(settings.value(QString::fromLatin1("delay"), 0.0).toInt());
330     QFont f;
331     f.setPointSize(20);
332     f.setBold(true);
333     setSubtitleFont(settings.value(QString::fromLatin1("font"), f).value<QFont>());
334     setSubtitleColor(settings.value(QString::fromLatin1("color"), QColor("white")).value<QColor>());
335     setSubtitleOutlineColor(settings.value(QString::fromLatin1("outline_color"), QColor("blue")).value<QColor>());
336     setSubtitleOutline(settings.value(QString::fromLatin1("outline"), true).toBool());
337     setSubtitleBottomMargin(settings.value(QString::fromLatin1("bottom margin"), 8).toInt());
338     settings.beginGroup(QString::fromLatin1("ass"));
339     setAssFontFile(settings.value(QString::fromLatin1("font_file"), QString()).toString());
340     setAssFontFileForced(settings.value(QString::fromLatin1("force_font_file"), false).toBool());
341     setAssFontsDir(settings.value(QString::fromLatin1("fonts_dir"), QString()).toString());
342     settings.endGroup();
343     settings.endGroup();
344     settings.beginGroup(QString::fromLatin1("preview"));
345     setPreviewEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool());
346     setPreviewWidth(settings.value(QString::fromLatin1("width"), 160).toInt());
347     setPreviewHeight(settings.value(QString::fromLatin1("height"), 90).toInt());
348     settings.endGroup();
349     settings.beginGroup(QString::fromLatin1("avformat"));
350     setAvformatOptionsEnabled(settings.value(QString::fromLatin1("enable"), false).toBool());
351     reduceBuffering(settings.value(QString::fromLatin1("avioflags"), 0).toString() == QLatin1String("direct"));
352     probeSize(settings.value(QString::fromLatin1("probesize"), 5000000).toUInt());
353     analyzeDuration(settings.value(QString::fromLatin1("analyzeduration"), 5000000).toInt());
354     avformatExtra(settings.value(QString::fromLatin1("extra"), QString()).toString());
355     settings.endGroup();
356     settings.beginGroup(QString::fromLatin1("avfilterVideo"));
357     avfilterVideoEnable(settings.value(QString::fromLatin1("enable"), true).toBool());
358     avfilterVideoOptions(settings.value(QString::fromLatin1("options"), QString()).toString());
359     settings.endGroup();
360     settings.beginGroup(QString::fromLatin1("avfilterAudio"));
361     avfilterAudioEnable(settings.value(QString::fromLatin1("enable"), true).toBool());
362     avfilterAudioOptions(settings.value(QString::fromLatin1("options"), QString()).toString());
363     settings.endGroup();
364 
365     settings.beginGroup(QString::fromLatin1("opengl"));
366     setEGL(settings.value(QString::fromLatin1("egl"), false).toBool());
367     const QString glname = settings.value(QString::fromLatin1("type"), QString::fromLatin1("OpenGLES")).toString();
368     setOpenGLType((Config::OpenGLType)Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).keysToValue(glname.toLatin1().constData()));
369     // d3d11 bad performance (gltexsubimage2d)
370     setANGLEPlatform(settings.value(QString::fromLatin1("angle_platform"), QString::fromLatin1("d3d9")).toString());
371     settings.endGroup();
372 
373     settings.beginGroup(QString::fromLatin1("shader"));
374     setUserShaderEnabled(settings.value(QString::fromLatin1("enable"), false).toBool());
375     setIntermediateFBO(settings.value(QString::fromLatin1("fbo"), false).toBool());
376     setFragHeader(settings.value(QString::fromLatin1("fragHeader"), QString()).toString());
377     setFragSample(settings.value(QString::fromLatin1("fragSample"), QString::fromLatin1("// horizontal mirror effect\n"
378                                                                                         "vec4 sample2d(sampler2D tex, vec2 pos, int p) {\n"
379                                                                                         "    return texture(tex, vec2(1.0-pos.x, pos.y));\n"
380                                                                                         "}")).toString());
381     setFragPostProcess(settings.value(QString::fromLatin1("fragPostProcess"), QString::fromLatin1("//negate color effect\n"
382                                                                                                   "gl_FragColor.rgb = vec3(1.0-gl_FragColor.r, 1.0-gl_FragColor.g, 1.0-gl_FragColor.b);")).toString());
383     settings.endGroup();
384 
385     settings.beginGroup(QString::fromLatin1("buffer"));
386     setBufferValue(settings.value(QString::fromLatin1("value"), -1).toInt());
387     settings.endGroup();
388     mpData->is_loading = false;
389 }
390 
forceFrameRate() const391 qreal Config::forceFrameRate() const
392 {
393     return mpData->force_fps;
394 }
395 
setForceFrameRate(qreal value)396 Config& Config::setForceFrameRate(qreal value)
397 {
398     if (mpData->force_fps == value)
399         return *this;
400     mpData->force_fps = value;
401     Q_EMIT forceFrameRateChanged();
402     Q_EMIT changed();
403     return *this;
404 }
405 
decoderPriorityNames() const406 QStringList Config::decoderPriorityNames() const
407 {
408     return mpData->video_decoders;
409 }
410 
setDecoderPriorityNames(const QStringList & value)411 Config& Config::setDecoderPriorityNames(const QStringList &value)
412 {
413     if (mpData->video_decoders == value) {
414         qDebug("decoderPriority not changed");
415         return *this;
416     }
417     mpData->video_decoders = value;
418     Q_EMIT decoderPriorityNamesChanged();
419     Q_EMIT changed();
420     mpData->save();
421     return *this;
422 }
423 
zeroCopy() const424 bool Config::zeroCopy() const
425 {
426     return mpData->zero_copy;
427 }
428 
setZeroCopy(bool value)429 Config& Config::setZeroCopy(bool value)
430 {
431     if (mpData->zero_copy == value)
432         return *this;
433     mpData->zero_copy = value;
434     Q_EMIT zeroCopyChanged();
435     Q_EMIT changed();
436     mpData->save();
437     return *this;
438 }
439 
captureDir() const440 QString Config::captureDir() const
441 {
442     return mpData->capture_dir;
443 }
444 
setCaptureDir(const QString & dir)445 Config& Config::setCaptureDir(const QString& dir)
446 {
447     if (mpData->capture_dir == dir)
448         return *this;
449     mpData->capture_dir = dir;
450     Q_EMIT captureDirChanged(dir);
451     Q_EMIT changed();
452     return *this;
453 }
454 
captureFormat() const455 QString Config::captureFormat() const
456 {
457     return mpData->capture_fmt;
458 }
459 
setCaptureFormat(const QString & format)460 Config& Config::setCaptureFormat(const QString& format)
461 {
462     if (mpData->capture_fmt == format)
463         return *this;
464     mpData->capture_fmt = format;
465     Q_EMIT captureFormatChanged(format);
466     Q_EMIT changed();
467     return *this;
468 }
469 
470 // only works for non-yuv capture
captureQuality() const471 int Config::captureQuality() const
472 {
473     return mpData->capture_quality;
474 }
475 
setCaptureQuality(int quality)476 Config& Config::setCaptureQuality(int quality)
477 {
478     if (mpData->capture_quality == quality)
479         return *this;
480     mpData->capture_quality = quality;
481     Q_EMIT captureQualityChanged(quality);
482     Q_EMIT changed();
483     return *this;
484 }
485 
subtitleEngines() const486 QStringList Config::subtitleEngines() const
487 {
488     return mpData->subtitle_engines;
489 }
490 
setSubtitleEngines(const QStringList & value)491 Config& Config::setSubtitleEngines(const QStringList &value)
492 {
493     if (mpData->subtitle_engines == value)
494         return *this;
495     mpData->subtitle_engines = value;
496     Q_EMIT subtitleEnginesChanged();
497     Q_EMIT changed();
498     return *this;
499 }
500 
subtitleAutoLoad() const501 bool Config::subtitleAutoLoad() const
502 {
503     return mpData->subtitle_autoload;
504 }
505 
setSubtitleAutoLoad(bool value)506 Config& Config::setSubtitleAutoLoad(bool value)
507 {
508     if (mpData->subtitle_autoload == value)
509         return *this;
510     mpData->subtitle_autoload = value;
511     Q_EMIT subtitleAutoLoadChanged();
512     Q_EMIT changed();
513     return *this;
514 }
515 
subtitleEnabled() const516 bool Config::subtitleEnabled() const
517 {
518     return mpData->subtitle_enabled;
519 }
520 
setSubtitleEnabled(bool value)521 Config& Config::setSubtitleEnabled(bool value)
522 {
523     if (mpData->subtitle_enabled == value)
524         return *this;
525     mpData->subtitle_enabled = value;
526     Q_EMIT subtitleEnabledChanged();
527     Q_EMIT changed();
528     return *this;
529 }
530 
subtitleFont() const531 QFont Config::subtitleFont() const
532 {
533     return mpData->subtitle_font;
534 }
535 
setSubtitleFont(const QFont & value)536 Config& Config::setSubtitleFont(const QFont& value)
537 {
538     if (mpData->subtitle_font == value)
539         return *this;
540     mpData->subtitle_font = value;
541     Q_EMIT subtitleFontChanged();
542     Q_EMIT changed();
543     return *this;
544 }
545 
subtitleOutline() const546 bool Config::subtitleOutline() const
547 {
548     return mpData->subtitle_outline;
549 }
setSubtitleOutline(bool value)550 Config& Config::setSubtitleOutline(bool value)
551 {
552     if (mpData->subtitle_outline == value)
553         return *this;
554     mpData->subtitle_outline = value;
555     Q_EMIT subtitleOutlineChanged();
556     Q_EMIT changed();
557     return *this;
558 }
559 
subtitleColor() const560 QColor Config::subtitleColor() const
561 {
562     return mpData->subtitle_color;
563 }
564 
setSubtitleColor(const QColor & value)565 Config& Config::setSubtitleColor(const QColor& value)
566 {
567     if (mpData->subtitle_color == value)
568         return *this;
569     mpData->subtitle_color = value;
570     Q_EMIT subtitleColorChanged();
571     Q_EMIT changed();
572     return *this;
573 }
574 
subtitleOutlineColor() const575 QColor Config::subtitleOutlineColor() const
576 {
577     return mpData->subtitle_outline_color;
578 }
setSubtitleOutlineColor(const QColor & value)579 Config& Config::setSubtitleOutlineColor(const QColor& value)
580 {
581     if (mpData->subtitle_outline_color == value)
582         return *this;
583     mpData->subtitle_outline_color = value;
584     Q_EMIT subtitleOutlineColorChanged();
585     Q_EMIT changed();
586     return *this;
587 }
588 
subtitleBottomMargin() const589 int Config::subtitleBottomMargin() const
590 {
591     return mpData->subtilte_bottom_margin;
592 }
593 
setSubtitleBottomMargin(int value)594 Config& Config::setSubtitleBottomMargin(int value)
595 {
596     if (mpData->subtilte_bottom_margin == value)
597         return *this;
598     mpData->subtilte_bottom_margin = value;
599     Q_EMIT subtitleBottomMarginChanged();
600     Q_EMIT changed();
601     return *this;
602 }
603 
subtitleDelay() const604 qreal Config::subtitleDelay() const
605 {
606     return mpData->subtitle_delay;
607 }
608 
setSubtitleDelay(qreal value)609 Config& Config::setSubtitleDelay(qreal value)
610 {
611     if (mpData->subtitle_delay == value)
612         return *this;
613     mpData->subtitle_delay = value;
614     Q_EMIT subtitleDelayChanged();
615     Q_EMIT changed();
616     return *this;
617 }
618 
assFontFile() const619 QString Config::assFontFile() const
620 {
621     return mpData->ass_font_file;
622 }
623 
setAssFontFile(const QString & value)624 Config& Config::setAssFontFile(const QString &value)
625 {
626     if (mpData->ass_font_file == value)
627         return *this;
628     mpData->ass_font_file = value;
629     Q_EMIT assFontFileChanged();
630     Q_EMIT changed();
631     return *this;
632 }
633 
634 
assFontsDir() const635 QString Config::assFontsDir() const
636 {
637     return mpData->ass_fonts_dir;
638 }
639 
setAssFontsDir(const QString & value)640 Config& Config::setAssFontsDir(const QString &value)
641 {
642     if (mpData->ass_fonts_dir == value)
643         return *this;
644     mpData->ass_fonts_dir = value;
645     Q_EMIT assFontsDirChanged();
646     Q_EMIT changed();
647     return *this;
648 }
649 
isAssFontFileForced() const650 bool Config::isAssFontFileForced() const
651 {
652     return mpData->ass_force_font_file;
653 }
654 
setAssFontFileForced(bool value)655 Config& Config::setAssFontFileForced(bool value)
656 {
657     if (mpData->ass_force_font_file == value)
658         return *this;
659     mpData->ass_force_font_file = value;
660     Q_EMIT assFontFileForcedChanged();
661     Q_EMIT changed();
662     return *this;
663 }
664 
previewEnabled() const665 bool Config::previewEnabled() const
666 {
667     return mpData->preview_enabled;
668 }
669 
setPreviewEnabled(bool value)670 Config& Config::setPreviewEnabled(bool value)
671 {
672     if (mpData->preview_enabled == value)
673         return *this;
674     mpData->preview_enabled = value;
675     Q_EMIT previewEnabledChanged();
676     Q_EMIT changed();
677     return *this;
678 }
679 
previewWidth() const680 int Config::previewWidth() const
681 {
682     return mpData->preview_w;
683 }
684 
setPreviewWidth(int value)685 Config& Config::setPreviewWidth(int value)
686 {
687     if (mpData->preview_w == value)
688         return *this;
689     mpData->preview_w = value;
690     Q_EMIT previewWidthChanged();
691     Q_EMIT changed();
692     return *this;
693 }
694 
previewHeight() const695 int Config::previewHeight() const
696 {
697     return mpData->preview_h;
698 }
699 
setPreviewHeight(int value)700 Config& Config::setPreviewHeight(int value)
701 {
702     if (mpData->preview_h == value)
703         return *this;
704     mpData->preview_h = value;
705     Q_EMIT previewHeightChanged();
706     Q_EMIT changed();
707     return *this;
708 }
avformatOptions() const709 QVariantHash Config::avformatOptions() const
710 {
711     QVariantHash vh;
712     if (!mpData->avformat_extra.isEmpty()) {
713         QStringList s(mpData->avformat_extra.split(QString::fromLatin1(" ")));
714         for (int i = 0; i < s.size(); ++i) {
715             int eq = s[i].indexOf(QLatin1String("="));
716             if (eq < 0) {
717                 continue;
718             } else {
719                 vh[s[i].mid(0, eq)] = s[i].mid(eq+1);
720             }
721         }
722     }
723     if (mpData->probe_size > 0) {
724         vh[QString::fromLatin1("probesize")] = mpData->probe_size;
725     }
726     if (mpData->analyze_duration) {
727         vh[QString::fromLatin1("analyzeduration")] = mpData->analyze_duration;
728     }
729     if (mpData->direct) {
730         vh[QString::fromLatin1("avioflags")] = QString::fromLatin1("direct");
731     };
732     return vh;
733 }
734 
avformatOptionsEnabled() const735 bool Config::avformatOptionsEnabled() const
736 {
737     return mpData->avformat_on;
738 }
739 
setAvformatOptionsEnabled(bool value)740 Config& Config::setAvformatOptionsEnabled(bool value)
741 {
742     if (mpData->avformat_on == value)
743         return *this;
744     mpData->avformat_on = value;
745     Q_EMIT avformatOptionsEnabledChanged();
746     Q_EMIT changed();
747     return *this;
748 }
749 
probeSize() const750 unsigned int Config::probeSize() const
751 {
752     return mpData->probe_size;
753 }
754 
probeSize(unsigned int ps)755 Config& Config::probeSize(unsigned int ps)
756 {
757     mpData->probe_size = ps;
758     return *this;
759 }
760 
analyzeDuration() const761 int Config::analyzeDuration() const
762 {
763     return mpData->analyze_duration;
764 }
765 
analyzeDuration(int ad)766 Config& Config::analyzeDuration(int ad)
767 {
768     mpData->analyze_duration = ad;
769     return *this;
770 }
771 
reduceBuffering() const772 bool Config::reduceBuffering() const
773 {
774     return mpData->direct;
775 }
776 
reduceBuffering(bool y)777 Config& Config::reduceBuffering(bool y)
778 {
779     mpData->direct = y;
780     return *this;
781 }
782 
avformatExtra() const783 QString Config::avformatExtra() const
784 {
785     return mpData->avformat_extra;
786 }
787 
avformatExtra(const QString & text)788 Config& Config::avformatExtra(const QString &text)
789 {
790     mpData->avformat_extra = text;
791     return *this;
792 }
793 
avfilterVideoOptions() const794 QString Config::avfilterVideoOptions() const
795 {
796     return mpData->avfilterVideo;
797 }
798 
avfilterVideoOptions(const QString & options)799 Config& Config::avfilterVideoOptions(const QString& options)
800 {
801     if (mpData->avfilterVideo == options)
802         return *this;
803     mpData->avfilterVideo = options;
804     Q_EMIT avfilterVideoChanged();
805     Q_EMIT changed();
806     return *this;
807 }
808 
avfilterVideoEnable() const809 bool Config::avfilterVideoEnable() const
810 {
811     return mpData->avfilterVideo_on;
812 }
813 
avfilterVideoEnable(bool e)814 Config& Config::avfilterVideoEnable(bool e)
815 {
816     if (mpData->avfilterVideo_on == e)
817         return *this;
818     mpData->avfilterVideo_on = e;
819     Q_EMIT avfilterVideoChanged();
820     Q_EMIT changed();
821     return *this;
822 }
823 
avfilterAudioOptions() const824 QString Config::avfilterAudioOptions() const
825 {
826     return mpData->avfilterAudio;
827 }
828 
avfilterAudioOptions(const QString & options)829 Config& Config::avfilterAudioOptions(const QString& options)
830 {
831     if (mpData->avfilterAudio == options)
832         return *this;
833     mpData->avfilterAudio = options;
834     Q_EMIT avfilterAudioChanged();
835     Q_EMIT changed();
836     return *this;
837 }
838 
avfilterAudioEnable() const839 bool Config::avfilterAudioEnable() const
840 {
841     return mpData->avfilterAudio_on;
842 }
843 
avfilterAudioEnable(bool e)844 Config& Config::avfilterAudioEnable(bool e)
845 {
846     if (mpData->avfilterAudio_on == e)
847         return *this;
848     mpData->avfilterAudio_on = e;
849     Q_EMIT avfilterAudioChanged();
850     Q_EMIT changed();
851     return *this;
852 }
853 
isEGL() const854 bool Config::isEGL() const
855 {
856     return mpData->egl;
857 }
858 
setEGL(bool value)859 Config& Config::setEGL(bool value)
860 {
861     if (mpData->egl == value)
862         return *this;
863     mpData->egl = value;
864     Q_EMIT EGLChanged();
865     Q_EMIT changed();
866     return *this;
867 }
868 
openGLType() const869 Config::OpenGLType Config::openGLType() const
870 {
871     return mpData->opengl;
872 }
873 
setOpenGLType(OpenGLType value)874 Config& Config::setOpenGLType(OpenGLType value)
875 {
876     if (mpData->opengl == value)
877         return *this;
878     mpData->opengl = value;
879     Q_EMIT openGLTypeChanged();
880     Q_EMIT changed();
881     return *this;
882 }
883 
getANGLEPlatform() const884 QString Config::getANGLEPlatform() const
885 {
886     return mpData->angle_dx;
887 }
888 
setANGLEPlatform(const QString & value)889 Config& Config::setANGLEPlatform(const QString& value)
890 {
891     if (mpData->angle_dx == value)
892         return *this;
893     mpData->angle_dx = value;
894     Q_EMIT ANGLEPlatformChanged();
895     Q_EMIT changed();
896     return *this;
897 }
898 
userShaderEnabled() const899 bool Config::userShaderEnabled() const
900 {
901     return mpData->user_shader;
902 }
903 
setUserShaderEnabled(bool value)904 Config& Config::setUserShaderEnabled(bool value)
905 {
906     if (mpData->user_shader == value)
907         return *this;
908     mpData->user_shader = value;
909     Q_EMIT userShaderEnabledChanged();
910     Q_EMIT changed();
911     return *this;
912 }
913 
intermediateFBO() const914 bool Config::intermediateFBO() const
915 {
916     return mpData->fbo;
917 }
918 
setIntermediateFBO(bool value)919 Config& Config::setIntermediateFBO(bool value)
920 {
921     if (mpData->fbo == value)
922         return *this;
923     mpData->fbo = value;
924     Q_EMIT intermediateFBOChanged();
925     Q_EMIT changed();
926     return *this;
927 }
928 
fragHeader() const929 QString Config::fragHeader() const
930 {
931     return mpData->frag_header;
932 }
933 
setFragHeader(const QString & text)934 Config& Config::setFragHeader(const QString &text)
935 {
936     if (mpData->frag_header == text)
937         return *this;
938     mpData->frag_header = text;
939     Q_EMIT fragHeaderChanged();
940     Q_EMIT changed();
941     return *this;
942 }
943 
fragSample() const944 QString Config::fragSample() const
945 {
946     return mpData->frag_sample;
947 }
948 
setFragSample(const QString & text)949 Config& Config::setFragSample(const QString &text)
950 {
951     if (mpData->frag_sample == text)
952         return *this;
953     mpData->frag_sample = text;
954     Q_EMIT fragSampleChanged();
955     Q_EMIT changed();
956     return *this;
957 }
958 
fragPostProcess() const959 QString Config::fragPostProcess() const
960 {
961     return mpData->frag_pp;
962 }
963 
setFragPostProcess(const QString & text)964 Config& Config::setFragPostProcess(const QString &text)
965 {
966     if (mpData->frag_pp == text)
967         return *this;
968     mpData->frag_pp = text;
969     Q_EMIT fragPostProcessChanged();
970     Q_EMIT changed();
971     return *this;
972 }
973 
bufferValue() const974 int Config::bufferValue() const
975 {
976     return mpData->buffer_value;
977 }
978 
setBufferValue(int value)979 Config& Config::setBufferValue(int value)
980 {
981     if (mpData->buffer_value == value)
982         return *this;
983     mpData->buffer_value = value;
984     Q_EMIT bufferValueChanged();
985     Q_EMIT changed();
986     return *this;
987 }
988 
timeout() const989 qreal Config::timeout() const
990 {
991     return mpData->timeout;
992 }
993 
setTimeout(qreal value)994 Config& Config::setTimeout(qreal value)
995 {
996     if (mpData->timeout == value)
997         return *this;
998     mpData->timeout = value;
999     Q_EMIT timeoutChanged();
1000     Q_EMIT changed();
1001     return *this;
1002 }
1003 
logLevel() const1004 QString Config::logLevel() const
1005 {
1006     return mpData->log;
1007 }
1008 
setLogLevel(const QString & value)1009 Config& Config::setLogLevel(const QString& value)
1010 {
1011     if (mpData->log == value.toLower())
1012         return *this;
1013     mpData->log = value.toLower();
1014     Q_EMIT logLevelChanged();
1015     Q_EMIT changed();
1016     return *this;
1017 }
1018 
language() const1019 QString Config::language() const
1020 {
1021     return mpData->lang;
1022 }
1023 
setLanguage(const QString & value)1024 Config& Config::setLanguage(const QString& value)
1025 {
1026     if (mpData->lang == value)
1027         return *this;
1028     mpData->lang = value;
1029     Q_EMIT languageChanged();
1030     Q_EMIT changed();
1031     return *this;
1032 }
1033 
history() const1034 QVariantList Config::history() const
1035 {
1036     return mpData->history;
1037 }
1038 
addHistory(const QVariantMap & value)1039 void Config::addHistory(const QVariantMap &value)
1040 {
1041     mpData->history.prepend(value);
1042     Q_EMIT historyChanged();
1043     QSqlDatabase db = QSqlDatabase::database();
1044     QSqlQuery query(db);
1045     if (!query.prepare(QString::fromUtf8("INSERT INTO history (url, start, duration) "
1046                               "VALUES (:url, :start, :duration)"))) {
1047             qWarning("error prepare sql query");
1048     }
1049     query.bindValue(QString::fromUtf8(":url"), value.value("url").toString());
1050     query.bindValue(QString::fromUtf8(":start"), value.value("start").toLongLong());
1051     query.bindValue(QString::fromUtf8(":duration"), value.value("duration").toLongLong());
1052     if (!query.exec())
1053         qWarning("failed to add history: %d", db.isOpen());
1054 }
1055 
removeHistory(const QString & url)1056 void Config::removeHistory(const QString &url)
1057 {
1058     QVariantList::Iterator it = mpData->history.begin();
1059     bool change = false;
1060     while (it != mpData->history.end()) {
1061         if (it->toMap().value("url") != url) {
1062             ++it;
1063             continue;
1064         }
1065         it = mpData->history.erase(it);
1066         change = true;
1067     }
1068     if (!change)
1069         return;
1070     Q_EMIT historyChanged();
1071     QSqlDatabase db = QSqlDatabase::database();
1072     QSqlQuery query(db);
1073     query.prepare(QString::fromUtf8("DELETE FROM history WHERE url = :url"));
1074     query.bindValue(QString::fromUtf8(":url"), url);
1075     if (!query.exec())
1076         qWarning("failed to remove history");
1077 }
1078 
clearHistory()1079 void Config::clearHistory()
1080 {
1081     if (mpData->history.isEmpty())
1082         return;
1083     mpData->history.clear();
1084     Q_EMIT historyChanged();
1085     QSqlDatabase db = QSqlDatabase::database();
1086     QSqlQuery query(db);
1087     query.prepare(QString::fromUtf8("DELETE FROM history"));
1088     // 'TRUNCATE table history' is faster
1089     if (!query.exec())
1090         qWarning("failed to clear history");
1091 }
1092 
abortOnTimeout() const1093 bool Config::abortOnTimeout() const
1094 {
1095     return mpData->abort_timeout;
1096 }
1097 
setAbortOnTimeout(bool value)1098 Config& Config::setAbortOnTimeout(bool value)
1099 {
1100     if (mpData->abort_timeout == value)
1101         return *this;
1102     mpData->abort_timeout = value;
1103     Q_EMIT abortOnTimeoutChanged();
1104     Q_EMIT changed();
1105     return *this;
1106 }
1107 
lastFile() const1108 QString Config::lastFile() const
1109 {
1110     return mpData->last_file;
1111 }
1112 
setLastFile(const QString & value)1113 Config& Config::setLastFile(const QString &value)
1114 {
1115     if (mpData->last_file == value)
1116         return *this;
1117     mpData->last_file = value;
1118     Q_EMIT lastFileChanged();
1119     Q_EMIT changed();
1120     return *this;
1121 }
1122 
save()1123 void Config::save()
1124 {
1125     mpData->save();
1126 }
1127