Added arrow tool
This commit is contained in:
parent
c983e4eb2a
commit
e2999b4267
@ -62,7 +62,8 @@ SOURCES += main.cpp\
|
|||||||
cropeditor/drawing/eraseritem.cpp \
|
cropeditor/drawing/eraseritem.cpp \
|
||||||
cropeditor/drawing/rectitem.cpp \
|
cropeditor/drawing/rectitem.cpp \
|
||||||
cropeditor/drawing/ellipseitem.cpp \
|
cropeditor/drawing/ellipseitem.cpp \
|
||||||
hotkeyinputdialog.cpp
|
hotkeyinputdialog.cpp \
|
||||||
|
cropeditor/drawing/arrowitem.cpp
|
||||||
|
|
||||||
HEADERS += mainwindow.hpp \
|
HEADERS += mainwindow.hpp \
|
||||||
cropeditor/cropeditor.hpp \
|
cropeditor/cropeditor.hpp \
|
||||||
@ -103,7 +104,8 @@ HEADERS += mainwindow.hpp \
|
|||||||
cropeditor/drawing/eraseritem.hpp \
|
cropeditor/drawing/eraseritem.hpp \
|
||||||
cropeditor/drawing/rectitem.hpp \
|
cropeditor/drawing/rectitem.hpp \
|
||||||
cropeditor/drawing/ellipseitem.hpp \
|
cropeditor/drawing/ellipseitem.hpp \
|
||||||
hotkeyinputdialog.hpp
|
hotkeyinputdialog.hpp \
|
||||||
|
cropeditor/drawing/arrowitem.hpp
|
||||||
|
|
||||||
LIBS += -lavcodec -lavformat -lavutil -lswscale -lavutil
|
LIBS += -lavcodec -lavformat -lavutil -lswscale -lavutil
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <cropeditor/drawing/arrowitem.hpp>
|
||||||
#include <cropeditor/drawing/bluritem.hpp>
|
#include <cropeditor/drawing/bluritem.hpp>
|
||||||
#include <cropeditor/drawing/dotitem.hpp>
|
#include <cropeditor/drawing/dotitem.hpp>
|
||||||
#include <cropeditor/drawing/ellipseitem.hpp>
|
#include <cropeditor/drawing/ellipseitem.hpp>
|
||||||
@ -36,6 +37,7 @@ CropScene::CropScene(QObject *parent, QPixmap *pixmap)
|
|||||||
addDrawingAction(menu, "Text", [] { return new TextItem; });
|
addDrawingAction(menu, "Text", [] { return new TextItem; });
|
||||||
addDrawingAction(menu, "Rectangle", [] { return new RectItem; });
|
addDrawingAction(menu, "Rectangle", [] { return new RectItem; });
|
||||||
addDrawingAction(menu, "Ellipse", [] { return new EllipseItem; });
|
addDrawingAction(menu, "Ellipse", [] { return new EllipseItem; });
|
||||||
|
addDrawingAction(menu, "Arrow", [] { return new ArrowItem; });
|
||||||
|
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
addDrawingAction(menu, "Eraser", [] { return new EraserItem; });
|
addDrawingAction(menu, "Eraser", [] { return new EraserItem; });
|
||||||
|
21
cropeditor/drawing/arrowitem.cpp
Normal file
21
cropeditor/drawing/arrowitem.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "arrowitem.hpp"
|
||||||
|
#include <QtMath>
|
||||||
|
#include <settings.hpp>
|
||||||
|
|
||||||
|
void ArrowItem::mouseDragEvent(QGraphicsSceneMouseEvent *e, CropScene *scene) {
|
||||||
|
if (init.isNull()) {
|
||||||
|
init = e->scenePos();
|
||||||
|
line = scene->addLine(QLineF(init, init), scene->pen());
|
||||||
|
QPainterPath poly;
|
||||||
|
qreal w = settings::settings().value("arrow/width", 20).toDouble() / 2;
|
||||||
|
qreal h = settings::settings().value("arrow/height", 10).toDouble();
|
||||||
|
poly.lineTo(QPoint(-w, h));
|
||||||
|
poly.lineTo(QPoint(0, 0));
|
||||||
|
poly.lineTo(QPoint(w, h));
|
||||||
|
head = scene->addPath(poly, scene->pen(), scene->brush());
|
||||||
|
} else {
|
||||||
|
line->setLine(QLineF(init, e->scenePos()));
|
||||||
|
head->setRotation(270 + qRadiansToDegrees(qAtan2((init.y() - e->scenePos().y()), (init.x() - e->scenePos().x()))));
|
||||||
|
}
|
||||||
|
head->setPos(e->scenePos());
|
||||||
|
}
|
24
cropeditor/drawing/arrowitem.hpp
Normal file
24
cropeditor/drawing/arrowitem.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef ARROWITEM_HPP
|
||||||
|
#define ARROWITEM_HPP
|
||||||
|
|
||||||
|
#include "drawitem.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
class ArrowItem : public DrawItem {
|
||||||
|
public:
|
||||||
|
ArrowItem() {
|
||||||
|
}
|
||||||
|
QString name() override {
|
||||||
|
return "Arrow";
|
||||||
|
}
|
||||||
|
void mouseDragEvent(QGraphicsSceneMouseEvent *e, CropScene *scene) override;
|
||||||
|
void mouseDragEndEvent(QGraphicsSceneMouseEvent *, CropScene *) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QGraphicsLineItem *line;
|
||||||
|
QGraphicsPathItem *head;
|
||||||
|
QPointF init;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARROWITEM_HPP
|
@ -26,6 +26,9 @@ BrushPenSelection::BrushPenSelection(CropScene *scene) : QDialog(), ui(new Ui::B
|
|||||||
ui->brushStyle->setCurrentIndex(settings::settings().value("brushStyle", 1).toInt());
|
ui->brushStyle->setCurrentIndex(settings::settings().value("brushStyle", 1).toInt());
|
||||||
ui->pathItemHasBrush->setChecked(settings::settings().value("brushPath", false).toBool());
|
ui->pathItemHasBrush->setChecked(settings::settings().value("brushPath", false).toBool());
|
||||||
|
|
||||||
|
ui->arroww->setValue(settings::settings().value("arrow/width", 20).toDouble());
|
||||||
|
ui->arrowh->setValue(settings::settings().value("arrow/height", 10).toDouble());
|
||||||
|
|
||||||
this->setFocus();
|
this->setFocus();
|
||||||
pen = scene->pen().color();
|
pen = scene->pen().color();
|
||||||
ui->penAlphaSlider->setValue(pen.alpha());
|
ui->penAlphaSlider->setValue(pen.alpha());
|
||||||
@ -69,6 +72,8 @@ void BrushPenSelection::on_buttonBox_accepted() {
|
|||||||
settings::settings().setValue("blur/animatedHint", ui->animated->isChecked());
|
settings::settings().setValue("blur/animatedHint", ui->animated->isChecked());
|
||||||
settings::settings().setValue("blur/qualityHint", ui->quality->isChecked());
|
settings::settings().setValue("blur/qualityHint", ui->quality->isChecked());
|
||||||
settings::settings().setValue("blur/performanceHint", ui->performance->isChecked());
|
settings::settings().setValue("blur/performanceHint", ui->performance->isChecked());
|
||||||
|
settings::settings().setValue("arrow/width", ui->arroww->value());
|
||||||
|
settings::settings().setValue("arrow/height", ui->arrowh->value());
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>449</width>
|
<width>442</width>
|
||||||
<height>489</height>
|
<height>493</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
@ -84,12 +84,115 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" rowspan="2">
|
<item row="5" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" rowspan="3">
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Blur settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="3" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="performance">
|
||||||
|
<property name="text">
|
||||||
|
<string>Performance Hint</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string>http://doc.qt.io/qt-5/qgraphicsblureffect.html#BlurHint-enum</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><a href="http://doc.qt.io/qt-5/qgraphicsblureffect.html#BlurHint-enum">Blur Hints</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextBrowserInteraction</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QDoubleSpinBox" name="radSpinner">
|
||||||
|
<property name="suffix">
|
||||||
|
<string>px</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>30.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QSlider" name="radSlider">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>3000</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Blur Radius</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="animated">
|
||||||
|
<property name="text">
|
||||||
|
<string>Animated Hint</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="quality">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quality Hint</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0" colspan="3">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" rowspan="4">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Brush settings</string>
|
<string>Brush settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Brush alpha</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QComboBox" name="brushStyle">
|
<widget class="QComboBox" name="brushStyle">
|
||||||
<item>
|
<item>
|
||||||
@ -169,10 +272,10 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="pathItemHasBrush">
|
<widget class="QPushButton" name="brushColor">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Path item has brush</string>
|
<string>Choose brush color</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -193,113 +296,46 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="brushColor">
|
<widget class="QCheckBox" name="pathItemHasBrush">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Choose brush color</string>
|
<string>Path item has brush</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Brush alpha</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="3" column="1" rowspan="2">
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
<property name="centerButtons">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1" rowspan="3">
|
|
||||||
<widget class="QGroupBox" name="groupBox_3">
|
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Blur settings</string>
|
<string>Arrow settings</string>
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="whatsThis">
|
|
||||||
<string>http://doc.qt.io/qt-5/qgraphicsblureffect.html#BlurHint-enum</string>
|
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_5">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><a href="http://doc.qt.io/qt-5/qgraphicsblureffect.html#BlurHint-enum">Blur Hints</string>
|
<string>Arrow width and height</string>
|
||||||
</property>
|
|
||||||
<property name="openExternalLinks">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::TextBrowserInteraction</set>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="1" column="0">
|
||||||
<widget class="QSlider" name="radSlider">
|
<widget class="QDoubleSpinBox" name="arroww">
|
||||||
<property name="maximum">
|
<property name="prefix">
|
||||||
<number>3000</number>
|
<string>w: </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QDoubleSpinBox" name="radSpinner">
|
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string>px</string>
|
<string>px</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
|
||||||
<double>30.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="3">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="performance">
|
<widget class="QDoubleSpinBox" name="arrowh">
|
||||||
<property name="text">
|
<property name="prefix">
|
||||||
<string>Performance Hint</string>
|
<string>h: </string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="suffix">
|
||||||
</item>
|
<string>px</string>
|
||||||
<item row="4" column="0" colspan="3">
|
|
||||||
<widget class="QCheckBox" name="animated">
|
|
||||||
<property name="text">
|
|
||||||
<string>Animated Hint</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0" rowspan="2" colspan="3">
|
|
||||||
<widget class="QCheckBox" name="quality">
|
|
||||||
<property name="text">
|
|
||||||
<string>Quality Hint</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="0" colspan="3">
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" colspan="3">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Blur Radius</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
171
recording/encoders/encoder.cpp.orig
Normal file
171
recording/encoders/encoder.cpp.orig
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include "encoder.hpp"
|
||||||
|
|
||||||
|
#include <settings.hpp>
|
||||||
|
extern "C" {
|
||||||
|
#include <libavutil/imgutils.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void throwAVErr(int ret, std::string section) {
|
||||||
|
char err[AV_ERROR_MAX_STRING_SIZE];
|
||||||
|
av_make_error_string(err, AV_ERROR_MAX_STRING_SIZE, ret);
|
||||||
|
std::string newString(err);
|
||||||
|
throw std::runtime_error("Error during: " + section + ": " + newString);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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());
|
||||||
|
if (ret < 0) throwAVErr(ret, "format alloc");
|
||||||
|
|
||||||
|
// Stream
|
||||||
|
out->st = avformat_new_stream(fc, NULL);
|
||||||
|
if (!out->st) throw std::runtime_error("Unable to allocate video context");
|
||||||
|
out->st->id = fc->nb_streams - 1;
|
||||||
|
if (fc->oformat->flags & AVFMT_GLOBALHEADER) fc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
av_dump_format(fc, 0, targetFile.toLocal8Bit().constData(), 1);
|
||||||
|
|
||||||
|
// Codec
|
||||||
|
if (!fc->oformat->video_codec) throw std::runtime_error("Video codec not found");
|
||||||
|
codec = avcodec_find_encoder(fc->oformat->video_codec);
|
||||||
|
if (!codec) throw std::runtime_error("Video codec not found");
|
||||||
|
out->enc = avcodec_alloc_context3(codec);
|
||||||
|
if (!out->enc) throw std::runtime_error("Unable to allocate video context");
|
||||||
|
|
||||||
|
int fps = settings::settings().value("recording/framerate", 30).toInt();
|
||||||
|
|
||||||
|
out->enc->codec_id = codec->id;
|
||||||
|
out->enc->codec = codec;
|
||||||
|
|
||||||
|
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 = OR_DEF(settings, gopSize, 12);
|
||||||
|
out->enc->pix_fmt = AV_PIX_FMT_YUV420P; // blaze it
|
||||||
|
AVDictionary *dict = NULL;
|
||||||
|
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_dict_set(&dict, "preset", settings ? settings->h264Profile.toLocal8Bit().constData() : "medium", 0);
|
||||||
|
av_dict_set_int(&dict, "crf", OR_DEF(settings, h264Crf, 12), 0);
|
||||||
|
} else if (out->enc->codec_id == AV_CODEC_ID_VP8 || out->enc->codec_id == AV_CODEC_ID_VP9)
|
||||||
|
av_dict_set_int(&dict, "lossless", OR_DEF(settings, vp9Lossless, false), 0);
|
||||||
|
|
||||||
|
|
||||||
|
ret = avcodec_open2(out->enc, codec, &dict);
|
||||||
|
av_dict_free(&dict);
|
||||||
|
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");
|
||||||
|
|
||||||
|
// Frames
|
||||||
|
out->frame = av_frame_alloc();
|
||||||
|
if (!out->frame) {
|
||||||
|
throw std::runtime_error("frame alloc");
|
||||||
|
}
|
||||||
|
out->frame->format = out->enc->pix_fmt;
|
||||||
|
out->frame->width = out->enc->width;
|
||||||
|
out->frame->height = out->enc->height;
|
||||||
|
ret = av_frame_get_buffer(out->frame, 32);
|
||||||
|
if (ret < 0) throwAVErr(ret, "frame buffer alloc");
|
||||||
|
|
||||||
|
// Writer
|
||||||
|
ret = avio_open(&fc->pb, targetFile.toLocal8Bit().constData(), AVIO_FLAG_WRITE);
|
||||||
|
if (ret < 0) throwAVErr(ret, "writer open");
|
||||||
|
ret = avformat_write_header(fc, NULL);
|
||||||
|
if (ret < 0) throwAVErr(ret, "write header");
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::setFrameRGB(QImage img) {
|
||||||
|
uint8_t *rgb = (uint8_t *)img.bits();
|
||||||
|
int ret = av_frame_make_writable(out->frame);
|
||||||
|
if (ret < 0) throwAVErr(ret, "setFrameRGB");
|
||||||
|
int lineSize[1] = { img.bytesPerLine() };
|
||||||
|
out->sws = sws_getCachedContext(out->sws, out->enc->width, out->enc->height, AV_PIX_FMT_RGB24, out->enc->width,
|
||||||
|
out->enc->height, (AVPixelFormat)out->frame->format, 0, 0, 0, 0);
|
||||||
|
sws_scale(out->sws, (const uint8_t *const *)&rgb, lineSize, 0, out->enc->height, out->frame->data, out->frame->linesize);
|
||||||
|
out->frame->pts = out->nextPts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoder::~Encoder() {
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Encoder::addFrame(QImage frm) {
|
||||||
|
if (!success) return false;
|
||||||
|
if (frm.size() != size) frm = frm.copy(QRect(QPoint(0, 0), size));
|
||||||
|
if (frm.format() != QImage::Format_RGB888) frm = frm.convertToFormat(QImage::Format_RGB888);
|
||||||
|
setFrameRGB(frm);
|
||||||
|
AVPacket pkt;
|
||||||
|
pkt.size = 0;
|
||||||
|
pkt.data = NULL;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
int ret = avcodec_send_frame(out->enc, out->frame);
|
||||||
|
if (ret == AVERROR(EAGAIN)) {
|
||||||
|
do {
|
||||||
|
ret = avcodec_receive_packet(out->enc, &pkt);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret != AVERROR(EAGAIN))
|
||||||
|
throwAVErr(ret, "receive packet");
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
av_packet_rescale_ts(&pkt, out->enc->time_base, out->st->time_base);
|
||||||
|
pkt.stream_index = out->st->index;
|
||||||
|
ret = av_interleaved_write_frame(fc, &pkt);
|
||||||
|
} while (ret >= 0);
|
||||||
|
if (ret < 0 && ret != AVERROR(EAGAIN)) {
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
throwAVErr(ret, "send frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
if (ret < 0 && ret != AVERROR(EAGAIN)) throwAVErr(ret, "send frame");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Encoder::isRunning() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Encoder::end() {
|
||||||
|
if (ended) return false;
|
||||||
|
ended = true;
|
||||||
|
if (!success) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
avcodec_send_frame(out->enc, NULL);
|
||||||
|
int ret;
|
||||||
|
AVPacket pkt;
|
||||||
|
pkt.size = 0;
|
||||||
|
pkt.data = NULL;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
do {
|
||||||
|
ret = avcodec_receive_packet(out->enc, &pkt);
|
||||||
|
if (ret < 0) break;
|
||||||
|
av_packet_rescale_ts(&pkt, out->enc->time_base, out->st->time_base);
|
||||||
|
pkt.stream_index = out->st->index;
|
||||||
|
av_interleaved_write_frame(fc, &pkt);
|
||||||
|
} while (ret >= 0);
|
||||||
|
av_write_trailer(fc);
|
||||||
|
cleanup:
|
||||||
|
avcodec_free_context(&out->enc);
|
||||||
|
av_frame_free(&out->frame);
|
||||||
|
sws_freeContext(out->sws);
|
||||||
|
delete out;
|
||||||
|
if (!(fc->oformat->flags & AVFMT_NOFILE)) //
|
||||||
|
avio_closep(&fc->pb);
|
||||||
|
avformat_free_context(fc);
|
||||||
|
return success;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user