From b623c689d9431805aa2447baf05e93bc6ed51e26 Mon Sep 17 00:00:00 2001 From: ArsenArsen Date: Sat, 17 Jun 2017 17:32:47 +0200 Subject: [PATCH] Add some encoder settings --- KShare.pro | 11 +- mainwindow.cpp | 11 +- mainwindow.hpp | 2 + mainwindow.ui | 35 ++- recording/encoders/encoder.cpp | 20 +- recording/encoders/encoder.hpp | 12 +- recording/encoders/encodersettings.cpp | 45 +++ recording/encoders/encodersettings.hpp | 32 +++ recording/encoders/encodersettingsdialog.cpp | 28 ++ recording/encoders/encodersettingsdialog.hpp | 23 ++ recording/encoders/encodersettingsdialog.ui | 278 +++++++++++++++++++ recording/recordingcontroller.cpp | 2 + recording/recordingcontroller.hpp | 1 + recording/recordingformats.cpp | 8 +- uploaders/uploadersingleton.cpp | 2 +- 15 files changed, 480 insertions(+), 30 deletions(-) create mode 100644 recording/encoders/encodersettings.cpp create mode 100644 recording/encoders/encodersettings.hpp create mode 100644 recording/encoders/encodersettingsdialog.cpp create mode 100644 recording/encoders/encodersettingsdialog.hpp create mode 100644 recording/encoders/encodersettingsdialog.ui diff --git a/KShare.pro b/KShare.pro index d8dceaa..a9138ef 100644 --- a/KShare.pro +++ b/KShare.pro @@ -53,7 +53,9 @@ SOURCES += main.cpp\ recording/recordingcontroller.cpp \ recording/recordingformats.cpp \ formats.cpp \ - recording/encoders/encoder.cpp + recording/encoders/encoder.cpp \ + recording/encoders/encodersettings.cpp \ + recording/encoders/encodersettingsdialog.cpp HEADERS += mainwindow.hpp \ cropeditor/cropeditor.hpp \ @@ -87,7 +89,9 @@ HEADERS += mainwindow.hpp \ recording/recordingcontroller.hpp \ recording/recordingformats.hpp \ formats.hpp \ - recording/encoders/encoder.hpp + recording/encoders/encoder.hpp \ + recording/encoders/encodersettings.hpp \ + recording/encoders/encodersettingsdialog.hpp LIBS += -lavcodec -lavformat -lavutil -lswscale -lavutil @@ -112,7 +116,8 @@ mac { FORMS += mainwindow.ui \ cropeditor/settings/brushpenselection.ui \ - cropeditor/settings/blurdialog.ui + cropeditor/settings/blurdialog.ui \ + recording/encoders/encodersettingsdialog.ui DISTFILES += \ README.md \ diff --git a/mainwindow.cpp b/mainwindow.cpp index 6443db0..f7fe5e2 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->hotkeys->setSelectionMode(QListWidget::SingleSelection); addHotkeyItem("Fullscreen image", "fullscreen", [] { screenshotter::fullscreen(); }); - addHotkeyItem("Area image", "area", [] { screenshotter::area(); }); + addHotkeyItem("Area image", "area", [] { // + screenshotter::area(); // + }); addHotkeyItem("Color picker", "picker", [] { ColorPickerScene::showPicker(); }); addHotkeyItem("Stop Recording", "recordingstop", [&] { controller->end(); }); addHotkeyItem("Start Recording", "recordingstart", [&] { @@ -230,3 +233,9 @@ void MainWindow::on_formatBox_currentIndexChanged(int index) { void MainWindow::on_imageFormatBox_currentIndexChanged(int index) { if (isVisible()) settings::settings().setValue("imageformat", index); } + +void MainWindow::on_pushButton_clicked() { + auto a = new EncoderSettingsDialog(); + a->setAttribute(Qt::WA_DeleteOnClose); + a->show(); +} diff --git a/mainwindow.hpp b/mainwindow.hpp index 6deaa46..043ee69 100644 --- a/mainwindow.hpp +++ b/mainwindow.hpp @@ -36,6 +36,8 @@ private slots: void on_formatBox_currentIndexChanged(int index); void on_imageFormatBox_currentIndexChanged(int index); + void on_pushButton_clicked(); + public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); diff --git a/mainwindow.ui b/mainwindow.ui index 3579434..910f49f 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -25,16 +25,6 @@ - - - - - - - -1 - - - @@ -73,7 +63,7 @@ - + <a href="https://github.com/ArsenArsen/KShare">Source code available free for everyone. Forever.</a> @@ -102,6 +92,9 @@ + + + @@ -112,9 +105,6 @@ - - - @@ -143,6 +133,23 @@ + + + + + + + -1 + + + + + + + Encoder settings + + + diff --git a/recording/encoders/encoder.cpp b/recording/encoders/encoder.cpp index bddafff..405bd94 100644 --- a/recording/encoders/encoder.cpp +++ b/recording/encoders/encoder.cpp @@ -13,7 +13,9 @@ inline void throwAVErr(int ret, std::string section) { throw std::runtime_error("Error during: " + section + ": " + newString); } -Encoder::Encoder(QString &targetFile, QSize res) { +#define OR_DEF(s, e1, e2) s ? s->e1 : e2 + +Encoder::Encoder(QString &targetFile, QSize res, CodecSettings *settings) { int ret; // Format ret = avformat_alloc_output_context2(&fc, NULL, NULL, targetFile.toLocal8Bit().constData()); @@ -38,25 +40,27 @@ Encoder::Encoder(QString &targetFile, QSize res) { out->enc->codec_id = codec->id; out->enc->codec = codec; - out->enc->bit_rate = 400000; + out->enc->bit_rate = OR_DEF(settings, bitrate, 400000); out->enc->width = res.width() % 2 ? res.width() - 1 : res.width(); out->enc->height = res.height() % 2 ? res.height() - 1 : res.height(); size = QSize(out->enc->width, out->enc->height); out->st->time_base = { 1, fps }; out->enc->time_base = out->st->time_base; - out->enc->gop_size = 12; + out->enc->gop_size = OR_DEF(settings, gopSize, 12); out->enc->pix_fmt = AV_PIX_FMT_YUV420P; // blaze it - if (out->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO) - out->enc->max_b_frames = 2; - else if (out->enc->codec_id == AV_CODEC_ID_MPEG1VIDEO) - out->enc->mb_decision = 2; - else if (out->enc->codec_id == AV_CODEC_ID_GIF) + if (out->enc->codec_id == AV_CODEC_ID_GIF) out->enc->pix_fmt = AV_PIX_FMT_RGB8; + else if (out->enc->codec_id == AV_CODEC_ID_H264 || out->enc->codec_id == AV_CODEC_ID_H265) { + av_opt_set(out->enc->priv_data, "preset", settings->h264Profile.toLocal8Bit().constData(), 0); + av_opt_set(out->enc->priv_data, "crf", std::to_string(settings->h264Crf).c_str(), 0); + } else if (out->enc->codec_id == AV_CODEC_ID_VP8 || out->enc->codec_id == AV_CODEC_ID_VP9) + av_opt_set(out->enc->priv_data, "lossless", std::to_string(settings->vp9Lossless).c_str(), 0); ret = avcodec_open2(out->enc, codec, NULL); if (ret < 0) throwAVErr(ret, "codec open"); + if (codec->capabilities & AV_CODEC_CAP_DR1) avcodec_align_dimensions(out->enc, &out->enc->width, &out->enc->height); ret = avcodec_parameters_from_context(out->st->codecpar, out->enc); if (ret < 0) throwAVErr(ret, "stream opt copy"); diff --git a/recording/encoders/encoder.hpp b/recording/encoders/encoder.hpp index 1bc972e..2d0be81 100644 --- a/recording/encoders/encoder.hpp +++ b/recording/encoders/encoder.hpp @@ -20,9 +20,19 @@ struct OutputStream { SwsContext *sws = NULL; }; +struct CodecSettings { + int bitrate; + int gopSize; + int bFrames; + int mbDecisions; + QString h264Profile; + int h264Crf; + bool vp9Lossless; +}; + class Encoder { public: - Encoder(QString &targetFile, QSize res); + Encoder(QString &targetFile, QSize res, CodecSettings *settings = NULL); ~Encoder(); bool addFrame(QImage frm); bool isRunning(); diff --git a/recording/encoders/encodersettings.cpp b/recording/encoders/encodersettings.cpp new file mode 100644 index 0000000..d75fb85 --- /dev/null +++ b/recording/encoders/encodersettings.cpp @@ -0,0 +1,45 @@ +#include "encodersettings.hpp" + +#undef SETTINGS_INTERFACE + +#define SETTINGS_FIELD(f, n, d, t) f = settings::settings().value(n, d).value(); +#define SETTINGS_INTERFACE(F, t) \ + t EncoderSettings::get##F() { \ + return F; \ + } \ + void EncoderSettings::set##F(t newf) { \ + F = newf; \ + } + +EncoderSettings::EncoderSettings() { + SETTINGS_FIELD(bitrate, "codec/bitrate", 400000, int); + SETTINGS_FIELD(gopSize, "codec/gopsize", 12, int); + SETTINGS_FIELD(h264Profile, "codec/h264Profile", "medium", QString); + SETTINGS_FIELD(h264Crf, "codec/h264Crf", 23, int); + SETTINGS_FIELD(vp9Lossless, "codec/vp9Lossless", false, bool); + SETTINGS_FIELD(imageQuality, "imageQuality", -1, int); +} + +SETTINGS_INTERFACE(bitrate, int) +SETTINGS_INTERFACE(gopSize, int) +SETTINGS_INTERFACE(h264Profile, QString) +SETTINGS_INTERFACE(h264Crf, int) +SETTINGS_INTERFACE(vp9Lossless, int) +SETTINGS_INTERFACE(imageQuality, int) + +EncoderSettings EncoderSettings::inst() { + static EncoderSettings e; + return e; +} + +#define SETTINGS_S_S(se) s->se = se; + +CodecSettings *EncoderSettings::getSettings() { + auto s = new CodecSettings; + SETTINGS_S_S(bitrate) + SETTINGS_S_S(gopSize) + SETTINGS_S_S(h264Profile) + SETTINGS_S_S(h264Crf) + SETTINGS_S_S(vp9Lossless) + return s; +} diff --git a/recording/encoders/encodersettings.hpp b/recording/encoders/encodersettings.hpp new file mode 100644 index 0000000..9a35b86 --- /dev/null +++ b/recording/encoders/encodersettings.hpp @@ -0,0 +1,32 @@ +#ifndef ENCODERSETTINGS_HPP +#define ENCODERSETTINGS_HPP + +#include "encoder.hpp" +#include +#define SETTINGS_INTERFACE(F, t) \ + t get##F(); \ + void set##F(t newf); + +class EncoderSettings { +public: + EncoderSettings(); + + static EncoderSettings inst(); + CodecSettings *getSettings(); + SETTINGS_INTERFACE(bitrate, int) + SETTINGS_INTERFACE(gopSize, int) + SETTINGS_INTERFACE(h264Profile, QString) + SETTINGS_INTERFACE(h264Crf, int) + SETTINGS_INTERFACE(vp9Lossless, int) + SETTINGS_INTERFACE(imageQuality, int) + +private: + int bitrate; + int gopSize; + QString h264Profile; + int h264Crf; + int imageQuality; + bool vp9Lossless; +}; + +#endif // ENCODERSETTINGS_HPP diff --git a/recording/encoders/encodersettingsdialog.cpp b/recording/encoders/encodersettingsdialog.cpp new file mode 100644 index 0000000..e182f8c --- /dev/null +++ b/recording/encoders/encodersettingsdialog.cpp @@ -0,0 +1,28 @@ +#include "encodersettingsdialog.hpp" +#include "encodersettings.hpp" +#include "ui_encodersettingsdialog.h" +#include +#include +#include +#include + +EncoderSettingsDialog::EncoderSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EncoderSettingsDialog) { + ui->setupUi(this); + setWindowTitle("KShare Encoder Settings"); + connect(ui->buttonBox, &QDialogButtonBox::accepted, [&] { + EncoderSettings::inst().setbitrate(ui->bitrate->value()); + EncoderSettings::inst().seth264Crf(ui->crf->value()); + EncoderSettings::inst().seth264Profile(ui->profileBox->currentText()); + EncoderSettings::inst().setvp9Lossless(ui->vp9Lossless->isChecked()); + EncoderSettings::inst().setgopSize(ui->gopSize->value()); + EncoderSettings::inst().setimageQuality(ui->defaultImageQuality->isChecked() ? -1 : ui->imageQuality->value()); + }); +} + +EncoderSettingsDialog::~EncoderSettingsDialog() { + delete ui; +} + +void EncoderSettingsDialog::on_defaultImageQuality_clicked(bool checked) { + ui->imageQuality->setEnabled(!checked); +} diff --git a/recording/encoders/encodersettingsdialog.hpp b/recording/encoders/encodersettingsdialog.hpp new file mode 100644 index 0000000..0891c16 --- /dev/null +++ b/recording/encoders/encodersettingsdialog.hpp @@ -0,0 +1,23 @@ +#ifndef ENCODERSETTINGSDIALOG_HPP +#define ENCODERSETTINGSDIALOG_HPP + +#include + +namespace Ui { +class EncoderSettingsDialog; +} + +class EncoderSettingsDialog : public QDialog { + Q_OBJECT +public: + explicit EncoderSettingsDialog(QWidget *parent = 0); + ~EncoderSettingsDialog(); + +private slots: + void on_defaultImageQuality_clicked(bool checked); + +private: + Ui::EncoderSettingsDialog *ui; +}; + +#endif // ENCODERSETTINGSDIALOG_HPP diff --git a/recording/encoders/encodersettingsdialog.ui b/recording/encoders/encodersettingsdialog.ui new file mode 100644 index 0000000..72ea6ac --- /dev/null +++ b/recording/encoders/encodersettingsdialog.ui @@ -0,0 +1,278 @@ + + + EncoderSettingsDialog + + + + 0 + 0 + 503 + 446 + + + + Dialog + + + + + + Image Encoder Settings + + + + + + false + + + 100 + + + 0 + + + + + + + <html><head/><body><p><a href="http://doc.qt.io/qt-5/qpixmap.html#save"><span style=" text-decoration: underline; color:#007af4;">Quality</span></a></p></body></html> + + + + + + + Format default + + + true + + + + + + + + + + Video Encoder Settings + + + + + + 2 + + + + Qt::LeftToRight + + + h264/h265 + + + + + + <html><head/><body><p><a href="https://trac.ffmpeg.org/wiki/Encode/H.264#crf"><span style=" text-decoration: underline; color:#007af4;">Preset</span></a></p></body></html> + + + + + + + ultrafast + + + + ultrafast + + + + + superfast + + + + + veryfast + + + + + faster + + + + + fast + + + + + medium + + + + + slow + + + + + slower + + + + + veryslow + + + + + placebo + + + + + + + + <html><head/><body><p><a href="https://trac.ffmpeg.org/wiki/Encode/H.264#crf"><span style=" text-decoration: underline; color:#007af4;">CRF</span></a></p></body></html> + + + + + + + 51 + + + 23 + + + + + + + + VP9 + + + + + + Lossless (not recommended) + + + + + + + + GIF + + + + + + TODO: Find whatever configuration GIF can have in ffmpeg's libav + + + + + + + + + + + Bitrate + + + + + + + kbps + + + 999999.000000000000000 + + + 400.000000000000000 + + + + + + + The number of pictures in a group of pictures, or 0 for intra only + + + GOP size + + + + + + + 0 + + + 12 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EncoderSettingsDialog + accept() + + + 227 + 518 + + + 157 + 274 + + + + + buttonBox + rejected() + EncoderSettingsDialog + reject() + + + 295 + 524 + + + 286 + 274 + + + + + diff --git a/recording/recordingcontroller.cpp b/recording/recordingcontroller.cpp index 551ba3a..b0722e5 100644 --- a/recording/recordingcontroller.cpp +++ b/recording/recordingcontroller.cpp @@ -70,6 +70,7 @@ void RecordingController::timeout() { time = 0; preview = 0; area = QRect(); + timer.stop(); return; } time++; @@ -91,6 +92,7 @@ void RecordingController::timeout() { if (isRunning()) preview->setTime(QString("%1:%2").arg(QString::number(minute)).arg(QString::number(second)), frame); } else { + timer.stop(); QMutexLocker l(&lock); if (!uploadQueue.isEmpty()) { auto a = uploadQueue.dequeue(); diff --git a/recording/recordingcontroller.hpp b/recording/recordingcontroller.hpp index 25290d9..a44291a 100644 --- a/recording/recordingcontroller.hpp +++ b/recording/recordingcontroller.hpp @@ -44,6 +44,7 @@ private slots: private: QMutex lock; + QMutex timerl; QQueue<_QueueContext> uploadQueue; QRect area; RecordingContext *_context = 0; diff --git a/recording/recordingformats.cpp b/recording/recordingformats.cpp index ca515a3..6cd5397 100644 --- a/recording/recordingformats.cpp +++ b/recording/recordingformats.cpp @@ -14,6 +14,8 @@ #include #include +#include + RecordingFormats::RecordingFormats(formats::Recording f) { QString tmp = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if (tmp.isEmpty()) { @@ -43,7 +45,9 @@ RecordingFormats::RecordingFormats(formats::Recording f) { validator = [&](QSize s) { if (!enc) { try { - enc = new Encoder(path, s); + auto es = EncoderSettings::inst().getSettings(); + enc = new Encoder(path, s, es); + delete es; if (!enc->isRunning()) { delete enc; return false; @@ -56,7 +60,7 @@ RecordingFormats::RecordingFormats(formats::Recording f) { return false; } } - return true; + return !interrupt; }; consumer = [&](QImage img) { if (!interrupt) try { diff --git a/uploaders/uploadersingleton.cpp b/uploaders/uploadersingleton.cpp index a1591e5..453cb5a 100644 --- a/uploaders/uploadersingleton.cpp +++ b/uploaders/uploadersingleton.cpp @@ -64,7 +64,7 @@ void UploaderSingleton::upload(QPixmap *pixmap) { QFile file(saveDir.absoluteFilePath(formatter::format(settings::settings().value("fileFormat").toString(), format.toLower()))); if (file.open(QFile::ReadWrite)) { - pixmap->save(&file, format.toLocal8Bit().constData()); + pixmap->save(&file, format.toLocal8Bit().constData(), settings::settings().value("imageQuality", -1).toInt()); file.seek(0); u->doUpload(file.readAll(), format); } else